From 41974ae5265fbc23a06f276f9e008d5dad020e0b Mon Sep 17 00:00:00 2001 From: Ben Longbons <b.r.longbons@gmail.com> Date: Thu, 30 Aug 2012 16:16:25 -0700 Subject: Rename files for C++ conversion. Does not compile. After updating, you can remove these files, as shown in 'git status': Untracked files: (use "git add <file>..." to include in what will be committed) src/map/magic-interpreter-lexer.c src/map/magic-interpreter-parser.c src/map/magic-interpreter-parser.h --- .gitignore | 9 +- GNUmakefile | 43 +- deps.make | 308 - make.defs | 15 +- src/char/char.c | 4076 ----------- src/char/char.cpp | 4076 +++++++++++ src/char/char.h | 32 - src/char/char.hpp | 32 + src/char/int_guild.c | 1757 ----- src/char/int_guild.cpp | 1757 +++++ src/char/int_guild.h | 16 - src/char/int_guild.hpp | 16 + src/char/int_party.c | 673 -- src/char/int_party.cpp | 673 ++ src/char/int_party.h | 14 - src/char/int_party.hpp | 14 + src/char/int_storage.c | 576 -- src/char/int_storage.cpp | 576 ++ src/char/int_storage.h | 18 - src/char/int_storage.hpp | 18 + src/char/inter.c | 635 -- src/char/inter.cpp | 635 ++ src/char/inter.h | 19 - src/char/inter.hpp | 19 + src/common/core.c | 97 - src/common/core.cpp | 97 + src/common/core.h | 19 - src/common/core.hpp | 19 + src/common/db.c | 546 -- src/common/db.cpp | 546 ++ src/common/db.h | 87 - src/common/db.hpp | 87 + src/common/grfio.c | 212 - src/common/grfio.cpp | 212 + src/common/grfio.h | 17 - src/common/grfio.hpp | 17 + src/common/lock.c | 36 - src/common/lock.cpp | 36 + src/common/lock.h | 8 - src/common/lock.hpp | 8 + src/common/md5calc.c | 339 - src/common/md5calc.cpp | 339 + src/common/md5calc.h | 64 - src/common/md5calc.hpp | 64 + src/common/mmo.h | 283 - src/common/mmo.hpp | 283 + src/common/mt_rand.c | 116 - src/common/mt_rand.cpp | 116 + src/common/mt_rand.h | 24 - src/common/mt_rand.hpp | 24 + src/common/nullpo.c | 66 - src/common/nullpo.cpp | 66 + src/common/nullpo.h | 61 - src/common/nullpo.hpp | 61 + src/common/sanity.h | 36 - src/common/sanity.hpp | 31 + src/common/socket.c | 405 -- src/common/socket.cpp | 405 ++ src/common/socket.h | 135 - src/common/socket.hpp | 135 + src/common/timer.c | 257 - src/common/timer.cpp | 257 + src/common/timer.h | 61 - src/common/timer.hpp | 61 + src/common/utils.h | 18 - src/common/utils.hpp | 18 + src/common/version.h | 24 - src/common/version.hpp | 24 + src/ladmin/ladmin.c | 6476 ----------------- src/ladmin/ladmin.cpp | 6476 +++++++++++++++++ src/ladmin/ladmin.h | 11 - src/ladmin/ladmin.hpp | 11 + src/login/login.c | 5038 -------------- src/login/login.cpp | 5038 ++++++++++++++ src/login/login.h | 41 - src/login/login.hpp | 41 + src/map/atcommand.c | 8728 ----------------------- src/map/atcommand.cpp | 8728 +++++++++++++++++++++++ src/map/atcommand.h | 226 - src/map/atcommand.hpp | 226 + src/map/battle.c | 6282 ----------------- src/map/battle.cpp | 6282 +++++++++++++++++ src/map/battle.h | 357 - src/map/battle.hpp | 357 + src/map/chat.c | 395 -- src/map/chat.cpp | 395 ++ src/map/chat.h | 25 - src/map/chat.hpp | 25 + src/map/chrif.c | 1307 ---- src/map/chrif.cpp | 1307 ++++ src/map/chrif.h | 36 - src/map/chrif.hpp | 36 + src/map/clif.c | 10332 --------------------------- src/map/clif.cpp | 10332 +++++++++++++++++++++++++++ src/map/clif.h | 285 - src/map/clif.hpp | 285 + src/map/guild.c | 1915 ----- src/map/guild.cpp | 1915 +++++ src/map/guild.h | 95 - src/map/guild.hpp | 95 + src/map/intif.c | 1201 ---- src/map/intif.cpp | 1201 ++++ src/map/intif.h | 56 - src/map/intif.hpp | 56 + src/map/itemdb.c | 753 -- src/map/itemdb.cpp | 753 ++ src/map/itemdb.h | 82 - src/map/itemdb.hpp | 82 + src/map/magic-expr-eval.h | 44 - src/map/magic-expr-eval.hpp | 44 + src/map/magic-expr.c | 1655 ----- src/map/magic-expr.cpp | 1655 +++++ src/map/magic-expr.h | 95 - src/map/magic-expr.hpp | 95 + src/map/magic-interpreter-aux.h | 8 - src/map/magic-interpreter-aux.hpp | 8 + src/map/magic-interpreter-base.c | 562 -- src/map/magic-interpreter-base.cpp | 562 ++ src/map/magic-interpreter-lexer.l | 140 - src/map/magic-interpreter-lexer.lpp | 140 + src/map/magic-interpreter-parser.y | 1089 --- src/map/magic-interpreter-parser.ypp | 1089 +++ src/map/magic-interpreter.h | 500 -- src/map/magic-interpreter.hpp | 500 ++ src/map/magic-stmt.c | 1597 ----- src/map/magic-stmt.cpp | 1597 +++++ src/map/magic.c | 130 - src/map/magic.cpp | 130 + src/map/magic.h | 90 - src/map/magic.hpp | 90 + src/map/map.c | 2215 ------ src/map/map.cpp | 2215 ++++++ src/map/map.h | 822 --- src/map/map.hpp | 822 +++ src/map/mob.c | 5070 -------------- src/map/mob.cpp | 5070 ++++++++++++++ src/map/mob.h | 151 - src/map/mob.hpp | 151 + src/map/npc.c | 2378 ------- src/map/npc.cpp | 2378 +++++++ src/map/npc.h | 64 - src/map/npc.hpp | 64 + src/map/party.c | 790 --- src/map/party.cpp | 790 +++ src/map/party.h | 52 - src/map/party.hpp | 52 + src/map/path.c | 445 -- src/map/path.cpp | 445 ++ src/map/pc.c | 9032 ------------------------ src/map/pc.cpp | 9032 ++++++++++++++++++++++++ src/map/pc.h | 210 - src/map/pc.hpp | 210 + src/map/script.c | 8145 ---------------------- src/map/script.cpp | 8145 ++++++++++++++++++++++ src/map/script.h | 52 - src/map/script.hpp | 52 + src/map/skill-pools.c | 158 - src/map/skill-pools.cpp | 158 + src/map/skill.c | 12279 --------------------------------- src/map/skill.cpp | 12279 +++++++++++++++++++++++++++++++++ src/map/skill.h | 891 --- src/map/skill.hpp | 891 +++ src/map/storage.c | 787 --- src/map/storage.cpp | 787 +++ src/map/storage.h | 54 - src/map/storage.hpp | 54 + src/map/tmw.c | 187 - src/map/tmw.cpp | 187 + src/map/tmw.h | 14 - src/map/tmw.hpp | 14 + src/map/trade.c | 432 -- src/map/trade.cpp | 432 ++ src/map/trade.h | 14 - src/map/trade.hpp | 14 + src/tool/adduser.c | 115 - src/tool/adduser.cpp | 115 + src/tool/convert.c | 302 - src/tool/convert.cpp | 302 + src/tool/eathena-monitor.c | 223 - src/tool/eathena-monitor.cpp | 223 + src/tool/itemfrob.c | 130 - src/tool/itemfrob.cpp | 130 + src/tool/mapfrob.c | 129 - src/tool/mapfrob.cpp | 129 + src/tool/marriage-info.c | 482 -- src/tool/marriage-info.cpp | 482 ++ src/tool/moneycount/athena_text.cpp | 4 +- src/tool/moneycount/athena_text.h | 9 - src/tool/moneycount/athena_text.hpp | 9 + src/tool/moneycount/main.cpp | 4 +- src/tool/moneycount/mmo.h | 309 - src/tool/moneycount/mmo.hpp | 309 + src/tool/skillfrob.c | 80 - src/tool/skillfrob.cpp | 80 + src/webserver/generate.c | 35 - src/webserver/generate.cpp | 35 + src/webserver/htmlstyle.c | 45 - src/webserver/htmlstyle.cpp | 45 + src/webserver/logs.c | 8 - src/webserver/logs.cpp | 8 + src/webserver/main.c | 145 - src/webserver/main.cpp | 145 + src/webserver/pages/about.c | 6 - src/webserver/pages/about.cpp | 6 + src/webserver/pages/notdone.c | 6 - src/webserver/pages/notdone.cpp | 6 + src/webserver/pages/sample.c | 22 - src/webserver/pages/sample.cpp | 22 + src/webserver/parse.c | 132 - src/webserver/parse.cpp | 132 + 210 files changed, 106729 insertions(+), 107045 deletions(-) delete mode 100644 deps.make delete mode 100644 src/char/char.c create mode 100644 src/char/char.cpp delete mode 100644 src/char/char.h create mode 100644 src/char/char.hpp delete mode 100644 src/char/int_guild.c create mode 100644 src/char/int_guild.cpp delete mode 100644 src/char/int_guild.h create mode 100644 src/char/int_guild.hpp delete mode 100644 src/char/int_party.c create mode 100644 src/char/int_party.cpp delete mode 100644 src/char/int_party.h create mode 100644 src/char/int_party.hpp delete mode 100644 src/char/int_storage.c create mode 100644 src/char/int_storage.cpp delete mode 100644 src/char/int_storage.h create mode 100644 src/char/int_storage.hpp delete mode 100644 src/char/inter.c create mode 100644 src/char/inter.cpp delete mode 100644 src/char/inter.h create mode 100644 src/char/inter.hpp delete mode 100644 src/common/core.c create mode 100644 src/common/core.cpp delete mode 100644 src/common/core.h create mode 100644 src/common/core.hpp delete mode 100644 src/common/db.c create mode 100644 src/common/db.cpp delete mode 100644 src/common/db.h create mode 100644 src/common/db.hpp delete mode 100644 src/common/grfio.c create mode 100644 src/common/grfio.cpp delete mode 100644 src/common/grfio.h create mode 100644 src/common/grfio.hpp delete mode 100644 src/common/lock.c create mode 100644 src/common/lock.cpp delete mode 100644 src/common/lock.h create mode 100644 src/common/lock.hpp delete mode 100644 src/common/md5calc.c create mode 100644 src/common/md5calc.cpp delete mode 100644 src/common/md5calc.h create mode 100644 src/common/md5calc.hpp delete mode 100644 src/common/mmo.h create mode 100644 src/common/mmo.hpp delete mode 100644 src/common/mt_rand.c create mode 100644 src/common/mt_rand.cpp delete mode 100644 src/common/mt_rand.h create mode 100644 src/common/mt_rand.hpp delete mode 100644 src/common/nullpo.c create mode 100644 src/common/nullpo.cpp delete mode 100644 src/common/nullpo.h create mode 100644 src/common/nullpo.hpp delete mode 100644 src/common/sanity.h create mode 100644 src/common/sanity.hpp delete mode 100644 src/common/socket.c create mode 100644 src/common/socket.cpp delete mode 100644 src/common/socket.h create mode 100644 src/common/socket.hpp delete mode 100644 src/common/timer.c create mode 100644 src/common/timer.cpp delete mode 100644 src/common/timer.h create mode 100644 src/common/timer.hpp delete mode 100644 src/common/utils.h create mode 100644 src/common/utils.hpp delete mode 100644 src/common/version.h create mode 100644 src/common/version.hpp delete mode 100644 src/ladmin/ladmin.c create mode 100644 src/ladmin/ladmin.cpp delete mode 100644 src/ladmin/ladmin.h create mode 100644 src/ladmin/ladmin.hpp delete mode 100644 src/login/login.c create mode 100644 src/login/login.cpp delete mode 100644 src/login/login.h create mode 100644 src/login/login.hpp delete mode 100644 src/map/atcommand.c create mode 100644 src/map/atcommand.cpp delete mode 100644 src/map/atcommand.h create mode 100644 src/map/atcommand.hpp delete mode 100644 src/map/battle.c create mode 100644 src/map/battle.cpp delete mode 100644 src/map/battle.h create mode 100644 src/map/battle.hpp delete mode 100644 src/map/chat.c create mode 100644 src/map/chat.cpp delete mode 100644 src/map/chat.h create mode 100644 src/map/chat.hpp delete mode 100644 src/map/chrif.c create mode 100644 src/map/chrif.cpp delete mode 100644 src/map/chrif.h create mode 100644 src/map/chrif.hpp delete mode 100644 src/map/clif.c create mode 100644 src/map/clif.cpp delete mode 100644 src/map/clif.h create mode 100644 src/map/clif.hpp delete mode 100644 src/map/guild.c create mode 100644 src/map/guild.cpp delete mode 100644 src/map/guild.h create mode 100644 src/map/guild.hpp delete mode 100644 src/map/intif.c create mode 100644 src/map/intif.cpp delete mode 100644 src/map/intif.h create mode 100644 src/map/intif.hpp delete mode 100644 src/map/itemdb.c create mode 100644 src/map/itemdb.cpp delete mode 100644 src/map/itemdb.h create mode 100644 src/map/itemdb.hpp delete mode 100644 src/map/magic-expr-eval.h create mode 100644 src/map/magic-expr-eval.hpp delete mode 100644 src/map/magic-expr.c create mode 100644 src/map/magic-expr.cpp delete mode 100644 src/map/magic-expr.h create mode 100644 src/map/magic-expr.hpp delete mode 100644 src/map/magic-interpreter-aux.h create mode 100644 src/map/magic-interpreter-aux.hpp delete mode 100644 src/map/magic-interpreter-base.c create mode 100644 src/map/magic-interpreter-base.cpp delete mode 100644 src/map/magic-interpreter-lexer.l create mode 100644 src/map/magic-interpreter-lexer.lpp delete mode 100644 src/map/magic-interpreter-parser.y create mode 100644 src/map/magic-interpreter-parser.ypp delete mode 100644 src/map/magic-interpreter.h create mode 100644 src/map/magic-interpreter.hpp delete mode 100644 src/map/magic-stmt.c create mode 100644 src/map/magic-stmt.cpp delete mode 100644 src/map/magic.c create mode 100644 src/map/magic.cpp delete mode 100644 src/map/magic.h create mode 100644 src/map/magic.hpp delete mode 100644 src/map/map.c create mode 100644 src/map/map.cpp delete mode 100644 src/map/map.h create mode 100644 src/map/map.hpp delete mode 100644 src/map/mob.c create mode 100644 src/map/mob.cpp delete mode 100644 src/map/mob.h create mode 100644 src/map/mob.hpp delete mode 100644 src/map/npc.c create mode 100644 src/map/npc.cpp delete mode 100644 src/map/npc.h create mode 100644 src/map/npc.hpp delete mode 100644 src/map/party.c create mode 100644 src/map/party.cpp delete mode 100644 src/map/party.h create mode 100644 src/map/party.hpp delete mode 100644 src/map/path.c create mode 100644 src/map/path.cpp delete mode 100644 src/map/pc.c create mode 100644 src/map/pc.cpp delete mode 100644 src/map/pc.h create mode 100644 src/map/pc.hpp delete mode 100644 src/map/script.c create mode 100644 src/map/script.cpp delete mode 100644 src/map/script.h create mode 100644 src/map/script.hpp delete mode 100644 src/map/skill-pools.c create mode 100644 src/map/skill-pools.cpp delete mode 100644 src/map/skill.c create mode 100644 src/map/skill.cpp delete mode 100644 src/map/skill.h create mode 100644 src/map/skill.hpp delete mode 100644 src/map/storage.c create mode 100644 src/map/storage.cpp delete mode 100644 src/map/storage.h create mode 100644 src/map/storage.hpp delete mode 100644 src/map/tmw.c create mode 100644 src/map/tmw.cpp delete mode 100644 src/map/tmw.h create mode 100644 src/map/tmw.hpp delete mode 100644 src/map/trade.c create mode 100644 src/map/trade.cpp delete mode 100644 src/map/trade.h create mode 100644 src/map/trade.hpp delete mode 100644 src/tool/adduser.c create mode 100644 src/tool/adduser.cpp delete mode 100644 src/tool/convert.c create mode 100644 src/tool/convert.cpp delete mode 100644 src/tool/eathena-monitor.c create mode 100644 src/tool/eathena-monitor.cpp delete mode 100644 src/tool/itemfrob.c create mode 100644 src/tool/itemfrob.cpp delete mode 100644 src/tool/mapfrob.c create mode 100644 src/tool/mapfrob.cpp delete mode 100644 src/tool/marriage-info.c create mode 100644 src/tool/marriage-info.cpp delete mode 100644 src/tool/moneycount/athena_text.h create mode 100644 src/tool/moneycount/athena_text.hpp delete mode 100644 src/tool/moneycount/mmo.h create mode 100644 src/tool/moneycount/mmo.hpp delete mode 100644 src/tool/skillfrob.c create mode 100644 src/tool/skillfrob.cpp delete mode 100644 src/webserver/generate.c create mode 100644 src/webserver/generate.cpp delete mode 100644 src/webserver/htmlstyle.c create mode 100644 src/webserver/htmlstyle.cpp delete mode 100644 src/webserver/logs.c create mode 100644 src/webserver/logs.cpp delete mode 100644 src/webserver/main.c create mode 100644 src/webserver/main.cpp delete mode 100644 src/webserver/pages/about.c create mode 100644 src/webserver/pages/about.cpp delete mode 100644 src/webserver/pages/notdone.c create mode 100644 src/webserver/pages/notdone.cpp delete mode 100644 src/webserver/pages/sample.c create mode 100644 src/webserver/pages/sample.cpp delete mode 100644 src/webserver/parse.c create mode 100644 src/webserver/parse.cpp diff --git a/.gitignore b/.gitignore index d59b2ef..361fe2d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,8 @@ /map-server /eathena-monitor # Generated source files -src/map/magic-interpreter-parser.h -src/map/magic-interpreter-parser.c -src/map/magic-interpreter-lexer.c - +/src/map/magic-interpreter-parser.hpp +/src/map/magic-interpreter-parser.cpp +/src/map/magic-interpreter-lexer.cpp +# generated header dependencies +/deps.make diff --git a/GNUmakefile b/GNUmakefile index 4162d7a..b00e55c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -11,29 +11,29 @@ include make.defs +mkdir -p $@ # The default recipe is suboptimal -%.c: %.l +%.cpp: %.lpp $(LEX) -o $@ $< -%.c %.h: %.y - $(BISON) -d -o $*.c $< +%.cpp %.h: %.ypp + $(BISON) -d -o $*.cpp $< # All this duplication is required because make handles pattern rules specially -${BUILD_DIR}/char/%.o: src/char/%.c | ${BUILD_DIR}/char/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/common/%.o: src/common/%.c | ${BUILD_DIR}/common/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/ladmin/%.o: src/ladmin/%.c | ${BUILD_DIR}/ladmin/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/login/%.o: src/login/%.c | ${BUILD_DIR}/login/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/map/%.o: src/map/%.c | ${BUILD_DIR}/map/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/tool/%.o: src/tool/%.c | ${BUILD_DIR}/tool/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/webserver/%.o: src/webserver/%.c | ${BUILD_DIR}/webserver/ - $(COMPILE.c) -o $@ $< -${BUILD_DIR}/webserver/pages/%.o: src/webserver/pages/%.c | ${BUILD_DIR}/webserver/pages/ - $(COMPILE.c) -o $@ $< +${BUILD_DIR}/char/%.o: src/char/%.cpp | ${BUILD_DIR}/char/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/common/%.o: src/common/%.cpp | ${BUILD_DIR}/common/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/ladmin/%.o: src/ladmin/%.cpp | ${BUILD_DIR}/ladmin/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/login/%.o: src/login/%.cpp | ${BUILD_DIR}/login/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/map/%.o: src/map/%.cpp | ${BUILD_DIR}/map/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/tool/%.o: src/tool/%.cpp | ${BUILD_DIR}/tool/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/webserver/%.o: src/webserver/%.cpp | ${BUILD_DIR}/webserver/ + $(COMPILE.cpp) -o $@ $< +${BUILD_DIR}/webserver/pages/%.o: src/webserver/pages/%.cpp | ${BUILD_DIR}/webserver/pages/ + $(COMPILE.cpp) -o $@ $< PROGS = login-server char-server map-server ladmin eathena-monitor webserver # Things to actually make @@ -60,7 +60,6 @@ webserver: ${BUILD_DIR}/webserver/main ${BUILD_DIR}/char/char: ${BUILD_DIR}/char/char.o ${BUILD_DIR}/char/inter.o ${BUILD_DIR}/char/int_party.o ${BUILD_DIR}/char/int_guild.o ${BUILD_DIR}/char/int_storage.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/mt_rand.o ${BUILD_DIR}/ladmin/ladmin: ${BUILD_DIR}/ladmin/ladmin.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/mt_rand.o ${BUILD_DIR}/login/login: ${BUILD_DIR}/login/login.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/mt_rand.o ${BUILD_DIR}/common/md5calc.o -${BUILD_DIR}/map/map: LDLIBS=-lm ${BUILD_DIR}/map/map: ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/tmw.o ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o ${BUILD_DIR}/map/magic-interpreter-base.o ${BUILD_DIR}/map/magic-expr.o ${BUILD_DIR}/map/magic-stmt.o ${BUILD_DIR}/map/magic.o ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/chrif.o ${BUILD_DIR}/map/clif.o ${BUILD_DIR}/map/pc.o ${BUILD_DIR}/map/npc.o ${BUILD_DIR}/map/chat.o ${BUILD_DIR}/map/path.o ${BUILD_DIR}/map/itemdb.o ${BUILD_DIR}/map/mob.o ${BUILD_DIR}/map/script.o ${BUILD_DIR}/map/storage.o ${BUILD_DIR}/map/skill.o ${BUILD_DIR}/map/skill-pools.o ${BUILD_DIR}/map/atcommand.o ${BUILD_DIR}/map/battle.o ${BUILD_DIR}/map/intif.o ${BUILD_DIR}/map/trade.o ${BUILD_DIR}/map/party.o ${BUILD_DIR}/map/guild.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/grfio.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/mt_rand.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/tool/eathena-monitor: ${BUILD_DIR}/tool/eathena-monitor.o ${BUILD_DIR}/tool/adduser: ${BUILD_DIR}/tool/adduser.o ${BUILD_DIR}/common/socket.o @@ -70,8 +69,8 @@ ${BUILD_DIR}/tool/marriage-info: ${BUILD_DIR}/tool/marriage-info.o ${BUILD_DIR}/ ${BUILD_DIR}/webserver/main: ${BUILD_DIR}/webserver/main.o ${BUILD_DIR}/webserver/parse.o ${BUILD_DIR}/webserver/generate.o ${BUILD_DIR}/webserver/htmlstyle.o ${BUILD_DIR}/webserver/logs.o ${BUILD_DIR}/webserver/pages/about.o ${BUILD_DIR}/webserver/pages/sample.o ${BUILD_DIR}/webserver/pages/notdone.o deps.make: - for F in `find src/ -name '*.c'`; do \ - gcc -m32 -std=c99 -MM "$$F" -MT "$$(sed 's/src/$${BUILD_DIR}/;s/\.c/.o/' <<< "$$F")"; \ + for F in `find src/ -name '*.cpp'`; do \ + ${CXX} ${CPPFLAGS} -MM "$$F" -MT "$$(sed 's/src/$${BUILD_DIR}/;s/\.cpp/.o/' <<< "$$F")"; \ done > deps.make include deps.make diff --git a/deps.make b/deps.make deleted file mode 100644 index 34c7328..0000000 --- a/deps.make +++ /dev/null @@ -1,308 +0,0 @@ -${BUILD_DIR}/webserver/main.o: src/webserver/main.c -${BUILD_DIR}/webserver/logs.o: src/webserver/logs.c -${BUILD_DIR}/webserver/generate.o: src/webserver/generate.c -${BUILD_DIR}/webserver/parse.o: src/webserver/parse.c -${BUILD_DIR}/webserver/pages/about.o: src/webserver/pages/about.c -${BUILD_DIR}/webserver/pages/notdone.o: src/webserver/pages/notdone.c -${BUILD_DIR}/webserver/pages/sample.o: src/webserver/pages/sample.c -${BUILD_DIR}/webserver/htmlstyle.o: src/webserver/htmlstyle.c -${BUILD_DIR}/common/mt_rand.o: src/common/mt_rand.c src/common/mt_rand.h \ - src/common/sanity.h -${BUILD_DIR}/common/timer.o: src/common/timer.c src/common/timer.h \ - src/common/sanity.h src/common/utils.h -${BUILD_DIR}/common/nullpo.o: src/common/nullpo.c src/common/nullpo.h \ - src/common/sanity.h -${BUILD_DIR}/common/lock.o: src/common/lock.c src/common/lock.h \ - src/common/socket.h src/common/sanity.h -${BUILD_DIR}/common/grfio.o: src/common/grfio.c src/common/utils.h \ - src/common/grfio.h src/common/mmo.h src/common/socket.h \ - src/common/sanity.h -${BUILD_DIR}/common/md5calc.o: src/common/md5calc.c src/common/md5calc.h \ - src/common/sanity.h src/common/mt_rand.h -${BUILD_DIR}/common/dbtest.o: src/common/dbtest.c src/common/db.h \ - src/common/sanity.h -${BUILD_DIR}/common/db.o: src/common/db.c src/common/db.h \ - src/common/sanity.h src/common/utils.h -${BUILD_DIR}/common/socket.o: src/common/socket.c src/common/mmo.h \ - src/common/utils.h src/common/socket.h src/common/sanity.h -${BUILD_DIR}/common/core.o: src/common/core.c src/common/core.h \ - src/common/socket.h src/common/sanity.h src/common/timer.h \ - src/common/version.h src/common/mt_rand.h src/common/nullpo.h -${BUILD_DIR}/login/login.o: src/login/login.c src/login/../common/core.h \ - src/login/../common/socket.h src/login/../common/sanity.h \ - src/login/../common/timer.h src/login/login.h src/login/../common/mmo.h \ - src/login/../common/utils.h src/login/../common/version.h \ - src/login/../common/db.h src/login/../common/lock.h \ - src/login/../common/mt_rand.h src/login/../common/md5calc.h -${BUILD_DIR}/char/int_guild.o: src/char/int_guild.c src/char/inter.h \ - src/char/int_guild.h src/char/int_storage.h src/char/../common/mmo.h \ - src/char/../common/utils.h src/char/char.h src/char/../common/socket.h \ - src/char/../common/sanity.h src/char/../common/db.h \ - src/char/../common/lock.h -${BUILD_DIR}/char/inter.o: src/char/inter.c src/char/../common/mmo.h \ - src/char/../common/utils.h src/char/char.h src/char/../common/socket.h \ - src/char/../common/sanity.h src/char/../common/timer.h \ - src/char/../common/db.h src/char/inter.h src/char/int_party.h \ - src/char/int_guild.h src/char/int_storage.h src/char/../common/lock.h -${BUILD_DIR}/char/int_party.o: src/char/int_party.c src/char/inter.h \ - src/char/int_party.h src/char/../common/mmo.h src/char/../common/utils.h \ - src/char/char.h src/char/../common/socket.h src/char/../common/sanity.h \ - src/char/../common/db.h src/char/../common/lock.h -${BUILD_DIR}/char/int_storage.o: src/char/int_storage.c \ - src/char/../common/mmo.h src/char/../common/utils.h \ - src/char/../common/socket.h src/char/../common/sanity.h \ - src/char/../common/db.h src/char/../common/lock.h src/char/char.h \ - src/char/inter.h src/char/int_storage.h src/char/int_guild.h -${BUILD_DIR}/char/char.o: src/char/char.c src/char/../common/core.h \ - src/char/../common/socket.h src/char/../common/sanity.h \ - src/char/../common/timer.h src/char/../common/mmo.h \ - src/char/../common/utils.h src/char/../common/version.h \ - src/char/../common/lock.h src/char/char.h src/char/inter.h \ - src/char/int_guild.h src/char/int_party.h src/char/int_storage.h -${BUILD_DIR}/tool/itemfrob.o: src/tool/itemfrob.c \ - src/tool/../common/mmo.h src/tool/../common/utils.h \ - src/tool/../char/char.c src/tool/../char/../common/core.h \ - src/tool/../char/../common/socket.h src/tool/../char/../common/sanity.h \ - src/tool/../char/../common/timer.h src/tool/../char/../common/mmo.h \ - src/tool/../char/../common/version.h src/tool/../char/../common/lock.h \ - src/tool/../char/char.h src/tool/../char/inter.h \ - src/tool/../char/int_guild.h src/tool/../char/int_party.h \ - src/tool/../char/int_storage.h -${BUILD_DIR}/tool/eathena-monitor.o: src/tool/eathena-monitor.c -${BUILD_DIR}/tool/adduser.o: src/tool/adduser.c -${BUILD_DIR}/tool/marriage-info.o: src/tool/marriage-info.c \ - src/tool/../login/login.h src/tool/../common/mmo.h \ - src/tool/../common/utils.h src/tool/../char/char.c \ - src/tool/../char/../common/core.h src/tool/../char/../common/socket.h \ - src/tool/../char/../common/sanity.h src/tool/../char/../common/timer.h \ - src/tool/../char/../common/mmo.h src/tool/../char/../common/version.h \ - src/tool/../char/../common/lock.h src/tool/../char/char.h \ - src/tool/../char/inter.h src/tool/../char/int_guild.h \ - src/tool/../char/int_party.h src/tool/../char/int_storage.h -${BUILD_DIR}/tool/skillfrob.o: src/tool/skillfrob.c \ - src/tool/../common/mmo.h src/tool/../common/utils.h \ - src/tool/../char/char.c src/tool/../char/../common/core.h \ - src/tool/../char/../common/socket.h src/tool/../char/../common/sanity.h \ - src/tool/../char/../common/timer.h src/tool/../char/../common/mmo.h \ - src/tool/../char/../common/version.h src/tool/../char/../common/lock.h \ - src/tool/../char/char.h src/tool/../char/inter.h \ - src/tool/../char/int_guild.h src/tool/../char/int_party.h \ - src/tool/../char/int_storage.h -${BUILD_DIR}/tool/convert.o: src/tool/convert.c -${BUILD_DIR}/tool/mapfrob.o: src/tool/mapfrob.c src/tool/../common/mmo.h \ - src/tool/../common/utils.h src/tool/../char/char.c \ - src/tool/../char/../common/core.h src/tool/../char/../common/socket.h \ - src/tool/../char/../common/sanity.h src/tool/../char/../common/timer.h \ - src/tool/../char/../common/mmo.h src/tool/../char/../common/version.h \ - src/tool/../char/../common/lock.h src/tool/../char/char.h \ - src/tool/../char/inter.h src/tool/../char/int_guild.h \ - src/tool/../char/int_party.h src/tool/../char/int_storage.h -${BUILD_DIR}/map/itemdb.o: src/map/itemdb.c src/map/../common/db.h \ - src/map/../common/sanity.h src/map/../common/grfio.h \ - src/map/../common/nullpo.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/timer.h src/map/battle.h \ - src/map/itemdb.h src/map/script.h src/map/pc.h \ - src/map/../common/socket.h src/map/../common/mt_rand.h -${BUILD_DIR}/map/magic.o: src/map/magic.c src/map/magic-interpreter.h \ - src/map/../common/nullpo.h src/map/../common/sanity.h src/map/battle.h \ - src/map/chat.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/timer.h \ - src/map/../common/db.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/intif.h src/map/itemdb.h src/map/magic.h src/map/mob.h \ - src/map/npc.h src/map/pc.h src/map/party.h src/map/script.h \ - src/map/skill.h src/map/trade.h src/map/../common/socket.h -${BUILD_DIR}/map/guild.o: src/map/guild.c src/map/guild.h \ - src/map/storage.h src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/db.h src/map/../common/sanity.h \ - src/map/../common/timer.h src/map/../common/socket.h \ - src/map/../common/nullpo.h src/map/battle.h src/map/npc.h src/map/pc.h \ - src/map/map.h src/map/mob.h src/map/intif.h src/map/clif.h src/map/tmw.h -${BUILD_DIR}/map/mob.o: src/map/mob.c src/map/../common/timer.h \ - src/map/../common/sanity.h src/map/../common/socket.h \ - src/map/../common/db.h src/map/../common/nullpo.h \ - src/map/../common/mt_rand.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/clif.h src/map/storage.h \ - src/map/intif.h src/map/pc.h src/map/mob.h src/map/guild.h \ - src/map/itemdb.h src/map/skill.h src/map/magic.h src/map/battle.h \ - src/map/party.h src/map/npc.h -${BUILD_DIR}/map/storage.o: src/map/storage.c src/map/../common/db.h \ - src/map/../common/sanity.h src/map/../common/nullpo.h src/map/storage.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/chrif.h \ - src/map/itemdb.h src/map/map.h src/map/../common/timer.h src/map/clif.h \ - src/map/intif.h src/map/pc.h src/map/guild.h src/map/battle.h \ - src/map/atcommand.h -${BUILD_DIR}/map/path.o: src/map/path.c src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/db.h src/map/battle.h src/map/../common/nullpo.h -${BUILD_DIR}/map/magic-interpreter-parser.o: \ - src/map/magic-interpreter-parser.c src/map/magic-interpreter.h \ - src/map/../common/nullpo.h src/map/../common/sanity.h src/map/battle.h \ - src/map/chat.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/timer.h \ - src/map/../common/db.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/intif.h src/map/itemdb.h src/map/magic.h src/map/mob.h \ - src/map/npc.h src/map/pc.h src/map/party.h src/map/script.h \ - src/map/skill.h src/map/trade.h src/map/../common/socket.h \ - src/map/magic-expr.h src/map/magic-interpreter-aux.h -${BUILD_DIR}/map/skill.o: src/map/skill.c src/map/../common/timer.h \ - src/map/../common/sanity.h src/map/../common/nullpo.h \ - src/map/../common/mt_rand.h src/map/magic.h src/map/clif.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/../common/db.h \ - src/map/storage.h src/map/intif.h src/map/battle.h src/map/itemdb.h \ - src/map/mob.h src/map/party.h src/map/pc.h src/map/script.h \ - src/map/skill.h src/map/../common/socket.h -${BUILD_DIR}/map/magic-interpreter-lexer.o: \ - src/map/magic-interpreter-lexer.c src/map/magic-interpreter.h \ - src/map/../common/nullpo.h src/map/../common/sanity.h src/map/battle.h \ - src/map/chat.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/timer.h \ - src/map/../common/db.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/intif.h src/map/itemdb.h src/map/magic.h src/map/mob.h \ - src/map/npc.h src/map/pc.h src/map/party.h src/map/script.h \ - src/map/skill.h src/map/trade.h src/map/../common/socket.h \ - src/map/magic-interpreter-parser.h -${BUILD_DIR}/map/magic-stmt.o: src/map/magic-stmt.c \ - src/map/magic-interpreter.h src/map/../common/nullpo.h \ - src/map/../common/sanity.h src/map/battle.h src/map/chat.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/db.h src/map/chrif.h \ - src/map/clif.h src/map/storage.h src/map/intif.h src/map/itemdb.h \ - src/map/magic.h src/map/mob.h src/map/npc.h src/map/pc.h src/map/party.h \ - src/map/script.h src/map/skill.h src/map/trade.h \ - src/map/../common/socket.h src/map/magic-expr.h \ - src/map/magic-interpreter-aux.h src/map/magic-expr-eval.h -${BUILD_DIR}/map/chat.o: src/map/chat.c src/map/../common/db.h \ - src/map/../common/sanity.h src/map/../common/nullpo.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/clif.h src/map/storage.h src/map/pc.h \ - src/map/chat.h src/map/npc.h -${BUILD_DIR}/map/skill-pools.o: src/map/skill-pools.c \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/nullpo.h src/map/../common/mt_rand.h src/map/magic.h \ - src/map/clif.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/db.h src/map/storage.h \ - src/map/intif.h src/map/battle.h src/map/itemdb.h src/map/mob.h \ - src/map/party.h src/map/pc.h src/map/script.h src/map/skill.h \ - src/map/../common/socket.h -${BUILD_DIR}/map/trade.o: src/map/trade.c src/map/clif.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/db.h src/map/storage.h src/map/itemdb.h \ - src/map/trade.h src/map/pc.h src/map/npc.h src/map/battle.h \ - src/map/../common/nullpo.h -${BUILD_DIR}/map/party.o: src/map/party.c src/map/party.h \ - src/map/../common/db.h src/map/../common/sanity.h \ - src/map/../common/timer.h src/map/../common/socket.h \ - src/map/../common/nullpo.h src/map/pc.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/battle.h \ - src/map/intif.h src/map/clif.h src/map/storage.h src/map/skill.h \ - src/map/magic.h src/map/tmw.h -${BUILD_DIR}/map/npc.o: src/map/npc.c src/map/../common/nullpo.h \ - src/map/../common/sanity.h src/map/../common/timer.h src/map/battle.h \ - src/map/clif.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/db.h src/map/storage.h \ - src/map/intif.h src/map/itemdb.h src/map/mob.h src/map/npc.h \ - src/map/pc.h src/map/script.h src/map/skill.h src/map/magic.h \ - src/map/../common/socket.h -${BUILD_DIR}/map/magic-interpreter-base.o: \ - src/map/magic-interpreter-base.c src/map/magic.h src/map/clif.h \ - src/map/map.h src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/db.h src/map/storage.h src/map/intif.h \ - src/map/magic-interpreter.h src/map/../common/nullpo.h src/map/battle.h \ - src/map/chat.h src/map/chrif.h src/map/itemdb.h src/map/mob.h \ - src/map/npc.h src/map/pc.h src/map/party.h src/map/script.h \ - src/map/skill.h src/map/trade.h src/map/../common/socket.h \ - src/map/magic-expr.h src/map/magic-interpreter-aux.h -${BUILD_DIR}/map/pc.o: src/map/pc.c src/map/../common/socket.h \ - src/map/../common/sanity.h src/map/../common/timer.h \ - src/map/../common/db.h src/map/../common/nullpo.h \ - src/map/../common/mt_rand.h src/map/atcommand.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/battle.h \ - src/map/chat.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/guild.h src/map/intif.h src/map/itemdb.h src/map/mob.h \ - src/map/npc.h src/map/party.h src/map/pc.h src/map/script.h \ - src/map/skill.h src/map/magic.h src/map/trade.h -${BUILD_DIR}/map/tmw.o: src/map/tmw.c src/map/tmw.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/db.h src/map/../common/socket.h \ - src/map/../common/version.h src/map/../common/nullpo.h \ - src/map/atcommand.h src/map/battle.h src/map/chat.h src/map/chrif.h \ - src/map/clif.h src/map/storage.h src/map/guild.h src/map/intif.h \ - src/map/itemdb.h src/map/magic.h src/map/mob.h src/map/npc.h \ - src/map/party.h src/map/pc.h src/map/script.h src/map/skill.h \ - src/map/trade.h -${BUILD_DIR}/map/intif.o: src/map/intif.c src/map/../common/nullpo.h \ - src/map/../common/sanity.h src/map/../common/socket.h \ - src/map/../common/timer.h src/map/battle.h src/map/chrif.h \ - src/map/clif.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/db.h src/map/storage.h \ - src/map/guild.h src/map/intif.h src/map/party.h src/map/pc.h -${BUILD_DIR}/map/magic-expr.o: src/map/magic-expr.c src/map/magic-expr.h \ - src/map/magic-interpreter.h src/map/../common/nullpo.h \ - src/map/../common/sanity.h src/map/battle.h src/map/chat.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/timer.h src/map/../common/db.h src/map/chrif.h \ - src/map/clif.h src/map/storage.h src/map/intif.h src/map/itemdb.h \ - src/map/magic.h src/map/mob.h src/map/npc.h src/map/pc.h src/map/party.h \ - src/map/script.h src/map/skill.h src/map/trade.h \ - src/map/../common/socket.h src/map/magic-interpreter-aux.h \ - src/map/magic-expr-eval.h src/map/../common/mt_rand.h -${BUILD_DIR}/map/battle.o: src/map/battle.c src/map/battle.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/nullpo.h src/map/clif.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/../common/db.h \ - src/map/storage.h src/map/guild.h src/map/itemdb.h src/map/mob.h \ - src/map/pc.h src/map/skill.h src/map/magic.h src/map/intif.h \ - src/map/../common/socket.h src/map/../common/mt_rand.h -${BUILD_DIR}/map/script.o: src/map/script.c src/map/../common/socket.h \ - src/map/../common/sanity.h src/map/../common/timer.h \ - src/map/../common/lock.h src/map/../common/mt_rand.h src/map/atcommand.h \ - src/map/map.h src/map/../common/mmo.h src/map/../common/utils.h \ - src/map/../common/db.h src/map/battle.h src/map/chat.h src/map/chrif.h \ - src/map/clif.h src/map/storage.h src/map/guild.h src/map/intif.h \ - src/map/itemdb.h src/map/mob.h src/map/npc.h src/map/party.h \ - src/map/pc.h src/map/script.h src/map/skill.h src/map/magic.h -${BUILD_DIR}/map/clif.o: src/map/clif.c src/map/../common/socket.h \ - src/map/../common/sanity.h src/map/../common/timer.h \ - src/map/../common/version.h src/map/../common/nullpo.h \ - src/map/../common/md5calc.h src/map/../common/mt_rand.h \ - src/map/atcommand.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/../common/db.h src/map/battle.h \ - src/map/chat.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/guild.h src/map/intif.h src/map/itemdb.h src/map/magic.h \ - src/map/mob.h src/map/npc.h src/map/party.h src/map/pc.h \ - src/map/script.h src/map/skill.h src/map/tmw.h src/map/trade.h -${BUILD_DIR}/map/chrif.o: src/map/chrif.c src/map/../common/socket.h \ - src/map/../common/sanity.h src/map/../common/timer.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/../common/db.h \ - src/map/battle.h src/map/chrif.h src/map/clif.h src/map/storage.h \ - src/map/intif.h src/map/npc.h src/map/pc.h src/map/../common/nullpo.h \ - src/map/itemdb.h -${BUILD_DIR}/map/atcommand.o: src/map/atcommand.c \ - src/map/../common/socket.h src/map/../common/sanity.h \ - src/map/../common/timer.h src/map/../common/nullpo.h \ - src/map/../common/mt_rand.h src/map/atcommand.h src/map/map.h \ - src/map/../common/mmo.h src/map/../common/utils.h src/map/../common/db.h \ - src/map/battle.h src/map/clif.h src/map/storage.h src/map/chrif.h \ - src/map/guild.h src/map/intif.h src/map/itemdb.h src/map/mob.h \ - src/map/npc.h src/map/pc.h src/map/party.h src/map/script.h \ - src/map/skill.h src/map/magic.h src/map/trade.h src/map/../common/core.h \ - src/map/tmw.h -${BUILD_DIR}/map/map.o: src/map/map.c src/map/../common/core.h \ - src/map/../common/timer.h src/map/../common/sanity.h \ - src/map/../common/db.h src/map/../common/grfio.h \ - src/map/../common/mt_rand.h src/map/map.h src/map/../common/mmo.h \ - src/map/../common/utils.h src/map/chrif.h src/map/clif.h \ - src/map/storage.h src/map/intif.h src/map/npc.h src/map/pc.h \ - src/map/mob.h src/map/chat.h src/map/itemdb.h src/map/skill.h \ - src/map/magic.h src/map/trade.h src/map/party.h src/map/battle.h \ - src/map/script.h src/map/guild.h src/map/atcommand.h \ - src/map/../common/nullpo.h src/map/../common/socket.h -${BUILD_DIR}/ladmin/ladmin.o: src/ladmin/ladmin.c \ - src/ladmin/../common/core.h src/ladmin/../common/socket.h \ - src/ladmin/../common/sanity.h src/ladmin/ladmin.h \ - src/ladmin/../common/version.h src/ladmin/../common/mmo.h \ - src/ladmin/../common/utils.h src/ladmin/../common/md5calc.h diff --git a/make.defs b/make.defs index 067c22b..9328688 100644 --- a/make.defs +++ b/make.defs @@ -1,16 +1,13 @@ # defaults -CC = gcc +CXX = g++ LEX = flex BISON = bison -CFLAGS = -pipe -g -O2 +CXXFLAGS = -pipe -g -O2 # works on both x86 and x86_64 -override CC += -m32 -std=gnu99 +override CXX += -m32 -std=c++0x +# for linking +override CC = ${CXX} -# TODO check if this is actually needed - I don't think it should be -ifeq ($(findstring CYGWIN,$(shell uname)), CYGWIN) - override CFLAGS += -DFD_SETSIZE=4096 -DCYGWIN -else - override CFLAGS += -fstack-protector -endif +override CXXFLAGS += -fstack-protector diff --git a/src/char/char.c b/src/char/char.c deleted file mode 100644 index 2a46283..0000000 --- a/src/char/char.c +++ /dev/null @@ -1,4076 +0,0 @@ -// $Id: char.c,v 1.3 2004/09/13 16:52:16 Yor Exp $ -// original : char2.c 2003/03/14 11:58:35 Rev.1.5 - -#include <sys/types.h> -#include <sys/socket.h> -#include <stdio.h> -#include <stdlib.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> -#include <sys/ioctl.h> -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <string.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <stdarg.h> -#include <sys/wait.h> - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/mmo.h" -#include "../common/version.h" -#include "../common/lock.h" -#include "char.h" - -#include "inter.h" -#include "int_guild.h" -#include "int_party.h" -#include "int_storage.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -struct mmo_map_server server[MAX_MAP_SERVERS]; -int server_fd[MAX_MAP_SERVERS]; -int server_freezeflag[MAX_MAP_SERVERS]; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed -int anti_freeze_enable = 0; -int ANTI_FREEZE_INTERVAL = 6; - -int login_fd, char_fd; -char userid[24]; -char passwd[24]; -char server_name[20]; -char wisp_server_name[24] = "Server"; -char login_ip_str[16]; -int login_ip; -int login_port = 6900; -char char_ip_str[16]; -int char_ip; -int char_port = 6121; -int char_maintenance; -int char_new; -int email_creation = 0; // disabled by default -char char_txt[1024]; -char backup_txt[1024]; //By zanetheinsane -char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] -char unknown_char_name[1024] = "Unknown"; -char char_log_filename[1024] = "log/char.log"; -//Added for lan support -char lan_map_ip[128]; -int subneti[4]; -int subnetmaski[4]; -int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] -int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] -char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] - -struct char_session_data -{ - int account_id, login_id1, login_id2, sex; - unsigned short packet_tmw_version; - int found_char[9]; - char email[40]; // e-mail (default: a@a.com) by [Yor] - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -}; - -#define AUTH_FIFO_SIZE 256 -struct -{ - int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag, - sex; - unsigned short packet_tmw_version; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -} auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - -int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) - -int char_id_count = 150000; -struct mmo_charstatus *char_dat; -int char_num, char_max; -int max_connect_user = 0; -int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int start_zeny = 500; -int start_weapon = 1201; -int start_armor = 1202; - -// Initial position (it's possible to set it in conf file) -struct point start_point = { "new_1-1.gat", 53, 111 }; - -struct gm_account *gm_account = NULL; -int GM_num = 0; - -// online players by [Yor] -char online_txt_filename[1024] = "online.txt"; -char online_html_filename[1024] = "online.html"; -int online_sorting_option = 0; // sorting option to display online players in online files -int online_display_option = 1; // display options: to know which columns must be displayed -int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer -int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it - -int *online_chars; // same size of char_dat, and id value of current server (or -1) -time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds) - -pid_t pid = 0; // For forked DB writes - -//------------------------------ -// Writing function of logs file -//------------------------------ -int char_log (char *fmt, ...) -{ - FILE *logfp; - va_list ap; - struct timeval tv; - char tmpstr[2048]; - - va_start (ap, fmt); - - logfp = fopen_ (char_log_filename, "a"); - if (logfp) - { - if (fmt[0] == '\0') // jump a line if no message - fprintf (logfp, "\n"); - else - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 24, "%d-%m-%Y %H:%M:%S", gmtime (&(tv.tv_sec))); - sprintf (tmpstr + 19, ".%03d: %s", (int) tv.tv_usec / 1000, fmt); - vfprintf (logfp, tmpstr, ap); - } - fclose_ (logfp); - } - - va_end (ap); - return 0; -} - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars (unsigned char *str) -{ - int i; - int change = 0; - - for (i = 0; str[i]; i++) - { - if (str[i] < 32) - { - str[i] = '_'; - change = 1; - } - } - - return change; -} - -//---------------------------------------------------------------------- -// Determine if an account (id) is a GM account -// and returns its level (or 0 if it isn't a GM account or if not found) -//---------------------------------------------------------------------- -int isGM (int account_id) -{ - int i; - - for (i = 0; i < GM_num; i++) - if (gm_account[i].account_id == account_id) - return gm_account[i].level; - return 0; -} - -//---------------------------------------------- -// Search an character id -// (return character index or -1 (if not found)) -// If exact character name is not found, -// the function checks without case sensitive -// and returns index if only 1 character is found -// and similar to the searched name. -//---------------------------------------------- -int search_character_index (char *character_name) -{ - int i, quantity, index; - - quantity = 0; - index = -1; - for (i = 0; i < char_num; i++) - { - // Without case sensitive check (increase the number of similar character names found) - if (strcasecmp (char_dat[i].name, character_name) == 0) - { - // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp (char_dat[i].name, character_name) == 0) - return i; - quantity++; - index = i; - } - } - // 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 index; - - // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found - return -1; -} - -//------------------------------------- -// Return character name with the index -//------------------------------------- -char *search_character_name (int index) -{ - - if (index >= 0 && index < char_num) - return char_dat[index].name; - - return unknown_char_name; -} - -//------------------------------------------------- -// Function to create the character line (for save) -//------------------------------------------------- -int mmo_char_tostr (char *str, struct mmo_charstatus *p) -{ - int i; - char *str_p = str; - - // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. - if (p->last_point.map[0] == '\0') - { - memcpy (p->last_point.map, "prontera.gat", 16); - p->last_point.x = 273; - p->last_point.y = 354; - } - - str_p += sprintf (str_p, "%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%s,%d,%d\t%s,%d,%d,%d\t", p->char_id, p->account_id, p->char_num, p->name, // - p->pc_class, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, p->hp, p->max_hp, p->sp, p->max_sp, p->str, p->agi, p->vit, p->int_, p->dex, p->luk, p->status_point, p->skill_point, p->option, p->karma, p->manner, // - p->party_id, p->guild_id, 0, p->hair, p->hair_color, p->clothes_color, p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, p->last_point.map, p->last_point.x, p->last_point.y, // - p->save_point.map, p->save_point.x, p->save_point.y, - p->partner_id); - for (i = 0; i < 10; i++) - if (p->memo_point[i].map[0]) - { - str_p += - sprintf (str_p, "%s,%d,%d", p->memo_point[i].map, - p->memo_point[i].x, p->memo_point[i].y); - } - *(str_p++) = '\t'; - - for (i = 0; i < MAX_INVENTORY; i++) - if (p->inventory[i].nameid) - { - str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->inventory[i].id, p->inventory[i].nameid, - p->inventory[i].amount, p->inventory[i].equip, - p->inventory[i].identify, - p->inventory[i].refine, - p->inventory[i].attribute, - p->inventory[i].card[0], - p->inventory[i].card[1], - p->inventory[i].card[2], - p->inventory[i].card[3], - p->inventory[i].broken); - } - *(str_p++) = '\t'; - - for (i = 0; i < MAX_CART; i++) - if (p->cart[i].nameid) - { - str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->cart[i].id, p->cart[i].nameid, - p->cart[i].amount, p->cart[i].equip, - p->cart[i].identify, p->cart[i].refine, - p->cart[i].attribute, p->cart[i].card[0], - p->cart[i].card[1], p->cart[i].card[2], - p->cart[i].card[3], p->cart[i].broken); - } - *(str_p++) = '\t'; - - for (i = 0; i < MAX_SKILL; i++) - if (p->skill[i].id) - { - str_p += - sprintf (str_p, "%d,%d ", p->skill[i].id, - p->skill[i].lv | (p->skill[i].flags << 16)); - } - *(str_p++) = '\t'; - - for (i = 0; i < p->global_reg_num; i++) - if (p->global_reg[i].str[0]) - str_p += - sprintf (str_p, "%s,%d ", p->global_reg[i].str, - p->global_reg[i].value); - *(str_p++) = '\t'; - - *str_p = '\0'; - return 0; -} - -//------------------------------------------------------------------------- -// Function to set the character from the line (at read of characters file) -//------------------------------------------------------------------------- -int mmo_char_fromstr (char *str, struct mmo_charstatus *p) -{ - int tmp_int[256]; - int set, next, len, i; - - // initilialise character - memset (p, '\0', sizeof (struct mmo_charstatus)); - - // If it's not char structure of version 1008 and after - if ((set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // - &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // - p->save_point.map, &tmp_int[37], &tmp_int[38], - &tmp_int[39], &next)) != 43) - { - tmp_int[39] = 0; // partner id - // If not char structure from version 384 to 1007 - if ((set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // - &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // - p->save_point.map, &tmp_int[37], &tmp_int[38], - &next)) != 42) - { - // It's char structure of a version before 384 - tmp_int[26] = 0; // pet id - set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // - &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], // - &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // - p->save_point.map, &tmp_int[37], &tmp_int[38], - &next); - set += 2; - //printf("char: old char data ver.1\n"); - // Char structure of version 1007 or older - } - else - { - set++; - //printf("char: old char data ver.2\n"); - } - // Char structure of version 1008+ - } - else - { - //printf("char: new char data ver.3\n"); - } - if (set != 43) - return 0; - - p->char_id = tmp_int[0]; - p->account_id = tmp_int[1]; - p->char_num = tmp_int[2]; - p->pc_class = tmp_int[3]; - p->base_level = tmp_int[4]; - p->job_level = tmp_int[5]; - p->base_exp = tmp_int[6]; - p->job_exp = tmp_int[7]; - p->zeny = tmp_int[8]; - p->hp = tmp_int[9]; - p->max_hp = tmp_int[10]; - p->sp = tmp_int[11]; - p->max_sp = tmp_int[12]; - p->str = tmp_int[13]; - p->agi = tmp_int[14]; - p->vit = tmp_int[15]; - p->int_ = tmp_int[16]; - p->dex = tmp_int[17]; - p->luk = tmp_int[18]; - p->status_point = tmp_int[19]; - p->skill_point = tmp_int[20]; - p->option = tmp_int[21]; - p->karma = tmp_int[22]; - p->manner = tmp_int[23]; - p->party_id = tmp_int[24]; - p->guild_id = tmp_int[25]; -// p->pet_id = tmp_int[26]; - p->hair = tmp_int[27]; - p->hair_color = tmp_int[28]; - p->clothes_color = tmp_int[29]; - p->weapon = tmp_int[30]; - p->shield = tmp_int[31]; - p->head_top = tmp_int[32]; - p->head_mid = tmp_int[33]; - p->head_bottom = tmp_int[34]; - p->last_point.x = tmp_int[35]; - p->last_point.y = tmp_int[36]; - p->save_point.x = tmp_int[37]; - p->save_point.y = tmp_int[38]; - p->partner_id = tmp_int[39]; - - // Some checks - for (i = 0; i < char_num; i++) - { - if (char_dat[i].char_id == p->char_id) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: a character has an identical id to another.\n"); - printf - (" character id #%d -> new character not readed.\n", - p->char_id); - printf (" Character saved in log file.\033[0m\n"); - return -1; - } - else if (strcmp (char_dat[i].name, p->name) == 0) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: character name already exists.\n"); - printf - (" character name '%s' -> new character not readed.\n", - p->name); - printf (" Character saved in log file.\033[0m\n"); - return -2; - } - } - - if (strcasecmp (wisp_server_name, p->name) == 0) - { - printf - ("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); - printf - (" Character name '%s' = wisp server name '%s'.\n", - p->name, wisp_server_name); - printf - (" Character readed. Suggestion: change the wisp server name.\n"); - char_log - ("mmo_auth_init: ******WARNING: character name has wisp server name: Character name '%s' = wisp server name '%s'.\n", - p->name, wisp_server_name); - } - - if (str[next] == '\n' || str[next] == '\r') - return 1; // 新規データ - - next++; - - for (i = 0; str[next] && str[next] != '\t'; i++) - { - if (sscanf - (str + next, "%[^,],%d,%d%n", p->memo_point[i].map, &tmp_int[0], - &tmp_int[1], &len) != 3) - return -3; - p->memo_point[i].x = tmp_int[0]; - p->memo_point[i].y = tmp_int[1]; - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for (i = 0; str[next] && str[next] != '\t'; i++) - { - if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &tmp_int[11], &len) == 12) - { - // do nothing, it's ok - } - else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len) == 11) - { - tmp_int[11] = 0; // broken doesn't exist in this version -> 0 - } - else // invalid structure - return -4; - p->inventory[i].id = tmp_int[0]; - p->inventory[i].nameid = tmp_int[1]; - p->inventory[i].amount = tmp_int[2]; - p->inventory[i].equip = tmp_int[3]; - p->inventory[i].identify = tmp_int[4]; - p->inventory[i].refine = tmp_int[5]; - p->inventory[i].attribute = tmp_int[6]; - p->inventory[i].card[0] = tmp_int[7]; - p->inventory[i].card[1] = tmp_int[8]; - p->inventory[i].card[2] = tmp_int[9]; - p->inventory[i].card[3] = tmp_int[10]; - p->inventory[i].broken = tmp_int[11]; - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for (i = 0; str[next] && str[next] != '\t'; i++) - { - if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &tmp_int[11], &len) == 12) - { - // do nothing, it's ok - } - else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len) == 11) - { - tmp_int[11] = 0; // broken doesn't exist in this version -> 0 - } - else // invalid structure - return -5; - p->cart[i].id = tmp_int[0]; - p->cart[i].nameid = tmp_int[1]; - p->cart[i].amount = tmp_int[2]; - p->cart[i].equip = tmp_int[3]; - p->cart[i].identify = tmp_int[4]; - p->cart[i].refine = tmp_int[5]; - p->cart[i].attribute = tmp_int[6]; - p->cart[i].card[0] = tmp_int[7]; - p->cart[i].card[1] = tmp_int[8]; - p->cart[i].card[2] = tmp_int[9]; - p->cart[i].card[3] = tmp_int[10]; - p->cart[i].broken = tmp_int[11]; - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for (i = 0; str[next] && str[next] != '\t'; i++) - { - if (sscanf (str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != - 2) - return -6; - p->skill[tmp_int[0]].id = tmp_int[0]; - p->skill[tmp_int[0]].lv = tmp_int[1] & 0xffff; - p->skill[tmp_int[0]].flags = ((tmp_int[1] >> 16) & 0xffff); - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for (i = 0; - str[next] && str[next] != '\t' && str[next] != '\n' - && str[next] != '\r'; i++) - { // global_reg実装以前のathena.txt互換のため一応'\n'チェック - if (sscanf - (str + next, "%[^,],%d%n", p->global_reg[i].str, - &p->global_reg[i].value, &len) != 2) - { - // because some scripts are not correct, the str can be "". So, we must check that. - // If it's, we must not refuse the character, but just this REG value. - // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) - if (str[next] == ',' - && sscanf (str + next, ",%d%n", &p->global_reg[i].value, - &len) == 1) - i--; - else - return -7; - } - next += len; - if (str[next] == ' ') - next++; - } - p->global_reg_num = i; - - return 1; -} - -//--------------------------------- -// Function to read characters file -//--------------------------------- -int mmo_char_init (void) -{ - char line[65536]; - int i; - int ret, line_count; - FILE *fp; - - char_max = 256; - CREATE (char_dat, struct mmo_charstatus, 256); - CREATE (online_chars, int, 256); - for (i = 0; i < char_max; i++) - online_chars[i] = -1; - - char_num = 0; - - fp = fopen_ (char_txt, "r"); - if (fp == NULL) - { - printf ("Characters file not found: %s.\n", char_txt); - char_log ("Characters file not found: %s.\n", char_txt); - char_log ("Id for the next created character: %d.\n", - char_id_count); - return 0; - } - - line_count = 0; - while (fgets (line, sizeof (line) - 1, fp)) - { - int i, j; - line_count++; - - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof (line) - 1] = '\0'; - - j = 0; - if (sscanf (line, "%d\t%%newid%%%n", &i, &j) == 1 && j > 0) - { - if (char_id_count < i) - char_id_count = i; - continue; - } - - if (char_num >= char_max) - { - char_max += 256; - RECREATE (char_dat, struct mmo_charstatus, char_max); - RECREATE (online_chars, int, char_max); - for (i = char_max - 256; i < char_max; i++) - online_chars[i] = -1; - } - - ret = mmo_char_fromstr (line, &char_dat[char_num]); - if (ret > 0) - { // negative value or zero for errors - if (char_dat[char_num].char_id >= char_id_count) - char_id_count = char_dat[char_num].char_id + 1; - char_num++; - } - else - { - printf - ("mmo_char_init: in characters file, unable to read the line #%d.\n", - line_count); - printf (" -> Character saved in log file.\n"); - switch (ret) - { - case -1: - char_log - ("Duplicate character id in the next character line (character not readed):\n"); - break; - case -2: - char_log - ("Duplicate character name in the next character line (character not readed):\n"); - break; - case -3: - char_log - ("Invalid memo point structure in the next character line (character not readed):\n"); - break; - case -4: - char_log - ("Invalid inventory item structure in the next character line (character not readed):\n"); - break; - case -5: - char_log - ("Invalid cart item structure in the next character line (character not readed):\n"); - break; - case -6: - char_log - ("Invalid skill structure in the next character line (character not readed):\n"); - break; - case -7: - char_log - ("Invalid register structure in the next character line (character not readed):\n"); - break; - default: // 0 - char_log - ("Unabled to get a character in the next line - Basic structure of line (before inventory) is incorrect (character not readed):\n"); - break; - } - char_log ("%s", line); - } - } - fclose_ (fp); - - if (char_num == 0) - { - printf ("mmo_char_init: No character found in %s.\n", char_txt); - char_log ("mmo_char_init: No character found in %s.\n", - char_txt); - } - else if (char_num == 1) - { - printf ("mmo_char_init: 1 character read in %s.\n", char_txt); - char_log ("mmo_char_init: 1 character read in %s.\n", char_txt); - } - else - { - printf ("mmo_char_init: %d characters read in %s.\n", char_num, - char_txt); - char_log ("mmo_char_init: %d characters read in %s.\n", - char_num, char_txt); - } - - char_log ("Id for the next created character: %d.\n", - char_id_count); - - return 0; -} - -//--------------------------------------------------------- -// Function to save characters in files (speed up by [Yor]) -//--------------------------------------------------------- -void mmo_char_sync (void) -{ - char line[65536]; - int i, j, k; - int lock; - FILE *fp; - int id[char_num]; - - // Sorting before save (by [Yor]) - for (i = 0; i < char_num; i++) - { - id[i] = i; - for (j = 0; j < i; j++) - { - if ((char_dat[i].account_id < char_dat[id[j]].account_id) || - // if same account id, we sort by slot. - (char_dat[i].account_id == char_dat[id[j]].account_id && - char_dat[i].char_num < char_dat[id[j]].char_num)) - { - for (k = i; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[i] - break; - } - } - } - - // Data save - fp = lock_fopen (char_txt, &lock); - if (fp == NULL) - { - printf ("WARNING: Server can't not save characters.\n"); - char_log ("WARNING: Server can't not save characters.\n"); - } - else - { - for (i = 0; i < char_num; i++) - { - // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) - mmo_char_tostr (line, &char_dat[id[i]]); // use of sorted index - fprintf (fp, "%s\n", line); - } - fprintf (fp, "%d\t%%newid%%\n", char_id_count); - lock_fclose (fp, char_txt, &lock); - } - - // Data save (backup) - if (backup_txt_flag) - { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] - fp = lock_fopen (backup_txt, &lock); - if (fp == NULL) - { - printf - ("WARNING: Server can't not create backup of characters file.\n"); - char_log - ("WARNING: Server can't not create backup of characters file.\n"); - return; - } - for (i = 0; i < char_num; i++) - { - // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) - mmo_char_tostr (line, &char_dat[id[i]]); // use of sorted index - fprintf (fp, "%s\n", line); - } - fprintf (fp, "%d\t%%newid%%\n", char_id_count); - lock_fclose (fp, backup_txt, &lock); - } - - return; -} - -//---------------------------------------------------- -// Function to save (in a periodic way) datas in files -//---------------------------------------------------- -void mmo_char_sync_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - if (pid != 0) - { - int status; - pid_t temp = waitpid (pid, &status, WNOHANG); - - // Need to check status too? - if (temp == 0) - { - return; - } - } - - // This can take a lot of time. Fork a child to handle the work and return at once - // If we're unable to fork just continue running the function normally - if ((pid = fork ()) > 0) - return; - - mmo_char_sync (); - inter_save (); - - // If we're a child we should suicide now. - if (pid == 0) - _exit (0); -} - -//---------------------------------------------------- -// Remove trailing whitespace from a name -//---------------------------------------------------- -static void remove_trailing_blanks (char *name) -{ - char *tail = name + strlen (name) - 1; - - while (tail > name && *tail == ' ') - *tail-- = 0; -} - -//---------------------------------------------------- -// Remove prefix whitespace from a name -//---------------------------------------------------- -static void remove_prefix_blanks (char *name) -{ - char *dst = name; - char *src = name; - - while (*src == ' ') // find first nonblank - ++src; - while ((*dst++ = *src++)); // `strmove' -} - -//----------------------------------- -// Function to create a new character -//----------------------------------- -int make_new_char (int fd, unsigned char *dat) -{ - int i, j; - struct char_session_data *sd = (struct char_session_data *)session[fd]->session_data; - - // remove control characters from the name - dat[23] = '\0'; - if (remove_control_chars (dat)) - { - char_log - ("Make new char error (control char received in the name): (connection #%d, account: %d).\n", - fd, sd->account_id); - return -1; - } - - // Eliminate whitespace - remove_trailing_blanks ((char *) dat); - remove_prefix_blanks ((char *) dat); - - // check lenght of character name - if (strlen (dat) < 4) - { - char_log - ("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", - fd, sd->account_id, dat); - return -1; - } - - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) - { // only letters/symbols in char_name_letters are authorised - for (i = 0; dat[i]; i++) - if (strchr (char_name_letters, dat[i]) == NULL) - { - char_log - ("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, dat, dat[i]); - return -1; - } - } - else if (char_name_option == 2) - { // letters/symbols in char_name_letters are forbidden - for (i = 0; dat[i]; i++) - if (strchr (char_name_letters, dat[i]) != NULL) - { - char_log - ("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, dat, dat[i]); - return -1; - } - } // else, all letters/symbols are authorised (except control char removed before) - - if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5 * 6 || // stats - dat[30] >= 9 || // slots (dat[30] can not be negativ) - dat[33] < 0 || dat[33] >= 20 || // hair style - dat[31] >= 12) - { // hair color (dat[31] can not be negativ) - char_log - ("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], - dat[26], dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], - dat[33], dat[31]); - return -1; - } - - // check individual stat value - for (i = 24; i <= 29; i++) - { - if (dat[i] < 1 || dat[i] > 9) - { - char_log - ("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], - dat[26], dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], - dat[33], dat[31]); - return -1; - } - } - - for (i = 0; i < char_num; i++) - { - if ((name_ignoring_case != 0 && strcmp (char_dat[i].name, dat) == 0) - || (name_ignoring_case == 0 - && strcasecmp (char_dat[i].name, dat) == 0)) - { - char_log - ("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], dat, char_dat[i].name, - dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], - dat[33], dat[31]); - return -1; - } - if (char_dat[i].account_id == sd->account_id - && char_dat[i].char_num == dat[30]) - { - char_log - ("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], dat, char_dat[i].name, - dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], - dat[33], dat[31]); - return -1; - } - } - - if (strcmp (wisp_server_name, dat) == 0) - { - char_log - ("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], dat, char_dat[i].name, - dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], - dat[33], dat[31]); - return -1; - } - - if (char_num >= char_max) - { - char_max += 256; - RECREATE (char_dat, struct mmo_charstatus, char_max); - RECREATE (online_chars, int, char_max); - for (j = char_max - 256; j < char_max; j++) - online_chars[j] = -1; - } - - char ip[16]; - unsigned char *sin_addr = - (unsigned char *) &session[fd]->client_addr.sin_addr; - sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], - sin_addr[3]); - - char_log - ("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d. [%s]\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], - dat[27], dat[28], dat[29], - dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], - dat[31], ip); - - memset (&char_dat[i], 0, sizeof (struct mmo_charstatus)); - - char_dat[i].char_id = char_id_count++; - char_dat[i].account_id = sd->account_id; - char_dat[i].char_num = dat[30]; - strcpy (char_dat[i].name, dat); - char_dat[i].pc_class = 0; - char_dat[i].base_level = 1; - char_dat[i].job_level = 1; - char_dat[i].base_exp = 0; - char_dat[i].job_exp = 0; - char_dat[i].zeny = start_zeny; - char_dat[i].str = dat[24]; - char_dat[i].agi = dat[25]; - char_dat[i].vit = dat[26]; - char_dat[i].int_ = dat[27]; - char_dat[i].dex = dat[28]; - char_dat[i].luk = dat[29]; - char_dat[i].max_hp = 40 * (100 + char_dat[i].vit) / 100; - char_dat[i].max_sp = 11 * (100 + char_dat[i].int_) / 100; - char_dat[i].hp = char_dat[i].max_hp; - char_dat[i].sp = char_dat[i].max_sp; - char_dat[i].status_point = 0; - char_dat[i].skill_point = 0; - char_dat[i].option = 0; - char_dat[i].karma = 0; - char_dat[i].manner = 0; - char_dat[i].party_id = 0; - char_dat[i].guild_id = 0; - char_dat[i].hair = dat[33]; - char_dat[i].hair_color = dat[31]; - char_dat[i].clothes_color = 0; - char_dat[i].inventory[0].nameid = start_weapon; // Knife - char_dat[i].inventory[0].amount = 1; - char_dat[i].inventory[0].equip = 0x02; - char_dat[i].inventory[0].identify = 1; - char_dat[i].inventory[0].broken = 0; - char_dat[i].inventory[1].nameid = start_armor; // Cotton Shirt - char_dat[i].inventory[1].amount = 1; - char_dat[i].inventory[1].equip = 0x10; - char_dat[i].inventory[1].identify = 1; - char_dat[i].inventory[1].broken = 0; - char_dat[i].weapon = 1; - char_dat[i].shield = 0; - char_dat[i].head_top = 0; - char_dat[i].head_mid = 0; - char_dat[i].head_bottom = 0; - memcpy (&char_dat[i].last_point, &start_point, sizeof (start_point)); - memcpy (&char_dat[i].save_point, &start_point, sizeof (start_point)); - char_num++; - - return i; -} - -//---------------------------------------------------- -// This function return the name of the job (by [Yor]) -//---------------------------------------------------- -char *job_name (int pc_class) -{ - switch (pc_class) - { - case 0: - return "Novice"; - case 1: - return "Swordsman"; - case 2: - return "Mage"; - case 3: - return "Archer"; - case 4: - return "Acolyte"; - case 5: - return "Merchant"; - case 6: - return "Thief"; - case 7: - return "Knight"; - case 8: - return "Priest"; - case 9: - return "Wizard"; - case 10: - return "Blacksmith"; - case 11: - return "Hunter"; - case 12: - return "Assassin"; - case 13: - return "Knight 2"; - case 14: - return "Crusader"; - case 15: - return "Monk"; - case 16: - return "Sage"; - case 17: - return "Rogue"; - case 18: - return "Alchemist"; - case 19: - return "Bard"; - case 20: - return "Dancer"; - case 21: - return "Crusader 2"; - case 22: - return "Wedding"; - case 23: - return "Super Novice"; - case 4001: - return "Novice High"; - case 4002: - return "Swordsman High"; - case 4003: - return "Mage High"; - case 4004: - return "Archer High"; - case 4005: - return "Acolyte High"; - case 4006: - return "Merchant High"; - case 4007: - return "Thief High"; - case 4008: - return "Lord Knight"; - case 4009: - return "High Priest"; - case 4010: - return "High Wizard"; - case 4011: - return "Whitesmith"; - case 4012: - return "Sniper"; - case 4013: - return "Assassin Cross"; - case 4014: - return "Peko Knight"; - case 4015: - return "Paladin"; - case 4016: - return "Champion"; - case 4017: - return "Professor"; - case 4018: - return "Stalker"; - case 4019: - return "Creator"; - case 4020: - return "Clown"; - case 4021: - return "Gypsy"; - case 4022: - return "Peko Paladin"; - case 4023: - return "Baby Novice"; - case 4024: - return "Baby Swordsman"; - case 4025: - return "Baby Mage"; - case 4026: - return "Baby Archer"; - case 4027: - return "Baby Acolyte"; - case 4028: - return "Baby Merchant"; - case 4029: - return "Baby Thief"; - case 4030: - return "Baby Knight"; - case 4031: - return "Baby Priest"; - case 4032: - return "Baby Wizard"; - case 4033: - return "Baby Blacksmith"; - case 4034: - return "Baby Hunter"; - case 4035: - return "Baby Assassin"; - case 4036: - return "Baby Peco Knight"; - case 4037: - return "Baby Crusader"; - case 4038: - return "Baby Monk"; - case 4039: - return "Baby Sage"; - case 4040: - return "Baby Rogue"; - case 4041: - return "Baby Alchemist"; - case 4042: - return "Baby Bard"; - case 4043: - return "Baby Dancer"; - case 4044: - return "Baby Peco Crusader"; - case 4045: - return "Super Baby"; - } - return "Unknown Job"; -} - -//------------------------------------------------------------- -// Function to create the online files (txt and html). by [Yor] -//------------------------------------------------------------- -void create_online_files (void) -{ - int i, j, k, l; // for loops - int players; // count the number of players - FILE *fp; // for the txt file - FILE *fp2; // for the html file - char temp[256]; // to prepare what we must display - time_t time_server; // for number of seconds - struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... - int id[char_num]; - - if (online_display_option == 0) // we display nothing, so return - return; - - //char_log("Creation of online players files.\n"); - - // Get number of online players, id of each online players - players = 0; - // sort online characters. - for (i = 0; i < char_num; i++) - { - if (online_chars[i] != -1) - { - id[players] = i; - // use sorting option - switch (online_sorting_option) - { - case 1: // by name (without case sensitive) - { - char *p_name = char_dat[i].name; //speed up sorting when there are a lot of players. But very rarely players have same name. - for (j = 0; j < players; j++) - if (strcasecmp (p_name, char_dat[id[j]].name) < 0 || - // if same name, we sort with case sensitive. - (strcasecmp (p_name, char_dat[id[j]].name) == 0 && - strcmp (p_name, char_dat[id[j]].name) < 0)) - { - for (k = players; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[players] - break; - } - } - break; - case 2: // by zeny - for (j = 0; j < players; j++) - if (char_dat[i].zeny < char_dat[id[j]].zeny || - // if same number of zenys, we sort by name. - (char_dat[i].zeny == char_dat[id[j]].zeny && - strcasecmp (char_dat[i].name, - char_dat[id[j]].name) < 0)) - { - for (k = players; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[players] - break; - } - break; - case 3: // by base level - for (j = 0; j < players; j++) - if (char_dat[i].base_level < - char_dat[id[j]].base_level || - // if same base level, we sort by base exp. - (char_dat[i].base_level == - char_dat[id[j]].base_level - && char_dat[i].base_exp < - char_dat[id[j]].base_exp)) - { - for (k = players; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[players] - break; - } - break; - case 4: // by job (and job level) - for (j = 0; j < players; j++) - if (char_dat[i].pc_class < char_dat[id[j]].pc_class || - // if same job, we sort by job level. - (char_dat[i].pc_class == char_dat[id[j]].pc_class && - char_dat[i].job_level < - char_dat[id[j]].job_level) || - // if same job and job level, we sort by job exp. - (char_dat[i].pc_class == char_dat[id[j]].pc_class && - char_dat[i].job_level == - char_dat[id[j]].job_level - && char_dat[i].job_exp < - char_dat[id[j]].job_exp)) - { - for (k = players; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[players] - break; - } - break; - case 5: // by location map name - { - int cpm_result; // A lot of player maps are identical. So, test if done often twice. - for (j = 0; j < players; j++) - if ((cpm_result = strcmp (char_dat[i].last_point.map, char_dat[id[j]].last_point.map)) < 0 || // no map are identical and with upper cases (not use strcasecmp) - // if same map name, we sort by name. - (cpm_result == 0 && - strcasecmp (char_dat[i].name, - char_dat[id[j]].name) < 0)) - { - for (k = players; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[players] - break; - } - } - break; - default: // 0 or invalid value: no sorting - break; - } - players++; - } - } - - // write files - fp = fopen_ (online_txt_filename, "w"); - if (fp != NULL) - { - fp2 = fopen_ (online_html_filename, "w"); - if (fp2 != NULL) - { - // get time - time (&time_server); // get time in seconds since 1/1/1970 - datetime = localtime (&time_server); // convert seconds in structure - strftime (temp, sizeof (temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52) - // write heading - fprintf (fp2, "<HTML>\n"); - fprintf (fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds - fprintf (fp2, " <HEAD>\n"); - fprintf (fp2, " <TITLE>Online Players on %s</TITLE>\n", - server_name); - fprintf (fp2, " </HEAD>\n"); - fprintf (fp2, " <BODY>\n"); - fprintf (fp2, " <H3>Online Players on %s (%s):</H3>\n", - server_name, temp); - fprintf (fp, "Online Players on %s (%s):\n", server_name, temp); - fprintf (fp, "\n"); - - // If we display at least 1 player - if (players > 0) - { - j = 0; // count the number of characters for the txt version and to set the separate line - fprintf (fp2, " <table border=\"1\" cellspacing=\"1\">\n"); - fprintf (fp2, " <tr>\n"); - if ((online_display_option & 1) - || (online_display_option & 64)) - { - fprintf (fp2, " <td><b>Name</b></td>\n"); - if (online_display_option & 64) - { - fprintf (fp, "Name "); // 30 - j += 30; - } - else - { - fprintf (fp, "Name "); // 25 - j += 25; - } - } - if ((online_display_option & 6) == 6) - { - fprintf (fp2, " <td><b>Job (levels)</b></td>\n"); - fprintf (fp, "Job Levels "); // 27 - j += 27; - } - else if (online_display_option & 2) - { - fprintf (fp2, " <td><b>Job</b></td>\n"); - fprintf (fp, "Job "); // 19 - j += 19; - } - else if (online_display_option & 4) - { - fprintf (fp2, " <td><b>Levels</b></td>\n"); - fprintf (fp, " Levels "); // 8 - j += 8; - } - if (online_display_option & 24) - { // 8 or 16 - fprintf (fp2, " <td><b>Location</b></td>\n"); - if (online_display_option & 16) - { - fprintf (fp, "Location ( x , y ) "); // 23 - j += 23; - } - else - { - fprintf (fp, "Location "); // 13 - j += 13; - } - } - if (online_display_option & 32) - { - fprintf (fp2, - " <td ALIGN=CENTER><b>zenys</b></td>\n"); - fprintf (fp, " Zenys "); // 16 - j += 16; - } - fprintf (fp2, " </tr>\n"); - fprintf (fp, "\n"); - for (k = 0; k < j; k++) - fprintf (fp, "-"); - fprintf (fp, "\n"); - - // display each player. - for (i = 0; i < players; i++) - { - // get id of the character (more speed) - j = id[i]; - fprintf (fp2, " <tr>\n"); - // displaying the character name - if ((online_display_option & 1) - || (online_display_option & 64)) - { // without/with 'GM' display - strcpy (temp, char_dat[j].name); - l = isGM (char_dat[j].account_id); - if (online_display_option & 64) - { - if (l >= online_gm_display_min_level) - fprintf (fp, "%-24s (GM) ", temp); - else - fprintf (fp, "%-24s ", temp); - } - else - fprintf (fp, "%-24s ", temp); - // name of the character in the html (no < >, because that create problem in html code) - fprintf (fp2, " <td>"); - if ((online_display_option & 64) - && l >= online_gm_display_min_level) - fprintf (fp2, "<b>"); - for (k = 0; temp[k]; k++) - { - switch (temp[k]) - { - case '<': // < - fprintf (fp2, "<"); - break; - case '>': // > - fprintf (fp2, ">"); - break; - default: - fprintf (fp2, "%c", temp[k]); - break; - }; - } - if ((online_display_option & 64) - && l >= online_gm_display_min_level) - fprintf (fp2, "</b> (GM)"); - fprintf (fp2, "</td>\n"); - } - // displaying of the job - if (online_display_option & 6) - { - char *jobname = job_name (char_dat[j].pc_class); - if ((online_display_option & 6) == 6) - { - fprintf (fp2, " <td>%s %d/%d</td>\n", - jobname, char_dat[j].base_level, - char_dat[j].job_level); - fprintf (fp, "%-18s %3d/%3d ", jobname, - char_dat[j].base_level, - char_dat[j].job_level); - } - else if (online_display_option & 2) - { - fprintf (fp2, " <td>%s</td>\n", jobname); - fprintf (fp, "%-18s ", jobname); - } - else if (online_display_option & 4) - { - fprintf (fp2, " <td>%d/%d</td>\n", - char_dat[j].base_level, - char_dat[j].job_level); - fprintf (fp, "%3d/%3d ", char_dat[j].base_level, - char_dat[j].job_level); - } - } - // displaying of the map - if (online_display_option & 24) - { // 8 or 16 - // prepare map name - memset (temp, 0, sizeof (temp)); - strncpy (temp, char_dat[j].last_point.map, 16); - if (strchr (temp, '.') != NULL) - temp[strchr (temp, '.') - temp] = '\0'; // suppress the '.gat' - // write map name - if (online_display_option & 16) - { // map-name AND coordonates - fprintf (fp2, " <td>%s (%d, %d)</td>\n", - temp, char_dat[j].last_point.x, - char_dat[j].last_point.y); - fprintf (fp, "%-12s (%3d,%3d) ", temp, - char_dat[j].last_point.x, - char_dat[j].last_point.y); - } - else - { - fprintf (fp2, " <td>%s</td>\n", temp); - fprintf (fp, "%-12s ", temp); - } - } - // displaying number of zenys - if (online_display_option & 32) - { - // write number of zenys - if (char_dat[j].zeny == 0) - { // if no zeny - fprintf (fp2, - " <td ALIGN=RIGHT>no zeny</td>\n"); - fprintf (fp, " no zeny "); - } - else - { - fprintf (fp2, - " <td ALIGN=RIGHT>%d z</td>\n", - char_dat[j].zeny); - fprintf (fp, "%13d z ", char_dat[j].zeny); - } - } - fprintf (fp, "\n"); - fprintf (fp2, " </tr>\n"); - } - fprintf (fp2, " </table>\n"); - fprintf (fp, "\n"); - } - - // Displaying number of online players - if (players == 0) - { - fprintf (fp2, " <p>No user is online.</p>\n"); - fprintf (fp, "No user is online.\n"); - // no display if only 1 player - } - else if (players == 1) - { - } - else - { - fprintf (fp2, " <p>%d users are online.</p>\n", players); - fprintf (fp, "%d users are online.\n", players); - } - fprintf (fp2, " </BODY>\n"); - fprintf (fp2, "</HTML>\n"); - fclose_ (fp2); - } - fclose_ (fp); - } - - return; -} - -//--------------------------------------------------------------------- -// This function return the number of online players in all map-servers -//--------------------------------------------------------------------- -int count_users (void) -{ - int i, users; - - users = 0; - for (i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - users += server[i].users; - - return users; -} - -//---------------------------------------- -// [Fate] Find inventory item based on equipment mask, return view. ID must match view ID (!). -//---------------------------------------- -static int find_equip_view (struct mmo_charstatus *p, unsigned int equipmask) -{ - int i; - for (i = 0; i < MAX_INVENTORY; i++) - if (p->inventory[i].nameid && p->inventory[i].amount - && p->inventory[i].equip & equipmask) - return p->inventory[i].nameid; - return 0; -} - -//---------------------------------------- -// Function to send characters to a player -//---------------------------------------- -int mmo_char_send006b (int fd, struct char_session_data *sd) -{ - int i, j, found_num; - struct mmo_charstatus *p; - const int offset = 24; - - found_num = 0; - for (i = 0; i < char_num; i++) - { - if (char_dat[i].account_id == sd->account_id) - { - sd->found_char[found_num] = i; - found_num++; - if (found_num == 9) - break; - } - } - for (i = found_num; i < 9; i++) - sd->found_char[i] = -1; - - memset (WFIFOP (fd, 0), 0, offset + found_num * 106); - WFIFOW (fd, 0) = 0x6b; - WFIFOW (fd, 2) = offset + found_num * 106; - - for (i = 0; i < found_num; i++) - { - p = &char_dat[sd->found_char[i]]; - j = offset + (i * 106); // increase speed of code - - WFIFOL (fd, j) = p->char_id; - WFIFOL (fd, j + 4) = p->base_exp; - WFIFOL (fd, j + 8) = p->zeny; - WFIFOL (fd, j + 12) = p->job_exp; - WFIFOL (fd, j + 16) = 0; //p->job_level; // [Fate] We no longer reveal this to the player, as its meaning is weird. - - WFIFOW (fd, j + 20) = find_equip_view (p, 0x0040); // 9: shoes - WFIFOW (fd, j + 22) = find_equip_view (p, 0x0004); // 10: gloves - WFIFOW (fd, j + 24) = find_equip_view (p, 0x0008); // 11: cape - WFIFOW (fd, j + 26) = find_equip_view (p, 0x0010); // 12: misc1 - WFIFOL (fd, j + 28) = p->option; - - WFIFOL (fd, j + 32) = p->karma; - WFIFOL (fd, j + 36) = p->manner; - - WFIFOW (fd, j + 40) = p->status_point; - WFIFOW (fd, j + 42) = (p->hp > 0x7fff) ? 0x7fff : p->hp; - WFIFOW (fd, j + 44) = (p->max_hp > 0x7fff) ? 0x7fff : p->max_hp; - WFIFOW (fd, j + 46) = (p->sp > 0x7fff) ? 0x7fff : p->sp; - WFIFOW (fd, j + 48) = (p->max_sp > 0x7fff) ? 0x7fff : p->max_sp; - WFIFOW (fd, j + 50) = DEFAULT_WALK_SPEED; // p->speed; - WFIFOW (fd, j + 52) = p->pc_class; - WFIFOW (fd, j + 54) = p->hair; -// WFIFOW(fd,j+56) = p->weapon; // dont send weapon since TMW does not support it - WFIFOW (fd, j + 56) = 0; - WFIFOW (fd, j + 58) = p->base_level; - WFIFOW (fd, j + 60) = p->skill_point; - WFIFOW (fd, j + 62) = p->head_bottom; - WFIFOW (fd, j + 64) = p->shield; - WFIFOW (fd, j + 66) = p->head_top; - WFIFOW (fd, j + 68) = p->head_mid; - WFIFOW (fd, j + 70) = p->hair_color; - WFIFOW (fd, j + 72) = find_equip_view (p, 0x0080); // 13: misc2 -// WFIFOW(fd,j+72) = p->clothes_color; - - memcpy (WFIFOP (fd, j + 74), p->name, 24); - - WFIFOB (fd, j + 98) = (p->str > 255) ? 255 : p->str; - WFIFOB (fd, j + 99) = (p->agi > 255) ? 255 : p->agi; - WFIFOB (fd, j + 100) = (p->vit > 255) ? 255 : p->vit; - WFIFOB (fd, j + 101) = (p->int_ > 255) ? 255 : p->int_; - WFIFOB (fd, j + 102) = (p->dex > 255) ? 255 : p->dex; - WFIFOB (fd, j + 103) = (p->luk > 255) ? 255 : p->luk; - WFIFOB (fd, j + 104) = p->char_num; - } - - WFIFOSET (fd, WFIFOW (fd, 2)); - - return 0; -} - -int set_account_reg2 (int acc, int num, struct global_reg *reg) -{ - int i, c; - - c = 0; - for (i = 0; i < char_num; i++) - { - if (char_dat[i].account_id == acc) - { - memcpy (char_dat[i].account_reg2, reg, - sizeof (char_dat[i].account_reg2)); - char_dat[i].account_reg2_num = num; - c++; - } - } - return c; -} - -// Divorce a character from it's partner and let the map server know -int char_divorce (struct mmo_charstatus *cs) -{ - int i; - char buf[10]; - - if (cs == NULL) - return 0; - - if (cs->partner_id <= 0) - { - WBUFW (buf, 0) = 0x2b12; - WBUFL (buf, 2) = cs->char_id; - WBUFL (buf, 6) = 0; // partner id 0 means failure - mapif_sendall (buf, 10); - return 0; - } - - WBUFW (buf, 0) = 0x2b12; - WBUFL (buf, 2) = cs->char_id; - - for (i = 0; i < char_num; i++) - { - if (char_dat[i].char_id == cs->partner_id - && char_dat[i].partner_id == cs->char_id) - { - WBUFL (buf, 6) = cs->partner_id; - mapif_sendall (buf, 10); - cs->partner_id = 0; - char_dat[i].partner_id = 0; - return 0; - } - // The other char doesn't have us as their partner, so just clear our partner - // Don't worry about this, as the map server should verify itself that the other doesn't have us as a partner, and so won't mess with their marriage - else if (char_dat[i].char_id == cs->partner_id) - { - WBUFL (buf, 6) = cs->partner_id; - mapif_sendall (buf, 10); - cs->partner_id = 0; - return 0; - } - } - - // Our partner wasn't found, so just clear our marriage - WBUFL (buf, 6) = cs->partner_id; - cs->partner_id = 0; - mapif_sendall (buf, 10); - - return 0; -} - -//------------------------------------------------------------ -// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] -//------------------------------------------------------------ -int e_mail_check (unsigned char *email) -{ - char ch; - unsigned 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; -} - -//---------------------------------------------------------------------- -// Force disconnection of an online player (with account value) by [Yor] -//---------------------------------------------------------------------- -int disconnect_player (int accound_id) -{ - int i; - struct char_session_data *sd; - - // disconnect player if online on char-server - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) - { - if (sd->account_id == accound_id) - { - session[i]->eof = 1; - return 1; - } - } - } - - return 0; -} - -// キャラ削除に伴うデータ削除 -static int char_delete (struct mmo_charstatus *cs) -{ - - // ギルド脱退 - if (cs->guild_id) - inter_guild_leave (cs->guild_id, cs->account_id, cs->char_id); - // パーティー脱退 - if (cs->party_id) - inter_party_leave (cs->party_id, cs->account_id); - // 離婚 - if (cs->partner_id) - char_divorce (cs); - - // Force the character (and all on the same account) to leave all map servers - { - unsigned char buf[6]; - WBUFW (buf, 0) = 0x2afe; - WBUFL (buf, 2) = cs->account_id; - mapif_sendall (buf, 6); - } - - return 0; -} - -void parse_tologin (int fd) -{ - int i; - struct char_session_data *sd; - - // only login-server can have an access to here. - // so, if it isn't the login-server, we disconnect the session (fd != login_fd). - if (fd != login_fd || session[fd]->eof) - { - if (fd == login_fd) - { - printf - ("Char-server can't connect to login-server (connection #%d).\n", - fd); - login_fd = -1; - } - close (fd); - delete_session (fd); - return; - } - - sd = (struct char_session_data*)session[fd]->session_data; - - while (RFIFOREST (fd) >= 2) - { -// printf("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW (fd, 0)) - { - case 0x2711: - if (RFIFOREST (fd) < 3) - return; - if (RFIFOB (fd, 2)) - { -// printf("connect login server error : %d\n", RFIFOB(fd,2)); - printf ("Can not connect to login-server.\n"); - printf - ("The server communication passwords (default s1/p1) is probably invalid.\n"); - printf - ("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); - printf - ("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); - exit (1); - } - else - { - printf ("Connected to login-server (connection #%d).\n", - fd); - // if no map-server already connected, display a message... - for (i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0 && server[i].map[0][0]) // if map-server online and at least 1 map - break; - if (i == MAX_MAP_SERVERS) - printf ("Awaiting maps from map-server.\n"); - } - RFIFOSKIP (fd, 3); - break; - - case 0x2713: - if (RFIFOREST (fd) < 51) - return; -// printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) - && sd->account_id == RFIFOL (fd, 2)) - { - if (RFIFOB (fd, 6) != 0) - { - WFIFOW (i, 0) = 0x6c; - WFIFOB (i, 2) = 0x42; - WFIFOSET (i, 3); - } - else if (max_connect_user == 0 - || count_users () < max_connect_user) - { -// if (max_connect_user == 0) -// printf("max_connect_user (unlimited) -> accepted.\n"); -// else -// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); - memcpy (sd->email, RFIFOP (fd, 7), 40); - if (e_mail_check (sd->email) == 0) - strncpy (sd->email, "a@a.com", 40); // default e-mail - sd->connect_until_time = (time_t) RFIFOL (fd, 47); - // send characters to player - mmo_char_send006b (i, sd); - } - else - { - // refuse connection: too much online players -// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); - WFIFOW (i, 0) = 0x6c; - WFIFOW (i, 2) = 0; - WFIFOSET (i, 3); - } - break; - } - } - RFIFOSKIP (fd, 51); - break; - - // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] - case 0x2717: - if (RFIFOREST (fd) < 50) - return; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) - { - if (sd->account_id == RFIFOL (fd, 2)) - { - memcpy (sd->email, RFIFOP (fd, 6), 40); - if (e_mail_check (sd->email) == 0) - strncpy (sd->email, "a@a.com", 40); // default e-mail - sd->connect_until_time = (time_t) RFIFOL (fd, 46); - break; - } - } - } - RFIFOSKIP (fd, 50); - break; - - case 0x2721: // gm reply - if (RFIFOREST (fd) < 10) - return; - { - unsigned char buf[10]; - WBUFW (buf, 0) = 0x2b0b; - WBUFL (buf, 2) = RFIFOL (fd, 2); // account - WBUFL (buf, 6) = RFIFOL (fd, 6); // GM level - mapif_sendall (buf, 10); -// printf("parse_tologin: To become GM answer: char -> map.\n"); - } - RFIFOSKIP (fd, 10); - break; - - case 0x2723: // changesex reply (modified by [Yor]) - if (RFIFOREST (fd) < 7) - return; - { - int acc, sex, i, j; - unsigned char buf[7]; - acc = RFIFOL (fd, 2); - sex = RFIFOB (fd, 6); - RFIFOSKIP (fd, 7); - if (acc > 0) - { - for (i = 0; i < char_num; i++) - { - if (char_dat[i].account_id == acc) - { - int jobclass = char_dat[i].pc_class; - char_dat[i].sex = sex; -// auth_fifo[i].sex = sex; - if (jobclass == 19 || jobclass == 20 || - jobclass == 4020 || jobclass == 4021 || - jobclass == 4042 || jobclass == 4043) - { - // job modification - if (jobclass == 19 || jobclass == 20) - { - char_dat[i].pc_class = (sex) ? 19 : 20; - } - else if (jobclass == 4020 - || jobclass == 4021) - { - char_dat[i].pc_class = - (sex) ? 4020 : 4021; - } - else if (jobclass == 4042 - || jobclass == 4043) - { - char_dat[i].pc_class = - (sex) ? 4042 : 4043; - } - } - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for (j = 0; j < MAX_INVENTORY; j++) - { - if (char_dat[i].inventory[j].nameid - && char_dat[i].inventory[j].equip) - char_dat[i].inventory[j].equip = 0; - } - char_dat[i].weapon = 0; - char_dat[i].shield = 0; - char_dat[i].head_top = 0; - char_dat[i].head_mid = 0; - char_dat[i].head_bottom = 0; - } - } - // disconnect player if online on char-server - disconnect_player (acc); - } - WBUFW (buf, 0) = 0x2b0d; - WBUFL (buf, 2) = acc; - WBUFB (buf, 6) = sex; - mapif_sendall (buf, 7); - } - break; - - case 0x2726: // Request to send a broadcast message (no answer) - if (RFIFOREST (fd) < 8 - || RFIFOREST (fd) < (8 + RFIFOL (fd, 4))) - return; - if (RFIFOL (fd, 4) < 1) - char_log - ("Receiving a message for broadcast, but message is void.\n"); - else - { - // at least 1 map-server - for (i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - break; - if (i == MAX_MAP_SERVERS) - char_log - ("'ladmin': Receiving a message for broadcast, but no map-server is online.\n"); - else - { - char buf[128]; - char message[RFIFOL (fd, 4) + 1]; // +1 to add a null terminated if not exist in the packet - int lp; - char *p; - memset (message, '\0', sizeof (message)); - memcpy (message, RFIFOP (fd, 8), RFIFOL (fd, 4)); - message[sizeof (message) - 1] = '\0'; - remove_control_chars (message); - // remove all first spaces - p = message; - while (p[0] == ' ') - p++; - // if message is only composed of spaces - if (p[0] == '\0') - char_log - ("Receiving a message for broadcast, but message is only a lot of spaces.\n"); - // else send message to all map-servers - else - { - if (RFIFOW (fd, 2) == 0) - { - char_log - ("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n", - message); - lp = 4; - } - else - { - char_log - ("'ladmin': Receiving a message for broadcast (message (in blue): %s)\n", - message); - lp = 8; - } - // split message to max 80 char - while (p[0] != '\0') - { // if not finish - if (p[0] == ' ') // jump if first char is a space - p++; - else - { - char split[80]; - char *last_space; - sscanf (p, "%79[^\t]", split); // max 79 char, any char (\t is control char and control char was removed before) - split[sizeof (split) - 1] = '\0'; // last char always \0 - if ((last_space = - strrchr (split, ' ')) != NULL) - { // searching space from end of the string - last_space[0] = '\0'; // replace it by NULL to have correct length of split - p++; // to jump the new NULL - } - p += strlen (split); - // send broadcast to all map-servers - WBUFW (buf, 0) = 0x3800; - WBUFW (buf, 2) = lp + strlen (split) + 1; - WBUFL (buf, 4) = 0x65756c62; // only write if in blue (lp = 8) - memcpy (WBUFP (buf, lp), split, - strlen (split) + 1); - mapif_sendall (buf, WBUFW (buf, 2)); - } - } - } - } - } - RFIFOSKIP (fd, 8 + RFIFOL (fd, 4)); - break; - - // account_reg2変更通知 - case 0x2729: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - struct global_reg reg[ACCOUNT_REG2_NUM]; - unsigned char buf[4096]; - int j, p, acc; - acc = RFIFOL (fd, 4); - for (p = 8, j = 0; - p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) - { - memcpy (reg[j].str, RFIFOP (fd, p), 32); - reg[j].value = RFIFOL (fd, p + 32); - } - set_account_reg2 (acc, j, reg); - // 同垢ログインを禁止していれば送る必要は無い - memcpy (buf, RFIFOP (fd, 0), RFIFOW (fd, 2)); - WBUFW (buf, 0) = 0x2b11; - mapif_sendall (buf, WBUFW (buf, 2)); - RFIFOSKIP (fd, RFIFOW (fd, 2)); -// printf("char: save_account_reg_reply\n"); - } - break; - - case 0x7924: - { // [Fate] Itemfrob package: forwarded from login-server - if (RFIFOREST (fd) < 10) - return; - int source_id = RFIFOL (fd, 2); - int dest_id = RFIFOL (fd, 6); - unsigned char buf[10]; - - WBUFW (buf, 0) = 0x2afa; - WBUFL (buf, 2) = source_id; - WBUFL (buf, 6) = dest_id; - - mapif_sendall (buf, 10); // forward package to map servers - for (i = 0; i < char_num; i++) - { - struct mmo_charstatus *c = char_dat + i; - struct storage *s = account2storage (c->account_id); - int changes = 0; - int j; -#define FIX(v) if (v == source_id) {v = dest_id; ++changes; } - for (j = 0; j < MAX_INVENTORY; j++) - FIX (c->inventory[j].nameid); - for (j = 0; j < MAX_CART; j++) - FIX (c->cart[j].nameid); - FIX (c->weapon); - FIX (c->shield); - FIX (c->head_top); - FIX (c->head_mid); - FIX (c->head_bottom); - - if (s) - for (j = 0; j < s->storage_amount; j++) - FIX (s->storage_[j].nameid); -#undef FIX - if (changes) - char_log - ("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n", - source_id, dest_id, c->name, c->char_id, - c->account_id, changes); - - } - - mmo_char_sync (); - inter_storage_save (); - RFIFOSKIP (fd, 10); - break; - } - - // Account deletion notification (from login-server) - case 0x2730: - if (RFIFOREST (fd) < 6) - return; - // Deletion of all characters of the account - for (i = 0; i < char_num; i++) - { - if (char_dat[i].account_id == RFIFOL (fd, 2)) - { - char_delete (&char_dat[i]); - if (i < char_num - 1) - { - memcpy (&char_dat[i], &char_dat[char_num - 1], - sizeof (struct mmo_charstatus)); - // if moved character owns to deleted account, check again it's character - if (char_dat[i].account_id == RFIFOL (fd, 2)) - { - i--; - // Correct moved character reference in the character's owner by [Yor] - } - else - { - int j, k; - struct char_session_data *sd2; - for (j = 0; j < fd_max; j++) - { - if (session[j] - && (sd2 = (struct char_session_data*)session[j]->session_data) - && sd2->account_id == - char_dat[char_num - 1].account_id) - { - for (k = 0; k < 9; k++) - { - if (sd2->found_char[k] == - char_num - 1) - { - sd2->found_char[k] = i; - break; - } - } - break; - } - } - } - } - char_num--; - } - } - // Deletion of the storage - inter_storage_delete (RFIFOL (fd, 2)); - // send to all map-servers to disconnect the player - { - unsigned char buf[6]; - WBUFW (buf, 0) = 0x2b13; - WBUFL (buf, 2) = RFIFOL (fd, 2); - mapif_sendall (buf, 6); - } - // disconnect player if online on char-server - disconnect_player (RFIFOL (fd, 2)); - RFIFOSKIP (fd, 6); - break; - - // State change of account/ban notification (from login-server) by [Yor] - case 0x2731: - if (RFIFOREST (fd) < 11) - return; - // send to all map-servers to disconnect the player - { - unsigned char buf[11]; - WBUFW (buf, 0) = 0x2b14; - WBUFL (buf, 2) = RFIFOL (fd, 2); - WBUFB (buf, 6) = RFIFOB (fd, 6); // 0: change of statut, 1: ban - WBUFL (buf, 7) = RFIFOL (fd, 7); // status or final date of a banishment - mapif_sendall (buf, 11); - } - // disconnect player if online on char-server - disconnect_player (RFIFOL (fd, 2)); - RFIFOSKIP (fd, 11); - break; - - // Receiving GM acounts info from login-server (by [Yor]) - case 0x2732: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - char buf[32000]; - if (gm_account != NULL) - free (gm_account); - CREATE (gm_account, struct gm_account, (RFIFOW (fd, 2) - 4) / 5); - GM_num = 0; - 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++; - } - printf - ("From login-server: receiving of %d GM accounts information.\n", - GM_num); - char_log - ("From login-server: receiving of %d GM accounts information.\n", - GM_num); - create_online_files (); // update online players files (perhaps some online players change of GM level) - // send new gm acccounts level to map-servers - memcpy (buf, RFIFOP (fd, 0), RFIFOW (fd, 2)); - WBUFW (buf, 0) = 0x2b15; - mapif_sendall (buf, RFIFOW (fd, 2)); - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - case 0x2741: // change password reply - if (RFIFOREST (fd) < 7) - return; - { - int acc, status, i; - acc = RFIFOL (fd, 2); - status = RFIFOB (fd, 6); - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) - { - if (sd->account_id == acc) - { - WFIFOW (i, 0) = 0x62; - WFIFOB (i, 2) = status; - WFIFOSET (i, 3); - break; - } - } - } - } - RFIFOSKIP (fd, 7); - break; - - default: - session[fd]->eof = 1; - return; - } - } - RFIFOFLUSH (fd); -} - -//-------------------------------- -// Map-server anti-freeze system -//-------------------------------- -void map_anti_freeze_system (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int i; - - //printf("Entering in map_anti_freeze_system function to check freeze of servers.\n"); - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - if (server_fd[i] >= 0) - { // if map-server is online - //printf("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); - if (server_freezeflag[i]-- < 1) - { // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed - printf - ("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", - i); - char_log - ("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", - i); - session[server_fd[i]]->eof = 1; - } - } - } -} - -void parse_frommap (int fd) -{ - int i, j; - int id; - - for (id = 0; id < MAX_MAP_SERVERS; id++) - if (server_fd[id] == fd) - break; - if (id == MAX_MAP_SERVERS || session[fd]->eof) - { - if (id < MAX_MAP_SERVERS) - { - printf ("Map-server %d (session #%d) has disconnected.\n", id, - fd); - memset (&server[id], 0, sizeof (struct mmo_map_server)); - server_fd[id] = -1; - for (j = 0; j < char_num; j++) - if (online_chars[j] == fd) - online_chars[j] = -1; - create_online_files (); // update online players files (to remove all online players of this server) - } - close (fd); - delete_session (fd); - return; - } - - while (RFIFOREST (fd) >= 2) - { -// printf("parse_frommap: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW (fd, 0)) - { - // request from map-server to reload GM accounts. Transmission to login-server (by Yor) - case 0x2af7: - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2709; - WFIFOSET (login_fd, 2); -// printf("char : request from map-server to reload GM accounts -> login-server.\n"); - } - RFIFOSKIP (fd, 2); - break; - - // Receiving map names list from the map-server - case 0x2afa: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - memset (server[id].map, 0, sizeof (server[id].map)); - j = 0; - for (i = 4; i < RFIFOW (fd, 2); i += 16) - { - memcpy (server[id].map[j], RFIFOP (fd, i), 16); -// printf("set map %d.%d : %s\n", id, j, server[id].map[j]); - j++; - } - { - unsigned char *p = (unsigned char *) &server[id].ip; - printf - ("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", - id, j, p[0], p[1], p[2], p[3], server[id].port); - printf ("Map-server %d loading complete.\n", id); - char_log - ("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d. Map-server %d loading complete.\n", - id, j, p[0], p[1], p[2], p[3], - server[id].port, id); - } - WFIFOW (fd, 0) = 0x2afb; - WFIFOB (fd, 2) = 0; - memcpy (WFIFOP (fd, 3), wisp_server_name, 24); // name for wisp to player - WFIFOSET (fd, 27); - { - unsigned char buf[16384]; - int x; - if (j == 0) - { - printf ("WARNING: Map-Server %d have NO map.\n", id); - char_log ("WARNING: Map-Server %d have NO map.\n", - id); - // Transmitting maps information to the other map-servers - } - else - { - WBUFW (buf, 0) = 0x2b04; - WBUFW (buf, 2) = j * 16 + 10; - WBUFL (buf, 4) = server[id].ip; - WBUFW (buf, 8) = server[id].port; - memcpy (WBUFP (buf, 10), RFIFOP (fd, 4), j * 16); - mapif_sendallwos (fd, buf, WBUFW (buf, 2)); - } - // Transmitting the maps of the other map-servers to the new map-server - for (x = 0; x < MAX_MAP_SERVERS; x++) - { - if (server_fd[x] >= 0 && x != id) - { - WFIFOW (fd, 0) = 0x2b04; - WFIFOL (fd, 4) = server[x].ip; - WFIFOW (fd, 8) = server[x].port; - j = 0; - for (i = 0; i < MAX_MAP_PER_SERVER; i++) - if (server[x].map[i][0]) - memcpy (WFIFOP (fd, 10 + (j++) * 16), - server[x].map[i], 16); - if (j > 0) - { - WFIFOW (fd, 2) = j * 16 + 10; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - } - } - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - // 認証要求 - case 0x2afc: - if (RFIFOREST (fd) < 22) - return; - //printf("auth_fifo search: account: %d, char: %d, secure: %08x-%08x\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14)); - for (i = 0; i < AUTH_FIFO_SIZE; i++) - { - if (auth_fifo[i].account_id == RFIFOL (fd, 2) && - auth_fifo[i].char_id == RFIFOL (fd, 6) && - auth_fifo[i].login_id1 == RFIFOL (fd, 10) && -#if CMP_AUTHFIFO_LOGIN2 != 0 - // here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value) - (auth_fifo[i].login_id2 == RFIFOL (fd, 14) || RFIFOL (fd, 14) == 0) && // relate to the versions higher than 18 -#endif - (!check_ip_flag || auth_fifo[i].ip == RFIFOL (fd, 18)) - && !auth_fifo[i].delflag) - { - auth_fifo[i].delflag = 1; - WFIFOW (fd, 0) = 0x2afd; - WFIFOW (fd, 2) = 18 + sizeof (struct mmo_charstatus); - WFIFOL (fd, 4) = RFIFOL (fd, 2); - WFIFOL (fd, 8) = auth_fifo[i].login_id2; - WFIFOL (fd, 12) = - (unsigned long) auth_fifo[i].connect_until_time; - char_dat[auth_fifo[i].char_pos].sex = - auth_fifo[i].sex; - WFIFOW (fd, 16) = auth_fifo[i].packet_tmw_version; - fprintf (stderr, - "From queue index %d: recalling packet version %d\n", - i, auth_fifo[i].packet_tmw_version); - memcpy (WFIFOP (fd, 18), - &char_dat[auth_fifo[i].char_pos], - sizeof (struct mmo_charstatus)); - WFIFOSET (fd, WFIFOW (fd, 2)); - //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); - break; - } - } - if (i == AUTH_FIFO_SIZE) - { - WFIFOW (fd, 0) = 0x2afe; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - WFIFOSET (fd, 6); - printf - ("auth_fifo search error! account %d not authentified.\n", - RFIFOL (fd, 2)); - } - RFIFOSKIP (fd, 22); - break; - - // MAPサーバー上のユーザー数受信 - case 0x2aff: - if (RFIFOREST (fd) < 6 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - server[id].users = RFIFOW (fd, 4); - if (anti_freeze_enable) - server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed - // remove all previously online players of the server - for (i = 0; i < char_num; i++) - if (online_chars[i] == id) - online_chars[i] = -1; - // add online players in the list by [Yor] - for (i = 0; i < server[id].users; i++) - { - int char_id = RFIFOL (fd, 6 + i * 4); - for (j = 0; j < char_num; j++) - if (char_dat[j].char_id == char_id) - { - online_chars[j] = id; - //printf("%d\n", char_id); - break; - } - } - if (update_online < time (NULL)) - { // Time is done - update_online = time (NULL) + 8; - create_online_files (); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. - // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. - } - RFIFOSKIP (fd, 6 + i * 4); - break; - - // キャラデータ保存 - case 0x2b01: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - for (i = 0; i < char_num; i++) - { - if (char_dat[i].account_id == RFIFOL (fd, 4) && - char_dat[i].char_id == RFIFOL (fd, 8)) - break; - } - if (i != char_num) - memcpy (&char_dat[i], RFIFOP (fd, 12), - sizeof (struct mmo_charstatus)); - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - // キャラセレ要求 - case 0x2b02: - if (RFIFOREST (fd) < 18) - return; - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); - auth_fifo[auth_fifo_pos].account_id = RFIFOL (fd, 2); - auth_fifo[auth_fifo_pos].char_id = 0; - auth_fifo[auth_fifo_pos].login_id1 = RFIFOL (fd, 6); - auth_fifo[auth_fifo_pos].login_id2 = RFIFOL (fd, 10); - auth_fifo[auth_fifo_pos].delflag = 2; - auth_fifo[auth_fifo_pos].char_pos = 0; - auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[auth_fifo_pos].ip = RFIFOL (fd, 14); - auth_fifo_pos++; - WFIFOW (fd, 0) = 0x2b03; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - WFIFOB (fd, 6) = 0; - WFIFOSET (fd, 7); - RFIFOSKIP (fd, 18); - break; - - // マップサーバー間移動要求 - case 0x2b05: - if (RFIFOREST (fd) < 49) - return; - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - WFIFOW (fd, 0) = 0x2b06; - memcpy (WFIFOP (fd, 2), RFIFOP (fd, 2), 42); - //printf("auth_fifo set (auth#%d) - account: %d, secure: 0x%08x-0x%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); - auth_fifo[auth_fifo_pos].account_id = RFIFOL (fd, 2); - auth_fifo[auth_fifo_pos].char_id = RFIFOL (fd, 14); - auth_fifo[auth_fifo_pos].login_id1 = RFIFOL (fd, 6); - auth_fifo[auth_fifo_pos].login_id2 = RFIFOL (fd, 10); - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].sex = RFIFOB (fd, 44); - auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[auth_fifo_pos].ip = RFIFOL (fd, 45); - for (i = 0; i < char_num; i++) - if (char_dat[i].account_id == RFIFOL (fd, 2) && - char_dat[i].char_id == RFIFOL (fd, 14)) - { - auth_fifo[auth_fifo_pos].char_pos = i; - auth_fifo_pos++; - WFIFOL (fd, 6) = 0; - break; - } - if (i == char_num) - WFIFOW (fd, 6) = 1; - WFIFOSET (fd, 44); - RFIFOSKIP (fd, 49); - break; - - // キャラ名検索 - case 0x2b08: - if (RFIFOREST (fd) < 6) - return; - for (i = 0; i < char_num; i++) - { - if (char_dat[i].char_id == RFIFOL (fd, 2)) - break; - } - WFIFOW (fd, 0) = 0x2b09; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - if (i != char_num) - memcpy (WFIFOP (fd, 6), char_dat[i].name, 24); - else - memcpy (WFIFOP (fd, 6), unknown_char_name, 24); - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 6); - break; - - // it is a request to become GM - case 0x2b0a: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; -// printf("parse_frommap: change gm -> login, account: %d, pass: '%s'.\n", RFIFOL(fd,4), RFIFOP(fd,8)); - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2720; - memcpy (WFIFOP (login_fd, 2), RFIFOP (fd, 2), - RFIFOW (fd, 2) - 2); - WFIFOSET (login_fd, RFIFOW (fd, 2)); - } - else - { - WFIFOW (fd, 0) = 0x2b0b; - WFIFOL (fd, 2) = RFIFOL (fd, 4); - WFIFOL (fd, 6) = 0; - WFIFOSET (fd, 10); - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - // Map server send information to change an email of an account -> login-server - case 0x2b0c: - if (RFIFOREST (fd) < 86) - return; - if (login_fd > 0) - { // don't send request if no login-server - memcpy (WFIFOP (login_fd, 0), RFIFOP (fd, 0), 86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B - WFIFOW (login_fd, 0) = 0x2722; - WFIFOSET (login_fd, 86); - } - RFIFOSKIP (fd, 86); - break; - - // Map server ask char-server about a character name to do some operations (all operations are transmitted to login-server) - case 0x2b0e: - if (RFIFOREST (fd) < 44) - return; - { - char character_name[24]; - int acc = RFIFOL (fd, 2); // account_id of who ask (-1 if nobody) - memcpy (character_name, RFIFOP (fd, 6), 24); - character_name[sizeof (character_name) - 1] = '\0'; - // prepare answer - WFIFOW (fd, 0) = 0x2b0f; // answer - WFIFOL (fd, 2) = acc; // who want do operation - WFIFOW (fd, 30) = RFIFOW (fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex - // search character - i = search_character_index (character_name); - if (i >= 0) - { - memcpy (WFIFOP (fd, 6), search_character_name (i), 24); // put correct name if found - WFIFOW (fd, 32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - switch (RFIFOW (fd, 30)) - { - case 1: // block - if (acc == -1 - || isGM (acc) >= - isGM (char_dat[i].account_id)) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2724; - WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value - WFIFOL (login_fd, 6) = 5; // status of the account - WFIFOSET (login_fd, 10); -// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 5); - } - else - WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - else - WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 2: // ban - if (acc == -1 - || isGM (acc) >= - isGM (char_dat[i].account_id)) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2725; - WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value - WFIFOW (login_fd, 6) = RFIFOW (fd, 32); // year - WFIFOW (login_fd, 8) = RFIFOW (fd, 34); // month - WFIFOW (login_fd, 10) = RFIFOW (fd, 36); // day - WFIFOW (login_fd, 12) = RFIFOW (fd, 38); // hour - WFIFOW (login_fd, 14) = RFIFOW (fd, 40); // minute - WFIFOW (login_fd, 16) = RFIFOW (fd, 42); // second - WFIFOSET (login_fd, 18); -// printf("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", -// char_dat[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); - } - else - WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - else - WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 3: // unblock - if (acc == -1 - || isGM (acc) >= - isGM (char_dat[i].account_id)) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2724; - WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value - WFIFOL (login_fd, 6) = 0; // status of the account - WFIFOSET (login_fd, 10); -// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 0); - } - else - WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - else - WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 4: // unban - if (acc == -1 - || isGM (acc) >= - isGM (char_dat[i].account_id)) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x272a; - WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value - WFIFOSET (login_fd, 6); -// printf("char : status -> login: account %d, unban request\n", char_dat[i].account_id); - } - else - WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - else - WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 5: // changesex - if (acc == -1 - || isGM (acc) >= - isGM (char_dat[i].account_id)) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2727; - WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value - WFIFOSET (login_fd, 6); -// printf("char : status -> login: account %d, change sex request\n", char_dat[i].account_id); - } - else - WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - else - WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - } - } - else - { - // character name not found - memcpy (WFIFOP (fd, 6), character_name, 24); - WFIFOW (fd, 32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - // send answer if a player ask, not if the server ask - if (acc != -1) - { - WFIFOSET (fd, 34); - } - RFIFOSKIP (fd, 44); - break; - } - -// case 0x2b0f: not more used (available for futur usage) - - // account_reg保存要求 - case 0x2b10: - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - struct global_reg reg[ACCOUNT_REG2_NUM]; - int p, acc; - acc = RFIFOL (fd, 4); - for (p = 8, j = 0; - p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) - { - memcpy (reg[j].str, RFIFOP (fd, p), 32); - reg[j].value = RFIFOL (fd, p + 32); - } - set_account_reg2 (acc, j, reg); - // loginサーバーへ送る - if (login_fd > 0) - { // don't send request if no login-server - memcpy (WFIFOP (login_fd, 0), RFIFOP (fd, 0), - RFIFOW (fd, 2)); - WFIFOW (login_fd, 0) = 0x2728; - WFIFOSET (login_fd, WFIFOW (login_fd, 2)); - } - // ワールドへの同垢ログインがなければmapサーバーに送る必要はない - //memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); - //WBUFW(buf,0) = 0x2b11; - //mapif_sendall(buf, WBUFW(buf,2)); - RFIFOSKIP (fd, RFIFOW (fd, 2)); -// printf("char: save_account_reg (from map)\n"); - break; - } - - // Map server is requesting a divorce - case 0x2b16: - if (RFIFOREST (fd) < 4) - return; - { - for (i = 0; i < char_num; i++) - if (char_dat[i].char_id == RFIFOL (fd, 2)) - break; - - if (i != char_num) - char_divorce (&char_dat[i]); - - RFIFOSKIP (fd, 6); - break; - - } - - default: - // inter server処理に渡す - { - int r = inter_parse_frommap (fd); - if (r == 1) // 処理できた - break; - if (r == 2) // パケット長が足りない - return; - } - // inter server処理でもない場合は切断 - printf - ("char: unknown packet 0x%04x (%d bytes to read in buffer)! (from map).\n", - RFIFOW (fd, 0), RFIFOREST (fd)); - session[fd]->eof = 1; - return; - } - } -} - -int search_mapserver (char *map) -{ - int i, j; - char temp_map[16]; - int temp_map_len; - -// printf("Searching the map-server for map '%s'... ", map); - strncpy (temp_map, map, sizeof (temp_map)); - temp_map[sizeof (temp_map) - 1] = '\0'; - if (strchr (temp_map, '.') != NULL) - temp_map[strchr (temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map - - temp_map_len = strlen (temp_map); - for (i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - for (j = 0; server[i].map[j][0]; j++) - //printf("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); - if (strncmp (server[i].map[j], temp_map, temp_map_len) == 0) - { -// printf("found -> server #%d.\n", i); - return i; - } - -// printf("not found.\n"); - return -1; -} - -// char_mapifの初期化処理(現在はinter_mapif初期化のみ) -static int char_mapif_init (int fd) -{ - return inter_mapif_init (fd); -} - -//----------------------------------------------------- -// Test to know if an IP come from LAN or WAN. by [Yor] -//----------------------------------------------------- -int lan_ip_check (unsigned char *p) -{ - int i; - int lancheck = 1; - -// printf("lan_ip_check: to compare: %d.%d.%d.%d, network: %d.%d.%d.%d/%d.%d.%d.%d\n", -// p[0], p[1], p[2], p[3], -// subneti[0], subneti[1], subneti[2], subneti[3], -// subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); - for (i = 0; i < 4; i++) - { - if ((subneti[i] & subnetmaski[i]) != (p[i] & subnetmaski[i])) - { - lancheck = 0; - break; - } - } - printf ("LAN test (result): %s source\033[0m.\n", - (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); - return lancheck; -} - -void parse_char (int fd) -{ - int i, ch; - char email[40]; - struct char_session_data *sd; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - - if (login_fd < 0 || session[fd]->eof) - { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. - if (fd == login_fd) - login_fd = -1; - close (fd); - delete_session (fd); - return; - } - - sd = (struct char_session_data*)session[fd]->session_data; - - while (RFIFOREST (fd) >= 2) - { -// if (RFIFOW(fd,0) < 30000) -// printf("parse_char: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW (fd, 0)) - { - case 0x20b: //20040622暗号化ragexe対応 - if (RFIFOREST (fd) < 19) - return; - RFIFOSKIP (fd, 19); - break; - - case 0x61: // change password request - if (RFIFOREST (fd) < 50) - return; - { - WFIFOW (login_fd, 0) = 0x2740; - WFIFOL (login_fd, 2) = sd->account_id; - memcpy (WFIFOP (login_fd, 6), RFIFOP (fd, 2), 24); - memcpy (WFIFOP (login_fd, 30), RFIFOP (fd, 26), 24); - WFIFOSET (login_fd, 54); - } - RFIFOSKIP (fd, 50); - break; - - case 0x65: // 接続要求 - if (RFIFOREST (fd) < 17) - return; - { - int GM_value; - if ((GM_value = isGM (RFIFOL (fd, 2)))) - printf - ("Account Logged On; Account ID: %d (GM level %d).\n", - RFIFOL (fd, 2), GM_value); - else - printf ("Account Logged On; Account ID: %d.\n", - RFIFOL (fd, 2)); - if (sd == NULL) - { - CREATE (sd, struct char_session_data, 1); - session[fd]->session_data = sd; - memcpy (sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail - sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) - } - sd->account_id = RFIFOL (fd, 2); - sd->login_id1 = RFIFOL (fd, 6); - sd->login_id2 = RFIFOL (fd, 10); - sd->packet_tmw_version = RFIFOW (fd, 14); - sd->sex = RFIFOB (fd, 16); - // send back account_id - WFIFOL (fd, 0) = RFIFOL (fd, 2); - WFIFOSET (fd, 4); - // search authentification - for (i = 0; i < AUTH_FIFO_SIZE; i++) - { - if (auth_fifo[i].account_id == sd->account_id && - auth_fifo[i].login_id1 == sd->login_id1 && -#if CMP_AUTHFIFO_LOGIN2 != 0 - auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 -#endif - (!check_ip_flag - || auth_fifo[i].ip == - session[fd]->client_addr.sin_addr.s_addr) - && auth_fifo[i].delflag == 2) - { - auth_fifo[i].delflag = 1; - if (max_connect_user == 0 - || count_users () < max_connect_user) - { - if (login_fd > 0) - { // don't send request if no login-server - // request to login-server to obtain e-mail/time limit - WFIFOW (login_fd, 0) = 0x2716; - WFIFOL (login_fd, 2) = sd->account_id; - WFIFOSET (login_fd, 6); - } - // Record client version - auth_fifo[i].packet_tmw_version = - sd->packet_tmw_version; - // send characters to player - mmo_char_send006b (fd, sd); - } - else - { - // refuse connection (over populated) - WFIFOW (fd, 0) = 0x6c; - WFIFOW (fd, 2) = 0; - WFIFOSET (fd, 3); - } - break; - } - } - // authentification not found - if (i == AUTH_FIFO_SIZE) - { - if (login_fd > 0) - { // don't send request if no login-server - WFIFOW (login_fd, 0) = 0x2712; // ask login-server to authentify an account - WFIFOL (login_fd, 2) = sd->account_id; - WFIFOL (login_fd, 6) = sd->login_id1; - WFIFOL (login_fd, 10) = sd->login_id2; // relate to the versions higher than 18 - WFIFOB (login_fd, 14) = sd->sex; - WFIFOL (login_fd, 15) = - session[fd]->client_addr.sin_addr.s_addr; - WFIFOSET (login_fd, 19); - } - else - { // if no login-server, we must refuse connection - WFIFOW (fd, 0) = 0x6c; - WFIFOW (fd, 2) = 0; - WFIFOSET (fd, 3); - } - } - } - RFIFOSKIP (fd, 17); - break; - - case 0x66: // キャラ選択 - if (!sd || RFIFOREST (fd) < 3) - return; - - char ip[16]; - unsigned char *sin_addr = - (unsigned char *) &session[fd]->client_addr.sin_addr; - sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], - sin_addr[2], sin_addr[3]); - - // if we activated email creation and email is default email - if (email_creation != 0 && strcmp (sd->email, "a@a.com") == 0 - && login_fd > 0) - { // to modify an e-mail, login-server must be online - WFIFOW (fd, 0) = 0x70; - WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET (fd, 3); - - // otherwise, load the character - } - else - { - for (ch = 0; ch < 9; ch++) - if (sd->found_char[ch] >= 0 - && char_dat[sd->found_char[ch]].char_num == - RFIFOB (fd, 2)) - break; - if (ch != 9) - { - char_log - ("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s [%s]\n", - sd->account_id, RFIFOB (fd, 2), - char_dat[sd->found_char[ch]].name, ip); - // searching map server - i = search_mapserver (char_dat - [sd->found_char[ch]].last_point. - map); - // if map is not found, we check major cities - if (i < 0) - { - if ((i = search_mapserver ("prontera.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "prontera.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 273; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 354; - } - else if ((i = - search_mapserver ("geffen.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "geffen.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 120; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 100; - } - else if ((i = - search_mapserver ("morocc.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "morocc.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 160; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 94; - } - else if ((i = - search_mapserver ("alberta.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "alberta.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 116; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 57; - } - else if ((i = - search_mapserver ("payon.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "payon.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 87; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 117; - } - else if ((i = - search_mapserver ("izlude.gat")) >= 0) - { // check is done without 'gat'. - memcpy (char_dat - [sd->found_char[ch]].last_point.map, - "izlude.gat", 16); - char_dat[sd->found_char[ch]].last_point.x = 94; // savepoint coordonates - char_dat[sd->found_char[ch]].last_point.y = - 103; - } - else - { - int j; - // get first online server (with a map) - i = 0; - for (j = 0; j < MAX_MAP_SERVERS; j++) - if (server_fd[j] >= 0 - && server[j].map[0][0]) - { // change save point to one of map found on the server (the first) - i = j; - memcpy (char_dat - [sd-> - found_char[ch]].last_point. - map, server[j].map[0], 16); - printf - ("Map-server #%d found with a map: '%s'.\n", - j, server[j].map[0]); - // coordonates are unknown - break; - } - // if no map-server is connected, we send: server closed - if (j == MAX_MAP_SERVERS) - { - WFIFOW (fd, 0) = 0x81; - WFIFOL (fd, 2) = 1; // 01 = Server closed - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 3); - break; - } - } - } - WFIFOW (fd, 0) = 0x71; - WFIFOL (fd, 2) = char_dat[sd->found_char[ch]].char_id; - memcpy (WFIFOP (fd, 6), - char_dat[sd->found_char[ch]].last_point.map, - 16); - printf - ("Character selection '%s' (account: %d, slot: %d) [%s]\n", - char_dat[sd->found_char[ch]].name, - sd->account_id, ch, ip); - printf ("--Send IP of map-server. "); - if (lan_ip_check (p)) - WFIFOL (fd, 22) = inet_addr (lan_map_ip); - else - WFIFOL (fd, 22) = server[i].ip; - WFIFOW (fd, 26) = server[i].port; - WFIFOSET (fd, 28); - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - //printf("auth_fifo set #%d - account %d, char: %d, secure: %08x-%08x\n", auth_fifo_pos, sd->account_id, char_dat[sd->found_char[ch]].char_id, sd->login_id1, sd->login_id2); - auth_fifo[auth_fifo_pos].account_id = sd->account_id; - auth_fifo[auth_fifo_pos].char_id = - char_dat[sd->found_char[ch]].char_id; - auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; - auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].char_pos = - sd->found_char[ch]; - auth_fifo[auth_fifo_pos].sex = sd->sex; - auth_fifo[auth_fifo_pos].connect_until_time = - sd->connect_until_time; - auth_fifo[auth_fifo_pos].ip = - session[fd]->client_addr.sin_addr.s_addr; - auth_fifo[auth_fifo_pos].packet_tmw_version = - sd->packet_tmw_version; - auth_fifo_pos++; - } - } - RFIFOSKIP (fd, 3); - break; - - case 0x67: // 作成 - if (!sd || RFIFOREST (fd) < 37) - return; - i = make_new_char (fd, RFIFOP (fd, 2)); - if (i < 0) - { - WFIFOW (fd, 0) = 0x6e; - WFIFOB (fd, 2) = 0x00; - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 37); - break; - } - - WFIFOW (fd, 0) = 0x6d; - memset (WFIFOP (fd, 2), 0, 106); - - WFIFOL (fd, 2) = char_dat[i].char_id; - WFIFOL (fd, 2 + 4) = char_dat[i].base_exp; - WFIFOL (fd, 2 + 8) = char_dat[i].zeny; - WFIFOL (fd, 2 + 12) = char_dat[i].job_exp; - WFIFOL (fd, 2 + 16) = char_dat[i].job_level; - - WFIFOL (fd, 2 + 28) = char_dat[i].karma; - WFIFOL (fd, 2 + 32) = char_dat[i].manner; - - WFIFOW (fd, 2 + 40) = 0x30; - WFIFOW (fd, 2 + 42) = - (char_dat[i].hp > 0x7fff) ? 0x7fff : char_dat[i].hp; - WFIFOW (fd, 2 + 44) = - (char_dat[i].max_hp > - 0x7fff) ? 0x7fff : char_dat[i].max_hp; - WFIFOW (fd, 2 + 46) = - (char_dat[i].sp > 0x7fff) ? 0x7fff : char_dat[i].sp; - WFIFOW (fd, 2 + 48) = - (char_dat[i].max_sp > - 0x7fff) ? 0x7fff : char_dat[i].max_sp; - WFIFOW (fd, 2 + 50) = DEFAULT_WALK_SPEED; // char_dat[i].speed; - WFIFOW (fd, 2 + 52) = char_dat[i].pc_class; - WFIFOW (fd, 2 + 54) = char_dat[i].hair; - - WFIFOW (fd, 2 + 58) = char_dat[i].base_level; - WFIFOW (fd, 2 + 60) = char_dat[i].skill_point; - - WFIFOW (fd, 2 + 64) = char_dat[i].shield; - WFIFOW (fd, 2 + 66) = char_dat[i].head_top; - WFIFOW (fd, 2 + 68) = char_dat[i].head_mid; - WFIFOW (fd, 2 + 70) = char_dat[i].hair_color; - - memcpy (WFIFOP (fd, 2 + 74), char_dat[i].name, 24); - - WFIFOB (fd, 2 + 98) = - (char_dat[i].str > 255) ? 255 : char_dat[i].str; - WFIFOB (fd, 2 + 99) = - (char_dat[i].agi > 255) ? 255 : char_dat[i].agi; - WFIFOB (fd, 2 + 100) = - (char_dat[i].vit > 255) ? 255 : char_dat[i].vit; - WFIFOB (fd, 2 + 101) = - (char_dat[i].int_ > 255) ? 255 : char_dat[i].int_; - WFIFOB (fd, 2 + 102) = - (char_dat[i].dex > 255) ? 255 : char_dat[i].dex; - WFIFOB (fd, 2 + 103) = - (char_dat[i].luk > 255) ? 255 : char_dat[i].luk; - WFIFOB (fd, 2 + 104) = char_dat[i].char_num; - - WFIFOSET (fd, 108); - RFIFOSKIP (fd, 37); - for (ch = 0; ch < 9; ch++) - { - if (sd->found_char[ch] == -1) - { - sd->found_char[ch] = i; - break; - } - } - - case 0x68: // delete char //Yor's Fix - if (!sd || RFIFOREST (fd) < 46) - return; - memcpy (email, RFIFOP (fd, 6), 40); - if (e_mail_check (email) == 0) - strncpy (email, "a@a.com", 40); // default e-mail - - // if we activated email creation and email is default email - if (email_creation != 0 && strcmp (sd->email, "a@a.com") == 0 - && login_fd > 0) - { // to modify an e-mail, login-server must be online - // if sended email is incorrect e-mail - if (strcmp (email, "a@a.com") == 0) - { - WFIFOW (fd, 0) = 0x70; - WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 46); - // we act like we have selected a character - } - else - { - // we change the packet to set it like selection. - for (i = 0; i < 9; i++) - if (char_dat[sd->found_char[i]].char_id == - RFIFOL (fd, 2)) - { - // we save new e-mail - memcpy (sd->email, email, 40); - // we send new e-mail to login-server ('online' login-server is checked before) - WFIFOW (login_fd, 0) = 0x2715; - WFIFOL (login_fd, 2) = sd->account_id; - memcpy (WFIFOP (login_fd, 6), email, 40); - WFIFOSET (login_fd, 46); - // skip part of the packet! (46, but leave the size of select packet: 3) - RFIFOSKIP (fd, 43); - // change value to put new packet (char selection) - RFIFOW (fd, 0) = 0x66; - RFIFOB (fd, 2) = - char_dat[sd->found_char[i]].char_num; - // not send packet, it's modify of actual packet - break; - } - if (i == 9) - { - WFIFOW (fd, 0) = 0x70; - WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 46); - } - } - - // otherwise, we delete the character - } - else - { - /*if (strcasecmp(email, sd->email) != 0) { // if it's an invalid email - * WFIFOW(fd, 0) = 0x70; - * WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address - * WFIFOSET(fd, 3); - * // if mail is correct - * } else { */ - for (i = 0; i < 9; i++) - { - struct mmo_charstatus *cs = NULL; - if (sd->found_char[i] >= 0 - && (cs = - &char_dat[sd->found_char[i]])->char_id == - RFIFOL (fd, 2)) - { - char_delete (cs); // deletion process - - if (sd->found_char[i] != char_num - 1) - { - memcpy (&char_dat[sd->found_char[i]], - &char_dat[char_num - 1], - sizeof (struct mmo_charstatus)); - // Correct moved character reference in the character's owner - { - int j, k; - struct char_session_data *sd2; - for (j = 0; j < fd_max; j++) - { - if (session[j] - && (sd2 = (struct char_session_data*) - session[j]->session_data) - && sd2->account_id == - char_dat[char_num - 1].account_id) - { - for (k = 0; k < 9; k++) - { - if (sd2->found_char[k] == - char_num - 1) - { - sd2->found_char[k] = - sd->found_char[i]; - break; - } - } - break; - } - } - } - } - - char_num--; - for (ch = i; ch < 9 - 1; ch++) - sd->found_char[ch] = sd->found_char[ch + 1]; - sd->found_char[8] = -1; - WFIFOW (fd, 0) = 0x6f; - WFIFOSET (fd, 2); - break; - } - } - - if (i == 9) - { - WFIFOW (fd, 0) = 0x70; - WFIFOB (fd, 2) = 0; - WFIFOSET (fd, 3); - } - //} - RFIFOSKIP (fd, 46); - } - break; - - case 0x2af8: // マップサーバーログイン - if (RFIFOREST (fd) < 60) - return; - WFIFOW (fd, 0) = 0x2af9; - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - if (server_fd[i] < 0) - break; - } - if (i == MAX_MAP_SERVERS || strcmp (RFIFOP (fd, 2), userid) - || strcmp (RFIFOP (fd, 26), passwd)) - { - WFIFOB (fd, 2) = 3; - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 60); - } - else - { - int len; - WFIFOB (fd, 2) = 0; - session[fd]->func_parse = parse_frommap; - server_fd[i] = fd; - if (anti_freeze_enable) - server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed - server[i].ip = RFIFOL (fd, 54); - server[i].port = RFIFOW (fd, 58); - server[i].users = 0; - memset (server[i].map, 0, sizeof (server[i].map)); - WFIFOSET (fd, 3); - RFIFOSKIP (fd, 60); - realloc_fifo (fd, FIFOSIZE_SERVERLINK, - FIFOSIZE_SERVERLINK); - char_mapif_init (fd); - // send gm acccounts level to map-servers - len = 4; - WFIFOW (fd, 0) = 0x2b15; - for (i = 0; i < GM_num; i++) - { - WFIFOL (fd, len) = gm_account[i].account_id; - WFIFOB (fd, len + 4) = - (unsigned char) gm_account[i].level; - len += 5; - } - WFIFOW (fd, 2) = len; - WFIFOSET (fd, len); - return; - } - break; - - case 0x187: // Alive信号? - if (RFIFOREST (fd) < 6) - return; - RFIFOSKIP (fd, 6); - break; - - case 0x7530: // Athena情報所得 - 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_INTER | ATHENA_SERVER_CHAR; - WFIFOW (fd, 8) = ATHENA_MOD_VERSION; - WFIFOSET (fd, 10); - RFIFOSKIP (fd, 2); - return; - - case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) - session[fd]->eof = 1; - return; - - default: - session[fd]->eof = 1; - return; - } - } -} - -// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendall (char *buf, unsigned int len) -{ - int i, c; - - c = 0; - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - int fd; - if ((fd = server_fd[i]) >= 0) - { - memcpy (WFIFOP (fd, 0), buf, len); - WFIFOSET (fd, len); - c++; - } - } - return c; -} - -// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendallwos (int sfd, unsigned char *buf, unsigned int len) -{ - int i, c; - - c = 0; - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - int fd; - if ((fd = server_fd[i]) >= 0 && fd != sfd) - { - memcpy (WFIFOP (fd, 0), buf, len); - WFIFOSET (fd, len); - c++; - } - } - return c; -} - -// MAPサーバーにデータ送信(map鯖生存確認有り) -int mapif_send (int fd, unsigned char *buf, unsigned int len) -{ - int i; - - if (fd >= 0) - { - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - if (fd == server_fd[i]) - { - memcpy (WFIFOP (fd, 0), buf, len); - WFIFOSET (fd, len); - return 1; - } - } - } - return 0; -} - -void send_users_tologin (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int users = count_users (); - char buf[16]; - - if (login_fd > 0 && session[login_fd]) - { - // send number of user to login server - WFIFOW (login_fd, 0) = 0x2714; - WFIFOL (login_fd, 2) = users; - WFIFOSET (login_fd, 6); - } - // send number of players to all map-servers - WBUFW (buf, 0) = 0x2b00; - WBUFL (buf, 2) = users; - mapif_sendall (buf, 6); -} - -void check_connect_login_server (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - if (login_fd <= 0 || session[login_fd] == NULL) - { - printf ("Attempt to connect to login-server...\n"); - if ((login_fd = make_connection (login_ip, login_port)) < 0) - return; - session[login_fd]->func_parse = parse_tologin; - realloc_fifo (login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - WFIFOW (login_fd, 0) = 0x2710; - memset (WFIFOP (login_fd, 2), 0, 24); - memcpy (WFIFOP (login_fd, 2), userid, - strlen (userid) < 24 ? strlen (userid) : 24); - memset (WFIFOP (login_fd, 26), 0, 24); - memcpy (WFIFOP (login_fd, 26), passwd, - strlen (passwd) < 24 ? strlen (passwd) : 24); - WFIFOL (login_fd, 50) = 0; - WFIFOL (login_fd, 54) = char_ip; - WFIFOL (login_fd, 58) = char_port; - memset (WFIFOP (login_fd, 60), 0, 20); - memcpy (WFIFOP (login_fd, 60), server_name, - strlen (server_name) < 20 ? strlen (server_name) : 20); - WFIFOW (login_fd, 80) = 0; - WFIFOW (login_fd, 82) = char_maintenance; - WFIFOW (login_fd, 84) = char_new; - WFIFOSET (login_fd, 86); - } -} - -//---------------------------------------------------------- -// Return numerical value of a switch configuration by [Yor] -// on/off, english, français, deutsch, español -//---------------------------------------------------------- -int config_switch (const char *str) -{ - if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 - || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 - || strcasecmp (str, "si") == 0) - return 1; - if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 - || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) - return 0; - - return atoi (str); -} - -//------------------------------------------- -// Reading Lan Support configuration by [Yor] -//------------------------------------------- -int lan_config_read (const char *lancfgName) -{ - int j; - struct hostent *h = NULL; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - // set default configuration - strncpy (lan_map_ip, "127.0.0.1", sizeof (lan_map_ip)); - subneti[0] = 127; - subneti[1] = 0; - subneti[2] = 0; - subneti[3] = 1; - for (j = 0; j < 4; j++) - subnetmaski[j] = 255; - - fp = fopen_ (lancfgName, "r"); - - if (fp == NULL) - { - printf ("LAN support configuration file not found: %s\n", lancfgName); - return 1; - } - - printf ("---start reading of Lan Support configuration...\n"); - - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof (line) - 1] = '\0'; - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - remove_control_chars (w1); - remove_control_chars (w2); - if (strcasecmp (w1, "lan_map_ip") == 0) - { // Read map-server Lan IP Address - h = gethostbyname (w2); - if (h != NULL) - { - sprintf (lan_map_ip, "%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]); - } - else - { - strncpy (lan_map_ip, w2, sizeof (lan_map_ip)); - lan_map_ip[sizeof (lan_map_ip) - 1] = 0; - } - printf ("LAN IP of map-server: %s.\n", lan_map_ip); - } - else if (strcasecmp (w1, "subnet") == 0) - { // Read Subnetwork - for (j = 0; j < 4; j++) - subneti[j] = 0; - h = gethostbyname (w2); - if (h != NULL) - { - for (j = 0; j < 4; j++) - subneti[j] = (unsigned char) h->h_addr[j]; - } - else - { - sscanf (w2, "%d.%d.%d.%d", &subneti[0], &subneti[1], - &subneti[2], &subneti[3]); - } - printf ("Sub-network of the map-server: %d.%d.%d.%d.\n", - subneti[0], subneti[1], subneti[2], subneti[3]); - } - else if (strcasecmp (w1, "subnetmask") == 0) - { // Read Subnetwork Mask - for (j = 0; j < 4; j++) - subnetmaski[j] = 255; - h = gethostbyname (w2); - if (h != NULL) - { - for (j = 0; j < 4; j++) - subnetmaski[j] = (unsigned char) h->h_addr[j]; - } - else - { - sscanf (w2, "%d.%d.%d.%d", &subnetmaski[0], &subnetmaski[1], - &subnetmaski[2], &subnetmaski[3]); - } - printf ("Sub-network mask of the map-server: %d.%d.%d.%d.\n", - subnetmaski[0], subnetmaski[1], subnetmaski[2], - subnetmaski[3]); - } - } - fclose_ (fp); - - // sub-network check of the map-server - { - unsigned int a0, a1, a2, a3; - unsigned char p[4]; - sscanf (lan_map_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); - p[0] = a0; - p[1] = a1; - p[2] = a2; - p[3] = a3; - printf ("LAN test of LAN IP of the map-server: "); - if (lan_ip_check (p) == 0) - { - printf - ("\033[1;31m***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network.\033[0m\n"); - } - } - - printf ("---End reading of Lan Support configuration...\n"); - - return 0; -} - -int char_config_read (const char *cfgName) -{ - struct hostent *h = NULL; - char line[1024], w1[1024], w2[1024]; - FILE *fp = fopen_ (cfgName, "r"); - - if (fp == NULL) - { - printf ("Configuration file not found: %s.\n", cfgName); - exit (1); - } - - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof (line) - 1] = '\0'; - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - remove_control_chars (w1); - remove_control_chars (w2); - if (strcasecmp (w1, "userid") == 0) - { - memcpy (userid, w2, 24); - } - else if (strcasecmp (w1, "passwd") == 0) - { - memcpy (passwd, w2, 24); - } - else if (strcasecmp (w1, "server_name") == 0) - { - memcpy (server_name, w2, sizeof (server_name)); - server_name[sizeof (server_name) - 1] = '\0'; - printf ("%s server has been intialized\n", w2); - } - else if (strcasecmp (w1, "wisp_server_name") == 0) - { - if (strlen (w2) >= 4) - { - strncpy (wisp_server_name, w2, sizeof (wisp_server_name)); - wisp_server_name[sizeof (wisp_server_name) - 1] = '\0'; - } - } - else if (strcasecmp (w1, "login_ip") == 0) - { - h = gethostbyname (w2); - if (h != NULL) - { - printf ("Login server IP address : %s -> %d.%d.%d.%d\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 (login_ip_str, "%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]); - } - else - memcpy (login_ip_str, w2, 16); - } - else if (strcasecmp (w1, "login_port") == 0) - { - login_port = atoi (w2); - } - else if (strcasecmp (w1, "char_ip") == 0) - { - h = gethostbyname (w2); - if (h != NULL) - { - printf ("Character server IP address : %s -> %d.%d.%d.%d\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 (char_ip_str, "%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]); - } - else - memcpy (char_ip_str, w2, 16); - } - else if (strcasecmp (w1, "char_port") == 0) - { - char_port = atoi (w2); - } - else if (strcasecmp (w1, "char_maintenance") == 0) - { - char_maintenance = atoi (w2); - } - else if (strcasecmp (w1, "char_new") == 0) - { - char_new = atoi (w2); - } - else if (strcasecmp (w1, "email_creation") == 0) - { - email_creation = config_switch (w2); - } - else if (strcasecmp (w1, "char_txt") == 0) - { - strcpy (char_txt, w2); - } - else if (strcasecmp (w1, "backup_txt") == 0) - { //By zanetheinsane - strcpy (backup_txt, w2); - } - else if (strcasecmp (w1, "backup_txt_flag") == 0) - { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. By [Yor] - backup_txt_flag = config_switch (w2); - } - else if (strcasecmp (w1, "max_connect_user") == 0) - { - max_connect_user = atoi (w2); - if (max_connect_user < 0) - max_connect_user = 0; // unlimited online players - } - else if (strcasecmp (w1, "check_ip_flag") == 0) - { - check_ip_flag = config_switch (w2); - } - else if (strcasecmp (w1, "autosave_time") == 0) - { - autosave_interval = atoi (w2) * 1000; - if (autosave_interval <= 0) - autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; - } - else if (strcasecmp (w1, "start_point") == 0) - { - char map[32]; - int x, y; - if (sscanf (w2, "%[^,],%d,%d", map, &x, &y) < 3) - continue; - if (strstr (map, ".gat") != NULL) - { // Verify at least if '.gat' is in the map name - memcpy (start_point.map, map, 16); - start_point.x = x; - start_point.y = y; - } - } - else if (strcasecmp (w1, "start_zeny") == 0) - { - start_zeny = atoi (w2); - if (start_zeny < 0) - start_zeny = 0; - } - else if (strcasecmp (w1, "start_weapon") == 0) - { - start_weapon = atoi (w2); - if (start_weapon < 0) - start_weapon = 0; - } - else if (strcasecmp (w1, "start_armor") == 0) - { - start_armor = atoi (w2); - if (start_armor < 0) - start_armor = 0; - } - else if (strcasecmp (w1, "unknown_char_name") == 0) - { - strcpy (unknown_char_name, w2); - unknown_char_name[24] = 0; - } - else if (strcasecmp (w1, "char_log_filename") == 0) - { - strcpy (char_log_filename, w2); - } - else if (strcasecmp (w1, "name_ignoring_case") == 0) - { - name_ignoring_case = config_switch (w2); - } - else if (strcasecmp (w1, "char_name_option") == 0) - { - char_name_option = atoi (w2); - } - else if (strcasecmp (w1, "char_name_letters") == 0) - { - strcpy (char_name_letters, w2); -// online files options - } - else if (strcasecmp (w1, "online_txt_filename") == 0) - { - strcpy (online_txt_filename, w2); - } - else if (strcasecmp (w1, "online_html_filename") == 0) - { - strcpy (online_html_filename, w2); - } - else if (strcasecmp (w1, "online_sorting_option") == 0) - { - online_sorting_option = atoi (w2); - } - else if (strcasecmp (w1, "online_display_option") == 0) - { - online_display_option = atoi (w2); - } - else if (strcasecmp (w1, "online_gm_display_min_level") == 0) - { // minimum GM level to display 'GM' when we want to display it - online_gm_display_min_level = atoi (w2); - if (online_gm_display_min_level < 5) // send online file every 5 seconds to player is enough - online_gm_display_min_level = 5; - } - else if (strcasecmp (w1, "online_refresh_html") == 0) - { - online_refresh_html = atoi (w2); - if (online_refresh_html < 1) - online_refresh_html = 1; - } - else if (strcasecmp (w1, "anti_freeze_enable") == 0) - { - anti_freeze_enable = config_switch (w2); - } - else if (strcasecmp (w1, "anti_freeze_interval") == 0) - { - ANTI_FREEZE_INTERVAL = atoi (w2); - if (ANTI_FREEZE_INTERVAL < 5) - ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds - } - else if (strcasecmp (w1, "import") == 0) - { - char_config_read (w2); - } - } - fclose_ (fp); - - return 0; -} - -void term_func (void) -{ - int i; - - // write online players files with no player - for (i = 0; i < char_num; i++) - online_chars[i] = -1; - create_online_files (); - free (online_chars); - - mmo_char_sync (); - inter_save (); - - if (gm_account != NULL) - free (gm_account); - - free (char_dat); - delete_session (login_fd); - delete_session (char_fd); - - char_log ("----End of char-server (normal end with closing of all files).\n"); -} - -int do_init (int argc, char **argv) -{ - int i; - - // a newline in the log... - char_log (""); - char_log ("The char-server starting...\n"); - - char_config_read ((argc < 2) ? CHAR_CONF_NAME : argv[1]); - lan_config_read ((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME); - - login_ip = inet_addr (login_ip_str); - char_ip = inet_addr (char_ip_str); - - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - memset (&server[i], 0, sizeof (struct mmo_map_server)); - server_fd[i] = -1; - } - - mmo_char_init (); - - update_online = time (NULL); - create_online_files (); // update online players files at start of the server - - inter_init ((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 - -// set_termfunc (do_final); - set_defaultparse (parse_char); - - char_fd = make_listen_port (char_port); - -// add_timer_func_list (check_connect_login_server, "check_connect_login_server"); -// add_timer_func_list (send_users_tologin, "send_users_tologin"); -// add_timer_func_list (mmo_char_sync_timer, "mmo_char_sync_timer"); - - i = add_timer_interval (gettick () + 1000, check_connect_login_server, 0, - 0, 10 * 1000); - i = add_timer_interval (gettick () + 1000, send_users_tologin, 0, 0, - 5 * 1000); - i = add_timer_interval (gettick () + autosave_interval, - mmo_char_sync_timer, 0, 0, autosave_interval); - - if (anti_freeze_enable > 0) - { -// add_timer_func_list (map_anti_freeze_system, "map_anti_freeze_system"); - i = add_timer_interval (gettick () + 1000, map_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); // checks every X seconds user specifies - } - - char_log ("The char-server is ready (Server is listening on the port %d).\n", - char_port); - - printf - ("The char-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", - char_port); - - return 0; -} diff --git a/src/char/char.cpp b/src/char/char.cpp new file mode 100644 index 0000000..a57520b --- /dev/null +++ b/src/char/char.cpp @@ -0,0 +1,4076 @@ +// $Id: char.c,v 1.3 2004/09/13 16:52:16 Yor Exp $ +// original : char2.c 2003/03/14 11:58:35 Rev.1.5 + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdarg.h> +#include <sys/wait.h> + +#include "../common/core.hpp" +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/mmo.hpp" +#include "../common/version.hpp" +#include "../common/lock.hpp" +#include "char.hpp" + +#include "inter.hpp" +#include "int_guild.hpp" +#include "int_party.hpp" +#include "int_storage.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +struct mmo_map_server server[MAX_MAP_SERVERS]; +int server_fd[MAX_MAP_SERVERS]; +int server_freezeflag[MAX_MAP_SERVERS]; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 6; + +int login_fd, char_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char wisp_server_name[24] = "Server"; +char login_ip_str[16]; +int login_ip; +int login_port = 6900; +char char_ip_str[16]; +int char_ip; +int char_port = 6121; +int char_maintenance; +int char_new; +int email_creation = 0; // disabled by default +char char_txt[1024]; +char backup_txt[1024]; //By zanetheinsane +char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] +char unknown_char_name[1024] = "Unknown"; +char char_log_filename[1024] = "log/char.log"; +//Added for lan support +char lan_map_ip[128]; +int subneti[4]; +int subnetmaski[4]; +int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] +int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] +char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] + +struct char_session_data +{ + int account_id, login_id1, login_id2, sex; + unsigned short packet_tmw_version; + int found_char[9]; + char email[40]; // e-mail (default: a@a.com) by [Yor] + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +}; + +#define AUTH_FIFO_SIZE 256 +struct +{ + int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag, + sex; + unsigned short packet_tmw_version; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) + +int char_id_count = 150000; +struct mmo_charstatus *char_dat; +int char_num, char_max; +int max_connect_user = 0; +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int start_zeny = 500; +int start_weapon = 1201; +int start_armor = 1202; + +// Initial position (it's possible to set it in conf file) +struct point start_point = { "new_1-1.gat", 53, 111 }; + +struct gm_account *gm_account = NULL; +int GM_num = 0; + +// online players by [Yor] +char online_txt_filename[1024] = "online.txt"; +char online_html_filename[1024] = "online.html"; +int online_sorting_option = 0; // sorting option to display online players in online files +int online_display_option = 1; // display options: to know which columns must be displayed +int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer +int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it + +int *online_chars; // same size of char_dat, and id value of current server (or -1) +time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds) + +pid_t pid = 0; // For forked DB writes + +//------------------------------ +// Writing function of logs file +//------------------------------ +int char_log (char *fmt, ...) +{ + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start (ap, fmt); + + logfp = fopen_ (char_log_filename, "a"); + if (logfp) + { + if (fmt[0] == '\0') // jump a line if no message + fprintf (logfp, "\n"); + else + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 24, "%d-%m-%Y %H:%M:%S", gmtime (&(tv.tv_sec))); + sprintf (tmpstr + 19, ".%03d: %s", (int) tv.tv_usec / 1000, fmt); + vfprintf (logfp, tmpstr, ap); + } + fclose_ (logfp); + } + + va_end (ap); + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars (unsigned char *str) +{ + int i; + int change = 0; + + for (i = 0; str[i]; i++) + { + if (str[i] < 32) + { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM (int account_id) +{ + int i; + + for (i = 0; i < GM_num; i++) + if (gm_account[i].account_id == account_id) + return gm_account[i].level; + return 0; +} + +//---------------------------------------------- +// Search an character id +// (return character index or -1 (if not found)) +// If exact character name is not found, +// the function checks without case sensitive +// and returns index if only 1 character is found +// and similar to the searched name. +//---------------------------------------------- +int search_character_index (char *character_name) +{ + int i, quantity, index; + + quantity = 0; + index = -1; + for (i = 0; i < char_num; i++) + { + // Without case sensitive check (increase the number of similar character names found) + if (strcasecmp (char_dat[i].name, character_name) == 0) + { + // Strict comparison (if found, we finish the function immediatly with correct value) + if (strcmp (char_dat[i].name, character_name) == 0) + return i; + quantity++; + index = i; + } + } + // 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 index; + + // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found + return -1; +} + +//------------------------------------- +// Return character name with the index +//------------------------------------- +char *search_character_name (int index) +{ + + if (index >= 0 && index < char_num) + return char_dat[index].name; + + return unknown_char_name; +} + +//------------------------------------------------- +// Function to create the character line (for save) +//------------------------------------------------- +int mmo_char_tostr (char *str, struct mmo_charstatus *p) +{ + int i; + char *str_p = str; + + // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. + if (p->last_point.map[0] == '\0') + { + memcpy (p->last_point.map, "prontera.gat", 16); + p->last_point.x = 273; + p->last_point.y = 354; + } + + str_p += sprintf (str_p, "%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%s,%d,%d\t%s,%d,%d,%d\t", p->char_id, p->account_id, p->char_num, p->name, // + p->pc_class, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, p->hp, p->max_hp, p->sp, p->max_sp, p->str, p->agi, p->vit, p->int_, p->dex, p->luk, p->status_point, p->skill_point, p->option, p->karma, p->manner, // + p->party_id, p->guild_id, 0, p->hair, p->hair_color, p->clothes_color, p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, p->last_point.map, p->last_point.x, p->last_point.y, // + p->save_point.map, p->save_point.x, p->save_point.y, + p->partner_id); + for (i = 0; i < 10; i++) + if (p->memo_point[i].map[0]) + { + str_p += + sprintf (str_p, "%s,%d,%d", p->memo_point[i].map, + p->memo_point[i].x, p->memo_point[i].y); + } + *(str_p++) = '\t'; + + for (i = 0; i < MAX_INVENTORY; i++) + if (p->inventory[i].nameid) + { + str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->inventory[i].id, p->inventory[i].nameid, + p->inventory[i].amount, p->inventory[i].equip, + p->inventory[i].identify, + p->inventory[i].refine, + p->inventory[i].attribute, + p->inventory[i].card[0], + p->inventory[i].card[1], + p->inventory[i].card[2], + p->inventory[i].card[3], + p->inventory[i].broken); + } + *(str_p++) = '\t'; + + for (i = 0; i < MAX_CART; i++) + if (p->cart[i].nameid) + { + str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->cart[i].id, p->cart[i].nameid, + p->cart[i].amount, p->cart[i].equip, + p->cart[i].identify, p->cart[i].refine, + p->cart[i].attribute, p->cart[i].card[0], + p->cart[i].card[1], p->cart[i].card[2], + p->cart[i].card[3], p->cart[i].broken); + } + *(str_p++) = '\t'; + + for (i = 0; i < MAX_SKILL; i++) + if (p->skill[i].id) + { + str_p += + sprintf (str_p, "%d,%d ", p->skill[i].id, + p->skill[i].lv | (p->skill[i].flags << 16)); + } + *(str_p++) = '\t'; + + for (i = 0; i < p->global_reg_num; i++) + if (p->global_reg[i].str[0]) + str_p += + sprintf (str_p, "%s,%d ", p->global_reg[i].str, + p->global_reg[i].value); + *(str_p++) = '\t'; + + *str_p = '\0'; + return 0; +} + +//------------------------------------------------------------------------- +// Function to set the character from the line (at read of characters file) +//------------------------------------------------------------------------- +int mmo_char_fromstr (char *str, struct mmo_charstatus *p) +{ + int tmp_int[256]; + int set, next, len, i; + + // initilialise character + memset (p, '\0', sizeof (struct mmo_charstatus)); + + // If it's not char structure of version 1008 and after + if ((set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], + &tmp_int[39], &next)) != 43) + { + tmp_int[39] = 0; // partner id + // If not char structure from version 384 to 1007 + if ((set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], + &next)) != 42) + { + // It's char structure of a version before 384 + tmp_int[26] = 0; // pet id + set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], // + &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], + &next); + set += 2; + //printf("char: old char data ver.1\n"); + // Char structure of version 1007 or older + } + else + { + set++; + //printf("char: old char data ver.2\n"); + } + // Char structure of version 1008+ + } + else + { + //printf("char: new char data ver.3\n"); + } + if (set != 43) + return 0; + + p->char_id = tmp_int[0]; + p->account_id = tmp_int[1]; + p->char_num = tmp_int[2]; + p->pc_class = tmp_int[3]; + p->base_level = tmp_int[4]; + p->job_level = tmp_int[5]; + p->base_exp = tmp_int[6]; + p->job_exp = tmp_int[7]; + p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; + p->sp = tmp_int[11]; + p->max_sp = tmp_int[12]; + p->str = tmp_int[13]; + p->agi = tmp_int[14]; + p->vit = tmp_int[15]; + p->int_ = tmp_int[16]; + p->dex = tmp_int[17]; + p->luk = tmp_int[18]; + p->status_point = tmp_int[19]; + p->skill_point = tmp_int[20]; + p->option = tmp_int[21]; + p->karma = tmp_int[22]; + p->manner = tmp_int[23]; + p->party_id = tmp_int[24]; + p->guild_id = tmp_int[25]; +// p->pet_id = tmp_int[26]; + p->hair = tmp_int[27]; + p->hair_color = tmp_int[28]; + p->clothes_color = tmp_int[29]; + p->weapon = tmp_int[30]; + p->shield = tmp_int[31]; + p->head_top = tmp_int[32]; + p->head_mid = tmp_int[33]; + p->head_bottom = tmp_int[34]; + p->last_point.x = tmp_int[35]; + p->last_point.y = tmp_int[36]; + p->save_point.x = tmp_int[37]; + p->save_point.y = tmp_int[38]; + p->partner_id = tmp_int[39]; + + // Some checks + for (i = 0; i < char_num; i++) + { + if (char_dat[i].char_id == p->char_id) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: a character has an identical id to another.\n"); + printf + (" character id #%d -> new character not readed.\n", + p->char_id); + printf (" Character saved in log file.\033[0m\n"); + return -1; + } + else if (strcmp (char_dat[i].name, p->name) == 0) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: character name already exists.\n"); + printf + (" character name '%s' -> new character not readed.\n", + p->name); + printf (" Character saved in log file.\033[0m\n"); + return -2; + } + } + + if (strcasecmp (wisp_server_name, p->name) == 0) + { + printf + ("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); + printf + (" Character name '%s' = wisp server name '%s'.\n", + p->name, wisp_server_name); + printf + (" Character readed. Suggestion: change the wisp server name.\n"); + char_log + ("mmo_auth_init: ******WARNING: character name has wisp server name: Character name '%s' = wisp server name '%s'.\n", + p->name, wisp_server_name); + } + + if (str[next] == '\n' || str[next] == '\r') + return 1; // 新規データ + + next++; + + for (i = 0; str[next] && str[next] != '\t'; i++) + { + if (sscanf + (str + next, "%[^,],%d,%d%n", p->memo_point[i].map, &tmp_int[0], + &tmp_int[1], &len) != 3) + return -3; + p->memo_point[i].x = tmp_int[0]; + p->memo_point[i].y = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for (i = 0; str[next] && str[next] != '\t'; i++) + { + if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &tmp_int[11], &len) == 12) + { + // do nothing, it's ok + } + else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len) == 11) + { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } + else // invalid structure + return -4; + p->inventory[i].id = tmp_int[0]; + p->inventory[i].nameid = tmp_int[1]; + p->inventory[i].amount = tmp_int[2]; + p->inventory[i].equip = tmp_int[3]; + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; + p->inventory[i].card[0] = tmp_int[7]; + p->inventory[i].card[1] = tmp_int[8]; + p->inventory[i].card[2] = tmp_int[9]; + p->inventory[i].card[3] = tmp_int[10]; + p->inventory[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for (i = 0; str[next] && str[next] != '\t'; i++) + { + if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &tmp_int[11], &len) == 12) + { + // do nothing, it's ok + } + else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len) == 11) + { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } + else // invalid structure + return -5; + p->cart[i].id = tmp_int[0]; + p->cart[i].nameid = tmp_int[1]; + p->cart[i].amount = tmp_int[2]; + p->cart[i].equip = tmp_int[3]; + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; + p->cart[i].card[0] = tmp_int[7]; + p->cart[i].card[1] = tmp_int[8]; + p->cart[i].card[2] = tmp_int[9]; + p->cart[i].card[3] = tmp_int[10]; + p->cart[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for (i = 0; str[next] && str[next] != '\t'; i++) + { + if (sscanf (str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != + 2) + return -6; + p->skill[tmp_int[0]].id = tmp_int[0]; + p->skill[tmp_int[0]].lv = tmp_int[1] & 0xffff; + p->skill[tmp_int[0]].flags = ((tmp_int[1] >> 16) & 0xffff); + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for (i = 0; + str[next] && str[next] != '\t' && str[next] != '\n' + && str[next] != '\r'; i++) + { // global_reg実装以前のathena.txt互換のため一応'\n'チェック + if (sscanf + (str + next, "%[^,],%d%n", p->global_reg[i].str, + &p->global_reg[i].value, &len) != 2) + { + // because some scripts are not correct, the str can be "". So, we must check that. + // If it's, we must not refuse the character, but just this REG value. + // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) + if (str[next] == ',' + && sscanf (str + next, ",%d%n", &p->global_reg[i].value, + &len) == 1) + i--; + else + return -7; + } + next += len; + if (str[next] == ' ') + next++; + } + p->global_reg_num = i; + + return 1; +} + +//--------------------------------- +// Function to read characters file +//--------------------------------- +int mmo_char_init (void) +{ + char line[65536]; + int i; + int ret, line_count; + FILE *fp; + + char_max = 256; + CREATE (char_dat, struct mmo_charstatus, 256); + CREATE (online_chars, int, 256); + for (i = 0; i < char_max; i++) + online_chars[i] = -1; + + char_num = 0; + + fp = fopen_ (char_txt, "r"); + if (fp == NULL) + { + printf ("Characters file not found: %s.\n", char_txt); + char_log ("Characters file not found: %s.\n", char_txt); + char_log ("Id for the next created character: %d.\n", + char_id_count); + return 0; + } + + line_count = 0; + while (fgets (line, sizeof (line) - 1, fp)) + { + int i, j; + line_count++; + + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof (line) - 1] = '\0'; + + j = 0; + if (sscanf (line, "%d\t%%newid%%%n", &i, &j) == 1 && j > 0) + { + if (char_id_count < i) + char_id_count = i; + continue; + } + + if (char_num >= char_max) + { + char_max += 256; + RECREATE (char_dat, struct mmo_charstatus, char_max); + RECREATE (online_chars, int, char_max); + for (i = char_max - 256; i < char_max; i++) + online_chars[i] = -1; + } + + ret = mmo_char_fromstr (line, &char_dat[char_num]); + if (ret > 0) + { // negative value or zero for errors + if (char_dat[char_num].char_id >= char_id_count) + char_id_count = char_dat[char_num].char_id + 1; + char_num++; + } + else + { + printf + ("mmo_char_init: in characters file, unable to read the line #%d.\n", + line_count); + printf (" -> Character saved in log file.\n"); + switch (ret) + { + case -1: + char_log + ("Duplicate character id in the next character line (character not readed):\n"); + break; + case -2: + char_log + ("Duplicate character name in the next character line (character not readed):\n"); + break; + case -3: + char_log + ("Invalid memo point structure in the next character line (character not readed):\n"); + break; + case -4: + char_log + ("Invalid inventory item structure in the next character line (character not readed):\n"); + break; + case -5: + char_log + ("Invalid cart item structure in the next character line (character not readed):\n"); + break; + case -6: + char_log + ("Invalid skill structure in the next character line (character not readed):\n"); + break; + case -7: + char_log + ("Invalid register structure in the next character line (character not readed):\n"); + break; + default: // 0 + char_log + ("Unabled to get a character in the next line - Basic structure of line (before inventory) is incorrect (character not readed):\n"); + break; + } + char_log ("%s", line); + } + } + fclose_ (fp); + + if (char_num == 0) + { + printf ("mmo_char_init: No character found in %s.\n", char_txt); + char_log ("mmo_char_init: No character found in %s.\n", + char_txt); + } + else if (char_num == 1) + { + printf ("mmo_char_init: 1 character read in %s.\n", char_txt); + char_log ("mmo_char_init: 1 character read in %s.\n", char_txt); + } + else + { + printf ("mmo_char_init: %d characters read in %s.\n", char_num, + char_txt); + char_log ("mmo_char_init: %d characters read in %s.\n", + char_num, char_txt); + } + + char_log ("Id for the next created character: %d.\n", + char_id_count); + + return 0; +} + +//--------------------------------------------------------- +// Function to save characters in files (speed up by [Yor]) +//--------------------------------------------------------- +void mmo_char_sync (void) +{ + char line[65536]; + int i, j, k; + int lock; + FILE *fp; + int id[char_num]; + + // Sorting before save (by [Yor]) + for (i = 0; i < char_num; i++) + { + id[i] = i; + for (j = 0; j < i; j++) + { + if ((char_dat[i].account_id < char_dat[id[j]].account_id) || + // if same account id, we sort by slot. + (char_dat[i].account_id == char_dat[id[j]].account_id && + char_dat[i].char_num < char_dat[id[j]].char_num)) + { + for (k = i; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + fp = lock_fopen (char_txt, &lock); + if (fp == NULL) + { + printf ("WARNING: Server can't not save characters.\n"); + char_log ("WARNING: Server can't not save characters.\n"); + } + else + { + for (i = 0; i < char_num; i++) + { + // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) + mmo_char_tostr (line, &char_dat[id[i]]); // use of sorted index + fprintf (fp, "%s\n", line); + } + fprintf (fp, "%d\t%%newid%%\n", char_id_count); + lock_fclose (fp, char_txt, &lock); + } + + // Data save (backup) + if (backup_txt_flag) + { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] + fp = lock_fopen (backup_txt, &lock); + if (fp == NULL) + { + printf + ("WARNING: Server can't not create backup of characters file.\n"); + char_log + ("WARNING: Server can't not create backup of characters file.\n"); + return; + } + for (i = 0; i < char_num; i++) + { + // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) + mmo_char_tostr (line, &char_dat[id[i]]); // use of sorted index + fprintf (fp, "%s\n", line); + } + fprintf (fp, "%d\t%%newid%%\n", char_id_count); + lock_fclose (fp, backup_txt, &lock); + } + + return; +} + +//---------------------------------------------------- +// Function to save (in a periodic way) datas in files +//---------------------------------------------------- +void mmo_char_sync_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + if (pid != 0) + { + int status; + pid_t temp = waitpid (pid, &status, WNOHANG); + + // Need to check status too? + if (temp == 0) + { + return; + } + } + + // This can take a lot of time. Fork a child to handle the work and return at once + // If we're unable to fork just continue running the function normally + if ((pid = fork ()) > 0) + return; + + mmo_char_sync (); + inter_save (); + + // If we're a child we should suicide now. + if (pid == 0) + _exit (0); +} + +//---------------------------------------------------- +// Remove trailing whitespace from a name +//---------------------------------------------------- +static void remove_trailing_blanks (char *name) +{ + char *tail = name + strlen (name) - 1; + + while (tail > name && *tail == ' ') + *tail-- = 0; +} + +//---------------------------------------------------- +// Remove prefix whitespace from a name +//---------------------------------------------------- +static void remove_prefix_blanks (char *name) +{ + char *dst = name; + char *src = name; + + while (*src == ' ') // find first nonblank + ++src; + while ((*dst++ = *src++)); // `strmove' +} + +//----------------------------------- +// Function to create a new character +//----------------------------------- +int make_new_char (int fd, unsigned char *dat) +{ + int i, j; + struct char_session_data *sd = (struct char_session_data *)session[fd]->session_data; + + // remove control characters from the name + dat[23] = '\0'; + if (remove_control_chars (dat)) + { + char_log + ("Make new char error (control char received in the name): (connection #%d, account: %d).\n", + fd, sd->account_id); + return -1; + } + + // Eliminate whitespace + remove_trailing_blanks ((char *) dat); + remove_prefix_blanks ((char *) dat); + + // check lenght of character name + if (strlen (dat) < 4) + { + char_log + ("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", + fd, sd->account_id, dat); + return -1; + } + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) + { // only letters/symbols in char_name_letters are authorised + for (i = 0; dat[i]; i++) + if (strchr (char_name_letters, dat[i]) == NULL) + { + char_log + ("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", + fd, sd->account_id, dat, dat[i]); + return -1; + } + } + else if (char_name_option == 2) + { // letters/symbols in char_name_letters are forbidden + for (i = 0; dat[i]; i++) + if (strchr (char_name_letters, dat[i]) != NULL) + { + char_log + ("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", + fd, sd->account_id, dat, dat[i]); + return -1; + } + } // else, all letters/symbols are authorised (except control char removed before) + + if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5 * 6 || // stats + dat[30] >= 9 || // slots (dat[30] can not be negativ) + dat[33] < 0 || dat[33] >= 20 || // hair style + dat[31] >= 12) + { // hair color (dat[31] can not be negativ) + char_log + ("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", + fd, sd->account_id, dat[30], dat, dat[24], dat[25], + dat[26], dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], + dat[33], dat[31]); + return -1; + } + + // check individual stat value + for (i = 24; i <= 29; i++) + { + if (dat[i] < 1 || dat[i] > 9) + { + char_log + ("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", + fd, sd->account_id, dat[30], dat, dat[24], dat[25], + dat[26], dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], + dat[33], dat[31]); + return -1; + } + } + + for (i = 0; i < char_num; i++) + { + if ((name_ignoring_case != 0 && strcmp (char_dat[i].name, dat) == 0) + || (name_ignoring_case == 0 + && strcasecmp (char_dat[i].name, dat) == 0)) + { + char_log + ("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", + fd, sd->account_id, dat[30], dat, char_dat[i].name, + dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], + dat[33], dat[31]); + return -1; + } + if (char_dat[i].account_id == sd->account_id + && char_dat[i].char_num == dat[30]) + { + char_log + ("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", + fd, sd->account_id, dat[30], dat, char_dat[i].name, + dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], + dat[33], dat[31]); + return -1; + } + } + + if (strcmp (wisp_server_name, dat) == 0) + { + char_log + ("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", + fd, sd->account_id, dat[30], dat, char_dat[i].name, + dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], + dat[33], dat[31]); + return -1; + } + + if (char_num >= char_max) + { + char_max += 256; + RECREATE (char_dat, struct mmo_charstatus, char_max); + RECREATE (online_chars, int, char_max); + for (j = char_max - 256; j < char_max; j++) + online_chars[j] = -1; + } + + char ip[16]; + unsigned char *sin_addr = + (unsigned char *) &session[fd]->client_addr.sin_addr; + sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], + sin_addr[3]); + + char_log + ("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d. [%s]\n", + fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], + dat[27], dat[28], dat[29], + dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], + dat[31], ip); + + memset (&char_dat[i], 0, sizeof (struct mmo_charstatus)); + + char_dat[i].char_id = char_id_count++; + char_dat[i].account_id = sd->account_id; + char_dat[i].char_num = dat[30]; + strcpy (char_dat[i].name, dat); + char_dat[i].pc_class = 0; + char_dat[i].base_level = 1; + char_dat[i].job_level = 1; + char_dat[i].base_exp = 0; + char_dat[i].job_exp = 0; + char_dat[i].zeny = start_zeny; + char_dat[i].str = dat[24]; + char_dat[i].agi = dat[25]; + char_dat[i].vit = dat[26]; + char_dat[i].int_ = dat[27]; + char_dat[i].dex = dat[28]; + char_dat[i].luk = dat[29]; + char_dat[i].max_hp = 40 * (100 + char_dat[i].vit) / 100; + char_dat[i].max_sp = 11 * (100 + char_dat[i].int_) / 100; + char_dat[i].hp = char_dat[i].max_hp; + char_dat[i].sp = char_dat[i].max_sp; + char_dat[i].status_point = 0; + char_dat[i].skill_point = 0; + char_dat[i].option = 0; + char_dat[i].karma = 0; + char_dat[i].manner = 0; + char_dat[i].party_id = 0; + char_dat[i].guild_id = 0; + char_dat[i].hair = dat[33]; + char_dat[i].hair_color = dat[31]; + char_dat[i].clothes_color = 0; + char_dat[i].inventory[0].nameid = start_weapon; // Knife + char_dat[i].inventory[0].amount = 1; + char_dat[i].inventory[0].equip = 0x02; + char_dat[i].inventory[0].identify = 1; + char_dat[i].inventory[0].broken = 0; + char_dat[i].inventory[1].nameid = start_armor; // Cotton Shirt + char_dat[i].inventory[1].amount = 1; + char_dat[i].inventory[1].equip = 0x10; + char_dat[i].inventory[1].identify = 1; + char_dat[i].inventory[1].broken = 0; + char_dat[i].weapon = 1; + char_dat[i].shield = 0; + char_dat[i].head_top = 0; + char_dat[i].head_mid = 0; + char_dat[i].head_bottom = 0; + memcpy (&char_dat[i].last_point, &start_point, sizeof (start_point)); + memcpy (&char_dat[i].save_point, &start_point, sizeof (start_point)); + char_num++; + + return i; +} + +//---------------------------------------------------- +// This function return the name of the job (by [Yor]) +//---------------------------------------------------- +char *job_name (int pc_class) +{ + switch (pc_class) + { + case 0: + return "Novice"; + case 1: + return "Swordsman"; + case 2: + return "Mage"; + case 3: + return "Archer"; + case 4: + return "Acolyte"; + case 5: + return "Merchant"; + case 6: + return "Thief"; + case 7: + return "Knight"; + case 8: + return "Priest"; + case 9: + return "Wizard"; + case 10: + return "Blacksmith"; + case 11: + return "Hunter"; + case 12: + return "Assassin"; + case 13: + return "Knight 2"; + case 14: + return "Crusader"; + case 15: + return "Monk"; + case 16: + return "Sage"; + case 17: + return "Rogue"; + case 18: + return "Alchemist"; + case 19: + return "Bard"; + case 20: + return "Dancer"; + case 21: + return "Crusader 2"; + case 22: + return "Wedding"; + case 23: + return "Super Novice"; + case 4001: + return "Novice High"; + case 4002: + return "Swordsman High"; + case 4003: + return "Mage High"; + case 4004: + return "Archer High"; + case 4005: + return "Acolyte High"; + case 4006: + return "Merchant High"; + case 4007: + return "Thief High"; + case 4008: + return "Lord Knight"; + case 4009: + return "High Priest"; + case 4010: + return "High Wizard"; + case 4011: + return "Whitesmith"; + case 4012: + return "Sniper"; + case 4013: + return "Assassin Cross"; + case 4014: + return "Peko Knight"; + case 4015: + return "Paladin"; + case 4016: + return "Champion"; + case 4017: + return "Professor"; + case 4018: + return "Stalker"; + case 4019: + return "Creator"; + case 4020: + return "Clown"; + case 4021: + return "Gypsy"; + case 4022: + return "Peko Paladin"; + case 4023: + return "Baby Novice"; + case 4024: + return "Baby Swordsman"; + case 4025: + return "Baby Mage"; + case 4026: + return "Baby Archer"; + case 4027: + return "Baby Acolyte"; + case 4028: + return "Baby Merchant"; + case 4029: + return "Baby Thief"; + case 4030: + return "Baby Knight"; + case 4031: + return "Baby Priest"; + case 4032: + return "Baby Wizard"; + case 4033: + return "Baby Blacksmith"; + case 4034: + return "Baby Hunter"; + case 4035: + return "Baby Assassin"; + case 4036: + return "Baby Peco Knight"; + case 4037: + return "Baby Crusader"; + case 4038: + return "Baby Monk"; + case 4039: + return "Baby Sage"; + case 4040: + return "Baby Rogue"; + case 4041: + return "Baby Alchemist"; + case 4042: + return "Baby Bard"; + case 4043: + return "Baby Dancer"; + case 4044: + return "Baby Peco Crusader"; + case 4045: + return "Super Baby"; + } + return "Unknown Job"; +} + +//------------------------------------------------------------- +// Function to create the online files (txt and html). by [Yor] +//------------------------------------------------------------- +void create_online_files (void) +{ + int i, j, k, l; // for loops + int players; // count the number of players + FILE *fp; // for the txt file + FILE *fp2; // for the html file + char temp[256]; // to prepare what we must display + time_t time_server; // for number of seconds + struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... + int id[char_num]; + + if (online_display_option == 0) // we display nothing, so return + return; + + //char_log("Creation of online players files.\n"); + + // Get number of online players, id of each online players + players = 0; + // sort online characters. + for (i = 0; i < char_num; i++) + { + if (online_chars[i] != -1) + { + id[players] = i; + // use sorting option + switch (online_sorting_option) + { + case 1: // by name (without case sensitive) + { + char *p_name = char_dat[i].name; //speed up sorting when there are a lot of players. But very rarely players have same name. + for (j = 0; j < players; j++) + if (strcasecmp (p_name, char_dat[id[j]].name) < 0 || + // if same name, we sort with case sensitive. + (strcasecmp (p_name, char_dat[id[j]].name) == 0 && + strcmp (p_name, char_dat[id[j]].name) < 0)) + { + for (k = players; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[players] + break; + } + } + break; + case 2: // by zeny + for (j = 0; j < players; j++) + if (char_dat[i].zeny < char_dat[id[j]].zeny || + // if same number of zenys, we sort by name. + (char_dat[i].zeny == char_dat[id[j]].zeny && + strcasecmp (char_dat[i].name, + char_dat[id[j]].name) < 0)) + { + for (k = players; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[players] + break; + } + break; + case 3: // by base level + for (j = 0; j < players; j++) + if (char_dat[i].base_level < + char_dat[id[j]].base_level || + // if same base level, we sort by base exp. + (char_dat[i].base_level == + char_dat[id[j]].base_level + && char_dat[i].base_exp < + char_dat[id[j]].base_exp)) + { + for (k = players; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[players] + break; + } + break; + case 4: // by job (and job level) + for (j = 0; j < players; j++) + if (char_dat[i].pc_class < char_dat[id[j]].pc_class || + // if same job, we sort by job level. + (char_dat[i].pc_class == char_dat[id[j]].pc_class && + char_dat[i].job_level < + char_dat[id[j]].job_level) || + // if same job and job level, we sort by job exp. + (char_dat[i].pc_class == char_dat[id[j]].pc_class && + char_dat[i].job_level == + char_dat[id[j]].job_level + && char_dat[i].job_exp < + char_dat[id[j]].job_exp)) + { + for (k = players; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[players] + break; + } + break; + case 5: // by location map name + { + int cpm_result; // A lot of player maps are identical. So, test if done often twice. + for (j = 0; j < players; j++) + if ((cpm_result = strcmp (char_dat[i].last_point.map, char_dat[id[j]].last_point.map)) < 0 || // no map are identical and with upper cases (not use strcasecmp) + // if same map name, we sort by name. + (cpm_result == 0 && + strcasecmp (char_dat[i].name, + char_dat[id[j]].name) < 0)) + { + for (k = players; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[players] + break; + } + } + break; + default: // 0 or invalid value: no sorting + break; + } + players++; + } + } + + // write files + fp = fopen_ (online_txt_filename, "w"); + if (fp != NULL) + { + fp2 = fopen_ (online_html_filename, "w"); + if (fp2 != NULL) + { + // get time + time (&time_server); // get time in seconds since 1/1/1970 + datetime = localtime (&time_server); // convert seconds in structure + strftime (temp, sizeof (temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52) + // write heading + fprintf (fp2, "<HTML>\n"); + fprintf (fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds + fprintf (fp2, " <HEAD>\n"); + fprintf (fp2, " <TITLE>Online Players on %s</TITLE>\n", + server_name); + fprintf (fp2, " </HEAD>\n"); + fprintf (fp2, " <BODY>\n"); + fprintf (fp2, " <H3>Online Players on %s (%s):</H3>\n", + server_name, temp); + fprintf (fp, "Online Players on %s (%s):\n", server_name, temp); + fprintf (fp, "\n"); + + // If we display at least 1 player + if (players > 0) + { + j = 0; // count the number of characters for the txt version and to set the separate line + fprintf (fp2, " <table border=\"1\" cellspacing=\"1\">\n"); + fprintf (fp2, " <tr>\n"); + if ((online_display_option & 1) + || (online_display_option & 64)) + { + fprintf (fp2, " <td><b>Name</b></td>\n"); + if (online_display_option & 64) + { + fprintf (fp, "Name "); // 30 + j += 30; + } + else + { + fprintf (fp, "Name "); // 25 + j += 25; + } + } + if ((online_display_option & 6) == 6) + { + fprintf (fp2, " <td><b>Job (levels)</b></td>\n"); + fprintf (fp, "Job Levels "); // 27 + j += 27; + } + else if (online_display_option & 2) + { + fprintf (fp2, " <td><b>Job</b></td>\n"); + fprintf (fp, "Job "); // 19 + j += 19; + } + else if (online_display_option & 4) + { + fprintf (fp2, " <td><b>Levels</b></td>\n"); + fprintf (fp, " Levels "); // 8 + j += 8; + } + if (online_display_option & 24) + { // 8 or 16 + fprintf (fp2, " <td><b>Location</b></td>\n"); + if (online_display_option & 16) + { + fprintf (fp, "Location ( x , y ) "); // 23 + j += 23; + } + else + { + fprintf (fp, "Location "); // 13 + j += 13; + } + } + if (online_display_option & 32) + { + fprintf (fp2, + " <td ALIGN=CENTER><b>zenys</b></td>\n"); + fprintf (fp, " Zenys "); // 16 + j += 16; + } + fprintf (fp2, " </tr>\n"); + fprintf (fp, "\n"); + for (k = 0; k < j; k++) + fprintf (fp, "-"); + fprintf (fp, "\n"); + + // display each player. + for (i = 0; i < players; i++) + { + // get id of the character (more speed) + j = id[i]; + fprintf (fp2, " <tr>\n"); + // displaying the character name + if ((online_display_option & 1) + || (online_display_option & 64)) + { // without/with 'GM' display + strcpy (temp, char_dat[j].name); + l = isGM (char_dat[j].account_id); + if (online_display_option & 64) + { + if (l >= online_gm_display_min_level) + fprintf (fp, "%-24s (GM) ", temp); + else + fprintf (fp, "%-24s ", temp); + } + else + fprintf (fp, "%-24s ", temp); + // name of the character in the html (no < >, because that create problem in html code) + fprintf (fp2, " <td>"); + if ((online_display_option & 64) + && l >= online_gm_display_min_level) + fprintf (fp2, "<b>"); + for (k = 0; temp[k]; k++) + { + switch (temp[k]) + { + case '<': // < + fprintf (fp2, "<"); + break; + case '>': // > + fprintf (fp2, ">"); + break; + default: + fprintf (fp2, "%c", temp[k]); + break; + }; + } + if ((online_display_option & 64) + && l >= online_gm_display_min_level) + fprintf (fp2, "</b> (GM)"); + fprintf (fp2, "</td>\n"); + } + // displaying of the job + if (online_display_option & 6) + { + char *jobname = job_name (char_dat[j].pc_class); + if ((online_display_option & 6) == 6) + { + fprintf (fp2, " <td>%s %d/%d</td>\n", + jobname, char_dat[j].base_level, + char_dat[j].job_level); + fprintf (fp, "%-18s %3d/%3d ", jobname, + char_dat[j].base_level, + char_dat[j].job_level); + } + else if (online_display_option & 2) + { + fprintf (fp2, " <td>%s</td>\n", jobname); + fprintf (fp, "%-18s ", jobname); + } + else if (online_display_option & 4) + { + fprintf (fp2, " <td>%d/%d</td>\n", + char_dat[j].base_level, + char_dat[j].job_level); + fprintf (fp, "%3d/%3d ", char_dat[j].base_level, + char_dat[j].job_level); + } + } + // displaying of the map + if (online_display_option & 24) + { // 8 or 16 + // prepare map name + memset (temp, 0, sizeof (temp)); + strncpy (temp, char_dat[j].last_point.map, 16); + if (strchr (temp, '.') != NULL) + temp[strchr (temp, '.') - temp] = '\0'; // suppress the '.gat' + // write map name + if (online_display_option & 16) + { // map-name AND coordonates + fprintf (fp2, " <td>%s (%d, %d)</td>\n", + temp, char_dat[j].last_point.x, + char_dat[j].last_point.y); + fprintf (fp, "%-12s (%3d,%3d) ", temp, + char_dat[j].last_point.x, + char_dat[j].last_point.y); + } + else + { + fprintf (fp2, " <td>%s</td>\n", temp); + fprintf (fp, "%-12s ", temp); + } + } + // displaying number of zenys + if (online_display_option & 32) + { + // write number of zenys + if (char_dat[j].zeny == 0) + { // if no zeny + fprintf (fp2, + " <td ALIGN=RIGHT>no zeny</td>\n"); + fprintf (fp, " no zeny "); + } + else + { + fprintf (fp2, + " <td ALIGN=RIGHT>%d z</td>\n", + char_dat[j].zeny); + fprintf (fp, "%13d z ", char_dat[j].zeny); + } + } + fprintf (fp, "\n"); + fprintf (fp2, " </tr>\n"); + } + fprintf (fp2, " </table>\n"); + fprintf (fp, "\n"); + } + + // Displaying number of online players + if (players == 0) + { + fprintf (fp2, " <p>No user is online.</p>\n"); + fprintf (fp, "No user is online.\n"); + // no display if only 1 player + } + else if (players == 1) + { + } + else + { + fprintf (fp2, " <p>%d users are online.</p>\n", players); + fprintf (fp, "%d users are online.\n", players); + } + fprintf (fp2, " </BODY>\n"); + fprintf (fp2, "</HTML>\n"); + fclose_ (fp2); + } + fclose_ (fp); + } + + return; +} + +//--------------------------------------------------------------------- +// This function return the number of online players in all map-servers +//--------------------------------------------------------------------- +int count_users (void) +{ + int i, users; + + users = 0; + for (i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + users += server[i].users; + + return users; +} + +//---------------------------------------- +// [Fate] Find inventory item based on equipment mask, return view. ID must match view ID (!). +//---------------------------------------- +static int find_equip_view (struct mmo_charstatus *p, unsigned int equipmask) +{ + int i; + for (i = 0; i < MAX_INVENTORY; i++) + if (p->inventory[i].nameid && p->inventory[i].amount + && p->inventory[i].equip & equipmask) + return p->inventory[i].nameid; + return 0; +} + +//---------------------------------------- +// Function to send characters to a player +//---------------------------------------- +int mmo_char_send006b (int fd, struct char_session_data *sd) +{ + int i, j, found_num; + struct mmo_charstatus *p; + const int offset = 24; + + found_num = 0; + for (i = 0; i < char_num; i++) + { + if (char_dat[i].account_id == sd->account_id) + { + sd->found_char[found_num] = i; + found_num++; + if (found_num == 9) + break; + } + } + for (i = found_num; i < 9; i++) + sd->found_char[i] = -1; + + memset (WFIFOP (fd, 0), 0, offset + found_num * 106); + WFIFOW (fd, 0) = 0x6b; + WFIFOW (fd, 2) = offset + found_num * 106; + + for (i = 0; i < found_num; i++) + { + p = &char_dat[sd->found_char[i]]; + j = offset + (i * 106); // increase speed of code + + WFIFOL (fd, j) = p->char_id; + WFIFOL (fd, j + 4) = p->base_exp; + WFIFOL (fd, j + 8) = p->zeny; + WFIFOL (fd, j + 12) = p->job_exp; + WFIFOL (fd, j + 16) = 0; //p->job_level; // [Fate] We no longer reveal this to the player, as its meaning is weird. + + WFIFOW (fd, j + 20) = find_equip_view (p, 0x0040); // 9: shoes + WFIFOW (fd, j + 22) = find_equip_view (p, 0x0004); // 10: gloves + WFIFOW (fd, j + 24) = find_equip_view (p, 0x0008); // 11: cape + WFIFOW (fd, j + 26) = find_equip_view (p, 0x0010); // 12: misc1 + WFIFOL (fd, j + 28) = p->option; + + WFIFOL (fd, j + 32) = p->karma; + WFIFOL (fd, j + 36) = p->manner; + + WFIFOW (fd, j + 40) = p->status_point; + WFIFOW (fd, j + 42) = (p->hp > 0x7fff) ? 0x7fff : p->hp; + WFIFOW (fd, j + 44) = (p->max_hp > 0x7fff) ? 0x7fff : p->max_hp; + WFIFOW (fd, j + 46) = (p->sp > 0x7fff) ? 0x7fff : p->sp; + WFIFOW (fd, j + 48) = (p->max_sp > 0x7fff) ? 0x7fff : p->max_sp; + WFIFOW (fd, j + 50) = DEFAULT_WALK_SPEED; // p->speed; + WFIFOW (fd, j + 52) = p->pc_class; + WFIFOW (fd, j + 54) = p->hair; +// WFIFOW(fd,j+56) = p->weapon; // dont send weapon since TMW does not support it + WFIFOW (fd, j + 56) = 0; + WFIFOW (fd, j + 58) = p->base_level; + WFIFOW (fd, j + 60) = p->skill_point; + WFIFOW (fd, j + 62) = p->head_bottom; + WFIFOW (fd, j + 64) = p->shield; + WFIFOW (fd, j + 66) = p->head_top; + WFIFOW (fd, j + 68) = p->head_mid; + WFIFOW (fd, j + 70) = p->hair_color; + WFIFOW (fd, j + 72) = find_equip_view (p, 0x0080); // 13: misc2 +// WFIFOW(fd,j+72) = p->clothes_color; + + memcpy (WFIFOP (fd, j + 74), p->name, 24); + + WFIFOB (fd, j + 98) = (p->str > 255) ? 255 : p->str; + WFIFOB (fd, j + 99) = (p->agi > 255) ? 255 : p->agi; + WFIFOB (fd, j + 100) = (p->vit > 255) ? 255 : p->vit; + WFIFOB (fd, j + 101) = (p->int_ > 255) ? 255 : p->int_; + WFIFOB (fd, j + 102) = (p->dex > 255) ? 255 : p->dex; + WFIFOB (fd, j + 103) = (p->luk > 255) ? 255 : p->luk; + WFIFOB (fd, j + 104) = p->char_num; + } + + WFIFOSET (fd, WFIFOW (fd, 2)); + + return 0; +} + +int set_account_reg2 (int acc, int num, struct global_reg *reg) +{ + int i, c; + + c = 0; + for (i = 0; i < char_num; i++) + { + if (char_dat[i].account_id == acc) + { + memcpy (char_dat[i].account_reg2, reg, + sizeof (char_dat[i].account_reg2)); + char_dat[i].account_reg2_num = num; + c++; + } + } + return c; +} + +// Divorce a character from it's partner and let the map server know +int char_divorce (struct mmo_charstatus *cs) +{ + int i; + char buf[10]; + + if (cs == NULL) + return 0; + + if (cs->partner_id <= 0) + { + WBUFW (buf, 0) = 0x2b12; + WBUFL (buf, 2) = cs->char_id; + WBUFL (buf, 6) = 0; // partner id 0 means failure + mapif_sendall (buf, 10); + return 0; + } + + WBUFW (buf, 0) = 0x2b12; + WBUFL (buf, 2) = cs->char_id; + + for (i = 0; i < char_num; i++) + { + if (char_dat[i].char_id == cs->partner_id + && char_dat[i].partner_id == cs->char_id) + { + WBUFL (buf, 6) = cs->partner_id; + mapif_sendall (buf, 10); + cs->partner_id = 0; + char_dat[i].partner_id = 0; + return 0; + } + // The other char doesn't have us as their partner, so just clear our partner + // Don't worry about this, as the map server should verify itself that the other doesn't have us as a partner, and so won't mess with their marriage + else if (char_dat[i].char_id == cs->partner_id) + { + WBUFL (buf, 6) = cs->partner_id; + mapif_sendall (buf, 10); + cs->partner_id = 0; + return 0; + } + } + + // Our partner wasn't found, so just clear our marriage + WBUFL (buf, 6) = cs->partner_id; + cs->partner_id = 0; + mapif_sendall (buf, 10); + + return 0; +} + +//------------------------------------------------------------ +// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] +//------------------------------------------------------------ +int e_mail_check (unsigned char *email) +{ + char ch; + unsigned 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; +} + +//---------------------------------------------------------------------- +// Force disconnection of an online player (with account value) by [Yor] +//---------------------------------------------------------------------- +int disconnect_player (int accound_id) +{ + int i; + struct char_session_data *sd; + + // disconnect player if online on char-server + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) + { + if (sd->account_id == accound_id) + { + session[i]->eof = 1; + return 1; + } + } + } + + return 0; +} + +// キャラ削除に伴うデータ削除 +static int char_delete (struct mmo_charstatus *cs) +{ + + // ギルド脱退 + if (cs->guild_id) + inter_guild_leave (cs->guild_id, cs->account_id, cs->char_id); + // パーティー脱退 + if (cs->party_id) + inter_party_leave (cs->party_id, cs->account_id); + // 離婚 + if (cs->partner_id) + char_divorce (cs); + + // Force the character (and all on the same account) to leave all map servers + { + unsigned char buf[6]; + WBUFW (buf, 0) = 0x2afe; + WBUFL (buf, 2) = cs->account_id; + mapif_sendall (buf, 6); + } + + return 0; +} + +void parse_tologin (int fd) +{ + int i; + struct char_session_data *sd; + + // only login-server can have an access to here. + // so, if it isn't the login-server, we disconnect the session (fd != login_fd). + if (fd != login_fd || session[fd]->eof) + { + if (fd == login_fd) + { + printf + ("Char-server can't connect to login-server (connection #%d).\n", + fd); + login_fd = -1; + } + close (fd); + delete_session (fd); + return; + } + + sd = (struct char_session_data*)session[fd]->session_data; + + while (RFIFOREST (fd) >= 2) + { +// printf("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch (RFIFOW (fd, 0)) + { + case 0x2711: + if (RFIFOREST (fd) < 3) + return; + if (RFIFOB (fd, 2)) + { +// printf("connect login server error : %d\n", RFIFOB(fd,2)); + printf ("Can not connect to login-server.\n"); + printf + ("The server communication passwords (default s1/p1) is probably invalid.\n"); + printf + ("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); + printf + ("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); + exit (1); + } + else + { + printf ("Connected to login-server (connection #%d).\n", + fd); + // if no map-server already connected, display a message... + for (i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0 && server[i].map[0][0]) // if map-server online and at least 1 map + break; + if (i == MAX_MAP_SERVERS) + printf ("Awaiting maps from map-server.\n"); + } + RFIFOSKIP (fd, 3); + break; + + case 0x2713: + if (RFIFOREST (fd) < 51) + return; +// printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) + && sd->account_id == RFIFOL (fd, 2)) + { + if (RFIFOB (fd, 6) != 0) + { + WFIFOW (i, 0) = 0x6c; + WFIFOB (i, 2) = 0x42; + WFIFOSET (i, 3); + } + else if (max_connect_user == 0 + || count_users () < max_connect_user) + { +// if (max_connect_user == 0) +// printf("max_connect_user (unlimited) -> accepted.\n"); +// else +// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); + memcpy (sd->email, RFIFOP (fd, 7), 40); + if (e_mail_check (sd->email) == 0) + strncpy (sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t) RFIFOL (fd, 47); + // send characters to player + mmo_char_send006b (i, sd); + } + else + { + // refuse connection: too much online players +// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); + WFIFOW (i, 0) = 0x6c; + WFIFOW (i, 2) = 0; + WFIFOSET (i, 3); + } + break; + } + } + RFIFOSKIP (fd, 51); + break; + + // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] + case 0x2717: + if (RFIFOREST (fd) < 50) + return; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) + { + if (sd->account_id == RFIFOL (fd, 2)) + { + memcpy (sd->email, RFIFOP (fd, 6), 40); + if (e_mail_check (sd->email) == 0) + strncpy (sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t) RFIFOL (fd, 46); + break; + } + } + } + RFIFOSKIP (fd, 50); + break; + + case 0x2721: // gm reply + if (RFIFOREST (fd) < 10) + return; + { + unsigned char buf[10]; + WBUFW (buf, 0) = 0x2b0b; + WBUFL (buf, 2) = RFIFOL (fd, 2); // account + WBUFL (buf, 6) = RFIFOL (fd, 6); // GM level + mapif_sendall (buf, 10); +// printf("parse_tologin: To become GM answer: char -> map.\n"); + } + RFIFOSKIP (fd, 10); + break; + + case 0x2723: // changesex reply (modified by [Yor]) + if (RFIFOREST (fd) < 7) + return; + { + int acc, sex, i, j; + unsigned char buf[7]; + acc = RFIFOL (fd, 2); + sex = RFIFOB (fd, 6); + RFIFOSKIP (fd, 7); + if (acc > 0) + { + for (i = 0; i < char_num; i++) + { + if (char_dat[i].account_id == acc) + { + int jobclass = char_dat[i].pc_class; + char_dat[i].sex = sex; +// auth_fifo[i].sex = sex; + if (jobclass == 19 || jobclass == 20 || + jobclass == 4020 || jobclass == 4021 || + jobclass == 4042 || jobclass == 4043) + { + // job modification + if (jobclass == 19 || jobclass == 20) + { + char_dat[i].pc_class = (sex) ? 19 : 20; + } + else if (jobclass == 4020 + || jobclass == 4021) + { + char_dat[i].pc_class = + (sex) ? 4020 : 4021; + } + else if (jobclass == 4042 + || jobclass == 4043) + { + char_dat[i].pc_class = + (sex) ? 4042 : 4043; + } + } + // to avoid any problem with equipment and invalid sex, equipment is unequiped. + for (j = 0; j < MAX_INVENTORY; j++) + { + if (char_dat[i].inventory[j].nameid + && char_dat[i].inventory[j].equip) + char_dat[i].inventory[j].equip = 0; + } + char_dat[i].weapon = 0; + char_dat[i].shield = 0; + char_dat[i].head_top = 0; + char_dat[i].head_mid = 0; + char_dat[i].head_bottom = 0; + } + } + // disconnect player if online on char-server + disconnect_player (acc); + } + WBUFW (buf, 0) = 0x2b0d; + WBUFL (buf, 2) = acc; + WBUFB (buf, 6) = sex; + mapif_sendall (buf, 7); + } + break; + + case 0x2726: // Request to send a broadcast message (no answer) + if (RFIFOREST (fd) < 8 + || RFIFOREST (fd) < (8 + RFIFOL (fd, 4))) + return; + if (RFIFOL (fd, 4) < 1) + char_log + ("Receiving a message for broadcast, but message is void.\n"); + else + { + // at least 1 map-server + for (i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + break; + if (i == MAX_MAP_SERVERS) + char_log + ("'ladmin': Receiving a message for broadcast, but no map-server is online.\n"); + else + { + char buf[128]; + char message[RFIFOL (fd, 4) + 1]; // +1 to add a null terminated if not exist in the packet + int lp; + char *p; + memset (message, '\0', sizeof (message)); + memcpy (message, RFIFOP (fd, 8), RFIFOL (fd, 4)); + message[sizeof (message) - 1] = '\0'; + remove_control_chars (message); + // remove all first spaces + p = message; + while (p[0] == ' ') + p++; + // if message is only composed of spaces + if (p[0] == '\0') + char_log + ("Receiving a message for broadcast, but message is only a lot of spaces.\n"); + // else send message to all map-servers + else + { + if (RFIFOW (fd, 2) == 0) + { + char_log + ("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n", + message); + lp = 4; + } + else + { + char_log + ("'ladmin': Receiving a message for broadcast (message (in blue): %s)\n", + message); + lp = 8; + } + // split message to max 80 char + while (p[0] != '\0') + { // if not finish + if (p[0] == ' ') // jump if first char is a space + p++; + else + { + char split[80]; + char *last_space; + sscanf (p, "%79[^\t]", split); // max 79 char, any char (\t is control char and control char was removed before) + split[sizeof (split) - 1] = '\0'; // last char always \0 + if ((last_space = + strrchr (split, ' ')) != NULL) + { // searching space from end of the string + last_space[0] = '\0'; // replace it by NULL to have correct length of split + p++; // to jump the new NULL + } + p += strlen (split); + // send broadcast to all map-servers + WBUFW (buf, 0) = 0x3800; + WBUFW (buf, 2) = lp + strlen (split) + 1; + WBUFL (buf, 4) = 0x65756c62; // only write if in blue (lp = 8) + memcpy (WBUFP (buf, lp), split, + strlen (split) + 1); + mapif_sendall (buf, WBUFW (buf, 2)); + } + } + } + } + } + RFIFOSKIP (fd, 8 + RFIFOL (fd, 4)); + break; + + // account_reg2変更通知 + case 0x2729: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + unsigned char buf[4096]; + int j, p, acc; + acc = RFIFOL (fd, 4); + for (p = 8, j = 0; + p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; + p += 36, j++) + { + memcpy (reg[j].str, RFIFOP (fd, p), 32); + reg[j].value = RFIFOL (fd, p + 32); + } + set_account_reg2 (acc, j, reg); + // 同垢ログインを禁止していれば送る必要は無い + memcpy (buf, RFIFOP (fd, 0), RFIFOW (fd, 2)); + WBUFW (buf, 0) = 0x2b11; + mapif_sendall (buf, WBUFW (buf, 2)); + RFIFOSKIP (fd, RFIFOW (fd, 2)); +// printf("char: save_account_reg_reply\n"); + } + break; + + case 0x7924: + { // [Fate] Itemfrob package: forwarded from login-server + if (RFIFOREST (fd) < 10) + return; + int source_id = RFIFOL (fd, 2); + int dest_id = RFIFOL (fd, 6); + unsigned char buf[10]; + + WBUFW (buf, 0) = 0x2afa; + WBUFL (buf, 2) = source_id; + WBUFL (buf, 6) = dest_id; + + mapif_sendall (buf, 10); // forward package to map servers + for (i = 0; i < char_num; i++) + { + struct mmo_charstatus *c = char_dat + i; + struct storage *s = account2storage (c->account_id); + int changes = 0; + int j; +#define FIX(v) if (v == source_id) {v = dest_id; ++changes; } + for (j = 0; j < MAX_INVENTORY; j++) + FIX (c->inventory[j].nameid); + for (j = 0; j < MAX_CART; j++) + FIX (c->cart[j].nameid); + FIX (c->weapon); + FIX (c->shield); + FIX (c->head_top); + FIX (c->head_mid); + FIX (c->head_bottom); + + if (s) + for (j = 0; j < s->storage_amount; j++) + FIX (s->storage_[j].nameid); +#undef FIX + if (changes) + char_log + ("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n", + source_id, dest_id, c->name, c->char_id, + c->account_id, changes); + + } + + mmo_char_sync (); + inter_storage_save (); + RFIFOSKIP (fd, 10); + break; + } + + // Account deletion notification (from login-server) + case 0x2730: + if (RFIFOREST (fd) < 6) + return; + // Deletion of all characters of the account + for (i = 0; i < char_num; i++) + { + if (char_dat[i].account_id == RFIFOL (fd, 2)) + { + char_delete (&char_dat[i]); + if (i < char_num - 1) + { + memcpy (&char_dat[i], &char_dat[char_num - 1], + sizeof (struct mmo_charstatus)); + // if moved character owns to deleted account, check again it's character + if (char_dat[i].account_id == RFIFOL (fd, 2)) + { + i--; + // Correct moved character reference in the character's owner by [Yor] + } + else + { + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) + { + if (session[j] + && (sd2 = (struct char_session_data*)session[j]->session_data) + && sd2->account_id == + char_dat[char_num - 1].account_id) + { + for (k = 0; k < 9; k++) + { + if (sd2->found_char[k] == + char_num - 1) + { + sd2->found_char[k] = i; + break; + } + } + break; + } + } + } + } + char_num--; + } + } + // Deletion of the storage + inter_storage_delete (RFIFOL (fd, 2)); + // send to all map-servers to disconnect the player + { + unsigned char buf[6]; + WBUFW (buf, 0) = 0x2b13; + WBUFL (buf, 2) = RFIFOL (fd, 2); + mapif_sendall (buf, 6); + } + // disconnect player if online on char-server + disconnect_player (RFIFOL (fd, 2)); + RFIFOSKIP (fd, 6); + break; + + // State change of account/ban notification (from login-server) by [Yor] + case 0x2731: + if (RFIFOREST (fd) < 11) + return; + // send to all map-servers to disconnect the player + { + unsigned char buf[11]; + WBUFW (buf, 0) = 0x2b14; + WBUFL (buf, 2) = RFIFOL (fd, 2); + WBUFB (buf, 6) = RFIFOB (fd, 6); // 0: change of statut, 1: ban + WBUFL (buf, 7) = RFIFOL (fd, 7); // status or final date of a banishment + mapif_sendall (buf, 11); + } + // disconnect player if online on char-server + disconnect_player (RFIFOL (fd, 2)); + RFIFOSKIP (fd, 11); + break; + + // Receiving GM acounts info from login-server (by [Yor]) + case 0x2732: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + char buf[32000]; + if (gm_account != NULL) + free (gm_account); + CREATE (gm_account, struct gm_account, (RFIFOW (fd, 2) - 4) / 5); + GM_num = 0; + 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++; + } + printf + ("From login-server: receiving of %d GM accounts information.\n", + GM_num); + char_log + ("From login-server: receiving of %d GM accounts information.\n", + GM_num); + create_online_files (); // update online players files (perhaps some online players change of GM level) + // send new gm acccounts level to map-servers + memcpy (buf, RFIFOP (fd, 0), RFIFOW (fd, 2)); + WBUFW (buf, 0) = 0x2b15; + mapif_sendall (buf, RFIFOW (fd, 2)); + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + case 0x2741: // change password reply + if (RFIFOREST (fd) < 7) + return; + { + int acc, status, i; + acc = RFIFOL (fd, 2); + status = RFIFOB (fd, 6); + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) + { + if (sd->account_id == acc) + { + WFIFOW (i, 0) = 0x62; + WFIFOB (i, 2) = status; + WFIFOSET (i, 3); + break; + } + } + } + } + RFIFOSKIP (fd, 7); + break; + + default: + session[fd]->eof = 1; + return; + } + } + RFIFOFLUSH (fd); +} + +//-------------------------------- +// Map-server anti-freeze system +//-------------------------------- +void map_anti_freeze_system (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int i; + + //printf("Entering in map_anti_freeze_system function to check freeze of servers.\n"); + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + if (server_fd[i] >= 0) + { // if map-server is online + //printf("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) + { // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed + printf + ("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", + i); + char_log + ("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", + i); + session[server_fd[i]]->eof = 1; + } + } + } +} + +void parse_frommap (int fd) +{ + int i, j; + int id; + + for (id = 0; id < MAX_MAP_SERVERS; id++) + if (server_fd[id] == fd) + break; + if (id == MAX_MAP_SERVERS || session[fd]->eof) + { + if (id < MAX_MAP_SERVERS) + { + printf ("Map-server %d (session #%d) has disconnected.\n", id, + fd); + memset (&server[id], 0, sizeof (struct mmo_map_server)); + server_fd[id] = -1; + for (j = 0; j < char_num; j++) + if (online_chars[j] == fd) + online_chars[j] = -1; + create_online_files (); // update online players files (to remove all online players of this server) + } + close (fd); + delete_session (fd); + return; + } + + while (RFIFOREST (fd) >= 2) + { +// printf("parse_frommap: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch (RFIFOW (fd, 0)) + { + // request from map-server to reload GM accounts. Transmission to login-server (by Yor) + case 0x2af7: + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2709; + WFIFOSET (login_fd, 2); +// printf("char : request from map-server to reload GM accounts -> login-server.\n"); + } + RFIFOSKIP (fd, 2); + break; + + // Receiving map names list from the map-server + case 0x2afa: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + memset (server[id].map, 0, sizeof (server[id].map)); + j = 0; + for (i = 4; i < RFIFOW (fd, 2); i += 16) + { + memcpy (server[id].map[j], RFIFOP (fd, i), 16); +// printf("set map %d.%d : %s\n", id, j, server[id].map[j]); + j++; + } + { + unsigned char *p = (unsigned char *) &server[id].ip; + printf + ("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", + id, j, p[0], p[1], p[2], p[3], server[id].port); + printf ("Map-server %d loading complete.\n", id); + char_log + ("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d. Map-server %d loading complete.\n", + id, j, p[0], p[1], p[2], p[3], + server[id].port, id); + } + WFIFOW (fd, 0) = 0x2afb; + WFIFOB (fd, 2) = 0; + memcpy (WFIFOP (fd, 3), wisp_server_name, 24); // name for wisp to player + WFIFOSET (fd, 27); + { + unsigned char buf[16384]; + int x; + if (j == 0) + { + printf ("WARNING: Map-Server %d have NO map.\n", id); + char_log ("WARNING: Map-Server %d have NO map.\n", + id); + // Transmitting maps information to the other map-servers + } + else + { + WBUFW (buf, 0) = 0x2b04; + WBUFW (buf, 2) = j * 16 + 10; + WBUFL (buf, 4) = server[id].ip; + WBUFW (buf, 8) = server[id].port; + memcpy (WBUFP (buf, 10), RFIFOP (fd, 4), j * 16); + mapif_sendallwos (fd, buf, WBUFW (buf, 2)); + } + // Transmitting the maps of the other map-servers to the new map-server + for (x = 0; x < MAX_MAP_SERVERS; x++) + { + if (server_fd[x] >= 0 && x != id) + { + WFIFOW (fd, 0) = 0x2b04; + WFIFOL (fd, 4) = server[x].ip; + WFIFOW (fd, 8) = server[x].port; + j = 0; + for (i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[x].map[i][0]) + memcpy (WFIFOP (fd, 10 + (j++) * 16), + server[x].map[i], 16); + if (j > 0) + { + WFIFOW (fd, 2) = j * 16 + 10; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + } + } + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + // 認証要求 + case 0x2afc: + if (RFIFOREST (fd) < 22) + return; + //printf("auth_fifo search: account: %d, char: %d, secure: %08x-%08x\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14)); + for (i = 0; i < AUTH_FIFO_SIZE; i++) + { + if (auth_fifo[i].account_id == RFIFOL (fd, 2) && + auth_fifo[i].char_id == RFIFOL (fd, 6) && + auth_fifo[i].login_id1 == RFIFOL (fd, 10) && +#if CMP_AUTHFIFO_LOGIN2 != 0 + // here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value) + (auth_fifo[i].login_id2 == RFIFOL (fd, 14) || RFIFOL (fd, 14) == 0) && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == RFIFOL (fd, 18)) + && !auth_fifo[i].delflag) + { + auth_fifo[i].delflag = 1; + WFIFOW (fd, 0) = 0x2afd; + WFIFOW (fd, 2) = 18 + sizeof (struct mmo_charstatus); + WFIFOL (fd, 4) = RFIFOL (fd, 2); + WFIFOL (fd, 8) = auth_fifo[i].login_id2; + WFIFOL (fd, 12) = + (unsigned long) auth_fifo[i].connect_until_time; + char_dat[auth_fifo[i].char_pos].sex = + auth_fifo[i].sex; + WFIFOW (fd, 16) = auth_fifo[i].packet_tmw_version; + fprintf (stderr, + "From queue index %d: recalling packet version %d\n", + i, auth_fifo[i].packet_tmw_version); + memcpy (WFIFOP (fd, 18), + &char_dat[auth_fifo[i].char_pos], + sizeof (struct mmo_charstatus)); + WFIFOSET (fd, WFIFOW (fd, 2)); + //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); + break; + } + } + if (i == AUTH_FIFO_SIZE) + { + WFIFOW (fd, 0) = 0x2afe; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + WFIFOSET (fd, 6); + printf + ("auth_fifo search error! account %d not authentified.\n", + RFIFOL (fd, 2)); + } + RFIFOSKIP (fd, 22); + break; + + // MAPサーバー上のユーザー数受信 + case 0x2aff: + if (RFIFOREST (fd) < 6 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + server[id].users = RFIFOW (fd, 4); + if (anti_freeze_enable) + server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + // remove all previously online players of the server + for (i = 0; i < char_num; i++) + if (online_chars[i] == id) + online_chars[i] = -1; + // add online players in the list by [Yor] + for (i = 0; i < server[id].users; i++) + { + int char_id = RFIFOL (fd, 6 + i * 4); + for (j = 0; j < char_num; j++) + if (char_dat[j].char_id == char_id) + { + online_chars[j] = id; + //printf("%d\n", char_id); + break; + } + } + if (update_online < time (NULL)) + { // Time is done + update_online = time (NULL) + 8; + create_online_files (); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. + // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. + } + RFIFOSKIP (fd, 6 + i * 4); + break; + + // キャラデータ保存 + case 0x2b01: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + for (i = 0; i < char_num; i++) + { + if (char_dat[i].account_id == RFIFOL (fd, 4) && + char_dat[i].char_id == RFIFOL (fd, 8)) + break; + } + if (i != char_num) + memcpy (&char_dat[i], RFIFOP (fd, 12), + sizeof (struct mmo_charstatus)); + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + // キャラセレ要求 + case 0x2b02: + if (RFIFOREST (fd) < 18) + return; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL (fd, 2); + auth_fifo[auth_fifo_pos].char_id = 0; + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL (fd, 6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL (fd, 10); + auth_fifo[auth_fifo_pos].delflag = 2; + auth_fifo[auth_fifo_pos].char_pos = 0; + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL (fd, 14); + auth_fifo_pos++; + WFIFOW (fd, 0) = 0x2b03; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + WFIFOB (fd, 6) = 0; + WFIFOSET (fd, 7); + RFIFOSKIP (fd, 18); + break; + + // マップサーバー間移動要求 + case 0x2b05: + if (RFIFOREST (fd) < 49) + return; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + WFIFOW (fd, 0) = 0x2b06; + memcpy (WFIFOP (fd, 2), RFIFOP (fd, 2), 42); + //printf("auth_fifo set (auth#%d) - account: %d, secure: 0x%08x-0x%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL (fd, 2); + auth_fifo[auth_fifo_pos].char_id = RFIFOL (fd, 14); + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL (fd, 6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL (fd, 10); + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].sex = RFIFOB (fd, 44); + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL (fd, 45); + for (i = 0; i < char_num; i++) + if (char_dat[i].account_id == RFIFOL (fd, 2) && + char_dat[i].char_id == RFIFOL (fd, 14)) + { + auth_fifo[auth_fifo_pos].char_pos = i; + auth_fifo_pos++; + WFIFOL (fd, 6) = 0; + break; + } + if (i == char_num) + WFIFOW (fd, 6) = 1; + WFIFOSET (fd, 44); + RFIFOSKIP (fd, 49); + break; + + // キャラ名検索 + case 0x2b08: + if (RFIFOREST (fd) < 6) + return; + for (i = 0; i < char_num; i++) + { + if (char_dat[i].char_id == RFIFOL (fd, 2)) + break; + } + WFIFOW (fd, 0) = 0x2b09; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + if (i != char_num) + memcpy (WFIFOP (fd, 6), char_dat[i].name, 24); + else + memcpy (WFIFOP (fd, 6), unknown_char_name, 24); + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 6); + break; + + // it is a request to become GM + case 0x2b0a: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; +// printf("parse_frommap: change gm -> login, account: %d, pass: '%s'.\n", RFIFOL(fd,4), RFIFOP(fd,8)); + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2720; + memcpy (WFIFOP (login_fd, 2), RFIFOP (fd, 2), + RFIFOW (fd, 2) - 2); + WFIFOSET (login_fd, RFIFOW (fd, 2)); + } + else + { + WFIFOW (fd, 0) = 0x2b0b; + WFIFOL (fd, 2) = RFIFOL (fd, 4); + WFIFOL (fd, 6) = 0; + WFIFOSET (fd, 10); + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + // Map server send information to change an email of an account -> login-server + case 0x2b0c: + if (RFIFOREST (fd) < 86) + return; + if (login_fd > 0) + { // don't send request if no login-server + memcpy (WFIFOP (login_fd, 0), RFIFOP (fd, 0), 86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B + WFIFOW (login_fd, 0) = 0x2722; + WFIFOSET (login_fd, 86); + } + RFIFOSKIP (fd, 86); + break; + + // Map server ask char-server about a character name to do some operations (all operations are transmitted to login-server) + case 0x2b0e: + if (RFIFOREST (fd) < 44) + return; + { + char character_name[24]; + int acc = RFIFOL (fd, 2); // account_id of who ask (-1 if nobody) + memcpy (character_name, RFIFOP (fd, 6), 24); + character_name[sizeof (character_name) - 1] = '\0'; + // prepare answer + WFIFOW (fd, 0) = 0x2b0f; // answer + WFIFOL (fd, 2) = acc; // who want do operation + WFIFOW (fd, 30) = RFIFOW (fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex + // search character + i = search_character_index (character_name); + if (i >= 0) + { + memcpy (WFIFOP (fd, 6), search_character_name (i), 24); // put correct name if found + WFIFOW (fd, 32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + switch (RFIFOW (fd, 30)) + { + case 1: // block + if (acc == -1 + || isGM (acc) >= + isGM (char_dat[i].account_id)) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2724; + WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value + WFIFOL (login_fd, 6) = 5; // status of the account + WFIFOSET (login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 5); + } + else + WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + else + WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 2: // ban + if (acc == -1 + || isGM (acc) >= + isGM (char_dat[i].account_id)) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2725; + WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value + WFIFOW (login_fd, 6) = RFIFOW (fd, 32); // year + WFIFOW (login_fd, 8) = RFIFOW (fd, 34); // month + WFIFOW (login_fd, 10) = RFIFOW (fd, 36); // day + WFIFOW (login_fd, 12) = RFIFOW (fd, 38); // hour + WFIFOW (login_fd, 14) = RFIFOW (fd, 40); // minute + WFIFOW (login_fd, 16) = RFIFOW (fd, 42); // second + WFIFOSET (login_fd, 18); +// printf("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", +// char_dat[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); + } + else + WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + else + WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 3: // unblock + if (acc == -1 + || isGM (acc) >= + isGM (char_dat[i].account_id)) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2724; + WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value + WFIFOL (login_fd, 6) = 0; // status of the account + WFIFOSET (login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 0); + } + else + WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + else + WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 4: // unban + if (acc == -1 + || isGM (acc) >= + isGM (char_dat[i].account_id)) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x272a; + WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value + WFIFOSET (login_fd, 6); +// printf("char : status -> login: account %d, unban request\n", char_dat[i].account_id); + } + else + WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + else + WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 5: // changesex + if (acc == -1 + || isGM (acc) >= + isGM (char_dat[i].account_id)) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2727; + WFIFOL (login_fd, 2) = char_dat[i].account_id; // account value + WFIFOSET (login_fd, 6); +// printf("char : status -> login: account %d, change sex request\n", char_dat[i].account_id); + } + else + WFIFOW (fd, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + else + WFIFOW (fd, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + } + } + else + { + // character name not found + memcpy (WFIFOP (fd, 6), character_name, 24); + WFIFOW (fd, 32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + // send answer if a player ask, not if the server ask + if (acc != -1) + { + WFIFOSET (fd, 34); + } + RFIFOSKIP (fd, 44); + break; + } + +// case 0x2b0f: not more used (available for futur usage) + + // account_reg保存要求 + case 0x2b10: + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + int p, acc; + acc = RFIFOL (fd, 4); + for (p = 8, j = 0; + p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; + p += 36, j++) + { + memcpy (reg[j].str, RFIFOP (fd, p), 32); + reg[j].value = RFIFOL (fd, p + 32); + } + set_account_reg2 (acc, j, reg); + // loginサーバーへ送る + if (login_fd > 0) + { // don't send request if no login-server + memcpy (WFIFOP (login_fd, 0), RFIFOP (fd, 0), + RFIFOW (fd, 2)); + WFIFOW (login_fd, 0) = 0x2728; + WFIFOSET (login_fd, WFIFOW (login_fd, 2)); + } + // ワールドへの同垢ログインがなければmapサーバーに送る必要はない + //memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); + //WBUFW(buf,0) = 0x2b11; + //mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP (fd, RFIFOW (fd, 2)); +// printf("char: save_account_reg (from map)\n"); + break; + } + + // Map server is requesting a divorce + case 0x2b16: + if (RFIFOREST (fd) < 4) + return; + { + for (i = 0; i < char_num; i++) + if (char_dat[i].char_id == RFIFOL (fd, 2)) + break; + + if (i != char_num) + char_divorce (&char_dat[i]); + + RFIFOSKIP (fd, 6); + break; + + } + + default: + // inter server処理に渡す + { + int r = inter_parse_frommap (fd); + if (r == 1) // 処理できた + break; + if (r == 2) // パケット長が足りない + return; + } + // inter server処理でもない場合は切断 + printf + ("char: unknown packet 0x%04x (%d bytes to read in buffer)! (from map).\n", + RFIFOW (fd, 0), RFIFOREST (fd)); + session[fd]->eof = 1; + return; + } + } +} + +int search_mapserver (char *map) +{ + int i, j; + char temp_map[16]; + int temp_map_len; + +// printf("Searching the map-server for map '%s'... ", map); + strncpy (temp_map, map, sizeof (temp_map)); + temp_map[sizeof (temp_map) - 1] = '\0'; + if (strchr (temp_map, '.') != NULL) + temp_map[strchr (temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map + + temp_map_len = strlen (temp_map); + for (i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + for (j = 0; server[i].map[j][0]; j++) + //printf("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); + if (strncmp (server[i].map[j], temp_map, temp_map_len) == 0) + { +// printf("found -> server #%d.\n", i); + return i; + } + +// printf("not found.\n"); + return -1; +} + +// char_mapifの初期化処理(現在はinter_mapif初期化のみ) +static int char_mapif_init (int fd) +{ + return inter_mapif_init (fd); +} + +//----------------------------------------------------- +// Test to know if an IP come from LAN or WAN. by [Yor] +//----------------------------------------------------- +int lan_ip_check (unsigned char *p) +{ + int i; + int lancheck = 1; + +// printf("lan_ip_check: to compare: %d.%d.%d.%d, network: %d.%d.%d.%d/%d.%d.%d.%d\n", +// p[0], p[1], p[2], p[3], +// subneti[0], subneti[1], subneti[2], subneti[3], +// subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + for (i = 0; i < 4; i++) + { + if ((subneti[i] & subnetmaski[i]) != (p[i] & subnetmaski[i])) + { + lancheck = 0; + break; + } + } + printf ("LAN test (result): %s source\033[0m.\n", + (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); + return lancheck; +} + +void parse_char (int fd) +{ + int i, ch; + char email[40]; + struct char_session_data *sd; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + + if (login_fd < 0 || session[fd]->eof) + { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. + if (fd == login_fd) + login_fd = -1; + close (fd); + delete_session (fd); + return; + } + + sd = (struct char_session_data*)session[fd]->session_data; + + while (RFIFOREST (fd) >= 2) + { +// if (RFIFOW(fd,0) < 30000) +// printf("parse_char: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch (RFIFOW (fd, 0)) + { + case 0x20b: //20040622暗号化ragexe対応 + if (RFIFOREST (fd) < 19) + return; + RFIFOSKIP (fd, 19); + break; + + case 0x61: // change password request + if (RFIFOREST (fd) < 50) + return; + { + WFIFOW (login_fd, 0) = 0x2740; + WFIFOL (login_fd, 2) = sd->account_id; + memcpy (WFIFOP (login_fd, 6), RFIFOP (fd, 2), 24); + memcpy (WFIFOP (login_fd, 30), RFIFOP (fd, 26), 24); + WFIFOSET (login_fd, 54); + } + RFIFOSKIP (fd, 50); + break; + + case 0x65: // 接続要求 + if (RFIFOREST (fd) < 17) + return; + { + int GM_value; + if ((GM_value = isGM (RFIFOL (fd, 2)))) + printf + ("Account Logged On; Account ID: %d (GM level %d).\n", + RFIFOL (fd, 2), GM_value); + else + printf ("Account Logged On; Account ID: %d.\n", + RFIFOL (fd, 2)); + if (sd == NULL) + { + CREATE (sd, struct char_session_data, 1); + session[fd]->session_data = sd; + memcpy (sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail + sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + } + sd->account_id = RFIFOL (fd, 2); + sd->login_id1 = RFIFOL (fd, 6); + sd->login_id2 = RFIFOL (fd, 10); + sd->packet_tmw_version = RFIFOW (fd, 14); + sd->sex = RFIFOB (fd, 16); + // send back account_id + WFIFOL (fd, 0) = RFIFOL (fd, 2); + WFIFOSET (fd, 4); + // search authentification + for (i = 0; i < AUTH_FIFO_SIZE; i++) + { + if (auth_fifo[i].account_id == sd->account_id && + auth_fifo[i].login_id1 == sd->login_id1 && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 +#endif + (!check_ip_flag + || auth_fifo[i].ip == + session[fd]->client_addr.sin_addr.s_addr) + && auth_fifo[i].delflag == 2) + { + auth_fifo[i].delflag = 1; + if (max_connect_user == 0 + || count_users () < max_connect_user) + { + if (login_fd > 0) + { // don't send request if no login-server + // request to login-server to obtain e-mail/time limit + WFIFOW (login_fd, 0) = 0x2716; + WFIFOL (login_fd, 2) = sd->account_id; + WFIFOSET (login_fd, 6); + } + // Record client version + auth_fifo[i].packet_tmw_version = + sd->packet_tmw_version; + // send characters to player + mmo_char_send006b (fd, sd); + } + else + { + // refuse connection (over populated) + WFIFOW (fd, 0) = 0x6c; + WFIFOW (fd, 2) = 0; + WFIFOSET (fd, 3); + } + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) + { + if (login_fd > 0) + { // don't send request if no login-server + WFIFOW (login_fd, 0) = 0x2712; // ask login-server to authentify an account + WFIFOL (login_fd, 2) = sd->account_id; + WFIFOL (login_fd, 6) = sd->login_id1; + WFIFOL (login_fd, 10) = sd->login_id2; // relate to the versions higher than 18 + WFIFOB (login_fd, 14) = sd->sex; + WFIFOL (login_fd, 15) = + session[fd]->client_addr.sin_addr.s_addr; + WFIFOSET (login_fd, 19); + } + else + { // if no login-server, we must refuse connection + WFIFOW (fd, 0) = 0x6c; + WFIFOW (fd, 2) = 0; + WFIFOSET (fd, 3); + } + } + } + RFIFOSKIP (fd, 17); + break; + + case 0x66: // キャラ選択 + if (!sd || RFIFOREST (fd) < 3) + return; + + char ip[16]; + unsigned char *sin_addr = + (unsigned char *) &session[fd]->client_addr.sin_addr; + sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], + sin_addr[2], sin_addr[3]); + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp (sd->email, "a@a.com") == 0 + && login_fd > 0) + { // to modify an e-mail, login-server must be online + WFIFOW (fd, 0) = 0x70; + WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET (fd, 3); + + // otherwise, load the character + } + else + { + for (ch = 0; ch < 9; ch++) + if (sd->found_char[ch] >= 0 + && char_dat[sd->found_char[ch]].char_num == + RFIFOB (fd, 2)) + break; + if (ch != 9) + { + char_log + ("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s [%s]\n", + sd->account_id, RFIFOB (fd, 2), + char_dat[sd->found_char[ch]].name, ip); + // searching map server + i = search_mapserver (char_dat + [sd->found_char[ch]].last_point. + map); + // if map is not found, we check major cities + if (i < 0) + { + if ((i = search_mapserver ("prontera.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "prontera.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 273; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 354; + } + else if ((i = + search_mapserver ("geffen.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "geffen.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 120; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 100; + } + else if ((i = + search_mapserver ("morocc.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "morocc.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 160; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 94; + } + else if ((i = + search_mapserver ("alberta.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "alberta.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 116; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 57; + } + else if ((i = + search_mapserver ("payon.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "payon.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 87; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 117; + } + else if ((i = + search_mapserver ("izlude.gat")) >= 0) + { // check is done without 'gat'. + memcpy (char_dat + [sd->found_char[ch]].last_point.map, + "izlude.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 94; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = + 103; + } + else + { + int j; + // get first online server (with a map) + i = 0; + for (j = 0; j < MAX_MAP_SERVERS; j++) + if (server_fd[j] >= 0 + && server[j].map[0][0]) + { // change save point to one of map found on the server (the first) + i = j; + memcpy (char_dat + [sd-> + found_char[ch]].last_point. + map, server[j].map[0], 16); + printf + ("Map-server #%d found with a map: '%s'.\n", + j, server[j].map[0]); + // coordonates are unknown + break; + } + // if no map-server is connected, we send: server closed + if (j == MAX_MAP_SERVERS) + { + WFIFOW (fd, 0) = 0x81; + WFIFOL (fd, 2) = 1; // 01 = Server closed + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 3); + break; + } + } + } + WFIFOW (fd, 0) = 0x71; + WFIFOL (fd, 2) = char_dat[sd->found_char[ch]].char_id; + memcpy (WFIFOP (fd, 6), + char_dat[sd->found_char[ch]].last_point.map, + 16); + printf + ("Character selection '%s' (account: %d, slot: %d) [%s]\n", + char_dat[sd->found_char[ch]].name, + sd->account_id, ch, ip); + printf ("--Send IP of map-server. "); + if (lan_ip_check (p)) + WFIFOL (fd, 22) = inet_addr (lan_map_ip); + else + WFIFOL (fd, 22) = server[i].ip; + WFIFOW (fd, 26) = server[i].port; + WFIFOSET (fd, 28); + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + //printf("auth_fifo set #%d - account %d, char: %d, secure: %08x-%08x\n", auth_fifo_pos, sd->account_id, char_dat[sd->found_char[ch]].char_id, sd->login_id1, sd->login_id2); + auth_fifo[auth_fifo_pos].account_id = sd->account_id; + auth_fifo[auth_fifo_pos].char_id = + char_dat[sd->found_char[ch]].char_id; + auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; + auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].char_pos = + sd->found_char[ch]; + auth_fifo[auth_fifo_pos].sex = sd->sex; + auth_fifo[auth_fifo_pos].connect_until_time = + sd->connect_until_time; + auth_fifo[auth_fifo_pos].ip = + session[fd]->client_addr.sin_addr.s_addr; + auth_fifo[auth_fifo_pos].packet_tmw_version = + sd->packet_tmw_version; + auth_fifo_pos++; + } + } + RFIFOSKIP (fd, 3); + break; + + case 0x67: // 作成 + if (!sd || RFIFOREST (fd) < 37) + return; + i = make_new_char (fd, RFIFOP (fd, 2)); + if (i < 0) + { + WFIFOW (fd, 0) = 0x6e; + WFIFOB (fd, 2) = 0x00; + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 37); + break; + } + + WFIFOW (fd, 0) = 0x6d; + memset (WFIFOP (fd, 2), 0, 106); + + WFIFOL (fd, 2) = char_dat[i].char_id; + WFIFOL (fd, 2 + 4) = char_dat[i].base_exp; + WFIFOL (fd, 2 + 8) = char_dat[i].zeny; + WFIFOL (fd, 2 + 12) = char_dat[i].job_exp; + WFIFOL (fd, 2 + 16) = char_dat[i].job_level; + + WFIFOL (fd, 2 + 28) = char_dat[i].karma; + WFIFOL (fd, 2 + 32) = char_dat[i].manner; + + WFIFOW (fd, 2 + 40) = 0x30; + WFIFOW (fd, 2 + 42) = + (char_dat[i].hp > 0x7fff) ? 0x7fff : char_dat[i].hp; + WFIFOW (fd, 2 + 44) = + (char_dat[i].max_hp > + 0x7fff) ? 0x7fff : char_dat[i].max_hp; + WFIFOW (fd, 2 + 46) = + (char_dat[i].sp > 0x7fff) ? 0x7fff : char_dat[i].sp; + WFIFOW (fd, 2 + 48) = + (char_dat[i].max_sp > + 0x7fff) ? 0x7fff : char_dat[i].max_sp; + WFIFOW (fd, 2 + 50) = DEFAULT_WALK_SPEED; // char_dat[i].speed; + WFIFOW (fd, 2 + 52) = char_dat[i].pc_class; + WFIFOW (fd, 2 + 54) = char_dat[i].hair; + + WFIFOW (fd, 2 + 58) = char_dat[i].base_level; + WFIFOW (fd, 2 + 60) = char_dat[i].skill_point; + + WFIFOW (fd, 2 + 64) = char_dat[i].shield; + WFIFOW (fd, 2 + 66) = char_dat[i].head_top; + WFIFOW (fd, 2 + 68) = char_dat[i].head_mid; + WFIFOW (fd, 2 + 70) = char_dat[i].hair_color; + + memcpy (WFIFOP (fd, 2 + 74), char_dat[i].name, 24); + + WFIFOB (fd, 2 + 98) = + (char_dat[i].str > 255) ? 255 : char_dat[i].str; + WFIFOB (fd, 2 + 99) = + (char_dat[i].agi > 255) ? 255 : char_dat[i].agi; + WFIFOB (fd, 2 + 100) = + (char_dat[i].vit > 255) ? 255 : char_dat[i].vit; + WFIFOB (fd, 2 + 101) = + (char_dat[i].int_ > 255) ? 255 : char_dat[i].int_; + WFIFOB (fd, 2 + 102) = + (char_dat[i].dex > 255) ? 255 : char_dat[i].dex; + WFIFOB (fd, 2 + 103) = + (char_dat[i].luk > 255) ? 255 : char_dat[i].luk; + WFIFOB (fd, 2 + 104) = char_dat[i].char_num; + + WFIFOSET (fd, 108); + RFIFOSKIP (fd, 37); + for (ch = 0; ch < 9; ch++) + { + if (sd->found_char[ch] == -1) + { + sd->found_char[ch] = i; + break; + } + } + + case 0x68: // delete char //Yor's Fix + if (!sd || RFIFOREST (fd) < 46) + return; + memcpy (email, RFIFOP (fd, 6), 40); + if (e_mail_check (email) == 0) + strncpy (email, "a@a.com", 40); // default e-mail + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp (sd->email, "a@a.com") == 0 + && login_fd > 0) + { // to modify an e-mail, login-server must be online + // if sended email is incorrect e-mail + if (strcmp (email, "a@a.com") == 0) + { + WFIFOW (fd, 0) = 0x70; + WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 46); + // we act like we have selected a character + } + else + { + // we change the packet to set it like selection. + for (i = 0; i < 9; i++) + if (char_dat[sd->found_char[i]].char_id == + RFIFOL (fd, 2)) + { + // we save new e-mail + memcpy (sd->email, email, 40); + // we send new e-mail to login-server ('online' login-server is checked before) + WFIFOW (login_fd, 0) = 0x2715; + WFIFOL (login_fd, 2) = sd->account_id; + memcpy (WFIFOP (login_fd, 6), email, 40); + WFIFOSET (login_fd, 46); + // skip part of the packet! (46, but leave the size of select packet: 3) + RFIFOSKIP (fd, 43); + // change value to put new packet (char selection) + RFIFOW (fd, 0) = 0x66; + RFIFOB (fd, 2) = + char_dat[sd->found_char[i]].char_num; + // not send packet, it's modify of actual packet + break; + } + if (i == 9) + { + WFIFOW (fd, 0) = 0x70; + WFIFOB (fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 46); + } + } + + // otherwise, we delete the character + } + else + { + /*if (strcasecmp(email, sd->email) != 0) { // if it's an invalid email + * WFIFOW(fd, 0) = 0x70; + * WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + * WFIFOSET(fd, 3); + * // if mail is correct + * } else { */ + for (i = 0; i < 9; i++) + { + struct mmo_charstatus *cs = NULL; + if (sd->found_char[i] >= 0 + && (cs = + &char_dat[sd->found_char[i]])->char_id == + RFIFOL (fd, 2)) + { + char_delete (cs); // deletion process + + if (sd->found_char[i] != char_num - 1) + { + memcpy (&char_dat[sd->found_char[i]], + &char_dat[char_num - 1], + sizeof (struct mmo_charstatus)); + // Correct moved character reference in the character's owner + { + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) + { + if (session[j] + && (sd2 = (struct char_session_data*) + session[j]->session_data) + && sd2->account_id == + char_dat[char_num - 1].account_id) + { + for (k = 0; k < 9; k++) + { + if (sd2->found_char[k] == + char_num - 1) + { + sd2->found_char[k] = + sd->found_char[i]; + break; + } + } + break; + } + } + } + } + + char_num--; + for (ch = i; ch < 9 - 1; ch++) + sd->found_char[ch] = sd->found_char[ch + 1]; + sd->found_char[8] = -1; + WFIFOW (fd, 0) = 0x6f; + WFIFOSET (fd, 2); + break; + } + } + + if (i == 9) + { + WFIFOW (fd, 0) = 0x70; + WFIFOB (fd, 2) = 0; + WFIFOSET (fd, 3); + } + //} + RFIFOSKIP (fd, 46); + } + break; + + case 0x2af8: // マップサーバーログイン + if (RFIFOREST (fd) < 60) + return; + WFIFOW (fd, 0) = 0x2af9; + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + if (server_fd[i] < 0) + break; + } + if (i == MAX_MAP_SERVERS || strcmp (RFIFOP (fd, 2), userid) + || strcmp (RFIFOP (fd, 26), passwd)) + { + WFIFOB (fd, 2) = 3; + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 60); + } + else + { + int len; + WFIFOB (fd, 2) = 0; + session[fd]->func_parse = parse_frommap; + server_fd[i] = fd; + if (anti_freeze_enable) + server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + server[i].ip = RFIFOL (fd, 54); + server[i].port = RFIFOW (fd, 58); + server[i].users = 0; + memset (server[i].map, 0, sizeof (server[i].map)); + WFIFOSET (fd, 3); + RFIFOSKIP (fd, 60); + realloc_fifo (fd, FIFOSIZE_SERVERLINK, + FIFOSIZE_SERVERLINK); + char_mapif_init (fd); + // send gm acccounts level to map-servers + len = 4; + WFIFOW (fd, 0) = 0x2b15; + for (i = 0; i < GM_num; i++) + { + WFIFOL (fd, len) = gm_account[i].account_id; + WFIFOB (fd, len + 4) = + (unsigned char) gm_account[i].level; + len += 5; + } + WFIFOW (fd, 2) = len; + WFIFOSET (fd, len); + return; + } + break; + + case 0x187: // Alive信号? + if (RFIFOREST (fd) < 6) + return; + RFIFOSKIP (fd, 6); + break; + + case 0x7530: // Athena情報所得 + 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_INTER | ATHENA_SERVER_CHAR; + WFIFOW (fd, 8) = ATHENA_MOD_VERSION; + WFIFOSET (fd, 10); + RFIFOSKIP (fd, 2); + return; + + case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) + session[fd]->eof = 1; + return; + + default: + session[fd]->eof = 1; + return; + } + } +} + +// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +int mapif_sendall (char *buf, unsigned int len) +{ + int i, c; + + c = 0; + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + int fd; + if ((fd = server_fd[i]) >= 0) + { + memcpy (WFIFOP (fd, 0), buf, len); + WFIFOSET (fd, len); + c++; + } + } + return c; +} + +// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +int mapif_sendallwos (int sfd, unsigned char *buf, unsigned int len) +{ + int i, c; + + c = 0; + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + int fd; + if ((fd = server_fd[i]) >= 0 && fd != sfd) + { + memcpy (WFIFOP (fd, 0), buf, len); + WFIFOSET (fd, len); + c++; + } + } + return c; +} + +// MAPサーバーにデータ送信(map鯖生存確認有り) +int mapif_send (int fd, unsigned char *buf, unsigned int len) +{ + int i; + + if (fd >= 0) + { + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + if (fd == server_fd[i]) + { + memcpy (WFIFOP (fd, 0), buf, len); + WFIFOSET (fd, len); + return 1; + } + } + } + return 0; +} + +void send_users_tologin (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int users = count_users (); + char buf[16]; + + if (login_fd > 0 && session[login_fd]) + { + // send number of user to login server + WFIFOW (login_fd, 0) = 0x2714; + WFIFOL (login_fd, 2) = users; + WFIFOSET (login_fd, 6); + } + // send number of players to all map-servers + WBUFW (buf, 0) = 0x2b00; + WBUFL (buf, 2) = users; + mapif_sendall (buf, 6); +} + +void check_connect_login_server (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + if (login_fd <= 0 || session[login_fd] == NULL) + { + printf ("Attempt to connect to login-server...\n"); + if ((login_fd = make_connection (login_ip, login_port)) < 0) + return; + session[login_fd]->func_parse = parse_tologin; + realloc_fifo (login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + WFIFOW (login_fd, 0) = 0x2710; + memset (WFIFOP (login_fd, 2), 0, 24); + memcpy (WFIFOP (login_fd, 2), userid, + strlen (userid) < 24 ? strlen (userid) : 24); + memset (WFIFOP (login_fd, 26), 0, 24); + memcpy (WFIFOP (login_fd, 26), passwd, + strlen (passwd) < 24 ? strlen (passwd) : 24); + WFIFOL (login_fd, 50) = 0; + WFIFOL (login_fd, 54) = char_ip; + WFIFOL (login_fd, 58) = char_port; + memset (WFIFOP (login_fd, 60), 0, 20); + memcpy (WFIFOP (login_fd, 60), server_name, + strlen (server_name) < 20 ? strlen (server_name) : 20); + WFIFOW (login_fd, 80) = 0; + WFIFOW (login_fd, 82) = char_maintenance; + WFIFOW (login_fd, 84) = char_new; + WFIFOSET (login_fd, 86); + } +} + +//---------------------------------------------------------- +// Return numerical value of a switch configuration by [Yor] +// on/off, english, français, deutsch, español +//---------------------------------------------------------- +int config_switch (const char *str) +{ + if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 + || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 + || strcasecmp (str, "si") == 0) + return 1; + if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 + || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) + return 0; + + return atoi (str); +} + +//------------------------------------------- +// Reading Lan Support configuration by [Yor] +//------------------------------------------- +int lan_config_read (const char *lancfgName) +{ + int j; + struct hostent *h = NULL; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + // set default configuration + strncpy (lan_map_ip, "127.0.0.1", sizeof (lan_map_ip)); + subneti[0] = 127; + subneti[1] = 0; + subneti[2] = 0; + subneti[3] = 1; + for (j = 0; j < 4; j++) + subnetmaski[j] = 255; + + fp = fopen_ (lancfgName, "r"); + + if (fp == NULL) + { + printf ("LAN support configuration file not found: %s\n", lancfgName); + return 1; + } + + printf ("---start reading of Lan Support configuration...\n"); + + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof (line) - 1] = '\0'; + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars (w1); + remove_control_chars (w2); + if (strcasecmp (w1, "lan_map_ip") == 0) + { // Read map-server Lan IP Address + h = gethostbyname (w2); + if (h != NULL) + { + sprintf (lan_map_ip, "%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]); + } + else + { + strncpy (lan_map_ip, w2, sizeof (lan_map_ip)); + lan_map_ip[sizeof (lan_map_ip) - 1] = 0; + } + printf ("LAN IP of map-server: %s.\n", lan_map_ip); + } + else if (strcasecmp (w1, "subnet") == 0) + { // Read Subnetwork + for (j = 0; j < 4; j++) + subneti[j] = 0; + h = gethostbyname (w2); + if (h != NULL) + { + for (j = 0; j < 4; j++) + subneti[j] = (unsigned char) h->h_addr[j]; + } + else + { + sscanf (w2, "%d.%d.%d.%d", &subneti[0], &subneti[1], + &subneti[2], &subneti[3]); + } + printf ("Sub-network of the map-server: %d.%d.%d.%d.\n", + subneti[0], subneti[1], subneti[2], subneti[3]); + } + else if (strcasecmp (w1, "subnetmask") == 0) + { // Read Subnetwork Mask + for (j = 0; j < 4; j++) + subnetmaski[j] = 255; + h = gethostbyname (w2); + if (h != NULL) + { + for (j = 0; j < 4; j++) + subnetmaski[j] = (unsigned char) h->h_addr[j]; + } + else + { + sscanf (w2, "%d.%d.%d.%d", &subnetmaski[0], &subnetmaski[1], + &subnetmaski[2], &subnetmaski[3]); + } + printf ("Sub-network mask of the map-server: %d.%d.%d.%d.\n", + subnetmaski[0], subnetmaski[1], subnetmaski[2], + subnetmaski[3]); + } + } + fclose_ (fp); + + // sub-network check of the map-server + { + unsigned int a0, a1, a2, a3; + unsigned char p[4]; + sscanf (lan_map_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); + p[0] = a0; + p[1] = a1; + p[2] = a2; + p[3] = a3; + printf ("LAN test of LAN IP of the map-server: "); + if (lan_ip_check (p) == 0) + { + printf + ("\033[1;31m***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network.\033[0m\n"); + } + } + + printf ("---End reading of Lan Support configuration...\n"); + + return 0; +} + +int char_config_read (const char *cfgName) +{ + struct hostent *h = NULL; + char line[1024], w1[1024], w2[1024]; + FILE *fp = fopen_ (cfgName, "r"); + + if (fp == NULL) + { + printf ("Configuration file not found: %s.\n", cfgName); + exit (1); + } + + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof (line) - 1] = '\0'; + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars (w1); + remove_control_chars (w2); + if (strcasecmp (w1, "userid") == 0) + { + memcpy (userid, w2, 24); + } + else if (strcasecmp (w1, "passwd") == 0) + { + memcpy (passwd, w2, 24); + } + else if (strcasecmp (w1, "server_name") == 0) + { + memcpy (server_name, w2, sizeof (server_name)); + server_name[sizeof (server_name) - 1] = '\0'; + printf ("%s server has been intialized\n", w2); + } + else if (strcasecmp (w1, "wisp_server_name") == 0) + { + if (strlen (w2) >= 4) + { + strncpy (wisp_server_name, w2, sizeof (wisp_server_name)); + wisp_server_name[sizeof (wisp_server_name) - 1] = '\0'; + } + } + else if (strcasecmp (w1, "login_ip") == 0) + { + h = gethostbyname (w2); + if (h != NULL) + { + printf ("Login server IP address : %s -> %d.%d.%d.%d\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 (login_ip_str, "%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]); + } + else + memcpy (login_ip_str, w2, 16); + } + else if (strcasecmp (w1, "login_port") == 0) + { + login_port = atoi (w2); + } + else if (strcasecmp (w1, "char_ip") == 0) + { + h = gethostbyname (w2); + if (h != NULL) + { + printf ("Character server IP address : %s -> %d.%d.%d.%d\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 (char_ip_str, "%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]); + } + else + memcpy (char_ip_str, w2, 16); + } + else if (strcasecmp (w1, "char_port") == 0) + { + char_port = atoi (w2); + } + else if (strcasecmp (w1, "char_maintenance") == 0) + { + char_maintenance = atoi (w2); + } + else if (strcasecmp (w1, "char_new") == 0) + { + char_new = atoi (w2); + } + else if (strcasecmp (w1, "email_creation") == 0) + { + email_creation = config_switch (w2); + } + else if (strcasecmp (w1, "char_txt") == 0) + { + strcpy (char_txt, w2); + } + else if (strcasecmp (w1, "backup_txt") == 0) + { //By zanetheinsane + strcpy (backup_txt, w2); + } + else if (strcasecmp (w1, "backup_txt_flag") == 0) + { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. By [Yor] + backup_txt_flag = config_switch (w2); + } + else if (strcasecmp (w1, "max_connect_user") == 0) + { + max_connect_user = atoi (w2); + if (max_connect_user < 0) + max_connect_user = 0; // unlimited online players + } + else if (strcasecmp (w1, "check_ip_flag") == 0) + { + check_ip_flag = config_switch (w2); + } + else if (strcasecmp (w1, "autosave_time") == 0) + { + autosave_interval = atoi (w2) * 1000; + if (autosave_interval <= 0) + autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; + } + else if (strcasecmp (w1, "start_point") == 0) + { + char map[32]; + int x, y; + if (sscanf (w2, "%[^,],%d,%d", map, &x, &y) < 3) + continue; + if (strstr (map, ".gat") != NULL) + { // Verify at least if '.gat' is in the map name + memcpy (start_point.map, map, 16); + start_point.x = x; + start_point.y = y; + } + } + else if (strcasecmp (w1, "start_zeny") == 0) + { + start_zeny = atoi (w2); + if (start_zeny < 0) + start_zeny = 0; + } + else if (strcasecmp (w1, "start_weapon") == 0) + { + start_weapon = atoi (w2); + if (start_weapon < 0) + start_weapon = 0; + } + else if (strcasecmp (w1, "start_armor") == 0) + { + start_armor = atoi (w2); + if (start_armor < 0) + start_armor = 0; + } + else if (strcasecmp (w1, "unknown_char_name") == 0) + { + strcpy (unknown_char_name, w2); + unknown_char_name[24] = 0; + } + else if (strcasecmp (w1, "char_log_filename") == 0) + { + strcpy (char_log_filename, w2); + } + else if (strcasecmp (w1, "name_ignoring_case") == 0) + { + name_ignoring_case = config_switch (w2); + } + else if (strcasecmp (w1, "char_name_option") == 0) + { + char_name_option = atoi (w2); + } + else if (strcasecmp (w1, "char_name_letters") == 0) + { + strcpy (char_name_letters, w2); +// online files options + } + else if (strcasecmp (w1, "online_txt_filename") == 0) + { + strcpy (online_txt_filename, w2); + } + else if (strcasecmp (w1, "online_html_filename") == 0) + { + strcpy (online_html_filename, w2); + } + else if (strcasecmp (w1, "online_sorting_option") == 0) + { + online_sorting_option = atoi (w2); + } + else if (strcasecmp (w1, "online_display_option") == 0) + { + online_display_option = atoi (w2); + } + else if (strcasecmp (w1, "online_gm_display_min_level") == 0) + { // minimum GM level to display 'GM' when we want to display it + online_gm_display_min_level = atoi (w2); + if (online_gm_display_min_level < 5) // send online file every 5 seconds to player is enough + online_gm_display_min_level = 5; + } + else if (strcasecmp (w1, "online_refresh_html") == 0) + { + online_refresh_html = atoi (w2); + if (online_refresh_html < 1) + online_refresh_html = 1; + } + else if (strcasecmp (w1, "anti_freeze_enable") == 0) + { + anti_freeze_enable = config_switch (w2); + } + else if (strcasecmp (w1, "anti_freeze_interval") == 0) + { + ANTI_FREEZE_INTERVAL = atoi (w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } + else if (strcasecmp (w1, "import") == 0) + { + char_config_read (w2); + } + } + fclose_ (fp); + + return 0; +} + +void term_func (void) +{ + int i; + + // write online players files with no player + for (i = 0; i < char_num; i++) + online_chars[i] = -1; + create_online_files (); + free (online_chars); + + mmo_char_sync (); + inter_save (); + + if (gm_account != NULL) + free (gm_account); + + free (char_dat); + delete_session (login_fd); + delete_session (char_fd); + + char_log ("----End of char-server (normal end with closing of all files).\n"); +} + +int do_init (int argc, char **argv) +{ + int i; + + // a newline in the log... + char_log (""); + char_log ("The char-server starting...\n"); + + char_config_read ((argc < 2) ? CHAR_CONF_NAME : argv[1]); + lan_config_read ((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME); + + login_ip = inet_addr (login_ip_str); + char_ip = inet_addr (char_ip_str); + + for (i = 0; i < MAX_MAP_SERVERS; i++) + { + memset (&server[i], 0, sizeof (struct mmo_map_server)); + server_fd[i] = -1; + } + + mmo_char_init (); + + update_online = time (NULL); + create_online_files (); // update online players files at start of the server + + inter_init ((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 + +// set_termfunc (do_final); + set_defaultparse (parse_char); + + char_fd = make_listen_port (char_port); + +// add_timer_func_list (check_connect_login_server, "check_connect_login_server"); +// add_timer_func_list (send_users_tologin, "send_users_tologin"); +// add_timer_func_list (mmo_char_sync_timer, "mmo_char_sync_timer"); + + i = add_timer_interval (gettick () + 1000, check_connect_login_server, 0, + 0, 10 * 1000); + i = add_timer_interval (gettick () + 1000, send_users_tologin, 0, 0, + 5 * 1000); + i = add_timer_interval (gettick () + autosave_interval, + mmo_char_sync_timer, 0, 0, autosave_interval); + + if (anti_freeze_enable > 0) + { +// add_timer_func_list (map_anti_freeze_system, "map_anti_freeze_system"); + i = add_timer_interval (gettick () + 1000, map_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); // checks every X seconds user specifies + } + + char_log ("The char-server is ready (Server is listening on the port %d).\n", + char_port); + + printf + ("The char-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", + char_port); + + return 0; +} diff --git a/src/char/char.h b/src/char/char.h deleted file mode 100644 index cc64024..0000000 --- a/src/char/char.h +++ /dev/null @@ -1,32 +0,0 @@ -// $Id: char.h,v 1.1.1.1 2004/09/10 17:26:50 MagicalTux Exp $ -#ifndef _CHAR_H_ -#define _CHAR_H_ - -#define MAX_MAP_SERVERS 30 - -#define CHAR_CONF_NAME "conf/char_athena.conf" - -#define LOGIN_LAN_CONF_NAME "conf/lan_support.conf" - -#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 - -struct mmo_map_server -{ - long ip; - short port; - int users; - char map[MAX_MAP_PER_SERVER][16]; -}; - -int search_character_index (char *character_name); -char *search_character_name (int index); - -int mapif_sendall (char *buf, unsigned int len); -int mapif_sendallwos (int fd, unsigned char *buf, unsigned int len); -int mapif_send (int fd, unsigned char *buf, unsigned int len); - -int char_log (char *fmt, ...); - -extern int autosave_interval; - -#endif diff --git a/src/char/char.hpp b/src/char/char.hpp new file mode 100644 index 0000000..22b7678 --- /dev/null +++ b/src/char/char.hpp @@ -0,0 +1,32 @@ +// $Id: char.h,v 1.1.1.1 2004/09/10 17:26:50 MagicalTux Exp $ +#ifndef CHAR_HPP +#define CHAR_HPP + +#define MAX_MAP_SERVERS 30 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +#define LOGIN_LAN_CONF_NAME "conf/lan_support.conf" + +#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 + +struct mmo_map_server +{ + long ip; + short port; + int users; + char map[MAX_MAP_PER_SERVER][16]; +}; + +int search_character_index (char *character_name); +char *search_character_name (int index); + +int mapif_sendall (char *buf, unsigned int len); +int mapif_sendallwos (int fd, unsigned char *buf, unsigned int len); +int mapif_send (int fd, unsigned char *buf, unsigned int len); + +int char_log (char *fmt, ...); + +extern int autosave_interval; + +#endif diff --git a/src/char/int_guild.c b/src/char/int_guild.c deleted file mode 100644 index 05aae6b..0000000 --- a/src/char/int_guild.c +++ /dev/null @@ -1,1757 +0,0 @@ -// $Id: int_guild.c,v 1.2 2004/09/25 19:36:53 Akitasha Exp $ -#include "inter.h" -#include "int_guild.h" -#include "int_storage.h" -#include "../common/mmo.h" -#include "char.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" - -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -char guild_txt[1024] = "save/guild.txt"; -char castle_txt[1024] = "save/castle.txt"; - -static struct dbt *guild_db; -static struct dbt *castle_db; - -static int guild_newid = 10000; - -static int guild_exp[100]; - -int mapif_parse_GuildLeave (int fd, int guild_id, int account_id, - int char_id, int flag, const char *mes); -int mapif_guild_broken (int guild_id, int flag); -int guild_check_empty (struct guild *g); -int guild_calcinfo (struct guild *g); -int mapif_guild_basicinfochanged (int guild_id, int type, const void *data, - int len); -int mapif_guild_info (int fd, struct guild *g); -void guild_break_sub (db_key_t key, db_val_t data, va_list ap); - -// ギルドデータの文字列への変換 -int inter_guild_tostr (char *str, struct guild *g) -{ - int i, c, len; - - // 基本データ - len = sprintf (str, "%d\t%s\t%s\t%d,%d,%d,%d,%d\t%s#\t%s#\t", - g->guild_id, g->name, g->master, - g->guild_lv, g->max_member, g->exp, g->skill_point, - g->castle_id, g->mes1, g->mes2); - // メンバー - for (i = 0; i < g->max_member; i++) - { - struct guild_member *m = &g->member[i]; - len += sprintf (str + len, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%s\t", - m->account_id, m->char_id, - m->hair, m->hair_color, m->gender, - m->pc_class, m->lv, m->exp, m->exp_payper, m->position, - ((m->account_id > 0) ? m->name : "-")); - } - // 役職 - for (i = 0; i < MAX_GUILDPOSITION; i++) - { - struct guild_position *p = &g->position[i]; - len += - sprintf (str + len, "%d,%d\t%s#\t", p->mode, p->exp_mode, - p->name); - } - // エンブレム - len += sprintf (str + len, "%d,%d,", g->emblem_len, g->emblem_id); - for (i = 0; i < g->emblem_len; i++) - { - len += - sprintf (str + len, "%02x", (unsigned char) (g->emblem_data[i])); - } - len += sprintf (str + len, "$\t"); - // 同盟リスト - c = 0; - for (i = 0; i < MAX_GUILDALLIANCE; i++) - if (g->alliance[i].guild_id > 0) - c++; - len += sprintf (str + len, "%d\t", c); - for (i = 0; i < MAX_GUILDALLIANCE; i++) - { - struct guild_alliance *a = &g->alliance[i]; - if (a->guild_id > 0) - len += - sprintf (str + len, "%d,%d\t%s\t", a->guild_id, a->opposition, - a->name); - } - // 追放リスト - c = 0; - for (i = 0; i < MAX_GUILDEXPLUSION; i++) - if (g->explusion[i].account_id > 0) - c++; - len += sprintf (str + len, "%d\t", c); - for (i = 0; i < MAX_GUILDEXPLUSION; i++) - { - struct guild_explusion *e = &g->explusion[i]; - if (e->account_id > 0) - len += sprintf (str + len, "%d,%d,%d,%d\t%s\t%s\t%s#\t", - e->account_id, e->rsv1, e->rsv2, e->rsv3, - e->name, e->acc, e->mes); - } - // ギルドスキル - for (i = 0; i < MAX_GUILDSKILL; i++) - { - len += sprintf (str + len, "%d,%d ", g->skill[i].id, g->skill[i].lv); - } - len += sprintf (str + len, "\t"); - - return 0; -} - -// ギルドデータの文字列からの変換 -int inter_guild_fromstr (char *str, struct guild *g) -{ - int i, j, c; - int tmp_int[16]; - char tmp_str[4][256]; - char tmp_str2[4096]; - char *pstr; - - // 基本データ - memset (g, 0, sizeof (struct guild)); - if (sscanf - (str, "%d\t%[^\t]\t%[^\t]\t%d,%d,%d,%d,%d\t%[^\t]\t%[^\t]\t", - &tmp_int[0], tmp_str[0], tmp_str[1], &tmp_int[1], &tmp_int[2], - &tmp_int[3], &tmp_int[4], &tmp_int[5], tmp_str[2], tmp_str[3]) < 8) - return 1; - - g->guild_id = tmp_int[0]; - g->guild_lv = tmp_int[1]; - g->max_member = tmp_int[2]; - g->exp = tmp_int[3]; - g->skill_point = tmp_int[4]; - g->castle_id = tmp_int[5]; - memcpy (g->name, tmp_str[0], 24); - memcpy (g->master, tmp_str[1], 24); - memcpy (g->mes1, tmp_str[2], 60); - memcpy (g->mes2, tmp_str[3], 120); - g->mes1[strlen (g->mes1) - 1] = 0; - g->mes2[strlen (g->mes2) - 1] = 0; - - for (j = 0; j < 6 && str != NULL; j++) // 位置スキップ - str = strchr (str + 1, '\t'); -// printf("GuildBaseInfo OK\n"); - - // メンバー - for (i = 0; i < g->max_member; i++) - { - struct guild_member *m = &g->member[i]; - if (sscanf (str + 1, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%[^\t]\t", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], - &tmp_int[8], &tmp_int[9], tmp_str[0]) < 11) - return 1; - m->account_id = tmp_int[0]; - m->char_id = 0 /*tmp_int[1]*/; - m->hair = tmp_int[2]; - m->hair_color = tmp_int[3]; - m->gender = tmp_int[4]; - m->pc_class = tmp_int[5]; - m->lv = tmp_int[6]; - m->exp = tmp_int[7]; - m->exp_payper = tmp_int[8]; - m->position = tmp_int[9]; - memcpy (m->name, tmp_str[0], 24); - - for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr (str + 1, '\t'); - } -// printf("GuildMemberInfo OK\n"); - // 役職 - i = 0; - while (sscanf (str + 1, "%d,%d%n", &tmp_int[0], &tmp_int[1], &j) == 2 - && str[1 + j] == '\t') - { - struct guild_position *p = &g->position[i]; - if (sscanf - (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], - tmp_str[0]) < 3) - return 1; - p->mode = tmp_int[0]; - p->exp_mode = tmp_int[1]; - tmp_str[0][strlen (tmp_str[0]) - 1] = 0; - memcpy (p->name, tmp_str[0], 24); - - for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr (str + 1, '\t'); - i++; - } -// printf("GuildPositionInfo OK\n"); - // エンブレム - tmp_int[1] = 0; - if (sscanf (str + 1, "%d,%d,%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str2) - < 3 && sscanf (str + 1, "%d,%[^\t]\t", &tmp_int[0], tmp_str2) < 2) - return 1; - g->emblem_len = tmp_int[0]; - g->emblem_id = tmp_int[1]; - for (i = 0, pstr = tmp_str2; i < g->emblem_len; i++, pstr += 2) - { - int c1 = pstr[0], c2 = pstr[1], x1 = 0, x2 = 0; - if (c1 >= '0' && c1 <= '9') - x1 = c1 - '0'; - if (c1 >= 'a' && c1 <= 'f') - x1 = c1 - 'a' + 10; - if (c1 >= 'A' && c1 <= 'F') - x1 = c1 - 'A' + 10; - if (c2 >= '0' && c2 <= '9') - x2 = c2 - '0'; - if (c2 >= 'a' && c2 <= 'f') - x2 = c2 - 'a' + 10; - if (c2 >= 'A' && c2 <= 'F') - x2 = c2 - 'A' + 10; - g->emblem_data[i] = (x1 << 4) | x2; - } -// printf("GuildEmblemInfo OK\n"); - str = strchr (str + 1, '\t'); // 位置スキップ - - // 同盟リスト - if (sscanf (str + 1, "%d\t", &c) < 1) - return 1; - str = strchr (str + 1, '\t'); // 位置スキップ - for (i = 0; i < c; i++) - { - struct guild_alliance *a = &g->alliance[i]; - if (sscanf - (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], - tmp_str[0]) < 3) - return 1; - a->guild_id = tmp_int[0]; - a->opposition = tmp_int[1]; - memcpy (a->name, tmp_str[0], 24); - - for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr (str + 1, '\t'); - } -// printf("GuildAllianceInfo OK\n"); - // 追放リスト - if (sscanf (str + 1, "%d\t", &c) < 1) - return 1; - str = strchr (str + 1, '\t'); // 位置スキップ - for (i = 0; i < c; i++) - { - struct guild_explusion *e = &g->explusion[i]; - if (sscanf (str + 1, "%d,%d,%d,%d\t%[^\t]\t%[^\t]\t%[^\t]\t", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - tmp_str[0], tmp_str[1], tmp_str[2]) < 6) - return 1; - e->account_id = tmp_int[0]; - e->rsv1 = tmp_int[1]; - e->rsv2 = tmp_int[2]; - e->rsv3 = tmp_int[3]; - memcpy (e->name, tmp_str[0], 24); - memcpy (e->acc, tmp_str[1], 24); - tmp_str[2][strlen (tmp_str[2]) - 1] = 0; - memcpy (e->mes, tmp_str[2], 40); - - for (j = 0; j < 4 && str != NULL; j++) // 位置スキップ - str = strchr (str + 1, '\t'); - } -// printf("GuildExplusionInfo OK\n"); - // ギルドスキル - for (i = 0; i < MAX_GUILDSKILL; i++) - { - if (sscanf (str + 1, "%d,%d ", &tmp_int[0], &tmp_int[1]) < 2) - break; - g->skill[i].id = tmp_int[0]; - g->skill[i].lv = tmp_int[1]; - str = strchr (str + 1, ' '); - } - str = strchr (str + 1, '\t'); -// printf("GuildSkillInfo OK\n"); - - return 0; -} - -// ギルド城データの文字列への変換 -int inter_guildcastle_tostr (char *str, struct guild_castle *gc) -{ - int len; - - len = sprintf (str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // added Guardian HP [Valaris] - gc->castle_id, gc->guild_id, gc->economy, gc->defense, - gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, - gc->createTime, gc->visibleC, gc->visibleG0, gc->visibleG1, - gc->visibleG2, gc->visibleG3, gc->visibleG4, gc->visibleG5, - gc->visibleG6, gc->visibleG7, gc->Ghp0, gc->Ghp1, gc->Ghp2, - gc->Ghp3, gc->Ghp4, gc->Ghp5, gc->Ghp6, gc->Ghp7); - - return 0; -} - -// ギルド城データの文字列からの変換 -int inter_guildcastle_fromstr (char *str, struct guild_castle *gc) -{ - int tmp_int[26]; - - memset (gc, 0, sizeof (struct guild_castle)); - // new structure of guild castle - if (sscanf - (str, - "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], - &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], - &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], - &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], - &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], &tmp_int[24], - &tmp_int[25]) == 26) - { - gc->castle_id = tmp_int[0]; - gc->guild_id = tmp_int[1]; - gc->economy = tmp_int[2]; - gc->defense = tmp_int[3]; - gc->triggerE = tmp_int[4]; - gc->triggerD = tmp_int[5]; - gc->nextTime = tmp_int[6]; - gc->payTime = tmp_int[7]; - gc->createTime = tmp_int[8]; - gc->visibleC = tmp_int[9]; - gc->visibleG0 = tmp_int[10]; - gc->visibleG1 = tmp_int[11]; - gc->visibleG2 = tmp_int[12]; - gc->visibleG3 = tmp_int[13]; - gc->visibleG4 = tmp_int[14]; - gc->visibleG5 = tmp_int[15]; - gc->visibleG6 = tmp_int[16]; - gc->visibleG7 = tmp_int[17]; - gc->Ghp0 = tmp_int[18]; - gc->Ghp1 = tmp_int[19]; - gc->Ghp2 = tmp_int[20]; - gc->Ghp3 = tmp_int[21]; - gc->Ghp4 = tmp_int[22]; - gc->Ghp5 = tmp_int[23]; - gc->Ghp6 = tmp_int[24]; - gc->Ghp7 = tmp_int[25]; // end additions [Valaris] - // old structure of guild castle - } - else if (sscanf - (str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], - &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], - &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], - &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17]) == 18) - { - gc->castle_id = tmp_int[0]; - gc->guild_id = tmp_int[1]; - gc->economy = tmp_int[2]; - gc->defense = tmp_int[3]; - gc->triggerE = tmp_int[4]; - gc->triggerD = tmp_int[5]; - gc->nextTime = tmp_int[6]; - gc->payTime = tmp_int[7]; - gc->createTime = tmp_int[8]; - gc->visibleC = tmp_int[9]; - gc->visibleG0 = tmp_int[10]; - gc->visibleG1 = tmp_int[11]; - gc->visibleG2 = tmp_int[12]; - gc->visibleG3 = tmp_int[13]; - gc->visibleG4 = tmp_int[14]; - gc->visibleG5 = tmp_int[15]; - gc->visibleG6 = tmp_int[16]; - gc->visibleG7 = tmp_int[17]; - if (gc->visibleG0 == 1) - gc->Ghp0 = 15670 + 2000 * gc->defense; - else - gc->Ghp0 = 0; - if (gc->visibleG1 == 1) - gc->Ghp1 = 15670 + 2000 * gc->defense; - else - gc->Ghp1 = 0; - if (gc->visibleG2 == 1) - gc->Ghp2 = 15670 + 2000 * gc->defense; - else - gc->Ghp2 = 0; - if (gc->visibleG3 == 1) - gc->Ghp3 = 30214 + 2000 * gc->defense; - else - gc->Ghp3 = 0; - if (gc->visibleG4 == 1) - gc->Ghp4 = 30214 + 2000 * gc->defense; - else - gc->Ghp4 = 0; - if (gc->visibleG5 == 1) - gc->Ghp5 = 28634 + 2000 * gc->defense; - else - gc->Ghp5 = 0; - if (gc->visibleG6 == 1) - gc->Ghp6 = 28634 + 2000 * gc->defense; - else - gc->Ghp6 = 0; - if (gc->visibleG7 == 1) - gc->Ghp7 = 28634 + 2000 * gc->defense; - else - gc->Ghp7 = 0; - } - else - { - return 1; - } - - return 0; -} - -// ギルド関連データベース読み込み -int inter_guild_readdb (void) -{ - int i; - FILE *fp; - char line[1024]; - - fp = fopen_ ("db/exp_guild.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/exp_guild.txt\n"); - return 1; - } - i = 0; - while (fgets (line, sizeof (line) - 1, fp) && i < 100) - { - if (line[0] == '/' && line[1] == '/') - continue; - guild_exp[i] = atoi (line); - i++; - } - fclose_ (fp); - - return 0; -} - -// ギルドデータの読み込み -int inter_guild_init (void) -{ - char line[16384]; - struct guild *g; - struct guild_castle *gc; - FILE *fp; - int i, j, c = 0; - - inter_guild_readdb (); - - guild_db = numdb_init (); - castle_db = numdb_init (); - - if ((fp = fopen_ (guild_txt, "r")) == NULL) - return 1; - while (fgets (line, sizeof (line) - 1, fp)) - { - j = 0; - if (sscanf (line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 - && guild_newid <= i) - { - guild_newid = i; - continue; - } - CREATE (g, struct guild, 1); - if (inter_guild_fromstr (line, g) == 0 && g->guild_id > 0) - { - if (g->guild_id >= guild_newid) - guild_newid = g->guild_id + 1; - numdb_insert (guild_db, g->guild_id, g); - guild_check_empty (g); - guild_calcinfo (g); - } - else - { - printf ("int_guild: broken data [%s] line %d\n", guild_txt, c); - free (g); - } - c++; - } - fclose_ (fp); -// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c); - - c = 0; //カウンタ初期化 - - if ((fp = fopen_ (castle_txt, "r")) == NULL) - { - return 1; - } - - while (fgets (line, sizeof (line) - 1, fp)) - { - CREATE (gc, struct guild_castle, 1); - if (inter_guildcastle_fromstr (line, gc) == 0) - { - numdb_insert (castle_db, gc->castle_id, gc); - } - else - { - printf ("int_guild: broken data [%s] line %d\n", castle_txt, c); - free (gc); - } - c++; - } - - if (!c) - { - printf (" %s - making Default Data...\n", castle_txt); - //デフォルトデータを作成 - for (i = 0; i < MAX_GUILDCASTLE; i++) - { - CREATE (gc, struct guild_castle, 1); - gc->castle_id = i; - gc->guild_id = 0; - gc->economy = 0; - gc->defense = 0; - gc->triggerE = 0; - gc->triggerD = 0; - gc->nextTime = 0; - gc->payTime = 0; - gc->createTime = 0; - gc->visibleC = 0; - gc->visibleG0 = 0; - gc->visibleG1 = 0; - gc->visibleG2 = 0; - gc->visibleG3 = 0; - gc->visibleG4 = 0; - gc->visibleG5 = 0; - gc->visibleG6 = 0; - gc->visibleG7 = 0; - gc->Ghp0 = 0; // guardian HP [Valaris] - gc->Ghp1 = 0; - gc->Ghp2 = 0; - gc->Ghp3 = 0; - gc->Ghp4 = 0; - gc->Ghp5 = 0; - gc->Ghp6 = 0; - gc->Ghp7 = 0; // end additions [Valaris] - numdb_insert (castle_db, gc->castle_id, gc); - } - printf (" %s - making done\n", castle_txt); - return 0; - } - - fclose_ (fp); - - return 0; -} - -struct guild *inter_guild_search (int guild_id) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - - return g; -} - -// ギルドデータのセーブ用 -void inter_guild_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[16384]; - FILE *fp; - - inter_guild_tostr (line, (struct guild *) data); - fp = va_arg (ap, FILE *); - fprintf (fp, "%s\n", line); -} - -// ギルド城データのセーブ用 -void inter_castle_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[16384]; - FILE *fp; - - inter_guildcastle_tostr (line, (struct guild_castle *) data); - fp = va_arg (ap, FILE *); - fprintf (fp, "%s\n", line); -} - -// ギルドデータのセーブ -int inter_guild_save (void) -{ - FILE *fp; - int lock; - - if ((fp = lock_fopen (guild_txt, &lock)) == NULL) - { - printf ("int_guild: cant write [%s] !!! data is lost !!!\n", - guild_txt); - return 1; - } - numdb_foreach (guild_db, inter_guild_save_sub, fp); -// fprintf(fp, "%d\t%%newid%%\n", guild_newid); - lock_fclose (fp, guild_txt, &lock); -// printf("int_guild: %s saved.\n", guild_txt); - - if ((fp = lock_fopen (castle_txt, &lock)) == NULL) - { - printf ("int_guild: cant write [%s] !!! data is lost !!!\n", - castle_txt); - return 1; - } - numdb_foreach (castle_db, inter_castle_save_sub, fp); - lock_fclose (fp, castle_txt, &lock); - - return 0; -} - -// ギルド名検索用 -void search_guildname_sub (db_key_t key, db_val_t 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 (strcasecmp (g->name, str) == 0) - *dst = g; -} - -// ギルド名検索 -struct guild *search_guildname (char *str) -{ - struct guild *g = NULL; - numdb_foreach (guild_db, search_guildname_sub, str, &g); - return g; -} - -// ギルドが空かどうかチェック -int guild_check_empty (struct guild *g) -{ - int i; - - for (i = 0; i < g->max_member; i++) - { - if (g->member[i].account_id > 0) - { - return 0; - } - } - // 誰もいないので解散 - numdb_foreach (guild_db, guild_break_sub, g->guild_id); - numdb_erase (guild_db, g->guild_id); - inter_guild_storage_delete (g->guild_id); - mapif_guild_broken (g->guild_id, 0); - free (g); - - return 1; -} - -// キャラの競合がないかチェック用 -void guild_check_conflict_sub (db_key_t key, db_val_t data, va_list ap) -{ - struct guild *g = (struct guild *) data; - int guild_id, account_id, char_id, i; - - guild_id = va_arg (ap, int); - account_id = va_arg (ap, int); - char_id = va_arg (ap, int); - - if (g->guild_id == guild_id) // 本来の所属なので問題なし - return; - - for (i = 0; i < MAX_GUILD; i++) - { - if (g->member[i].account_id == account_id) - { - // 別のギルドに偽の所属データがあるので脱退 - printf ("int_guild: guild conflict! %d,%d %d!=%d\n", account_id, - char_id, guild_id, g->guild_id); - mapif_parse_GuildLeave (-1, g->guild_id, account_id, 0 /*char_id*/, 0, - "**データ競合**"); - } - } -} - -// キャラの競合がないかチェック -int guild_check_conflict (int guild_id, int account_id, int char_id) -{ - numdb_foreach (guild_db, guild_check_conflict_sub, guild_id, account_id, - 0 /*char_id*/); - - return 0; -} - -int guild_nextexp (int level) -{ - if (level < 100) - return guild_exp[level - 1]; - - return 0; -} - -// ギルドスキルがあるか確認 -int guild_checkskill (struct guild *g, int id) -{ - return g->skill[id - 10000].lv; -} - -// ギルドの情報の再計算 -int guild_calcinfo (struct guild *g) -{ - int i, c, nextexp; - struct guild before = *g; - - // スキルIDの設定 - for (i = 0; i < 5; i++) - g->skill[i].id = i + 10000; - - // ギルドレベル - if (g->guild_lv <= 0) - g->guild_lv = 1; - nextexp = guild_nextexp (g->guild_lv); - if (nextexp > 0) - { - while (g->exp >= nextexp) - { // レベルアップ処理 - g->exp -= nextexp; - g->guild_lv++; - g->skill_point++; - nextexp = guild_nextexp (g->guild_lv); - } - } - - // ギルドの次の経験値 - g->next_exp = guild_nextexp (g->guild_lv); - - // メンバ上限(ギルド拡張適用) - g->max_member = 100 + guild_checkskill (g, 10004) * 2; - - // 平均レベルとオンライン人数 - g->average_lv = 0; - g->connect_member = 0; - c = 0; - for (i = 0; i < g->max_member; i++) - { - if (g->member[i].account_id > 0) - { - g->average_lv += g->member[i].lv; - c++; - if (g->member[i].online > 0) - g->connect_member++; - } - } - g->average_lv /= c; - - // 全データを送る必要がありそう - if (g->max_member != before.max_member || - g->guild_lv != before.guild_lv || - g->skill_point != before.skill_point) - { - mapif_guild_info (-1, g); - return 1; - } - - return 0; -} - -//------------------------------------------------------------------- -// map serverへの通信 - -// ギルド作成可否 -int mapif_guild_created (int fd, int account_id, struct guild *g) -{ - WFIFOW (fd, 0) = 0x3830; - WFIFOL (fd, 2) = account_id; - if (g != NULL) - { - WFIFOL (fd, 6) = g->guild_id; - printf ("int_guild: created! %d %s\n", g->guild_id, g->name); - } - else - { - WFIFOL (fd, 6) = 0; - } - WFIFOSET (fd, 10); - return 0; -} - -// ギルド情報見つからず -int mapif_guild_noinfo (int fd, int guild_id) -{ - WFIFOW (fd, 0) = 0x3831; - WFIFOW (fd, 2) = 8; - WFIFOL (fd, 4) = guild_id; - WFIFOSET (fd, 8); - printf ("int_guild: info not found %d\n", guild_id); - - return 0; -} - -// ギルド情報まとめ送り -int mapif_guild_info (int fd, struct guild *g) -{ - unsigned char buf[4 + sizeof (struct guild)]; - - WBUFW (buf, 0) = 0x3831; - memcpy (buf + 4, g, sizeof (struct guild)); - WBUFW (buf, 2) = 4 + sizeof (struct guild); -// printf("int_guild: sizeof(guild)=%d\n", sizeof(struct guild)); - if (fd < 0) - mapif_sendall (buf, WBUFW (buf, 2)); - else - mapif_send (fd, buf, WBUFW (buf, 2)); -// printf("int_guild: info %d %s\n", p->guild_id, p->name); - - return 0; -} - -// メンバ追加可否 -int mapif_guild_memberadded (int fd, int guild_id, int account_id, - int char_id, int flag) -{ - WFIFOW (fd, 0) = 0x3832; - WFIFOL (fd, 2) = guild_id; - WFIFOL (fd, 6) = account_id; - WFIFOL (fd, 10) = 0 /*char_id*/; - WFIFOB (fd, 14) = flag; - WFIFOSET (fd, 15); - - return 0; -} - -// 脱退/追放通知 -int mapif_guild_leaved (int guild_id, int account_id, int char_id, int flag, - const char *name, const char *mes) -{ - unsigned char buf[79]; - - WBUFW (buf, 0) = 0x3834; - WBUFL (buf, 2) = guild_id; - WBUFL (buf, 6) = account_id; - WBUFL (buf, 10) = 0 /*char_id*/; - WBUFB (buf, 14) = flag; - memcpy (WBUFP (buf, 15), mes, 40); - memcpy (WBUFP (buf, 55), name, 24); - mapif_sendall (buf, 79); - printf ("int_guild: guild leaved %d %d %s %s\n", guild_id, account_id, - name, mes); - - return 0; -} - -// オンライン状態とLv更新通知 -int mapif_guild_memberinfoshort (struct guild *g, int idx) -{ - unsigned char buf[19]; - - WBUFW (buf, 0) = 0x3835; - WBUFL (buf, 2) = g->guild_id; - WBUFL (buf, 6) = g->member[idx].account_id; - WBUFL (buf, 10) = 0 /*g->member[idx].char_id*/; - WBUFB (buf, 14) = g->member[idx].online; - WBUFW (buf, 15) = g->member[idx].lv; - WBUFW (buf, 17) = g->member[idx].pc_class; - mapif_sendall (buf, 19); - return 0; -} - -// 解散通知 -int mapif_guild_broken (int guild_id, int flag) -{ - unsigned char buf[7]; - - WBUFW (buf, 0) = 0x3836; - WBUFL (buf, 2) = guild_id; - WBUFB (buf, 6) = flag; - mapif_sendall (buf, 7); - printf ("int_guild: broken %d\n", guild_id); - - return 0; -} - -// ギルド内発言 -int mapif_guild_message (int guild_id, int account_id, char *mes, int len) -{ - unsigned char buf[len + 12]; - - WBUFW (buf, 0) = 0x3837; - WBUFW (buf, 2) = len + 12; - WBUFL (buf, 4) = guild_id; - WBUFL (buf, 8) = account_id; - memcpy (WBUFP (buf, 12), mes, len); - mapif_sendall (buf, len + 12); - - return 0; -} - -// ギルド基本情報変更通知 -int mapif_guild_basicinfochanged (int guild_id, int type, const void *data, - int len) -{ - unsigned char buf[2048]; - - WBUFW (buf, 0) = 0x3839; - WBUFW (buf, 2) = len + 10; - WBUFL (buf, 4) = guild_id; - WBUFW (buf, 8) = type; - memcpy (WBUFP (buf, 10), data, len); - mapif_sendall (buf, len + 10); - return 0; -} - -// ギルドメンバ情報変更通知 -int mapif_guild_memberinfochanged (int guild_id, int account_id, int char_id, - int type, const void *data, int len) -{ - unsigned char buf[len + 18]; - - WBUFW (buf, 0) = 0x383a; - WBUFW (buf, 2) = len + 18; - WBUFL (buf, 4) = guild_id; - WBUFL (buf, 8) = account_id; - WBUFL (buf, 12) = 0 /*char_id*/; - WBUFW (buf, 16) = type; - memcpy (WBUFP (buf, 18), data, len); - mapif_sendall (buf, len + 18); - - return 0; -} - -// ギルドスキルアップ通知 -int mapif_guild_skillupack (int guild_id, int skill_num, int account_id) -{ - unsigned char buf[14]; - - WBUFW (buf, 0) = 0x383c; - WBUFL (buf, 2) = guild_id; - WBUFL (buf, 6) = skill_num; - WBUFL (buf, 10) = account_id; - mapif_sendall (buf, 14); - - return 0; -} - -// ギルド同盟/敵対通知 -int mapif_guild_alliance (int guild_id1, int guild_id2, int account_id1, - int account_id2, int flag, const char *name1, - const char *name2) -{ - unsigned char buf[67]; - - WBUFW (buf, 0) = 0x383d; - WBUFL (buf, 2) = guild_id1; - WBUFL (buf, 6) = guild_id2; - WBUFL (buf, 10) = account_id1; - WBUFL (buf, 14) = account_id2; - WBUFB (buf, 18) = flag; - memcpy (WBUFP (buf, 19), name1, 24); - memcpy (WBUFP (buf, 43), name2, 24); - mapif_sendall (buf, 67); - - return 0; -} - -// ギルド役職変更通知 -int mapif_guild_position (struct guild *g, int idx) -{ - unsigned char buf[sizeof (struct guild_position) + 12]; - - WBUFW (buf, 0) = 0x383b; - WBUFW (buf, 2) = sizeof (struct guild_position) + 12; - WBUFL (buf, 4) = g->guild_id; - WBUFL (buf, 8) = idx; - memcpy (WBUFP (buf, 12), &g->position[idx], - sizeof (struct guild_position)); - mapif_sendall (buf, WBUFW (buf, 2)); - - return 0; -} - -// ギルド告知変更通知 -int mapif_guild_notice (struct guild *g) -{ - unsigned char buf[186]; - - WBUFW (buf, 0) = 0x383e; - WBUFL (buf, 2) = g->guild_id; - memcpy (WBUFP (buf, 6), g->mes1, 60); - memcpy (WBUFP (buf, 66), g->mes2, 120); - mapif_sendall (buf, 186); - - return 0; -} - -// ギルドエンブレム変更通知 -int mapif_guild_emblem (struct guild *g) -{ - unsigned char buf[2048]; - - WBUFW (buf, 0) = 0x383f; - WBUFW (buf, 2) = g->emblem_len + 12; - WBUFL (buf, 4) = g->guild_id; - WBUFL (buf, 8) = g->emblem_id; - memcpy (WBUFP (buf, 12), g->emblem_data, g->emblem_len); - mapif_sendall (buf, WBUFW (buf, 2)); - - return 0; -} - -int mapif_guild_castle_dataload (int castle_id, int index, int value) -{ - unsigned char buf[9]; - - WBUFW (buf, 0) = 0x3840; - WBUFW (buf, 2) = castle_id; - WBUFB (buf, 4) = index; - WBUFL (buf, 5) = value; - mapif_sendall (buf, 9); - - return 0; -} - -int mapif_guild_castle_datasave (int castle_id, int index, int value) -{ - unsigned char buf[9]; - - WBUFW (buf, 0) = 0x3841; - WBUFW (buf, 2) = castle_id; - WBUFB (buf, 4) = index; - WBUFL (buf, 5) = value; - mapif_sendall (buf, 9); - - return 0; -} - -void mapif_guild_castle_alldataload_sub (db_key_t key, db_val_t data, va_list ap) -{ - int fd = va_arg (ap, int); - int *p = va_arg (ap, int *); - - memcpy (WFIFOP (fd, *p), (struct guild_castle *) data, - sizeof (struct guild_castle)); - (*p) += sizeof (struct guild_castle); -} - -int mapif_guild_castle_alldataload (int fd) -{ - int len = 4; - - WFIFOW (fd, 0) = 0x3842; - numdb_foreach (castle_db, mapif_guild_castle_alldataload_sub, fd, &len); - WFIFOW (fd, 2) = len; - WFIFOSET (fd, len); - - return 0; -} - -//------------------------------------------------------------------- -// map serverからの通信 - -// ギルド作成要求 -int mapif_parse_CreateGuild (int fd, int account_id, char *name, - struct guild_member *master) -{ - struct guild *g; - int i; - - for (i = 0; i < 24 && name[i]; i++) - { - if (!(name[i] & 0xe0) || name[i] == 0x7f) - { - printf ("int_guild: illeagal guild name [%s]\n", name); - mapif_guild_created (fd, account_id, NULL); - return 0; - } - } - - if ((g = search_guildname (name)) != NULL) - { - printf ("int_guild: same name guild exists [%s]\n", name); - mapif_guild_created (fd, account_id, NULL); - return 0; - } - CREATE (g, struct guild, 1); - g->guild_id = guild_newid++; - memcpy (g->name, name, 24); - memcpy (g->master, master->name, 24); - memcpy (&g->member[0], master, sizeof (struct guild_member)); - - g->position[0].mode = 0x11; - strcpy (g->position[0].name, "GuildMaster"); - strcpy (g->position[MAX_GUILDPOSITION - 1].name, "Newbie"); - for (i = 1; i < MAX_GUILDPOSITION - 1; i++) - sprintf (g->position[i].name, "Position %d", i + 1); - - // ここでギルド情報計算が必要と思われる - g->max_member = 100; - g->average_lv = master->lv; - for (i = 0; i < 5; i++) - g->skill[i].id = i + 10000; - - numdb_insert (guild_db, g->guild_id, g); - - mapif_guild_created (fd, account_id, g); - mapif_guild_info (fd, g); - - inter_log ("guild %s (id=%d) created by master %s (id=%d)\n", - name, g->guild_id, master->name, master->account_id); - - return 0; -} - -// ギルド情報要求 -int mapif_parse_GuildInfo (int fd, int guild_id) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g != NULL) - { - guild_calcinfo (g); - mapif_guild_info (fd, g); - } - else - mapif_guild_noinfo (fd, guild_id); - - return 0; -} - -// ギルドメンバ追加要求 -int mapif_parse_GuildAddMember (int fd, int guild_id, struct guild_member *m) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - { - mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, 1); - return 0; - } - - for (int i = 0; i < g->max_member; i++) - { - if (g->member[i].account_id == 0) - { - memcpy (&g->member[i], m, sizeof (struct guild_member)); - mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, - 0); - guild_calcinfo (g); - mapif_guild_info (-1, g); - - return 0; - } - } - mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, 1); - - return 0; -} - -// ギルド脱退/追放要求 -int mapif_parse_GuildLeave (int fd, int guild_id, int account_id, int char_id, - int flag, const char *mes) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g != NULL) - { - for (int i = 0; i < MAX_GUILD; i++) - { - if (g->member[i].account_id == account_id) - { -// printf("%d %d\n", i, (int)(&g->member[i])); -// printf("%d %s\n", i, g->member[i].name); - - if (flag) - { - int j; - // 追放の場合追放リストに入れる - for (j = 0; j < MAX_GUILDEXPLUSION; j++) - { - if (g->explusion[j].account_id == 0) - break; - } - if (j == MAX_GUILDEXPLUSION) - { // 一杯なので古いのを消す - for (j = 0; j < MAX_GUILDEXPLUSION - 1; j++) - g->explusion[j] = g->explusion[j + 1]; - j = MAX_GUILDEXPLUSION - 1; - } - g->explusion[j].account_id = account_id; - memcpy (g->explusion[j].acc, "dummy", 24); - memcpy (g->explusion[j].name, g->member[i].name, 24); - memcpy (g->explusion[j].mes, mes, 40); - } - - mapif_guild_leaved (guild_id, account_id, 0 /*char_id*/, flag, - g->member[i].name, mes); -// printf("%d %d\n", i, (int)(&g->member[i])); -// printf("%d %s\n", i, (&g->member[i])->name); - memset (&g->member[i], 0, sizeof (struct guild_member)); - - if (guild_check_empty (g) == 0) - mapif_guild_info (-1, g); // まだ人がいるのでデータ送信 - - return 0; - } - } - } - return 0; -} - -// オンライン/Lv更新 -int mapif_parse_GuildChangeMemberInfoShort (int fd, int guild_id, - int account_id, int char_id, - int online, int lv, int pc_class) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - - g->connect_member = 0; - - int alv = 0; - int c = 0; - for (int i = 0; i < MAX_GUILD; i++) - { - if (g->member[i].account_id == account_id) - { - g->member[i].online = online; - g->member[i].lv = lv; - g->member[i].pc_class = pc_class; - mapif_guild_memberinfoshort (g, i); - } - if (g->member[i].account_id > 0) - { - alv += g->member[i].lv; - c++; - } - if (g->member[i].online) - g->connect_member++; - } - // 平均レベル - g->average_lv = alv / c; - - return 0; -} - -// ギルド解散処理用(同盟/敵対を解除) -void guild_break_sub (db_key_t key, db_val_t data, va_list ap) -{ - struct guild *g = (struct guild *) data; - int guild_id = va_arg (ap, int); - int i; - - for (i = 0; i < MAX_GUILDALLIANCE; i++) - { - if (g->alliance[i].guild_id == guild_id) - g->alliance[i].guild_id = 0; - } -} - -// ギルド解散要求 -int mapif_parse_BreakGuild (int fd, int guild_id) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - - numdb_foreach (guild_db, guild_break_sub, guild_id); - numdb_erase (guild_db, guild_id); - inter_guild_storage_delete (guild_id); - mapif_guild_broken (guild_id, 0); - - inter_log ("guild %s (id=%d) broken\n", g->name, guild_id); - free (g); - - return 0; -} - -// ギルドメッセージ送信 -int mapif_parse_GuildMessage (int fd, int guild_id, int account_id, char *mes, - int len) -{ - return mapif_guild_message (guild_id, account_id, mes, len); -} - -// ギルド基本データ変更要求 -int mapif_parse_GuildBasicInfoChange (int fd, int guild_id, int type, - const char *data, int len) -{ - short dw = *((short *) data); - - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - - switch (type) - { - case GBI_GUILDLV: - if (dw > 0 && g->guild_lv + dw <= 50) - { - g->guild_lv += dw; - g->skill_point += dw; - } - else if (dw < 0 && g->guild_lv + dw >= 1) - g->guild_lv += dw; - mapif_guild_info (-1, g); - return 0; - default: - printf ("int_guild: GuildBasicInfoChange: Unknown type %d\n", - type); - break; - } - mapif_guild_basicinfochanged (guild_id, type, data, len); - - return 0; -} - -// ギルドメンバデータ変更要求 -int mapif_parse_GuildMemberInfoChange (int fd, int guild_id, int account_id, - int char_id, int type, - const char *data, int len) -{ - int i; - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - - for (i = 0; i < g->max_member; i++) - if (g->member[i].account_id == account_id) - break; - if (i == g->max_member) - { - printf ("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", - account_id, char_id, guild_id, g->name); - return 0; - } - switch (type) - { - case GMI_POSITION: // 役職 - g->member[i].position = *((int *) data); - break; - case GMI_EXP: // EXP - { - int exp, oldexp = g->member[i].exp; - exp = g->member[i].exp = *((unsigned int *) data); - g->exp += (exp - oldexp); - guild_calcinfo (g); // Lvアップ判断 - mapif_guild_basicinfochanged (guild_id, GBI_EXP, &g->exp, 4); - } - break; - default: - printf ("int_guild: GuildMemberInfoChange: Unknown type %d\n", - type); - break; - } - mapif_guild_memberinfochanged (guild_id, account_id, char_id, type, data, - len); - - return 0; -} - -// ギルド役職名変更要求 -int mapif_parse_GuildPosition (int fd, int guild_id, int idx, - struct guild_position *p) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - - if (g == NULL || idx < 0 || idx >= MAX_GUILDPOSITION) - { - return 0; - } - memcpy (&g->position[idx], p, sizeof (struct guild_position)); - mapif_guild_position (g, idx); - printf ("int_guild: position changed %d\n", idx); - - return 0; -} - -// ギルドスキルアップ要求 -int mapif_parse_GuildSkillUp (int fd, int guild_id, int skill_num, - int account_id) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - int idx = skill_num - 10000; - - if (g == NULL || skill_num < 10000) - return 0; - - if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < 10) - { - g->skill[idx].lv++; - g->skill_point--; - if (guild_calcinfo (g) == 0) - mapif_guild_info (-1, g); - mapif_guild_skillupack (guild_id, skill_num, account_id); - printf ("int_guild: skill %d up\n", skill_num); - } - - return 0; -} - -// ギルド同盟要求 -int mapif_parse_GuildAlliance (int fd, int guild_id1, int guild_id2, - int account_id1, int account_id2, int flag) -{ - struct guild *g[2]; - int j, i; - - g[0] = (struct guild *)numdb_search (guild_db, guild_id1); - g[1] = (struct guild *)numdb_search (guild_db, guild_id2); - if (g[0] == NULL || g[1] == NULL) - return 0; - - if (!(flag & 0x8)) - { - for (i = 0; i < 2 - (flag & 1); i++) - { - for (j = 0; j < MAX_GUILDALLIANCE; j++) - if (g[i]->alliance[j].guild_id == 0) - { - g[i]->alliance[j].guild_id = g[1 - i]->guild_id; - memcpy (g[i]->alliance[j].name, g[1 - i]->name, 24); - g[i]->alliance[j].opposition = flag & 1; - break; - } - } - } - else - { // 関係解消 - for (i = 0; i < 2 - (flag & 1); i++) - { - for (j = 0; j < MAX_GUILDALLIANCE; j++) - if (g[i]->alliance[j].guild_id == g[1 - i]->guild_id - && g[i]->alliance[j].opposition == (flag & 1)) - { - g[i]->alliance[j].guild_id = 0; - break; - } - } - } - mapif_guild_alliance (guild_id1, guild_id2, account_id1, account_id2, - flag, g[0]->name, g[1]->name); - - return 0; -} - -// ギルド告知変更要求 -int mapif_parse_GuildNotice (int fd, int guild_id, const char *mes1, - const char *mes2) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - memcpy (g->mes1, mes1, 60); - memcpy (g->mes2, mes2, 120); - - return mapif_guild_notice (g); -} - -// ギルドエンブレム変更要求 -int mapif_parse_GuildEmblem (int fd, int len, int guild_id, int dummy, - const char *data) -{ - struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); - if (g == NULL) - return 0; - memcpy (g->emblem_data, data, len); - g->emblem_len = len; - g->emblem_id++; - - return mapif_guild_emblem (g); -} - -int mapif_parse_GuildCastleDataLoad (int fd, int castle_id, int index) -{ - struct guild_castle *gc = (struct guild_castle *)numdb_search (castle_db, castle_id); - - if (gc == NULL) - { - return mapif_guild_castle_dataload (castle_id, 0, 0); - } - switch (index) - { - case 1: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->guild_id); - case 2: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->economy); - case 3: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->defense); - case 4: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->triggerE); - case 5: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->triggerD); - case 6: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->nextTime); - case 7: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->payTime); - case 8: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->createTime); - case 9: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleC); - case 10: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG0); - case 11: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG1); - case 12: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG2); - case 13: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG3); - case 14: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG4); - case 15: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG5); - case 16: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG6); - case 17: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->visibleG7); - case 18: - return mapif_guild_castle_dataload (gc->castle_id, index, gc->Ghp0); // guardian HP [Valaris] - case 19: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp1); - case 20: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp2); - case 21: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp3); - case 22: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp4); - case 23: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp5); - case 24: - return mapif_guild_castle_dataload (gc->castle_id, index, - gc->Ghp6); - case 25: - return mapif_guild_castle_dataload (gc->castle_id, index, gc->Ghp7); // end additions [Valaris] - - default: - printf - ("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", - index); - return 0; - } - - return 0; -} - -int mapif_parse_GuildCastleDataSave (int fd, int castle_id, int index, - int value) -{ - struct guild_castle *gc = (struct guild_castle *)numdb_search (castle_db, castle_id); - - if (gc == NULL) - { - return mapif_guild_castle_datasave (castle_id, index, value); - } - switch (index) - { - case 1: - if (gc->guild_id != value) - { - int gid = (value) ? value : gc->guild_id; - struct guild *g = (struct guild *)numdb_search (guild_db, gid); - inter_log ("guild %s (id=%d) %s castle id=%d\n", - (g) ? g->name : "??", gid, - (value) ? "occupy" : "abandon", index); - } - 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: - gc->visibleG0 = value; - break; - case 11: - gc->visibleG1 = value; - break; - case 12: - gc->visibleG2 = value; - break; - case 13: - gc->visibleG3 = value; - break; - case 14: - gc->visibleG4 = value; - break; - case 15: - gc->visibleG5 = value; - break; - case 16: - gc->visibleG6 = value; - break; - case 17: - gc->visibleG7 = value; - break; - case 18: - gc->Ghp0 = value; - break; // guardian HP [Valaris] - case 19: - gc->Ghp1 = value; - break; - case 20: - gc->Ghp2 = value; - break; - case 21: - gc->Ghp3 = value; - break; - case 22: - gc->Ghp4 = value; - break; - case 23: - gc->Ghp5 = value; - break; - case 24: - gc->Ghp6 = value; - break; - case 25: - gc->Ghp7 = value; - break; // end additions [Valaris] - default: - printf - ("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", - index); - return 0; - } - - return mapif_guild_castle_datasave (gc->castle_id, index, value); -} - -// ギルドチェック要求 -int mapif_parse_GuildCheck (int fd, int guild_id, int account_id, int char_id) -{ - return guild_check_conflict (guild_id, account_id, 0 /*char_id*/); -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_guild_parse_frommap (int fd) -{ - switch (RFIFOW (fd, 0)) - { - case 0x3030: - mapif_parse_CreateGuild (fd, RFIFOL (fd, 4), RFIFOP (fd, 8), - (struct guild_member *) RFIFOP (fd, 32)); - break; - case 0x3031: - mapif_parse_GuildInfo (fd, RFIFOL (fd, 2)); - break; - case 0x3032: - mapif_parse_GuildAddMember (fd, RFIFOL (fd, 4), - (struct guild_member *) RFIFOP (fd, - 8)); - break; - case 0x3034: - mapif_parse_GuildLeave (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOL (fd, 10), RFIFOB (fd, 14), - RFIFOP (fd, 15)); - break; - case 0x3035: - mapif_parse_GuildChangeMemberInfoShort (fd, RFIFOL (fd, 2), - RFIFOL (fd, 6), - RFIFOL (fd, 10), - RFIFOB (fd, 14), - RFIFOW (fd, 15), - RFIFOW (fd, 17)); - break; - case 0x3036: - mapif_parse_BreakGuild (fd, RFIFOL (fd, 2)); - break; - case 0x3037: - mapif_parse_GuildMessage (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), - RFIFOP (fd, 12), RFIFOW (fd, 2) - 12); - break; - case 0x3038: - mapif_parse_GuildCheck (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOL (fd, 10)); - break; - case 0x3039: - mapif_parse_GuildBasicInfoChange (fd, RFIFOL (fd, 4), - RFIFOW (fd, 8), RFIFOP (fd, 10), - RFIFOW (fd, 2) - 10); - break; - case 0x303A: - mapif_parse_GuildMemberInfoChange (fd, RFIFOL (fd, 4), - RFIFOL (fd, 8), RFIFOL (fd, - 12), - RFIFOW (fd, 16), RFIFOP (fd, - 18), - RFIFOW (fd, 2) - 18); - break; - case 0x303B: - mapif_parse_GuildPosition (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), - (struct guild_position *) RFIFOP (fd, - 12)); - break; - case 0x303C: - mapif_parse_GuildSkillUp (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOL (fd, 10)); - break; - case 0x303D: - mapif_parse_GuildAlliance (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOL (fd, 10), RFIFOL (fd, 14), - RFIFOB (fd, 18)); - break; - case 0x303E: - mapif_parse_GuildNotice (fd, RFIFOL (fd, 2), RFIFOP (fd, 6), - RFIFOP (fd, 66)); - break; - case 0x303F: - mapif_parse_GuildEmblem (fd, RFIFOW (fd, 2) - 12, RFIFOL (fd, 4), - RFIFOL (fd, 8), RFIFOP (fd, 12)); - break; - case 0x3040: - mapif_parse_GuildCastleDataLoad (fd, RFIFOW (fd, 2), - RFIFOB (fd, 4)); - break; - case 0x3041: - mapif_parse_GuildCastleDataSave (fd, RFIFOW (fd, 2), - RFIFOB (fd, 4), RFIFOL (fd, 5)); - break; - - default: - return 0; - } - - return 1; -} - -// マップサーバーの接続時処理 -int inter_guild_mapif_init (int fd) -{ - return mapif_guild_castle_alldataload (fd); -} - -// サーバーから脱退要求(キャラ削除用) -int inter_guild_leave (int guild_id, int account_id, int char_id) -{ - return mapif_parse_GuildLeave (-1, guild_id, account_id, 0 /*char_id*/, 0, - "**サーバー命令**"); -} diff --git a/src/char/int_guild.cpp b/src/char/int_guild.cpp new file mode 100644 index 0000000..fe9a95b --- /dev/null +++ b/src/char/int_guild.cpp @@ -0,0 +1,1757 @@ +// $Id: int_guild.c,v 1.2 2004/09/25 19:36:53 Akitasha Exp $ +#include "inter.hpp" +#include "int_guild.hpp" +#include "int_storage.hpp" +#include "../common/mmo.hpp" +#include "char.hpp" +#include "../common/socket.hpp" +#include "../common/db.hpp" +#include "../common/lock.hpp" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +char guild_txt[1024] = "save/guild.txt"; +char castle_txt[1024] = "save/castle.txt"; + +static struct dbt *guild_db; +static struct dbt *castle_db; + +static int guild_newid = 10000; + +static int guild_exp[100]; + +int mapif_parse_GuildLeave (int fd, int guild_id, int account_id, + int char_id, int flag, const char *mes); +int mapif_guild_broken (int guild_id, int flag); +int guild_check_empty (struct guild *g); +int guild_calcinfo (struct guild *g); +int mapif_guild_basicinfochanged (int guild_id, int type, const void *data, + int len); +int mapif_guild_info (int fd, struct guild *g); +void guild_break_sub (db_key_t key, db_val_t data, va_list ap); + +// ギルドデータの文字列への変換 +int inter_guild_tostr (char *str, struct guild *g) +{ + int i, c, len; + + // 基本データ + len = sprintf (str, "%d\t%s\t%s\t%d,%d,%d,%d,%d\t%s#\t%s#\t", + g->guild_id, g->name, g->master, + g->guild_lv, g->max_member, g->exp, g->skill_point, + g->castle_id, g->mes1, g->mes2); + // メンバー + for (i = 0; i < g->max_member; i++) + { + struct guild_member *m = &g->member[i]; + len += sprintf (str + len, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%s\t", + m->account_id, m->char_id, + m->hair, m->hair_color, m->gender, + m->pc_class, m->lv, m->exp, m->exp_payper, m->position, + ((m->account_id > 0) ? m->name : "-")); + } + // 役職 + for (i = 0; i < MAX_GUILDPOSITION; i++) + { + struct guild_position *p = &g->position[i]; + len += + sprintf (str + len, "%d,%d\t%s#\t", p->mode, p->exp_mode, + p->name); + } + // エンブレム + len += sprintf (str + len, "%d,%d,", g->emblem_len, g->emblem_id); + for (i = 0; i < g->emblem_len; i++) + { + len += + sprintf (str + len, "%02x", (unsigned char) (g->emblem_data[i])); + } + len += sprintf (str + len, "$\t"); + // 同盟リスト + c = 0; + for (i = 0; i < MAX_GUILDALLIANCE; i++) + if (g->alliance[i].guild_id > 0) + c++; + len += sprintf (str + len, "%d\t", c); + for (i = 0; i < MAX_GUILDALLIANCE; i++) + { + struct guild_alliance *a = &g->alliance[i]; + if (a->guild_id > 0) + len += + sprintf (str + len, "%d,%d\t%s\t", a->guild_id, a->opposition, + a->name); + } + // 追放リスト + c = 0; + for (i = 0; i < MAX_GUILDEXPLUSION; i++) + if (g->explusion[i].account_id > 0) + c++; + len += sprintf (str + len, "%d\t", c); + for (i = 0; i < MAX_GUILDEXPLUSION; i++) + { + struct guild_explusion *e = &g->explusion[i]; + if (e->account_id > 0) + len += sprintf (str + len, "%d,%d,%d,%d\t%s\t%s\t%s#\t", + e->account_id, e->rsv1, e->rsv2, e->rsv3, + e->name, e->acc, e->mes); + } + // ギルドスキル + for (i = 0; i < MAX_GUILDSKILL; i++) + { + len += sprintf (str + len, "%d,%d ", g->skill[i].id, g->skill[i].lv); + } + len += sprintf (str + len, "\t"); + + return 0; +} + +// ギルドデータの文字列からの変換 +int inter_guild_fromstr (char *str, struct guild *g) +{ + int i, j, c; + int tmp_int[16]; + char tmp_str[4][256]; + char tmp_str2[4096]; + char *pstr; + + // 基本データ + memset (g, 0, sizeof (struct guild)); + if (sscanf + (str, "%d\t%[^\t]\t%[^\t]\t%d,%d,%d,%d,%d\t%[^\t]\t%[^\t]\t", + &tmp_int[0], tmp_str[0], tmp_str[1], &tmp_int[1], &tmp_int[2], + &tmp_int[3], &tmp_int[4], &tmp_int[5], tmp_str[2], tmp_str[3]) < 8) + return 1; + + g->guild_id = tmp_int[0]; + g->guild_lv = tmp_int[1]; + g->max_member = tmp_int[2]; + g->exp = tmp_int[3]; + g->skill_point = tmp_int[4]; + g->castle_id = tmp_int[5]; + memcpy (g->name, tmp_str[0], 24); + memcpy (g->master, tmp_str[1], 24); + memcpy (g->mes1, tmp_str[2], 60); + memcpy (g->mes2, tmp_str[3], 120); + g->mes1[strlen (g->mes1) - 1] = 0; + g->mes2[strlen (g->mes2) - 1] = 0; + + for (j = 0; j < 6 && str != NULL; j++) // 位置スキップ + str = strchr (str + 1, '\t'); +// printf("GuildBaseInfo OK\n"); + + // メンバー + for (i = 0; i < g->max_member; i++) + { + struct guild_member *m = &g->member[i]; + if (sscanf (str + 1, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], + &tmp_int[8], &tmp_int[9], tmp_str[0]) < 11) + return 1; + m->account_id = tmp_int[0]; + m->char_id = 0 /*tmp_int[1]*/; + m->hair = tmp_int[2]; + m->hair_color = tmp_int[3]; + m->gender = tmp_int[4]; + m->pc_class = tmp_int[5]; + m->lv = tmp_int[6]; + m->exp = tmp_int[7]; + m->exp_payper = tmp_int[8]; + m->position = tmp_int[9]; + memcpy (m->name, tmp_str[0], 24); + + for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr (str + 1, '\t'); + } +// printf("GuildMemberInfo OK\n"); + // 役職 + i = 0; + while (sscanf (str + 1, "%d,%d%n", &tmp_int[0], &tmp_int[1], &j) == 2 + && str[1 + j] == '\t') + { + struct guild_position *p = &g->position[i]; + if (sscanf + (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], + tmp_str[0]) < 3) + return 1; + p->mode = tmp_int[0]; + p->exp_mode = tmp_int[1]; + tmp_str[0][strlen (tmp_str[0]) - 1] = 0; + memcpy (p->name, tmp_str[0], 24); + + for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr (str + 1, '\t'); + i++; + } +// printf("GuildPositionInfo OK\n"); + // エンブレム + tmp_int[1] = 0; + if (sscanf (str + 1, "%d,%d,%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str2) + < 3 && sscanf (str + 1, "%d,%[^\t]\t", &tmp_int[0], tmp_str2) < 2) + return 1; + g->emblem_len = tmp_int[0]; + g->emblem_id = tmp_int[1]; + for (i = 0, pstr = tmp_str2; i < g->emblem_len; i++, pstr += 2) + { + int c1 = pstr[0], c2 = pstr[1], x1 = 0, x2 = 0; + if (c1 >= '0' && c1 <= '9') + x1 = c1 - '0'; + if (c1 >= 'a' && c1 <= 'f') + x1 = c1 - 'a' + 10; + if (c1 >= 'A' && c1 <= 'F') + x1 = c1 - 'A' + 10; + if (c2 >= '0' && c2 <= '9') + x2 = c2 - '0'; + if (c2 >= 'a' && c2 <= 'f') + x2 = c2 - 'a' + 10; + if (c2 >= 'A' && c2 <= 'F') + x2 = c2 - 'A' + 10; + g->emblem_data[i] = (x1 << 4) | x2; + } +// printf("GuildEmblemInfo OK\n"); + str = strchr (str + 1, '\t'); // 位置スキップ + + // 同盟リスト + if (sscanf (str + 1, "%d\t", &c) < 1) + return 1; + str = strchr (str + 1, '\t'); // 位置スキップ + for (i = 0; i < c; i++) + { + struct guild_alliance *a = &g->alliance[i]; + if (sscanf + (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], + tmp_str[0]) < 3) + return 1; + a->guild_id = tmp_int[0]; + a->opposition = tmp_int[1]; + memcpy (a->name, tmp_str[0], 24); + + for (j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr (str + 1, '\t'); + } +// printf("GuildAllianceInfo OK\n"); + // 追放リスト + if (sscanf (str + 1, "%d\t", &c) < 1) + return 1; + str = strchr (str + 1, '\t'); // 位置スキップ + for (i = 0; i < c; i++) + { + struct guild_explusion *e = &g->explusion[i]; + if (sscanf (str + 1, "%d,%d,%d,%d\t%[^\t]\t%[^\t]\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + tmp_str[0], tmp_str[1], tmp_str[2]) < 6) + return 1; + e->account_id = tmp_int[0]; + e->rsv1 = tmp_int[1]; + e->rsv2 = tmp_int[2]; + e->rsv3 = tmp_int[3]; + memcpy (e->name, tmp_str[0], 24); + memcpy (e->acc, tmp_str[1], 24); + tmp_str[2][strlen (tmp_str[2]) - 1] = 0; + memcpy (e->mes, tmp_str[2], 40); + + for (j = 0; j < 4 && str != NULL; j++) // 位置スキップ + str = strchr (str + 1, '\t'); + } +// printf("GuildExplusionInfo OK\n"); + // ギルドスキル + for (i = 0; i < MAX_GUILDSKILL; i++) + { + if (sscanf (str + 1, "%d,%d ", &tmp_int[0], &tmp_int[1]) < 2) + break; + g->skill[i].id = tmp_int[0]; + g->skill[i].lv = tmp_int[1]; + str = strchr (str + 1, ' '); + } + str = strchr (str + 1, '\t'); +// printf("GuildSkillInfo OK\n"); + + return 0; +} + +// ギルド城データの文字列への変換 +int inter_guildcastle_tostr (char *str, struct guild_castle *gc) +{ + int len; + + len = sprintf (str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // added Guardian HP [Valaris] + gc->castle_id, gc->guild_id, gc->economy, gc->defense, + gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, + gc->createTime, gc->visibleC, gc->visibleG0, gc->visibleG1, + gc->visibleG2, gc->visibleG3, gc->visibleG4, gc->visibleG5, + gc->visibleG6, gc->visibleG7, gc->Ghp0, gc->Ghp1, gc->Ghp2, + gc->Ghp3, gc->Ghp4, gc->Ghp5, gc->Ghp6, gc->Ghp7); + + return 0; +} + +// ギルド城データの文字列からの変換 +int inter_guildcastle_fromstr (char *str, struct guild_castle *gc) +{ + int tmp_int[26]; + + memset (gc, 0, sizeof (struct guild_castle)); + // new structure of guild castle + if (sscanf + (str, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], + &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], + &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], + &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], + &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], &tmp_int[24], + &tmp_int[25]) == 26) + { + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->visibleG0 = tmp_int[10]; + gc->visibleG1 = tmp_int[11]; + gc->visibleG2 = tmp_int[12]; + gc->visibleG3 = tmp_int[13]; + gc->visibleG4 = tmp_int[14]; + gc->visibleG5 = tmp_int[15]; + gc->visibleG6 = tmp_int[16]; + gc->visibleG7 = tmp_int[17]; + gc->Ghp0 = tmp_int[18]; + gc->Ghp1 = tmp_int[19]; + gc->Ghp2 = tmp_int[20]; + gc->Ghp3 = tmp_int[21]; + gc->Ghp4 = tmp_int[22]; + gc->Ghp5 = tmp_int[23]; + gc->Ghp6 = tmp_int[24]; + gc->Ghp7 = tmp_int[25]; // end additions [Valaris] + // old structure of guild castle + } + else if (sscanf + (str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], + &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], + &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], + &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17]) == 18) + { + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->visibleG0 = tmp_int[10]; + gc->visibleG1 = tmp_int[11]; + gc->visibleG2 = tmp_int[12]; + gc->visibleG3 = tmp_int[13]; + gc->visibleG4 = tmp_int[14]; + gc->visibleG5 = tmp_int[15]; + gc->visibleG6 = tmp_int[16]; + gc->visibleG7 = tmp_int[17]; + if (gc->visibleG0 == 1) + gc->Ghp0 = 15670 + 2000 * gc->defense; + else + gc->Ghp0 = 0; + if (gc->visibleG1 == 1) + gc->Ghp1 = 15670 + 2000 * gc->defense; + else + gc->Ghp1 = 0; + if (gc->visibleG2 == 1) + gc->Ghp2 = 15670 + 2000 * gc->defense; + else + gc->Ghp2 = 0; + if (gc->visibleG3 == 1) + gc->Ghp3 = 30214 + 2000 * gc->defense; + else + gc->Ghp3 = 0; + if (gc->visibleG4 == 1) + gc->Ghp4 = 30214 + 2000 * gc->defense; + else + gc->Ghp4 = 0; + if (gc->visibleG5 == 1) + gc->Ghp5 = 28634 + 2000 * gc->defense; + else + gc->Ghp5 = 0; + if (gc->visibleG6 == 1) + gc->Ghp6 = 28634 + 2000 * gc->defense; + else + gc->Ghp6 = 0; + if (gc->visibleG7 == 1) + gc->Ghp7 = 28634 + 2000 * gc->defense; + else + gc->Ghp7 = 0; + } + else + { + return 1; + } + + return 0; +} + +// ギルド関連データベース読み込み +int inter_guild_readdb (void) +{ + int i; + FILE *fp; + char line[1024]; + + fp = fopen_ ("db/exp_guild.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/exp_guild.txt\n"); + return 1; + } + i = 0; + while (fgets (line, sizeof (line) - 1, fp) && i < 100) + { + if (line[0] == '/' && line[1] == '/') + continue; + guild_exp[i] = atoi (line); + i++; + } + fclose_ (fp); + + return 0; +} + +// ギルドデータの読み込み +int inter_guild_init (void) +{ + char line[16384]; + struct guild *g; + struct guild_castle *gc; + FILE *fp; + int i, j, c = 0; + + inter_guild_readdb (); + + guild_db = numdb_init (); + castle_db = numdb_init (); + + if ((fp = fopen_ (guild_txt, "r")) == NULL) + return 1; + while (fgets (line, sizeof (line) - 1, fp)) + { + j = 0; + if (sscanf (line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 + && guild_newid <= i) + { + guild_newid = i; + continue; + } + CREATE (g, struct guild, 1); + if (inter_guild_fromstr (line, g) == 0 && g->guild_id > 0) + { + if (g->guild_id >= guild_newid) + guild_newid = g->guild_id + 1; + numdb_insert (guild_db, g->guild_id, g); + guild_check_empty (g); + guild_calcinfo (g); + } + else + { + printf ("int_guild: broken data [%s] line %d\n", guild_txt, c); + free (g); + } + c++; + } + fclose_ (fp); +// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c); + + c = 0; //カウンタ初期化 + + if ((fp = fopen_ (castle_txt, "r")) == NULL) + { + return 1; + } + + while (fgets (line, sizeof (line) - 1, fp)) + { + CREATE (gc, struct guild_castle, 1); + if (inter_guildcastle_fromstr (line, gc) == 0) + { + numdb_insert (castle_db, gc->castle_id, gc); + } + else + { + printf ("int_guild: broken data [%s] line %d\n", castle_txt, c); + free (gc); + } + c++; + } + + if (!c) + { + printf (" %s - making Default Data...\n", castle_txt); + //デフォルトデータを作成 + for (i = 0; i < MAX_GUILDCASTLE; i++) + { + CREATE (gc, struct guild_castle, 1); + gc->castle_id = i; + gc->guild_id = 0; + gc->economy = 0; + gc->defense = 0; + gc->triggerE = 0; + gc->triggerD = 0; + gc->nextTime = 0; + gc->payTime = 0; + gc->createTime = 0; + gc->visibleC = 0; + gc->visibleG0 = 0; + gc->visibleG1 = 0; + gc->visibleG2 = 0; + gc->visibleG3 = 0; + gc->visibleG4 = 0; + gc->visibleG5 = 0; + gc->visibleG6 = 0; + gc->visibleG7 = 0; + gc->Ghp0 = 0; // guardian HP [Valaris] + gc->Ghp1 = 0; + gc->Ghp2 = 0; + gc->Ghp3 = 0; + gc->Ghp4 = 0; + gc->Ghp5 = 0; + gc->Ghp6 = 0; + gc->Ghp7 = 0; // end additions [Valaris] + numdb_insert (castle_db, gc->castle_id, gc); + } + printf (" %s - making done\n", castle_txt); + return 0; + } + + fclose_ (fp); + + return 0; +} + +struct guild *inter_guild_search (int guild_id) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + + return g; +} + +// ギルドデータのセーブ用 +void inter_guild_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[16384]; + FILE *fp; + + inter_guild_tostr (line, (struct guild *) data); + fp = va_arg (ap, FILE *); + fprintf (fp, "%s\n", line); +} + +// ギルド城データのセーブ用 +void inter_castle_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[16384]; + FILE *fp; + + inter_guildcastle_tostr (line, (struct guild_castle *) data); + fp = va_arg (ap, FILE *); + fprintf (fp, "%s\n", line); +} + +// ギルドデータのセーブ +int inter_guild_save (void) +{ + FILE *fp; + int lock; + + if ((fp = lock_fopen (guild_txt, &lock)) == NULL) + { + printf ("int_guild: cant write [%s] !!! data is lost !!!\n", + guild_txt); + return 1; + } + numdb_foreach (guild_db, inter_guild_save_sub, fp); +// fprintf(fp, "%d\t%%newid%%\n", guild_newid); + lock_fclose (fp, guild_txt, &lock); +// printf("int_guild: %s saved.\n", guild_txt); + + if ((fp = lock_fopen (castle_txt, &lock)) == NULL) + { + printf ("int_guild: cant write [%s] !!! data is lost !!!\n", + castle_txt); + return 1; + } + numdb_foreach (castle_db, inter_castle_save_sub, fp); + lock_fclose (fp, castle_txt, &lock); + + return 0; +} + +// ギルド名検索用 +void search_guildname_sub (db_key_t key, db_val_t 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 (strcasecmp (g->name, str) == 0) + *dst = g; +} + +// ギルド名検索 +struct guild *search_guildname (char *str) +{ + struct guild *g = NULL; + numdb_foreach (guild_db, search_guildname_sub, str, &g); + return g; +} + +// ギルドが空かどうかチェック +int guild_check_empty (struct guild *g) +{ + int i; + + for (i = 0; i < g->max_member; i++) + { + if (g->member[i].account_id > 0) + { + return 0; + } + } + // 誰もいないので解散 + numdb_foreach (guild_db, guild_break_sub, g->guild_id); + numdb_erase (guild_db, g->guild_id); + inter_guild_storage_delete (g->guild_id); + mapif_guild_broken (g->guild_id, 0); + free (g); + + return 1; +} + +// キャラの競合がないかチェック用 +void guild_check_conflict_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct guild *g = (struct guild *) data; + int guild_id, account_id, char_id, i; + + guild_id = va_arg (ap, int); + account_id = va_arg (ap, int); + char_id = va_arg (ap, int); + + if (g->guild_id == guild_id) // 本来の所属なので問題なし + return; + + for (i = 0; i < MAX_GUILD; i++) + { + if (g->member[i].account_id == account_id) + { + // 別のギルドに偽の所属データがあるので脱退 + printf ("int_guild: guild conflict! %d,%d %d!=%d\n", account_id, + char_id, guild_id, g->guild_id); + mapif_parse_GuildLeave (-1, g->guild_id, account_id, 0 /*char_id*/, 0, + "**データ競合**"); + } + } +} + +// キャラの競合がないかチェック +int guild_check_conflict (int guild_id, int account_id, int char_id) +{ + numdb_foreach (guild_db, guild_check_conflict_sub, guild_id, account_id, + 0 /*char_id*/); + + return 0; +} + +int guild_nextexp (int level) +{ + if (level < 100) + return guild_exp[level - 1]; + + return 0; +} + +// ギルドスキルがあるか確認 +int guild_checkskill (struct guild *g, int id) +{ + return g->skill[id - 10000].lv; +} + +// ギルドの情報の再計算 +int guild_calcinfo (struct guild *g) +{ + int i, c, nextexp; + struct guild before = *g; + + // スキルIDの設定 + for (i = 0; i < 5; i++) + g->skill[i].id = i + 10000; + + // ギルドレベル + if (g->guild_lv <= 0) + g->guild_lv = 1; + nextexp = guild_nextexp (g->guild_lv); + if (nextexp > 0) + { + while (g->exp >= nextexp) + { // レベルアップ処理 + g->exp -= nextexp; + g->guild_lv++; + g->skill_point++; + nextexp = guild_nextexp (g->guild_lv); + } + } + + // ギルドの次の経験値 + g->next_exp = guild_nextexp (g->guild_lv); + + // メンバ上限(ギルド拡張適用) + g->max_member = 100 + guild_checkskill (g, 10004) * 2; + + // 平均レベルとオンライン人数 + g->average_lv = 0; + g->connect_member = 0; + c = 0; + for (i = 0; i < g->max_member; i++) + { + if (g->member[i].account_id > 0) + { + g->average_lv += g->member[i].lv; + c++; + if (g->member[i].online > 0) + g->connect_member++; + } + } + g->average_lv /= c; + + // 全データを送る必要がありそう + if (g->max_member != before.max_member || + g->guild_lv != before.guild_lv || + g->skill_point != before.skill_point) + { + mapif_guild_info (-1, g); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// ギルド作成可否 +int mapif_guild_created (int fd, int account_id, struct guild *g) +{ + WFIFOW (fd, 0) = 0x3830; + WFIFOL (fd, 2) = account_id; + if (g != NULL) + { + WFIFOL (fd, 6) = g->guild_id; + printf ("int_guild: created! %d %s\n", g->guild_id, g->name); + } + else + { + WFIFOL (fd, 6) = 0; + } + WFIFOSET (fd, 10); + return 0; +} + +// ギルド情報見つからず +int mapif_guild_noinfo (int fd, int guild_id) +{ + WFIFOW (fd, 0) = 0x3831; + WFIFOW (fd, 2) = 8; + WFIFOL (fd, 4) = guild_id; + WFIFOSET (fd, 8); + printf ("int_guild: info not found %d\n", guild_id); + + return 0; +} + +// ギルド情報まとめ送り +int mapif_guild_info (int fd, struct guild *g) +{ + unsigned char buf[4 + sizeof (struct guild)]; + + WBUFW (buf, 0) = 0x3831; + memcpy (buf + 4, g, sizeof (struct guild)); + WBUFW (buf, 2) = 4 + sizeof (struct guild); +// printf("int_guild: sizeof(guild)=%d\n", sizeof(struct guild)); + if (fd < 0) + mapif_sendall (buf, WBUFW (buf, 2)); + else + mapif_send (fd, buf, WBUFW (buf, 2)); +// printf("int_guild: info %d %s\n", p->guild_id, p->name); + + return 0; +} + +// メンバ追加可否 +int mapif_guild_memberadded (int fd, int guild_id, int account_id, + int char_id, int flag) +{ + WFIFOW (fd, 0) = 0x3832; + WFIFOL (fd, 2) = guild_id; + WFIFOL (fd, 6) = account_id; + WFIFOL (fd, 10) = 0 /*char_id*/; + WFIFOB (fd, 14) = flag; + WFIFOSET (fd, 15); + + return 0; +} + +// 脱退/追放通知 +int mapif_guild_leaved (int guild_id, int account_id, int char_id, int flag, + const char *name, const char *mes) +{ + unsigned char buf[79]; + + WBUFW (buf, 0) = 0x3834; + WBUFL (buf, 2) = guild_id; + WBUFL (buf, 6) = account_id; + WBUFL (buf, 10) = 0 /*char_id*/; + WBUFB (buf, 14) = flag; + memcpy (WBUFP (buf, 15), mes, 40); + memcpy (WBUFP (buf, 55), name, 24); + mapif_sendall (buf, 79); + printf ("int_guild: guild leaved %d %d %s %s\n", guild_id, account_id, + name, mes); + + return 0; +} + +// オンライン状態とLv更新通知 +int mapif_guild_memberinfoshort (struct guild *g, int idx) +{ + unsigned char buf[19]; + + WBUFW (buf, 0) = 0x3835; + WBUFL (buf, 2) = g->guild_id; + WBUFL (buf, 6) = g->member[idx].account_id; + WBUFL (buf, 10) = 0 /*g->member[idx].char_id*/; + WBUFB (buf, 14) = g->member[idx].online; + WBUFW (buf, 15) = g->member[idx].lv; + WBUFW (buf, 17) = g->member[idx].pc_class; + mapif_sendall (buf, 19); + return 0; +} + +// 解散通知 +int mapif_guild_broken (int guild_id, int flag) +{ + unsigned char buf[7]; + + WBUFW (buf, 0) = 0x3836; + WBUFL (buf, 2) = guild_id; + WBUFB (buf, 6) = flag; + mapif_sendall (buf, 7); + printf ("int_guild: broken %d\n", guild_id); + + return 0; +} + +// ギルド内発言 +int mapif_guild_message (int guild_id, int account_id, char *mes, int len) +{ + unsigned char buf[len + 12]; + + WBUFW (buf, 0) = 0x3837; + WBUFW (buf, 2) = len + 12; + WBUFL (buf, 4) = guild_id; + WBUFL (buf, 8) = account_id; + memcpy (WBUFP (buf, 12), mes, len); + mapif_sendall (buf, len + 12); + + return 0; +} + +// ギルド基本情報変更通知 +int mapif_guild_basicinfochanged (int guild_id, int type, const void *data, + int len) +{ + unsigned char buf[2048]; + + WBUFW (buf, 0) = 0x3839; + WBUFW (buf, 2) = len + 10; + WBUFL (buf, 4) = guild_id; + WBUFW (buf, 8) = type; + memcpy (WBUFP (buf, 10), data, len); + mapif_sendall (buf, len + 10); + return 0; +} + +// ギルドメンバ情報変更通知 +int mapif_guild_memberinfochanged (int guild_id, int account_id, int char_id, + int type, const void *data, int len) +{ + unsigned char buf[len + 18]; + + WBUFW (buf, 0) = 0x383a; + WBUFW (buf, 2) = len + 18; + WBUFL (buf, 4) = guild_id; + WBUFL (buf, 8) = account_id; + WBUFL (buf, 12) = 0 /*char_id*/; + WBUFW (buf, 16) = type; + memcpy (WBUFP (buf, 18), data, len); + mapif_sendall (buf, len + 18); + + return 0; +} + +// ギルドスキルアップ通知 +int mapif_guild_skillupack (int guild_id, int skill_num, int account_id) +{ + unsigned char buf[14]; + + WBUFW (buf, 0) = 0x383c; + WBUFL (buf, 2) = guild_id; + WBUFL (buf, 6) = skill_num; + WBUFL (buf, 10) = account_id; + mapif_sendall (buf, 14); + + return 0; +} + +// ギルド同盟/敵対通知 +int mapif_guild_alliance (int guild_id1, int guild_id2, int account_id1, + int account_id2, int flag, const char *name1, + const char *name2) +{ + unsigned char buf[67]; + + WBUFW (buf, 0) = 0x383d; + WBUFL (buf, 2) = guild_id1; + WBUFL (buf, 6) = guild_id2; + WBUFL (buf, 10) = account_id1; + WBUFL (buf, 14) = account_id2; + WBUFB (buf, 18) = flag; + memcpy (WBUFP (buf, 19), name1, 24); + memcpy (WBUFP (buf, 43), name2, 24); + mapif_sendall (buf, 67); + + return 0; +} + +// ギルド役職変更通知 +int mapif_guild_position (struct guild *g, int idx) +{ + unsigned char buf[sizeof (struct guild_position) + 12]; + + WBUFW (buf, 0) = 0x383b; + WBUFW (buf, 2) = sizeof (struct guild_position) + 12; + WBUFL (buf, 4) = g->guild_id; + WBUFL (buf, 8) = idx; + memcpy (WBUFP (buf, 12), &g->position[idx], + sizeof (struct guild_position)); + mapif_sendall (buf, WBUFW (buf, 2)); + + return 0; +} + +// ギルド告知変更通知 +int mapif_guild_notice (struct guild *g) +{ + unsigned char buf[186]; + + WBUFW (buf, 0) = 0x383e; + WBUFL (buf, 2) = g->guild_id; + memcpy (WBUFP (buf, 6), g->mes1, 60); + memcpy (WBUFP (buf, 66), g->mes2, 120); + mapif_sendall (buf, 186); + + return 0; +} + +// ギルドエンブレム変更通知 +int mapif_guild_emblem (struct guild *g) +{ + unsigned char buf[2048]; + + WBUFW (buf, 0) = 0x383f; + WBUFW (buf, 2) = g->emblem_len + 12; + WBUFL (buf, 4) = g->guild_id; + WBUFL (buf, 8) = g->emblem_id; + memcpy (WBUFP (buf, 12), g->emblem_data, g->emblem_len); + mapif_sendall (buf, WBUFW (buf, 2)); + + return 0; +} + +int mapif_guild_castle_dataload (int castle_id, int index, int value) +{ + unsigned char buf[9]; + + WBUFW (buf, 0) = 0x3840; + WBUFW (buf, 2) = castle_id; + WBUFB (buf, 4) = index; + WBUFL (buf, 5) = value; + mapif_sendall (buf, 9); + + return 0; +} + +int mapif_guild_castle_datasave (int castle_id, int index, int value) +{ + unsigned char buf[9]; + + WBUFW (buf, 0) = 0x3841; + WBUFW (buf, 2) = castle_id; + WBUFB (buf, 4) = index; + WBUFL (buf, 5) = value; + mapif_sendall (buf, 9); + + return 0; +} + +void mapif_guild_castle_alldataload_sub (db_key_t key, db_val_t data, va_list ap) +{ + int fd = va_arg (ap, int); + int *p = va_arg (ap, int *); + + memcpy (WFIFOP (fd, *p), (struct guild_castle *) data, + sizeof (struct guild_castle)); + (*p) += sizeof (struct guild_castle); +} + +int mapif_guild_castle_alldataload (int fd) +{ + int len = 4; + + WFIFOW (fd, 0) = 0x3842; + numdb_foreach (castle_db, mapif_guild_castle_alldataload_sub, fd, &len); + WFIFOW (fd, 2) = len; + WFIFOSET (fd, len); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + +// ギルド作成要求 +int mapif_parse_CreateGuild (int fd, int account_id, char *name, + struct guild_member *master) +{ + struct guild *g; + int i; + + for (i = 0; i < 24 && name[i]; i++) + { + if (!(name[i] & 0xe0) || name[i] == 0x7f) + { + printf ("int_guild: illeagal guild name [%s]\n", name); + mapif_guild_created (fd, account_id, NULL); + return 0; + } + } + + if ((g = search_guildname (name)) != NULL) + { + printf ("int_guild: same name guild exists [%s]\n", name); + mapif_guild_created (fd, account_id, NULL); + return 0; + } + CREATE (g, struct guild, 1); + g->guild_id = guild_newid++; + memcpy (g->name, name, 24); + memcpy (g->master, master->name, 24); + memcpy (&g->member[0], master, sizeof (struct guild_member)); + + g->position[0].mode = 0x11; + strcpy (g->position[0].name, "GuildMaster"); + strcpy (g->position[MAX_GUILDPOSITION - 1].name, "Newbie"); + for (i = 1; i < MAX_GUILDPOSITION - 1; i++) + sprintf (g->position[i].name, "Position %d", i + 1); + + // ここでギルド情報計算が必要と思われる + g->max_member = 100; + g->average_lv = master->lv; + for (i = 0; i < 5; i++) + g->skill[i].id = i + 10000; + + numdb_insert (guild_db, g->guild_id, g); + + mapif_guild_created (fd, account_id, g); + mapif_guild_info (fd, g); + + inter_log ("guild %s (id=%d) created by master %s (id=%d)\n", + name, g->guild_id, master->name, master->account_id); + + return 0; +} + +// ギルド情報要求 +int mapif_parse_GuildInfo (int fd, int guild_id) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g != NULL) + { + guild_calcinfo (g); + mapif_guild_info (fd, g); + } + else + mapif_guild_noinfo (fd, guild_id); + + return 0; +} + +// ギルドメンバ追加要求 +int mapif_parse_GuildAddMember (int fd, int guild_id, struct guild_member *m) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + { + mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, 1); + return 0; + } + + for (int i = 0; i < g->max_member; i++) + { + if (g->member[i].account_id == 0) + { + memcpy (&g->member[i], m, sizeof (struct guild_member)); + mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, + 0); + guild_calcinfo (g); + mapif_guild_info (-1, g); + + return 0; + } + } + mapif_guild_memberadded (fd, guild_id, m->account_id, 0 /*char_id*/, 1); + + return 0; +} + +// ギルド脱退/追放要求 +int mapif_parse_GuildLeave (int fd, int guild_id, int account_id, int char_id, + int flag, const char *mes) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g != NULL) + { + for (int i = 0; i < MAX_GUILD; i++) + { + if (g->member[i].account_id == account_id) + { +// printf("%d %d\n", i, (int)(&g->member[i])); +// printf("%d %s\n", i, g->member[i].name); + + if (flag) + { + int j; + // 追放の場合追放リストに入れる + for (j = 0; j < MAX_GUILDEXPLUSION; j++) + { + if (g->explusion[j].account_id == 0) + break; + } + if (j == MAX_GUILDEXPLUSION) + { // 一杯なので古いのを消す + for (j = 0; j < MAX_GUILDEXPLUSION - 1; j++) + g->explusion[j] = g->explusion[j + 1]; + j = MAX_GUILDEXPLUSION - 1; + } + g->explusion[j].account_id = account_id; + memcpy (g->explusion[j].acc, "dummy", 24); + memcpy (g->explusion[j].name, g->member[i].name, 24); + memcpy (g->explusion[j].mes, mes, 40); + } + + mapif_guild_leaved (guild_id, account_id, 0 /*char_id*/, flag, + g->member[i].name, mes); +// printf("%d %d\n", i, (int)(&g->member[i])); +// printf("%d %s\n", i, (&g->member[i])->name); + memset (&g->member[i], 0, sizeof (struct guild_member)); + + if (guild_check_empty (g) == 0) + mapif_guild_info (-1, g); // まだ人がいるのでデータ送信 + + return 0; + } + } + } + return 0; +} + +// オンライン/Lv更新 +int mapif_parse_GuildChangeMemberInfoShort (int fd, int guild_id, + int account_id, int char_id, + int online, int lv, int pc_class) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + + g->connect_member = 0; + + int alv = 0; + int c = 0; + for (int i = 0; i < MAX_GUILD; i++) + { + if (g->member[i].account_id == account_id) + { + g->member[i].online = online; + g->member[i].lv = lv; + g->member[i].pc_class = pc_class; + mapif_guild_memberinfoshort (g, i); + } + if (g->member[i].account_id > 0) + { + alv += g->member[i].lv; + c++; + } + if (g->member[i].online) + g->connect_member++; + } + // 平均レベル + g->average_lv = alv / c; + + return 0; +} + +// ギルド解散処理用(同盟/敵対を解除) +void guild_break_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct guild *g = (struct guild *) data; + int guild_id = va_arg (ap, int); + int i; + + for (i = 0; i < MAX_GUILDALLIANCE; i++) + { + if (g->alliance[i].guild_id == guild_id) + g->alliance[i].guild_id = 0; + } +} + +// ギルド解散要求 +int mapif_parse_BreakGuild (int fd, int guild_id) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + + numdb_foreach (guild_db, guild_break_sub, guild_id); + numdb_erase (guild_db, guild_id); + inter_guild_storage_delete (guild_id); + mapif_guild_broken (guild_id, 0); + + inter_log ("guild %s (id=%d) broken\n", g->name, guild_id); + free (g); + + return 0; +} + +// ギルドメッセージ送信 +int mapif_parse_GuildMessage (int fd, int guild_id, int account_id, char *mes, + int len) +{ + return mapif_guild_message (guild_id, account_id, mes, len); +} + +// ギルド基本データ変更要求 +int mapif_parse_GuildBasicInfoChange (int fd, int guild_id, int type, + const char *data, int len) +{ + short dw = *((short *) data); + + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + + switch (type) + { + case GBI_GUILDLV: + if (dw > 0 && g->guild_lv + dw <= 50) + { + g->guild_lv += dw; + g->skill_point += dw; + } + else if (dw < 0 && g->guild_lv + dw >= 1) + g->guild_lv += dw; + mapif_guild_info (-1, g); + return 0; + default: + printf ("int_guild: GuildBasicInfoChange: Unknown type %d\n", + type); + break; + } + mapif_guild_basicinfochanged (guild_id, type, data, len); + + return 0; +} + +// ギルドメンバデータ変更要求 +int mapif_parse_GuildMemberInfoChange (int fd, int guild_id, int account_id, + int char_id, int type, + const char *data, int len) +{ + int i; + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + + for (i = 0; i < g->max_member; i++) + if (g->member[i].account_id == account_id) + break; + if (i == g->max_member) + { + printf ("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", + account_id, char_id, guild_id, g->name); + return 0; + } + switch (type) + { + case GMI_POSITION: // 役職 + g->member[i].position = *((int *) data); + break; + case GMI_EXP: // EXP + { + int exp, oldexp = g->member[i].exp; + exp = g->member[i].exp = *((unsigned int *) data); + g->exp += (exp - oldexp); + guild_calcinfo (g); // Lvアップ判断 + mapif_guild_basicinfochanged (guild_id, GBI_EXP, &g->exp, 4); + } + break; + default: + printf ("int_guild: GuildMemberInfoChange: Unknown type %d\n", + type); + break; + } + mapif_guild_memberinfochanged (guild_id, account_id, char_id, type, data, + len); + + return 0; +} + +// ギルド役職名変更要求 +int mapif_parse_GuildPosition (int fd, int guild_id, int idx, + struct guild_position *p) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + + if (g == NULL || idx < 0 || idx >= MAX_GUILDPOSITION) + { + return 0; + } + memcpy (&g->position[idx], p, sizeof (struct guild_position)); + mapif_guild_position (g, idx); + printf ("int_guild: position changed %d\n", idx); + + return 0; +} + +// ギルドスキルアップ要求 +int mapif_parse_GuildSkillUp (int fd, int guild_id, int skill_num, + int account_id) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + int idx = skill_num - 10000; + + if (g == NULL || skill_num < 10000) + return 0; + + if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < 10) + { + g->skill[idx].lv++; + g->skill_point--; + if (guild_calcinfo (g) == 0) + mapif_guild_info (-1, g); + mapif_guild_skillupack (guild_id, skill_num, account_id); + printf ("int_guild: skill %d up\n", skill_num); + } + + return 0; +} + +// ギルド同盟要求 +int mapif_parse_GuildAlliance (int fd, int guild_id1, int guild_id2, + int account_id1, int account_id2, int flag) +{ + struct guild *g[2]; + int j, i; + + g[0] = (struct guild *)numdb_search (guild_db, guild_id1); + g[1] = (struct guild *)numdb_search (guild_db, guild_id2); + if (g[0] == NULL || g[1] == NULL) + return 0; + + if (!(flag & 0x8)) + { + for (i = 0; i < 2 - (flag & 1); i++) + { + for (j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == 0) + { + g[i]->alliance[j].guild_id = g[1 - i]->guild_id; + memcpy (g[i]->alliance[j].name, g[1 - i]->name, 24); + g[i]->alliance[j].opposition = flag & 1; + break; + } + } + } + else + { // 関係解消 + for (i = 0; i < 2 - (flag & 1); i++) + { + for (j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == g[1 - i]->guild_id + && g[i]->alliance[j].opposition == (flag & 1)) + { + g[i]->alliance[j].guild_id = 0; + break; + } + } + } + mapif_guild_alliance (guild_id1, guild_id2, account_id1, account_id2, + flag, g[0]->name, g[1]->name); + + return 0; +} + +// ギルド告知変更要求 +int mapif_parse_GuildNotice (int fd, int guild_id, const char *mes1, + const char *mes2) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + memcpy (g->mes1, mes1, 60); + memcpy (g->mes2, mes2, 120); + + return mapif_guild_notice (g); +} + +// ギルドエンブレム変更要求 +int mapif_parse_GuildEmblem (int fd, int len, int guild_id, int dummy, + const char *data) +{ + struct guild *g = (struct guild *)numdb_search (guild_db, guild_id); + if (g == NULL) + return 0; + memcpy (g->emblem_data, data, len); + g->emblem_len = len; + g->emblem_id++; + + return mapif_guild_emblem (g); +} + +int mapif_parse_GuildCastleDataLoad (int fd, int castle_id, int index) +{ + struct guild_castle *gc = (struct guild_castle *)numdb_search (castle_db, castle_id); + + if (gc == NULL) + { + return mapif_guild_castle_dataload (castle_id, 0, 0); + } + switch (index) + { + case 1: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->guild_id); + case 2: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->economy); + case 3: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->defense); + case 4: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->triggerE); + case 5: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->triggerD); + case 6: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->nextTime); + case 7: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->payTime); + case 8: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->createTime); + case 9: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleC); + case 10: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG0); + case 11: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG1); + case 12: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG2); + case 13: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG3); + case 14: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG4); + case 15: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG5); + case 16: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG6); + case 17: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->visibleG7); + case 18: + return mapif_guild_castle_dataload (gc->castle_id, index, gc->Ghp0); // guardian HP [Valaris] + case 19: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp1); + case 20: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp2); + case 21: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp3); + case 22: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp4); + case 23: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp5); + case 24: + return mapif_guild_castle_dataload (gc->castle_id, index, + gc->Ghp6); + case 25: + return mapif_guild_castle_dataload (gc->castle_id, index, gc->Ghp7); // end additions [Valaris] + + default: + printf + ("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", + index); + return 0; + } + + return 0; +} + +int mapif_parse_GuildCastleDataSave (int fd, int castle_id, int index, + int value) +{ + struct guild_castle *gc = (struct guild_castle *)numdb_search (castle_db, castle_id); + + if (gc == NULL) + { + return mapif_guild_castle_datasave (castle_id, index, value); + } + switch (index) + { + case 1: + if (gc->guild_id != value) + { + int gid = (value) ? value : gc->guild_id; + struct guild *g = (struct guild *)numdb_search (guild_db, gid); + inter_log ("guild %s (id=%d) %s castle id=%d\n", + (g) ? g->name : "??", gid, + (value) ? "occupy" : "abandon", index); + } + 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: + gc->visibleG0 = value; + break; + case 11: + gc->visibleG1 = value; + break; + case 12: + gc->visibleG2 = value; + break; + case 13: + gc->visibleG3 = value; + break; + case 14: + gc->visibleG4 = value; + break; + case 15: + gc->visibleG5 = value; + break; + case 16: + gc->visibleG6 = value; + break; + case 17: + gc->visibleG7 = value; + break; + case 18: + gc->Ghp0 = value; + break; // guardian HP [Valaris] + case 19: + gc->Ghp1 = value; + break; + case 20: + gc->Ghp2 = value; + break; + case 21: + gc->Ghp3 = value; + break; + case 22: + gc->Ghp4 = value; + break; + case 23: + gc->Ghp5 = value; + break; + case 24: + gc->Ghp6 = value; + break; + case 25: + gc->Ghp7 = value; + break; // end additions [Valaris] + default: + printf + ("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", + index); + return 0; + } + + return mapif_guild_castle_datasave (gc->castle_id, index, value); +} + +// ギルドチェック要求 +int mapif_parse_GuildCheck (int fd, int guild_id, int account_id, int char_id) +{ + return guild_check_conflict (guild_id, account_id, 0 /*char_id*/); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_guild_parse_frommap (int fd) +{ + switch (RFIFOW (fd, 0)) + { + case 0x3030: + mapif_parse_CreateGuild (fd, RFIFOL (fd, 4), RFIFOP (fd, 8), + (struct guild_member *) RFIFOP (fd, 32)); + break; + case 0x3031: + mapif_parse_GuildInfo (fd, RFIFOL (fd, 2)); + break; + case 0x3032: + mapif_parse_GuildAddMember (fd, RFIFOL (fd, 4), + (struct guild_member *) RFIFOP (fd, + 8)); + break; + case 0x3034: + mapif_parse_GuildLeave (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOL (fd, 10), RFIFOB (fd, 14), + RFIFOP (fd, 15)); + break; + case 0x3035: + mapif_parse_GuildChangeMemberInfoShort (fd, RFIFOL (fd, 2), + RFIFOL (fd, 6), + RFIFOL (fd, 10), + RFIFOB (fd, 14), + RFIFOW (fd, 15), + RFIFOW (fd, 17)); + break; + case 0x3036: + mapif_parse_BreakGuild (fd, RFIFOL (fd, 2)); + break; + case 0x3037: + mapif_parse_GuildMessage (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), + RFIFOP (fd, 12), RFIFOW (fd, 2) - 12); + break; + case 0x3038: + mapif_parse_GuildCheck (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOL (fd, 10)); + break; + case 0x3039: + mapif_parse_GuildBasicInfoChange (fd, RFIFOL (fd, 4), + RFIFOW (fd, 8), RFIFOP (fd, 10), + RFIFOW (fd, 2) - 10); + break; + case 0x303A: + mapif_parse_GuildMemberInfoChange (fd, RFIFOL (fd, 4), + RFIFOL (fd, 8), RFIFOL (fd, + 12), + RFIFOW (fd, 16), RFIFOP (fd, + 18), + RFIFOW (fd, 2) - 18); + break; + case 0x303B: + mapif_parse_GuildPosition (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), + (struct guild_position *) RFIFOP (fd, + 12)); + break; + case 0x303C: + mapif_parse_GuildSkillUp (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOL (fd, 10)); + break; + case 0x303D: + mapif_parse_GuildAlliance (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOL (fd, 10), RFIFOL (fd, 14), + RFIFOB (fd, 18)); + break; + case 0x303E: + mapif_parse_GuildNotice (fd, RFIFOL (fd, 2), RFIFOP (fd, 6), + RFIFOP (fd, 66)); + break; + case 0x303F: + mapif_parse_GuildEmblem (fd, RFIFOW (fd, 2) - 12, RFIFOL (fd, 4), + RFIFOL (fd, 8), RFIFOP (fd, 12)); + break; + case 0x3040: + mapif_parse_GuildCastleDataLoad (fd, RFIFOW (fd, 2), + RFIFOB (fd, 4)); + break; + case 0x3041: + mapif_parse_GuildCastleDataSave (fd, RFIFOW (fd, 2), + RFIFOB (fd, 4), RFIFOL (fd, 5)); + break; + + default: + return 0; + } + + return 1; +} + +// マップサーバーの接続時処理 +int inter_guild_mapif_init (int fd) +{ + return mapif_guild_castle_alldataload (fd); +} + +// サーバーから脱退要求(キャラ削除用) +int inter_guild_leave (int guild_id, int account_id, int char_id) +{ + return mapif_parse_GuildLeave (-1, guild_id, account_id, 0 /*char_id*/, 0, + "**サーバー命令**"); +} diff --git a/src/char/int_guild.h b/src/char/int_guild.h deleted file mode 100644 index 5ac9a51..0000000 --- a/src/char/int_guild.h +++ /dev/null @@ -1,16 +0,0 @@ -// $Id: int_guild.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#ifndef _INT_GUILD_H_ -#define _INT_GUILD_H_ - -int inter_guild_init (void); -int inter_guild_save (void); -int inter_guild_parse_frommap (int fd); -struct guild *inter_guild_search (int guild_id); -int inter_guild_mapif_init (int fd); - -int inter_guild_leave (int guild_id, int account_id, int char_id); - -extern char guild_txt[1024]; -extern char castle_txt[1024]; - -#endif diff --git a/src/char/int_guild.hpp b/src/char/int_guild.hpp new file mode 100644 index 0000000..2833e2d --- /dev/null +++ b/src/char/int_guild.hpp @@ -0,0 +1,16 @@ +// $Id: int_guild.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef INT_GUILD_HPP +#define INT_GUILD_HPP + +int inter_guild_init (void); +int inter_guild_save (void); +int inter_guild_parse_frommap (int fd); +struct guild *inter_guild_search (int guild_id); +int inter_guild_mapif_init (int fd); + +int inter_guild_leave (int guild_id, int account_id, int char_id); + +extern char guild_txt[1024]; +extern char castle_txt[1024]; + +#endif diff --git a/src/char/int_party.c b/src/char/int_party.c deleted file mode 100644 index 6f8d023..0000000 --- a/src/char/int_party.c +++ /dev/null @@ -1,673 +0,0 @@ -// $Id: int_party.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#include "inter.h" -#include "int_party.h" -#include "../common/mmo.h" -#include "char.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -char party_txt[1024] = "save/party.txt"; - -static struct dbt *party_db; -static int party_newid = 100; - -int mapif_party_broken (int party_id, int flag); -int party_check_empty (struct party *p); -int mapif_parse_PartyLeave (int fd, int party_id, int account_id); - -// パーティデータの文字列への変換 -int inter_party_tostr (char *str, struct party *p) -{ - int i, len; - - len = - sprintf (str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, - p->item); - for (i = 0; i < MAX_PARTY; i++) - { - struct party_member *m = &p->member[i]; - len += - sprintf (str + len, "%d,%d\t%s\t", m->account_id, m->leader, - ((m->account_id > 0) ? m->name : "NoMember")); - } - - return 0; -} - -// パーティデータの文字列からの変換 -int inter_party_fromstr (char *str, struct party *p) -{ - int i, j; - int tmp_int[16]; - char tmp_str[256]; - - memset (p, 0, sizeof (struct party)); - -// printf("sscanf party main info\n"); - if (sscanf - (str, "%d\t%[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], - &tmp_int[2]) != 4) - return 1; - - p->party_id = tmp_int[0]; - strcpy (p->name, tmp_str); - p->exp = tmp_int[1]; - p->item = tmp_int[2]; -// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]); - - for (j = 0; j < 3 && str != NULL; j++) - str = strchr (str + 1, '\t'); - - for (i = 0; i < MAX_PARTY; i++) - { - struct party_member *m = &p->member[i]; - if (str == NULL) - return 1; -// printf("sscanf party member info %d\n", i); - - if (sscanf - (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], - tmp_str) != 3) - return 1; - - m->account_id = tmp_int[0]; - m->leader = tmp_int[1]; - strncpy (m->name, tmp_str, sizeof (m->name)); -// printf(" %d %d [%s]\n", tmp_int[0], tmp_int[1], tmp_str); - - for (j = 0; j < 2 && str != NULL; j++) - str = strchr (str + 1, '\t'); - } - - return 0; -} - -// パーティデータのロード -int inter_party_init (void) -{ - char line[8192]; - struct party *p; - FILE *fp; - int c = 0; - int i, j; - - party_db = numdb_init (); - - if ((fp = fopen_ (party_txt, "r")) == NULL) - return 1; - - while (fgets (line, sizeof (line) - 1, fp)) - { - j = 0; - if (sscanf (line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 - && party_newid <= i) - { - party_newid = i; - continue; - } - - CREATE (p, struct party, 1); - if (inter_party_fromstr (line, p) == 0 && p->party_id > 0) - { - if (p->party_id >= party_newid) - party_newid = p->party_id + 1; - numdb_insert (party_db, p->party_id, p); - party_check_empty (p); - } - else - { - printf ("int_party: broken data [%s] line %d\n", party_txt, - c + 1); - free (p); - } - c++; - } - fclose_ (fp); -// printf("int_party: %s read done (%d parties)\n", party_txt, c); - - return 0; -} - -// パーティーデータのセーブ用 -void inter_party_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[8192]; - FILE *fp; - - inter_party_tostr (line, (struct party *) data); - fp = va_arg (ap, FILE *); - fprintf (fp, "%s\n", line); -} - -// パーティーデータのセーブ -int inter_party_save (void) -{ - FILE *fp; - int lock; - - if ((fp = lock_fopen (party_txt, &lock)) == NULL) - { - printf ("int_party: cant write [%s] !!! data is lost !!!\n", - party_txt); - return 1; - } - numdb_foreach (party_db, inter_party_save_sub, fp); -// fprintf(fp, "%d\t%%newid%%\n", party_newid); - lock_fclose (fp, party_txt, &lock); -// printf("int_party: %s saved.\n", party_txt); - - return 0; -} - -// パーティ名検索用 -void search_partyname_sub (db_key_t key, db_val_t 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 (strcasecmp (p->name, str) == 0) - *dst = p; -} - -// パーティ名検索 -struct party *search_partyname (char *str) -{ - struct party *p = NULL; - numdb_foreach (party_db, search_partyname_sub, str, &p); - - return p; -} - -// EXP公平分配できるかチェック -int party_check_exp_share (struct party *p) -{ - int i; - int maxlv = 0, minlv = 0x7fffffff; - - for (i = 0; i < MAX_PARTY; i++) - { - int lv = p->member[i].lv; - if (p->member[i].online) - { - if (lv < minlv) - minlv = lv; - if (maxlv < lv) - maxlv = lv; - } - } - - return (maxlv == 0 || maxlv - minlv <= party_share_level); -} - -// パーティが空かどうかチェック -int party_check_empty (struct party *p) -{ - int i; - -// printf("party check empty %08X\n", (int)p); - for (i = 0; i < MAX_PARTY; i++) - { -// printf("%d acc=%d\n", i, p->member[i].account_id); - if (p->member[i].account_id > 0) - { - return 0; - } - } - // 誰もいないので解散 - mapif_party_broken (p->party_id, 0); - numdb_erase (party_db, p->party_id); - free (p); - - return 1; -} - -// キャラの競合がないかチェック用 -void party_check_conflict_sub (db_key_t key, db_val_t data, va_list ap) -{ - struct party *p = (struct party *) data; - int party_id, account_id, i; - char *nick; - - party_id = va_arg (ap, int); - account_id = va_arg (ap, int); - nick = va_arg (ap, char *); - - if (p->party_id == party_id) // 本来の所属なので問題なし - return; - - for (i = 0; i < MAX_PARTY; i++) - { - if (p->member[i].account_id == account_id - && strcmp (p->member[i].name, nick) == 0) - { - // 別のパーティに偽の所属データがあるので脱退 - printf ("int_party: party conflict! %d %d %d\n", account_id, - party_id, p->party_id); - mapif_parse_PartyLeave (-1, p->party_id, account_id); - } - } -} - -// キャラの競合がないかチェック -int party_check_conflict (int party_id, int account_id, char *nick) -{ - numdb_foreach (party_db, party_check_conflict_sub, party_id, account_id, - nick); - - return 0; -} - -//------------------------------------------------------------------- -// map serverへの通信 - -// パーティ作成可否 -int mapif_party_created (int fd, int account_id, struct party *p) -{ - WFIFOW (fd, 0) = 0x3820; - WFIFOL (fd, 2) = account_id; - if (p != NULL) - { - WFIFOB (fd, 6) = 0; - WFIFOL (fd, 7) = p->party_id; - memcpy (WFIFOP (fd, 11), p->name, 24); - printf ("int_party: created! %d %s\n", p->party_id, p->name); - } - else - { - WFIFOB (fd, 6) = 1; - WFIFOL (fd, 7) = 0; - memcpy (WFIFOP (fd, 11), "error", 24); - } - WFIFOSET (fd, 35); - - return 0; -} - -// パーティ情報見つからず -int mapif_party_noinfo (int fd, int party_id) -{ - WFIFOW (fd, 0) = 0x3821; - WFIFOW (fd, 2) = 8; - WFIFOL (fd, 4) = party_id; - WFIFOSET (fd, 8); - printf ("int_party: info not found %d\n", party_id); - - return 0; -} - -// パーティ情報まとめ送り -int mapif_party_info (int fd, struct party *p) -{ - unsigned char buf[4 + sizeof (struct party)]; - - WBUFW (buf, 0) = 0x3821; - memcpy (buf + 4, p, sizeof (struct party)); - WBUFW (buf, 2) = 4 + sizeof (struct party); - if (fd < 0) - mapif_sendall (buf, WBUFW (buf, 2)); - else - mapif_send (fd, buf, WBUFW (buf, 2)); -// printf("int_party: info %d %s\n", p->party_id, p->name); - - return 0; -} - -// パーティメンバ追加可否 -int mapif_party_memberadded (int fd, int party_id, int account_id, int flag) -{ - WFIFOW (fd, 0) = 0x3822; - WFIFOL (fd, 2) = party_id; - WFIFOL (fd, 6) = account_id; - WFIFOB (fd, 10) = flag; - WFIFOSET (fd, 11); - - return 0; -} - -// パーティ設定変更通知 -int mapif_party_optionchanged (int fd, struct party *p, int account_id, - int flag) -{ - unsigned char buf[15]; - - WBUFW (buf, 0) = 0x3823; - WBUFL (buf, 2) = p->party_id; - WBUFL (buf, 6) = account_id; - WBUFW (buf, 10) = p->exp; - WBUFW (buf, 12) = p->item; - WBUFB (buf, 14) = flag; - if (flag == 0) - mapif_sendall (buf, 15); - else - mapif_send (fd, buf, 15); - printf ("int_party: option changed %d %d %d %d %d\n", p->party_id, - account_id, p->exp, p->item, flag); - - return 0; -} - -// パーティ脱退通知 -int mapif_party_leaved (int party_id, int account_id, char *name) -{ - unsigned char buf[34]; - - WBUFW (buf, 0) = 0x3824; - WBUFL (buf, 2) = party_id; - WBUFL (buf, 6) = account_id; - memcpy (WBUFP (buf, 10), name, 24); - mapif_sendall (buf, 34); - printf ("int_party: party leaved %d %d %s\n", party_id, account_id, name); - - return 0; -} - -// パーティマップ更新通知 -int mapif_party_membermoved (struct party *p, int idx) -{ - unsigned char buf[29]; - - WBUFW (buf, 0) = 0x3825; - WBUFL (buf, 2) = p->party_id; - WBUFL (buf, 6) = p->member[idx].account_id; - memcpy (WBUFP (buf, 10), p->member[idx].map, 16); - WBUFB (buf, 26) = p->member[idx].online; - WBUFW (buf, 27) = p->member[idx].lv; - mapif_sendall (buf, 29); - - return 0; -} - -// パーティ解散通知 -int mapif_party_broken (int party_id, int flag) -{ - unsigned char buf[7]; - WBUFW (buf, 0) = 0x3826; - WBUFL (buf, 2) = party_id; - WBUFB (buf, 6) = flag; - mapif_sendall (buf, 7); - printf ("int_party: broken %d\n", party_id); - - return 0; -} - -// パーティ内発言 -int mapif_party_message (int party_id, int account_id, char *mes, int len) -{ - unsigned char buf[len + 12]; - - WBUFW (buf, 0) = 0x3827; - WBUFW (buf, 2) = len + 12; - WBUFL (buf, 4) = party_id; - WBUFL (buf, 8) = account_id; - memcpy (WBUFP (buf, 12), mes, len); - mapif_sendall (buf, len + 12); - - return 0; -} - -//------------------------------------------------------------------- -// map serverからの通信 - -// パーティ -int mapif_parse_CreateParty (int fd, int account_id, char *name, char *nick, - char *map, int lv) -{ - struct party *p; - int i; - - for (i = 0; i < 24 && name[i]; i++) - { - if (!(name[i] & 0xe0) || name[i] == 0x7f) - { - printf ("int_party: illegal party name [%s]\n", name); - mapif_party_created (fd, account_id, NULL); - return 0; - } - } - - if ((p = search_partyname (name)) != NULL) - { - printf ("int_party: same name party exists [%s]\n", name); - mapif_party_created (fd, account_id, NULL); - return 0; - } - CREATE (p, struct party, 1); - p->party_id = party_newid++; - memcpy (p->name, name, 24); - p->exp = 0; - p->item = 0; - p->member[0].account_id = account_id; - memcpy (p->member[0].name, nick, 24); - memcpy (p->member[0].map, map, 16); - p->member[0].leader = 1; - p->member[0].online = 1; - p->member[0].lv = lv; - - numdb_insert (party_db, p->party_id, p); - - mapif_party_created (fd, account_id, p); - mapif_party_info (fd, p); - - return 0; -} - -// パーティ情報要求 -int mapif_parse_PartyInfo (int fd, int party_id) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p != NULL) - mapif_party_info (fd, p); - else - mapif_party_noinfo (fd, party_id); - - return 0; -} - -// パーティ追加要求 -int mapif_parse_PartyAddMember (int fd, int party_id, int account_id, - char *nick, char *map, int lv) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p == NULL) - { - mapif_party_memberadded (fd, party_id, account_id, 1); - return 0; - } - - for (int i = 0; i < MAX_PARTY; i++) - { - if (p->member[i].account_id == 0) - { - int flag = 0; - - p->member[i].account_id = account_id; - memcpy (p->member[i].name, nick, 24); - memcpy (p->member[i].map, map, 16); - p->member[i].leader = 0; - p->member[i].online = 1; - p->member[i].lv = lv; - mapif_party_memberadded (fd, party_id, account_id, 0); - mapif_party_info (-1, p); - - if (p->exp > 0 && !party_check_exp_share (p)) - { - p->exp = 0; - flag = 0x01; - } - if (flag) - mapif_party_optionchanged (fd, p, 0, 0); - return 0; - } - } - mapif_party_memberadded (fd, party_id, account_id, 1); - - return 0; -} - -// パーティー設定変更要求 -int mapif_parse_PartyChangeOption (int fd, int party_id, int account_id, - int exp, int item) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p == NULL) - return 0; - - p->exp = exp; - int flag = 0; - if (exp > 0 && !party_check_exp_share (p)) - { - flag |= 0x01; - p->exp = 0; - } - - p->item = item; - - mapif_party_optionchanged (fd, p, account_id, flag); - return 0; -} - -// パーティ脱退要求 -int mapif_parse_PartyLeave (int fd, int party_id, int account_id) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p != NULL) - { - for (int i = 0; i < MAX_PARTY; i++) - { - if (p->member[i].account_id == account_id) - { - mapif_party_leaved (party_id, account_id, p->member[i].name); - - memset (&p->member[i], 0, sizeof (struct party_member)); - if (party_check_empty (p) == 0) - mapif_party_info (-1, p); // まだ人がいるのでデータ送信 - return 0; - } - } - } - - return 0; -} - -// パーティマップ更新要求 -int mapif_parse_PartyChangeMap (int fd, int party_id, int account_id, - char *map, int online, int lv) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p == NULL) - return 0; - - for (int i = 0; i < MAX_PARTY; i++) - { - if (p->member[i].account_id == account_id) - { - int flag = 0; - - memcpy (p->member[i].map, map, 16); - p->member[i].online = online; - p->member[i].lv = lv; - mapif_party_membermoved (p, i); - - if (p->exp > 0 && !party_check_exp_share (p)) - { - p->exp = 0; - flag = 1; - } - if (flag) - mapif_party_optionchanged (fd, p, 0, 0); - break; - } - } - - return 0; -} - -// パーティ解散要求 -int mapif_parse_BreakParty (int fd, int party_id) -{ - struct party *p = (struct party *)numdb_search (party_db, party_id); - if (p == NULL) - return 0; - - numdb_erase (party_db, party_id); - mapif_party_broken (fd, party_id); - - return 0; -} - -// パーティメッセージ送信 -int mapif_parse_PartyMessage (int fd, int party_id, int account_id, char *mes, - int len) -{ - return mapif_party_message (party_id, account_id, mes, len); -} - -// パーティチェック要求 -int mapif_parse_PartyCheck (int fd, int party_id, int account_id, char *nick) -{ - return party_check_conflict (party_id, account_id, nick); -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_party_parse_frommap (int fd) -{ - switch (RFIFOW (fd, 0)) - { - case 0x3020: - mapif_parse_CreateParty (fd, RFIFOL (fd, 2), RFIFOP (fd, 6), - RFIFOP (fd, 30), RFIFOP (fd, 54), - RFIFOW (fd, 70)); - break; - case 0x3021: - mapif_parse_PartyInfo (fd, RFIFOL (fd, 2)); - break; - case 0x3022: - mapif_parse_PartyAddMember (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOP (fd, 10), RFIFOP (fd, 34), - RFIFOW (fd, 50)); - break; - case 0x3023: - mapif_parse_PartyChangeOption (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOW (fd, 10), RFIFOW (fd, 12)); - break; - case 0x3024: - mapif_parse_PartyLeave (fd, RFIFOL (fd, 2), RFIFOL (fd, 6)); - break; - case 0x3025: - mapif_parse_PartyChangeMap (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOP (fd, 10), RFIFOB (fd, 26), - RFIFOW (fd, 27)); - break; - case 0x3026: - mapif_parse_BreakParty (fd, RFIFOL (fd, 2)); - break; - case 0x3027: - mapif_parse_PartyMessage (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), - RFIFOP (fd, 12), RFIFOW (fd, 2) - 12); - break; - case 0x3028: - mapif_parse_PartyCheck (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), - RFIFOP (fd, 10)); - break; - default: - return 0; - } - - return 1; -} - -// サーバーから脱退要求(キャラ削除用) -int inter_party_leave (int party_id, int account_id) -{ - return mapif_parse_PartyLeave (-1, party_id, account_id); -} diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp new file mode 100644 index 0000000..7d0e0ce --- /dev/null +++ b/src/char/int_party.cpp @@ -0,0 +1,673 @@ +// $Id: int_party.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "inter.hpp" +#include "int_party.hpp" +#include "../common/mmo.hpp" +#include "char.hpp" +#include "../common/socket.hpp" +#include "../common/db.hpp" +#include "../common/lock.hpp" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char party_txt[1024] = "save/party.txt"; + +static struct dbt *party_db; +static int party_newid = 100; + +int mapif_party_broken (int party_id, int flag); +int party_check_empty (struct party *p); +int mapif_parse_PartyLeave (int fd, int party_id, int account_id); + +// パーティデータの文字列への変換 +int inter_party_tostr (char *str, struct party *p) +{ + int i, len; + + len = + sprintf (str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, + p->item); + for (i = 0; i < MAX_PARTY; i++) + { + struct party_member *m = &p->member[i]; + len += + sprintf (str + len, "%d,%d\t%s\t", m->account_id, m->leader, + ((m->account_id > 0) ? m->name : "NoMember")); + } + + return 0; +} + +// パーティデータの文字列からの変換 +int inter_party_fromstr (char *str, struct party *p) +{ + int i, j; + int tmp_int[16]; + char tmp_str[256]; + + memset (p, 0, sizeof (struct party)); + +// printf("sscanf party main info\n"); + if (sscanf + (str, "%d\t%[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], + &tmp_int[2]) != 4) + return 1; + + p->party_id = tmp_int[0]; + strcpy (p->name, tmp_str); + p->exp = tmp_int[1]; + p->item = tmp_int[2]; +// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]); + + for (j = 0; j < 3 && str != NULL; j++) + str = strchr (str + 1, '\t'); + + for (i = 0; i < MAX_PARTY; i++) + { + struct party_member *m = &p->member[i]; + if (str == NULL) + return 1; +// printf("sscanf party member info %d\n", i); + + if (sscanf + (str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], + tmp_str) != 3) + return 1; + + m->account_id = tmp_int[0]; + m->leader = tmp_int[1]; + strncpy (m->name, tmp_str, sizeof (m->name)); +// printf(" %d %d [%s]\n", tmp_int[0], tmp_int[1], tmp_str); + + for (j = 0; j < 2 && str != NULL; j++) + str = strchr (str + 1, '\t'); + } + + return 0; +} + +// パーティデータのロード +int inter_party_init (void) +{ + char line[8192]; + struct party *p; + FILE *fp; + int c = 0; + int i, j; + + party_db = numdb_init (); + + if ((fp = fopen_ (party_txt, "r")) == NULL) + return 1; + + while (fgets (line, sizeof (line) - 1, fp)) + { + j = 0; + if (sscanf (line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 + && party_newid <= i) + { + party_newid = i; + continue; + } + + CREATE (p, struct party, 1); + if (inter_party_fromstr (line, p) == 0 && p->party_id > 0) + { + if (p->party_id >= party_newid) + party_newid = p->party_id + 1; + numdb_insert (party_db, p->party_id, p); + party_check_empty (p); + } + else + { + printf ("int_party: broken data [%s] line %d\n", party_txt, + c + 1); + free (p); + } + c++; + } + fclose_ (fp); +// printf("int_party: %s read done (%d parties)\n", party_txt, c); + + return 0; +} + +// パーティーデータのセーブ用 +void inter_party_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[8192]; + FILE *fp; + + inter_party_tostr (line, (struct party *) data); + fp = va_arg (ap, FILE *); + fprintf (fp, "%s\n", line); +} + +// パーティーデータのセーブ +int inter_party_save (void) +{ + FILE *fp; + int lock; + + if ((fp = lock_fopen (party_txt, &lock)) == NULL) + { + printf ("int_party: cant write [%s] !!! data is lost !!!\n", + party_txt); + return 1; + } + numdb_foreach (party_db, inter_party_save_sub, fp); +// fprintf(fp, "%d\t%%newid%%\n", party_newid); + lock_fclose (fp, party_txt, &lock); +// printf("int_party: %s saved.\n", party_txt); + + return 0; +} + +// パーティ名検索用 +void search_partyname_sub (db_key_t key, db_val_t 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 (strcasecmp (p->name, str) == 0) + *dst = p; +} + +// パーティ名検索 +struct party *search_partyname (char *str) +{ + struct party *p = NULL; + numdb_foreach (party_db, search_partyname_sub, str, &p); + + return p; +} + +// EXP公平分配できるかチェック +int party_check_exp_share (struct party *p) +{ + int i; + int maxlv = 0, minlv = 0x7fffffff; + + for (i = 0; i < MAX_PARTY; i++) + { + int lv = p->member[i].lv; + if (p->member[i].online) + { + if (lv < minlv) + minlv = lv; + if (maxlv < lv) + maxlv = lv; + } + } + + return (maxlv == 0 || maxlv - minlv <= party_share_level); +} + +// パーティが空かどうかチェック +int party_check_empty (struct party *p) +{ + int i; + +// printf("party check empty %08X\n", (int)p); + for (i = 0; i < MAX_PARTY; i++) + { +// printf("%d acc=%d\n", i, p->member[i].account_id); + if (p->member[i].account_id > 0) + { + return 0; + } + } + // 誰もいないので解散 + mapif_party_broken (p->party_id, 0); + numdb_erase (party_db, p->party_id); + free (p); + + return 1; +} + +// キャラの競合がないかチェック用 +void party_check_conflict_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct party *p = (struct party *) data; + int party_id, account_id, i; + char *nick; + + party_id = va_arg (ap, int); + account_id = va_arg (ap, int); + nick = va_arg (ap, char *); + + if (p->party_id == party_id) // 本来の所属なので問題なし + return; + + for (i = 0; i < MAX_PARTY; i++) + { + if (p->member[i].account_id == account_id + && strcmp (p->member[i].name, nick) == 0) + { + // 別のパーティに偽の所属データがあるので脱退 + printf ("int_party: party conflict! %d %d %d\n", account_id, + party_id, p->party_id); + mapif_parse_PartyLeave (-1, p->party_id, account_id); + } + } +} + +// キャラの競合がないかチェック +int party_check_conflict (int party_id, int account_id, char *nick) +{ + numdb_foreach (party_db, party_check_conflict_sub, party_id, account_id, + nick); + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// パーティ作成可否 +int mapif_party_created (int fd, int account_id, struct party *p) +{ + WFIFOW (fd, 0) = 0x3820; + WFIFOL (fd, 2) = account_id; + if (p != NULL) + { + WFIFOB (fd, 6) = 0; + WFIFOL (fd, 7) = p->party_id; + memcpy (WFIFOP (fd, 11), p->name, 24); + printf ("int_party: created! %d %s\n", p->party_id, p->name); + } + else + { + WFIFOB (fd, 6) = 1; + WFIFOL (fd, 7) = 0; + memcpy (WFIFOP (fd, 11), "error", 24); + } + WFIFOSET (fd, 35); + + return 0; +} + +// パーティ情報見つからず +int mapif_party_noinfo (int fd, int party_id) +{ + WFIFOW (fd, 0) = 0x3821; + WFIFOW (fd, 2) = 8; + WFIFOL (fd, 4) = party_id; + WFIFOSET (fd, 8); + printf ("int_party: info not found %d\n", party_id); + + return 0; +} + +// パーティ情報まとめ送り +int mapif_party_info (int fd, struct party *p) +{ + unsigned char buf[4 + sizeof (struct party)]; + + WBUFW (buf, 0) = 0x3821; + memcpy (buf + 4, p, sizeof (struct party)); + WBUFW (buf, 2) = 4 + sizeof (struct party); + if (fd < 0) + mapif_sendall (buf, WBUFW (buf, 2)); + else + mapif_send (fd, buf, WBUFW (buf, 2)); +// printf("int_party: info %d %s\n", p->party_id, p->name); + + return 0; +} + +// パーティメンバ追加可否 +int mapif_party_memberadded (int fd, int party_id, int account_id, int flag) +{ + WFIFOW (fd, 0) = 0x3822; + WFIFOL (fd, 2) = party_id; + WFIFOL (fd, 6) = account_id; + WFIFOB (fd, 10) = flag; + WFIFOSET (fd, 11); + + return 0; +} + +// パーティ設定変更通知 +int mapif_party_optionchanged (int fd, struct party *p, int account_id, + int flag) +{ + unsigned char buf[15]; + + WBUFW (buf, 0) = 0x3823; + WBUFL (buf, 2) = p->party_id; + WBUFL (buf, 6) = account_id; + WBUFW (buf, 10) = p->exp; + WBUFW (buf, 12) = p->item; + WBUFB (buf, 14) = flag; + if (flag == 0) + mapif_sendall (buf, 15); + else + mapif_send (fd, buf, 15); + printf ("int_party: option changed %d %d %d %d %d\n", p->party_id, + account_id, p->exp, p->item, flag); + + return 0; +} + +// パーティ脱退通知 +int mapif_party_leaved (int party_id, int account_id, char *name) +{ + unsigned char buf[34]; + + WBUFW (buf, 0) = 0x3824; + WBUFL (buf, 2) = party_id; + WBUFL (buf, 6) = account_id; + memcpy (WBUFP (buf, 10), name, 24); + mapif_sendall (buf, 34); + printf ("int_party: party leaved %d %d %s\n", party_id, account_id, name); + + return 0; +} + +// パーティマップ更新通知 +int mapif_party_membermoved (struct party *p, int idx) +{ + unsigned char buf[29]; + + WBUFW (buf, 0) = 0x3825; + WBUFL (buf, 2) = p->party_id; + WBUFL (buf, 6) = p->member[idx].account_id; + memcpy (WBUFP (buf, 10), p->member[idx].map, 16); + WBUFB (buf, 26) = p->member[idx].online; + WBUFW (buf, 27) = p->member[idx].lv; + mapif_sendall (buf, 29); + + return 0; +} + +// パーティ解散通知 +int mapif_party_broken (int party_id, int flag) +{ + unsigned char buf[7]; + WBUFW (buf, 0) = 0x3826; + WBUFL (buf, 2) = party_id; + WBUFB (buf, 6) = flag; + mapif_sendall (buf, 7); + printf ("int_party: broken %d\n", party_id); + + return 0; +} + +// パーティ内発言 +int mapif_party_message (int party_id, int account_id, char *mes, int len) +{ + unsigned char buf[len + 12]; + + WBUFW (buf, 0) = 0x3827; + WBUFW (buf, 2) = len + 12; + WBUFL (buf, 4) = party_id; + WBUFL (buf, 8) = account_id; + memcpy (WBUFP (buf, 12), mes, len); + mapif_sendall (buf, len + 12); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + +// パーティ +int mapif_parse_CreateParty (int fd, int account_id, char *name, char *nick, + char *map, int lv) +{ + struct party *p; + int i; + + for (i = 0; i < 24 && name[i]; i++) + { + if (!(name[i] & 0xe0) || name[i] == 0x7f) + { + printf ("int_party: illegal party name [%s]\n", name); + mapif_party_created (fd, account_id, NULL); + return 0; + } + } + + if ((p = search_partyname (name)) != NULL) + { + printf ("int_party: same name party exists [%s]\n", name); + mapif_party_created (fd, account_id, NULL); + return 0; + } + CREATE (p, struct party, 1); + p->party_id = party_newid++; + memcpy (p->name, name, 24); + p->exp = 0; + p->item = 0; + p->member[0].account_id = account_id; + memcpy (p->member[0].name, nick, 24); + memcpy (p->member[0].map, map, 16); + p->member[0].leader = 1; + p->member[0].online = 1; + p->member[0].lv = lv; + + numdb_insert (party_db, p->party_id, p); + + mapif_party_created (fd, account_id, p); + mapif_party_info (fd, p); + + return 0; +} + +// パーティ情報要求 +int mapif_parse_PartyInfo (int fd, int party_id) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p != NULL) + mapif_party_info (fd, p); + else + mapif_party_noinfo (fd, party_id); + + return 0; +} + +// パーティ追加要求 +int mapif_parse_PartyAddMember (int fd, int party_id, int account_id, + char *nick, char *map, int lv) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p == NULL) + { + mapif_party_memberadded (fd, party_id, account_id, 1); + return 0; + } + + for (int i = 0; i < MAX_PARTY; i++) + { + if (p->member[i].account_id == 0) + { + int flag = 0; + + p->member[i].account_id = account_id; + memcpy (p->member[i].name, nick, 24); + memcpy (p->member[i].map, map, 16); + p->member[i].leader = 0; + p->member[i].online = 1; + p->member[i].lv = lv; + mapif_party_memberadded (fd, party_id, account_id, 0); + mapif_party_info (-1, p); + + if (p->exp > 0 && !party_check_exp_share (p)) + { + p->exp = 0; + flag = 0x01; + } + if (flag) + mapif_party_optionchanged (fd, p, 0, 0); + return 0; + } + } + mapif_party_memberadded (fd, party_id, account_id, 1); + + return 0; +} + +// パーティー設定変更要求 +int mapif_parse_PartyChangeOption (int fd, int party_id, int account_id, + int exp, int item) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p == NULL) + return 0; + + p->exp = exp; + int flag = 0; + if (exp > 0 && !party_check_exp_share (p)) + { + flag |= 0x01; + p->exp = 0; + } + + p->item = item; + + mapif_party_optionchanged (fd, p, account_id, flag); + return 0; +} + +// パーティ脱退要求 +int mapif_parse_PartyLeave (int fd, int party_id, int account_id) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p != NULL) + { + for (int i = 0; i < MAX_PARTY; i++) + { + if (p->member[i].account_id == account_id) + { + mapif_party_leaved (party_id, account_id, p->member[i].name); + + memset (&p->member[i], 0, sizeof (struct party_member)); + if (party_check_empty (p) == 0) + mapif_party_info (-1, p); // まだ人がいるのでデータ送信 + return 0; + } + } + } + + return 0; +} + +// パーティマップ更新要求 +int mapif_parse_PartyChangeMap (int fd, int party_id, int account_id, + char *map, int online, int lv) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p == NULL) + return 0; + + for (int i = 0; i < MAX_PARTY; i++) + { + if (p->member[i].account_id == account_id) + { + int flag = 0; + + memcpy (p->member[i].map, map, 16); + p->member[i].online = online; + p->member[i].lv = lv; + mapif_party_membermoved (p, i); + + if (p->exp > 0 && !party_check_exp_share (p)) + { + p->exp = 0; + flag = 1; + } + if (flag) + mapif_party_optionchanged (fd, p, 0, 0); + break; + } + } + + return 0; +} + +// パーティ解散要求 +int mapif_parse_BreakParty (int fd, int party_id) +{ + struct party *p = (struct party *)numdb_search (party_db, party_id); + if (p == NULL) + return 0; + + numdb_erase (party_db, party_id); + mapif_party_broken (fd, party_id); + + return 0; +} + +// パーティメッセージ送信 +int mapif_parse_PartyMessage (int fd, int party_id, int account_id, char *mes, + int len) +{ + return mapif_party_message (party_id, account_id, mes, len); +} + +// パーティチェック要求 +int mapif_parse_PartyCheck (int fd, int party_id, int account_id, char *nick) +{ + return party_check_conflict (party_id, account_id, nick); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_party_parse_frommap (int fd) +{ + switch (RFIFOW (fd, 0)) + { + case 0x3020: + mapif_parse_CreateParty (fd, RFIFOL (fd, 2), RFIFOP (fd, 6), + RFIFOP (fd, 30), RFIFOP (fd, 54), + RFIFOW (fd, 70)); + break; + case 0x3021: + mapif_parse_PartyInfo (fd, RFIFOL (fd, 2)); + break; + case 0x3022: + mapif_parse_PartyAddMember (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOP (fd, 10), RFIFOP (fd, 34), + RFIFOW (fd, 50)); + break; + case 0x3023: + mapif_parse_PartyChangeOption (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOW (fd, 10), RFIFOW (fd, 12)); + break; + case 0x3024: + mapif_parse_PartyLeave (fd, RFIFOL (fd, 2), RFIFOL (fd, 6)); + break; + case 0x3025: + mapif_parse_PartyChangeMap (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOP (fd, 10), RFIFOB (fd, 26), + RFIFOW (fd, 27)); + break; + case 0x3026: + mapif_parse_BreakParty (fd, RFIFOL (fd, 2)); + break; + case 0x3027: + mapif_parse_PartyMessage (fd, RFIFOL (fd, 4), RFIFOL (fd, 8), + RFIFOP (fd, 12), RFIFOW (fd, 2) - 12); + break; + case 0x3028: + mapif_parse_PartyCheck (fd, RFIFOL (fd, 2), RFIFOL (fd, 6), + RFIFOP (fd, 10)); + break; + default: + return 0; + } + + return 1; +} + +// サーバーから脱退要求(キャラ削除用) +int inter_party_leave (int party_id, int account_id) +{ + return mapif_parse_PartyLeave (-1, party_id, account_id); +} diff --git a/src/char/int_party.h b/src/char/int_party.h deleted file mode 100644 index 2007ed5..0000000 --- a/src/char/int_party.h +++ /dev/null @@ -1,14 +0,0 @@ -// $Id: int_party.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#ifndef _INT_PARTY_H_ -#define _INT_PARTY_H_ - -int inter_party_init (void); -int inter_party_save (void); - -int inter_party_parse_frommap (int fd); - -int inter_party_leave (int party_id, int account_id); - -extern char party_txt[1024]; - -#endif diff --git a/src/char/int_party.hpp b/src/char/int_party.hpp new file mode 100644 index 0000000..93e8887 --- /dev/null +++ b/src/char/int_party.hpp @@ -0,0 +1,14 @@ +// $Id: int_party.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef INT_PARTY_HPP +#define INT_PARTY_HPP + +int inter_party_init (void); +int inter_party_save (void); + +int inter_party_parse_frommap (int fd); + +int inter_party_leave (int party_id, int account_id); + +extern char party_txt[1024]; + +#endif diff --git a/src/char/int_storage.c b/src/char/int_storage.c deleted file mode 100644 index e565572..0000000 --- a/src/char/int_storage.c +++ /dev/null @@ -1,576 +0,0 @@ -// $Id: int_storage.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ - -#include <string.h> -#include <stdlib.h> - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "char.h" -#include "inter.h" -#include "int_storage.h" -#include "int_guild.h" - -// ファイル名のデフォルト -// inter_config_read()で再設定される -char storage_txt[1024] = "save/storage.txt"; -char guild_storage_txt[1024] = "save/g_storage.txt"; - -static struct dbt *storage_db; -static struct dbt *guild_storage_db; - -// 倉庫データを文字列に変換 -int storage_tostr (char *str, struct storage *p) -{ - int i, f = 0; - char *str_p = str; - str_p += sprintf (str_p, "%d,%d\t", p->account_id, p->storage_amount); - - for (i = 0; i < MAX_STORAGE; i++) - if ((p->storage_[i].nameid) && (p->storage_[i].amount)) - { - str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->storage_[i].id, p->storage_[i].nameid, - p->storage_[i].amount, p->storage_[i].equip, - p->storage_[i].identify, p->storage_[i].refine, - p->storage_[i].attribute, - p->storage_[i].card[0], p->storage_[i].card[1], - p->storage_[i].card[2], p->storage_[i].card[3]); - f++; - } - - *(str_p++) = '\t'; - - *str_p = '\0'; - if (!f) - str[0] = 0; - return 0; -} - -// 文字列を倉庫データに変換 -int storage_fromstr (char *str, struct storage *p) -{ - int tmp_int[256]; - int set, next, len, i; - - set = sscanf (str, "%d,%d%n", &tmp_int[0], &tmp_int[1], &next); - p->storage_amount = tmp_int[1]; - - if (set != 2) - return 1; - if (str[next] == '\n' || str[next] == '\r') - return 0; - next++; - for (i = 0; str[next] && str[next] != '\t' && i < MAX_STORAGE; i++) - { - if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &tmp_int[10], &len) == 12) - { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - p->storage_[i].card[0] = tmp_int[7]; - p->storage_[i].card[1] = tmp_int[8]; - p->storage_[i].card[2] = tmp_int[9]; - p->storage_[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - - else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len) == 11) - { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - p->storage_[i].card[0] = tmp_int[7]; - p->storage_[i].card[1] = tmp_int[8]; - p->storage_[i].card[2] = tmp_int[9]; - p->storage_[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - - else - return 1; - } - if (i >= MAX_STORAGE && str[next] && str[next] != '\t') - printf - ("storage_fromstr: Found a storage line with more items than MAX_STORAGE (%d), remaining items have been discarded!\n", - MAX_STORAGE); - return 0; -} - -int guild_storage_tostr (char *str, struct guild_storage *p) -{ - int i, f = 0; - char *str_p = str; - str_p += sprintf (str, "%d,%d\t", p->guild_id, p->storage_amount); - - for (i = 0; i < MAX_GUILD_STORAGE; i++) - if ((p->storage_[i].nameid) && (p->storage_[i].amount)) - { - str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->storage_[i].id, p->storage_[i].nameid, - p->storage_[i].amount, p->storage_[i].equip, - p->storage_[i].identify, p->storage_[i].refine, - p->storage_[i].attribute, - p->storage_[i].card[0], p->storage_[i].card[1], - p->storage_[i].card[2], p->storage_[i].card[3]); - f++; - } - - *(str_p++) = '\t'; - - *str_p = '\0'; - if (!f) - str[0] = 0; - return 0; -} - -int guild_storage_fromstr (char *str, struct guild_storage *p) -{ - int tmp_int[256]; - int set, next, len, i; - - set = sscanf (str, "%d,%d%n", &tmp_int[0], &tmp_int[1], &next); - p->storage_amount = tmp_int[1]; - - if (set != 2) - return 1; - if (str[next] == '\n' || str[next] == '\r') - return 0; - next++; - for (i = 0; str[next] && str[next] != '\t' && i < MAX_GUILD_STORAGE; i++) - { - if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &tmp_int[10], &len) == 12) - { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - p->storage_[i].card[0] = tmp_int[7]; - p->storage_[i].card[1] = tmp_int[8]; - p->storage_[i].card[2] = tmp_int[9]; - p->storage_[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - - else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len) == 11) - { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - p->storage_[i].card[0] = tmp_int[7]; - p->storage_[i].card[1] = tmp_int[8]; - p->storage_[i].card[2] = tmp_int[9]; - p->storage_[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - - else - return 1; - } - if (i >= MAX_GUILD_STORAGE && str[next] && str[next] != '\t') - printf - ("guild_storage_fromstr: Found a storage line with more items than MAX_GUILD_STORAGE (%d), remaining items have been discarded!\n", - MAX_GUILD_STORAGE); - return 0; -} - -// アカウントから倉庫データインデックスを得る(新規倉庫追加可能) -struct storage *account2storage (int account_id) -{ - struct storage *s; - s = (struct storage *) numdb_search (storage_db, account_id); - if (s == NULL) - { - CREATE (s, struct storage, 1); - memset (s, 0, sizeof (struct storage)); - s->account_id = account_id; - numdb_insert (storage_db, s->account_id, s); - } - return s; -} - -struct guild_storage *guild2storage (int guild_id) -{ - struct guild_storage *gs = NULL; - if (inter_guild_search (guild_id) != NULL) - { - gs = (struct guild_storage *) numdb_search (guild_storage_db, - guild_id); - if (gs == NULL) - { - CREATE (gs, struct guild_storage, 1); - gs->guild_id = guild_id; - numdb_insert (guild_storage_db, gs->guild_id, gs); - } - } - return gs; -} - -//--------------------------------------------------------- -// 倉庫データを読み込む -int inter_storage_init (void) -{ - char line[65536]; - int c = 0, tmp_int; - struct storage *s; - struct guild_storage *gs; - FILE *fp; - - storage_db = numdb_init (); - - fp = fopen_ (storage_txt, "r"); - if (fp == NULL) - { - printf ("cant't read : %s\n", storage_txt); - return 1; - } - while (fgets (line, 65535, fp)) - { - sscanf (line, "%d", &tmp_int); - CREATE (s, struct storage, 1); - s->account_id = tmp_int; - if (s->account_id > 0 && storage_fromstr (line, s) == 0) - { - numdb_insert (storage_db, s->account_id, s); - } - else - { - printf ("int_storage: broken data [%s] line %d\n", storage_txt, - c); - free (s); - } - c++; - } - fclose_ (fp); - - c = 0; - guild_storage_db = numdb_init (); - - fp = fopen_ (guild_storage_txt, "r"); - if (fp == NULL) - { - printf ("cant't read : %s\n", guild_storage_txt); - return 1; - } - while (fgets (line, 65535, fp)) - { - sscanf (line, "%d", &tmp_int); - CREATE (gs, struct guild_storage, 1); - gs->guild_id = tmp_int; - if (gs->guild_id > 0 && guild_storage_fromstr (line, gs) == 0) - { - numdb_insert (guild_storage_db, gs->guild_id, gs); - } - else - { - printf ("int_storage: broken data [%s] line %d\n", - guild_storage_txt, c); - free (gs); - } - c++; - } - fclose_ (fp); - - return 0; -} - -void storage_db_final (db_key_t k, db_val_t data, va_list ap) -{ - struct storage *p = (struct storage *) data; - if (p) - free (p); -} - -void guild_storage_db_final (db_key_t k, db_val_t data, va_list ap) -{ - struct guild_storage *p = (struct guild_storage *) data; - if (p) - free (p); -} - -void inter_storage_final (void) -{ - numdb_final (storage_db, storage_db_final); - numdb_final (guild_storage_db, guild_storage_db_final); - return; -} - -void inter_storage_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[65536]; - FILE *fp; - storage_tostr (line, (struct storage *) data); - fp = va_arg (ap, FILE *); - if (*line) - fprintf (fp, "%s\n", line); -} - -//--------------------------------------------------------- -// 倉庫データを書き込む -int inter_storage_save (void) -{ - FILE *fp; - int lock; - - if (!storage_db) - return 1; - - if ((fp = lock_fopen (storage_txt, &lock)) == NULL) - { - printf ("int_storage: cant write [%s] !!! data is lost !!!\n", - storage_txt); - return 1; - } - numdb_foreach (storage_db, inter_storage_save_sub, fp); - lock_fclose (fp, storage_txt, &lock); -// printf("int_storage: %s saved.\n",storage_txt); - return 0; -} - -void inter_guild_storage_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[65536]; - FILE *fp; - - if (inter_guild_search (((struct guild_storage *) data)->guild_id) != - NULL) - { - guild_storage_tostr (line, (struct guild_storage *) data); - fp = va_arg (ap, FILE *); - if (*line) - fprintf (fp, "%s\n", line); - } -} - -//--------------------------------------------------------- -// 倉庫データを書き込む -int inter_guild_storage_save (void) -{ - FILE *fp; - int lock; - - if (!guild_storage_db) - return 1; - - if ((fp = lock_fopen (guild_storage_txt, &lock)) == NULL) - { - printf ("int_storage: cant write [%s] !!! data is lost !!!\n", - guild_storage_txt); - return 1; - } - numdb_foreach (guild_storage_db, inter_guild_storage_save_sub, fp); - lock_fclose (fp, guild_storage_txt, &lock); -// printf("int_storage: %s saved.\n",guild_storage_txt); - return 0; -} - -// 倉庫データ削除 -int inter_storage_delete (int account_id) -{ - struct storage *s = - (struct storage *) numdb_search (storage_db, account_id); - if (s) - { - numdb_erase (storage_db, account_id); - free (s); - } - return 0; -} - -// ギルド倉庫データ削除 -int inter_guild_storage_delete (int guild_id) -{ - struct guild_storage *gs = - (struct guild_storage *) numdb_search (guild_storage_db, guild_id); - if (gs) - { - numdb_erase (guild_storage_db, guild_id); - free (gs); - } - return 0; -} - -//--------------------------------------------------------- -// map serverへの通信 - -// 倉庫データの送信 -int mapif_load_storage (int fd, int account_id) -{ - struct storage *s = account2storage (account_id); - WFIFOW (fd, 0) = 0x3810; - WFIFOW (fd, 2) = sizeof (struct storage) + 8; - WFIFOL (fd, 4) = account_id; - memcpy (WFIFOP (fd, 8), s, sizeof (struct storage)); - WFIFOSET (fd, WFIFOW (fd, 2)); - return 0; -} - -// 倉庫データ保存完了送信 -int mapif_save_storage_ack (int fd, int account_id) -{ - WFIFOW (fd, 0) = 0x3811; - WFIFOL (fd, 2) = account_id; - WFIFOB (fd, 6) = 0; - WFIFOSET (fd, 7); - return 0; -} - -int mapif_load_guild_storage (int fd, int account_id, int guild_id) -{ - struct guild_storage *gs = guild2storage (guild_id); - WFIFOW (fd, 0) = 0x3818; - if (gs) - { - WFIFOW (fd, 2) = sizeof (struct guild_storage) + 12; - WFIFOL (fd, 4) = account_id; - WFIFOL (fd, 8) = guild_id; - memcpy (WFIFOP (fd, 12), gs, sizeof (struct guild_storage)); - } - else - { - WFIFOW (fd, 2) = 12; - WFIFOL (fd, 4) = account_id; - WFIFOL (fd, 8) = 0; - } - WFIFOSET (fd, WFIFOW (fd, 2)); - - return 0; -} - -int mapif_save_guild_storage_ack (int fd, int account_id, int guild_id, - int fail) -{ - WFIFOW (fd, 0) = 0x3819; - WFIFOL (fd, 2) = account_id; - WFIFOL (fd, 6) = guild_id; - WFIFOB (fd, 10) = fail; - WFIFOSET (fd, 11); - return 0; -} - -//--------------------------------------------------------- -// map serverからの通信 - -// 倉庫データ要求受信 -int mapif_parse_LoadStorage (int fd) -{ - mapif_load_storage (fd, RFIFOL (fd, 2)); - return 0; -} - -// 倉庫データ受信&保存 -int mapif_parse_SaveStorage (int fd) -{ - struct storage *s; - int account_id = RFIFOL (fd, 4); - int len = RFIFOW (fd, 2); - if (sizeof (struct storage) != len - 8) - { - printf ("inter storage: data size error %d %d\n", - sizeof (struct storage), len - 8); - } - else - { - s = account2storage (account_id); - memcpy (s, RFIFOP (fd, 8), sizeof (struct storage)); - mapif_save_storage_ack (fd, account_id); - } - return 0; -} - -int mapif_parse_LoadGuildStorage (int fd) -{ - mapif_load_guild_storage (fd, RFIFOL (fd, 2), RFIFOL (fd, 6)); - return 0; -} - -int mapif_parse_SaveGuildStorage (int fd) -{ - struct guild_storage *gs; - int guild_id = RFIFOL (fd, 8); - int len = RFIFOW (fd, 2); - if (sizeof (struct guild_storage) != len - 12) - { - printf ("inter storage: data size error %d %d\n", - sizeof (struct guild_storage), len - 12); - } - else - { - gs = guild2storage (guild_id); - if (gs) - { - memcpy (gs, RFIFOP (fd, 12), sizeof (struct guild_storage)); - mapif_save_guild_storage_ack (fd, RFIFOL (fd, 4), guild_id, 0); - } - else - mapif_save_guild_storage_ack (fd, RFIFOL (fd, 4), guild_id, 1); - } - return 0; -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_storage_parse_frommap (int fd) -{ - switch (RFIFOW (fd, 0)) - { - case 0x3010: - mapif_parse_LoadStorage (fd); - break; - case 0x3011: - mapif_parse_SaveStorage (fd); - break; - case 0x3018: - mapif_parse_LoadGuildStorage (fd); - break; - case 0x3019: - mapif_parse_SaveGuildStorage (fd); - break; - default: - return 0; - } - return 1; -} diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp new file mode 100644 index 0000000..b197213 --- /dev/null +++ b/src/char/int_storage.cpp @@ -0,0 +1,576 @@ +// $Id: int_storage.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ + +#include <string.h> +#include <stdlib.h> + +#include "../common/mmo.hpp" +#include "../common/socket.hpp" +#include "../common/db.hpp" +#include "../common/lock.hpp" +#include "char.hpp" +#include "inter.hpp" +#include "int_storage.hpp" +#include "int_guild.hpp" + +// ファイル名のデフォルト +// inter_config_read()で再設定される +char storage_txt[1024] = "save/storage.txt"; +char guild_storage_txt[1024] = "save/g_storage.txt"; + +static struct dbt *storage_db; +static struct dbt *guild_storage_db; + +// 倉庫データを文字列に変換 +int storage_tostr (char *str, struct storage *p) +{ + int i, f = 0; + char *str_p = str; + str_p += sprintf (str_p, "%d,%d\t", p->account_id, p->storage_amount); + + for (i = 0; i < MAX_STORAGE; i++) + if ((p->storage_[i].nameid) && (p->storage_[i].amount)) + { + str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->storage_[i].id, p->storage_[i].nameid, + p->storage_[i].amount, p->storage_[i].equip, + p->storage_[i].identify, p->storage_[i].refine, + p->storage_[i].attribute, + p->storage_[i].card[0], p->storage_[i].card[1], + p->storage_[i].card[2], p->storage_[i].card[3]); + f++; + } + + *(str_p++) = '\t'; + + *str_p = '\0'; + if (!f) + str[0] = 0; + return 0; +} + +// 文字列を倉庫データに変換 +int storage_fromstr (char *str, struct storage *p) +{ + int tmp_int[256]; + int set, next, len, i; + + set = sscanf (str, "%d,%d%n", &tmp_int[0], &tmp_int[1], &next); + p->storage_amount = tmp_int[1]; + + if (set != 2) + return 1; + if (str[next] == '\n' || str[next] == '\r') + return 0; + next++; + for (i = 0; str[next] && str[next] != '\t' && i < MAX_STORAGE; i++) + { + if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &tmp_int[10], &len) == 12) + { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + p->storage_[i].card[0] = tmp_int[7]; + p->storage_[i].card[1] = tmp_int[8]; + p->storage_[i].card[2] = tmp_int[9]; + p->storage_[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + + else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len) == 11) + { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + p->storage_[i].card[0] = tmp_int[7]; + p->storage_[i].card[1] = tmp_int[8]; + p->storage_[i].card[2] = tmp_int[9]; + p->storage_[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + + else + return 1; + } + if (i >= MAX_STORAGE && str[next] && str[next] != '\t') + printf + ("storage_fromstr: Found a storage line with more items than MAX_STORAGE (%d), remaining items have been discarded!\n", + MAX_STORAGE); + return 0; +} + +int guild_storage_tostr (char *str, struct guild_storage *p) +{ + int i, f = 0; + char *str_p = str; + str_p += sprintf (str, "%d,%d\t", p->guild_id, p->storage_amount); + + for (i = 0; i < MAX_GUILD_STORAGE; i++) + if ((p->storage_[i].nameid) && (p->storage_[i].amount)) + { + str_p += sprintf (str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->storage_[i].id, p->storage_[i].nameid, + p->storage_[i].amount, p->storage_[i].equip, + p->storage_[i].identify, p->storage_[i].refine, + p->storage_[i].attribute, + p->storage_[i].card[0], p->storage_[i].card[1], + p->storage_[i].card[2], p->storage_[i].card[3]); + f++; + } + + *(str_p++) = '\t'; + + *str_p = '\0'; + if (!f) + str[0] = 0; + return 0; +} + +int guild_storage_fromstr (char *str, struct guild_storage *p) +{ + int tmp_int[256]; + int set, next, len, i; + + set = sscanf (str, "%d,%d%n", &tmp_int[0], &tmp_int[1], &next); + p->storage_amount = tmp_int[1]; + + if (set != 2) + return 1; + if (str[next] == '\n' || str[next] == '\r') + return 0; + next++; + for (i = 0; str[next] && str[next] != '\t' && i < MAX_GUILD_STORAGE; i++) + { + if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &tmp_int[10], &len) == 12) + { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + p->storage_[i].card[0] = tmp_int[7]; + p->storage_[i].card[1] = tmp_int[8]; + p->storage_[i].card[2] = tmp_int[9]; + p->storage_[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + + else if (sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len) == 11) + { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + p->storage_[i].card[0] = tmp_int[7]; + p->storage_[i].card[1] = tmp_int[8]; + p->storage_[i].card[2] = tmp_int[9]; + p->storage_[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + + else + return 1; + } + if (i >= MAX_GUILD_STORAGE && str[next] && str[next] != '\t') + printf + ("guild_storage_fromstr: Found a storage line with more items than MAX_GUILD_STORAGE (%d), remaining items have been discarded!\n", + MAX_GUILD_STORAGE); + return 0; +} + +// アカウントから倉庫データインデックスを得る(新規倉庫追加可能) +struct storage *account2storage (int account_id) +{ + struct storage *s; + s = (struct storage *) numdb_search (storage_db, account_id); + if (s == NULL) + { + CREATE (s, struct storage, 1); + memset (s, 0, sizeof (struct storage)); + s->account_id = account_id; + numdb_insert (storage_db, s->account_id, s); + } + return s; +} + +struct guild_storage *guild2storage (int guild_id) +{ + struct guild_storage *gs = NULL; + if (inter_guild_search (guild_id) != NULL) + { + gs = (struct guild_storage *) numdb_search (guild_storage_db, + guild_id); + if (gs == NULL) + { + CREATE (gs, struct guild_storage, 1); + gs->guild_id = guild_id; + numdb_insert (guild_storage_db, gs->guild_id, gs); + } + } + return gs; +} + +//--------------------------------------------------------- +// 倉庫データを読み込む +int inter_storage_init (void) +{ + char line[65536]; + int c = 0, tmp_int; + struct storage *s; + struct guild_storage *gs; + FILE *fp; + + storage_db = numdb_init (); + + fp = fopen_ (storage_txt, "r"); + if (fp == NULL) + { + printf ("cant't read : %s\n", storage_txt); + return 1; + } + while (fgets (line, 65535, fp)) + { + sscanf (line, "%d", &tmp_int); + CREATE (s, struct storage, 1); + s->account_id = tmp_int; + if (s->account_id > 0 && storage_fromstr (line, s) == 0) + { + numdb_insert (storage_db, s->account_id, s); + } + else + { + printf ("int_storage: broken data [%s] line %d\n", storage_txt, + c); + free (s); + } + c++; + } + fclose_ (fp); + + c = 0; + guild_storage_db = numdb_init (); + + fp = fopen_ (guild_storage_txt, "r"); + if (fp == NULL) + { + printf ("cant't read : %s\n", guild_storage_txt); + return 1; + } + while (fgets (line, 65535, fp)) + { + sscanf (line, "%d", &tmp_int); + CREATE (gs, struct guild_storage, 1); + gs->guild_id = tmp_int; + if (gs->guild_id > 0 && guild_storage_fromstr (line, gs) == 0) + { + numdb_insert (guild_storage_db, gs->guild_id, gs); + } + else + { + printf ("int_storage: broken data [%s] line %d\n", + guild_storage_txt, c); + free (gs); + } + c++; + } + fclose_ (fp); + + return 0; +} + +void storage_db_final (db_key_t k, db_val_t data, va_list ap) +{ + struct storage *p = (struct storage *) data; + if (p) + free (p); +} + +void guild_storage_db_final (db_key_t k, db_val_t data, va_list ap) +{ + struct guild_storage *p = (struct guild_storage *) data; + if (p) + free (p); +} + +void inter_storage_final (void) +{ + numdb_final (storage_db, storage_db_final); + numdb_final (guild_storage_db, guild_storage_db_final); + return; +} + +void inter_storage_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[65536]; + FILE *fp; + storage_tostr (line, (struct storage *) data); + fp = va_arg (ap, FILE *); + if (*line) + fprintf (fp, "%s\n", line); +} + +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_storage_save (void) +{ + FILE *fp; + int lock; + + if (!storage_db) + return 1; + + if ((fp = lock_fopen (storage_txt, &lock)) == NULL) + { + printf ("int_storage: cant write [%s] !!! data is lost !!!\n", + storage_txt); + return 1; + } + numdb_foreach (storage_db, inter_storage_save_sub, fp); + lock_fclose (fp, storage_txt, &lock); +// printf("int_storage: %s saved.\n",storage_txt); + return 0; +} + +void inter_guild_storage_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[65536]; + FILE *fp; + + if (inter_guild_search (((struct guild_storage *) data)->guild_id) != + NULL) + { + guild_storage_tostr (line, (struct guild_storage *) data); + fp = va_arg (ap, FILE *); + if (*line) + fprintf (fp, "%s\n", line); + } +} + +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_guild_storage_save (void) +{ + FILE *fp; + int lock; + + if (!guild_storage_db) + return 1; + + if ((fp = lock_fopen (guild_storage_txt, &lock)) == NULL) + { + printf ("int_storage: cant write [%s] !!! data is lost !!!\n", + guild_storage_txt); + return 1; + } + numdb_foreach (guild_storage_db, inter_guild_storage_save_sub, fp); + lock_fclose (fp, guild_storage_txt, &lock); +// printf("int_storage: %s saved.\n",guild_storage_txt); + return 0; +} + +// 倉庫データ削除 +int inter_storage_delete (int account_id) +{ + struct storage *s = + (struct storage *) numdb_search (storage_db, account_id); + if (s) + { + numdb_erase (storage_db, account_id); + free (s); + } + return 0; +} + +// ギルド倉庫データ削除 +int inter_guild_storage_delete (int guild_id) +{ + struct guild_storage *gs = + (struct guild_storage *) numdb_search (guild_storage_db, guild_id); + if (gs) + { + numdb_erase (guild_storage_db, guild_id); + free (gs); + } + return 0; +} + +//--------------------------------------------------------- +// map serverへの通信 + +// 倉庫データの送信 +int mapif_load_storage (int fd, int account_id) +{ + struct storage *s = account2storage (account_id); + WFIFOW (fd, 0) = 0x3810; + WFIFOW (fd, 2) = sizeof (struct storage) + 8; + WFIFOL (fd, 4) = account_id; + memcpy (WFIFOP (fd, 8), s, sizeof (struct storage)); + WFIFOSET (fd, WFIFOW (fd, 2)); + return 0; +} + +// 倉庫データ保存完了送信 +int mapif_save_storage_ack (int fd, int account_id) +{ + WFIFOW (fd, 0) = 0x3811; + WFIFOL (fd, 2) = account_id; + WFIFOB (fd, 6) = 0; + WFIFOSET (fd, 7); + return 0; +} + +int mapif_load_guild_storage (int fd, int account_id, int guild_id) +{ + struct guild_storage *gs = guild2storage (guild_id); + WFIFOW (fd, 0) = 0x3818; + if (gs) + { + WFIFOW (fd, 2) = sizeof (struct guild_storage) + 12; + WFIFOL (fd, 4) = account_id; + WFIFOL (fd, 8) = guild_id; + memcpy (WFIFOP (fd, 12), gs, sizeof (struct guild_storage)); + } + else + { + WFIFOW (fd, 2) = 12; + WFIFOL (fd, 4) = account_id; + WFIFOL (fd, 8) = 0; + } + WFIFOSET (fd, WFIFOW (fd, 2)); + + return 0; +} + +int mapif_save_guild_storage_ack (int fd, int account_id, int guild_id, + int fail) +{ + WFIFOW (fd, 0) = 0x3819; + WFIFOL (fd, 2) = account_id; + WFIFOL (fd, 6) = guild_id; + WFIFOB (fd, 10) = fail; + WFIFOSET (fd, 11); + return 0; +} + +//--------------------------------------------------------- +// map serverからの通信 + +// 倉庫データ要求受信 +int mapif_parse_LoadStorage (int fd) +{ + mapif_load_storage (fd, RFIFOL (fd, 2)); + return 0; +} + +// 倉庫データ受信&保存 +int mapif_parse_SaveStorage (int fd) +{ + struct storage *s; + int account_id = RFIFOL (fd, 4); + int len = RFIFOW (fd, 2); + if (sizeof (struct storage) != len - 8) + { + printf ("inter storage: data size error %d %d\n", + sizeof (struct storage), len - 8); + } + else + { + s = account2storage (account_id); + memcpy (s, RFIFOP (fd, 8), sizeof (struct storage)); + mapif_save_storage_ack (fd, account_id); + } + return 0; +} + +int mapif_parse_LoadGuildStorage (int fd) +{ + mapif_load_guild_storage (fd, RFIFOL (fd, 2), RFIFOL (fd, 6)); + return 0; +} + +int mapif_parse_SaveGuildStorage (int fd) +{ + struct guild_storage *gs; + int guild_id = RFIFOL (fd, 8); + int len = RFIFOW (fd, 2); + if (sizeof (struct guild_storage) != len - 12) + { + printf ("inter storage: data size error %d %d\n", + sizeof (struct guild_storage), len - 12); + } + else + { + gs = guild2storage (guild_id); + if (gs) + { + memcpy (gs, RFIFOP (fd, 12), sizeof (struct guild_storage)); + mapif_save_guild_storage_ack (fd, RFIFOL (fd, 4), guild_id, 0); + } + else + mapif_save_guild_storage_ack (fd, RFIFOL (fd, 4), guild_id, 1); + } + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_storage_parse_frommap (int fd) +{ + switch (RFIFOW (fd, 0)) + { + case 0x3010: + mapif_parse_LoadStorage (fd); + break; + case 0x3011: + mapif_parse_SaveStorage (fd); + break; + case 0x3018: + mapif_parse_LoadGuildStorage (fd); + break; + case 0x3019: + mapif_parse_SaveGuildStorage (fd); + break; + default: + return 0; + } + return 1; +} diff --git a/src/char/int_storage.h b/src/char/int_storage.h deleted file mode 100644 index f1859c6..0000000 --- a/src/char/int_storage.h +++ /dev/null @@ -1,18 +0,0 @@ -// $Id: int_storage.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#ifndef _INT_STORAGE_H_ -#define _INT_STORAGE_H_ - -int inter_storage_init (void); -void inter_storage_final (void); -int inter_storage_save (void); -int inter_guild_storage_save (void); -int inter_storage_delete (int account_id); -int inter_guild_storage_delete (int guild_id); -struct storage *account2storage (int account_id); - -int inter_storage_parse_frommap (int fd); - -extern char storage_txt[1024]; -extern char guild_storage_txt[1024]; - -#endif diff --git a/src/char/int_storage.hpp b/src/char/int_storage.hpp new file mode 100644 index 0000000..9986f2d --- /dev/null +++ b/src/char/int_storage.hpp @@ -0,0 +1,18 @@ +// $Id: int_storage.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef INT_STORAGE_HPP +#define INT_STORAGE_HPP + +int inter_storage_init (void); +void inter_storage_final (void); +int inter_storage_save (void); +int inter_guild_storage_save (void); +int inter_storage_delete (int account_id); +int inter_guild_storage_delete (int guild_id); +struct storage *account2storage (int account_id); + +int inter_storage_parse_frommap (int fd); + +extern char storage_txt[1024]; +extern char guild_storage_txt[1024]; + +#endif diff --git a/src/char/inter.c b/src/char/inter.c deleted file mode 100644 index e886bf6..0000000 --- a/src/char/inter.c +++ /dev/null @@ -1,635 +0,0 @@ -// $Id: inter.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#include "../common/mmo.h" -#include "char.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/db.h" -#include <string.h> -#include <stdlib.h> - -#include "inter.h" -#include "int_party.h" -#include "int_guild.h" -#include "int_storage.h" -#include "../common/lock.h" - -#define WISDATA_TTL (60*1000) // Existence time of Wisp/page data (60 seconds) - // that is the waiting time of answers of all map-servers -#define WISDELLIST_MAX 256 // Number of elements of Wisp/page data deletion list - -char inter_log_filename[1024] = "log/inter.log"; - -char accreg_txt[1024] = "save/accreg.txt"; -static struct dbt *accreg_db = NULL; - -struct accreg -{ - int account_id, reg_num; - struct global_reg reg[ACCOUNT_REG_NUM]; -}; - -int party_share_level = 10; - -// 送信パケット長リスト -int inter_send_packet_length[] = { - -1, -1, 27, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -1, 7, 0, 0, 0, 0, 0, 0, -1, 11, 0, 0, 0, 0, 0, 0, - 35, -1, 11, 15, 34, 29, 7, -1, 0, 0, 0, 0, 0, 0, 0, 0, - 10, -1, 15, 0, 79, 19, 7, -1, 0, -1, -1, -1, 14, 67, 186, -1, - 9, 9, -1, 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, - 11, -1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// 受信パケット長リスト -int inter_recv_packet_length[] = { - -1, -1, 7, -1, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, -1, 0, 0, 0, 0, 0, 0, 10, -1, 0, 0, 0, 0, 0, 0, - 72, 6, 52, 14, 10, 29, 6, -1, 34, 0, 0, 0, 0, 0, 0, 0, - -1, 6, -1, 0, 55, 19, 6, -1, 14, -1, -1, -1, 14, 19, 186, -1, - 5, 9, 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, - 48, 14, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -struct WisData -{ - int id, fd, count, len; - unsigned long tick; - unsigned char src[24], dst[24], msg[1024]; -}; -static struct dbt *wis_db = NULL; -static int wis_dellist[WISDELLIST_MAX], wis_delnum; - -//-------------------------------------------------------- - -// アカウント変数を文字列へ変換 -int inter_accreg_tostr (char *str, struct accreg *reg) -{ - int j; - char *p = str; - - p += sprintf (p, "%d\t", reg->account_id); - for (j = 0; j < reg->reg_num; j++) - { - p += sprintf (p, "%s,%d ", reg->reg[j].str, reg->reg[j].value); - } - - return 0; -} - -// アカウント変数を文字列から変換 -int inter_accreg_fromstr (const char *str, struct accreg *reg) -{ - int j, v, n; - char buf[128]; - const char *p = str; - - if (sscanf (p, "%d\t%n", ®->account_id, &n) != 1 - || reg->account_id <= 0) - return 1; - - for (j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) - { - if (sscanf (p, "%[^,],%d %n", buf, &v, &n) != 2) - break; - memcpy (reg->reg[j].str, buf, 32); - reg->reg[j].value = v; - } - reg->reg_num = j; - - return 0; -} - -// アカウント変数の読み込み -int inter_accreg_init (void) -{ - char line[8192]; - FILE *fp; - int c = 0; - struct accreg *reg; - - accreg_db = numdb_init (); - - if ((fp = fopen_ (accreg_txt, "r")) == NULL) - return 1; - while (fgets (line, sizeof (line) - 1, fp)) - { - line[sizeof (line) - 1] = '\0'; - CREATE (reg, struct accreg, 1); - if (inter_accreg_fromstr (line, reg) == 0 && reg->account_id > 0) - { - numdb_insert (accreg_db, reg->account_id, reg); - } - else - { - printf ("inter: accreg: broken data [%s] line %d\n", accreg_txt, - c); - free (reg); - } - c++; - } - fclose_ (fp); -// printf("inter: %s read done (%d)\n", accreg_txt, c); - - return 0; -} - -// アカウント変数のセーブ用 -void inter_accreg_save_sub (db_key_t key, db_val_t data, va_list ap) -{ - char line[8192]; - FILE *fp; - struct accreg *reg = (struct accreg *) data; - - if (reg->reg_num > 0) - { - inter_accreg_tostr (line, reg); - fp = va_arg (ap, FILE *); - fprintf (fp, "%s\n", line); - } -} - -// アカウント変数のセーブ -int inter_accreg_save (void) -{ - FILE *fp; - int lock; - - if ((fp = lock_fopen (accreg_txt, &lock)) == NULL) - { - printf ("int_accreg: cant write [%s] !!! data is lost !!!\n", - accreg_txt); - return 1; - } - numdb_foreach (accreg_db, inter_accreg_save_sub, fp); - lock_fclose (fp, accreg_txt, &lock); -// printf("inter: %s saved.\n", accreg_txt); - - return 0; -} - -//-------------------------------------------------------- - -/*========================================== - * 設定ファイルを読み込む - *------------------------------------------ - */ -int inter_config_read (const char *cfgName) -{ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen_ (cfgName, "r"); - if (fp == NULL) - { - printf ("file not found: %s\n", cfgName); - return 1; - } - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof (line) - 1] = '\0'; - - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - if (strcasecmp (w1, "storage_txt") == 0) - { - strncpy (storage_txt, w2, sizeof (storage_txt)); - } - else if (strcasecmp (w1, "party_txt") == 0) - { - strncpy (party_txt, w2, sizeof (party_txt)); - } - else if (strcasecmp (w1, "guild_txt") == 0) - { - strncpy (guild_txt, w2, sizeof (guild_txt)); - } - else if (strcasecmp (w1, "castle_txt") == 0) - { - strncpy (castle_txt, w2, sizeof (castle_txt)); - } - else if (strcasecmp (w1, "accreg_txt") == 0) - { - strncpy (accreg_txt, w2, sizeof (accreg_txt)); - } - else if (strcasecmp (w1, "guild_storage_txt") == 0) - { - strncpy (guild_storage_txt, w2, sizeof (guild_storage_txt)); - } - else if (strcasecmp (w1, "party_share_level") == 0) - { - party_share_level = atoi (w2); - if (party_share_level < 0) - party_share_level = 0; - } - else if (strcasecmp (w1, "inter_log_filename") == 0) - { - strncpy (inter_log_filename, w2, sizeof (inter_log_filename)); - } - else if (strcasecmp (w1, "import") == 0) - { - inter_config_read (w2); - } - } - fclose_ (fp); - - return 0; -} - -// ログ書き出し -int inter_log (char *fmt, ...) -{ - FILE *logfp; - va_list ap; - - va_start (ap, fmt); - logfp = fopen_ (inter_log_filename, "a"); - if (logfp) - { - vfprintf (logfp, fmt, ap); - fclose_ (logfp); - } - va_end (ap); - - return 0; -} - -// セーブ -int inter_save (void) -{ - inter_party_save (); - inter_guild_save (); - inter_storage_save (); - inter_guild_storage_save (); - inter_accreg_save (); - - return 0; -} - -// 初期化 -int inter_init (const char *file) -{ - inter_config_read (file); - - wis_db = numdb_init (); - - inter_party_init (); - inter_guild_init (); - inter_storage_init (); - inter_accreg_init (); - - return 0; -} - -// マップサーバー接続 -int inter_mapif_init (int fd) -{ - inter_guild_mapif_init (fd); - - return 0; -} - -//-------------------------------------------------------- -// sended packets to map-server - -// GMメッセージ送信 -int mapif_GMmessage (unsigned char *mes, int len) -{ - unsigned char buf[len]; - - WBUFW (buf, 0) = 0x3800; - WBUFW (buf, 2) = len; - memcpy (WBUFP (buf, 4), mes, len - 4); - mapif_sendall (buf, len); -// printf("inter server: GM:%d %s\n", len, mes); - - return 0; -} - -// Wisp/page transmission to all map-server -int mapif_wis_message (struct WisData *wd) -{ - unsigned char buf[56 + wd->len]; - - WBUFW (buf, 0) = 0x3801; - WBUFW (buf, 2) = 56 + wd->len; - WBUFL (buf, 4) = wd->id; - memcpy (WBUFP (buf, 8), wd->src, 24); - memcpy (WBUFP (buf, 32), wd->dst, 24); - memcpy (WBUFP (buf, 56), wd->msg, wd->len); - wd->count = mapif_sendall (buf, WBUFW (buf, 2)); - - return 0; -} - -// Wisp/page transmission result to map-server -int mapif_wis_end (struct WisData *wd, int flag) -{ - unsigned char buf[27]; - - WBUFW (buf, 0) = 0x3802; - memcpy (WBUFP (buf, 2), wd->src, 24); - WBUFB (buf, 26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send (wd->fd, buf, 27); -// printf("inter server wis_end: flag: %d\n", flag); - - return 0; -} - -// アカウント変数送信 -int mapif_account_reg (int fd, unsigned char *src) -{ - unsigned char buf[WBUFW (src, 2)]; - - memcpy (WBUFP (buf, 0), src, WBUFW (src, 2)); - WBUFW (buf, 0) = 0x3804; - mapif_sendallwos (fd, buf, WBUFW (buf, 2)); - - return 0; -} - -// アカウント変数要求返信 -int mapif_account_reg_reply (int fd, int account_id) -{ - struct accreg *reg = (struct accreg *)numdb_search (accreg_db, account_id); - - WFIFOW (fd, 0) = 0x3804; - WFIFOL (fd, 4) = account_id; - if (reg == NULL) - { - WFIFOW (fd, 2) = 8; - } - else - { - int j, p; - for (j = 0, p = 8; j < reg->reg_num; j++, p += 36) - { - memcpy (WFIFOP (fd, p), reg->reg[j].str, 32); - WFIFOL (fd, p + 32) = reg->reg[j].value; - } - WFIFOW (fd, 2) = p; - } - WFIFOSET (fd, WFIFOW (fd, 2)); - - return 0; -} - -//-------------------------------------------------------- - -// Existence check of WISP data -void check_ttl_wisdata_sub (db_key_t key, db_val_t data, va_list ap) -{ - unsigned long tick; - struct WisData *wd = (struct WisData *) data; - tick = va_arg (ap, unsigned long); - - if (DIFF_TICK (tick, wd->tick) > WISDATA_TTL - && wis_delnum < WISDELLIST_MAX) - wis_dellist[wis_delnum++] = wd->id; -} - -int check_ttl_wisdata (void) -{ - unsigned long tick = gettick (); - int i; - - do - { - wis_delnum = 0; - numdb_foreach (wis_db, check_ttl_wisdata_sub, tick); - for (i = 0; i < wis_delnum; i++) - { - struct WisData *wd = (struct WisData *)numdb_search (wis_db, wis_dellist[i]); - printf ("inter: wis data id=%d time out : from %s to %s\n", - wd->id, wd->src, wd->dst); - // removed. not send information after a timeout. Just no answer for the player - //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - numdb_erase (wis_db, wd->id); - free (wd); - } - } - while (wis_delnum >= WISDELLIST_MAX); - - return 0; -} - -//-------------------------------------------------------- -// received packets from map-server - -// GMメッセージ送信 -int mapif_parse_GMmessage (int fd) -{ - mapif_GMmessage (RFIFOP (fd, 4), RFIFOW (fd, 2)); - - return 0; -} - -// Wisp/page request to send -int mapif_parse_WisRequest (int fd) -{ - struct WisData *wd; - static int wisid = 0; - int index; - - if (RFIFOW (fd, 2) - 52 >= sizeof (wd->msg)) - { - printf ("inter: Wis message size too long.\n"); - return 0; - } - else if (RFIFOW (fd, 2) - 52 <= 0) - { // normaly, impossible, but who knows... - printf ("inter: Wis message doesn't exist.\n"); - return 0; - } - - // search if character exists before to ask all map-servers - if ((index = search_character_index (RFIFOP (fd, 28))) == -1) - { - unsigned char buf[27]; - WBUFW (buf, 0) = 0x3802; - memcpy (WBUFP (buf, 2), RFIFOP (fd, 4), 24); - WBUFB (buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send (fd, buf, 27); - // Character exists. So, ask all map-servers - } - else - { - // to be sure of the correct name, rewrite it - memset (RFIFOP (fd, 28), 0, 24); - strncpy (RFIFOP (fd, 28), search_character_name (index), 24); - // if source is destination, don't ask other servers. - if (strcmp (RFIFOP (fd, 4), RFIFOP (fd, 28)) == 0) - { - unsigned char buf[27]; - WBUFW (buf, 0) = 0x3802; - memcpy (WBUFP (buf, 2), RFIFOP (fd, 4), 24); - WBUFB (buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send (fd, buf, 27); - } - else - { - CREATE (wd, struct WisData, 1); - - // Whether the failure of previous wisp/page transmission (timeout) - check_ttl_wisdata (); - - wd->id = ++wisid; - wd->fd = fd; - wd->len = RFIFOW (fd, 2) - 52; - memcpy (wd->src, RFIFOP (fd, 4), 24); - memcpy (wd->dst, RFIFOP (fd, 28), 24); - memcpy (wd->msg, RFIFOP (fd, 52), wd->len); - wd->tick = gettick (); - numdb_insert (wis_db, wd->id, wd); - mapif_wis_message (wd); - } - } - - return 0; -} - -// Wisp/page transmission result -int mapif_parse_WisReply (int fd) -{ - int id = RFIFOL (fd, 2), flag = RFIFOB (fd, 6); - struct WisData *wd = (struct WisData *)numdb_search (wis_db, id); - - if (wd == NULL) - return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server - - if ((--wd->count) <= 0 || flag != 1) - { - mapif_wis_end (wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - numdb_erase (wis_db, id); - free (wd); - } - - return 0; -} - -// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) -int mapif_parse_WisToGM (int fd) -{ - unsigned char buf[RFIFOW (fd, 2)]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B - - memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), RFIFOW (fd, 2)); - WBUFW (buf, 0) = 0x3803; - mapif_sendall (buf, RFIFOW (fd, 2)); - - return 0; -} - -// アカウント変数保存要求 -int mapif_parse_AccReg (int fd) -{ - int j, p; - struct accreg *reg = (struct accreg*)numdb_search (accreg_db, (numdb_key_t)RFIFOL (fd, 4)); - - if (reg == NULL) - { - CREATE (reg, struct accreg, 1); - reg->account_id = RFIFOL (fd, 4); - numdb_insert (accreg_db, (numdb_key_t)RFIFOL (fd, 4), reg); - } - - for (j = 0, p = 8; j < ACCOUNT_REG_NUM && p < RFIFOW (fd, 2); - j++, p += 36) - { - memcpy (reg->reg[j].str, RFIFOP (fd, p), 32); - reg->reg[j].value = RFIFOL (fd, p + 32); - } - reg->reg_num = j; - - mapif_account_reg (fd, RFIFOP (fd, 0)); // 他のMAPサーバーに送信 - - return 0; -} - -// アカウント変数送信要求 -int mapif_parse_AccRegRequest (int fd) -{ -// printf("mapif: accreg request\n"); - return mapif_account_reg_reply (fd, RFIFOL (fd, 2)); -} - -//-------------------------------------------------------- - -// map server からの通信(1パケットのみ解析すること) -// エラーなら0(false)、処理できたなら1、 -// パケット長が足りなければ2をかえさなければならない -int inter_parse_frommap (int fd) -{ - int cmd = RFIFOW (fd, 0); - int len = 0; - - // inter鯖管轄かを調べる - if (cmd < 0x3000 - || cmd >= - 0x3000 + - (sizeof (inter_recv_packet_length) / - sizeof (inter_recv_packet_length[0]))) - return 0; - - // パケット長を調べる - if ((len = - inter_check_length (fd, - inter_recv_packet_length[cmd - 0x3000])) == 0) - return 2; - - switch (cmd) - { - case 0x3000: - mapif_parse_GMmessage (fd); - break; - case 0x3001: - mapif_parse_WisRequest (fd); - break; - case 0x3002: - mapif_parse_WisReply (fd); - break; - case 0x3003: - mapif_parse_WisToGM (fd); - break; - case 0x3004: - mapif_parse_AccReg (fd); - break; - case 0x3005: - mapif_parse_AccRegRequest (fd); - break; - default: - if (inter_party_parse_frommap (fd)) - break; - if (inter_guild_parse_frommap (fd)) - break; - if (inter_storage_parse_frommap (fd)) - break; - return 0; - } - RFIFOSKIP (fd, len); - - return 1; -} - -// RFIFOのパケット長確認 -// 必要パケット長があればパケット長、まだ足りなければ0 -int inter_check_length (int fd, int length) -{ - if (length == -1) - { // 可変パケット長 - if (RFIFOREST (fd) < 4) // パケット長が未着 - return 0; - length = RFIFOW (fd, 2); - } - - if (RFIFOREST (fd) < length) // パケットが未着 - return 0; - - return length; -} diff --git a/src/char/inter.cpp b/src/char/inter.cpp new file mode 100644 index 0000000..af95a2d --- /dev/null +++ b/src/char/inter.cpp @@ -0,0 +1,635 @@ +// $Id: inter.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "../common/mmo.hpp" +#include "char.hpp" +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/db.hpp" +#include <string.h> +#include <stdlib.h> + +#include "inter.hpp" +#include "int_party.hpp" +#include "int_guild.hpp" +#include "int_storage.hpp" +#include "../common/lock.hpp" + +#define WISDATA_TTL (60*1000) // Existence time of Wisp/page data (60 seconds) + // that is the waiting time of answers of all map-servers +#define WISDELLIST_MAX 256 // Number of elements of Wisp/page data deletion list + +char inter_log_filename[1024] = "log/inter.log"; + +char accreg_txt[1024] = "save/accreg.txt"; +static struct dbt *accreg_db = NULL; + +struct accreg +{ + int account_id, reg_num; + struct global_reg reg[ACCOUNT_REG_NUM]; +}; + +int party_share_level = 10; + +// 送信パケット長リスト +int inter_send_packet_length[] = { + -1, -1, 27, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 7, 0, 0, 0, 0, 0, 0, -1, 11, 0, 0, 0, 0, 0, 0, + 35, -1, 11, 15, 34, 29, 7, -1, 0, 0, 0, 0, 0, 0, 0, 0, + 10, -1, 15, 0, 79, 19, 7, -1, 0, -1, -1, -1, 14, 67, 186, -1, + 9, 9, -1, 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, + 11, -1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// 受信パケット長リスト +int inter_recv_packet_length[] = { + -1, -1, 7, -1, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, -1, 0, 0, 0, 0, 0, 0, 10, -1, 0, 0, 0, 0, 0, 0, + 72, 6, 52, 14, 10, 29, 6, -1, 34, 0, 0, 0, 0, 0, 0, 0, + -1, 6, -1, 0, 55, 19, 6, -1, 14, -1, -1, -1, 14, 19, 186, -1, + 5, 9, 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, + 48, 14, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +struct WisData +{ + int id, fd, count, len; + unsigned long tick; + unsigned char src[24], dst[24], msg[1024]; +}; +static struct dbt *wis_db = NULL; +static int wis_dellist[WISDELLIST_MAX], wis_delnum; + +//-------------------------------------------------------- + +// アカウント変数を文字列へ変換 +int inter_accreg_tostr (char *str, struct accreg *reg) +{ + int j; + char *p = str; + + p += sprintf (p, "%d\t", reg->account_id); + for (j = 0; j < reg->reg_num; j++) + { + p += sprintf (p, "%s,%d ", reg->reg[j].str, reg->reg[j].value); + } + + return 0; +} + +// アカウント変数を文字列から変換 +int inter_accreg_fromstr (const char *str, struct accreg *reg) +{ + int j, v, n; + char buf[128]; + const char *p = str; + + if (sscanf (p, "%d\t%n", ®->account_id, &n) != 1 + || reg->account_id <= 0) + return 1; + + for (j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) + { + if (sscanf (p, "%[^,],%d %n", buf, &v, &n) != 2) + break; + memcpy (reg->reg[j].str, buf, 32); + reg->reg[j].value = v; + } + reg->reg_num = j; + + return 0; +} + +// アカウント変数の読み込み +int inter_accreg_init (void) +{ + char line[8192]; + FILE *fp; + int c = 0; + struct accreg *reg; + + accreg_db = numdb_init (); + + if ((fp = fopen_ (accreg_txt, "r")) == NULL) + return 1; + while (fgets (line, sizeof (line) - 1, fp)) + { + line[sizeof (line) - 1] = '\0'; + CREATE (reg, struct accreg, 1); + if (inter_accreg_fromstr (line, reg) == 0 && reg->account_id > 0) + { + numdb_insert (accreg_db, reg->account_id, reg); + } + else + { + printf ("inter: accreg: broken data [%s] line %d\n", accreg_txt, + c); + free (reg); + } + c++; + } + fclose_ (fp); +// printf("inter: %s read done (%d)\n", accreg_txt, c); + + return 0; +} + +// アカウント変数のセーブ用 +void inter_accreg_save_sub (db_key_t key, db_val_t data, va_list ap) +{ + char line[8192]; + FILE *fp; + struct accreg *reg = (struct accreg *) data; + + if (reg->reg_num > 0) + { + inter_accreg_tostr (line, reg); + fp = va_arg (ap, FILE *); + fprintf (fp, "%s\n", line); + } +} + +// アカウント変数のセーブ +int inter_accreg_save (void) +{ + FILE *fp; + int lock; + + if ((fp = lock_fopen (accreg_txt, &lock)) == NULL) + { + printf ("int_accreg: cant write [%s] !!! data is lost !!!\n", + accreg_txt); + return 1; + } + numdb_foreach (accreg_db, inter_accreg_save_sub, fp); + lock_fclose (fp, accreg_txt, &lock); +// printf("inter: %s saved.\n", accreg_txt); + + return 0; +} + +//-------------------------------------------------------- + +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +int inter_config_read (const char *cfgName) +{ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen_ (cfgName, "r"); + if (fp == NULL) + { + printf ("file not found: %s\n", cfgName); + return 1; + } + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof (line) - 1] = '\0'; + + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + if (strcasecmp (w1, "storage_txt") == 0) + { + strncpy (storage_txt, w2, sizeof (storage_txt)); + } + else if (strcasecmp (w1, "party_txt") == 0) + { + strncpy (party_txt, w2, sizeof (party_txt)); + } + else if (strcasecmp (w1, "guild_txt") == 0) + { + strncpy (guild_txt, w2, sizeof (guild_txt)); + } + else if (strcasecmp (w1, "castle_txt") == 0) + { + strncpy (castle_txt, w2, sizeof (castle_txt)); + } + else if (strcasecmp (w1, "accreg_txt") == 0) + { + strncpy (accreg_txt, w2, sizeof (accreg_txt)); + } + else if (strcasecmp (w1, "guild_storage_txt") == 0) + { + strncpy (guild_storage_txt, w2, sizeof (guild_storage_txt)); + } + else if (strcasecmp (w1, "party_share_level") == 0) + { + party_share_level = atoi (w2); + if (party_share_level < 0) + party_share_level = 0; + } + else if (strcasecmp (w1, "inter_log_filename") == 0) + { + strncpy (inter_log_filename, w2, sizeof (inter_log_filename)); + } + else if (strcasecmp (w1, "import") == 0) + { + inter_config_read (w2); + } + } + fclose_ (fp); + + return 0; +} + +// ログ書き出し +int inter_log (char *fmt, ...) +{ + FILE *logfp; + va_list ap; + + va_start (ap, fmt); + logfp = fopen_ (inter_log_filename, "a"); + if (logfp) + { + vfprintf (logfp, fmt, ap); + fclose_ (logfp); + } + va_end (ap); + + return 0; +} + +// セーブ +int inter_save (void) +{ + inter_party_save (); + inter_guild_save (); + inter_storage_save (); + inter_guild_storage_save (); + inter_accreg_save (); + + return 0; +} + +// 初期化 +int inter_init (const char *file) +{ + inter_config_read (file); + + wis_db = numdb_init (); + + inter_party_init (); + inter_guild_init (); + inter_storage_init (); + inter_accreg_init (); + + return 0; +} + +// マップサーバー接続 +int inter_mapif_init (int fd) +{ + inter_guild_mapif_init (fd); + + return 0; +} + +//-------------------------------------------------------- +// sended packets to map-server + +// GMメッセージ送信 +int mapif_GMmessage (unsigned char *mes, int len) +{ + unsigned char buf[len]; + + WBUFW (buf, 0) = 0x3800; + WBUFW (buf, 2) = len; + memcpy (WBUFP (buf, 4), mes, len - 4); + mapif_sendall (buf, len); +// printf("inter server: GM:%d %s\n", len, mes); + + return 0; +} + +// Wisp/page transmission to all map-server +int mapif_wis_message (struct WisData *wd) +{ + unsigned char buf[56 + wd->len]; + + WBUFW (buf, 0) = 0x3801; + WBUFW (buf, 2) = 56 + wd->len; + WBUFL (buf, 4) = wd->id; + memcpy (WBUFP (buf, 8), wd->src, 24); + memcpy (WBUFP (buf, 32), wd->dst, 24); + memcpy (WBUFP (buf, 56), wd->msg, wd->len); + wd->count = mapif_sendall (buf, WBUFW (buf, 2)); + + return 0; +} + +// Wisp/page transmission result to map-server +int mapif_wis_end (struct WisData *wd, int flag) +{ + unsigned char buf[27]; + + WBUFW (buf, 0) = 0x3802; + memcpy (WBUFP (buf, 2), wd->src, 24); + WBUFB (buf, 26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send (wd->fd, buf, 27); +// printf("inter server wis_end: flag: %d\n", flag); + + return 0; +} + +// アカウント変数送信 +int mapif_account_reg (int fd, unsigned char *src) +{ + unsigned char buf[WBUFW (src, 2)]; + + memcpy (WBUFP (buf, 0), src, WBUFW (src, 2)); + WBUFW (buf, 0) = 0x3804; + mapif_sendallwos (fd, buf, WBUFW (buf, 2)); + + return 0; +} + +// アカウント変数要求返信 +int mapif_account_reg_reply (int fd, int account_id) +{ + struct accreg *reg = (struct accreg *)numdb_search (accreg_db, account_id); + + WFIFOW (fd, 0) = 0x3804; + WFIFOL (fd, 4) = account_id; + if (reg == NULL) + { + WFIFOW (fd, 2) = 8; + } + else + { + int j, p; + for (j = 0, p = 8; j < reg->reg_num; j++, p += 36) + { + memcpy (WFIFOP (fd, p), reg->reg[j].str, 32); + WFIFOL (fd, p + 32) = reg->reg[j].value; + } + WFIFOW (fd, 2) = p; + } + WFIFOSET (fd, WFIFOW (fd, 2)); + + return 0; +} + +//-------------------------------------------------------- + +// Existence check of WISP data +void check_ttl_wisdata_sub (db_key_t key, db_val_t data, va_list ap) +{ + unsigned long tick; + struct WisData *wd = (struct WisData *) data; + tick = va_arg (ap, unsigned long); + + if (DIFF_TICK (tick, wd->tick) > WISDATA_TTL + && wis_delnum < WISDELLIST_MAX) + wis_dellist[wis_delnum++] = wd->id; +} + +int check_ttl_wisdata (void) +{ + unsigned long tick = gettick (); + int i; + + do + { + wis_delnum = 0; + numdb_foreach (wis_db, check_ttl_wisdata_sub, tick); + for (i = 0; i < wis_delnum; i++) + { + struct WisData *wd = (struct WisData *)numdb_search (wis_db, wis_dellist[i]); + printf ("inter: wis data id=%d time out : from %s to %s\n", + wd->id, wd->src, wd->dst); + // removed. not send information after a timeout. Just no answer for the player + //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase (wis_db, wd->id); + free (wd); + } + } + while (wis_delnum >= WISDELLIST_MAX); + + return 0; +} + +//-------------------------------------------------------- +// received packets from map-server + +// GMメッセージ送信 +int mapif_parse_GMmessage (int fd) +{ + mapif_GMmessage (RFIFOP (fd, 4), RFIFOW (fd, 2)); + + return 0; +} + +// Wisp/page request to send +int mapif_parse_WisRequest (int fd) +{ + struct WisData *wd; + static int wisid = 0; + int index; + + if (RFIFOW (fd, 2) - 52 >= sizeof (wd->msg)) + { + printf ("inter: Wis message size too long.\n"); + return 0; + } + else if (RFIFOW (fd, 2) - 52 <= 0) + { // normaly, impossible, but who knows... + printf ("inter: Wis message doesn't exist.\n"); + return 0; + } + + // search if character exists before to ask all map-servers + if ((index = search_character_index (RFIFOP (fd, 28))) == -1) + { + unsigned char buf[27]; + WBUFW (buf, 0) = 0x3802; + memcpy (WBUFP (buf, 2), RFIFOP (fd, 4), 24); + WBUFB (buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send (fd, buf, 27); + // Character exists. So, ask all map-servers + } + else + { + // to be sure of the correct name, rewrite it + memset (RFIFOP (fd, 28), 0, 24); + strncpy (RFIFOP (fd, 28), search_character_name (index), 24); + // if source is destination, don't ask other servers. + if (strcmp (RFIFOP (fd, 4), RFIFOP (fd, 28)) == 0) + { + unsigned char buf[27]; + WBUFW (buf, 0) = 0x3802; + memcpy (WBUFP (buf, 2), RFIFOP (fd, 4), 24); + WBUFB (buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send (fd, buf, 27); + } + else + { + CREATE (wd, struct WisData, 1); + + // Whether the failure of previous wisp/page transmission (timeout) + check_ttl_wisdata (); + + wd->id = ++wisid; + wd->fd = fd; + wd->len = RFIFOW (fd, 2) - 52; + memcpy (wd->src, RFIFOP (fd, 4), 24); + memcpy (wd->dst, RFIFOP (fd, 28), 24); + memcpy (wd->msg, RFIFOP (fd, 52), wd->len); + wd->tick = gettick (); + numdb_insert (wis_db, wd->id, wd); + mapif_wis_message (wd); + } + } + + return 0; +} + +// Wisp/page transmission result +int mapif_parse_WisReply (int fd) +{ + int id = RFIFOL (fd, 2), flag = RFIFOB (fd, 6); + struct WisData *wd = (struct WisData *)numdb_search (wis_db, id); + + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) + { + mapif_wis_end (wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase (wis_db, id); + free (wd); + } + + return 0; +} + +// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) +int mapif_parse_WisToGM (int fd) +{ + unsigned char buf[RFIFOW (fd, 2)]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B + + memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), RFIFOW (fd, 2)); + WBUFW (buf, 0) = 0x3803; + mapif_sendall (buf, RFIFOW (fd, 2)); + + return 0; +} + +// アカウント変数保存要求 +int mapif_parse_AccReg (int fd) +{ + int j, p; + struct accreg *reg = (struct accreg*)numdb_search (accreg_db, (numdb_key_t)RFIFOL (fd, 4)); + + if (reg == NULL) + { + CREATE (reg, struct accreg, 1); + reg->account_id = RFIFOL (fd, 4); + numdb_insert (accreg_db, (numdb_key_t)RFIFOL (fd, 4), reg); + } + + for (j = 0, p = 8; j < ACCOUNT_REG_NUM && p < RFIFOW (fd, 2); + j++, p += 36) + { + memcpy (reg->reg[j].str, RFIFOP (fd, p), 32); + reg->reg[j].value = RFIFOL (fd, p + 32); + } + reg->reg_num = j; + + mapif_account_reg (fd, RFIFOP (fd, 0)); // 他のMAPサーバーに送信 + + return 0; +} + +// アカウント変数送信要求 +int mapif_parse_AccRegRequest (int fd) +{ +// printf("mapif: accreg request\n"); + return mapif_account_reg_reply (fd, RFIFOL (fd, 2)); +} + +//-------------------------------------------------------- + +// map server からの通信(1パケットのみ解析すること) +// エラーなら0(false)、処理できたなら1、 +// パケット長が足りなければ2をかえさなければならない +int inter_parse_frommap (int fd) +{ + int cmd = RFIFOW (fd, 0); + int len = 0; + + // inter鯖管轄かを調べる + if (cmd < 0x3000 + || cmd >= + 0x3000 + + (sizeof (inter_recv_packet_length) / + sizeof (inter_recv_packet_length[0]))) + return 0; + + // パケット長を調べる + if ((len = + inter_check_length (fd, + inter_recv_packet_length[cmd - 0x3000])) == 0) + return 2; + + switch (cmd) + { + case 0x3000: + mapif_parse_GMmessage (fd); + break; + case 0x3001: + mapif_parse_WisRequest (fd); + break; + case 0x3002: + mapif_parse_WisReply (fd); + break; + case 0x3003: + mapif_parse_WisToGM (fd); + break; + case 0x3004: + mapif_parse_AccReg (fd); + break; + case 0x3005: + mapif_parse_AccRegRequest (fd); + break; + default: + if (inter_party_parse_frommap (fd)) + break; + if (inter_guild_parse_frommap (fd)) + break; + if (inter_storage_parse_frommap (fd)) + break; + return 0; + } + RFIFOSKIP (fd, len); + + return 1; +} + +// RFIFOのパケット長確認 +// 必要パケット長があればパケット長、まだ足りなければ0 +int inter_check_length (int fd, int length) +{ + if (length == -1) + { // 可変パケット長 + if (RFIFOREST (fd) < 4) // パケット長が未着 + return 0; + length = RFIFOW (fd, 2); + } + + if (RFIFOREST (fd) < length) // パケットが未着 + return 0; + + return length; +} diff --git a/src/char/inter.h b/src/char/inter.h deleted file mode 100644 index 219f195..0000000 --- a/src/char/inter.h +++ /dev/null @@ -1,19 +0,0 @@ -// $Id: inter.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ -#ifndef _INTER_H_ -#define _INTER_H_ - -int inter_init (const char *file); -int inter_save (void); -int inter_parse_frommap (int fd); -int inter_mapif_init (int fd); - -int inter_check_length (int fd, int length); - -int inter_log (char *fmt, ...); - -#define inter_cfgName "conf/inter_athena.conf" - -extern int party_share_level; -extern char inter_log_filename[1024]; - -#endif diff --git a/src/char/inter.hpp b/src/char/inter.hpp new file mode 100644 index 0000000..769324c --- /dev/null +++ b/src/char/inter.hpp @@ -0,0 +1,19 @@ +// $Id: inter.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef INTER_HPP +#define INTER_HPP + +int inter_init (const char *file); +int inter_save (void); +int inter_parse_frommap (int fd); +int inter_mapif_init (int fd); + +int inter_check_length (int fd, int length); + +int inter_log (char *fmt, ...); + +#define inter_cfgName "conf/inter_athena.conf" + +extern int party_share_level; +extern char inter_log_filename[1024]; + +#endif diff --git a/src/common/core.c b/src/common/core.c deleted file mode 100644 index b08276c..0000000 --- a/src/common/core.c +++ /dev/null @@ -1,97 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> -#include <sys/wait.h> - -#include "core.h" -#include "socket.h" -#include "timer.h" -#include "version.h" -#include "mt_rand.h" -#include "nullpo.h" - -/// Defined by each server -extern int do_init (int, char **); -extern void term_func (void); - -// Added by Gabuzomeu -// -// This is an implementation of signal() using sigaction() for portability. -// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced -// Programming in the UNIX Environment_. -// -typedef void (*sigfunc)(int); -sigfunc compat_signal (int signo, sigfunc func) -{ - struct sigaction sact, oact; - - sact.sa_handler = func; - sigfillset (&sact.sa_mask); - sigdelset(&sact.sa_mask, SIGSEGV); - sigdelset(&sact.sa_mask, SIGBUS); - sigdelset(&sact.sa_mask, SIGTRAP); - sigdelset(&sact.sa_mask, SIGILL); - sigdelset(&sact.sa_mask, SIGFPE); - sact.sa_flags = 0; - - if (sigaction (signo, &sact, &oact) < 0) - return SIG_ERR; - - return oact.sa_handler; -} - -static void chld_proc (int UNUSED) -{ - wait(NULL); -} -static void sig_proc (int UNUSED) -{ - for (int i = 1; i < 31; ++i) - compat_signal(i, SIG_IGN); - term_func (); - _exit (0); -} - -bool runflag = true; - -/* - Note about fatal signals: - - Under certain circumstances, - the following signals MUST not be ignored: - SIGFPE, SIGSEGV, SIGILL - Unless you use SA_SIGINFO and *carefully* check the origin, - that means they must be SIG_DFL. - */ -int main (int argc, char **argv) -{ - /// Note that getpid() and getppid() may be very close - mt_seed (time (NULL) ^ (getpid () << 16) ^ (getppid () << 8)); - - do_socket (); - - do_init (argc, argv); - // set up exit handlers *after* the initialization has happened. - // This is because term_func is likely to depend on successful init. - - compat_signal (SIGPIPE, SIG_IGN); - compat_signal (SIGTERM, sig_proc); - compat_signal (SIGINT, sig_proc); - compat_signal (SIGCHLD, chld_proc); - - // Signal to create coredumps by system when necessary (crash) - compat_signal (SIGSEGV, SIG_DFL); - compat_signal (SIGBUS, SIG_DFL); - compat_signal (SIGTRAP, SIG_DFL); - compat_signal (SIGILL, SIG_DFL); - compat_signal (SIGFPE, SIG_DFL); - - atexit (term_func); - - while (runflag) - { - do_sendrecv (do_timer (gettick_nocache ())); - do_parsepacket (); - } -} diff --git a/src/common/core.cpp b/src/common/core.cpp new file mode 100644 index 0000000..38b2260 --- /dev/null +++ b/src/common/core.cpp @@ -0,0 +1,97 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> + +#include "core.hpp" +#include "socket.hpp" +#include "timer.hpp" +#include "version.hpp" +#include "mt_rand.hpp" +#include "nullpo.hpp" + +/// Defined by each server +extern int do_init (int, char **); +extern void term_func (void); + +// Added by Gabuzomeu +// +// This is an implementation of signal() using sigaction() for portability. +// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced +// Programming in the UNIX Environment_. +// +typedef void (*sigfunc)(int); +sigfunc compat_signal (int signo, sigfunc func) +{ + struct sigaction sact, oact; + + sact.sa_handler = func; + sigfillset (&sact.sa_mask); + sigdelset(&sact.sa_mask, SIGSEGV); + sigdelset(&sact.sa_mask, SIGBUS); + sigdelset(&sact.sa_mask, SIGTRAP); + sigdelset(&sact.sa_mask, SIGILL); + sigdelset(&sact.sa_mask, SIGFPE); + sact.sa_flags = 0; + + if (sigaction (signo, &sact, &oact) < 0) + return SIG_ERR; + + return oact.sa_handler; +} + +static void chld_proc (int UNUSED) +{ + wait(NULL); +} +static void sig_proc (int UNUSED) +{ + for (int i = 1; i < 31; ++i) + compat_signal(i, SIG_IGN); + term_func (); + _exit (0); +} + +bool runflag = true; + +/* + Note about fatal signals: + + Under certain circumstances, + the following signals MUST not be ignored: + SIGFPE, SIGSEGV, SIGILL + Unless you use SA_SIGINFO and *carefully* check the origin, + that means they must be SIG_DFL. + */ +int main (int argc, char **argv) +{ + /// Note that getpid() and getppid() may be very close + mt_seed (time (NULL) ^ (getpid () << 16) ^ (getppid () << 8)); + + do_socket (); + + do_init (argc, argv); + // set up exit handlers *after* the initialization has happened. + // This is because term_func is likely to depend on successful init. + + compat_signal (SIGPIPE, SIG_IGN); + compat_signal (SIGTERM, sig_proc); + compat_signal (SIGINT, sig_proc); + compat_signal (SIGCHLD, chld_proc); + + // Signal to create coredumps by system when necessary (crash) + compat_signal (SIGSEGV, SIG_DFL); + compat_signal (SIGBUS, SIG_DFL); + compat_signal (SIGTRAP, SIG_DFL); + compat_signal (SIGILL, SIG_DFL); + compat_signal (SIGFPE, SIG_DFL); + + atexit (term_func); + + while (runflag) + { + do_sendrecv (do_timer (gettick_nocache ())); + do_parsepacket (); + } +} diff --git a/src/common/core.h b/src/common/core.h deleted file mode 100644 index 44473e9..0000000 --- a/src/common/core.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CORE_H -#define CORE_H -#include <stdbool.h> -/// core.c contains a server-independent main() function -/// and then runs a do_sendrecv loop - -/// When this is cleared, the server exits gracefully -/// only used by map server's GM command: @mapexit -extern bool runflag; - -/// This is an external function defined by each server -/// This function must register stuff for the parse loop -extern int do_init (int, char **); - -/// Cleanup function called whenever a signal kills us -/// or when if we manage to exit() gracefully. -extern void term_func (void); - -#endif // CORE_H diff --git a/src/common/core.hpp b/src/common/core.hpp new file mode 100644 index 0000000..8a52c55 --- /dev/null +++ b/src/common/core.hpp @@ -0,0 +1,19 @@ +#ifndef CORE_HPP +#define CORE_HPP +#include <stdbool.h> +/// core.c contains a server-independent main() function +/// and then runs a do_sendrecv loop + +/// When this is cleared, the server exits gracefully +/// only used by map server's GM command: @mapexit +extern bool runflag; + +/// This is an external function defined by each server +/// This function must register stuff for the parse loop +extern int do_init (int, char **); + +/// Cleanup function called whenever a signal kills us +/// or when if we manage to exit() gracefully. +extern void term_func (void); + +#endif // CORE_HPP diff --git a/src/common/db.c b/src/common/db.c deleted file mode 100644 index f56a511..0000000 --- a/src/common/db.c +++ /dev/null @@ -1,546 +0,0 @@ -#include "db.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "utils.h" - -#define ROOT_SIZE 4096 - -static int strdb_cmp (struct dbt *table, const char *a, const char* b) -{ - if (table->maxlen) - return strncmp (a, b, table->maxlen); - return strcmp (a, b); -} - -static hash_t strdb_hash (struct dbt *table, const char *a) -{ - size_t i = table->maxlen; - if (i == 0) - i = (size_t)-1; - hash_t h = 0; - const unsigned char *p = (const unsigned char*)a; - while (*p && i--) - { - h = (h * 33 + *p++) ^ (h >> 24); - } - return h; -} - -struct dbt *strdb_init (size_t maxlen) -{ - struct dbt *table; - CREATE (table, struct dbt, 1); - table->type = DB_STRING; - table->maxlen = maxlen; - return table; -} - -static int numdb_cmp (numdb_key_t a, numdb_key_t b) -{ - if (a == b) - return 0; - if (a < b) - return -1; - return 1; -} - -static hash_t numdb_hash (numdb_key_t a) -{ - return (hash_t) a; -} - -struct dbt *numdb_init (void) -{ - struct dbt *table; - CREATE (table, struct dbt, 1); - table->type = DB_NUMBER; - return table; -} - -static int table_cmp (struct dbt *table, db_key_t a, db_key_t b) -{ - switch(table->type) - { - case DB_NUMBER: return numdb_cmp (a.i, b.i); - case DB_STRING: return strdb_cmp (table, a.s, b.s); - } - abort(); -} - -static hash_t table_hash (struct dbt *table, db_key_t key) -{ - switch(table->type) - { - case DB_NUMBER: return numdb_hash (key.i); - case DB_STRING: return strdb_hash (table, key.s); - } - abort(); -} - -/// Search for a node with the given key -db_val_t db_search (struct dbt *table, db_key_t key) -{ - struct dbn *p = table->ht[table_hash (table, key) % HASH_SIZE]; - - while (p) - { - int c = table_cmp (table, key, p->key); - if (c == 0) - return p->data; - if (c < 0) - p = p->left; - else - p = p->right; - } - return NULL; -} - -// Tree maintainance methods -static void db_rotate_left (struct dbn *p, struct dbn **root) -{ - struct dbn *y = p->right; - p->right = y->left; - if (y->left) - y->left->parent = p; - y->parent = p->parent; - - if (p == *root) - *root = y; - else if (p == p->parent->left) - p->parent->left = y; - else - p->parent->right = y; - y->left = p; - p->parent = y; -} - -static void db_rotate_right (struct dbn *p, struct dbn **root) -{ - struct dbn *y = p->left; - p->left = y->right; - if (y->right) - y->right->parent = p; - y->parent = p->parent; - - if (p == *root) - *root = y; - else if (p == p->parent->right) - p->parent->right = y; - else - p->parent->left = y; - y->right = p; - p->parent = y; -} - -static void db_rebalance (struct dbn *p, struct dbn **root) -{ - p->color = RED; - while (p != *root && p->parent->color == RED) - { - if (p->parent == p->parent->parent->left) - { - struct dbn *y = p->parent->parent->right; - if (y && y->color == RED) - { - p->parent->color = BLACK; - y->color = BLACK; - p->parent->parent->color = RED; - p = p->parent->parent; - } - else - { - if (p == p->parent->right) - { - p = p->parent; - db_rotate_left (p, root); - } - p->parent->color = BLACK; - p->parent->parent->color = RED; - db_rotate_right (p->parent->parent, root); - } - } - else - { - struct dbn *y = p->parent->parent->left; - if (y && y->color == RED) - { - p->parent->color = BLACK; - y->color = BLACK; - p->parent->parent->color = RED; - p = p->parent->parent; - } - else - { - if (p == p->parent->left) - { - p = p->parent; - db_rotate_right (p, root); - } - p->parent->color = BLACK; - p->parent->parent->color = RED; - db_rotate_left (p->parent->parent, root); - } - } - } - (*root)->color = BLACK; -} - -// param z = node to remove -static void db_rebalance_erase (struct dbn *z, struct dbn **root) -{ - struct dbn *y = z; - struct dbn *x = NULL; - - if (!y->left) - x = y->right; - else if (!y->right) - x = y->left; - else - { - y = y->right; - while (y->left) - y = y->left; - x = y->right; - } - struct dbn *x_parent = NULL; - if (y != z) - { - z->left->parent = y; - y->left = z->left; - if (y != z->right) - { - x_parent = y->parent; - if (x) - x->parent = y->parent; - y->parent->left = x; - y->right = z->right; - z->right->parent = y; - } - else - x_parent = y; - if (*root == z) - *root = y; - else if (z->parent->left == z) - z->parent->left = y; - else - z->parent->right = y; - y->parent = z->parent; - { - dbn_color tmp = y->color; - y->color = z->color; - z->color = tmp; - } - y = z; - } - else - { - x_parent = y->parent; - if (x) - x->parent = y->parent; - if (*root == z) - *root = x; - else if (z->parent->left == z) - z->parent->left = x; - else - z->parent->right = x; - } - if (y->color != RED) - { - while (x != *root && (!x || x->color == BLACK)) - if (x == x_parent->left) - { - struct dbn *w = x_parent->right; - if (w->color == RED) - { - w->color = BLACK; - x_parent->color = RED; - db_rotate_left (x_parent, root); - w = x_parent->right; - } - if ((!w->left || w->left->color == BLACK) && - (!w->right || w->right->color == BLACK)) - { - w->color = RED; - x = x_parent; - x_parent = x->parent; - } - else - { - if (!w->right|| w->right->color == BLACK) - { - if (w->left) - w->left->color = BLACK; - w->color = RED; - db_rotate_right (w, root); - w = x_parent->right; - } - w->color = x_parent->color; - x_parent->color = BLACK; - if (w->right) - w->right->color = BLACK; - db_rotate_left (x_parent, root); - break; - } - } - else - { - // same as above, with right <-> left. - struct dbn *w = x_parent->left; - if (w->color == RED) - { - w->color = BLACK; - x_parent->color = RED; - db_rotate_right (x_parent, root); - w = x_parent->left; - } - if ((!w->right || w->right->color == BLACK) && - (!w->left || w->left->color == BLACK)) - { - w->color = RED; - x = x_parent; - x_parent = x_parent->parent; - } - else - { - if (!w->left || w->left->color == BLACK) - { - if (w->right) - w->right->color = BLACK; - w->color = RED; - db_rotate_left (w, root); - w = x_parent->left; - } - w->color = x_parent->color; - x_parent->color = BLACK; - if (w->left) - w->left->color = BLACK; - db_rotate_right (x_parent, root); - break; - } - } - if (x) - x->color = BLACK; - } -} - -struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data) -{ - hash_t hash = table_hash (table, key) % HASH_SIZE; - int c = 0; - struct dbn *prev = NULL; - struct dbn *p = table->ht[hash]; - while (p) - { - c = table_cmp (table, key, p->key); - if (c == 0) - { - // key found in table, replace - // Tell the user of the table to free the key and value - if (table->release) - table->release (p->key, p->data); - p->data = data; - p->key = key; - return p; - } - // prev is always p->parent? - prev = p; - if (c < 0) - p = p->left; - else - p = p->right; - } - CREATE (p, struct dbn, 1); - p->key = key; - p->data = data; - p->color = RED; - if (c == 0) - { // hash entry is empty - table->ht[hash] = p; - p->color = BLACK; - return p; - } - p->parent = prev; - if (c < 0) - prev->left = p; - else - prev->right = p; - if (prev->color == RED) - { - // must rebalance - db_rebalance (p, &table->ht[hash]); - } - return p; -} - -db_val_t db_erase (struct dbt *table, db_key_t key) -{ - hash_t hash = table_hash (table, key) % HASH_SIZE; - struct dbn *p = table->ht[hash]; - while (p) - { - int c = table_cmp (table, key, p->key); - if (c == 0) - break; - if (c < 0) - p = p->left; - else - p = p->right; - } - if (!p) - return NULL; - db_val_t data = p->data; - db_rebalance_erase (p, &table->ht[hash]); - free (p); - return data; -} -#ifdef SMART_WALK_TREE -static inline void db_walk_tree (bool dealloc, struct dbn* p, db_func_t func, va_list ap) -{ - if (!p) - return; - if (!dealloc && !func) - { - fprintf(stderr, "DEBUG: Must walk tree to either free or invoke a function.\n"); - abort(); - } - if (p->parent) - { - fprintf(stderr, "DEBUG: Root nodes must not have parents\n"); - abort(); - } - while (true) - { - // apply_func loop - if (func) - func (p->key, p->data, ap); - if (p->left) - { - // continue descending - p = p->left; - continue; //goto apply_func; - } - if (p->right) - { - // descending the other side - p = p->right; - continue; //goto apply_func; - } - while (true) - { - // backtrack loop - if (!p->parent) - { - if (dealloc) - free (p); - // if we have already done both children, there is no more to do - return; - } - if (p->parent->left == p && p->parent->right) - { - // finished the left tree, now walk the right tree - p = p->parent->right; - if (dealloc) - free (p->parent->left); - break; //goto apply_func; - } - // p->parent->right == p - // or p->parent->left == p but p->parent->right == NULL - // keep backtracking - p = p->parent; - if (dealloc) - free (p->right?:p->left); - } //backtrack loop - } // apply_func loop -} -#endif // SMART_WALK_TREE - -void db_foreach (struct dbt *table, db_func_t func, ...) -{ - va_list ap; - va_start (ap, func); - - for (int i = 0; i < HASH_SIZE; i++) - { -#ifdef SMART_WALK_TREE - db_walk_tree (false, table->ht[i], func, ap); -#else - struct dbn *p = table->ht[i]; - if (!p) - continue; - struct dbn *stack[64]; - int sp = 0; - while (1) - { - func (p->key, p->data, ap); - struct dbn *pn = p->left; - if (pn) - { - if (p->right) - stack[sp++] = p->right; - p = pn; - } - else // pn == NULL, time to do the right branch - { - if (p->right) - p = p->right; - else - { - if (sp == 0) - break; - p = stack[--sp]; - } - } // if pn else if !pn - } // while true -#endif // else ! SMART_WALK_TREE - } // for i - va_end (ap); -} - -// This function is suspiciously similar to the previous -void db_final (struct dbt *table, db_func_t func, ...) -{ - va_list ap; - va_start (ap, func); - - for (int i = 0; i < HASH_SIZE; i++) - { -#ifdef SMART_WALK_TREE - db_walk_tree (true, table->ht[i], func, ap); -#else - struct dbn *p = table->ht[i]; - if (!p) - continue; - struct dbn *stack[64]; - int sp = 0; - while (1) - { - if (func) - func (p->key, p->data, ap); - struct dbn *pn = p->left; - if (pn) - { - if (p->right) - stack[sp++] = p->right; - } - else // pn == NULL, check the right - { - if (p->right) - pn = p->right; - else - { - if (sp == 0) - break; - pn = stack[--sp]; - } - } // if pn else if !pn - free (p); - p = pn; - } // while true -#endif // else ! SMART_WALK_TREE - } // for i - free (table); - va_end (ap); -} diff --git a/src/common/db.cpp b/src/common/db.cpp new file mode 100644 index 0000000..21a3597 --- /dev/null +++ b/src/common/db.cpp @@ -0,0 +1,546 @@ +#include "db.hpp" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "utils.hpp" + +#define ROOT_SIZE 4096 + +static int strdb_cmp (struct dbt *table, const char *a, const char* b) +{ + if (table->maxlen) + return strncmp (a, b, table->maxlen); + return strcmp (a, b); +} + +static hash_t strdb_hash (struct dbt *table, const char *a) +{ + size_t i = table->maxlen; + if (i == 0) + i = (size_t)-1; + hash_t h = 0; + const unsigned char *p = (const unsigned char*)a; + while (*p && i--) + { + h = (h * 33 + *p++) ^ (h >> 24); + } + return h; +} + +struct dbt *strdb_init (size_t maxlen) +{ + struct dbt *table; + CREATE (table, struct dbt, 1); + table->type = DB_STRING; + table->maxlen = maxlen; + return table; +} + +static int numdb_cmp (numdb_key_t a, numdb_key_t b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +static hash_t numdb_hash (numdb_key_t a) +{ + return (hash_t) a; +} + +struct dbt *numdb_init (void) +{ + struct dbt *table; + CREATE (table, struct dbt, 1); + table->type = DB_NUMBER; + return table; +} + +static int table_cmp (struct dbt *table, db_key_t a, db_key_t b) +{ + switch(table->type) + { + case DB_NUMBER: return numdb_cmp (a.i, b.i); + case DB_STRING: return strdb_cmp (table, a.s, b.s); + } + abort(); +} + +static hash_t table_hash (struct dbt *table, db_key_t key) +{ + switch(table->type) + { + case DB_NUMBER: return numdb_hash (key.i); + case DB_STRING: return strdb_hash (table, key.s); + } + abort(); +} + +/// Search for a node with the given key +db_val_t db_search (struct dbt *table, db_key_t key) +{ + struct dbn *p = table->ht[table_hash (table, key) % HASH_SIZE]; + + while (p) + { + int c = table_cmp (table, key, p->key); + if (c == 0) + return p->data; + if (c < 0) + p = p->left; + else + p = p->right; + } + return NULL; +} + +// Tree maintainance methods +static void db_rotate_left (struct dbn *p, struct dbn **root) +{ + struct dbn *y = p->right; + p->right = y->left; + if (y->left) + y->left->parent = p; + y->parent = p->parent; + + if (p == *root) + *root = y; + else if (p == p->parent->left) + p->parent->left = y; + else + p->parent->right = y; + y->left = p; + p->parent = y; +} + +static void db_rotate_right (struct dbn *p, struct dbn **root) +{ + struct dbn *y = p->left; + p->left = y->right; + if (y->right) + y->right->parent = p; + y->parent = p->parent; + + if (p == *root) + *root = y; + else if (p == p->parent->right) + p->parent->right = y; + else + p->parent->left = y; + y->right = p; + p->parent = y; +} + +static void db_rebalance (struct dbn *p, struct dbn **root) +{ + p->color = RED; + while (p != *root && p->parent->color == RED) + { + if (p->parent == p->parent->parent->left) + { + struct dbn *y = p->parent->parent->right; + if (y && y->color == RED) + { + p->parent->color = BLACK; + y->color = BLACK; + p->parent->parent->color = RED; + p = p->parent->parent; + } + else + { + if (p == p->parent->right) + { + p = p->parent; + db_rotate_left (p, root); + } + p->parent->color = BLACK; + p->parent->parent->color = RED; + db_rotate_right (p->parent->parent, root); + } + } + else + { + struct dbn *y = p->parent->parent->left; + if (y && y->color == RED) + { + p->parent->color = BLACK; + y->color = BLACK; + p->parent->parent->color = RED; + p = p->parent->parent; + } + else + { + if (p == p->parent->left) + { + p = p->parent; + db_rotate_right (p, root); + } + p->parent->color = BLACK; + p->parent->parent->color = RED; + db_rotate_left (p->parent->parent, root); + } + } + } + (*root)->color = BLACK; +} + +// param z = node to remove +static void db_rebalance_erase (struct dbn *z, struct dbn **root) +{ + struct dbn *y = z; + struct dbn *x = NULL; + + if (!y->left) + x = y->right; + else if (!y->right) + x = y->left; + else + { + y = y->right; + while (y->left) + y = y->left; + x = y->right; + } + struct dbn *x_parent = NULL; + if (y != z) + { + z->left->parent = y; + y->left = z->left; + if (y != z->right) + { + x_parent = y->parent; + if (x) + x->parent = y->parent; + y->parent->left = x; + y->right = z->right; + z->right->parent = y; + } + else + x_parent = y; + if (*root == z) + *root = y; + else if (z->parent->left == z) + z->parent->left = y; + else + z->parent->right = y; + y->parent = z->parent; + { + dbn_color tmp = y->color; + y->color = z->color; + z->color = tmp; + } + y = z; + } + else + { + x_parent = y->parent; + if (x) + x->parent = y->parent; + if (*root == z) + *root = x; + else if (z->parent->left == z) + z->parent->left = x; + else + z->parent->right = x; + } + if (y->color != RED) + { + while (x != *root && (!x || x->color == BLACK)) + if (x == x_parent->left) + { + struct dbn *w = x_parent->right; + if (w->color == RED) + { + w->color = BLACK; + x_parent->color = RED; + db_rotate_left (x_parent, root); + w = x_parent->right; + } + if ((!w->left || w->left->color == BLACK) && + (!w->right || w->right->color == BLACK)) + { + w->color = RED; + x = x_parent; + x_parent = x->parent; + } + else + { + if (!w->right|| w->right->color == BLACK) + { + if (w->left) + w->left->color = BLACK; + w->color = RED; + db_rotate_right (w, root); + w = x_parent->right; + } + w->color = x_parent->color; + x_parent->color = BLACK; + if (w->right) + w->right->color = BLACK; + db_rotate_left (x_parent, root); + break; + } + } + else + { + // same as above, with right <-> left. + struct dbn *w = x_parent->left; + if (w->color == RED) + { + w->color = BLACK; + x_parent->color = RED; + db_rotate_right (x_parent, root); + w = x_parent->left; + } + if ((!w->right || w->right->color == BLACK) && + (!w->left || w->left->color == BLACK)) + { + w->color = RED; + x = x_parent; + x_parent = x_parent->parent; + } + else + { + if (!w->left || w->left->color == BLACK) + { + if (w->right) + w->right->color = BLACK; + w->color = RED; + db_rotate_left (w, root); + w = x_parent->left; + } + w->color = x_parent->color; + x_parent->color = BLACK; + if (w->left) + w->left->color = BLACK; + db_rotate_right (x_parent, root); + break; + } + } + if (x) + x->color = BLACK; + } +} + +struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data) +{ + hash_t hash = table_hash (table, key) % HASH_SIZE; + int c = 0; + struct dbn *prev = NULL; + struct dbn *p = table->ht[hash]; + while (p) + { + c = table_cmp (table, key, p->key); + if (c == 0) + { + // key found in table, replace + // Tell the user of the table to free the key and value + if (table->release) + table->release (p->key, p->data); + p->data = data; + p->key = key; + return p; + } + // prev is always p->parent? + prev = p; + if (c < 0) + p = p->left; + else + p = p->right; + } + CREATE (p, struct dbn, 1); + p->key = key; + p->data = data; + p->color = RED; + if (c == 0) + { // hash entry is empty + table->ht[hash] = p; + p->color = BLACK; + return p; + } + p->parent = prev; + if (c < 0) + prev->left = p; + else + prev->right = p; + if (prev->color == RED) + { + // must rebalance + db_rebalance (p, &table->ht[hash]); + } + return p; +} + +db_val_t db_erase (struct dbt *table, db_key_t key) +{ + hash_t hash = table_hash (table, key) % HASH_SIZE; + struct dbn *p = table->ht[hash]; + while (p) + { + int c = table_cmp (table, key, p->key); + if (c == 0) + break; + if (c < 0) + p = p->left; + else + p = p->right; + } + if (!p) + return NULL; + db_val_t data = p->data; + db_rebalance_erase (p, &table->ht[hash]); + free (p); + return data; +} +#ifdef SMART_WALK_TREE +static inline void db_walk_tree (bool dealloc, struct dbn* p, db_func_t func, va_list ap) +{ + if (!p) + return; + if (!dealloc && !func) + { + fprintf(stderr, "DEBUG: Must walk tree to either free or invoke a function.\n"); + abort(); + } + if (p->parent) + { + fprintf(stderr, "DEBUG: Root nodes must not have parents\n"); + abort(); + } + while (true) + { + // apply_func loop + if (func) + func (p->key, p->data, ap); + if (p->left) + { + // continue descending + p = p->left; + continue; //goto apply_func; + } + if (p->right) + { + // descending the other side + p = p->right; + continue; //goto apply_func; + } + while (true) + { + // backtrack loop + if (!p->parent) + { + if (dealloc) + free (p); + // if we have already done both children, there is no more to do + return; + } + if (p->parent->left == p && p->parent->right) + { + // finished the left tree, now walk the right tree + p = p->parent->right; + if (dealloc) + free (p->parent->left); + break; //goto apply_func; + } + // p->parent->right == p + // or p->parent->left == p but p->parent->right == NULL + // keep backtracking + p = p->parent; + if (dealloc) + free (p->right?:p->left); + } //backtrack loop + } // apply_func loop +} +#endif // SMART_WALK_TREE + +void db_foreach (struct dbt *table, db_func_t func, ...) +{ + va_list ap; + va_start (ap, func); + + for (int i = 0; i < HASH_SIZE; i++) + { +#ifdef SMART_WALK_TREE + db_walk_tree (false, table->ht[i], func, ap); +#else + struct dbn *p = table->ht[i]; + if (!p) + continue; + struct dbn *stack[64]; + int sp = 0; + while (1) + { + func (p->key, p->data, ap); + struct dbn *pn = p->left; + if (pn) + { + if (p->right) + stack[sp++] = p->right; + p = pn; + } + else // pn == NULL, time to do the right branch + { + if (p->right) + p = p->right; + else + { + if (sp == 0) + break; + p = stack[--sp]; + } + } // if pn else if !pn + } // while true +#endif // else ! SMART_WALK_TREE + } // for i + va_end (ap); +} + +// This function is suspiciously similar to the previous +void db_final (struct dbt *table, db_func_t func, ...) +{ + va_list ap; + va_start (ap, func); + + for (int i = 0; i < HASH_SIZE; i++) + { +#ifdef SMART_WALK_TREE + db_walk_tree (true, table->ht[i], func, ap); +#else + struct dbn *p = table->ht[i]; + if (!p) + continue; + struct dbn *stack[64]; + int sp = 0; + while (1) + { + if (func) + func (p->key, p->data, ap); + struct dbn *pn = p->left; + if (pn) + { + if (p->right) + stack[sp++] = p->right; + } + else // pn == NULL, check the right + { + if (p->right) + pn = p->right; + else + { + if (sp == 0) + break; + pn = stack[--sp]; + } + } // if pn else if !pn + free (p); + p = pn; + } // while true +#endif // else ! SMART_WALK_TREE + } // for i + free (table); + va_end (ap); +} diff --git a/src/common/db.h b/src/common/db.h deleted file mode 100644 index 7152854..0000000 --- a/src/common/db.h +++ /dev/null @@ -1,87 +0,0 @@ -// WARNING: there is a system header by this name -#ifndef DB_H -#define DB_H -# include "sanity.h" - -# include <stdarg.h> - -/// Number of tree roots -// Somewhat arbitrary - larger wastes more space but is faster for large trees -// num % HASH_SIZE minimize collisions even for similar num -# define HASH_SIZE (256+27) - -typedef enum dbn_color -{ - RED, - BLACK -} dbn_color; - -typedef intptr_t numdb_key_t; -typedef union db_key_t -{ - char *ms __attribute__((deprecated)); - const char* s; - numdb_key_t i; -} db_key_t; -typedef void* db_val_t; -typedef uint32_t hash_t; -typedef void (*db_func_t)(db_key_t, db_val_t, va_list); - -/// DataBase Node -struct dbn -{ - struct dbn *parent, *left, *right; - dbn_color color; - db_key_t key; - db_val_t data; -}; - -typedef enum dbt_type -{ - DB_NUMBER, - DB_STRING, -} dbt_type; - -/// DataBase Table -struct dbt -{ - dbt_type type; - /// Note, before replacement, key/values to be replaced - // TODO refactor to decrease/eliminate the uses of this? - void (*release) (db_key_t, db_val_t) __attribute__((deprecated)); - /// Maximum length of a string key - TODO refactor to ensure all strings are NUL-terminated - size_t maxlen __attribute__((deprecated)); - /// The root trees - struct dbn *ht[HASH_SIZE]; -}; - -# define strdb_search(t,k) db_search((t),(db_key_t)(k)) -# define strdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d)) -# define strdb_erase(t,k) db_erase ((t),(db_key_t)(k)) -# define strdb_foreach db_foreach -# define strdb_final db_final -# define numdb_search(t,k) db_search((t),(db_key_t)(k)) -# define numdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d)) -# define numdb_erase(t,k) db_erase ((t),(db_key_t)(k)) -# define numdb_foreach db_foreach -# define numdb_final db_final - -/// Create a map from char* to void*, with strings possibly not null-terminated -struct dbt *strdb_init (size_t maxlen); -/// Create a map from int to void* -struct dbt *numdb_init (void); -/// Return the value corresponding to the key, or NULL if not found -db_val_t db_search (struct dbt *table, db_key_t key); -/// Add or replace table[key] = data -// if it was already there, call release -struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data); -/// Remove a key from the table, returning the data -db_val_t db_erase (struct dbt *table, db_key_t key); - -/// Execute a function for every element, in unspecified order -void db_foreach (struct dbt *, db_func_t, ...); -// opposite of init? Calls release for every element and frees memory -// This probably isn't really needed: we don't have to free memory while exiting -void db_final (struct dbt *, db_func_t, ...) __attribute__((deprecated)); - -#endif diff --git a/src/common/db.hpp b/src/common/db.hpp new file mode 100644 index 0000000..145ba13 --- /dev/null +++ b/src/common/db.hpp @@ -0,0 +1,87 @@ +// WARNING: there is a system header by this name +#ifndef DB_HPP +#define DB_HPP +# include "sanity.hpp" + +# include <stdarg.h> + +/// Number of tree roots +// Somewhat arbitrary - larger wastes more space but is faster for large trees +// num % HASH_SIZE minimize collisions even for similar num +# define HASH_SIZE (256+27) + +typedef enum dbn_color +{ + RED, + BLACK +} dbn_color; + +typedef intptr_t numdb_key_t; +typedef union db_key_t +{ + char *ms __attribute__((deprecated)); + const char* s; + numdb_key_t i; +} db_key_t; +typedef void* db_val_t; +typedef uint32_t hash_t; +typedef void (*db_func_t)(db_key_t, db_val_t, va_list); + +/// DataBase Node +struct dbn +{ + struct dbn *parent, *left, *right; + dbn_color color; + db_key_t key; + db_val_t data; +}; + +typedef enum dbt_type +{ + DB_NUMBER, + DB_STRING, +} dbt_type; + +/// DataBase Table +struct dbt +{ + dbt_type type; + /// Note, before replacement, key/values to be replaced + // TODO refactor to decrease/eliminate the uses of this? + void (*release) (db_key_t, db_val_t) __attribute__((deprecated)); + /// Maximum length of a string key - TODO refactor to ensure all strings are NUL-terminated + size_t maxlen __attribute__((deprecated)); + /// The root trees + struct dbn *ht[HASH_SIZE]; +}; + +# define strdb_search(t,k) db_search((t),(db_key_t)(k)) +# define strdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d)) +# define strdb_erase(t,k) db_erase ((t),(db_key_t)(k)) +# define strdb_foreach db_foreach +# define strdb_final db_final +# define numdb_search(t,k) db_search((t),(db_key_t)(k)) +# define numdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d)) +# define numdb_erase(t,k) db_erase ((t),(db_key_t)(k)) +# define numdb_foreach db_foreach +# define numdb_final db_final + +/// Create a map from char* to void*, with strings possibly not null-terminated +struct dbt *strdb_init (size_t maxlen); +/// Create a map from int to void* +struct dbt *numdb_init (void); +/// Return the value corresponding to the key, or NULL if not found +db_val_t db_search (struct dbt *table, db_key_t key); +/// Add or replace table[key] = data +// if it was already there, call release +struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data); +/// Remove a key from the table, returning the data +db_val_t db_erase (struct dbt *table, db_key_t key); + +/// Execute a function for every element, in unspecified order +void db_foreach (struct dbt *, db_func_t, ...); +// opposite of init? Calls release for every element and frees memory +// This probably isn't really needed: we don't have to free memory while exiting +void db_final (struct dbt *, db_func_t, ...) __attribute__((deprecated)); + +#endif diff --git a/src/common/grfio.c b/src/common/grfio.c deleted file mode 100644 index d640263..0000000 --- a/src/common/grfio.c +++ /dev/null @@ -1,212 +0,0 @@ -// Reads .gat files by name-mapping .wlk files -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -#include "utils.h" -#include "grfio.h" -#include "mmo.h" -#include "socket.h" - -//---------------------------- -// file entry table struct -//---------------------------- -typedef struct -{ - size_t declen; - int16_t next; // next index into the filelist[] array, or -1 - char fn[128 - 4 - 2]; // file name -} FILELIST; - -#define FILELIST_LIMIT 32768 // limit to number of filelists - if you increase this, change all shorts to int -#define FILELIST_ADDS 1024 // amount to increment when reallocing - -static FILELIST *filelist = NULL; -/// Number of entries used -static uint16_t filelist_entrys = 0; -/// Number of FILELIST entries actually allocated -static uint16_t filelist_maxentry = 0; - -/// First index of the given hash, into the filelist[] array -static int16_t filelist_hash[256] = {[0 ... 255] = -1}; - -/// Hash a filename -static uint8_t filehash (const char *fname) -{ - // Larger than the return type - upper bits are used in the process - uint32_t hash = 0; - while (*fname) - { - hash = (hash << 1) + (hash >> 7) * 9 + (unsigned char)*fname; - fname++; - } - return hash; -} - -/// Find the filelist entry for the given filename, or NULL if it is not -FILELIST *filelist_find (const char *fname) -{ - int16_t index = filelist_hash[filehash (fname)]; - while (index >= 0) - { - if (strcmp (filelist[index].fn, fname) == 0) - return &filelist[index]; - index = filelist[index].next; - } - return NULL; -} - -/// Copy a temporary entry into the hash map -static FILELIST *filelist_add (FILELIST * entry) -{ - if (filelist_entrys >= FILELIST_LIMIT) - { - fprintf (stderr, "filelist limit : filelist_add\n"); - exit (1); - } - - if (filelist_entrys >= filelist_maxentry) - { - RECREATE(filelist, FILELIST, filelist_maxentry + FILELIST_ADDS); - memset (filelist + filelist_maxentry, '\0', - FILELIST_ADDS * sizeof (FILELIST)); - filelist_maxentry += FILELIST_ADDS; - } - - uint16_t new_index = filelist_entrys++; - uint8_t hash = filehash (entry->fn); - entry->next = filelist_hash[hash]; - filelist_hash[hash] = new_index; - - filelist[new_index] = *entry; - - return &filelist[new_index]; -} - -static FILELIST *filelist_modify (FILELIST * entry) -{ - FILELIST *fentry = filelist_find (entry->fn); - if (fentry) - { - entry->next = fentry->next; - *fentry = *entry; - return fentry; - } - return filelist_add (entry); -} - -/// Change fname data/*.gat to lfname data/*.wlk -// TODO even if the file exists, don't keep reopening it every time one loads -void grfio_resnametable (const char *fname, char *lfname) -{ - char restable[] = "data/resnametable.txt"; - - FILE *fp = fopen_ (restable, "rb"); - if (fp == NULL) - { - fprintf(stderr, "No resnametable, can't look for %s\n", fname); - strcpy(lfname, fname); - char* ext = lfname + strlen(lfname) - 4; - if (!strcmp(ext, ".gat")) - strcpy(ext, ".wlk"); - return; - } - - char line[512]; - while (fgets (line, sizeof (line), fp)) - { - char w1[256], w2[256]; - if ( - // line is of the form foo.gat#foo.wlk# - (sscanf (line, "%[^#]#%[^#]#", w1, w2) == 2) - // strip data/ from foo.gat before comparing - && (!strcmp (w1, fname + 5))) - { - strcpy (lfname, "data/"); - strcpy (lfname + 5, w2); - fclose_ (fp); - return; - } - } - fprintf(stderr, "Unable to find resource: %s\n", fname); - fclose_ (fp); - - strcpy(lfname, fname); - char* ext = lfname + strlen(lfname) - 4; - if (!strcmp(ext, ".gat")) - strcpy(ext, ".wlk"); - return; -} - -/// Size of resource -size_t grfio_size (const char *fname) -{ - FILELIST *entry = filelist_find (fname); - if (entry) - return entry->declen; - - char lfname[256]; - FILELIST lentry; - struct stat st; - - grfio_resnametable (fname, lfname); - - for (char *p = lfname; *p; p++) - if (*p == '\\') - *p = '/'; - - if (stat (lfname, &st) == 0) - { - strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1); - lentry.declen = st.st_size; - entry = filelist_modify (&lentry); - } - else - { - printf ("%s not found\n", fname); - return 0; - } - return entry->declen; -} - -void *grfio_reads (const char *fname, size_t *size) -{ - char lfname[256]; - grfio_resnametable (fname, lfname); - - for (char *p = &lfname[0]; *p != 0; p++) - if (*p == '\\') - *p = '/'; // * At the time of Unix - - FILE *in = fopen_ (lfname, "rb"); - if (!in) - { - fprintf (stderr, "%s not found\n", fname); - return NULL; - } - FILELIST lentry; - FILELIST *entry = filelist_find (fname); - if (entry) - { - lentry.declen = entry->declen; - } - else - { - fseek (in, 0, SEEK_END); - lentry.declen = ftell (in); - fseek (in, 0, SEEK_SET); - strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1); - entry = filelist_modify (&lentry); - } - uint8_t *buf2; - CREATE (buf2, uint8_t, lentry.declen + 1024); - if (fread (buf2, 1, lentry.declen, in) != lentry.declen) - exit(1); - fclose_ (in); - in = NULL; - - if (size) - *size = entry->declen; - return buf2; -} diff --git a/src/common/grfio.cpp b/src/common/grfio.cpp new file mode 100644 index 0000000..1b89b18 --- /dev/null +++ b/src/common/grfio.cpp @@ -0,0 +1,212 @@ +// Reads .gat files by name-mapping .wlk files +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "utils.hpp" +#include "grfio.hpp" +#include "mmo.hpp" +#include "socket.hpp" + +//---------------------------- +// file entry table struct +//---------------------------- +typedef struct +{ + size_t declen; + int16_t next; // next index into the filelist[] array, or -1 + char fn[128 - 4 - 2]; // file name +} FILELIST; + +#define FILELIST_LIMIT 32768 // limit to number of filelists - if you increase this, change all shorts to int +#define FILELIST_ADDS 1024 // amount to increment when reallocing + +static FILELIST *filelist = NULL; +/// Number of entries used +static uint16_t filelist_entrys = 0; +/// Number of FILELIST entries actually allocated +static uint16_t filelist_maxentry = 0; + +/// First index of the given hash, into the filelist[] array +static int16_t filelist_hash[256] = {[0 ... 255] = -1}; + +/// Hash a filename +static uint8_t filehash (const char *fname) +{ + // Larger than the return type - upper bits are used in the process + uint32_t hash = 0; + while (*fname) + { + hash = (hash << 1) + (hash >> 7) * 9 + (unsigned char)*fname; + fname++; + } + return hash; +} + +/// Find the filelist entry for the given filename, or NULL if it is not +FILELIST *filelist_find (const char *fname) +{ + int16_t index = filelist_hash[filehash (fname)]; + while (index >= 0) + { + if (strcmp (filelist[index].fn, fname) == 0) + return &filelist[index]; + index = filelist[index].next; + } + return NULL; +} + +/// Copy a temporary entry into the hash map +static FILELIST *filelist_add (FILELIST * entry) +{ + if (filelist_entrys >= FILELIST_LIMIT) + { + fprintf (stderr, "filelist limit : filelist_add\n"); + exit (1); + } + + if (filelist_entrys >= filelist_maxentry) + { + RECREATE(filelist, FILELIST, filelist_maxentry + FILELIST_ADDS); + memset (filelist + filelist_maxentry, '\0', + FILELIST_ADDS * sizeof (FILELIST)); + filelist_maxentry += FILELIST_ADDS; + } + + uint16_t new_index = filelist_entrys++; + uint8_t hash = filehash (entry->fn); + entry->next = filelist_hash[hash]; + filelist_hash[hash] = new_index; + + filelist[new_index] = *entry; + + return &filelist[new_index]; +} + +static FILELIST *filelist_modify (FILELIST * entry) +{ + FILELIST *fentry = filelist_find (entry->fn); + if (fentry) + { + entry->next = fentry->next; + *fentry = *entry; + return fentry; + } + return filelist_add (entry); +} + +/// Change fname data/*.gat to lfname data/*.wlk +// TODO even if the file exists, don't keep reopening it every time one loads +void grfio_resnametable (const char *fname, char *lfname) +{ + char restable[] = "data/resnametable.txt"; + + FILE *fp = fopen_ (restable, "rb"); + if (fp == NULL) + { + fprintf(stderr, "No resnametable, can't look for %s\n", fname); + strcpy(lfname, fname); + char* ext = lfname + strlen(lfname) - 4; + if (!strcmp(ext, ".gat")) + strcpy(ext, ".wlk"); + return; + } + + char line[512]; + while (fgets (line, sizeof (line), fp)) + { + char w1[256], w2[256]; + if ( + // line is of the form foo.gat#foo.wlk# + (sscanf (line, "%[^#]#%[^#]#", w1, w2) == 2) + // strip data/ from foo.gat before comparing + && (!strcmp (w1, fname + 5))) + { + strcpy (lfname, "data/"); + strcpy (lfname + 5, w2); + fclose_ (fp); + return; + } + } + fprintf(stderr, "Unable to find resource: %s\n", fname); + fclose_ (fp); + + strcpy(lfname, fname); + char* ext = lfname + strlen(lfname) - 4; + if (!strcmp(ext, ".gat")) + strcpy(ext, ".wlk"); + return; +} + +/// Size of resource +size_t grfio_size (const char *fname) +{ + FILELIST *entry = filelist_find (fname); + if (entry) + return entry->declen; + + char lfname[256]; + FILELIST lentry; + struct stat st; + + grfio_resnametable (fname, lfname); + + for (char *p = lfname; *p; p++) + if (*p == '\\') + *p = '/'; + + if (stat (lfname, &st) == 0) + { + strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1); + lentry.declen = st.st_size; + entry = filelist_modify (&lentry); + } + else + { + printf ("%s not found\n", fname); + return 0; + } + return entry->declen; +} + +void *grfio_reads (const char *fname, size_t *size) +{ + char lfname[256]; + grfio_resnametable (fname, lfname); + + for (char *p = &lfname[0]; *p != 0; p++) + if (*p == '\\') + *p = '/'; // * At the time of Unix + + FILE *in = fopen_ (lfname, "rb"); + if (!in) + { + fprintf (stderr, "%s not found\n", fname); + return NULL; + } + FILELIST lentry; + FILELIST *entry = filelist_find (fname); + if (entry) + { + lentry.declen = entry->declen; + } + else + { + fseek (in, 0, SEEK_END); + lentry.declen = ftell (in); + fseek (in, 0, SEEK_SET); + strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1); + entry = filelist_modify (&lentry); + } + uint8_t *buf2; + CREATE (buf2, uint8_t, lentry.declen + 1024); + if (fread (buf2, 1, lentry.declen, in) != lentry.declen) + exit(1); + fclose_ (in); + in = NULL; + + if (size) + *size = entry->declen; + return buf2; +} diff --git a/src/common/grfio.h b/src/common/grfio.h deleted file mode 100644 index 4919a7b..0000000 --- a/src/common/grfio.h +++ /dev/null @@ -1,17 +0,0 @@ -/// Accessor to the .gat map virtual files -// Note .gat files are mapped to .wlk files by data/resnametable.txt -// Note that there currently is a 1-1 correlation between them, -// but it is possible for a single .wlk to have multiple .gats reference it -#ifndef GRFIO_H -#define GRFIO_H - -/// Load file into memory -# define grfio_read(resourcename) grfio_reads (resourcename, NULL) -/// Load file into memory and possibly record length -// For some reason, this allocates an extra 1024 bytes at the end -void *grfio_reads (const char *resourcename, size_t *size); -/// Get size of file -// This is only called once, and that is to check the existence of a file. -size_t grfio_size (const char *resourcename) __attribute__((deprecated)); - -#endif // GRFIO_H diff --git a/src/common/grfio.hpp b/src/common/grfio.hpp new file mode 100644 index 0000000..3485904 --- /dev/null +++ b/src/common/grfio.hpp @@ -0,0 +1,17 @@ +/// Accessor to the .gat map virtual files +// Note .gat files are mapped to .wlk files by data/resnametable.txt +// Note that there currently is a 1-1 correlation between them, +// but it is possible for a single .wlk to have multiple .gats reference it +#ifndef GRFIO_HPP +#define GRFIO_HPP + +/// Load file into memory +# define grfio_read(resourcename) grfio_reads (resourcename, NULL) +/// Load file into memory and possibly record length +// For some reason, this allocates an extra 1024 bytes at the end +void *grfio_reads (const char *resourcename, size_t *size); +/// Get size of file +// This is only called once, and that is to check the existence of a file. +size_t grfio_size (const char *resourcename) __attribute__((deprecated)); + +#endif // GRFIO_HPP diff --git a/src/common/lock.c b/src/common/lock.c deleted file mode 100644 index dd42ef2..0000000 --- a/src/common/lock.c +++ /dev/null @@ -1,36 +0,0 @@ -#include <unistd.h> -#include <stdio.h> -#include "lock.h" -#include "socket.h" - -/// Protected file writing -/// (Until the file is closed, it keeps the old file) - -// Start writing a tmpfile -FILE *lock_fopen (const char *filename, int *info) -{ - char newfile[512]; - FILE *fp; - int no = getpid (); - - // Get a filename that doesn't already exist - do - { - sprintf (newfile, "%s_%d.tmp", filename, no++); - } - while ((fp = fopen_ (newfile, "r")) && (fclose_ (fp), 1)); - *info = --no; - return fopen_ (newfile, "w"); -} - -// Delete the old file and rename the new file -void lock_fclose (FILE * fp, const char *filename, int *info) -{ - char newfile[512]; - if (fp) - { - fclose_ (fp); - sprintf (newfile, "%s_%d.tmp", filename, *info); - rename (newfile, filename); - } -} diff --git a/src/common/lock.cpp b/src/common/lock.cpp new file mode 100644 index 0000000..2ba9a0a --- /dev/null +++ b/src/common/lock.cpp @@ -0,0 +1,36 @@ +#include <unistd.h> +#include <stdio.h> +#include "lock.hpp" +#include "socket.hpp" + +/// Protected file writing +/// (Until the file is closed, it keeps the old file) + +// Start writing a tmpfile +FILE *lock_fopen (const char *filename, int *info) +{ + char newfile[512]; + FILE *fp; + int no = getpid (); + + // Get a filename that doesn't already exist + do + { + sprintf (newfile, "%s_%d.tmp", filename, no++); + } + while ((fp = fopen_ (newfile, "r")) && (fclose_ (fp), 1)); + *info = --no; + return fopen_ (newfile, "w"); +} + +// Delete the old file and rename the new file +void lock_fclose (FILE * fp, const char *filename, int *info) +{ + char newfile[512]; + if (fp) + { + fclose_ (fp); + sprintf (newfile, "%s_%d.tmp", filename, *info); + rename (newfile, filename); + } +} diff --git a/src/common/lock.h b/src/common/lock.h deleted file mode 100644 index 2f087fd..0000000 --- a/src/common/lock.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LOCK_H -#define LOCK_H -/// Locked FILE I/O -// Changes are made in a separate file until lock_fclose -FILE *lock_fopen (const char *filename, int *info); -void lock_fclose (FILE * fp, const char *filename, int *info); - -#endif // LOCK_H diff --git a/src/common/lock.hpp b/src/common/lock.hpp new file mode 100644 index 0000000..19c1302 --- /dev/null +++ b/src/common/lock.hpp @@ -0,0 +1,8 @@ +#ifndef LOCK_HPP +#define LOCK_HPP +/// Locked FILE I/O +// Changes are made in a separate file until lock_fclose +FILE *lock_fopen (const char *filename, int *info); +void lock_fclose (FILE * fp, const char *filename, int *info); + +#endif // LOCK_HPP diff --git a/src/common/md5calc.c b/src/common/md5calc.c deleted file mode 100644 index ba8f6af..0000000 --- a/src/common/md5calc.c +++ /dev/null @@ -1,339 +0,0 @@ -#include "md5calc.h" -#include <string.h> -#include "mt_rand.h" - -// auxilary data -/* -sin() constant table -# Reformatted output of: -echo 'scale=40; obase=16; for (i=1;i<=64;i++) print 2^32 * sin(i), "\n"' | -bc | sed 's/^-//;s/^/0x/;s/\..*$/,/' -*/ -static const uint32_t T[64] = -{ - // used by round 1 - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 - // used by round 2 - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, //20 - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 - // used by round 3 - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, //40 - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 - // used by round 4 - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, //60 -}; - -// auxilary functions -// note - the RFC defines these by non-CS conventions: or=v, and=(empty) -static inline uint32_t rotate_left(uint32_t val, unsigned shift) -{ - return val << shift | val >> (32-shift); -} - -static inline uint32_t F(uint32_t X, uint32_t Y, uint32_t Z) -{ - return (X & Y) | (~X & Z); -} -static inline uint32_t G(uint32_t X, uint32_t Y, uint32_t Z) -{ - return (X & Z) | (Y & ~Z); -} -static inline uint32_t H(uint32_t X, uint32_t Y, uint32_t Z) -{ - return X ^ Y ^ Z; -} -static inline uint32_t I(uint32_t X, uint32_t Y, uint32_t Z) -{ - return Y ^ (X | ~Z); -} - -static const struct -{ - uint8_t k : 4; - uint8_t : 0; - uint8_t s : 5; -// uint8_t i : 6; just increments constantly, from 1 .. 64 over all rounds -} -MD5_round1[16] = -{ - { 0, 7}, { 1, 12}, { 2, 17}, { 3, 22}, - { 4, 7}, { 5, 12}, { 6, 17}, { 7, 22}, - { 8, 7}, { 9, 12}, {10, 17}, {11, 22}, - {12, 7}, {13, 12}, {14, 17}, {15, 22}, -}, -MD5_round2[16] = -{ - { 1, 5}, { 6, 9}, {11, 14}, { 0, 20}, - { 5, 5}, {10, 9}, {15, 14}, { 4, 20}, - { 9, 5}, {14, 9}, { 3, 14}, { 8, 20}, - {13, 5}, { 2, 9}, { 7, 14}, {12, 20}, -}, -MD5_round3[16] = -{ - { 5, 4}, { 8, 11}, {11, 16}, {14, 23}, - { 1, 4}, { 4, 11}, { 7, 16}, {10, 23}, - {13, 4}, { 0, 11}, { 3, 16}, { 6, 23}, - { 9, 4}, {12, 11}, {15, 16}, { 2, 23}, -}, -MD5_round4[16] = -{ - { 0, 6}, { 7, 10}, {14, 15}, { 5, 21}, - {12, 6}, { 3, 10}, {10, 15}, { 1, 21}, - { 8, 6}, {15, 10}, { 6, 15}, {13, 21}, - { 4, 6}, {11, 10}, { 2, 15}, { 9, 21}, -}; - - -void MD5_init(MD5_state* state) -{ - // in the RFC, these are specified as bytes, interpreted as little-endian - state->val[0] = 0x67452301; - state->val[1] = 0xEFCDAB89; - state->val[2] = 0x98BADCFE; - state->val[3] = 0x10325476; -} - -void MD5_do_block(MD5_state* state, MD5_block block) -{ -#define X block.data -#define a state->val[(16-i)%4] -#define b state->val[(17-i)%4] -#define c state->val[(18-i)%4] -#define d state->val[(19-i)%4] - // save the values - const MD5_state saved = *state; - // round 1 - for (int i=0; i<16; i++) - { -#define k MD5_round1[i].k -#define s MD5_round1[i].s - a = b + rotate_left(a + F(b,c,d) + X[k] + T[i+0x0], s); -#undef k -#undef s - } - // round 2 - for (int i=0; i<16; i++) - { -#define k MD5_round2[i].k -#define s MD5_round2[i].s - a = b + rotate_left(a + G(b,c,d) + X[k] + T[i+0x10], s); -#undef k -#undef s - } - // round 3 - for (int i=0; i<16; i++) - { -#define k MD5_round3[i].k -#define s MD5_round3[i].s - a = b + rotate_left(a + H(b,c,d) + X[k] + T[i+0x20], s); -#undef k -#undef s - } - // round 4 - for (int i=0; i<16; i++) - { -#define k MD5_round4[i].k -#define s MD5_round4[i].s - a = b + rotate_left(a + I(b,c,d) + X[k] + T[i+0x30], s); -#undef k -#undef s - } - // adjust state based on original - state->val[0] += saved.val[0]; - state->val[1] += saved.val[1]; - state->val[2] += saved.val[2]; - state->val[3] += saved.val[3]; -#undef a -#undef b -#undef c -#undef d -} - -void MD5_to_bin(MD5_state state, uint8_t out[0x10]) -{ - for (int i=0; i<0x10; i++) - out[i] = state.val[i/4] >> 8*(i%4); -} - -static const char hex[] = "0123456789abcdef"; - -void MD5_to_str(MD5_state state, char out[0x21]) -{ - uint8_t bin[16]; - MD5_to_bin(state, bin); - for (int i=0; i<0x10; i++) - out[2*i] = hex[bin[i] >> 4], - out[2*i+1] = hex[bin[i] & 0xf]; - out[0x20] = '\0'; -} - -MD5_state MD5_from_string(const char* msg, const size_t msglen) -{ - MD5_state state; - MD5_init(&state); - MD5_block block; - size_t rem = msglen; - while (rem >= 64) - { - for (int i=0; i<0x10; i++) - X[i] = msg[4*i+0] | msg[4*i+1]<<8 | msg[4*i+2]<<16 | msg[4*i+3]<<24; - MD5_do_block(&state, block); - msg += 64; - rem -= 64; - } - // now pad 1-512 bits + the 64-bit length - may be two blocks - uint8_t buf[0x40] = {}; - memcpy (buf, msg, rem); - buf[rem] = 0x80; // a single one bit - if (64 - rem > 8) - { - for (int i=0; i<8; i++) - buf[0x38+i] = ((uint64_t)msglen*8) >> (i*8); - } - for (int i=0; i<0x10; i++) - X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; - MD5_do_block(&state, block); - if (64 - rem <= 8) - { - memset(buf,'\0', 0x38); - for (int i=0; i<8; i++) - buf[0x38+i] = ((uint64_t)msglen*8) >> (i*8); - for (int i=0; i<0x10; i++) - X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; - MD5_do_block(&state, block); - } - return state; -} - -// This could be reimplemented without the strlen() -MD5_state MD5_from_cstring(const char* msg) -{ - return MD5_from_string(msg, strlen(msg)); -} - -MD5_state MD5_from_FILE(FILE* in) { - uint64_t total_len = 0; - - uint8_t buf[0x40]; - uint8_t block_len = 0; - - MD5_state state; - MD5_init(&state); - - MD5_block block; - - while (true) - { - size_t rv = fread(buf + block_len, 1, 0x40 - block_len, in); - if (!rv) - break; - total_len += 8*rv; // in bits - block_len += rv; - if (block_len != 0x40) - continue; - for (int i=0; i<0x10; i++) - X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; - MD5_do_block(&state, block); - block_len = 0; - } - // no more input, just pad and append the length - buf[block_len] = 0x80; - memset(buf + block_len + 1, '\0', 0x40 - block_len - 1); - if (block_len < 0x38) - { - for (int i=0; i<8; i++) - buf[0x38+i] = total_len >> i*8; - } - for (int i=0; i<0x10; i++) - X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; - MD5_do_block(&state, block); - if (0x38 <= block_len) - { - memset(buf, '\0', 0x38); - for (int i=0; i<8; i++) - buf[0x38+i] = total_len >> i*8; - for (int i=0; i<0x10; i++) - X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; - MD5_do_block(&state, block); - } - return state; -} - - -// Hash a password with a salt. -// Whoever wrote this FAILS programming -const char *MD5_saltcrypt(const char *key, const char *salt) -{ - char buf[65]; - - // hash the key then the salt - // buf ends up as a 64-char NUL-terminated string - MD5_to_str(MD5_from_cstring(key), buf); - MD5_to_str(MD5_from_cstring(salt), buf+32); - - // Hash the buffer back into sbuf - this is stupid - // (luckily, putting the result into itself is safe) - MD5_to_str(MD5_from_cstring(buf), buf+32); - - static char obuf[33]; - // This truncates the string, but we have to keep it like that for compatibility - snprintf(obuf, 32, "!%s$%s", salt, buf+32); - return obuf; -} - -const char *make_salt(void) { - static char salt[6]; - for (int i=0; i<5; i++) - salt[i] = MPRAND(48, 78); - return salt; -} - -bool pass_ok(const char *password, const char *crypted) { - char buf[40]; - strncpy(buf, crypted, 40); - char *salt = buf + 1; - *strchr(salt, '$') = '\0'; - - return !strcmp(crypted, MD5_saltcrypt(password, salt)); -} - -// [M|h]ashes up an IP address and a secret key -// to return a hopefully unique masked IP. -in_addr_t MD5_ip(char *secret, in_addr_t ip) -{ - char ipbuf[32]; - uint8_t obuf[16]; - union { - struct bytes { - uint8_t b1; - uint8_t b2; - uint8_t b3; - uint8_t b4; - } bytes; - in_addr_t ip; - } conv; - - // MD5sum a secret + the IP address - memset(&ipbuf, 0, sizeof(ipbuf)); - snprintf(ipbuf, sizeof(ipbuf), "%lu%s", (unsigned long)ip, secret); - /// TODO stop it from being a cstring - MD5_to_bin(MD5_from_cstring(ipbuf), obuf); - - // Fold the md5sum to 32 bits, pack the bytes to an in_addr_t - conv.bytes.b1 = obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]; - conv.bytes.b2 = obuf[2] ^ obuf[3] ^ obuf[10] ^ obuf[11]; - conv.bytes.b3 = obuf[4] ^ obuf[5] ^ obuf[12] ^ obuf[13]; - conv.bytes.b4 = obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]; - - return conv.ip; -} diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp new file mode 100644 index 0000000..f00861d --- /dev/null +++ b/src/common/md5calc.cpp @@ -0,0 +1,339 @@ +#include "md5calc.hpp" +#include <string.h> +#include "mt_rand.hpp" + +// auxilary data +/* +sin() constant table +# Reformatted output of: +echo 'scale=40; obase=16; for (i=1;i<=64;i++) print 2^32 * sin(i), "\n"' | +bc | sed 's/^-//;s/^/0x/;s/\..*$/,/' +*/ +static const uint32_t T[64] = +{ + // used by round 1 + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 + // used by round 2 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, //20 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 + // used by round 3 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, //40 + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 + // used by round 4 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, //60 +}; + +// auxilary functions +// note - the RFC defines these by non-CS conventions: or=v, and=(empty) +static inline uint32_t rotate_left(uint32_t val, unsigned shift) +{ + return val << shift | val >> (32-shift); +} + +static inline uint32_t F(uint32_t X, uint32_t Y, uint32_t Z) +{ + return (X & Y) | (~X & Z); +} +static inline uint32_t G(uint32_t X, uint32_t Y, uint32_t Z) +{ + return (X & Z) | (Y & ~Z); +} +static inline uint32_t H(uint32_t X, uint32_t Y, uint32_t Z) +{ + return X ^ Y ^ Z; +} +static inline uint32_t I(uint32_t X, uint32_t Y, uint32_t Z) +{ + return Y ^ (X | ~Z); +} + +static const struct +{ + uint8_t k : 4; + uint8_t : 0; + uint8_t s : 5; +// uint8_t i : 6; just increments constantly, from 1 .. 64 over all rounds +} +MD5_round1[16] = +{ + { 0, 7}, { 1, 12}, { 2, 17}, { 3, 22}, + { 4, 7}, { 5, 12}, { 6, 17}, { 7, 22}, + { 8, 7}, { 9, 12}, {10, 17}, {11, 22}, + {12, 7}, {13, 12}, {14, 17}, {15, 22}, +}, +MD5_round2[16] = +{ + { 1, 5}, { 6, 9}, {11, 14}, { 0, 20}, + { 5, 5}, {10, 9}, {15, 14}, { 4, 20}, + { 9, 5}, {14, 9}, { 3, 14}, { 8, 20}, + {13, 5}, { 2, 9}, { 7, 14}, {12, 20}, +}, +MD5_round3[16] = +{ + { 5, 4}, { 8, 11}, {11, 16}, {14, 23}, + { 1, 4}, { 4, 11}, { 7, 16}, {10, 23}, + {13, 4}, { 0, 11}, { 3, 16}, { 6, 23}, + { 9, 4}, {12, 11}, {15, 16}, { 2, 23}, +}, +MD5_round4[16] = +{ + { 0, 6}, { 7, 10}, {14, 15}, { 5, 21}, + {12, 6}, { 3, 10}, {10, 15}, { 1, 21}, + { 8, 6}, {15, 10}, { 6, 15}, {13, 21}, + { 4, 6}, {11, 10}, { 2, 15}, { 9, 21}, +}; + + +void MD5_init(MD5_state* state) +{ + // in the RFC, these are specified as bytes, interpreted as little-endian + state->val[0] = 0x67452301; + state->val[1] = 0xEFCDAB89; + state->val[2] = 0x98BADCFE; + state->val[3] = 0x10325476; +} + +void MD5_do_block(MD5_state* state, MD5_block block) +{ +#define X block.data +#define a state->val[(16-i)%4] +#define b state->val[(17-i)%4] +#define c state->val[(18-i)%4] +#define d state->val[(19-i)%4] + // save the values + const MD5_state saved = *state; + // round 1 + for (int i=0; i<16; i++) + { +#define k MD5_round1[i].k +#define s MD5_round1[i].s + a = b + rotate_left(a + F(b,c,d) + X[k] + T[i+0x0], s); +#undef k +#undef s + } + // round 2 + for (int i=0; i<16; i++) + { +#define k MD5_round2[i].k +#define s MD5_round2[i].s + a = b + rotate_left(a + G(b,c,d) + X[k] + T[i+0x10], s); +#undef k +#undef s + } + // round 3 + for (int i=0; i<16; i++) + { +#define k MD5_round3[i].k +#define s MD5_round3[i].s + a = b + rotate_left(a + H(b,c,d) + X[k] + T[i+0x20], s); +#undef k +#undef s + } + // round 4 + for (int i=0; i<16; i++) + { +#define k MD5_round4[i].k +#define s MD5_round4[i].s + a = b + rotate_left(a + I(b,c,d) + X[k] + T[i+0x30], s); +#undef k +#undef s + } + // adjust state based on original + state->val[0] += saved.val[0]; + state->val[1] += saved.val[1]; + state->val[2] += saved.val[2]; + state->val[3] += saved.val[3]; +#undef a +#undef b +#undef c +#undef d +} + +void MD5_to_bin(MD5_state state, uint8_t out[0x10]) +{ + for (int i=0; i<0x10; i++) + out[i] = state.val[i/4] >> 8*(i%4); +} + +static const char hex[] = "0123456789abcdef"; + +void MD5_to_str(MD5_state state, char out[0x21]) +{ + uint8_t bin[16]; + MD5_to_bin(state, bin); + for (int i=0; i<0x10; i++) + out[2*i] = hex[bin[i] >> 4], + out[2*i+1] = hex[bin[i] & 0xf]; + out[0x20] = '\0'; +} + +MD5_state MD5_from_string(const char* msg, const size_t msglen) +{ + MD5_state state; + MD5_init(&state); + MD5_block block; + size_t rem = msglen; + while (rem >= 64) + { + for (int i=0; i<0x10; i++) + X[i] = msg[4*i+0] | msg[4*i+1]<<8 | msg[4*i+2]<<16 | msg[4*i+3]<<24; + MD5_do_block(&state, block); + msg += 64; + rem -= 64; + } + // now pad 1-512 bits + the 64-bit length - may be two blocks + uint8_t buf[0x40] = {}; + memcpy (buf, msg, rem); + buf[rem] = 0x80; // a single one bit + if (64 - rem > 8) + { + for (int i=0; i<8; i++) + buf[0x38+i] = ((uint64_t)msglen*8) >> (i*8); + } + for (int i=0; i<0x10; i++) + X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; + MD5_do_block(&state, block); + if (64 - rem <= 8) + { + memset(buf,'\0', 0x38); + for (int i=0; i<8; i++) + buf[0x38+i] = ((uint64_t)msglen*8) >> (i*8); + for (int i=0; i<0x10; i++) + X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; + MD5_do_block(&state, block); + } + return state; +} + +// This could be reimplemented without the strlen() +MD5_state MD5_from_cstring(const char* msg) +{ + return MD5_from_string(msg, strlen(msg)); +} + +MD5_state MD5_from_FILE(FILE* in) { + uint64_t total_len = 0; + + uint8_t buf[0x40]; + uint8_t block_len = 0; + + MD5_state state; + MD5_init(&state); + + MD5_block block; + + while (true) + { + size_t rv = fread(buf + block_len, 1, 0x40 - block_len, in); + if (!rv) + break; + total_len += 8*rv; // in bits + block_len += rv; + if (block_len != 0x40) + continue; + for (int i=0; i<0x10; i++) + X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; + MD5_do_block(&state, block); + block_len = 0; + } + // no more input, just pad and append the length + buf[block_len] = 0x80; + memset(buf + block_len + 1, '\0', 0x40 - block_len - 1); + if (block_len < 0x38) + { + for (int i=0; i<8; i++) + buf[0x38+i] = total_len >> i*8; + } + for (int i=0; i<0x10; i++) + X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; + MD5_do_block(&state, block); + if (0x38 <= block_len) + { + memset(buf, '\0', 0x38); + for (int i=0; i<8; i++) + buf[0x38+i] = total_len >> i*8; + for (int i=0; i<0x10; i++) + X[i] = buf[4*i+0] | buf[4*i+1]<<8 | buf[4*i+2]<<16 | buf[4*i+3]<<24; + MD5_do_block(&state, block); + } + return state; +} + + +// Hash a password with a salt. +// Whoever wrote this FAILS programming +const char *MD5_saltcrypt(const char *key, const char *salt) +{ + char buf[65]; + + // hash the key then the salt + // buf ends up as a 64-char NUL-terminated string + MD5_to_str(MD5_from_cstring(key), buf); + MD5_to_str(MD5_from_cstring(salt), buf+32); + + // Hash the buffer back into sbuf - this is stupid + // (luckily, putting the result into itself is safe) + MD5_to_str(MD5_from_cstring(buf), buf+32); + + static char obuf[33]; + // This truncates the string, but we have to keep it like that for compatibility + snprintf(obuf, 32, "!%s$%s", salt, buf+32); + return obuf; +} + +const char *make_salt(void) { + static char salt[6]; + for (int i=0; i<5; i++) + salt[i] = MPRAND(48, 78); + return salt; +} + +bool pass_ok(const char *password, const char *crypted) { + char buf[40]; + strncpy(buf, crypted, 40); + char *salt = buf + 1; + *strchr(salt, '$') = '\0'; + + return !strcmp(crypted, MD5_saltcrypt(password, salt)); +} + +// [M|h]ashes up an IP address and a secret key +// to return a hopefully unique masked IP. +in_addr_t MD5_ip(char *secret, in_addr_t ip) +{ + char ipbuf[32]; + uint8_t obuf[16]; + union { + struct bytes { + uint8_t b1; + uint8_t b2; + uint8_t b3; + uint8_t b4; + } bytes; + in_addr_t ip; + } conv; + + // MD5sum a secret + the IP address + memset(&ipbuf, 0, sizeof(ipbuf)); + snprintf(ipbuf, sizeof(ipbuf), "%lu%s", (unsigned long)ip, secret); + /// TODO stop it from being a cstring + MD5_to_bin(MD5_from_cstring(ipbuf), obuf); + + // Fold the md5sum to 32 bits, pack the bytes to an in_addr_t + conv.bytes.b1 = obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]; + conv.bytes.b2 = obuf[2] ^ obuf[3] ^ obuf[10] ^ obuf[11]; + conv.bytes.b3 = obuf[4] ^ obuf[5] ^ obuf[12] ^ obuf[13]; + conv.bytes.b4 = obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]; + + return conv.ip; +} diff --git a/src/common/md5calc.h b/src/common/md5calc.h deleted file mode 100644 index b864791..0000000 --- a/src/common/md5calc.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef MD5CALC_H -#define MD5CALC_H - -#include "sanity.h" - -#include <netinet/in.h> - -#include <stdint.h> // uint32_t, uint8_t -#include <stddef.h> // size_t -#include <stdio.h> // FILE* - -/// The digest state - becomes the output -typedef struct -{ - // classically named {A,B,C,D} - // but use an so we can index - uint32_t val[4]; -} MD5_state; -typedef struct -{ - uint32_t data[16]; -} MD5_block; - -// Implementation -void MD5_init(MD5_state* state); -void MD5_do_block(MD5_state* state, MD5_block block); - -// Output formatting -void MD5_to_bin(MD5_state state, uint8_t out[0x10]); -void MD5_to_str(MD5_state state, char out[0x21]); - -// Convenience -MD5_state MD5_from_string(const char* msg, const size_t msglen); -MD5_state MD5_from_cstring(const char* msg); -MD5_state MD5_from_FILE(FILE* in); - - -/// Output in ASCII - with lowercase hex digits, null-terminated -// these may overlap safely -static void MD5_String (const char *string, char output[33]) __attribute__((deprecated)); -static inline void MD5_String (const char *string, char output[33]) { - MD5_to_str(MD5_from_cstring(string), output); -} -/// Output in binary -static void MD5_String2binary (const char *string, uint8_t output[16]) __attribute__((deprecated)); -static inline void MD5_String2binary (const char *string, uint8_t output[16]) { - MD5_to_bin(MD5_from_cstring(string), output); -} - -// statically-allocated output -// whoever wrote this fails basic understanding of -const char *MD5_saltcrypt(const char *key, const char *salt); - -/// return some random characters (statically allocated) -// Currently, returns a 5-char string -const char *make_salt(void); - -/// check plaintext password against saved saltcrypt -bool pass_ok(const char *password, const char *crypted); - -/// This returns an in_addr_t because it is configurable whether it gets called at all -in_addr_t MD5_ip(char *secret, in_addr_t ip); - -#endif diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp new file mode 100644 index 0000000..1dde2ed --- /dev/null +++ b/src/common/md5calc.hpp @@ -0,0 +1,64 @@ +#ifndef MD5CALC_HPP +#define MD5CALC_HPP + +#include "sanity.hpp" + +#include <netinet/in.h> + +#include <stdint.h> // uint32_t, uint8_t +#include <stddef.h> // size_t +#include <stdio.h> // FILE* + +/// The digest state - becomes the output +typedef struct +{ + // classically named {A,B,C,D} + // but use an so we can index + uint32_t val[4]; +} MD5_state; +typedef struct +{ + uint32_t data[16]; +} MD5_block; + +// Implementation +void MD5_init(MD5_state* state); +void MD5_do_block(MD5_state* state, MD5_block block); + +// Output formatting +void MD5_to_bin(MD5_state state, uint8_t out[0x10]); +void MD5_to_str(MD5_state state, char out[0x21]); + +// Convenience +MD5_state MD5_from_string(const char* msg, const size_t msglen); +MD5_state MD5_from_cstring(const char* msg); +MD5_state MD5_from_FILE(FILE* in); + + +/// Output in ASCII - with lowercase hex digits, null-terminated +// these may overlap safely +static void MD5_String (const char *string, char output[33]) __attribute__((deprecated)); +static inline void MD5_String (const char *string, char output[33]) { + MD5_to_str(MD5_from_cstring(string), output); +} +/// Output in binary +static void MD5_String2binary (const char *string, uint8_t output[16]) __attribute__((deprecated)); +static inline void MD5_String2binary (const char *string, uint8_t output[16]) { + MD5_to_bin(MD5_from_cstring(string), output); +} + +// statically-allocated output +// whoever wrote this fails basic understanding of +const char *MD5_saltcrypt(const char *key, const char *salt); + +/// return some random characters (statically allocated) +// Currently, returns a 5-char string +const char *make_salt(void); + +/// check plaintext password against saved saltcrypt +bool pass_ok(const char *password, const char *crypted); + +/// This returns an in_addr_t because it is configurable whether it gets called at all +in_addr_t MD5_ip(char *secret, in_addr_t ip); + +#endif diff --git a/src/common/mmo.h b/src/common/mmo.h deleted file mode 100644 index 64e0523..0000000 --- a/src/common/mmo.h +++ /dev/null @@ -1,283 +0,0 @@ -/// Global structures and defines -#ifndef MMO_H -#define MMO_H - -# include <time.h> -# include "utils.h" // LCCWIN32 - -# define FIFOSIZE_SERVERLINK 256*1024 - -// set to 0 to not check IP of player between each server. -// set to another value if you want to check (1) -# define CMP_AUTHFIFO_IP 1 - -# define CMP_AUTHFIFO_LOGIN2 1 - -# define MAX_MAP_PER_SERVER 512 -# define MAX_INVENTORY 100 -# define MAX_AMOUNT 30000 -# define MAX_ZENY 1000000000 // 1G zeny -# define MAX_CART 100 -# define MAX_SKILL 450 -# define GLOBAL_REG_NUM 96 -# define ACCOUNT_REG_NUM 16 -# define ACCOUNT_REG2_NUM 16 -# define DEFAULT_WALK_SPEED 150 -# define MIN_WALK_SPEED 0 -# define MAX_WALK_SPEED 1000 -# define MAX_STORAGE 300 -# define MAX_GUILD_STORAGE 1000 -# define MAX_PARTY 12 -# define MAX_GUILD 120 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW] -# define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] -# define MAX_GUILDEXPLUSION 32 -# define MAX_GUILDALLIANCE 16 -# define MAX_GUILDSKILL 8 -# define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] -# define MAX_GUILDLEVEL 50 - -# define MIN_HAIR_STYLE battle_config.min_hair_style -# define MAX_HAIR_STYLE battle_config.max_hair_style -# define MIN_HAIR_COLOR battle_config.min_hair_color -# define MAX_HAIR_COLOR battle_config.max_hair_color -# define MIN_CLOTH_COLOR battle_config.min_cloth_color -# define MAX_CLOTH_COLOR battle_config.max_cloth_color - -// for produce -# define MIN_ATTRIBUTE 0 -# define MAX_ATTRIBUTE 4 -# define ATTRIBUTE_NORMAL 0 -# define MIN_STAR 0 -# define MAX_STAR 3 - -# define MIN_PORTAL_MEMO 0 -# define MAX_PORTAL_MEMO 2 - -# define MAX_STATUS_TYPE 5 - -# define CHAR_CONF_NAME "conf/char_athena.conf" - -struct item -{ - int id; - short nameid; - short amount; - unsigned short equip; - char identify; - char refine; - char attribute; - short card[4]; - short broken; -}; - -struct point -{ - char map[24]; - short x, y; -}; - -struct skill -{ - unsigned short id, lv, flags; -}; - -struct global_reg -{ - char str[32]; - int value; -}; - -struct mmo_charstatus -{ - int char_id; - int account_id; - int partner_id; - - int base_exp, job_exp, zeny; - - short pc_class; - short status_point, skill_point; - int hp, max_hp, sp, max_sp; - short option, karma, manner; - short hair, hair_color, clothes_color; - int party_id, guild_id; - - short weapon, shield; - short head_top, head_mid, head_bottom; - - char name[24]; - unsigned char base_level, job_level; - short str, agi, vit, int_, dex, luk; - unsigned char char_num, sex; - - unsigned long mapip; - unsigned int mapport; - - struct point last_point, save_point, memo_point[10]; - struct item inventory[MAX_INVENTORY], cart[MAX_CART]; - struct skill skill[MAX_SKILL]; - int global_reg_num; - struct global_reg global_reg[GLOBAL_REG_NUM]; - int account_reg_num; - struct global_reg account_reg[ACCOUNT_REG_NUM]; - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -}; - -struct storage -{ - int dirty; - int account_id; - short storage_status; - short storage_amount; - struct item storage_[MAX_STORAGE]; -}; - -struct guild_storage -{ - int dirty; - int guild_id; - short storage_status; - short storage_amount; - struct item storage_[MAX_GUILD_STORAGE]; -}; - -struct map_session_data; - -struct gm_account -{ - int account_id; - int level; -}; - -struct party_member -{ - int account_id; - char name[24], map[24]; - int leader, online, lv; - struct map_session_data *sd; -}; - -struct party -{ - int party_id; - char name[24]; - int exp; - int item; - struct party_member member[MAX_PARTY]; -}; - -struct guild_member -{ - int account_id, char_id; - short hair, hair_color, gender, pc_class, lv; - int exp, exp_payper; - short online, position; - int rsv1, rsv2; - char name[24]; - struct map_session_data *sd; -}; - -struct guild_position -{ - char name[24]; - int mode; - int exp_mode; -}; - -struct guild_alliance -{ - int opposition; - int guild_id; - char name[24]; -}; - -struct guild_explusion -{ - char name[24]; - char mes[40]; - char acc[40]; - int account_id; - int rsv1, rsv2, rsv3; -}; - -struct guild_skill -{ - int id, lv; -}; - -struct guild -{ - int guild_id; - short guild_lv, connect_member, max_member, average_lv; - int exp, next_exp, skill_point, castle_id; - char name[24], master[24]; - struct guild_member member[MAX_GUILD]; - struct guild_position position[MAX_GUILDPOSITION]; - char mes1[60], mes2[120]; - int emblem_len, emblem_id; - char emblem_data[2048]; - struct guild_alliance alliance[MAX_GUILDALLIANCE]; - struct guild_explusion explusion[MAX_GUILDEXPLUSION]; - struct guild_skill skill[MAX_GUILDSKILL]; -}; - -struct guild_castle -{ - int castle_id; - char map_name[24]; - char castle_name[24]; - char castle_event[24]; - int guild_id; - int economy; - int defense; - int triggerE; - int triggerD; - int nextTime; - int payTime; - int createTime; - int visibleC; - int visibleG0; - int visibleG1; - int visibleG2; - int visibleG3; - int visibleG4; - int visibleG5; - int visibleG6; - int visibleG7; - int Ghp0; // added Guardian HP [Valaris] - int Ghp1; - int Ghp2; - int Ghp3; - int Ghp4; - int Ghp5; - int Ghp6; - int Ghp7; - int GID0; - int GID1; - int GID2; - int GID3; - int GID4; - int GID5; - int GID6; - int GID7; // end addition [Valaris] -}; -struct square -{ - int val1[5]; - int val2[5]; -}; - -enum -{ - GBI_EXP = 1, // ギルドのEXP - GBI_GUILDLV = 2, // ギルドのLv - GBI_SKILLPOINT = 3, // ギルドのスキルポイント - GBI_SKILLLV = 4, // ギルドスキルLv - - GMI_POSITION = 0, // メンバーの役職変更 - GMI_EXP = 1, // メンバーのEXP - -}; - -#endif // MMO_H diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp new file mode 100644 index 0000000..2ca93b0 --- /dev/null +++ b/src/common/mmo.hpp @@ -0,0 +1,283 @@ +/// Global structures and defines +#ifndef MMO_HPP +#define MMO_HPP + +# include <time.h> +# include "utils.hpp" // LCCWIN32 + +# define FIFOSIZE_SERVERLINK 256*1024 + +// set to 0 to not check IP of player between each server. +// set to another value if you want to check (1) +# define CMP_AUTHFIFO_IP 1 + +# define CMP_AUTHFIFO_LOGIN2 1 + +# define MAX_MAP_PER_SERVER 512 +# define MAX_INVENTORY 100 +# define MAX_AMOUNT 30000 +# define MAX_ZENY 1000000000 // 1G zeny +# define MAX_CART 100 +# define MAX_SKILL 450 +# define GLOBAL_REG_NUM 96 +# define ACCOUNT_REG_NUM 16 +# define ACCOUNT_REG2_NUM 16 +# define DEFAULT_WALK_SPEED 150 +# define MIN_WALK_SPEED 0 +# define MAX_WALK_SPEED 1000 +# define MAX_STORAGE 300 +# define MAX_GUILD_STORAGE 1000 +# define MAX_PARTY 12 +# define MAX_GUILD 120 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW] +# define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] +# define MAX_GUILDEXPLUSION 32 +# define MAX_GUILDALLIANCE 16 +# define MAX_GUILDSKILL 8 +# define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] +# define MAX_GUILDLEVEL 50 + +# define MIN_HAIR_STYLE battle_config.min_hair_style +# define MAX_HAIR_STYLE battle_config.max_hair_style +# define MIN_HAIR_COLOR battle_config.min_hair_color +# define MAX_HAIR_COLOR battle_config.max_hair_color +# define MIN_CLOTH_COLOR battle_config.min_cloth_color +# define MAX_CLOTH_COLOR battle_config.max_cloth_color + +// for produce +# define MIN_ATTRIBUTE 0 +# define MAX_ATTRIBUTE 4 +# define ATTRIBUTE_NORMAL 0 +# define MIN_STAR 0 +# define MAX_STAR 3 + +# define MIN_PORTAL_MEMO 0 +# define MAX_PORTAL_MEMO 2 + +# define MAX_STATUS_TYPE 5 + +# define CHAR_CONF_NAME "conf/char_athena.conf" + +struct item +{ + int id; + short nameid; + short amount; + unsigned short equip; + char identify; + char refine; + char attribute; + short card[4]; + short broken; +}; + +struct point +{ + char map[24]; + short x, y; +}; + +struct skill +{ + unsigned short id, lv, flags; +}; + +struct global_reg +{ + char str[32]; + int value; +}; + +struct mmo_charstatus +{ + int char_id; + int account_id; + int partner_id; + + int base_exp, job_exp, zeny; + + short pc_class; + short status_point, skill_point; + int hp, max_hp, sp, max_sp; + short option, karma, manner; + short hair, hair_color, clothes_color; + int party_id, guild_id; + + short weapon, shield; + short head_top, head_mid, head_bottom; + + char name[24]; + unsigned char base_level, job_level; + short str, agi, vit, int_, dex, luk; + unsigned char char_num, sex; + + unsigned long mapip; + unsigned int mapport; + + struct point last_point, save_point, memo_point[10]; + struct item inventory[MAX_INVENTORY], cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; + int account_reg_num; + struct global_reg account_reg[ACCOUNT_REG_NUM]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +}; + +struct storage +{ + int dirty; + int account_id; + short storage_status; + short storage_amount; + struct item storage_[MAX_STORAGE]; +}; + +struct guild_storage +{ + int dirty; + int guild_id; + short storage_status; + short storage_amount; + struct item storage_[MAX_GUILD_STORAGE]; +}; + +struct map_session_data; + +struct gm_account +{ + int account_id; + int level; +}; + +struct party_member +{ + int account_id; + char name[24], map[24]; + int leader, online, lv; + struct map_session_data *sd; +}; + +struct party +{ + int party_id; + char name[24]; + int exp; + int item; + struct party_member member[MAX_PARTY]; +}; + +struct guild_member +{ + int account_id, char_id; + short hair, hair_color, gender, pc_class, lv; + int exp, exp_payper; + short online, position; + int rsv1, rsv2; + char name[24]; + struct map_session_data *sd; +}; + +struct guild_position +{ + char name[24]; + int mode; + int exp_mode; +}; + +struct guild_alliance +{ + int opposition; + int guild_id; + char name[24]; +}; + +struct guild_explusion +{ + char name[24]; + char mes[40]; + char acc[40]; + int account_id; + int rsv1, rsv2, rsv3; +}; + +struct guild_skill +{ + int id, lv; +}; + +struct guild +{ + int guild_id; + short guild_lv, connect_member, max_member, average_lv; + int exp, next_exp, skill_point, castle_id; + char name[24], master[24]; + struct guild_member member[MAX_GUILD]; + struct guild_position position[MAX_GUILDPOSITION]; + char mes1[60], mes2[120]; + int emblem_len, emblem_id; + char emblem_data[2048]; + struct guild_alliance alliance[MAX_GUILDALLIANCE]; + struct guild_explusion explusion[MAX_GUILDEXPLUSION]; + struct guild_skill skill[MAX_GUILDSKILL]; +}; + +struct guild_castle +{ + int castle_id; + char map_name[24]; + char castle_name[24]; + char castle_event[24]; + int guild_id; + int economy; + int defense; + int triggerE; + int triggerD; + int nextTime; + int payTime; + int createTime; + int visibleC; + int visibleG0; + int visibleG1; + int visibleG2; + int visibleG3; + int visibleG4; + int visibleG5; + int visibleG6; + int visibleG7; + int Ghp0; // added Guardian HP [Valaris] + int Ghp1; + int Ghp2; + int Ghp3; + int Ghp4; + int Ghp5; + int Ghp6; + int Ghp7; + int GID0; + int GID1; + int GID2; + int GID3; + int GID4; + int GID5; + int GID6; + int GID7; // end addition [Valaris] +}; +struct square +{ + int val1[5]; + int val2[5]; +}; + +enum +{ + GBI_EXP = 1, // ギルドのEXP + GBI_GUILDLV = 2, // ギルドのLv + GBI_SKILLPOINT = 3, // ギルドのスキルポイント + GBI_SKILLLV = 4, // ギルドスキルLv + + GMI_POSITION = 0, // メンバーの役職変更 + GMI_EXP = 1, // メンバーのEXP + +}; + +#endif // MMO_HPP diff --git a/src/common/mt_rand.c b/src/common/mt_rand.c deleted file mode 100644 index e4e8d12..0000000 --- a/src/common/mt_rand.c +++ /dev/null @@ -1,116 +0,0 @@ -/* -// This is the ``Mersenne Twister'' random number generator MT19937, which -// generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) -// starting from any odd seed in 0..(2^32 - 1). This version is a recode -// by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by -// Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in -// July-August 1997). -// -// Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha -// running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to -// generate 300 million random numbers; after recoding: 24.0 sec. for the same -// (i.e., 46.5% of original time), so speed is now about 12.5 million random -// number generations per second on this machine. -// -// According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html> -// (and paraphrasing a bit in places), the Mersenne Twister is ``designed -// with consideration of the flaws of various existing generators,'' has -// a period of 2^19937 - 1, gives a sequence that is 623-dimensionally -// equidistributed, and ``has passed many stringent tests, including the -// die-hard test of G. Marsaglia and the load test of P. Hellekalek and -// S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 -// to 5012 bytes of static data, depending on data type sizes, and the code -// is quite short as well). It generates random numbers in batches of 624 -// at a time, so the caching and pipelining of modern systems is exploited. -// It is also divide- and mod-free. -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Library General Public License as published by -// the Free Software Foundation (either version 2 of the License or, at your -// option, any later version). This library is distributed in the hope that -// it will be useful, but WITHOUT ANY WARRANTY, without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See -// the GNU Library General Public License for more details. You should have -// received a copy of the GNU Library General Public License along with this -// library; if not, write to the Free Software Foundation, Inc., 59 Temple -// Place, Suite 330, Boston, MA 02111-1307, USA. -// -// The code as Shawn received it included the following notice: -// -// Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When -// you use this, send an e-mail to <matumoto@math.keio.ac.jp> with -// an appropriate reference to your work. -// -// It would be nice to CC: <Cokus@math.washington.edu> when you write. -// -*/ - -#include <time.h> -#include "mt_rand.h" - -#define N 624 // length of state vector -#define M 397 // a period parameter -#define K 0x9908B0DFU // a magic constant - -#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u -#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u -#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u -#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v - -static uint32_t state[N+1]; // state vector the +1 is needed due to the coding -static uint32_t *next; // next random value is computed from here -static int left = -1; // can *next++ this many times before reloading - -void mt_seed (uint32_t seed) -{ - uint32_t x = seed | 1U; - uint32_t *s = state; - left = 0; - - for (int j = N; *s++ = x, --j; x *= 69069U); -} - -void mt_reload (void) -{ - // if mt_seed has never been called - if (left < -1) - mt_seed (time (NULL)); - - // conceptually, these are indices into the state that wrap - uint32_t *p0 = state; - uint32_t *p2 = state + 2; - uint32_t *pM = state + M; - - uint32_t s0 = state[0]; - uint32_t s1 = state[1]; - - // regenerate the lower N-M elements of the state - for (int j = N-M+1; --j != 0; s0 = s1, s1 = *p2++) - *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); - - pM = state; - // regenerate the next M-1 elements of the state - // note that s1 is set to state[N] at the end, but discarded - for (int j = M; --j != 0; s0 = s1, s1 = *p2++) - *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); - - // regenerate the last 1 element of the state - s1 = state[0]; - *p0 = *pM ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); - - // ready for the normal mt_random algorithm - left = N; - next = state; -} - -uint32_t mt_random (void) -{ - if (--left < 0) - mt_reload (); - - uint32_t y = *next++; - y ^= (y >> 11); - y ^= (y << 7) & 0x9D2C5680U; - y ^= (y << 15) & 0xEFC60000U; - return y ^ (y >> 18); -} diff --git a/src/common/mt_rand.cpp b/src/common/mt_rand.cpp new file mode 100644 index 0000000..89872ad --- /dev/null +++ b/src/common/mt_rand.cpp @@ -0,0 +1,116 @@ +/* +// This is the ``Mersenne Twister'' random number generator MT19937, which +// generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) +// starting from any odd seed in 0..(2^32 - 1). This version is a recode +// by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by +// Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in +// July-August 1997). +// +// Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha +// running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to +// generate 300 million random numbers; after recoding: 24.0 sec. for the same +// (i.e., 46.5% of original time), so speed is now about 12.5 million random +// number generations per second on this machine. +// +// According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html> +// (and paraphrasing a bit in places), the Mersenne Twister is ``designed +// with consideration of the flaws of various existing generators,'' has +// a period of 2^19937 - 1, gives a sequence that is 623-dimensionally +// equidistributed, and ``has passed many stringent tests, including the +// die-hard test of G. Marsaglia and the load test of P. Hellekalek and +// S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 +// to 5012 bytes of static data, depending on data type sizes, and the code +// is quite short as well). It generates random numbers in batches of 624 +// at a time, so the caching and pipelining of modern systems is exploited. +// It is also divide- and mod-free. +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Library General Public License as published by +// the Free Software Foundation (either version 2 of the License or, at your +// option, any later version). This library is distributed in the hope that +// it will be useful, but WITHOUT ANY WARRANTY, without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +// the GNU Library General Public License for more details. You should have +// received a copy of the GNU Library General Public License along with this +// library; if not, write to the Free Software Foundation, Inc., 59 Temple +// Place, Suite 330, Boston, MA 02111-1307, USA. +// +// The code as Shawn received it included the following notice: +// +// Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When +// you use this, send an e-mail to <matumoto@math.keio.ac.jp> with +// an appropriate reference to your work. +// +// It would be nice to CC: <Cokus@math.washington.edu> when you write. +// +*/ + +#include <time.h> +#include "mt_rand.hpp" + +#define N 624 // length of state vector +#define M 397 // a period parameter +#define K 0x9908B0DFU // a magic constant + +#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u +#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u +#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u +#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v + +static uint32_t state[N+1]; // state vector the +1 is needed due to the coding +static uint32_t *next; // next random value is computed from here +static int left = -1; // can *next++ this many times before reloading + +void mt_seed (uint32_t seed) +{ + uint32_t x = seed | 1U; + uint32_t *s = state; + left = 0; + + for (int j = N; *s++ = x, --j; x *= 69069U); +} + +void mt_reload (void) +{ + // if mt_seed has never been called + if (left < -1) + mt_seed (time (NULL)); + + // conceptually, these are indices into the state that wrap + uint32_t *p0 = state; + uint32_t *p2 = state + 2; + uint32_t *pM = state + M; + + uint32_t s0 = state[0]; + uint32_t s1 = state[1]; + + // regenerate the lower N-M elements of the state + for (int j = N-M+1; --j != 0; s0 = s1, s1 = *p2++) + *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); + + pM = state; + // regenerate the next M-1 elements of the state + // note that s1 is set to state[N] at the end, but discarded + for (int j = M; --j != 0; s0 = s1, s1 = *p2++) + *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); + + // regenerate the last 1 element of the state + s1 = state[0]; + *p0 = *pM ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U); + + // ready for the normal mt_random algorithm + left = N; + next = state; +} + +uint32_t mt_random (void) +{ + if (--left < 0) + mt_reload (); + + uint32_t y = *next++; + y ^= (y >> 11); + y ^= (y << 7) & 0x9D2C5680U; + y ^= (y << 15) & 0xEFC60000U; + return y ^ (y >> 18); +} diff --git a/src/common/mt_rand.h b/src/common/mt_rand.h deleted file mode 100644 index 84d32e5..0000000 --- a/src/common/mt_rand.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef MT_RAND_H -#define MT_RAND_H - -# include "sanity.h" - -/// Initialize the generator (called automatically with time() if you don't) -void mt_seed (uint32_t seed); -/// Get a random number -uint32_t mt_random (void); - -/** - * ModuloRand and ModuloPlusRand - * These macros are used to replace the vast number of calls to rand()%mod - * TODO eliminate the rest of the calls to rand() - * MRAND(10) returns 0..9 - * MPRAND(5,10) returns 5..14 - */ -// The cast is essential because the result is sometimes -// compared with a possibly negative number. -// Because it's using modulus, high numbers shouldn't happen anyway. -# define MRAND(mod) ((int)(mt_random() % (mod))) -# define MPRAND(add, mod) ((add) + MRAND(mod)) - -#endif // MT_RAND_H diff --git a/src/common/mt_rand.hpp b/src/common/mt_rand.hpp new file mode 100644 index 0000000..c7bae4e --- /dev/null +++ b/src/common/mt_rand.hpp @@ -0,0 +1,24 @@ +#ifndef MT_RAND_HPP +#define MT_RAND_HPP + +# include "sanity.hpp" + +/// Initialize the generator (called automatically with time() if you don't) +void mt_seed (uint32_t seed); +/// Get a random number +uint32_t mt_random (void); + +/** + * ModuloRand and ModuloPlusRand + * These macros are used to replace the vast number of calls to rand()%mod + * TODO eliminate the rest of the calls to rand() + * MRAND(10) returns 0..9 + * MPRAND(5,10) returns 5..14 + */ +// The cast is essential because the result is sometimes +// compared with a possibly negative number. +// Because it's using modulus, high numbers shouldn't happen anyway. +# define MRAND(mod) ((int)(mt_random() % (mod))) +# define MPRAND(add, mod) ((add) + MRAND(mod)) + +#endif // MT_RAND_HPP diff --git a/src/common/nullpo.c b/src/common/nullpo.c deleted file mode 100644 index 8c7c405..0000000 --- a/src/common/nullpo.c +++ /dev/null @@ -1,66 +0,0 @@ -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include "nullpo.h" - -static void nullpo_info_core (const char *file, int line, const char *func, - const char *fmt, va_list ap); - -/// Null check and print format -bool nullpo_chk_f (const char *file, int line, const char *func, - const void *target, const char *fmt, ...) -{ - va_list ap; - - if (target) - return 0; - - va_start (ap, fmt); - nullpo_info_core (file, line, func, fmt, ap); - va_end (ap); - return 1; -} -bool nullpo_chk (const char *file, int line, const char *func, - const void *target) -{ - if (target) - return 0; - - nullpo_info_core (file, line, func, NULL, NULL); - return 1; -} - -/// External functions -void nullpo_info_f (const char *file, int line, const char *func, - const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - nullpo_info_core (file, line, func, fmt, ap); - va_end (ap); -} -void nullpo_info (const char *file, int line, const char *func) -{ - nullpo_info_core (file, line, func, NULL, NULL); -} - -/// Actual output function -static void nullpo_info_core (const char *file, int line, const char *func, - const char *fmt, va_list ap) __attribute__((format(printf, 4, 0))); -static void nullpo_info_core (const char *file, int line, const char *func, - const char *fmt, va_list ap) -{ - if (!file) - file = "??"; - if (!func || !*func) - func = "unknown"; - - fprintf (stderr, "%s:%d: in func `%s': NULL pointer\n", file, line, func); - if (fmt && *fmt) - { - vfprintf (stderr, fmt, ap); - if (fmt[strlen (fmt) - 1] != '\n') - fputc('\n', stderr); - } -} diff --git a/src/common/nullpo.cpp b/src/common/nullpo.cpp new file mode 100644 index 0000000..6dd2736 --- /dev/null +++ b/src/common/nullpo.cpp @@ -0,0 +1,66 @@ +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "nullpo.hpp" + +static void nullpo_info_core (const char *file, int line, const char *func, + const char *fmt, va_list ap); + +/// Null check and print format +bool nullpo_chk_f (const char *file, int line, const char *func, + const void *target, const char *fmt, ...) +{ + va_list ap; + + if (target) + return 0; + + va_start (ap, fmt); + nullpo_info_core (file, line, func, fmt, ap); + va_end (ap); + return 1; +} +bool nullpo_chk (const char *file, int line, const char *func, + const void *target) +{ + if (target) + return 0; + + nullpo_info_core (file, line, func, NULL, NULL); + return 1; +} + +/// External functions +void nullpo_info_f (const char *file, int line, const char *func, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + nullpo_info_core (file, line, func, fmt, ap); + va_end (ap); +} +void nullpo_info (const char *file, int line, const char *func) +{ + nullpo_info_core (file, line, func, NULL, NULL); +} + +/// Actual output function +static void nullpo_info_core (const char *file, int line, const char *func, + const char *fmt, va_list ap) __attribute__((format(printf, 4, 0))); +static void nullpo_info_core (const char *file, int line, const char *func, + const char *fmt, va_list ap) +{ + if (!file) + file = "??"; + if (!func || !*func) + func = "unknown"; + + fprintf (stderr, "%s:%d: in func `%s': NULL pointer\n", file, line, func); + if (fmt && *fmt) + { + vfprintf (stderr, fmt, ap); + if (fmt[strlen (fmt) - 1] != '\n') + fputc('\n', stderr); + } +} diff --git a/src/common/nullpo.h b/src/common/nullpo.h deleted file mode 100644 index 9b33b4b..0000000 --- a/src/common/nullpo.h +++ /dev/null @@ -1,61 +0,0 @@ -/// return wrappers for unexpected NULL pointers -#ifndef NULLPO_H -#define NULLPO_H -/// Comment this out to live dangerously -# define NULLPO_CHECK - -/// All functions print to standard error (was: standard output) -/// nullpo_ret(cond) - return 0 if given pointer is NULL -/// nullpo_retv(cond) - just return (function returns void) -/// nullpo_retr(rv, cond) - return given value instead -/// the _f variants take a printf-format string and arguments - -# ifdef NULLPO_CHECK -# define NLP_MARK __FILE__, __LINE__, __func__ -# define nullpo_ret(t) \ - if (nullpo_chk(NLP_MARK, t)) \ - return 0; -# define nullpo_retv(t) \ - if (nullpo_chk(NLP_MARK, t)) \ - return; -# define nullpo_retr(ret, t) \ - if (nullpo_chk(NLP_MARK, t)) \ - return ret; -# define nullpo_ret_f(t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ - return 0; -# define nullpo_retv_f(t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ - return; -# define nullpo_retr_f(ret, t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ - return ret; -# else // NULLPO_CHECK -# define nullpo_ret(t) t; -# define nullpo_retv(t) t; -# define nullpo_retr(ret, t) t; -# define nullpo_ret_f(t, fmt, ...) t; -# define nullpo_retv_f(t, fmt, ...) t; -# define nullpo_retr_f(ret, t, fmt, ...) t; -# endif // NULLPO_CHECK - -# include "sanity.h" - -/// Used by macros in this header -bool nullpo_chk (const char *file, int line, const char *func, - const void *target); - -/// Used by macros in this header -bool nullpo_chk_f (const char *file, int line, const char *func, - const void *target, const char *fmt, ...) - __attribute__ ((format (printf, 5, 6))); - -/// Used only by map/battle.c -void nullpo_info (const char *file, int line, const char *func); - -/// Not used -void nullpo_info_f (const char *file, int line, const char *func, - const char *fmt, ...) - __attribute__ ((format (printf, 4, 5))); - -#endif // NULLPO_H diff --git a/src/common/nullpo.hpp b/src/common/nullpo.hpp new file mode 100644 index 0000000..7aff691 --- /dev/null +++ b/src/common/nullpo.hpp @@ -0,0 +1,61 @@ +/// return wrappers for unexpected NULL pointers +#ifndef NULLPO_HPP +#define NULLPO_HPP +/// Comment this out to live dangerously +# define NULLPO_CHECK + +/// All functions print to standard error (was: standard output) +/// nullpo_ret(cond) - return 0 if given pointer is NULL +/// nullpo_retv(cond) - just return (function returns void) +/// nullpo_retr(rv, cond) - return given value instead +/// the _f variants take a printf-format string and arguments + +# ifdef NULLPO_CHECK +# define NLP_MARK __FILE__, __LINE__, __func__ +# define nullpo_ret(t) \ + if (nullpo_chk(NLP_MARK, t)) \ + return 0; +# define nullpo_retv(t) \ + if (nullpo_chk(NLP_MARK, t)) \ + return; +# define nullpo_retr(ret, t) \ + if (nullpo_chk(NLP_MARK, t)) \ + return ret; +# define nullpo_ret_f(t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ + return 0; +# define nullpo_retv_f(t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ + return; +# define nullpo_retr_f(ret, t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \ + return ret; +# else // NULLPO_CHECK +# define nullpo_ret(t) t; +# define nullpo_retv(t) t; +# define nullpo_retr(ret, t) t; +# define nullpo_ret_f(t, fmt, ...) t; +# define nullpo_retv_f(t, fmt, ...) t; +# define nullpo_retr_f(ret, t, fmt, ...) t; +# endif // NULLPO_CHECK + +# include "sanity.hpp" + +/// Used by macros in this header +bool nullpo_chk (const char *file, int line, const char *func, + const void *target); + +/// Used by macros in this header +bool nullpo_chk_f (const char *file, int line, const char *func, + const void *target, const char *fmt, ...) + __attribute__ ((format (printf, 5, 6))); + +/// Used only by map/battle.c +void nullpo_info (const char *file, int line, const char *func); + +/// Not used +void nullpo_info_f (const char *file, int line, const char *func, + const char *fmt, ...) + __attribute__ ((format (printf, 4, 5))); + +#endif // NULLPO_HPP diff --git a/src/common/sanity.h b/src/common/sanity.h deleted file mode 100644 index 168671f..0000000 --- a/src/common/sanity.h +++ /dev/null @@ -1,36 +0,0 @@ -/// return wrappers for unexpected NULL pointers -#ifndef SANITY_H -#define SANITY_H -# if __STDC_VERSION__ < 199901L -# error "Please compile in C99 mode" -# endif -# if __GNUC__ < 3 -// I don't specifically know what version this requires, -// but GCC 3 was the beginning of modern GCC -# error "Please upgrade your compiler to at least GCC 3" -# endif -# ifndef __i386__ -// Known platform dependencies: -// endianness for the [RW]FIFO.* macros -// possibly, some signal-handling -# error "Unsupported platform" -# endif -# ifdef __x86_64__ -// I'm working on it - I know there are some pointer-size assumptions. -# error "Sorry, this code is believed not to be 64-bit safe" -# error "please compile with -m32" -# endif - -/// A name for unused function arguments - can be repeated -# define UNUSED UNUSED_IMPL(__COUNTER__) -// Don't you just love the hoops the preprocessor makes you go through? -# define UNUSED_IMPL(arg) UNUSED_IMPL2(arg) -# define UNUSED_IMPL2(suffix) unused_ ## suffix __attribute__((unused)) -/// Convert conditions to use the bool type -# include <stdbool.h> -/// Convert type assumptions to use the standard types here -# include <stdint.h> -/// size_t, NULL -# include <stddef.h> - -#endif // SANITY_H diff --git a/src/common/sanity.hpp b/src/common/sanity.hpp new file mode 100644 index 0000000..7ffd077 --- /dev/null +++ b/src/common/sanity.hpp @@ -0,0 +1,31 @@ +/// return wrappers for unexpected NULL pointers +#ifndef SANITY_HPP +#define SANITY_HPP +# ifndef __cplusplus +# error "Please compile in C++ mode" +# endif +# if __GNUC__ < 3 +// I don't specifically know what version this requires, +// but GCC 3 was the beginning of modern GCC +# error "Please upgrade your compiler to at least GCC 3" +# endif +# ifndef __i386__ +// Known platform dependencies: +// endianness for the [RW]FIFO.* macros +// possibly, some signal-handling +# error "Unsupported platform" +# endif +# ifdef __x86_64__ +// I'm working on it - I know there are some pointer-size assumptions. +# error "Sorry, this code is believed not to be 64-bit safe" +# error "please compile with -m32" +# endif + +/// A name for unused function arguments - can be repeated +# define UNUSED /* empty works for C++ */ +/// Convert type assumptions to use the standard types here +# include <cstdint> +/// size_t, NULL +# include <cstddef> + +#endif // SANITY_HPP diff --git a/src/common/socket.c b/src/common/socket.c deleted file mode 100644 index 67a5102..0000000 --- a/src/common/socket.c +++ /dev/null @@ -1,405 +0,0 @@ -// $Id: socket.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ -// original : core.c 2003/02/26 18:03:12 Rev 1.7 - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <errno.h> - -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/time.h> -#include <unistd.h> - -#include <fcntl.h> -#include <string.h> - -#include "mmo.h" // [Valaris] thanks to fov -#include "socket.h" -#include "utils.h" - -fd_set readfds; -int fd_max; -int currentuse; - -const uint32_t RFIFO_SIZE = 65536; -const uint32_t WFIFO_SIZE = 65536; - -struct socket_data *session[FD_SETSIZE]; - -/// Discard all input -static void null_parse (int fd); -/// Default parser for new connections -static void (*default_func_parse) (int) = null_parse; - -void set_defaultparse (void (*defaultparse) (int)) -{ - default_func_parse = defaultparse; -} - -/// Read from socket to the queue -static void recv_to_fifo (int fd) -{ - if (session[fd]->eof) - return; - - ssize_t len = read (fd, session[fd]->rdata + session[fd]->rdata_size, - RFIFOSPACE (fd)); - - if (len > 0) - { - session[fd]->rdata_size += len; - session[fd]->connected = 1; - } - else - { - session[fd]->eof = 1; - } -} - -static void send_from_fifo (int fd) -{ - if (session[fd]->eof) - return; - - ssize_t len = write (fd, session[fd]->wdata, session[fd]->wdata_size); - - if (len > 0) - { - session[fd]->wdata_size -= len; - if (len < (ssize_t)session[fd]->wdata_size) - { - memmove (session[fd]->wdata, session[fd]->wdata + len, - session[fd]->wdata_size); - } - session[fd]->connected = 1; - } - else - { - session[fd]->eof = 1; - } -} - -static void null_parse (int fd) -{ - printf ("null_parse : %d\n", fd); - RFIFOSKIP (fd, RFIFOREST (fd)); -} - - -static void connect_client (int listen_fd) -{ - struct sockaddr_in client_address; - socklen_t len = sizeof (client_address); - - int fd = accept (listen_fd, (struct sockaddr *) &client_address, &len); - if (fd == -1) - { - perror ("accept"); - return; - } - if (fd_max <= fd) - { - fd_max = fd + 1; - } - if (!free_fds ()) - { - fprintf (stderr, "softlimit reached, disconnecting : %d\n", fd); - delete_session (fd); - return; - } - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - FD_SET (fd, &readfds); - - fcntl (fd, F_SETFL, O_NONBLOCK); - - CREATE (session[fd], struct socket_data, 1); - CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); - CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); - - session[fd]->max_rdata = RFIFO_SIZE; - session[fd]->max_wdata = WFIFO_SIZE; - session[fd]->func_recv = recv_to_fifo; - session[fd]->func_send = send_from_fifo; - session[fd]->func_parse = default_func_parse; - session[fd]->client_addr = client_address; - session[fd]->created = time (NULL); - session[fd]->connected = 0; - - currentuse++; -} - -int make_listen_port (uint16_t port) -{ - struct sockaddr_in server_address; - int fd = socket (AF_INET, SOCK_STREAM, 0); - if (fd == -1) - { - perror ("socket"); - return -1; - } - if (fd_max <= fd) - fd_max = fd + 1; - - fcntl (fd, F_SETFL, O_NONBLOCK); - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = htonl (INADDR_ANY); - server_address.sin_port = htons (port); - - if (bind (fd, (struct sockaddr *) &server_address, - sizeof (server_address)) == -1) - { - perror ("bind"); - exit (1); - } - if (listen (fd, 5) == -1) - { /* error */ - perror ("listen"); - exit (1); - } - - FD_SET (fd, &readfds); - - CREATE (session[fd], struct socket_data, 1); - - session[fd]->func_recv = connect_client; - session[fd]->created = time (NULL); - session[fd]->connected = 1; - - currentuse++; - return fd; -} - -int make_connection (uint32_t ip, uint16_t port) -{ - struct sockaddr_in server_address; - int fd = socket (AF_INET, SOCK_STREAM, 0); - if (fd == -1) - { - perror ("socket"); - return -1; - } - if (fd_max <= fd) - fd_max = fd + 1; - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = ip; - server_address.sin_port = htons (port); - - fcntl (fd, F_SETFL, O_NONBLOCK); - - /// Errors not caught - we must not block - /// Let the main select() loop detect when we know the state - connect (fd, (struct sockaddr *) &server_address, - sizeof (struct sockaddr_in)); - - FD_SET (fd, &readfds); - - CREATE (session[fd], struct socket_data, 1); - CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); - CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); - - session[fd]->max_rdata = RFIFO_SIZE; - session[fd]->max_wdata = WFIFO_SIZE; - session[fd]->func_recv = recv_to_fifo; - session[fd]->func_send = send_from_fifo; - session[fd]->func_parse = default_func_parse; - session[fd]->created = time (NULL); - session[fd]->connected = 1; - - currentuse++; - return fd; -} - -void delete_session (int fd) -{ - if (fd < 0 || fd >= FD_SETSIZE) - return; - // If this was the highest fd, decrease it - // We could add a loop to decrement fd_max further for every null session, - // but this is cheap and good enough for the typical case - if (fd == fd_max - 1) - fd_max--; - FD_CLR (fd, &readfds); - if (session[fd]) - { - free (session[fd]->rdata); - free (session[fd]->wdata); - free (session[fd]->session_data); - free (session[fd]); - } - session[fd] = NULL; - - // just close() would try to keep sending buffers - shutdown (fd, SHUT_RDWR); - close (fd); - currentuse--; - if (currentuse < 0) - { - fprintf (stderr, "delete_session: current sessions negative!\n"); - currentuse = 0; - } - return; -} - -void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size) -{ - struct socket_data *s = session[fd]; - if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) - { - RECREATE (s->rdata, uint8_t, rfifo_size); - s->max_rdata = rfifo_size; - } - if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) - { - RECREATE (s->wdata, uint8_t, wfifo_size); - s->max_wdata = wfifo_size; - } -} - -void WFIFOSET (int fd, size_t len) -{ - struct socket_data *s = session[fd]; - if (s->wdata_size + len + 16384 > s->max_wdata) - { - realloc_fifo (fd, s->max_rdata, s->max_wdata << 1); - printf ("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata); - } - if (s->wdata_size + len + 2048 < s->max_wdata) - s->wdata_size += len; - else - fprintf (stderr, "socket: %d wdata lost !!\n", fd), abort (); -} - -void do_sendrecv (uint32_t next) -{ - fd_set rfd = readfds, wfd; - FD_ZERO (&wfd); - for (int i = 0; i < fd_max; i++) - { - if (session[i] && session[i]->wdata_size) - FD_SET (i, &wfd); - } - struct timeval timeout; - timeout.tv_sec = next / 1000; - timeout.tv_usec = next % 1000 * 1000; - if (select (fd_max, &rfd, &wfd, NULL, &timeout) <= 0) - return; - for (int i = 0; i < fd_max; i++) - { - if (!session[i]) - continue; - if (FD_ISSET (i, &wfd)) - { - if (session[i]->func_send) - //send_from_fifo(i); - session[i]->func_send (i); - } - if (FD_ISSET (i, &rfd)) - { - if (session[i]->func_recv) - //recv_to_fifo(i); - //or connect_client(i); - session[i]->func_recv (i); - } - } -} - -void do_parsepacket (void) -{ - for (int i = 0; i < fd_max; i++) - { - if (!session[i]) - continue; - if (!session[i]->connected - && time (NULL) - session[i]->created > CONNECT_TIMEOUT) - { - printf ("Session #%d timed out\n", i); - session[i]->eof = 1; - } - if (!session[i]->rdata_size && !session[i]->eof) - continue; - if (session[i]->func_parse) - { - session[i]->func_parse (i); - /// some func_parse may call delete_session - if (!session[i]) - continue; - } - /// Reclaim buffer space for what was read - RFIFOFLUSH (i); - } -} - -void do_socket (void) -{ - FD_ZERO (&readfds); - currentuse = 3; -} - -void RFIFOSKIP (int fd, size_t len) -{ - struct socket_data *s = session[fd]; - s->rdata_pos += len; - - if (s->rdata_size < s->rdata_pos) - { - fprintf (stderr, "too many skip\n"); - abort (); - } -} - -void fclose_ (FILE * fp) -{ - if (fclose (fp)) - perror ("fclose"), abort (); - currentuse--; -} - -FILE *fopen_ (const char *path, const char *mode) -{ - FILE *f = fopen (path, mode); - if (f) - currentuse++; - return f; -} - -bool free_fds (void) -{ - return currentuse < SOFT_LIMIT; -} diff --git a/src/common/socket.cpp b/src/common/socket.cpp new file mode 100644 index 0000000..cc6e4b3 --- /dev/null +++ b/src/common/socket.cpp @@ -0,0 +1,405 @@ +// $Id: socket.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ +// original : core.c 2003/02/26 18:03:12 Rev 1.7 + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/time.h> +#include <unistd.h> + +#include <fcntl.h> +#include <string.h> + +#include "mmo.hpp" // [Valaris] thanks to fov +#include "socket.hpp" +#include "utils.hpp" + +fd_set readfds; +int fd_max; +int currentuse; + +const uint32_t RFIFO_SIZE = 65536; +const uint32_t WFIFO_SIZE = 65536; + +struct socket_data *session[FD_SETSIZE]; + +/// Discard all input +static void null_parse (int fd); +/// Default parser for new connections +static void (*default_func_parse) (int) = null_parse; + +void set_defaultparse (void (*defaultparse) (int)) +{ + default_func_parse = defaultparse; +} + +/// Read from socket to the queue +static void recv_to_fifo (int fd) +{ + if (session[fd]->eof) + return; + + ssize_t len = read (fd, session[fd]->rdata + session[fd]->rdata_size, + RFIFOSPACE (fd)); + + if (len > 0) + { + session[fd]->rdata_size += len; + session[fd]->connected = 1; + } + else + { + session[fd]->eof = 1; + } +} + +static void send_from_fifo (int fd) +{ + if (session[fd]->eof) + return; + + ssize_t len = write (fd, session[fd]->wdata, session[fd]->wdata_size); + + if (len > 0) + { + session[fd]->wdata_size -= len; + if (len < (ssize_t)session[fd]->wdata_size) + { + memmove (session[fd]->wdata, session[fd]->wdata + len, + session[fd]->wdata_size); + } + session[fd]->connected = 1; + } + else + { + session[fd]->eof = 1; + } +} + +static void null_parse (int fd) +{ + printf ("null_parse : %d\n", fd); + RFIFOSKIP (fd, RFIFOREST (fd)); +} + + +static void connect_client (int listen_fd) +{ + struct sockaddr_in client_address; + socklen_t len = sizeof (client_address); + + int fd = accept (listen_fd, (struct sockaddr *) &client_address, &len); + if (fd == -1) + { + perror ("accept"); + return; + } + if (fd_max <= fd) + { + fd_max = fd + 1; + } + if (!free_fds ()) + { + fprintf (stderr, "softlimit reached, disconnecting : %d\n", fd); + delete_session (fd); + return; + } + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + // I'm not convinced this is a good idea; although in minimizes the + // latency for an individual write, it increases traffic in general. + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + FD_SET (fd, &readfds); + + fcntl (fd, F_SETFL, O_NONBLOCK); + + CREATE (session[fd], struct socket_data, 1); + CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); + CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); + + session[fd]->max_rdata = RFIFO_SIZE; + session[fd]->max_wdata = WFIFO_SIZE; + session[fd]->func_recv = recv_to_fifo; + session[fd]->func_send = send_from_fifo; + session[fd]->func_parse = default_func_parse; + session[fd]->client_addr = client_address; + session[fd]->created = time (NULL); + session[fd]->connected = 0; + + currentuse++; +} + +int make_listen_port (uint16_t port) +{ + struct sockaddr_in server_address; + int fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd == -1) + { + perror ("socket"); + return -1; + } + if (fd_max <= fd) + fd_max = fd + 1; + + fcntl (fd, F_SETFL, O_NONBLOCK); + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + // I'm not convinced this is a good idea; although in minimizes the + // latency for an individual write, it increases traffic in general. + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl (INADDR_ANY); + server_address.sin_port = htons (port); + + if (bind (fd, (struct sockaddr *) &server_address, + sizeof (server_address)) == -1) + { + perror ("bind"); + exit (1); + } + if (listen (fd, 5) == -1) + { /* error */ + perror ("listen"); + exit (1); + } + + FD_SET (fd, &readfds); + + CREATE (session[fd], struct socket_data, 1); + + session[fd]->func_recv = connect_client; + session[fd]->created = time (NULL); + session[fd]->connected = 1; + + currentuse++; + return fd; +} + +int make_connection (uint32_t ip, uint16_t port) +{ + struct sockaddr_in server_address; + int fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd == -1) + { + perror ("socket"); + return -1; + } + if (fd_max <= fd) + fd_max = fd + 1; + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + // I'm not convinced this is a good idea; although in minimizes the + // latency for an individual write, it increases traffic in general. + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = ip; + server_address.sin_port = htons (port); + + fcntl (fd, F_SETFL, O_NONBLOCK); + + /// Errors not caught - we must not block + /// Let the main select() loop detect when we know the state + connect (fd, (struct sockaddr *) &server_address, + sizeof (struct sockaddr_in)); + + FD_SET (fd, &readfds); + + CREATE (session[fd], struct socket_data, 1); + CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); + CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); + + session[fd]->max_rdata = RFIFO_SIZE; + session[fd]->max_wdata = WFIFO_SIZE; + session[fd]->func_recv = recv_to_fifo; + session[fd]->func_send = send_from_fifo; + session[fd]->func_parse = default_func_parse; + session[fd]->created = time (NULL); + session[fd]->connected = 1; + + currentuse++; + return fd; +} + +void delete_session (int fd) +{ + if (fd < 0 || fd >= FD_SETSIZE) + return; + // If this was the highest fd, decrease it + // We could add a loop to decrement fd_max further for every null session, + // but this is cheap and good enough for the typical case + if (fd == fd_max - 1) + fd_max--; + FD_CLR (fd, &readfds); + if (session[fd]) + { + free (session[fd]->rdata); + free (session[fd]->wdata); + free (session[fd]->session_data); + free (session[fd]); + } + session[fd] = NULL; + + // just close() would try to keep sending buffers + shutdown (fd, SHUT_RDWR); + close (fd); + currentuse--; + if (currentuse < 0) + { + fprintf (stderr, "delete_session: current sessions negative!\n"); + currentuse = 0; + } + return; +} + +void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size) +{ + struct socket_data *s = session[fd]; + if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) + { + RECREATE (s->rdata, uint8_t, rfifo_size); + s->max_rdata = rfifo_size; + } + if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) + { + RECREATE (s->wdata, uint8_t, wfifo_size); + s->max_wdata = wfifo_size; + } +} + +void WFIFOSET (int fd, size_t len) +{ + struct socket_data *s = session[fd]; + if (s->wdata_size + len + 16384 > s->max_wdata) + { + realloc_fifo (fd, s->max_rdata, s->max_wdata << 1); + printf ("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata); + } + if (s->wdata_size + len + 2048 < s->max_wdata) + s->wdata_size += len; + else + fprintf (stderr, "socket: %d wdata lost !!\n", fd), abort (); +} + +void do_sendrecv (uint32_t next) +{ + fd_set rfd = readfds, wfd; + FD_ZERO (&wfd); + for (int i = 0; i < fd_max; i++) + { + if (session[i] && session[i]->wdata_size) + FD_SET (i, &wfd); + } + struct timeval timeout; + timeout.tv_sec = next / 1000; + timeout.tv_usec = next % 1000 * 1000; + if (select (fd_max, &rfd, &wfd, NULL, &timeout) <= 0) + return; + for (int i = 0; i < fd_max; i++) + { + if (!session[i]) + continue; + if (FD_ISSET (i, &wfd)) + { + if (session[i]->func_send) + //send_from_fifo(i); + session[i]->func_send (i); + } + if (FD_ISSET (i, &rfd)) + { + if (session[i]->func_recv) + //recv_to_fifo(i); + //or connect_client(i); + session[i]->func_recv (i); + } + } +} + +void do_parsepacket (void) +{ + for (int i = 0; i < fd_max; i++) + { + if (!session[i]) + continue; + if (!session[i]->connected + && time (NULL) - session[i]->created > CONNECT_TIMEOUT) + { + printf ("Session #%d timed out\n", i); + session[i]->eof = 1; + } + if (!session[i]->rdata_size && !session[i]->eof) + continue; + if (session[i]->func_parse) + { + session[i]->func_parse (i); + /// some func_parse may call delete_session + if (!session[i]) + continue; + } + /// Reclaim buffer space for what was read + RFIFOFLUSH (i); + } +} + +void do_socket (void) +{ + FD_ZERO (&readfds); + currentuse = 3; +} + +void RFIFOSKIP (int fd, size_t len) +{ + struct socket_data *s = session[fd]; + s->rdata_pos += len; + + if (s->rdata_size < s->rdata_pos) + { + fprintf (stderr, "too many skip\n"); + abort (); + } +} + +void fclose_ (FILE * fp) +{ + if (fclose (fp)) + perror ("fclose"), abort (); + currentuse--; +} + +FILE *fopen_ (const char *path, const char *mode) +{ + FILE *f = fopen (path, mode); + if (f) + currentuse++; + return f; +} + +bool free_fds (void) +{ + return currentuse < SOFT_LIMIT; +} diff --git a/src/common/socket.h b/src/common/socket.h deleted file mode 100644 index b886df0..0000000 --- a/src/common/socket.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef SOCKET_H -#define SOCKET_H - -# include "sanity.h" - -# include <stdio.h> - -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> - -# include <time.h> - -/// Check how much can be read -# define RFIFOREST(fd) (session[fd]->rdata_size-session[fd]->rdata_pos) -/// Read from the queue -# define RFIFOP(fd,pos) (session[fd]->rdata+session[fd]->rdata_pos+(pos)) -# define RFIFOB(fd,pos) (*(uint8_t*)(RFIFOP(fd, pos))) -# define RFIFOW(fd,pos) (*(uint16_t*)(RFIFOP(fd, pos))) -# define RFIFOL(fd,pos) (*(uint32_t*)(RFIFOP(fd, pos))) -/// Done reading -void RFIFOSKIP (int fd, size_t len); -/// Internal - clean up by discarding handled bytes -// Atm this is also called in char/char.c, but that is unnecessary -# define RFIFOFLUSH(fd) (memmove(session[fd]->rdata,RFIFOP(fd,0),RFIFOREST(fd)),\ -session[fd]->rdata_size=RFIFOREST(fd),\ -session[fd]->rdata_pos=0) - -/// Used internally - how much room there is to read more data -# define RFIFOSPACE(fd) (session[fd]->max_rdata-session[fd]->rdata_size) - -/// Read from an arbitrary buffer -# define RBUFP(p,pos) (((uint8_t*)(p))+(pos)) -# define RBUFB(p,pos) (*(uint8_t*)RBUFP((p),(pos))) -# define RBUFW(p,pos) (*(uint16_t*)RBUFP((p),(pos))) -# define RBUFL(p,pos) (*(uint32_t*)RBUFP((p),(pos))) - - - -/// Unused - check how much data can be written -# define WFIFOSPACE(fd) (session[fd]->max_wdata-session[fd]->wdata_size) -/// Write to the queue -# define WFIFOP(fd,pos) (session[fd]->wdata+session[fd]->wdata_size+(pos)) -# define WFIFOB(fd,pos) (*(uint8_t*)(WFIFOP(fd,pos))) -# define WFIFOW(fd,pos) (*(uint16_t*)(WFIFOP(fd,pos))) -# define WFIFOL(fd,pos) (*(uint32_t*)(WFIFOP(fd,pos))) -/// Finish writing -void WFIFOSET (int fd, size_t len); - -/// Write to an arbitrary buffer -#define WBUFP(p,pos) (((uint8_t*)(p))+(pos)) -#define WBUFB(p,pos) (*(uint8_t*)WBUFP((p),(pos))) -#define WBUFW(p,pos) (*(uint16_t*)WBUFP((p),(pos))) -#define WBUFL(p,pos) (*(uint32_t*)WBUFP((p),(pos))) - -// Struct declaration - -struct socket_data -{ - /// Checks whether a newly-connected socket actually does anything - time_t created; - bool connected; - - /// Flag needed since structure must be freed in a server-dependent manner - bool eof; - - /// Since this is a single-threaded application, it can't block - /// These are the read/write queues - uint8_t *rdata, *wdata; - size_t max_rdata, max_wdata; - /// How much is actually in the queue - size_t rdata_size, wdata_size; - /// How much has already been read from the queue - /// Note that there is no need for a wdata_pos - size_t rdata_pos; - - struct sockaddr_in client_addr; - - /// Send or recieve - /// Only called when select() indicates the socket is ready - /// If, after that, nothing is read, it sets eof - // These could probably be hard-coded with a little work - void (*func_recv) (int); - void (*func_send) (int); - /// This is the important one - /// Set to different functions depending on whether the connection - /// is a player or a server/ladmin - /// Can be set explicitly or via set_defaultparse - void (*func_parse) (int); - /// Server-specific data type - void *session_data; -}; - -// save file descriptors for important stuff -# define SOFT_LIMIT (FD_SETSIZE - 50) - -// socket timeout to establish a full connection in seconds -# define CONNECT_TIMEOUT 15 - -/// Everyone who has connected -// note: call delete_session(i) to null out an element -extern struct socket_data *session[FD_SETSIZE]; - -/// Maximum used FD, +1 -extern int fd_max; - -/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails, -/// but exit if bind() or listen() fails -int make_listen_port (uint16_t port); -/// Connect to an address, return a connected socket or -1 -// FIXME - this is IPv4 only! -int make_connection (uint32_t ip, uint16_t port); -/// free() the structure and close() the fd -void delete_session (int); -/// Make a the internal queues bigger -void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size); -/// Update all sockets that can be read/written from the queues -void do_sendrecv (uint32_t next); -/// Call the parser function for every socket that has read data -void do_parsepacket (void); - -/// An init function -void do_socket (void); - -/// Change the default parser for newly connected clients -// typically called once per server, but individual clients may identify -// themselves as servers -void set_defaultparse (void (*defaultparse) (int)); - -/// Wrappers to track number of free FDs -void fclose_ (FILE * fp); -FILE *fopen_ (const char *path, const char *mode); -bool free_fds (void); - -#endif // SOCKET_H diff --git a/src/common/socket.hpp b/src/common/socket.hpp new file mode 100644 index 0000000..00f2df0 --- /dev/null +++ b/src/common/socket.hpp @@ -0,0 +1,135 @@ +#ifndef SOCKET_HPP +#define SOCKET_HPP + +# include "sanity.hpp" + +# include <stdio.h> + +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> + +# include <time.h> + +/// Check how much can be read +# define RFIFOREST(fd) (session[fd]->rdata_size-session[fd]->rdata_pos) +/// Read from the queue +# define RFIFOP(fd,pos) (session[fd]->rdata+session[fd]->rdata_pos+(pos)) +# define RFIFOB(fd,pos) (*(uint8_t*)(RFIFOP(fd, pos))) +# define RFIFOW(fd,pos) (*(uint16_t*)(RFIFOP(fd, pos))) +# define RFIFOL(fd,pos) (*(uint32_t*)(RFIFOP(fd, pos))) +/// Done reading +void RFIFOSKIP (int fd, size_t len); +/// Internal - clean up by discarding handled bytes +// Atm this is also called in char/char.c, but that is unnecessary +# define RFIFOFLUSH(fd) (memmove(session[fd]->rdata,RFIFOP(fd,0),RFIFOREST(fd)),\ +session[fd]->rdata_size=RFIFOREST(fd),\ +session[fd]->rdata_pos=0) + +/// Used internally - how much room there is to read more data +# define RFIFOSPACE(fd) (session[fd]->max_rdata-session[fd]->rdata_size) + +/// Read from an arbitrary buffer +# define RBUFP(p,pos) (((uint8_t*)(p))+(pos)) +# define RBUFB(p,pos) (*(uint8_t*)RBUFP((p),(pos))) +# define RBUFW(p,pos) (*(uint16_t*)RBUFP((p),(pos))) +# define RBUFL(p,pos) (*(uint32_t*)RBUFP((p),(pos))) + + + +/// Unused - check how much data can be written +# define WFIFOSPACE(fd) (session[fd]->max_wdata-session[fd]->wdata_size) +/// Write to the queue +# define WFIFOP(fd,pos) (session[fd]->wdata+session[fd]->wdata_size+(pos)) +# define WFIFOB(fd,pos) (*(uint8_t*)(WFIFOP(fd,pos))) +# define WFIFOW(fd,pos) (*(uint16_t*)(WFIFOP(fd,pos))) +# define WFIFOL(fd,pos) (*(uint32_t*)(WFIFOP(fd,pos))) +/// Finish writing +void WFIFOSET (int fd, size_t len); + +/// Write to an arbitrary buffer +#define WBUFP(p,pos) (((uint8_t*)(p))+(pos)) +#define WBUFB(p,pos) (*(uint8_t*)WBUFP((p),(pos))) +#define WBUFW(p,pos) (*(uint16_t*)WBUFP((p),(pos))) +#define WBUFL(p,pos) (*(uint32_t*)WBUFP((p),(pos))) + +// Struct declaration + +struct socket_data +{ + /// Checks whether a newly-connected socket actually does anything + time_t created; + bool connected; + + /// Flag needed since structure must be freed in a server-dependent manner + bool eof; + + /// Since this is a single-threaded application, it can't block + /// These are the read/write queues + uint8_t *rdata, *wdata; + size_t max_rdata, max_wdata; + /// How much is actually in the queue + size_t rdata_size, wdata_size; + /// How much has already been read from the queue + /// Note that there is no need for a wdata_pos + size_t rdata_pos; + + struct sockaddr_in client_addr; + + /// Send or recieve + /// Only called when select() indicates the socket is ready + /// If, after that, nothing is read, it sets eof + // These could probably be hard-coded with a little work + void (*func_recv) (int); + void (*func_send) (int); + /// This is the important one + /// Set to different functions depending on whether the connection + /// is a player or a server/ladmin + /// Can be set explicitly or via set_defaultparse + void (*func_parse) (int); + /// Server-specific data type + void *session_data; +}; + +// save file descriptors for important stuff +# define SOFT_LIMIT (FD_SETSIZE - 50) + +// socket timeout to establish a full connection in seconds +# define CONNECT_TIMEOUT 15 + +/// Everyone who has connected +// note: call delete_session(i) to null out an element +extern struct socket_data *session[FD_SETSIZE]; + +/// Maximum used FD, +1 +extern int fd_max; + +/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails, +/// but exit if bind() or listen() fails +int make_listen_port (uint16_t port); +/// Connect to an address, return a connected socket or -1 +// FIXME - this is IPv4 only! +int make_connection (uint32_t ip, uint16_t port); +/// free() the structure and close() the fd +void delete_session (int); +/// Make a the internal queues bigger +void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size); +/// Update all sockets that can be read/written from the queues +void do_sendrecv (uint32_t next); +/// Call the parser function for every socket that has read data +void do_parsepacket (void); + +/// An init function +void do_socket (void); + +/// Change the default parser for newly connected clients +// typically called once per server, but individual clients may identify +// themselves as servers +void set_defaultparse (void (*defaultparse) (int)); + +/// Wrappers to track number of free FDs +void fclose_ (FILE * fp); +FILE *fopen_ (const char *path, const char *mode); +bool free_fds (void); + +#endif // SOCKET_HPP diff --git a/src/common/timer.c b/src/common/timer.c deleted file mode 100644 index 6795824..0000000 --- a/src/common/timer.c +++ /dev/null @@ -1,257 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> - -#include <sys/socket.h> -#include <sys/time.h> - -#include "timer.h" -#include "utils.h" - -static struct TimerData *timer_data; -static uint32_t timer_data_max, timer_data_num; -static timer_id *free_timer_list; -static uint32_t free_timer_list_max, free_timer_list_pos; - -/// Okay, I think I understand this structure now: -/// the timer heap is a magic queue that allows inserting timers and then popping them in order -/// designed to copy only log2(N) entries instead of N -// timer_heap[0] is the size (greatest index into the heap) -// timer_heap[1] is the first actual element -// timer_heap_max increases 256 at a time and never decreases -static uint32_t timer_heap_max = 0; -/// FIXME: refactor the code to put the size in a separate variable -//nontrivial because indices get multiplied -static timer_id *timer_heap = NULL; - - -static uint32_t gettick_cache; -static uint8_t gettick_count = 0; - -uint32_t gettick_nocache (void) -{ - struct timeval tval; - // BUG: This will cause strange behavior if the system clock is changed! - // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) - gettimeofday (&tval, NULL); - gettick_count = 255; - return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000; -} - -uint32_t gettick (void) -{ - if (gettick_count--) - return gettick_cache; - return gettick_nocache (); -} - -static void push_timer_heap (timer_id index) -{ - if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max) - { - timer_heap_max += 256; - RECREATE (timer_heap, timer_id, timer_heap_max); - memset (timer_heap + (timer_heap_max - 256), 0, sizeof (timer_id) * 256); - } -// timer_heap[0] is the greatest index into the heap, which increases - timer_heap[0]++; - - timer_id h = timer_heap[0]-1, i = (h - 1) / 2; - while (h) - { - // avoid wraparound problems, it really means this: - // timer_data[index].tick >= timer_data[timer_heap[i+1]].tick - if ( DIFF_TICK(timer_data[index].tick, timer_data[timer_heap[i+1]].tick) >= 0) - break; - timer_heap[h + 1] = timer_heap[i + 1]; - h = i; - i = (h - 1) / 2; - } - timer_heap[h + 1] = index; -} - -static timer_id top_timer_heap (void) -{ - if (!timer_heap || !timer_heap[0]) - return -1; - return timer_heap[1]; -} - -static timer_id pop_timer_heap (void) -{ - if (!timer_heap || !timer_heap[0]) - return -1; - timer_id ret = timer_heap[1]; - timer_id last = timer_heap[timer_heap[0]]; - timer_heap[0]--; - - uint32_t h, k; - for (h = 0, k = 2; k < timer_heap[0]; k = k * 2 + 2) - { - if (DIFF_TICK(timer_data[timer_heap[k + 1]].tick, timer_data[timer_heap[k]].tick) > 0) - k--; - timer_heap[h + 1] = timer_heap[k + 1], h = k; - } - if (k == timer_heap[0]) - timer_heap[h + 1] = timer_heap[k], h = k - 1; - - uint32_t i = (h - 1) / 2; - while (h) - { - if (DIFF_TICK (timer_data[timer_heap[i + 1]].tick, timer_data[last].tick) <= 0) - break; - timer_heap[h + 1] = timer_heap[i + 1]; - h = i; - i = (h - 1) / 2; - } - timer_heap[h + 1] = last; - - return ret; -} - -timer_id add_timer (tick_t tick, timer_func func, custom_id_t id, custom_data_t data) -{ - timer_id i; - - if (free_timer_list_pos) - { - // Retrieve a freed timer id instead of a new one - // I think it should be possible to avoid the loop somehow - do - { - i = free_timer_list[--free_timer_list_pos]; - } - while (i >= timer_data_num && free_timer_list_pos > 0); - } - else - i = timer_data_num; - - // I have no idea what this is doing - if (i >= timer_data_num) - for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++) - ; - if (i >= timer_data_num && i >= timer_data_max) - { - if (timer_data_max == 0) - { - timer_data_max = 256; - CREATE (timer_data, struct TimerData, timer_data_max); - } - else - { - timer_data_max += 256; - RECREATE (timer_data, struct TimerData, timer_data_max); - memset (timer_data + (timer_data_max - 256), 0, - sizeof (struct TimerData) * 256); - } - } - timer_data[i].tick = tick; - timer_data[i].func = func; - timer_data[i].id = id; - timer_data[i].data = data; - timer_data[i].type = TIMER_ONCE_AUTODEL; - timer_data[i].interval = 1000; - push_timer_heap (i); - if (i >= timer_data_num) - timer_data_num = i + 1; - return i; -} - -timer_id add_timer_interval (tick_t tick, timer_func func, custom_id_t id, - custom_data_t data, interval_t interval) -{ - timer_id tid = add_timer (tick, func, id, data); - timer_data[tid].type = TIMER_INTERVAL; - timer_data[tid].interval = interval; - return tid; -} - -void delete_timer (timer_id id, timer_func func) -{ - if (id == 0 || id >= timer_data_num) - { - fprintf (stderr, "delete_timer error : no such timer %d\n", id); - abort (); - } - if (timer_data[id].func != func) - { - fprintf (stderr, "Timer mismatch\n"); - abort (); - } - // "to let them disappear" - is this just in case? - timer_data[id].func = NULL; - timer_data[id].type = TIMER_ONCE_AUTODEL; - timer_data[id].tick -= 60 * 60 * 1000; -} - -tick_t addtick_timer (timer_id tid, interval_t tick) -{ - return timer_data[tid].tick += tick; -} - -struct TimerData *get_timer (timer_id tid) -{ - return &timer_data[tid]; -} - -interval_t do_timer (tick_t tick) -{ - timer_id i; - /// Number of milliseconds until it calls this again - // this says to wait 1 sec if all timers get popped - interval_t nextmin = 1000; - - while ((i = top_timer_heap ()) != (timer_id)-1) - { - // while the heap is not empty and - if (DIFF_TICK (timer_data[i].tick, tick) > 0) - { - /// Return the time until the next timer needs to goes off - nextmin = DIFF_TICK (timer_data[i].tick, tick); - break; - } - pop_timer_heap (); - if (timer_data[i].func) - { - if (DIFF_TICK (timer_data[i].tick, tick) < -1000) - { - // If we are too far past the requested tick, call with the current tick instead to fix reregistering problems - timer_data[i].func (i, tick, timer_data[i].id, timer_data[i].data); - } - else - { - timer_data[i].func (i, timer_data[i].tick, timer_data[i].id, timer_data[i].data); - } - } - switch (timer_data[i].type) - { - case TIMER_ONCE_AUTODEL: - timer_data[i].type = TIMER_NONE; - if (free_timer_list_pos >= free_timer_list_max) - { - free_timer_list_max += 256; - RECREATE (free_timer_list, uint32_t, free_timer_list_max); - memset (free_timer_list + (free_timer_list_max - 256), - 0, 256 * sizeof (uint32_t)); - } - free_timer_list[free_timer_list_pos++] = i; - break; - case TIMER_INTERVAL: - if (DIFF_TICK (timer_data[i].tick, tick) < -1000) - { - timer_data[i].tick = tick + timer_data[i].interval; - } - else - { - timer_data[i].tick += timer_data[i].interval; - } - push_timer_heap (i); - break; - } - } - - if (nextmin < 10) - nextmin = 10; - return nextmin; -} diff --git a/src/common/timer.cpp b/src/common/timer.cpp new file mode 100644 index 0000000..66aaa9b --- /dev/null +++ b/src/common/timer.cpp @@ -0,0 +1,257 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <sys/socket.h> +#include <sys/time.h> + +#include "timer.hpp" +#include "utils.hpp" + +static struct TimerData *timer_data; +static uint32_t timer_data_max, timer_data_num; +static timer_id *free_timer_list; +static uint32_t free_timer_list_max, free_timer_list_pos; + +/// Okay, I think I understand this structure now: +/// the timer heap is a magic queue that allows inserting timers and then popping them in order +/// designed to copy only log2(N) entries instead of N +// timer_heap[0] is the size (greatest index into the heap) +// timer_heap[1] is the first actual element +// timer_heap_max increases 256 at a time and never decreases +static uint32_t timer_heap_max = 0; +/// FIXME: refactor the code to put the size in a separate variable +//nontrivial because indices get multiplied +static timer_id *timer_heap = NULL; + + +static uint32_t gettick_cache; +static uint8_t gettick_count = 0; + +uint32_t gettick_nocache (void) +{ + struct timeval tval; + // BUG: This will cause strange behavior if the system clock is changed! + // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) + gettimeofday (&tval, NULL); + gettick_count = 255; + return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000; +} + +uint32_t gettick (void) +{ + if (gettick_count--) + return gettick_cache; + return gettick_nocache (); +} + +static void push_timer_heap (timer_id index) +{ + if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max) + { + timer_heap_max += 256; + RECREATE (timer_heap, timer_id, timer_heap_max); + memset (timer_heap + (timer_heap_max - 256), 0, sizeof (timer_id) * 256); + } +// timer_heap[0] is the greatest index into the heap, which increases + timer_heap[0]++; + + timer_id h = timer_heap[0]-1, i = (h - 1) / 2; + while (h) + { + // avoid wraparound problems, it really means this: + // timer_data[index].tick >= timer_data[timer_heap[i+1]].tick + if ( DIFF_TICK(timer_data[index].tick, timer_data[timer_heap[i+1]].tick) >= 0) + break; + timer_heap[h + 1] = timer_heap[i + 1]; + h = i; + i = (h - 1) / 2; + } + timer_heap[h + 1] = index; +} + +static timer_id top_timer_heap (void) +{ + if (!timer_heap || !timer_heap[0]) + return -1; + return timer_heap[1]; +} + +static timer_id pop_timer_heap (void) +{ + if (!timer_heap || !timer_heap[0]) + return -1; + timer_id ret = timer_heap[1]; + timer_id last = timer_heap[timer_heap[0]]; + timer_heap[0]--; + + uint32_t h, k; + for (h = 0, k = 2; k < timer_heap[0]; k = k * 2 + 2) + { + if (DIFF_TICK(timer_data[timer_heap[k + 1]].tick, timer_data[timer_heap[k]].tick) > 0) + k--; + timer_heap[h + 1] = timer_heap[k + 1], h = k; + } + if (k == timer_heap[0]) + timer_heap[h + 1] = timer_heap[k], h = k - 1; + + uint32_t i = (h - 1) / 2; + while (h) + { + if (DIFF_TICK (timer_data[timer_heap[i + 1]].tick, timer_data[last].tick) <= 0) + break; + timer_heap[h + 1] = timer_heap[i + 1]; + h = i; + i = (h - 1) / 2; + } + timer_heap[h + 1] = last; + + return ret; +} + +timer_id add_timer (tick_t tick, timer_func func, custom_id_t id, custom_data_t data) +{ + timer_id i; + + if (free_timer_list_pos) + { + // Retrieve a freed timer id instead of a new one + // I think it should be possible to avoid the loop somehow + do + { + i = free_timer_list[--free_timer_list_pos]; + } + while (i >= timer_data_num && free_timer_list_pos > 0); + } + else + i = timer_data_num; + + // I have no idea what this is doing + if (i >= timer_data_num) + for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++) + ; + if (i >= timer_data_num && i >= timer_data_max) + { + if (timer_data_max == 0) + { + timer_data_max = 256; + CREATE (timer_data, struct TimerData, timer_data_max); + } + else + { + timer_data_max += 256; + RECREATE (timer_data, struct TimerData, timer_data_max); + memset (timer_data + (timer_data_max - 256), 0, + sizeof (struct TimerData) * 256); + } + } + timer_data[i].tick = tick; + timer_data[i].func = func; + timer_data[i].id = id; + timer_data[i].data = data; + timer_data[i].type = TIMER_ONCE_AUTODEL; + timer_data[i].interval = 1000; + push_timer_heap (i); + if (i >= timer_data_num) + timer_data_num = i + 1; + return i; +} + +timer_id add_timer_interval (tick_t tick, timer_func func, custom_id_t id, + custom_data_t data, interval_t interval) +{ + timer_id tid = add_timer (tick, func, id, data); + timer_data[tid].type = TIMER_INTERVAL; + timer_data[tid].interval = interval; + return tid; +} + +void delete_timer (timer_id id, timer_func func) +{ + if (id == 0 || id >= timer_data_num) + { + fprintf (stderr, "delete_timer error : no such timer %d\n", id); + abort (); + } + if (timer_data[id].func != func) + { + fprintf (stderr, "Timer mismatch\n"); + abort (); + } + // "to let them disappear" - is this just in case? + timer_data[id].func = NULL; + timer_data[id].type = TIMER_ONCE_AUTODEL; + timer_data[id].tick -= 60 * 60 * 1000; +} + +tick_t addtick_timer (timer_id tid, interval_t tick) +{ + return timer_data[tid].tick += tick; +} + +struct TimerData *get_timer (timer_id tid) +{ + return &timer_data[tid]; +} + +interval_t do_timer (tick_t tick) +{ + timer_id i; + /// Number of milliseconds until it calls this again + // this says to wait 1 sec if all timers get popped + interval_t nextmin = 1000; + + while ((i = top_timer_heap ()) != (timer_id)-1) + { + // while the heap is not empty and + if (DIFF_TICK (timer_data[i].tick, tick) > 0) + { + /// Return the time until the next timer needs to goes off + nextmin = DIFF_TICK (timer_data[i].tick, tick); + break; + } + pop_timer_heap (); + if (timer_data[i].func) + { + if (DIFF_TICK (timer_data[i].tick, tick) < -1000) + { + // If we are too far past the requested tick, call with the current tick instead to fix reregistering problems + timer_data[i].func (i, tick, timer_data[i].id, timer_data[i].data); + } + else + { + timer_data[i].func (i, timer_data[i].tick, timer_data[i].id, timer_data[i].data); + } + } + switch (timer_data[i].type) + { + case TIMER_ONCE_AUTODEL: + timer_data[i].type = TIMER_NONE; + if (free_timer_list_pos >= free_timer_list_max) + { + free_timer_list_max += 256; + RECREATE (free_timer_list, uint32_t, free_timer_list_max); + memset (free_timer_list + (free_timer_list_max - 256), + 0, 256 * sizeof (uint32_t)); + } + free_timer_list[free_timer_list_pos++] = i; + break; + case TIMER_INTERVAL: + if (DIFF_TICK (timer_data[i].tick, tick) < -1000) + { + timer_data[i].tick = tick + timer_data[i].interval; + } + else + { + timer_data[i].tick += timer_data[i].interval; + } + push_timer_heap (i); + break; + } + } + + if (nextmin < 10) + nextmin = 10; + return nextmin; +} diff --git a/src/common/timer.h b/src/common/timer.h deleted file mode 100644 index e6a292c..0000000 --- a/src/common/timer.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TIMER_H -#define TIMER_H - -# include "sanity.h" - -enum TIMER_TYPE -{ - TIMER_NONE, - TIMER_ONCE_AUTODEL, - TIMER_INTERVAL, -}; -/// This is needed to produce a signed result when 2 ticks are subtracted -# define DIFF_TICK(a,b) ((int32_t)((a)-(b))) - -// TODO replace with signed 64-bit to make code more clear and protect from the future -typedef uint32_t tick_t; -typedef uint32_t interval_t; -typedef uint32_t timer_id; -// BUG: pointers are stored in here -typedef int32_t custom_id_t; -typedef int32_t custom_data_t; -typedef void (*timer_func) (timer_id, tick_t, custom_id_t, custom_data_t); - -struct TimerData -{ - /// When it will be triggered - tick_t tick; - /// What will be done - timer_func func; - /// Arbitrary data. WARNING, callers are stupid and put pointers in here - // Should we change to void* or intptr_t ? - custom_id_t id; - custom_data_t data; - /// Type of timer - 0 initially - enum TIMER_TYPE type; - /// Repeat rate - interval_t interval; -}; - -/// Server time, in milliseconds, since the epoch, -/// but use of 32-bit integers means it wraps every 49 days. -// The only external caller of this function is the core.c main loop, but that makes sense -// in fact, it might make more sense if gettick() ALWAYS returned that cached value -tick_t gettick_nocache (void); -/// This function is called enough that it's worth caching the result for -/// the next 255 times -tick_t gettick (void); - -timer_id add_timer (tick_t, timer_func, custom_id_t, custom_data_t); -timer_id add_timer_interval (tick_t, timer_func, custom_id_t, custom_data_t, interval_t); -void delete_timer (timer_id, timer_func); - -tick_t addtick_timer (timer_id, interval_t); -struct TimerData *get_timer (timer_id tid); - -/// Do all timers scheduled before tick, and return the number of milliseconds until the next timer happens -interval_t do_timer (tick_t tick); - - - -#endif // TIMER_H diff --git a/src/common/timer.hpp b/src/common/timer.hpp new file mode 100644 index 0000000..fdda344 --- /dev/null +++ b/src/common/timer.hpp @@ -0,0 +1,61 @@ +#ifndef TIMER_HPP +#define TIMER_HPP + +# include "sanity.hpp" + +enum TIMER_TYPE +{ + TIMER_NONE, + TIMER_ONCE_AUTODEL, + TIMER_INTERVAL, +}; +/// This is needed to produce a signed result when 2 ticks are subtracted +# define DIFF_TICK(a,b) ((int32_t)((a)-(b))) + +// TODO replace with signed 64-bit to make code more clear and protect from the future +typedef uint32_t tick_t; +typedef uint32_t interval_t; +typedef uint32_t timer_id; +// BUG: pointers are stored in here +typedef int32_t custom_id_t; +typedef int32_t custom_data_t; +typedef void (*timer_func) (timer_id, tick_t, custom_id_t, custom_data_t); + +struct TimerData +{ + /// When it will be triggered + tick_t tick; + /// What will be done + timer_func func; + /// Arbitrary data. WARNING, callers are stupid and put pointers in here + // Should we change to void* or intptr_t ? + custom_id_t id; + custom_data_t data; + /// Type of timer - 0 initially + enum TIMER_TYPE type; + /// Repeat rate + interval_t interval; +}; + +/// Server time, in milliseconds, since the epoch, +/// but use of 32-bit integers means it wraps every 49 days. +// The only external caller of this function is the core.c main loop, but that makes sense +// in fact, it might make more sense if gettick() ALWAYS returned that cached value +tick_t gettick_nocache (void); +/// This function is called enough that it's worth caching the result for +/// the next 255 times +tick_t gettick (void); + +timer_id add_timer (tick_t, timer_func, custom_id_t, custom_data_t); +timer_id add_timer_interval (tick_t, timer_func, custom_id_t, custom_data_t, interval_t); +void delete_timer (timer_id, timer_func); + +tick_t addtick_timer (timer_id, interval_t); +struct TimerData *get_timer (timer_id tid); + +/// Do all timers scheduled before tick, and return the number of milliseconds until the next timer happens +interval_t do_timer (tick_t tick); + + + +#endif // TIMER_HPP diff --git a/src/common/utils.h b/src/common/utils.h deleted file mode 100644 index 961d960..0000000 --- a/src/common/utils.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UTILS_H -#define UTILS_H -/* -Notes about memory allocation in tmwAthena: -There used to be 3 sources of allocation: these macros, -a{C,M,Re}alloc in common/malloc.{h,c}, and direct calls. -I deleted malloc.{h,c} because it was redundant; -future calls should either use this or depend on the coming segfault. -*/ -# define CREATE(result, type, number) \ - if (!((result) = (type *) calloc ((number), sizeof(type)))) \ - { perror("SYSERR: malloc failure"); abort(); } else (void)0 - -# define RECREATE(result,type,number) \ - if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\ - { perror("SYSERR: realloc failure"); abort(); } else (void)0 - -#endif //UTILS_H diff --git a/src/common/utils.hpp b/src/common/utils.hpp new file mode 100644 index 0000000..7c7da16 --- /dev/null +++ b/src/common/utils.hpp @@ -0,0 +1,18 @@ +#ifndef UTILS_HPP +#define UTILS_HPP +/* +Notes about memory allocation in tmwAthena: +There used to be 3 sources of allocation: these macros, +a{C,M,Re}alloc in common/malloc.{h,c}, and direct calls. +I deleted malloc.{h,c} because it was redundant; +future calls should either use this or depend on the coming segfault. +*/ +# define CREATE(result, type, number) \ + if (!((result) = (type *) calloc ((number), sizeof(type)))) \ + { perror("SYSERR: malloc failure"); abort(); } else (void)0 + +# define RECREATE(result,type,number) \ + if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\ + { perror("SYSERR: realloc failure"); abort(); } else (void)0 + +#endif //UTILS_HPP diff --git a/src/common/version.h b/src/common/version.h deleted file mode 100644 index 46165aa..0000000 --- a/src/common/version.h +++ /dev/null @@ -1,24 +0,0 @@ -/// Some constants to identify the version of (e)Athena -/// The values are different if the client connects (-1,'T','M','W',flags32) -// These numbers have never been changed while TMW -#ifndef VERSION_H -#define VERSION_H -//When a server receives a 0x7530 packet from an admin connection, -//it sends an 0x7531 packet with the following bytes -# define ATHENA_MAJOR_VERSION 1 // Major Version -# define ATHENA_MINOR_VERSION 0 // Minor Version -# define ATHENA_REVISION 0 // Revision - -# define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable -# define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official - -// and a bitmask of these (the char server sends char and inter) -# define ATHENA_SERVER_LOGIN 1 // login server -# define ATHENA_SERVER_CHAR 2 // char server -# define ATHENA_SERVER_INTER 4 // inter server -# define ATHENA_SERVER_MAP 8 // map server - -// and this as two bytes -# define ATHENA_MOD_VERSION 1052 // mod version (patch No.) - -#endif // VERSION_H diff --git a/src/common/version.hpp b/src/common/version.hpp new file mode 100644 index 0000000..d72f41e --- /dev/null +++ b/src/common/version.hpp @@ -0,0 +1,24 @@ +/// Some constants to identify the version of (e)Athena +/// The values are different if the client connects (-1,'T','M','W',flags32) +// These numbers have never been changed while TMW +#ifndef VERSION_HPP +#define VERSION_HPP +//When a server receives a 0x7530 packet from an admin connection, +//it sends an 0x7531 packet with the following bytes +# define ATHENA_MAJOR_VERSION 1 // Major Version +# define ATHENA_MINOR_VERSION 0 // Minor Version +# define ATHENA_REVISION 0 // Revision + +# define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable +# define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official + +// and a bitmask of these (the char server sends char and inter) +# define ATHENA_SERVER_LOGIN 1 // login server +# define ATHENA_SERVER_CHAR 2 // char server +# define ATHENA_SERVER_INTER 4 // inter server +# define ATHENA_SERVER_MAP 8 // map server + +// and this as two bytes +# define ATHENA_MOD_VERSION 1052 // mod version (patch No.) + +#endif // VERSION_HPP diff --git a/src/ladmin/ladmin.c b/src/ladmin/ladmin.c deleted file mode 100644 index 3728799..0000000 --- a/src/ladmin/ladmin.c +++ /dev/null @@ -1,6476 +0,0 @@ -// $Id: ladmin.c,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ -/////////////////////////////////////////////////////////////////////////// -// EAthena login-server remote administration tool -// Ladamin in C by [Yor] -// if you modify this software, modify ladmin in tool too. -/////////////////////////////////////////////////////////////////////////// - -#include <sys/types.h> -#include <sys/socket.h> -#include <stdio.h> -#include <stdlib.h> -#include <netinet/in.h> -#include <sys/time.h> // gettimeofday -#include <time.h> -#include <sys/ioctl.h> -#include <unistd.h> // close -#include <signal.h> -#include <fcntl.h> -#include <string.h> // str* -#include <arpa/inet.h> // inet_addr -#include <netdb.h> // gethostbyname -#include <stdarg.h> // valist -#include <ctype.h> // tolower - -#include "../common/core.h" -#include "../common/socket.h" -#include "ladmin.h" -#include "../common/version.h" -#include "../common/mmo.h" - -#ifdef PASSWORDENC -#include "../common/md5calc.h" -#endif - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -int eathena_interactive_session; // from core.c -#define Iprintf if (eathena_interactive_session) printf - -//-------------------------------INSTRUCTIONS------------------------------ -// Set the variables below: -// IP of the login server. -// Port where the login-server listens incoming packets. -// Password of administration (same of config_athena.conf). -// Displayed language of the sofware (if not correct, english is used). -// IMPORTANT: -// Be sure that you authorize remote administration in login-server -// (see login_athena.conf, 'admin_state' parameter) -//------------------------------------------------------------------------- -char loginserverip[16] = "127.0.0.1"; // IP of login-server -int loginserverport = 6900; // Port of login-server -char loginserveradminpassword[24] = "admin"; // Administration password -#ifdef PASSWORDENC -int passenc = 2; // Encoding type of the password -#else -int passenc = 0; // Encoding type of the password -#endif -char defaultlanguage = 'E'; // Default language (F: Français/E: English) - // (if it's not 'F', default is English) -char ladmin_log_filename[1024] = "log/ladmin.log"; -char date_format[32] = "%Y-%m-%d %H:%M:%S"; -//------------------------------------------------------------------------- -// LIST of COMMANDs that you can type at the prompt: -// To use these commands you can only type only the first letters. -// You must type a minimum of letters (you can not type 'a', -// because ladmin doesn't know if it's for 'aide' or for 'add') -// <Example> q <= quit, li <= list, pass <= passwd, etc. -// -// Note: every time you must give a account_name, you can use "" or '' (spaces can be included) -// -// aide/help/? -// Display the description of the commands -// aide/help/? [command] -// Display the description of the specified command -// -// add <account_name> <sex> <password> -// Create an account with the default email (a@a.com). -// Concerning the sex, only the first letter is used (F or M). -// The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail. -// When the password is omitted, the input is done without displaying of the pressed keys. -// <example> add testname Male testpass -// -// ban/banish yyyy/mm/dd hh:mm:ss <account name> -// Changes the final date of a banishment of an account. -// Like banset, but <account name> is at end. -// -// banadd <account_name> <modifier> -// Adds or substracts time from the final date of a banishment of an account. -// Modifier 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> banadd testname +1m-2mn1s-6y -// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. -// NOTE: If you modify the final date of a non-banished account, -// you fix the final date to (actual time +- adjustments) -// -// banset <account_name> yyyy/mm/dd [hh:mm:ss] -// Changes the final date of a banishment of an account. -// Default time [hh:mm:ss]: 23:59:59. -// banset <account_name> 0 -// Set a non-banished account (0 = unbanished). -// -// block <account name> -// Set state 5 (You have been blocked by the GM Team) to an account. -// Like state <account name> 5. -// -// check <account_name> <password> -// Check the validity of a password for an account -// NOTE: Server will never sends back a password. -// It's the only method you have to know if a password is correct. -// The other method is to have a ('physical') access to the accounts file. -// -// create <account_name> <sex> <email> <password> -// Like the 'add' command, but with e-mail moreover. -// <example> create testname Male my@mail.com testpass -// -// del <account name> -// Remove an account. -// This order requires confirmation. After confirmation, the account is deleted. -// -// email <account_name> <email> -// Modify the e-mail of an account. -// -// getcount -// Give the number of players online on all char-servers. -// -// gm <account_name> [GM_level] -// Modify the GM level of an account. -// Default value remove GM level (GM level = 0). -// <example> gm testname 80 -// -// id <account name> -// Give the id of an account. -// -// info <account_id> -// Display complete information of an account. -// -// kami <message> -// Sends a broadcast message on all map-server (in yellow). -// kamib <message> -// Sends a broadcast message on all map-server (in blue). -// -// language <language> -// Change the language of displaying. -// -// list/ls [start_id [end_id]] -// Display a list of accounts. -// 'start_id', 'end_id': indicate end and start identifiers. -// Research by name is not possible with this command. -// <example> list 10 9999999 -// -// listBan/lsBan [start_id [end_id]] -// Like list/ls, but only for accounts with state or banished -// -// listGM/lsGM [start_id [end_id]] -// Like list/ls, but only for GM accounts -// -// listOK/lsOK [start_id [end_id]] -// Like list/ls, but only for accounts without state and not banished -// -// memo <account_name> <memo> -// Modify the memo of an account. -// 'memo': it can have until 253 characters (with spaces or not). -// -// name <account_id> -// Give the name of an account. -// -// passwd <account_name> <new_password> -// Change the password of an account. -// When new password is omitted, the input is done without displaying of the pressed keys. -// -// quit/end/exit -// End of the program of administration -// -// reloadGM -// Reload GM configuration file -// -// search <expression> -// Seek accounts. -// Displays the accounts whose names correspond. -// search -r/-e/--expr/--regex <expression> -// Seek accounts by regular expression. -// Displays the accounts whose names correspond. -// -// sex <account_name> <sex> -// Modify the sex of an account. -// <example> sex testname Male -// -// state <account_name> <new_state> <error_message_#7> -// Change the state of an account. -// 'new_state': state is the state of the packet 0x006a + 1. The possibilities are: -// 0 = Account ok 6 = Your Game's EXE file is not the latest version -// 1 = Unregistered ID 7 = You are Prohibited to log in until %s -// 2 = Incorrect Password 8 = Server is jammed due to over populated -// 3 = This ID is expired 9 = No MSG -// 4 = Rejected from Server 100 = This ID has been totally erased -// 5 = You have been blocked by the GM Team -// all other values are 'No MSG', then use state 9 please. -// 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a) -// -// timeadd <account_name> <modifier> -// Adds or substracts time from the validity limit of an account. -// Modifier 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> timeadd testname +1m-2mn1s-6y -// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. -// NOTE: You can not modify a unlimited validity limit. -// If you want modify it, you want probably create a limited validity limit. -// So, at first, you must set the validity limit to a date/time. -// -// timeset <account_name> yyyy/mm/dd [hh:mm:ss] -// Changes the validity limit of an account. -// Default time [hh:mm:ss]: 23:59:59. -// timeset <account_name> 0 -// Gives an unlimited validity limit (0 = unlimited). -// -// unban/unbanish <account name> -// Unban an account. -// Like banset <account name> 0. -// -// unblock <account name> -// Set state 0 (Account ok) to an account. -// Like state <account name> 0. -// -// version -// Display the version of the login-server. -// -// who <account name> -// Displays complete information of an account. -// -//------------------------------------------------------------------------- -int login_fd; -int login_ip; -int bytes_to_read = 0; // flag to know if we waiting bytes from login-server -char command[1024]; -char parameters[1024]; -int list_first, list_last, list_type, list_count; // parameter to display a list of accounts -int already_exit_function = 0; // sometimes, the exit function is called twice... so, don't log twice the message - -//------------------------------ -// Writing function of logs file -//------------------------------ -int ladmin_log (const char *fmt, ...) -{ - FILE *logfp; - va_list ap; - struct timeval tv; - char tmpstr[2048]; - - va_start (ap, fmt); - - logfp = fopen_ (ladmin_log_filename, "a"); - if (logfp) - { - if (fmt[0] == '\0') // jump a line if no message - fprintf (logfp, "\n"); - else - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 24, date_format, localtime (&(tv.tv_sec))); - sprintf (tmpstr + strlen (tmpstr), ".%03d: %s", - (int) tv.tv_usec / 1000, fmt); - vfprintf (logfp, tmpstr, ap); - } - fclose_ (logfp); - } - - va_end (ap); - return 0; -} - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars (unsigned char *str) -{ - int i; - int change = 0; - - for (i = 0; str[i]; i++) - { - if (str[i] < 32) - { - str[i] = '_'; - change = 1; - } - } - - return change; -} - -//--------------------------------------------- -// Function to return ordonal text of a number. -//--------------------------------------------- -const char *makeordinal (int number) -{ - if (defaultlanguage == 'F') - { - if (number == 0) - return ""; - else if (number == 1) - return "er"; - else - return "ème"; - } - else - { - if ((number % 10) < 4 && (number % 10) != 0 - && (number < 10 || number > 20)) - { - if ((number % 10) == 1) - return "st"; - else if ((number % 10) == 2) - return "nd"; - else - return "rd"; - } - else - { - return "th"; - } - } - return ""; -} - -//----------------------------------------------------------------------------------------- -// Function to test of the validity of an account name (return 0 if incorrect, and 1 if ok) -//----------------------------------------------------------------------------------------- -int verify_accountname (char *account_name) -{ - int i; - - for (i = 0; account_name[i]; i++) - { - if (account_name[i] < 32) - { - if (defaultlanguage == 'F') - { - printf - ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", - i + 1, makeordinal (i + 1)); - ladmin_log - ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", - i + 1, makeordinal (i + 1)); - } - else - { - printf - ("Illegal character found in the account name (%d%s character).\n", - i + 1, makeordinal (i + 1)); - ladmin_log - ("Illegal character found in the account name (%d%s character).\n", - i + 1, makeordinal (i + 1)); - } - return 0; - } - } - - if (strlen (account_name) < 4) - { - if (defaultlanguage == 'F') - { - printf - ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); - ladmin_log - ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); - } - else - { - printf - ("Account name is too short. Please input an account name of 4-23 bytes.\n"); - ladmin_log - ("Account name is too short. Please input an account name of 4-23 bytes.\n"); - } - return 0; - } - - if (strlen (account_name) > 23) - { - if (defaultlanguage == 'F') - { - printf - ("Nom du compte trop long. Entrez un nom de compte de 4-23 caractères.\n"); - ladmin_log - ("Nom du compte trop long. Entrez un nom de compte de 4-23 caractères.\n"); - } - else - { - printf - ("Account name is too long. Please input an account name of 4-23 bytes.\n"); - ladmin_log - ("Account name is too long. Please input an account name of 4-23 bytes.\n"); - } - return 0; - } - - return 1; -} - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -int e_mail_check (unsigned char *email) -{ - char ch; - unsigned 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; -} - -//---------------------------------- -// Sub-function: Input of a password -//---------------------------------- -int typepasswd (char *password) -{ - char password1[1023], password2[1023]; - int letter; - int i; - - if (defaultlanguage == 'F') - { - ladmin_log - ("Aucun mot de passe n'a été donné. Demande d'un mot de passe.\n"); - } - else - { - ladmin_log ("No password was given. Request to obtain a password.\n"); - } - - memset (password1, '\0', sizeof (password1)); - memset (password2, '\0', sizeof (password2)); - if (defaultlanguage == 'F') - printf ("\033[1;36m Entrez le mot de passe > \033[0;32;42m"); - else - printf ("\033[1;36m Type the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar ()) != '\n') - password1[i++] = letter; - if (defaultlanguage == 'F') - printf - ("\033[0m\033[1;36m Ré-entrez le mot de passe > \033[0;32;42m"); - else - printf ("\033[0m\033[1;36m Verify the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar ()) != '\n') - password2[i++] = letter; - - printf ("\033[0m"); - fflush (stdout); - fflush (stdin); - - if (strcmp (password1, password2) != 0) - { - if (defaultlanguage == 'F') - { - printf - ("Erreur de vérification du mot de passe: Saisissez le même mot de passe svp.\n"); - ladmin_log - ("Erreur de vérification du mot de passe: Saisissez le même mot de passe svp.\n"); - ladmin_log (" Premier mot de passe: %s, second mot de passe: %s.\n", - password1, password2); - } - else - { - printf - ("Password verification failed. Please input same password.\n"); - ladmin_log - ("Password verification failed. Please input same password.\n"); - ladmin_log (" First password: %s, second password: %s.\n", - password1, password2); - } - return 0; - } - if (defaultlanguage == 'F') - { - ladmin_log ("Mot de passe saisi: %s.\n", password1); - } - else - { - ladmin_log ("Typed password: %s.\n", password1); - } - strcpy (password, password1); - return 1; -} - -//------------------------------------------------------------------------------------ -// Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok) -//------------------------------------------------------------------------------------ -int verify_password (char *password) -{ - int i; - - for (i = 0; password[i]; i++) - { - if (password[i] < 32) - { - if (defaultlanguage == 'F') - { - printf - ("Caractère interdit trouvé dans le mot de passe (%d%s caractère).\n", - i + 1, makeordinal (i + 1)); - ladmin_log - ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", - i + 1, makeordinal (i + 1)); - } - else - { - printf - ("Illegal character found in the password (%d%s character).\n", - i + 1, makeordinal (i + 1)); - ladmin_log - ("Illegal character found in the password (%d%s character).\n", - i + 1, makeordinal (i + 1)); - } - return 0; - } - } - - if (strlen (password) < 4) - { - if (defaultlanguage == 'F') - { - printf - ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); - ladmin_log - ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); - } - else - { - printf - ("Account name is too short. Please input an account name of 4-23 bytes.\n"); - ladmin_log - ("Account name is too short. Please input an account name of 4-23 bytes.\n"); - } - return 0; - } - - if (strlen (password) > 23) - { - if (defaultlanguage == 'F') - { - printf - ("Mot de passe trop long. Entrez un mot de passe de 4-23 caractères.\n"); - ladmin_log - ("Mot de passe trop long. Entrez un mot de passe de 4-23 caractères.\n"); - } - else - { - printf - ("Password is too long. Please input a password of 4-23 bytes.\n"); - ladmin_log - ("Password is too long. Please input a password of 4-23 bytes.\n"); - } - return 0; - } - - return 1; -} - -//------------------------------------------------------------------ -// Sub-function: Check the name of a command (return complete name) -//----------------------------------------------------------------- -int check_command (char *command) -{ -// help - if (strncmp (command, "aide", 2) == 0 && strncmp (command, "aide", strlen (command)) == 0) // not 1 letter command: 'aide' or 'add'? - strcpy (command, "aide"); - else if (strncmp (command, "help", 1) == 0 - && strncmp (command, "help", strlen (command)) == 0) - strcpy (command, "help"); -// general commands - else if (strncmp (command, "add", 2) == 0 && strncmp (command, "add", strlen (command)) == 0) // not 1 letter command: 'aide' or 'add'? - strcpy (command, "add"); - else if ((strncmp (command, "ban", 3) == 0 - && strncmp (command, "ban", strlen (command)) == 0) - || (strncmp (command, "banish", 4) == 0 - && strncmp (command, "banish", strlen (command)) == 0)) - strcpy (command, "ban"); - else if ((strncmp (command, "banadd", 4) == 0 && strncmp (command, "banadd", strlen (command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp (command, "ba") == 0) - strcpy (command, "banadd"); - else if ((strncmp (command, "banset", 4) == 0 && strncmp (command, "banset", strlen (command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp (command, "bs") == 0) - strcpy (command, "banset"); - else if (strncmp (command, "block", 2) == 0 - && strncmp (command, "block", strlen (command)) == 0) - strcpy (command, "block"); - else if (strncmp (command, "check", 2) == 0 && strncmp (command, "check", strlen (command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy (command, "check"); - else if (strncmp (command, "create", 2) == 0 && strncmp (command, "create", strlen (command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy (command, "create"); - else if (strncmp (command, "delete", 1) == 0 - && strncmp (command, "delete", strlen (command)) == 0) - strcpy (command, "delete"); - else if ((strncmp (command, "email", 2) == 0 && strncmp (command, "email", strlen (command)) == 0) || // not 1 letter command: 'email', 'end' or 'exit'? - (strncmp (command, "e-mail", 2) == 0 - && strncmp (command, "e-mail", strlen (command)) == 0)) - strcpy (command, "email"); - else if (strncmp (command, "getcount", 2) == 0 && strncmp (command, "getcount", strlen (command)) == 0) // not 1 letter command: 'getcount' or 'gm'? - strcpy (command, "getcount"); -// else if (strncmp(command, "gm", 2) == 0 && strncmp(command, "gm", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? -// strcpy(command, "gm"); -// else if (strncmp(command, "id", 2) == 0 && strncmp(command, "id", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? -// strcpy(command, "id"); - else if (strncmp (command, "info", 2) == 0 && strncmp (command, "info", strlen (command)) == 0) // not 1 letter command: 'id' or 'info'? - strcpy (command, "info"); -// else if (strncmp(command, "kami", 4) == 0 && strncmp(command, "kami", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kami"); -// else if (strncmp(command, "kamib", 5) == 0 && strncmp(command, "kamib", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kamib"); - else if ((strncmp (command, "language", 2) == 0 && strncmp (command, "language", strlen (command)) == 0)) // not 1 letter command: 'language' or 'list'? - strcpy (command, "language"); - else if ((strncmp (command, "list", 2) == 0 && strncmp (command, "list", strlen (command)) == 0) || // 'list' is default list command // not 1 letter command: 'language' or 'list'? - strcmp (command, "ls") == 0) - strcpy (command, "list"); - else if (strncmp (command, "itemfrob", 6) == 0) - strcpy (command, "itemfrob"); - else if ((strncmp (command, "listban", 5) == 0 - && strncmp (command, "listban", strlen (command)) == 0) - || (strncmp (command, "lsban", 3) == 0 - && strncmp (command, "lsban", strlen (command)) == 0) - || strcmp (command, "lb") == 0) - strcpy (command, "listban"); - else if ((strncmp (command, "listgm", 5) == 0 - && strncmp (command, "listgm", strlen (command)) == 0) - || (strncmp (command, "lsgm", 3) == 0 - && strncmp (command, "lsgm", strlen (command)) == 0) - || strcmp (command, "lg") == 0) - strcpy (command, "listgm"); - else if ((strncmp (command, "listok", 5) == 0 - && strncmp (command, "listok", strlen (command)) == 0) - || (strncmp (command, "lsok", 3) == 0 - && strncmp (command, "lsok", strlen (command)) == 0) - || strcmp (command, "lo") == 0) - strcpy (command, "listok"); - else if (strncmp (command, "memo", 1) == 0 - && strncmp (command, "memo", strlen (command)) == 0) - strcpy (command, "memo"); - else if (strncmp (command, "name", 1) == 0 - && strncmp (command, "name", strlen (command)) == 0) - strcpy (command, "name"); - else if ((strncmp (command, "password", 1) == 0 - && strncmp (command, "password", strlen (command)) == 0) - || strcmp (command, "passwd") == 0) - strcpy (command, "password"); - else if (strncmp (command, "reloadgm", 1) == 0 - && strncmp (command, "reloadgm", strlen (command)) == 0) - strcpy (command, "reloadgm"); - else if (strncmp (command, "search", 3) == 0 && strncmp (command, "search", strlen (command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy (command, "search"); // not 2 letters command: 'search' or 'sex'? -// else if (strncmp(command, "sex", 3) == 0 && strncmp(command, "sex", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? -// strcpy(command, "sex"); // not 2 letters command: 'search' or 'sex'? - else if (strncmp (command, "state", 2) == 0 && strncmp (command, "state", strlen (command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy (command, "state"); - else if ((strncmp (command, "timeadd", 5) == 0 && strncmp (command, "timeadd", strlen (command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp (command, "ta") == 0) - strcpy (command, "timeadd"); - else if ((strncmp (command, "timeset", 5) == 0 && strncmp (command, "timeset", strlen (command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp (command, "ts") == 0) - strcpy (command, "timeset"); - else if ((strncmp (command, "unban", 5) == 0 - && strncmp (command, "unban", strlen (command)) == 0) - || (strncmp (command, "unbanish", 4) == 0 - && strncmp (command, "unbanish", strlen (command)) == 0)) - strcpy (command, "unban"); - else if (strncmp (command, "unblock", 4) == 0 - && strncmp (command, "unblock", strlen (command)) == 0) - strcpy (command, "unblock"); - else if (strncmp (command, "version", 1) == 0 - && strncmp (command, "version", strlen (command)) == 0) - strcpy (command, "version"); - else if (strncmp (command, "who", 1) == 0 - && strncmp (command, "who", strlen (command)) == 0) - strcpy (command, "who"); -// quit - else if (strncmp (command, "quit", 1) == 0 - && strncmp (command, "quit", strlen (command)) == 0) - strcpy (command, "quit"); - else if (strncmp (command, "exit", 2) == 0 && strncmp (command, "exit", strlen (command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy (command, "exit"); - else if (strncmp (command, "end", 2) == 0 && strncmp (command, "end", strlen (command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy (command, "end"); - - return 0; -} - -//----------------------------------------- -// Sub-function: Display commands of ladmin -//----------------------------------------- -void display_help (char *param, int language) -{ - char command[1023]; - int i; - - memset (command, '\0', sizeof (command)); - - if (sscanf (param, "%s ", command) < 1 || strlen (command) == 0) - strcpy (command, ""); // any value that is not a command - - if (command[0] == '?') - { - if (defaultlanguage == 'F') - strcpy (command, "aide"); - else - strcpy (command, "help"); - } - - // lowercase for command - for (i = 0; command[i]; i++) - command[i] = tolower (command[i]); - - // Analyse of the command - check_command (command); // give complete name to the command - - if (defaultlanguage == 'F') - { - ladmin_log ("Affichage des commandes ou d'une commande.\n"); - } - else - { - ladmin_log ("Displaying of the commands or a command.\n"); - } - - if (language == 1) - { - if (strcmp (command, "aide") == 0) - { - printf ("aide/help/?\n"); - printf (" Affiche la description des commandes\n"); - printf ("aide/help/? [commande]\n"); - printf (" Affiche la description de la commande specifiée\n"); - } - else if (strcmp (command, "help") == 0) - { - printf ("aide/help/?\n"); - printf (" Display the description of the commands\n"); - printf ("aide/help/? [command]\n"); - printf (" Display the description of the specified command\n"); -// general commands - } - else if (strcmp (command, "add") == 0) - { - printf ("add <nomcompte> <sexe> <motdepasse>\n"); - printf (" Crée un compte avec l'email par défaut (a@a.com).\n"); - printf - (" Concernant le sexe, seule la première lettre compte (F ou M).\n"); - printf - (" L'e-mail est a@a.com (e-mail par défaut). C'est comme n'avoir aucun e-mail.\n"); - printf - (" Lorsque motdepasse est omis, la saisie se fait sans que la frappe se voit.\n"); - printf (" <exemple> add testname Male testpass\n"); - } - else if (strcmp (command, "ban") == 0) - { - printf ("ban/banish aaaa/mm/jj hh:mm:ss <nom compte>\n"); - printf (" Change la date de fin de bannissement d'un compte.\n"); - printf (" Comme banset, mais <nom compte> est à la fin.\n"); - } - else if (strcmp (command, "banadd") == 0) - { - printf ("banadd <nomcompte> <Modificateur>\n"); - printf - (" Ajoute ou soustrait du temps à la date de banissement d'un compte.\n"); - printf (" Les modificateurs sont construits comme suit:\n"); - printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf (" Elément modifié:\n"); - printf (" a ou y: année\n"); - printf (" m: mois\n"); - printf (" j ou d: jour\n"); - printf (" h: heure\n"); - printf (" mn: minute\n"); - printf (" s: seconde\n"); - printf (" <exemple> banadd testname +1m-2mn1s-6a\n"); - printf - (" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - printf - ("NOTE: Si vous modifez la date de banissement d'un compte non bani,\n"); - printf - (" vous indiquez comme date (le moment actuel +- les ajustements)\n"); - } - else if (strcmp (command, "banset") == 0) - { - printf ("banset <nomcompte> aaaa/mm/jj [hh:mm:ss]\n"); - printf (" Change la date de fin de bannissement d'un compte.\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - printf ("banset <nomcompte> 0\n"); - printf (" Débanni un compte (0 = de-banni).\n"); - } - else if (strcmp (command, "block") == 0) - { - printf ("block <nom compte>\n"); - printf - (" Place le status d'un compte à 5 (You have been blocked by the GM Team).\n"); - printf - (" La commande est l'équivalent de state <nom_compte> 5.\n"); - } - else if (strcmp (command, "check") == 0) - { - printf ("check <nomcompte> <motdepasse>\n"); - printf - (" Vérifie la validité d'un mot de passe pour un compte\n"); - printf (" NOTE: Le serveur n'enverra jamais un mot de passe.\n"); - printf - (" C'est la seule méthode que vous possédez pour savoir\n"); - printf - (" si un mot de passe est le bon. L'autre méthode est\n"); - printf - (" d'avoir un accès ('physique') au fichier des comptes.\n"); - } - else if (strcmp (command, "create") == 0) - { - printf ("create <nomcompte> <sexe> <email> <motdepasse>\n"); - printf (" Comme la commande add, mais avec l'e-mail en plus.\n"); - printf - (" <exemple> create testname Male mon@mail.com testpass\n"); - } - else if (strcmp (command, "delete") == 0) - { - printf ("del <nom compte>\n"); - printf (" Supprime un compte.\n"); - printf - (" La commande demande confirmation. Après confirmation, le compte est détruit.\n"); - } - else if (strcmp (command, "email") == 0) - { - printf ("email <nomcompte> <email>\n"); - printf (" Modifie l'e-mail d'un compte.\n"); - } - else if (strcmp (command, "getcount") == 0) - { - printf ("getcount\n"); - printf - (" Donne le nombre de joueurs en ligne par serveur de char.\n"); - } - else if (strcmp (command, "gm") == 0) - { - printf ("gm <nomcompte> [Niveau_GM]\n"); - printf (" Modifie le niveau de GM d'un compte.\n"); - printf - (" Valeur par défaut: 0 (suppression du niveau de GM).\n"); - printf (" <exemple> gm nomtest 80\n"); - } - else if (strcmp (command, "id") == 0) - { - printf ("id <nom compte>\n"); - printf (" Donne l'id d'un compte.\n"); - } - else if (strcmp (command, "info") == 0) - { - printf ("info <idcompte>\n"); - printf (" Affiche les informations sur un compte.\n"); - } - else if (strcmp (command, "kami") == 0) - { - printf ("kami <message>\n"); - printf - (" Envoi un message général sur tous les serveurs de map (en jaune).\n"); - } - else if (strcmp (command, "kamib") == 0) - { - printf ("kamib <message>\n"); - printf - (" Envoi un message général sur tous les serveurs de map (en bleu).\n"); - } - else if (strcmp (command, "language") == 0) - { - printf ("language <langue>\n"); - printf (" Change la langue d'affichage.\n"); - printf (" Langues possibles: 'Français' ou 'English'.\n"); - } - else if (strcmp (command, "list") == 0) - { - printf ("list/ls [Premier_id [Dernier_id]]\n"); - printf (" Affiche une liste de comptes.\n"); - printf - (" 'Premier_id', 'Dernier_id': indique les identifiants de départ et de fin.\n"); - printf - (" La recherche par nom n'est pas possible avec cette commande.\n"); - printf (" <example> list 10 9999999\n"); - } - else if (strcmp (command, "itemfrob") == 0) - { - printf ("Not localised yet.\n"); - } - else if (strcmp (command, "listban") == 0) - { - printf ("listBan/lsBan [Premier_id [Dernier_id]]\n"); - printf - (" Comme list/ls, mais seulement pour les comptes avec statut ou bannis.\n"); - } - else if (strcmp (command, "listgm") == 0) - { - printf ("listGM/lsGM [Premier_id [Dernier_id]]\n"); - printf (" Comme list/ls, mais seulement pour les comptes GM.\n"); - } - else if (strcmp (command, "listok") == 0) - { - printf ("listOK/lsOK [Premier_id [Dernier_id]]\n"); - printf - (" Comme list/ls, mais seulement pour les comptes sans statut et non bannis.\n"); - } - else if (strcmp (command, "memo") == 0) - { - printf ("memo <nomcompte> <memo>\n"); - printf (" Modifie le mémo d'un compte.\n"); - printf - (" 'memo': Il peut avoir jusqu'à 253 caractères (avec des espaces ou non).\n"); - } - else if (strcmp (command, "name") == 0) - { - printf ("name <idcompte>\n"); - printf (" Donne le nom d'un compte.\n"); - } - else if (strcmp (command, "password") == 0) - { - printf ("passwd <nomcompte> <nouveaumotdepasse>\n"); - printf (" Change le mot de passe d'un compte.\n"); - printf (" Lorsque nouveaumotdepasse est omis,\n"); - printf (" la saisie se fait sans que la frappe ne se voit.\n"); - } - else if (strcmp (command, "reloadgm") == 0) - { - printf ("reloadGM\n"); - printf (" Reload GM configuration file\n"); - } - else if (strcmp (command, "search") == 0) - { - printf ("search <expression>\n"); - printf (" Cherche des comptes.\n"); - printf (" Affiche les comptes dont les noms correspondent.\n"); -// printf("search -r/-e/--expr/--regex <expression>\n"); -// printf(" Cherche des comptes par expression regulière.\n"); -// printf(" Affiche les comptes dont les noms correspondent.\n"); - } - else if (strcmp (command, "sex") == 0) - { - printf ("sex <nomcompte> <sexe>\n"); - printf (" Modifie le sexe d'un compte.\n"); - printf (" <exemple> sex testname Male\n"); - } - else if (strcmp (command, "state") == 0) - { - printf ("state <nomcompte> <nouveaustatut> <message_erreur_7>\n"); - printf (" Change le statut d'un compte.\n"); - printf - (" 'nouveaustatut': Le statut est le même que celui du packet 0x006a + 1.\n"); - printf (" les possibilités sont:\n"); - printf (" 0 = Compte ok\n"); - printf (" 1 = Unregistered ID\n"); - printf (" 2 = Incorrect Password\n"); - printf (" 3 = This ID is expired\n"); - printf (" 4 = Rejected from Server\n"); - printf - (" 5 = You have been blocked by the GM Team\n"); - printf - (" 6 = Your Game's EXE file is not the latest version\n"); - printf - (" 7 = You are Prohibited to log in until...\n"); - printf - (" 8 = Server is jammed due to over populated\n"); - printf (" 9 = No MSG\n"); - printf (" 100 = This ID has been totally erased\n"); - printf - (" all other values are 'No MSG', then use state 9 please.\n"); - printf (" 'message_erreur_7': message du code erreur 6 =\n"); - printf - (" = Your are Prohibited to log in until... (packet 0x006a)\n"); - } - else if (strcmp (command, "timeadd") == 0) - { - printf ("timeadd <nomcompte> <modificateur>\n"); - printf - (" Ajoute/soustrait du temps à la limite de validité d'un compte.\n"); - printf (" Le modificateur est composé comme suit:\n"); - printf (" Valeur modificatrice (-1, 1, +1, etc...)\n"); - printf (" Elément modifié:\n"); - printf (" a ou y: année\n"); - printf (" m: mois\n"); - printf (" j ou d: jour\n"); - printf (" h: heure\n"); - printf (" mn: minute\n"); - printf (" s: seconde\n"); - printf (" <exemple> timeadd testname +1m-2mn1s-6a\n"); - printf - (" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - printf - ("NOTE: Vous ne pouvez pas modifier une limite de validité illimitée. Si vous\n"); - printf - (" désirez le faire, c'est que vous voulez probablement créer un limite de\n"); - printf - (" validité limitée. Donc, en premier, fixé une limite de valitidé.\n"); - } - else if (strcmp (command, "timeadd") == 0) - { - printf ("timeset <nomcompte> aaaa/mm/jj [hh:mm:ss]\n"); - printf (" Change la limite de validité d'un compte.\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - printf ("timeset <nomcompte> 0\n"); - printf - (" Donne une limite de validité illimitée (0 = illimitée).\n"); - } - else if (strcmp (command, "unban") == 0) - { - printf ("unban/unbanish <nom compte>\n"); - printf (" Ote le banissement d'un compte.\n"); - printf - (" La commande est l'équivalent de banset <nom_compte> 0.\n"); - } - else if (strcmp (command, "unblock") == 0) - { - printf ("unblock <nom compte>\n"); - printf (" Place le status d'un compte à 0 (Compte ok).\n"); - printf - (" La commande est l'équivalent de state <nom_compte> 0.\n"); - } - else if (strcmp (command, "version") == 0) - { - printf ("version\n"); - printf (" Affiche la version du login-serveur.\n"); - } - else if (strcmp (command, "who") == 0) - { - printf ("who <nom compte>\n"); - printf (" Affiche les informations sur un compte.\n"); -// quit - } - else if (strcmp (command, "quit") == 0 || - strcmp (command, "exit") == 0 || - strcmp (command, "end") == 0) - { - printf ("quit/end/exit\n"); - printf (" Fin du programme d'administration.\n"); -// unknown command - } - else - { - if (strlen (command) > 0) - printf - ("Commande inconnue [%s] pour l'aide. Affichage de toutes les commandes.\n", - command); - printf - (" aide/help/? -- Affiche cet aide\n"); - printf - (" aide/help/? [commande] -- Affiche l'aide de la commande\n"); - printf - (" add <nomcompte> <sexe> <motdepasse> -- Crée un compte (sans email)\n"); - printf - (" ban/banish aaaa/mm/jj hh:mm:ss <nom compte> -- Fixe la date finale de banismnt\n"); - printf - (" banadd/ba <nomcompte> <modificateur> -- Ajout/soustrait du temps à la\n"); - printf - (" exemple: ba moncompte +1m-2mn1s-2y date finale de banissement\n"); - printf - (" banset/bs <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la date fin de banisemnt\n"); - printf - (" banset/bs <nomcompte> 0 -- Dé-banis un compte.\n"); - printf - (" block <nom compte> -- Mets le status d'un compte à 5 (blocked by the GM Team)\n"); - printf - (" check <nomcompte> <motdepasse> -- Vérifie un mot de passe d'un compte\n"); - printf - (" create <nomcompte> <sexe> <email> <motdepasse> -- Crée un compte (avec email)\n"); - printf - (" del <nom compte> -- Supprime un compte\n"); - printf - (" email <nomcompte> <email> -- Modifie l'e-mail d'un compte\n"); - printf - (" getcount -- Donne le nb de joueurs en ligne\n"); - printf - (" gm <nomcompte> [Niveau_GM] -- Modifie le niveau de GM d'un compte\n"); - printf - (" id <nom compte> -- Donne l'id d'un compte\n"); - printf - (" info <idcompte> -- Affiche les infos sur un compte\n"); - printf - (" kami <message> -- Envoi un message général (en jaune)\n"); - printf - (" kamib <message> -- Envoi un message général (en bleu)\n"); - printf - (" language <langue> -- Change la langue d'affichage.\n"); - printf - (" list/ls [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf - (" listBan/lsBan [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf - (" avec un statut ou bannis\n"); - printf - (" listGM/lsGM [Premier_id [Dernier_id] ] -- Affiche une liste de comptes GM\n"); - printf - (" listOK/lsOK [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf - (" sans status et non bannis\n"); - printf - (" memo <nomcompte> <memo> -- Modifie le memo d'un compte\n"); - printf - (" name <idcompte> -- Donne le nom d'un compte\n"); - printf - (" passwd <nomcompte> <nouveaumotdepasse> -- Change le mot de passe d'un compte\n"); - printf - (" quit/end/exit -- Fin du programme d'administation\n"); - printf - (" reloadGM -- Recharger le fichier de config des GM\n"); - printf - (" search <expression> -- Cherche des comptes\n"); -// printf(" search -e/-r/--expr/--regex <expression> -- Cherche des comptes par REGEX\n"); - printf - (" sex <nomcompte> <sexe> -- Modifie le sexe d'un compte\n"); - printf - (" state <nomcompte> <nouveaustatut> <messageerr7> -- Change le statut d'1 compte\n"); - printf - (" timeadd/ta <nomcompte> <modificateur> -- Ajout/soustrait du temps à la\n"); - printf - (" exemple: ta moncompte +1m-2mn1s-2y limite de validité\n"); - printf - (" timeset/ts <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la limite de validité\n"); - printf - (" timeset/ts <nomcompte> 0 -- limite de validité = illimitée\n"); - printf - (" unban/unbanish <nom compte> -- Ote le banissement d'un compte\n"); - printf - (" unblock <nom compte> -- Mets le status d'un compte à 0 (Compte ok)\n"); - printf - (" version -- Donne la version du login-serveur\n"); - printf - (" who <nom compte> -- Affiche les infos sur un compte\n"); - printf - (" Note: Pour les noms de compte avec des espaces, tapez \"<nom compte>\" (ou ').\n"); - } - } - else - { - if (strcmp (command, "aide") == 0) - { - printf ("aide/help/?\n"); - printf (" Display the description of the commands\n"); - printf ("aide/help/? [command]\n"); - printf (" Display the description of the specified command\n"); - } - else if (strcmp (command, "help") == 0) - { - printf ("aide/help/?\n"); - printf (" Display the description of the commands\n"); - printf ("aide/help/? [command]\n"); - printf (" Display the description of the specified command\n"); -// general commands - } - else if (strcmp (command, "add") == 0) - { - printf ("add <account_name> <sex> <password>\n"); - printf - (" Create an account with the default email (a@a.com).\n"); - printf - (" Concerning the sex, only the first letter is used (F or M).\n"); - printf - (" The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail.\n"); - printf (" When the password is omitted,\n"); - printf - (" the input is done without displaying of the pressed keys.\n"); - printf (" <example> add testname Male testpass\n"); - } - else if (strcmp (command, "ban") == 0) - { - printf ("ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); - printf - (" Changes the final date of a banishment of an account.\n"); - printf (" Like banset, but <account name> is at end.\n"); - } - else if (strcmp (command, "banadd") == 0) - { - printf ("banadd <account_name> <modifier>\n"); - printf - (" Adds or substracts time from the final date of a banishment of an account.\n"); - printf (" Modifier is done as follows:\n"); - printf (" Adjustment value (-1, 1, +1, etc...)\n"); - printf (" Modified element:\n"); - printf (" a or y: year\n"); - printf (" m: month\n"); - printf (" j or d: day\n"); - printf (" h: hour\n"); - printf (" mn: minute\n"); - printf (" s: second\n"); - printf (" <example> banadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - printf - ("NOTE: If you modify the final date of a non-banished account,\n"); - printf - (" you fix the final date to (actual time +- adjustments)\n"); - } - else if (strcmp (command, "banset") == 0) - { - printf ("banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf - (" Changes the final date of a banishment of an account.\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - printf ("banset <account_name> 0\n"); - printf (" Set a non-banished account (0 = unbanished).\n"); - } - else if (strcmp (command, "block") == 0) - { - printf ("block <account name>\n"); - printf - (" Set state 5 (You have been blocked by the GM Team) to an account.\n"); - printf (" This command works like state <account_name> 5.\n"); - } - else if (strcmp (command, "check") == 0) - { - printf ("check <account_name> <password>\n"); - printf (" Check the validity of a password for an account.\n"); - printf (" NOTE: Server will never sends back a password.\n"); - printf - (" It's the only method you have to know if a password is correct.\n"); - printf - (" The other method is to have a ('physical') access to the accounts file.\n"); - } - else if (strcmp (command, "create") == 0) - { - printf ("create <account_name> <sex> <email> <password>\n"); - printf (" Like the 'add' command, but with e-mail moreover.\n"); - printf - (" <example> create testname Male my@mail.com testpass\n"); - } - else if (strcmp (command, "delete") == 0) - { - printf ("del <account name>\n"); - printf (" Remove an account.\n"); - printf - (" This order requires confirmation. After confirmation, the account is deleted.\n"); - } - else if (strcmp (command, "email") == 0) - { - printf ("email <account_name> <email>\n"); - printf (" Modify the e-mail of an account.\n"); - } - else if (strcmp (command, "getcount") == 0) - { - printf ("getcount\n"); - printf - (" Give the number of players online on all char-servers.\n"); - } - else if (strcmp (command, "gm") == 0) - { - printf ("gm <account_name> [GM_level]\n"); - printf (" Modify the GM level of an account.\n"); - printf (" Default value remove GM level (GM level = 0).\n"); - printf (" <example> gm testname 80\n"); - } - else if (strcmp (command, "id") == 0) - { - printf ("id <account name>\n"); - printf (" Give the id of an account.\n"); - } - else if (strcmp (command, "info") == 0) - { - printf ("info <account_id>\n"); - printf (" Display complete information of an account.\n"); - } - else if (strcmp (command, "kami") == 0) - { - printf ("kami <message>\n"); - printf - (" Sends a broadcast message on all map-server (in yellow).\n"); - } - else if (strcmp (command, "kamib") == 0) - { - printf ("kamib <message>\n"); - printf - (" Sends a broadcast message on all map-server (in blue).\n"); - } - else if (strcmp (command, "language") == 0) - { - printf ("language <language>\n"); - printf (" Change the language of displaying.\n"); - printf (" Possible languages: Français or English.\n"); - } - else if (strcmp (command, "list") == 0) - { - printf ("list/ls [start_id [end_id]]\n"); - printf (" Display a list of accounts.\n"); - printf - (" 'start_id', 'end_id': indicate end and start identifiers.\n"); - printf - (" Research by name is not possible with this command.\n"); - printf (" <example> list 10 9999999\n"); - } - else if (strcmp (command, "itemfrob") == 0) - { - printf ("itemfrob <source-id> <dest-id>\n"); - printf (" Translates item IDs for all accounts.\n"); - printf - (" Any items matching the source item ID will be mapped to the dest-id.\n"); - printf (" <example> itemfrob 500 700\n"); - } - else if (strcmp (command, "listban") == 0) - { - printf ("listBan/lsBan [start_id [end_id]]\n"); - printf - (" Like list/ls, but only for accounts with state or banished.\n"); - } - else if (strcmp (command, "listgm") == 0) - { - printf ("listGM/lsGM [start_id [end_id]]\n"); - printf (" Like list/ls, but only for GM accounts.\n"); - } - else if (strcmp (command, "listok") == 0) - { - printf ("listOK/lsOK [start_id [end_id]]\n"); - printf - (" Like list/ls, but only for accounts without state and not banished.\n"); - } - else if (strcmp (command, "memo") == 0) - { - printf ("memo <account_name> <memo>\n"); - printf (" Modify the memo of an account.\n"); - printf - (" 'memo': it can have until 253 characters (with spaces or not).\n"); - } - else if (strcmp (command, "name") == 0) - { - printf ("name <account_id>\n"); - printf (" Give the name of an account.\n"); - } - else if (strcmp (command, "password") == 0) - { - printf ("passwd <account_name> <new_password>\n"); - printf (" Change the password of an account.\n"); - printf (" When new password is omitted,\n"); - printf - (" the input is done without displaying of the pressed keys.\n"); - } - else if (strcmp (command, "reloadgm") == 0) - { - printf ("reloadGM\n"); - printf (" Reload GM configuration file\n"); - } - else if (strcmp (command, "search") == 0) - { - printf ("search <expression>\n"); - printf (" Seek accounts.\n"); - printf (" Displays the accounts whose names correspond.\n"); -// printf("search -r/-e/--expr/--regex <expression>\n"); -// printf(" Seek accounts by regular expression.\n"); -// printf(" Displays the accounts whose names correspond.\n"); - } - else if (strcmp (command, "sex") == 0) - { - printf ("sex <account_name> <sex>\n"); - printf (" Modify the sex of an account.\n"); - printf (" <example> sex testname Male\n"); - } - else if (strcmp (command, "state") == 0) - { - printf ("state <account_name> <new_state> <error_message_#7>\n"); - printf (" Change the state of an account.\n"); - printf - (" 'new_state': state is the state of the packet 0x006a + 1.\n"); - printf (" The possibilities are:\n"); - printf (" 0 = Account ok\n"); - printf (" 1 = Unregistered ID\n"); - printf (" 2 = Incorrect Password\n"); - printf (" 3 = This ID is expired\n"); - printf (" 4 = Rejected from Server\n"); - printf - (" 5 = You have been blocked by the GM Team\n"); - printf - (" 6 = Your Game's EXE file is not the latest version\n"); - printf - (" 7 = You are Prohibited to log in until...\n"); - printf - (" 8 = Server is jammed due to over populated\n"); - printf (" 9 = No MSG\n"); - printf (" 100 = This ID has been totally erased\n"); - printf - (" all other values are 'No MSG', then use state 9 please.\n"); - printf (" 'error_message_#7': message of the code error 6\n"); - printf - (" = Your are Prohibited to log in until... (packet 0x006a)\n"); - } - else if (strcmp (command, "timeadd") == 0) - { - printf ("timeadd <account_name> <modifier>\n"); - printf - (" Adds or substracts time from the validity limit of an account.\n"); - printf (" Modifier is done as follows:\n"); - printf (" Adjustment value (-1, 1, +1, etc...)\n"); - printf (" Modified element:\n"); - printf (" a or y: year\n"); - printf (" m: month\n"); - printf (" j or d: day\n"); - printf (" h: hour\n"); - printf (" mn: minute\n"); - printf (" s: second\n"); - printf (" <example> timeadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - printf ("NOTE: You can not modify a unlimited validity limit.\n"); - printf - (" If you want modify it, you want probably create a limited validity limit.\n"); - printf - (" So, at first, you must set the validity limit to a date/time.\n"); - } - else if (strcmp (command, "timeadd") == 0) - { - printf ("timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf (" Changes the validity limit of an account.\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - printf ("timeset <account_name> 0\n"); - printf (" Gives an unlimited validity limit (0 = unlimited).\n"); - } - else if (strcmp (command, "unban") == 0) - { - printf ("unban/unbanish <account name>\n"); - printf (" Remove the banishment of an account.\n"); - printf (" This command works like banset <account_name> 0.\n"); - } - else if (strcmp (command, "unblock") == 0) - { - printf ("unblock <account name>\n"); - printf (" Set state 0 (Account ok) to an account.\n"); - printf (" This command works like state <account_name> 0.\n"); - } - else if (strcmp (command, "version") == 0) - { - printf ("version\n"); - printf (" Display the version of the login-server.\n"); - } - else if (strcmp (command, "who") == 0) - { - printf ("who <account name>\n"); - printf (" Displays complete information of an account.\n"); -// quit - } - else if (strcmp (command, "quit") == 0 || - strcmp (command, "exit") == 0 || - strcmp (command, "end") == 0) - { - printf ("quit/end/exit\n"); - printf (" End of the program of administration.\n"); -// unknown command - } - else - { - if (strlen (command) > 0) - printf - ("Unknown command [%s] for help. Displaying of all commands.\n", - command); - printf - (" aide/help/? -- Display this help\n"); - printf - (" aide/help/? [command] -- Display the help of the command\n"); - printf - (" add <account_name> <sex> <password> -- Create an account with default email\n"); - printf - (" ban/banish yyyy/mm/dd hh:mm:ss <account name> -- Change final date of a ban\n"); - printf - (" banadd/ba <account_name> <modifier> -- Add or substract time from the final\n"); - printf - (" example: ba apple +1m-2mn1s-2y date of a banishment of an account\n"); - printf - (" banset/bs <account_name> yyyy/mm/dd [hh:mm:ss] -- Change final date of a ban\n"); - printf - (" banset/bs <account_name> 0 -- Un-banish an account\n"); - printf - (" block <account name> -- Set state 5 (blocked by the GM Team) to an account\n"); - printf - (" check <account_name> <password> -- Check the validity of a password\n"); - printf - (" create <account_name> <sex> <email> <passwrd> -- Create an account with email\n"); - printf - (" del <account name> -- Remove an account\n"); - printf - (" email <account_name> <email> -- Modify an email of an account\n"); - printf - (" getcount -- Give the number of players online\n"); - printf - (" gm <account_name> [GM_level] -- Modify the GM level of an account\n"); - printf - (" id <account name> -- Give the id of an account\n"); - printf - (" info <account_id> -- Display all information of an account\n"); - printf - (" itemfrob <source-id> <dest-id> -- Map all items from one item ID to another\n"); - printf - (" kami <message> -- Sends a broadcast message (in yellow)\n"); - printf - (" kamib <message> -- Sends a broadcast message (in blue)\n"); - printf - (" language <language> -- Change the language of displaying.\n"); - printf - (" list/ls [First_id [Last_id]] -- Display a list of accounts\n"); - printf - (" listBan/lsBan [First_id [Last_id] ] -- Display a list of accounts\n"); - printf - (" with state or banished\n"); - printf - (" listGM/lsGM [First_id [Last_id]] -- Display a list of GM accounts\n"); - printf - (" listOK/lsOK [First_id [Last_id] ] -- Display a list of accounts\n"); - printf - (" without state and not banished\n"); - printf - (" memo <account_name> <memo> -- Modify the memo of an account\n"); - printf - (" name <account_id> -- Give the name of an account\n"); - printf - (" passwd <account_name> <new_password> -- Change the password of an account\n"); - printf - (" quit/end/exit -- End of the program of administation\n"); - printf - (" reloadGM -- Reload GM configuration file\n"); - printf - (" search <expression> -- Seek accounts\n"); -// printf(" search -e/-r/--expr/--regex <expressn> -- Seek accounts by regular-expression\n"); - printf - (" sex <nomcompte> <sexe> -- Modify the sex of an account\n"); - printf - (" state <account_name> <new_state> <error_message_#7> -- Change the state\n"); - printf - (" timeadd/ta <account_name> <modifier> -- Add or substract time from the\n"); - printf - (" example: ta apple +1m-2mn1s-2y validity limit of an account\n"); - printf - (" timeset/ts <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"); - printf - (" timeset/ts <account_name> 0 -- Give a unlimited validity limit\n"); - printf - (" unban/unbanish <account name> -- Remove the banishment of an account\n"); - printf - (" unblock <account name> -- Set state 0 (Account ok) to an account\n"); - printf - (" version -- Gives the version of the login-server\n"); - printf - (" who <account name> -- Display all information of an account\n"); - printf - (" who <account name> -- Display all information of an account\n"); - printf - (" Note: To use spaces in an account name, type \"<account name>\" (or ').\n"); - } - } -} - -//----------------------------- -// Sub-function: add an account -//----------------------------- -int addaccount (char *param, int emailflag) -{ - char name[1023], sex[1023], email[1023], password[1023]; -// int i; - - memset (name, '\0', sizeof (name)); - memset (sex, '\0', sizeof (sex)); - memset (email, '\0', sizeof (email)); - memset (password, '\0', sizeof (password)); - - if (emailflag == 0) - { // add command - if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf (param, "'%[^']' %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf (param, "%s %s %[^\r\n]", name, sex, password) < 2) - { // password can be void - if (defaultlanguage == 'F') - { - printf - ("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); - printf ("<exemple> add nomtest Male motdepassetest\n"); - ladmin_log - ("Nombre incorrect de paramètres pour créer un compte (commande 'add').\n"); - } - else - { - printf - ("Please input an account name, a sex and a password.\n"); - printf ("<example> add testname Male testpass\n"); - ladmin_log - ("Incomplete parameters to create an account ('add' command).\n"); - } - return 136; - } - strcpy (email, "a@a.com"); // default email - } - else - { // 1: create command - if (sscanf (param, "\"%[^\"]\" %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf (param, "'%[^']' %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf (param, "%s %s %s %[^\r\n]", name, sex, email, - password) < 3) - { // password can be void - if (defaultlanguage == 'F') - { - printf - ("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); - printf - ("<exemple> create nomtest Male mo@mail.com motdepassetest\n"); - ladmin_log - ("Nombre incorrect de paramètres pour créer un compte (commande 'create').\n"); - } - else - { - printf - ("Please input an account name, a sex and a password.\n"); - printf - ("<example> create testname Male my@mail.com testpass\n"); - ladmin_log - ("Incomplete parameters to create an account ('create' command).\n"); - } - return 136; - } - } - if (verify_accountname (name) == 0) - { - return 102; - } - -/* for(i = 0; name[i]; i++) { - if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_", name[i]) == NULL) { - if (defaultlanguage == 'F') { - printf("Caractère interdit (%c) trouvé dans le nom du compte (%d%s caractère).\n", name[i], i+1, makeordinal(i+1)); - ladmin_log("Caractère interdit (%c) trouvé dans le nom du compte (%d%s caractère).\n", name[i], i+1, makeordinal(i+1)); - } else { - printf("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); - ladmin_log("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); - } - return 101; - } - }*/ - - sex[0] = toupper (sex[0]); - if (strchr ("MF", sex[0]) == NULL) - { - if (defaultlanguage == 'F') - { - printf ("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); - ladmin_log ("Sexe incorrect [%s]. Entrez M ou F svp.\n", - sex); - } - else - { - printf ("Illegal gender [%s]. Please input M or F.\n", sex); - ladmin_log ("Illegal gender [%s]. Please input M or F.\n", - sex); - } - return 103; - } - - if (strlen (email) < 3) - { - if (defaultlanguage == 'F') - { - printf ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", - email); - ladmin_log - ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", - email); - } - else - { - printf ("Email is too short [%s]. Please input a valid e-mail.\n", - email); - ladmin_log - ("Email is too short [%s]. Please input a valid e-mail.\n", - email); - } - return 109; - } - if (strlen (email) > 39) - { - if (defaultlanguage == 'F') - { - printf - ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", - email); - ladmin_log - ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", - email); - } - else - { - printf - ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - ladmin_log - ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - } - return 109; - } - if (e_mail_check (email) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", - email); - ladmin_log ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", - email); - } - else - { - printf ("Invalid email [%s]. Please input a valid e-mail.\n", - email); - ladmin_log ("Invalid email [%s]. Please input a valid e-mail.\n", - email); - } - return 109; - } - - if (strlen (password) == 0) - { - if (typepasswd (password) == 0) - return 108; - } - if (verify_password (password) == 0) - return 104; - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour créer un compte.\n"); - } - else - { - ladmin_log ("Request to login-server to create an account.\n"); - } - - WFIFOW (login_fd, 0) = 0x7930; - memcpy (WFIFOP (login_fd, 2), name, 24); - memcpy (WFIFOP (login_fd, 26), password, 24); - WFIFOB (login_fd, 50) = sex[0]; - memcpy (WFIFOP (login_fd, 51), email, 40); - WFIFOSET (login_fd, 91); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------------------------------------------- -// Sub-function: Add/substract time to the final date of a banishment of an account -//--------------------------------------------------------------------------------- -int banaddaccount (char *param) -{ - char name[1023], modif[1023]; - int year, month, day, hour, minute, second; - char *p_modif; - int value, i; - - memset (name, '\0', sizeof (name)); - memset (modif, '\0', sizeof (modif)); - year = month = day = hour = minute = second = 0; - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf (param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf (param, "%s %[^\r\n]", name, modif) < 2) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un modificateur svp.\n"); - printf (" <exemple> banadd nomtest +1m-2mn1s-6y\n"); - printf - (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour modifier la fin de ban d'un compte (commande 'banadd').\n"); - } - else - { - printf ("Please input an account name and a modifier.\n"); - printf (" <example>: banadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - ladmin_log - ("Incomplete parameters to modify the ban date/time of an account ('banadd' command).\n"); - } - return 136; - } - if (verify_accountname (name) == 0) - { - return 102; - } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower (modif[i]); - p_modif = modif; - while (strlen (p_modif) > 0) - { - value = atoi (p_modif); - if (value == 0) - { - p_modif++; - } - else - { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen (p_modif) > 0 && p_modif[0] >= '0' - && p_modif[0] <= '9') - { - p_modif++; - } - if (p_modif[0] == 's') - { - second = value; - p_modif++; - } - else if (p_modif[0] == 'm' && p_modif[1] == 'n') - { - minute = value; - p_modif += 2; - } - else if (p_modif[0] == 'h') - { - hour = value; - p_modif++; - } - else if (p_modif[0] == 'd' || p_modif[0] == 'j') - { - day = value; - p_modif += 2; - } - else if (p_modif[0] == 'm') - { - month = value; - p_modif++; - } - else if (p_modif[0] == 'y' || p_modif[0] == 'a') - { - year = value; - p_modif++; - } - else - { - p_modif++; - } - } - } - - if (defaultlanguage == 'F') - { - printf (" année: %d\n", year); - printf (" mois: %d\n", month); - printf (" jour: %d\n", day); - printf (" heure: %d\n", hour); - printf (" minute: %d\n", minute); - printf (" seconde: %d\n", second); - } - else - { - printf (" year: %d\n", year); - printf (" month: %d\n", month); - printf (" day: %d\n", day); - printf (" hour: %d\n", hour); - printf (" minute: %d\n", minute); - printf (" second: %d\n", second); - } - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 - && second == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Vous devez entrer un ajustement avec cette commande, svp:\n"); - printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf (" Element modifié:\n"); - printf (" a ou y: année\n"); - printf (" m: mois\n"); - printf (" j ou d: jour\n"); - printf (" h: heure\n"); - printf (" mn: minute\n"); - printf (" s: seconde\n"); - printf (" <exemple> banadd nomtest +1m-2mn1s-6y\n"); - printf - (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - ladmin_log - ("Aucun ajustement n'est pas un ajustement (commande 'banadd').\n"); - } - else - { - printf ("Please give an adjustment with this command:\n"); - printf (" Adjustment value (-1, 1, +1, etc...)\n"); - printf (" Modified element:\n"); - printf (" a or y: year\n"); - printf (" m: month\n"); - printf (" j or d: day\n"); - printf (" h: hour\n"); - printf (" mn: minute\n"); - printf (" s: second\n"); - printf (" <example> banadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - ladmin_log - ("No adjustment isn't an adjustment ('banadd' command).\n"); - } - return 137; - } - if (year > 127 || year < -127) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement d'années correct (de -127 à 127), svp.\n"); - ladmin_log - ("Ajustement de l'année hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the years (from -127 to 127).\n"); - ladmin_log - ("Abnormal adjustement for the year ('banadd' command).\n"); - } - return 137; - } - if (month > 255 || month < -255) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de mois correct (de -255 à 255), svp.\n"); - ladmin_log ("Ajustement du mois hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the months (from -255 to 255).\n"); - ladmin_log - ("Abnormal adjustement for the month ('banadd' command).\n"); - } - return 137; - } - if (day > 32767 || day < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de jours correct (de -32767 à 32767), svp.\n"); - ladmin_log ("Ajustement des jours hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the days ('banadd' command).\n"); - } - return 137; - } - if (hour > 32767 || hour < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement d'heures correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des heures hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the hours ('banadd' command).\n"); - } - return 137; - } - if (minute > 32767 || minute < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de minutes correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des minutes hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the minutes ('banadd' command).\n"); - } - return 137; - } - if (second > 32767 || second < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de secondes correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des secondes hors norme (commande 'banadd').\n"); - } - else - { - printf - ("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the seconds ('banadd' command).\n"); - } - return 137; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour modifier la date d'un bannissement.\n"); - } - else - { - ladmin_log ("Request to login-server to modify a ban date/time.\n"); - } - - WFIFOW (login_fd, 0) = 0x794c; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOW (login_fd, 26) = (short) year; - WFIFOW (login_fd, 28) = (short) month; - WFIFOW (login_fd, 30) = (short) day; - WFIFOW (login_fd, 32) = (short) hour; - WFIFOW (login_fd, 34) = (short) minute; - WFIFOW (login_fd, 36) = (short) second; - WFIFOSET (login_fd, 38); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------------------------------- -// Sub-function of sub-function banaccount, unbanaccount or bansetaccount -// Set the final date of a banishment of an account -//----------------------------------------------------------------------- -int bansetaccountsub (char *name, char *date, char *time) -{ - int year, month, day, hour, minute, second; - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - struct tm *tmtime; - - year = month = day = hour = minute = second = 0; - ban_until_time = 0; - tmtime = localtime (&ban_until_time); // initialize - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (atoi (date) != 0 && - ((sscanf (date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf (date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf (date, "%d.%d.%d", &year, &month, &day) < 3) || - sscanf (time, "%d:%d:%d", &hour, &minute, &second) < 3)) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); - printf - ("Vous pouvez aussi mettre 0 à la place si vous utilisez la commande 'banset'.\n"); - ladmin_log - ("Format incorrect pour la date/heure (commande'banset' ou 'ban').\n"); - } - else - { - printf - ("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); - printf - ("You can imput 0 instead of if you use 'banset' command.\n"); - ladmin_log - ("Invalid format for the date/time ('banset' or 'ban' command).\n"); - } - return 102; - } - - if (atoi (date) == 0) - { - ban_until_time = 0; - } - else - { - if (year < 70) - { - year = year + 100; - } - if (year >= 1900) - { - year = year - 1900; - } - if (month < 1 || month > 12) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un mois correct svp (entre 1 et 12).\n"); - ladmin_log - ("Mois incorrect pour la date (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for the month (from 1 to 12).\n"); - ladmin_log - ("Invalid month for the date ('banset' or 'ban' command).\n"); - } - return 102; - } - month = month - 1; - if (day < 1 || day > 31) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un jour correct svp (entre 1 et 31).\n"); - ladmin_log - ("Jour incorrect pour la date (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for the day (from 1 to 31).\n"); - ladmin_log - ("Invalid day for the date ('banset' or 'ban' command).\n"); - } - return 102; - } - if (((month == 3 || month == 5 || month == 8 || month == 10) - && day > 30) || (month == 1 && day > 29)) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un jour correct en fonction du mois (%d) svp.\n", - month); - ladmin_log - ("Jour incorrect pour ce mois correspondant (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for a day of this month (%d).\n", - month); - ladmin_log - ("Invalid day for this month ('banset' or 'ban' command).\n"); - } - return 102; - } - if (hour < 0 || hour > 23) - { - if (defaultlanguage == 'F') - { - printf ("Entrez une heure correcte svp (entre 0 et 23).\n"); - ladmin_log - ("Heure incorrecte pour l'heure (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for the hour (from 0 to 23).\n"); - ladmin_log - ("Invalid hour for the time ('banset' or 'ban' command).\n"); - } - return 102; - } - if (minute < 0 || minute > 59) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez des minutes correctes svp (entre 0 et 59).\n"); - ladmin_log - ("Minute incorrecte pour l'heure (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for the minutes (from 0 to 59).\n"); - ladmin_log - ("Invalid minute for the time ('banset' or 'ban' command).\n"); - } - return 102; - } - if (second < 0 || second > 59) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez des secondes correctes svp (entre 0 et 59).\n"); - ladmin_log - ("Seconde incorrecte pour l'heure (command 'banset' ou 'ban').\n"); - } - else - { - printf - ("Please give a correct value for the seconds (from 0 to 59).\n"); - ladmin_log - ("Invalid second for the time ('banset' or 'ban' command).\n"); - } - return 102; - } - tmtime->tm_year = year; - tmtime->tm_mon = month; - tmtime->tm_mday = day; - tmtime->tm_hour = hour; - tmtime->tm_min = minute; - tmtime->tm_sec = second; - tmtime->tm_isdst = -1; // -1: no winter/summer time modification - ban_until_time = timegm (tmtime); - if (ban_until_time == -1) - { - if (defaultlanguage == 'F') - { - printf ("Date incorrecte.\n"); - printf - ("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); - printf - ("Vous pouvez aussi mettre 0 à la place si vous utilisez la commande 'banset'.\n"); - ladmin_log ("Date incorrecte. (command 'banset' ou 'ban').\n"); - } - else - { - printf ("Invalid date.\n"); - printf - ("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); - printf - ("You can imput 0 instead of if you use 'banset' command.\n"); - ladmin_log ("Invalid date. ('banset' or 'ban' command).\n"); - } - return 102; - } - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour fixer un ban.\n"); - } - else - { - ladmin_log ("Request to login-server to set a ban.\n"); - } - - WFIFOW (login_fd, 0) = 0x794a; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOL (login_fd, 26) = (int) ban_until_time; - WFIFOSET (login_fd, 30); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------------------------------- -// Sub-function: Set the final date of a banishment of an account (ban) -//--------------------------------------------------------------------- -int banaccount (char *param) -{ - char name[1023], date[1023], time[1023]; - - memset (name, '\0', sizeof (name)); - memset (date, '\0', sizeof (date)); - memset (time, '\0', sizeof (time)); - - if (sscanf (param, "%s %s \"%[^\"]\"", date, time, name) < 3 && - sscanf (param, "%s %s '%[^']'", date, time, name) < 3 && - sscanf (param, "%s %s %[^\r\n]", date, time, name) < 3) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte, une date et une heure svp.\n"); - printf - ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); - printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); - printf - (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); - printf (" unban/unbanish <nom du compte>\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour fixer un ban (commande 'banset' ou 'ban').\n"); - } - else - { - printf ("Please input an account name, a date and a hour.\n"); - printf - ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf - (" banset <account_name> 0 (0 = un-banished)\n"); - printf - (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); - printf (" unban/unbanish <account name>\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); - } - return 136; - } - - return bansetaccountsub (name, date, time); -} - -//------------------------------------------------------------------------ -// Sub-function: Set the final date of a banishment of an account (banset) -//------------------------------------------------------------------------ -int bansetaccount (char *param) -{ - char name[1023], date[1023], time[1023]; - - memset (name, '\0', sizeof (name)); - memset (date, '\0', sizeof (date)); - memset (time, '\0', sizeof (time)); - - if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf (param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf (param, "%s %s %[^\r\n]", name, date, time) < 2) - { // if date = 0, time can be void - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte, une date et une heure svp.\n"); - printf - ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); - printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); - printf - (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); - printf (" unban/unbanish <nom du compte>\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour fixer un ban (commande 'banset' ou 'ban').\n"); - } - else - { - printf ("Please input an account name, a date and a hour.\n"); - printf - ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf - (" banset <account_name> 0 (0 = un-banished)\n"); - printf - (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); - printf (" unban/unbanish <account name>\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); - } - return 136; - } - - if (time[0] == '\0') - strcpy (time, "23:59:59"); - - return bansetaccountsub (name, date, time); -} - -//------------------------------------------------- -// Sub-function: unbanishment of an account (unban) -//------------------------------------------------- -int unbanaccount (char *param) -{ - char name[1023]; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf - ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); - printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); - printf - (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); - printf (" unban/unbanish <nom du compte>\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour fixer un ban (commande 'unban').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf - ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf - (" banset <account_name> 0 (0 = un-banished)\n"); - printf - (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); - printf (" unban/unbanish <account name>\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Incomplete parameters to set a ban ('unban' command).\n"); - } - return 136; - } - - return bansetaccountsub (name, "0", ""); -} - -//--------------------------------------------------------- -// Sub-function: Asking to check the validity of a password -// (Note: never send back a password with login-server!! security of passwords) -//--------------------------------------------------------- -int checkaccount (char *param) -{ - char name[1023], password[1023]; - - memset (name, '\0', sizeof (name)); - memset (password, '\0', sizeof (password)); - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && // password can be void - sscanf (param, "'%[^']' %[^\r\n]", name, password) < 1 && // password can be void - sscanf (param, "%s %[^\r\n]", name, password) < 1) - { // password can be void - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemple> check testname motdepasse\n"); - ladmin_log - ("Nombre incorrect de paramètres pour tester le mot d'un passe d'un compte (commande 'check').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<example> check testname password\n"); - ladmin_log - ("Incomplete parameters to check the password of an account ('check' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (strlen (password) == 0) - { - if (typepasswd (password) == 0) - return 134; - } - if (verify_password (password) == 0) - return 131; - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour test un mot de passe.\n"); - } - else - { - ladmin_log ("Request to login-server to check a password.\n"); - } - - WFIFOW (login_fd, 0) = 0x793a; - memcpy (WFIFOP (login_fd, 2), name, 24); - memcpy (WFIFOP (login_fd, 26), password, 24); - WFIFOSET (login_fd, 50); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------ -// Sub-function: Asking for deletion of an account -//------------------------------------------------ -int delaccount (char *param) -{ - char name[1023]; - char letter; - char confirm[1023]; - int i; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemple> del nomtestasupprimer\n"); - ladmin_log - ("Aucun nom donné pour supprimer un compte (commande 'delete').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<example> del testnametodelete\n"); - ladmin_log - ("No name given to delete an account ('delete' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - memset (confirm, '\0', sizeof (confirm)); - while ((confirm[0] != 'o' || defaultlanguage != 'F') && confirm[0] != 'n' - && (confirm[0] != 'y' || defaultlanguage == 'F')) - { - if (defaultlanguage == 'F') - printf - ("\033[1;36m ** Etes-vous vraiment sûr de vouloir SUPPRIMER le compte [$userid]? (o/n) > \033[0m"); - else - printf - ("\033[1;36m ** Are you really sure to DELETE account [$userid]? (y/n) > \033[0m"); - fflush (stdout); - memset (confirm, '\0', sizeof (confirm)); - i = 0; - while ((letter = getchar ()) != '\n') - confirm[i++] = letter; - } - - if (confirm[0] == 'n') - { - if (defaultlanguage == 'F') - { - printf ("Suppression annulée.\n"); - ladmin_log - ("Suppression annulée par l'utilisateur (commande 'delete').\n"); - } - else - { - printf ("Deletion canceled.\n"); - ladmin_log ("Deletion canceled by user ('delete' command).\n"); - } - return 121; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour détruire un compte.\n"); - } - else - { - ladmin_log ("Request to login-server to delete an acount.\n"); - } - - WFIFOW (login_fd, 0) = 0x7932; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOSET (login_fd, 26); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------- -// Sub-function: Asking to modification of an account e-mail -//---------------------------------------------------------- -int changeemail (char *param) -{ - char name[1023], email[1023]; - - memset (name, '\0', sizeof (name)); - memset (email, '\0', sizeof (email)); - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, email) < 2 && - sscanf (param, "'%[^']' %[^\r\n]", name, email) < 2 && - sscanf (param, "%s %[^\r\n]", name, email) < 2) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et une email svp.\n"); - printf ("<exemple> email testname nouveauemail\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer l'email d'un compte (commande 'email').\n"); - } - else - { - printf ("Please input an account name and an email.\n"); - printf ("<example> email testname newemail\n"); - ladmin_log - ("Incomplete parameters to change the email of an account ('email' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (strlen (email) < 3) - { - if (defaultlanguage == 'F') - { - printf ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", - email); - ladmin_log - ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", - email); - } - else - { - printf ("Email is too short [%s]. Please input a valid e-mail.\n", - email); - ladmin_log - ("Email is too short [%s]. Please input a valid e-mail.\n", - email); - } - return 109; - } - if (strlen (email) > 39) - { - if (defaultlanguage == 'F') - { - printf - ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", - email); - ladmin_log - ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", - email); - } - else - { - printf - ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - ladmin_log - ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - } - return 109; - } - if (e_mail_check (email) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", - email); - ladmin_log ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", - email); - } - else - { - printf ("Invalid email [%s]. Please input a valid e-mail.\n", - email); - ladmin_log ("Invalid email [%s]. Please input a valid e-mail.\n", - email); - } - return 109; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer une email.\n"); - } - else - { - ladmin_log ("Request to login-server to change an email.\n"); - } - - WFIFOW (login_fd, 0) = 0x7940; - memcpy (WFIFOP (login_fd, 2), name, 24); - memcpy (WFIFOP (login_fd, 26), email, 40); - WFIFOSET (login_fd, 66); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------------- -// Sub-function: Asking of the number of online players -//----------------------------------------------------- -int getlogincount (void) -{ - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir le nombre de joueurs en jeu.\n"); - } - else - { - ladmin_log - ("Request to login-server to obtain the # of online players.\n"); - } - - WFIFOW (login_fd, 0) = 0x7938; - WFIFOSET (login_fd, 2); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------- -// Sub-function: Asking to modify the GM level of an account -//---------------------------------------------------------- -int changegmlevel (char *param) -{ - char name[1023]; - int GM_level; - - memset (name, '\0', sizeof (name)); - GM_level = 0; - - if (sscanf (param, "\"%[^\"]\" %d", name, &GM_level) < 1 && - sscanf (param, "'%[^']' %d", name, &GM_level) < 1 && - sscanf (param, "%s %d", name, &GM_level) < 1) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un niveau de GM svp.\n"); - printf ("<exemple> gm nomtest 80\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le Niveau de GM d'un compte (commande 'gm').\n"); - } - else - { - printf ("Please input an account name and a GM level.\n"); - printf ("<example> gm testname 80\n"); - ladmin_log - ("Incomplete parameters to change the GM level of an account ('gm' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (GM_level < 0 || GM_level > 99) - { - if (defaultlanguage == 'F') - { - printf - ("Niveau de GM incorrect [%d]. Entrez une valeur de 0 à 99 svp.\n", - GM_level); - ladmin_log - ("Niveau de GM incorrect [%d]. La valeur peut être de 0 à 99.\n", - GM_level); - } - else - { - printf - ("Illegal GM level [%d]. Please input a value from 0 to 99.\n", - GM_level); - ladmin_log - ("Illegal GM level [%d]. The value can be from 0 to 99.\n", - GM_level); - } - return 103; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer un niveau de GM.\n"); - } - else - { - ladmin_log ("Request to login-server to change a GM level.\n"); - } - - WFIFOW (login_fd, 0) = 0x793e; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOB (login_fd, 26) = GM_level; - WFIFOSET (login_fd, 27); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------- -// Sub-function: Asking to obtain an account id -//--------------------------------------------- -int idaccount (char *param) -{ - char name[1023]; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemple> id nomtest\n"); - ladmin_log - ("Aucun nom donné pour rechecher l'id d'un compte (commande 'id').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<example> id testname\n"); - ladmin_log - ("No name given to search an account id ('id' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour connaître l'id d'un compte.\n"); - } - else - { - ladmin_log ("Request to login-server to know an account id.\n"); - } - - WFIFOW (login_fd, 0) = 0x7944; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOSET (login_fd, 26); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------------------------- -// Sub-function: Asking to displaying information about an account (by its id) -//---------------------------------------------------------------------------- -int infoaccount (int account_id) -{ - if (account_id < 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un id ayant une valeur positive svp.\n"); - ladmin_log - ("Une valeur négative a été donné pour trouver le compte.\n"); - } - else - { - printf ("Please input a positive value for the id.\n"); - ladmin_log ("Negative value was given to found the account.\n"); - } - return 136; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir le information d'un compte (par l'id).\n"); - } - else - { - ladmin_log - ("Request to login-server to obtain information about an account (by its id).\n"); - } - - WFIFOW (login_fd, 0) = 0x7954; - WFIFOL (login_fd, 2) = account_id; - WFIFOSET (login_fd, 6); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------- -// Sub-function: Send a broadcast message -//--------------------------------------- -int sendbroadcast (short type, char *message) -{ - if (strlen (message) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un message svp.\n"); - if (type == 0) - { - printf ("<exemple> kami un message\n"); - } - else - { - printf ("<exemple> kamib un message\n"); - } - ladmin_log ("Le message est vide (commande 'kami(b)').\n"); - } - else - { - printf ("Please input a message.\n"); - if (type == 0) - { - printf ("<example> kami a message\n"); - } - else - { - printf ("<example> kamib a message\n"); - } - ladmin_log ("The message is void ('kami(b)' command).\n"); - } - return 136; - } - - WFIFOW (login_fd, 0) = 0x794e; - WFIFOW (login_fd, 2) = type; - WFIFOL (login_fd, 4) = strlen (message) + 1; - memcpy (WFIFOP (login_fd, 8), message, strlen (message) + 1); - WFIFOSET (login_fd, 8 + strlen (message) + 1); - bytes_to_read = 1; - - return 0; -} - -//-------------------------------------------- -// Sub-function: Change language of displaying -//-------------------------------------------- -int changelanguage (char *language) -{ - if (strlen (language) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez une langue svp.\n"); - printf ("<exemple> language english\n"); - printf (" language français\n"); - ladmin_log ("La langue est vide (commande 'language').\n"); - } - else - { - printf ("Please input a language.\n"); - printf ("<example> language english\n"); - printf (" language français\n"); - ladmin_log ("The language is void ('language' command).\n"); - } - return 136; - } - - language[0] = toupper (language[0]); - if (language[0] == 'F' || language[0] == 'E') - { - defaultlanguage = language[0]; - if (defaultlanguage == 'F') - { - printf ("Changement de la langue d'affichage en Français.\n"); - ladmin_log ("Changement de la langue d'affichage en Français.\n"); - } - else - { - printf ("Displaying language changed to English.\n"); - ladmin_log ("Displaying language changed to English.\n"); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Langue non paramétrée (langues possibles: 'Français' ou 'English').\n"); - ladmin_log - ("Langue non paramétrée (Français ou English nécessaire).\n"); - } - else - { - printf - ("Undefined language (possible languages: Français or English).\n"); - ladmin_log ("Undefined language (must be Français or English).\n"); - } - } - - return 0; -} - -//-------------------------------------------------------- -// Sub-function: Asking to Displaying of the accounts list -//-------------------------------------------------------- -int listaccount (char *param, int type) -{ -//int list_first, list_last, list_type; // parameter to display a list of accounts - int i; - - list_type = type; - - // set default values - list_first = 0; - list_last = 0; - - if (list_type == 1) - { // if listgm - // get all accounts = use default - } - else if (list_type == 2) - { // if search - for (i = 0; param[i]; i++) - param[i] = tolower (param[i]); - // get all accounts = use default - } - else if (list_type == 3) - { // if listban - // get all accounts = use default - } - else if (list_type == 4) - { // if listok - // get all accounts = use default - } - else - { // if list (list_type == 0) - switch (sscanf (param, "%d %d", &list_first, &list_last)) - { - case 0: - // get all accounts = use default - break; - case 1: - list_last = 0; - // use tests of the following value - default: - if (list_first < 0) - list_first = 0; - if (list_last < list_first || list_last < 0) - list_last = 0; - break; - } - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir la liste des comptes de %d à %d.\n", - list_first, list_last); - } - else - { - ladmin_log - ("Request to login-server to obtain the list of accounts from %d to %d.\n", - list_first, list_last); - } - - WFIFOW (login_fd, 0) = 0x7920; - WFIFOL (login_fd, 2) = list_first; - WFIFOL (login_fd, 6) = list_last; - WFIFOSET (login_fd, 10); - bytes_to_read = 1; - - // 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567 - if (defaultlanguage == 'F') - { - Iprintf - (" id_compte GM nom_utilisateur sexe count statut\n"); - } - else - { - Iprintf - ("account_id GM user_name sex count state\n"); - } - Iprintf - ("-------------------------------------------------------------------------------\n"); - list_count = 0; - - return 0; -} - -//-------------------------------------------------------- -// Sub-function: Frobnicate items -//-------------------------------------------------------- -int itemfrob (char *param) -{ - int source_id, dest_id; - - if (sscanf (param, "%d %d", &source_id, &dest_id) < 2) - { - printf ("You must provide the source and destination item IDs.\n"); - return 1; - } - - WFIFOW (login_fd, 0) = 0x7924; - WFIFOL (login_fd, 2) = source_id; - WFIFOL (login_fd, 6) = dest_id; - WFIFOSET (login_fd, 10); - bytes_to_read = 1; // all logging is done to the three main servers - - return 0; -} - -//-------------------------------------------- -// Sub-function: Asking to modify a memo field -//-------------------------------------------- -int changememo (char *param) -{ - char name[1023], memo[1023]; - - memset (name, '\0', sizeof (name)); - memset (memo, '\0', sizeof (memo)); - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf (param, "'%[^']' %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf (param, "%s %[^\r\n]", name, memo) < 1) - { // memo can be void - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un mémo svp.\n"); - printf ("<exemple> memo nomtest nouveau memo\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le mémo d'un compte (commande 'email').\n"); - } - else - { - printf ("Please input an account name and a memo.\n"); - printf ("<example> memo testname new memo\n"); - ladmin_log - ("Incomplete parameters to change the memo of an account ('email' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (strlen (memo) > 254) - { - if (defaultlanguage == 'F') - { - printf ("Mémo trop long (%d caractères).\n", strlen (memo)); - printf ("Entrez un mémo de 254 caractères maximum svp.\n"); - ladmin_log - ("Mémo trop long (%d caractères). Entrez un mémo de 254 caractères maximum svp.\n", - strlen (memo)); - } - else - { - printf ("Memo is too long (%d characters).\n", strlen (memo)); - printf ("Please input a memo of 254 bytes at the maximum.\n"); - ladmin_log - ("Email is too long (%d characters). Please input a memo of 254 bytes at the maximum.\n", - strlen (memo)); - } - return 102; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer un mémo.\n"); - } - else - { - ladmin_log ("Request to login-server to change a memo.\n"); - } - - WFIFOW (login_fd, 0) = 0x7942; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOW (login_fd, 26) = strlen (memo); - if (strlen (memo) > 0) - memcpy (WFIFOP (login_fd, 28), memo, strlen (memo)); - WFIFOSET (login_fd, 28 + strlen (memo)); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------- -// Sub-function: Asking to obtain an account name -//----------------------------------------------- -int nameaccount (int id) -{ - if (id < 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un id ayant une valeur positive svp.\n"); - ladmin_log - ("Id négatif donné pour rechecher le nom d'un compte (commande 'name').\n"); - } - else - { - printf ("Please input a positive value for the id.\n"); - ladmin_log - ("Negativ id given to search an account name ('name' command).\n"); - } - return 136; - } - - if (defaultlanguage == 'F') - ladmin_log - ("Envoi d'un requête au serveur de logins pour connaître le nom d'un compte.\n"); - else - ladmin_log ("Request to login-server to know an account name.\n"); - - WFIFOW (login_fd, 0) = 0x7946; - WFIFOL (login_fd, 2) = id; - WFIFOSET (login_fd, 6); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------ -// Sub-function: Asking to modify a password -// (Note: never send back a password with login-server!! security of passwords) -//------------------------------------------ -int changepasswd (char *param) -{ - char name[1023], password[1023]; - - memset (name, '\0', sizeof (name)); - memset (password, '\0', sizeof (password)); - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && - sscanf (param, "'%[^']' %[^\r\n]", name, password) < 1 && - sscanf (param, "%s %[^\r\n]", name, password) < 1) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemple> passwd nomtest nouveaumotdepasse\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le mot d'un passe d'un compte (commande 'password').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<example> passwd testname newpassword\n"); - ladmin_log - ("Incomplete parameters to change the password of an account ('password' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (strlen (password) == 0) - { - if (typepasswd (password) == 0) - return 134; - } - if (verify_password (password) == 0) - return 131; - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer un mot de passe.\n"); - } - else - { - ladmin_log ("Request to login-server to change a password.\n"); - } - - WFIFOW (login_fd, 0) = 0x7934; - memcpy (WFIFOP (login_fd, 2), name, 24); - memcpy (WFIFOP (login_fd, 26), password, 24); - WFIFOSET (login_fd, 50); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------------------- -// Sub-function: Request to login-server to reload GM configuration file -// this function have no answer -//---------------------------------------------------------------------- -int reloadGM (void) -{ - WFIFOW (login_fd, 0) = 0x7955; - WFIFOSET (login_fd, 2); - bytes_to_read = 0; - - if (defaultlanguage == 'F') - { - ladmin_log - ("Demande de recharger le fichier de configuration des GM envoyée.\n"); - printf - ("Demande de recharger le fichier de configuration des GM envoyée.\n"); - printf ("Vérifiez les comptes GM actuels (après rechargement):\n"); - } - else - { - ladmin_log ("Request to reload the GM configuration file sended.\n"); - printf ("Request to reload the GM configuration file sended.\n"); - printf ("Check the actual GM accounts (after reloading):\n"); - } - listaccount (parameters, 1); // 1: to list only GM - - return 180; -} - -//----------------------------------------------------- -// Sub-function: Asking to modify the sex of an account -//----------------------------------------------------- -int changesex (char *param) -{ - char name[1023], sex[1023]; - - memset (name, '\0', sizeof (name)); - memset (sex, '\0', sizeof (sex)); - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, sex) < 2 && - sscanf (param, "'%[^']' %[^\r\n]", name, sex) < 2 && - sscanf (param, "%s %[^\r\n]", name, sex) < 2) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un sexe svp.\n"); - printf ("<exemple> sex nomtest Male\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le sexe d'un compte (commande 'sex').\n"); - } - else - { - printf ("Please input an account name and a sex.\n"); - printf ("<example> sex testname Male\n"); - ladmin_log - ("Incomplete parameters to change the sex of an account ('sex' command).\n"); - } - return 136; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - sex[0] = toupper (sex[0]); - if (strchr ("MF", sex[0]) == NULL) - { - if (defaultlanguage == 'F') - { - printf ("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); - ladmin_log ("Sexe incorrect [%s]. Entrez M ou F svp.\n", - sex); - } - else - { - printf ("Illegal gender [%s]. Please input M or F.\n", sex); - ladmin_log ("Illegal gender [%s]. Please input M or F.\n", - sex); - } - return 103; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer un sexe.\n"); - } - else - { - ladmin_log ("Request to login-server to change a sex.\n"); - } - - WFIFOW (login_fd, 0) = 0x793c; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOB (login_fd, 26) = sex[0]; - WFIFOSET (login_fd, 27); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------------------------- -// Sub-function of sub-function changestate, blockaccount or unblockaccount -// Asking to modify the state of an account -//------------------------------------------------------------------------- -int changestatesub (char *name, int state, char *error_message7) -{ - char error_message[1023]; // need to use, because we can modify error_message7 - - memset (error_message, '\0', sizeof (error_message)); - strncpy (error_message, error_message7, sizeof (error_message) - 1); - - if ((state < 0 || state > 9) && state != 100) - { // Valid values: 0: ok, or value of the 0x006a packet + 1 - if (defaultlanguage == 'F') - { - printf ("Entrez une des statuts suivantes svp:\n"); - printf - (" 0 = Compte ok 6 = Your Game's EXE file is not the latest version\n"); - } - else - { - printf ("Please input one of these states:\n"); - printf - (" 0 = Account ok 6 = Your Game's EXE file is not the latest version\n"); - } - printf - (" 1 = Unregistered ID 7 = You are Prohibited to log in until + message\n"); - printf - (" 2 = Incorrect Password 8 = Server is jammed due to over populated\n"); - printf (" 3 = This ID is expired 9 = No MSG\n"); - printf - (" 4 = Rejected from Server 100 = This ID has been totally erased\n"); - printf (" 5 = You have been blocked by the GM Team\n"); - if (defaultlanguage == 'F') - { - printf ("<exemples> state nomtest 5\n"); - printf (" state nomtest 7 fin de votre ban\n"); - printf (" block <nom compte>\n"); - printf (" unblock <nom compte>\n"); - ladmin_log - ("Valeur incorrecte pour le statut d'un compte (commande 'state', 'block' ou 'unblock').\n"); - } - else - { - printf ("<examples> state testname 5\n"); - printf (" state testname 7 end of your ban\n"); - printf (" block <account name>\n"); - printf (" unblock <account name>\n"); - ladmin_log - ("Invalid value for the state of an account ('state', 'block' or 'unblock' command).\n"); - } - return 151; - } - - if (verify_accountname (name) == 0) - { - return 102; - } - - if (state != 7) - { - strcpy (error_message, "-"); - } - else - { - if (strlen (error_message) < 1) - { - if (defaultlanguage == 'F') - { - printf - ("Message d'erreur trop court. Entrez un message de 1-19 caractères.\n"); - ladmin_log - ("Message d'erreur trop court. Entrez un message de 1-19 caractères.\n"); - } - else - { - printf - ("Error message is too short. Please input a message of 1-19 bytes.\n"); - ladmin_log - ("Error message is too short. Please input a message of 1-19 bytes.\n"); - } - return 102; - } - if (strlen (error_message) > 19) - { - if (defaultlanguage == 'F') - { - printf - ("Message d'erreur trop long. Entrez un message de 1-19 caractères.\n"); - ladmin_log - ("Message d'erreur trop long. Entrez un message de 1-19 caractères.\n"); - } - else - { - printf - ("Error message is too long. Please input a message of 1-19 bytes.\n"); - ladmin_log - ("Error message is too long. Please input a message of 1-19 bytes.\n"); - } - return 102; - } - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour changer un statut.\n"); - } - else - { - ladmin_log ("Request to login-server to change a state.\n"); - } - - WFIFOW (login_fd, 0) = 0x7936; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOL (login_fd, 26) = state; - memcpy (WFIFOP (login_fd, 30), error_message, 20); - WFIFOSET (login_fd, 50); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------- -// Sub-function: Asking to modify the state of an account -//------------------------------------------------------- -int changestate (char *param) -{ - char name[1023], error_message[1023]; - int state; - - memset (name, '\0', sizeof (name)); - memset (error_message, '\0', sizeof (error_message)); - - if (sscanf (param, "\"%[^\"]\" %d %[^\r\n]", name, &state, error_message) - < 2 - && sscanf (param, "'%[^']' %d %[^\r\n]", name, &state, - error_message) < 2 - && sscanf (param, "%s %d %[^\r\n]", name, &state, error_message) < 2) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un statut svp.\n"); - printf ("<exemples> state nomtest 5\n"); - printf (" state nomtest 7 fin de votre ban\n"); - printf (" block <nom compte>\n"); - printf (" unblock <nom compte>\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'state').\n"); - } - else - { - printf ("Please input an account name and a state.\n"); - printf ("<examples> state testname 5\n"); - printf (" state testname 7 end of your ban\n"); - printf (" block <account name>\n"); - printf (" unblock <account name>\n"); - ladmin_log - ("Incomplete parameters to change the state of an account ('state' command).\n"); - } - return 136; - } - - return changestatesub (name, state, error_message); -} - -//------------------------------------------- -// Sub-function: Asking to unblock an account -//------------------------------------------- -int unblockaccount (char *param) -{ - char name[1023]; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemples> state nomtest 5\n"); - printf (" state nomtest 7 fin de votre ban\n"); - printf (" block <nom compte>\n"); - printf (" unblock <nom compte>\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'unblock').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<examples> state testname 5\n"); - printf (" state testname 7 end of your ban\n"); - printf (" block <account name>\n"); - printf (" unblock <account name>\n"); - ladmin_log - ("Incomplete parameters to change the state of an account ('unblock' command).\n"); - } - return 136; - } - - return changestatesub (name, 0, "-"); // state 0, no error message -} - -//------------------------------------------- -// Sub-function: Asking to unblock an account -//------------------------------------------- -int blockaccount (char *param) -{ - char name[1023]; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemples> state nomtest 5\n"); - printf (" state nomtest 7 fin de votre ban\n"); - printf (" block <nom compte>\n"); - printf (" unblock <nom compte>\n"); - ladmin_log - ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'block').\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<examples> state testname 5\n"); - printf (" state testname 7 end of your ban\n"); - printf (" block <account name>\n"); - printf (" unblock <account name>\n"); - ladmin_log - ("Incomplete parameters to change the state of an account ('block' command).\n"); - } - return 136; - } - - return changestatesub (name, 5, "-"); // state 5, no error message -} - -//--------------------------------------------------------------------- -// Sub-function: Add/substract time to the validity limit of an account -//--------------------------------------------------------------------- -int timeaddaccount (char *param) -{ - char name[1023], modif[1023]; - int year, month, day, hour, minute, second; - char *p_modif; - int value, i; - - memset (name, '\0', sizeof (name)); - memset (modif, '\0', sizeof (modif)); - year = month = day = hour = minute = second = 0; - - if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf (param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf (param, "%s %[^\r\n]", name, modif) < 2) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte et un modificateur svp.\n"); - printf (" <exemple> timeadd nomtest +1m-2mn1s-6y\n"); - printf - (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour modifier une date limite d'utilisation (commande 'timeadd').\n"); - } - else - { - printf ("Please input an account name and a modifier.\n"); - printf (" <example>: timeadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - ladmin_log - ("Incomplete parameters to modify a limit time ('timeadd' command).\n"); - } - return 136; - } - if (verify_accountname (name) == 0) - { - return 102; - } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower (modif[i]); - p_modif = modif; - while (strlen (p_modif) > 0) - { - value = atoi (p_modif); - if (value == 0) - { - p_modif++; - } - else - { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen (p_modif) > 0 && p_modif[0] >= '0' - && p_modif[0] <= '9') - { - p_modif++; - } - if (p_modif[0] == 's') - { - second = value; - p_modif++; - } - else if (p_modif[0] == 'm' && p_modif[1] == 'n') - { - minute = value; - p_modif += 2; - } - else if (p_modif[0] == 'h') - { - hour = value; - p_modif++; - } - else if (p_modif[0] == 'd' || p_modif[0] == 'j') - { - day = value; - p_modif += 2; - } - else if (p_modif[0] == 'm') - { - month = value; - p_modif++; - } - else if (p_modif[0] == 'y' || p_modif[0] == 'a') - { - year = value; - p_modif++; - } - else - { - p_modif++; - } - } - } - - if (defaultlanguage == 'F') - { - printf (" année: %d\n", year); - printf (" mois: %d\n", month); - printf (" jour: %d\n", day); - printf (" heure: %d\n", hour); - printf (" minute: %d\n", minute); - printf (" seconde: %d\n", second); - } - else - { - printf (" year: %d\n", year); - printf (" month: %d\n", month); - printf (" day: %d\n", day); - printf (" hour: %d\n", hour); - printf (" minute: %d\n", minute); - printf (" second: %d\n", second); - } - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 - && second == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Vous devez entrer un ajustement avec cette commande, svp:\n"); - printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf (" Elément modifié:\n"); - printf (" a ou y: année\n"); - printf (" m: mois\n"); - printf (" j ou d: jour\n"); - printf (" h: heure\n"); - printf (" mn: minute\n"); - printf (" s: seconde\n"); - printf (" <exemple> timeadd nomtest +1m-2mn1s-6y\n"); - printf - (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf (" et 6 ans dans le même temps.\n"); - ladmin_log - ("Aucun ajustement n'est pas un ajustement (commande 'timeadd').\n"); - } - else - { - printf ("Please give an adjustment with this command:\n"); - printf (" Adjustment value (-1, 1, +1, etc...)\n"); - printf (" Modified element:\n"); - printf (" a or y: year\n"); - printf (" m: month\n"); - printf (" j or d: day\n"); - printf (" h: hour\n"); - printf (" mn: minute\n"); - printf (" s: second\n"); - printf (" <example> timeadd testname +1m-2mn1s-6y\n"); - printf - (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf (" and 6 years at the same time.\n"); - ladmin_log - ("No adjustment isn't an adjustment ('timeadd' command).\n"); - } - return 137; - } - if (year > 127 || year < -127) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement d'années correct (de -127 à 127), svp.\n"); - ladmin_log - ("Ajustement de l'année hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the years (from -127 to 127).\n"); - ladmin_log - ("Abnormal adjustement for the year ('timeadd' command).\n"); - } - return 137; - } - if (month > 255 || month < -255) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de mois correct (de -255 à 255), svp.\n"); - ladmin_log ("Ajustement du mois hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the months (from -255 to 255).\n"); - ladmin_log - ("Abnormal adjustement for the month ('timeadd' command).\n"); - } - return 137; - } - if (day > 32767 || day < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de jours correct (de -32767 à 32767), svp.\n"); - ladmin_log ("Ajustement des jours hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the days ('timeadd' command).\n"); - } - return 137; - } - if (hour > 32767 || hour < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement d'heures correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des heures hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the hours ('timeadd' command).\n"); - } - return 137; - } - if (minute > 32767 || minute < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de minutes correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des minutes hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the minutes ('timeadd' command).\n"); - } - return 137; - } - if (second > 32767 || second < -32767) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un ajustement de secondes correct (de -32767 à 32767), svp.\n"); - ladmin_log - ("Ajustement des secondes hors norme ('timeadd' command).\n"); - } - else - { - printf - ("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - ladmin_log - ("Abnormal adjustement for the seconds ('timeadd' command).\n"); - } - return 137; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour modifier une date limite d'utilisation.\n"); - } - else - { - ladmin_log ("Request to login-server to modify a time limit.\n"); - } - - WFIFOW (login_fd, 0) = 0x7950; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOW (login_fd, 26) = (short) year; - WFIFOW (login_fd, 28) = (short) month; - WFIFOW (login_fd, 30) = (short) day; - WFIFOW (login_fd, 32) = (short) hour; - WFIFOW (login_fd, 34) = (short) minute; - WFIFOW (login_fd, 36) = (short) second; - WFIFOSET (login_fd, 38); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------- -// Sub-function: Set a validity limit of an account -//------------------------------------------------- -int timesetaccount (char *param) -{ - char name[1023], date[1023], time[1023]; - int year, month, day, hour, minute, second; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - struct tm *tmtime; - - memset (name, '\0', sizeof (name)); - memset (date, '\0', sizeof (date)); - memset (time, '\0', sizeof (time)); - year = month = day = hour = minute = second = 0; - connect_until_time = 0; - tmtime = localtime (&connect_until_time); // initialize - - if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf (param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf (param, "%s %s %[^\r\n]", name, date, time) < 2) - { // if date = 0, time can be void - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte, une date et une heure svp.\n"); - printf - ("<exemple>: timeset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); - printf - (" timeset <nom_du_compte> 0 (0 = illimité)\n"); - printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Nombre incorrect de paramètres pour fixer une date limite d'utilisation (commande 'timeset').\n"); - } - else - { - printf ("Please input an account name, a date and a hour.\n"); - printf - ("<example>: timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); - printf - (" timeset <account_name> 0 (0 = unlimited)\n"); - printf (" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log - ("Incomplete parameters to set a limit time ('timeset' command).\n"); - } - return 136; - } - if (verify_accountname (name) == 0) - { - return 102; - } - - if (time[0] == '\0') - strcpy (time, "23:59:59"); - - if (atoi (date) != 0 && - ((sscanf (date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf (date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf (date, "%d.%d.%d", &year, &month, &day) < 3 && - sscanf (date, "%d'%d'%d", &year, &month, &day) < 3) || - sscanf (time, "%d:%d:%d", &hour, &minute, &second) < 3)) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); - ladmin_log - ("Format incorrect pour la date/heure ('timeset' command).\n"); - } - else - { - printf - ("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); - ladmin_log - ("Invalid format for the date/time ('timeset' command).\n"); - } - return 102; - } - - if (atoi (date) == 0) - { - connect_until_time = 0; - } - else - { - if (year < 70) - { - year = year + 100; - } - if (year >= 1900) - { - year = year - 1900; - } - if (month < 1 || month > 12) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un mois correct svp (entre 1 et 12).\n"); - ladmin_log ("Mois incorrect pour la date ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for the month (from 1 to 12).\n"); - ladmin_log ("Invalid month for the date ('timeset' command).\n"); - } - return 102; - } - month = month - 1; - if (day < 1 || day > 31) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un jour correct svp (entre 1 et 31).\n"); - ladmin_log ("Jour incorrect pour la date ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for the day (from 1 to 31).\n"); - ladmin_log ("Invalid day for the date ('timeset' command).\n"); - } - return 102; - } - if (((month == 3 || month == 5 || month == 8 || month == 10) - && day > 30) || (month == 1 && day > 29)) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez un jour correct en fonction du mois (%d) svp.\n", - month); - ladmin_log - ("Jour incorrect pour ce mois correspondant ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for a day of this month (%d).\n", - month); - ladmin_log ("Invalid day for this month ('timeset' command).\n"); - } - return 102; - } - if (hour < 0 || hour > 23) - { - if (defaultlanguage == 'F') - { - printf ("Entrez une heure correcte svp (entre 0 et 23).\n"); - ladmin_log - ("Heure incorrecte pour l'heure ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for the hour (from 0 to 23).\n"); - ladmin_log ("Invalid hour for the time ('timeset' command).\n"); - } - return 102; - } - if (minute < 0 || minute > 59) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez des minutes correctes svp (entre 0 et 59).\n"); - ladmin_log - ("Minute incorrecte pour l'heure ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for the minutes (from 0 to 59).\n"); - ladmin_log ("Invalid minute for the time ('timeset' command).\n"); - } - return 102; - } - if (second < 0 || second > 59) - { - if (defaultlanguage == 'F') - { - printf - ("Entrez des secondes correctes svp (entre 0 et 59).\n"); - ladmin_log - ("Seconde incorrecte pour l'heure ('timeset' command).\n"); - } - else - { - printf - ("Please give a correct value for the seconds (from 0 to 59).\n"); - ladmin_log ("Invalid second for the time ('timeset' command).\n"); - } - return 102; - } - tmtime->tm_year = year; - tmtime->tm_mon = month; - tmtime->tm_mday = day; - tmtime->tm_hour = hour; - tmtime->tm_min = minute; - tmtime->tm_sec = second; - tmtime->tm_isdst = -1; // -1: no winter/summer time modification - connect_until_time = timegm (tmtime); - if (connect_until_time == -1) - { - if (defaultlanguage == 'F') - { - printf ("Date incorrecte.\n"); - printf - ("Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); - ladmin_log ("Date incorrecte. ('timeset' command).\n"); - } - else - { - printf ("Invalid date.\n"); - printf - ("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); - ladmin_log ("Invalid date. ('timeset' command).\n"); - } - return 102; - } - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour fixer une date limite d'utilisation.\n"); - } - else - { - ladmin_log ("Request to login-server to set a time limit.\n"); - } - - WFIFOW (login_fd, 0) = 0x7948; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOL (login_fd, 26) = (int) connect_until_time; - WFIFOSET (login_fd, 30); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------------------------------ -// Sub-function: Asking to displaying information about an account (by its name) -//------------------------------------------------------------------------------ -int whoaccount (char *param) -{ - char name[1023]; - - memset (name, '\0', sizeof (name)); - - if (strlen (param) == 0 || - (sscanf (param, "\"%[^\"]\"", name) < 1 && - sscanf (param, "'%[^']'", name) < 1 && - sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) - { - if (defaultlanguage == 'F') - { - printf ("Entrez un nom de compte svp.\n"); - printf ("<exemple> who nomtest\n"); - ladmin_log ("Aucun nom n'a été donné pour trouver le compte.\n"); - } - else - { - printf ("Please input an account name.\n"); - printf ("<example> who testname\n"); - ladmin_log ("No name was given to found the account.\n"); - } - return 136; - } - if (verify_accountname (name) == 0) - { - return 102; - } - - if (defaultlanguage == 'F') - { - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir le information d'un compte (par le nom).\n"); - } - else - { - ladmin_log - ("Request to login-server to obtain information about an account (by its name).\n"); - } - - WFIFOW (login_fd, 0) = 0x7952; - memcpy (WFIFOP (login_fd, 2), name, 24); - WFIFOSET (login_fd, 26); - bytes_to_read = 1; - - return 0; -} - -//-------------------------------------------------------- -// Sub-function: Asking of the version of the login-server -//-------------------------------------------------------- -int checkloginversion (void) -{ - if (defaultlanguage == 'F') - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir sa version.\n"); - else - ladmin_log ("Request to login-server to obtain its version.\n"); - - WFIFOW (login_fd, 0) = 0x7530; - WFIFOSET (login_fd, 2); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------- -// Prompt function -// this function wait until user type a command -// and analyse the command. -//--------------------------------------------- -int prompt (void) -{ - int i, j; - char buf[1024]; - char *p; - - // while we don't wait new packets - while (bytes_to_read == 0) - { - // for help with the console colors look here: - // http://www.edoceo.com/liberum/?doc=printf-with-color - // some code explanation (used here): - // \033[2J : clear screen and go up/left (0, 0 position) - // \033[K : clear line from actual position to end of the line - // \033[0m : reset color parameter - // \033[1m : use bold for font - Iprintf ("\n"); - if (defaultlanguage == 'F') - { - Iprintf - ("\033[32mPour afficher les commandes, tapez 'Entrée'.\033[0m\n"); - } - else - Iprintf ("\033[32mTo list the commands, type 'enter'.\033[0m\n"); - Iprintf ("\033[0;36mLadmin-> \033[0m"); - Iprintf ("\033[1m"); - fflush (stdout); - - // get command and parameter - memset (buf, '\0', sizeof (buf)); - fflush (stdin); - if (!fgets (buf, 1023, stdin)) - exit (0); - buf[1023] = '\0'; - - Iprintf ("\033[0m"); - fflush (stdout); - - if (!eathena_interactive_session && !strlen (buf)) - exit (0); - - // remove final \n - if ((p = strrchr (buf, '\n')) != NULL) - p[0] = '\0'; - // remove all control char - for (i = 0; buf[i]; i++) - if (buf[i] < 32) - { - // remove cursor control. - if (buf[i] == 27 && buf[i + 1] == '[' && (buf[i + 2] == 'H' || // home position (cursor) - buf[i + 2] == 'J' || // clear screen - buf[i + 2] == 'A' || // up 1 line - buf[i + 2] == 'B' || // down 1 line - buf[i + 2] == 'C' || // right 1 position - buf[i + 2] == 'D' || // left 1 position - buf[i + 2] == 'G')) - { // center cursor (windows) - for (j = i; buf[j]; j++) - buf[j] = buf[j + 3]; - } - else if (buf[i] == 27 && buf[i + 1] == '[' - && buf[i + 2] == '2' && buf[i + 3] == 'J') - { // clear screen - for (j = i; buf[j]; j++) - buf[j] = buf[j + 4]; - } - else if (buf[i] == 27 && buf[i + 1] == '[' && buf[i + 3] == '~' && (buf[i + 2] == '1' || // home (windows) - buf[i + 2] == '2' || // insert (windows) - buf[i + 2] == '3' || // del (windows) - buf[i + 2] == '4' || // end (windows) - buf[i + 2] == '5' || // pgup (windows) - buf - [i - + - 2] - == - '6')) - { // pgdown (windows) - for (j = i; buf[j]; j++) - buf[j] = buf[j + 4]; - } - else - { - // remove other control char. - for (j = i; buf[j]; j++) - buf[j] = buf[j + 1]; - } - i--; - } - - // extract command name and parameters - memset (command, '\0', sizeof (command)); - memset (parameters, '\0', sizeof (parameters)); - sscanf (buf, "%1023s %[^\n]", command, parameters); - command[1023] = '\0'; - parameters[1023] = '\0'; - - // lowercase for command line - for (i = 0; command[i]; i++) - command[i] = tolower (command[i]); - - if (command[0] == '?' || strlen (command) == 0) - { - if (defaultlanguage == 'F') - { - strcpy (buf, "aide"); - strcpy (command, "aide"); - } - else - { - strcpy (buf, "help"); - strcpy (command, "help"); - } - } - - // Analyse of the command - check_command (command); // give complete name to the command - - if (strlen (parameters) == 0) - { - if (defaultlanguage == 'F') - { - ladmin_log ("Commande: '%s' (sans paramètre)\n", - command, parameters); - } - else - { - ladmin_log ("Command: '%s' (without parameters)\n", - command, parameters); - } - } - else - { - if (defaultlanguage == 'F') - { - ladmin_log ("Commande: '%s', paramètres: '%s'\n", - command, parameters); - } - else - { - ladmin_log ("Command: '%s', parameters: '%s'\n", - command, parameters); - } - } - - // Analyse of the command -// help - if (strcmp (command, "aide") == 0) - { - display_help (parameters, 1); // 1: french - } - else if (strcmp (command, "help") == 0) - { - display_help (parameters, 0); // 0: english -// general commands - } - else if (strcmp (command, "add") == 0) - { - addaccount (parameters, 0); // 0: no email - } - else if (strcmp (command, "ban") == 0) - { - banaccount (parameters); - } - else if (strcmp (command, "banadd") == 0) - { - banaddaccount (parameters); - } - else if (strcmp (command, "banset") == 0) - { - bansetaccount (parameters); - } - else if (strcmp (command, "block") == 0) - { - blockaccount (parameters); - } - else if (strcmp (command, "check") == 0) - { - checkaccount (parameters); - } - else if (strcmp (command, "create") == 0) - { - addaccount (parameters, 1); // 1: with email - } - else if (strcmp (command, "delete") == 0) - { - delaccount (parameters); - } - else if (strcmp (command, "email") == 0) - { - changeemail (parameters); - } - else if (strcmp (command, "getcount") == 0) - { - getlogincount (); - } - else if (strcmp (command, "gm") == 0) - { - changegmlevel (parameters); - } - else if (strcmp (command, "id") == 0) - { - idaccount (parameters); - } - else if (strcmp (command, "info") == 0) - { - infoaccount (atoi (parameters)); - } - else if (strcmp (command, "kami") == 0) - { - sendbroadcast (0, parameters); // flag for normal - } - else if (strcmp (command, "kamib") == 0) - { - sendbroadcast (0x10, parameters); // flag for blue - } - else if (strcmp (command, "language") == 0) - { - changelanguage (parameters); - } - else if (strcmp (command, "itemfrob") == 0) - { - itemfrob (parameters); // 0: to list all - } - else if (strcmp (command, "list") == 0) - { - listaccount (parameters, 0); // 0: to list all - } - else if (strcmp (command, "listban") == 0) - { - listaccount (parameters, 3); // 3: to list only accounts with state or bannished - } - else if (strcmp (command, "listgm") == 0) - { - listaccount (parameters, 1); // 1: to list only GM - } - else if (strcmp (command, "listok") == 0) - { - listaccount (parameters, 4); // 4: to list only accounts without state and not bannished - } - else if (strcmp (command, "memo") == 0) - { - changememo (parameters); - } - else if (strcmp (command, "name") == 0) - { - nameaccount (atoi (parameters)); - } - else if (strcmp (command, "password") == 0) - { - changepasswd (parameters); - } - else if (strcmp (command, "reloadgm") == 0) - { - reloadGM (); - } - else if (strcmp (command, "search") == 0) - { // no regex in C version - listaccount (parameters, 2); // 2: to list with pattern - } - else if (strcmp (command, "sex") == 0) - { - changesex (parameters); - } - else if (strcmp (command, "state") == 0) - { - changestate (parameters); - } - else if (strcmp (command, "timeadd") == 0) - { - timeaddaccount (parameters); - } - else if (strcmp (command, "timeset") == 0) - { - timesetaccount (parameters); - } - else if (strcmp (command, "unban") == 0) - { - unbanaccount (parameters); - } - else if (strcmp (command, "unblock") == 0) - { - unblockaccount (parameters); - } - else if (strcmp (command, "version") == 0) - { - checkloginversion (); - } - else if (strcmp (command, "who") == 0) - { - whoaccount (parameters); -// quit - } - else if (strcmp (command, "quit") == 0 || - strcmp (command, "exit") == 0 || - strcmp (command, "end") == 0) - { - if (defaultlanguage == 'F') - { - printf ("Au revoir.\n"); - } - else - { - printf ("Bye.\n"); - } - exit (0); -// unknown command - } - else - { - if (defaultlanguage == 'F') - { - printf ("Commande inconnue [%s].\n", buf); - ladmin_log ("Commande inconnue [%s].\n", buf); - } - else - { - printf ("Unknown command [%s].\n", buf); - ladmin_log ("Unknown command [%s].\n", buf); - } - } - } - - return 0; -} - -//------------------------------------------------------------- -// Function: Parse receiving informations from the login-server -//------------------------------------------------------------- -void parse_fromlogin (int fd) -{ - struct char_session_data *sd; - - if (session[fd]->eof) - { - if (defaultlanguage == 'F') - { - printf - ("Impossible de se connecter au serveur de login [%s:%d] !\n", - loginserverip, loginserverport); - ladmin_log - ("Impossible de se connecter au serveur de login [%s:%d] !\n", - loginserverip, loginserverport); - } - else - { - printf - ("Impossible to have a connection with the login-server [%s:%d] !\n", - loginserverip, loginserverport); - ladmin_log - ("Impossible to have a connection with the login-server [%s:%d] !\n", - loginserverip, loginserverport); - } - close (fd); - delete_session (fd); - exit (0); - } - -// printf("parse_fromlogin : %d %d %d\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); - sd = (struct char_session_data *)session[fd]->session_data; - - while (RFIFOREST (fd) >= 2) - { - switch (RFIFOW (fd, 0)) - { - case 0x7919: // answer of a connection request - if (RFIFOREST (fd) < 3) - return; - if (RFIFOB (fd, 2) != 0) - { - if (defaultlanguage == 'F') - { - printf ("Erreur de login:\n"); - printf (" - mot de passe incorrect,\n"); - printf - (" - système d'administration non activé, ou\n"); - printf (" - IP non autorisée.\n"); - ladmin_log - ("Erreur de login: mot de passe incorrect, système d'administration non activé, ou IP non autorisée.\n"); - } - else - { - printf ("Error at login:\n"); - printf (" - incorrect password,\n"); - printf - (" - administration system not activated, or\n"); - printf (" - unauthorised IP.\n"); - ladmin_log - ("Error at login: incorrect password, administration system not activated, or unauthorised IP.\n"); - } - session[fd]->eof = 1; - //bytes_to_read = 1; // not stop at prompt - } - else - { - if (defaultlanguage == 'F') - { - printf ("Connexion établie.\n"); - ladmin_log ("Connexion établie.\n"); - printf - ("Lecture de la version du serveur de login...\n"); - ladmin_log - ("Lecture de la version du serveur de login...\n"); - } - else - { - Iprintf ("Established connection.\n"); - ladmin_log ("Established connection.\n"); - Iprintf - ("Reading of the version of the login-server...\n"); - ladmin_log - ("Reading of the version of the login-server...\n"); - } - //bytes_to_read = 1; // unchanged - checkloginversion (); - } - RFIFOSKIP (fd, 3); - break; - -#ifdef PASSWORDENC - case 0x01dc: // answer of a coding key request - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - char md5str[64] = - "", md5bin[32], md5key[RFIFOW (fd, 2) - 4 + 1]; - memcpy (md5key, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); - md5key[sizeof (md5key) - 1] = '0'; - if (passenc == 1) - { - strncpy (md5str, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); - strcat (md5str, loginserveradminpassword); - } - else if (passenc == 2) - { - strncpy (md5str, loginserveradminpassword, - sizeof (loginserveradminpassword)); - strcat (md5str, RFIFOP (fd, 4)); - } - MD5_to_bin(MD5_from_cstring(md5str), md5bin); - WFIFOW (login_fd, 0) = 0x7918; // Request for administation login (encrypted password) - WFIFOW (login_fd, 2) = passenc; // Encrypted type - memcpy (WFIFOP (login_fd, 4), md5bin, 16); - WFIFOSET (login_fd, 20); - if (defaultlanguage == 'F') - { - Iprintf ("Réception de la clef MD5.\n"); - ladmin_log ("Réception de la clef MD5.\n"); - Iprintf ("Envoi du mot de passe crypté...\n"); - ladmin_log ("Envoi du mot de passe crypté...\n"); - } - else - { - Iprintf ("Receiving of the MD5 key.\n"); - ladmin_log ("Receiving of the MD5 key.\n"); - Iprintf ("Sending of the encrypted password...\n"); - ladmin_log ("Sending of the encrypted password...\n"); - } - } - bytes_to_read = 1; - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; -#endif - - case 0x7531: // Displaying of the version of the login-server - if (RFIFOREST (fd) < 10) - return; - Iprintf (" Login-Server [%s:%d]\n", loginserverip, - loginserverport); - if (((int) RFIFOB (login_fd, 5)) == 0) - { - Iprintf (" eAthena version stable-%d.%d", - (int) RFIFOB (login_fd, 2), - (int) RFIFOB (login_fd, 3)); - } - else - { - Iprintf (" eAthena version dev-%d.%d", - (int) RFIFOB (login_fd, 2), - (int) RFIFOB (login_fd, 3)); - } - if (((int) RFIFOB (login_fd, 4)) == 0) - Iprintf (" revision %d", (int) RFIFOB (login_fd, 4)); - if (((int) RFIFOB (login_fd, 6)) == 0) - { - Iprintf ("%d.\n", RFIFOW (login_fd, 8)); - } - else - Iprintf ("-mod%d.\n", RFIFOW (login_fd, 8)); - bytes_to_read = 0; - RFIFOSKIP (fd, 10); - break; - - case 0x7925: // Itemfrob-OK - RFIFOSKIP (fd, 2); - bytes_to_read = 0; - break; - - case 0x7921: // Displaying of the list of accounts - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - if (RFIFOW (fd, 2) < 5) - { - if (defaultlanguage == 'F') - { - ladmin_log - (" Réception d'une liste des comptes vide.\n"); - if (list_count == 0) - printf ("Aucun compte trouvé.\n"); - else if (list_count == 1) - printf ("1 compte trouvé.\n"); - else - printf ("%d comptes trouvés.\n", list_count); - } - else - { - ladmin_log (" Receiving of a void accounts list.\n"); - if (list_count == 0) - { - Iprintf ("No account found.\n"); - } - else if (list_count == 1) - { - Iprintf ("1 account found.\n"); - } - else - Iprintf ("%d accounts found.\n", list_count); - } - bytes_to_read = 0; - } - else - { - int i; - if (defaultlanguage == 'F') - ladmin_log (" Réception d'une liste des comptes.\n"); - else - ladmin_log (" Receiving of a accounts list.\n"); - for (i = 4; i < RFIFOW (fd, 2); i += 38) - { - int j; - char userid[24]; - char lower_userid[24]; - memcpy (userid, RFIFOP (fd, i + 5), sizeof (userid)); - userid[sizeof (userid) - 1] = '\0'; - memset (lower_userid, '\0', sizeof (lower_userid)); - for (j = 0; userid[j]; j++) - lower_userid[j] = tolower (userid[j]); - list_first = RFIFOL (fd, i) + 1; - // here are checks... - if (list_type == 0 || - (list_type == 1 && RFIFOB (fd, i + 4) > 0) || - (list_type == 2 - && strstr (lower_userid, parameters) != NULL) - || (list_type == 3 && RFIFOL (fd, i + 34) != 0) - || (list_type == 4 && RFIFOL (fd, i + 34) == 0)) - { - printf ("%10d ", RFIFOL (fd, i)); - if (RFIFOB (fd, i + 4) == 0) - printf (" "); - else - printf ("%2d ", (int) RFIFOB (fd, i + 4)); - printf ("%-24s", userid); - if (defaultlanguage == 'F') - { - if (RFIFOB (fd, i + 29) == 0) - printf ("%-5s ", "Femme"); - else if (RFIFOB (fd, i + 29) == 1) - printf ("%-5s ", "Male"); - else - printf ("%-5s ", "Servr"); - } - else - { - if (RFIFOB (fd, i + 29) == 0) - printf ("%-5s ", "Femal"); - else if (RFIFOB (fd, i + 29) == 1) - printf ("%-5s ", "Male"); - else - printf ("%-5s ", "Servr"); - } - printf ("%6d ", RFIFOL (fd, i + 30)); - switch (RFIFOL (fd, i + 34)) - { - case 0: - if (defaultlanguage == 'F') - printf ("%-27s\n", "Compte Ok"); - else - printf ("%-27s\n", "Account OK"); - break; - case 1: - printf ("%-27s\n", "Unregistered ID"); - break; - case 2: - printf ("%-27s\n", "Incorrect Password"); - break; - case 3: - printf ("%-27s\n", "This ID is expired"); - break; - case 4: - printf ("%-27s\n", - "Rejected from Server"); - break; - case 5: - printf ("%-27s\n", "Blocked by the GM Team"); // You have been blocked by the GM Team - break; - case 6: - printf ("%-27s\n", "Your EXE file is too old"); // Your Game's EXE file is not the latest version - break; - case 7: - printf ("%-27s\n", "Banishement or"); - printf (" Prohibited to login until...\n"); // You are Prohibited to log in until %s - break; - case 8: - printf ("%-27s\n", - "Server is over populated"); - break; - case 9: - printf ("%-27s\n", "No MSG"); - break; - default: // 100 - printf ("%-27s\n", "This ID is totally erased"); // This ID has been totally erased - break; - } - list_count++; - } - } - // asking of the following acounts - if (defaultlanguage == 'F') - ladmin_log - ("Envoi d'un requête au serveur de logins pour obtenir la liste des comptes de %d à %d (complément).\n", - list_first, list_last); - else - ladmin_log - ("Request to login-server to obtain the list of accounts from %d to %d (complement).\n", - list_first, list_last); - WFIFOW (login_fd, 0) = 0x7920; - WFIFOL (login_fd, 2) = list_first; - WFIFOL (login_fd, 6) = list_last; - WFIFOSET (login_fd, 10); - bytes_to_read = 1; - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - case 0x7931: // Answer of login-server about an account creation - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec à la création du compte [%s]. Un compte identique existe déjà.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec à la création du compte [%s]. Un compte identique existe déjà.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] creation failed. Same account already exists.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] creation failed. Same account already exists.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf ("Compte [%s] créé avec succès [id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log ("Compte [%s] créé avec succès [id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s] is successfully created [id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s] is successfully created [id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7933: // Answer of login-server about an account deletion - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] deletion failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] deletion failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf ("Compte [%s][id: %d] SUPPRIME avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Compte [%s][id: %d] SUPPRIME avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] is successfully DELETED.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] is successfully DELETED.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7935: // answer of the change of an account password - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de la modification du mot de passe du compte [%s].\n", - RFIFOP (fd, 6)); - printf ("Le compte [%s] n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec de la modification du mot de passe du compte. Le compte [%s] n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf ("Account [%s] password changing failed.\n", - RFIFOP (fd, 6)); - printf ("Account [%s] doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account password changing failed. The compte [%s] doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Modification du mot de passe du compte [%s][id: %d] réussie.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Modification du mot de passe du compte [%s][id: %d] réussie.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] password successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] password successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7937: // answer of the change of an account state - if (RFIFOREST (fd) < 34) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] state changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] state changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - char tmpstr[256]; - if (defaultlanguage == 'F') - { - sprintf (tmpstr, - "Statut du compte [%s] changé avec succès en [", - RFIFOP (fd, 6)); - } - else - { - sprintf (tmpstr, - "Account [%s] state successfully changed in [", - RFIFOP (fd, 6)); - } - switch (RFIFOL (fd, 30)) - { - case 0: - if (defaultlanguage == 'F') - strcat (tmpstr, "0: Compte Ok"); - else - strcat (tmpstr, "0: Account OK"); - break; - case 1: - strcat (tmpstr, "1: Unregistered ID"); - break; - case 2: - strcat (tmpstr, "2: Incorrect Password"); - break; - case 3: - strcat (tmpstr, "3: This ID is expired"); - break; - case 4: - strcat (tmpstr, "4: Rejected from Server"); - break; - case 5: - strcat (tmpstr, - "5: You have been blocked by the GM Team"); - break; - case 6: - strcat (tmpstr, - "6: [Your Game's EXE file is not the latest version"); - break; - case 7: - strcat (tmpstr, - "7: You are Prohibited to log in until..."); - break; - case 8: - strcat (tmpstr, - "8: Server is jammed due to over populated"); - break; - case 9: - strcat (tmpstr, "9: No MSG"); - break; - default: // 100 - strcat (tmpstr, "100: This ID is totally erased"); - break; - } - strcat (tmpstr, "]"); - printf ("%s\n", tmpstr); - ladmin_log ("%s%s", tmpstr, "\n"); - } - bytes_to_read = 0; - RFIFOSKIP (fd, 34); - break; - - case 0x7939: // answer of the number of online players - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - // Get length of the received packet - int i; - char name[20]; - if (defaultlanguage == 'F') - { - ladmin_log - (" Réception du nombre de joueurs en ligne.\n"); - } - else - { - ladmin_log - (" Receiving of the number of online players.\n"); - } - // Read information of the servers - if (RFIFOW (fd, 2) < 5) - { - if (defaultlanguage == 'F') - { - printf - (" Aucun serveur n'est connecté au login serveur.\n"); - } - else - { - printf - (" No server is connected to the login-server.\n"); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - (" Nombre de joueurs en ligne (serveur: nb):\n"); - } - else - { - printf - (" Number of online players (server: number).\n"); - } - // Displaying of result - for (i = 4; i < RFIFOW (fd, 2); i += 32) - { - memcpy (name, RFIFOP (fd, i + 6), sizeof (name)); - name[sizeof (name) - 1] = '\0'; - printf (" %-20s : %5d\n", name, - RFIFOW (fd, i + 26)); - } - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - case 0x793b: // answer of the check of a password - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("The account [%s] doesn't exist or the password is incorrect.\n", - RFIFOP (fd, 6)); - ladmin_log - ("The account [%s] doesn't exist or the password is incorrect.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Le mot de passe donné correspond bien au compte [%s][id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Le mot de passe donné correspond bien au compte [%s][id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("The proposed password is correct for the account [%s][id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("The proposed password is correct for the account [%s][id: %d].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x793d: // answer of the change of an account sex - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de la modification du sexe du compte [%s].\n", - RFIFOP (fd, 6)); - printf - ("Le compte [%s] n'existe pas ou le sexe est déjà celui demandé.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec de la modification du sexe du compte. Le compte [%s] n'existe pas ou le sexe est déjà celui demandé.\n", - RFIFOP (fd, 6)); - } - else - { - printf ("Account [%s] sex changing failed.\n", - RFIFOP (fd, 6)); - printf - ("Account [%s] doesn't exist or the sex is already the good sex.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account sex changing failed. The compte [%s] doesn't exist or the sex is already the good sex.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Sexe du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Sexe du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] sex successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] sex successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x793f: // answer of the change of an account GM level - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de la modification du niveau de GM du compte [%s].\n", - RFIFOP (fd, 6)); - printf - ("Le compte [%s] n'existe pas, le niveau de GM est déjà celui demandé\n", - RFIFOP (fd, 6)); - printf - ("ou il est impossible de modifier le fichier des comptes GM.\n"); - ladmin_log - ("Echec de la modification du niveau de GM du compte. Le compte [%s] n'existe pas, le niveau de GM est déjà celui demandé ou il est impossible de modifier le fichier des comptes GM.\n", - RFIFOP (fd, 6)); - } - else - { - printf ("Account [%s] GM level changing failed.\n", - RFIFOP (fd, 6)); - printf - ("Account [%s] doesn't exist, the GM level is already the good GM level\n", - RFIFOP (fd, 6)); - printf - ("or it's impossible to modify the GM accounts file.\n"); - ladmin_log - ("Account GM level changing failed. The compte [%s] doesn't exist, the GM level is already the good sex or it's impossible to modify the GM accounts file.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Niveau de GM du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Niveau de GM du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] GM level successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] GM level successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7941: // answer of the change of an account email - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de la modification de l'e-mail du compte [%s].\n", - RFIFOP (fd, 6)); - printf ("Le compte [%s] n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec de la modification de l'e-mail du compte. Le compte [%s] n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf ("Account [%s] e-mail changing failed.\n", - RFIFOP (fd, 6)); - printf ("Account [%s] doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account e-mail changing failed. The compte [%s] doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Modification de l'e-mail du compte [%s][id: %d] réussie.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Modification de l'e-mail du compte [%s][id: %d] réussie.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] e-mail successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] e-mail successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7943: // answer of the change of an account memo - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement du mémo du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement du mémo du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] memo changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] memo changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Mémo du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Mémo du compte [%s][id: %d] changé avec succès.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Account [%s][id: %d] memo successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Account [%s][id: %d] memo successfully changed.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7945: // answer of an account id search - if (RFIFOREST (fd) < 30) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Unable to find the account [%s] id. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Unable to find the account [%s] id. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf ("Le compte [%s] a pour id: %d.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log ("Le compte [%s] a pour id: %d.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf ("The account [%s] have the id: %d.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log ("The account [%s] have the id: %d.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7947: // answer of an account name search - if (RFIFOREST (fd) < 30) - return; - if (strcmp (RFIFOP (fd, 6), "") == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", - RFIFOL (fd, 2)); - ladmin_log - ("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", - RFIFOL (fd, 2)); - } - else - { - printf - ("Unable to find the account [%d] name. Account doesn't exist.\n", - RFIFOL (fd, 2)); - ladmin_log - ("Unable to find the account [%d] name. Account doesn't exist.\n", - RFIFOL (fd, 2)); - } - } - else - { - if (defaultlanguage == 'F') - { - printf ("Le compte [id: %d] a pour nom: %s.\n", - RFIFOL (fd, 2), RFIFOP (fd, 6)); - ladmin_log ("Le compte [id: %d] a pour nom: %s.\n", - RFIFOL (fd, 2), RFIFOP (fd, 6)); - } - else - { - printf ("The account [id: %d] have the name: %s.\n", - RFIFOL (fd, 2), RFIFOP (fd, 6)); - ladmin_log ("The account [id: %d] have the name: %s.\n", - RFIFOL (fd, 2), RFIFOP (fd, 6)); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 30); - break; - - case 0x7949: // answer of an account validity limit set - if (RFIFOREST (fd) < 34) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] validity limit changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] validity limit changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - time_t timestamp = RFIFOL (fd, 30); - if (timestamp == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Limite de validité du compte [%s][id: %d] changée avec succès en [illimité].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Limite de validité du compte [%s][id: %d] changée avec succès en [illimité].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (×tamp)); - if (defaultlanguage == 'F') - { - printf - ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - else - { - printf - ("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 34); - break; - - case 0x794b: // answer of an account ban set - if (RFIFOREST (fd) < 34) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - time_t timestamp = RFIFOL (fd, 30); - if (timestamp == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (×tamp)); - if (defaultlanguage == 'F') - { - printf - ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - else - { - printf - ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 34); - break; - - case 0x794d: // answer of an account ban date/time changing - if (RFIFOREST (fd) < 34) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - time_t timestamp = RFIFOL (fd, 30); - if (timestamp == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - ladmin_log - ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (×tamp)); - if (defaultlanguage == 'F') - { - printf - ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - else - { - printf - ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 34); - break; - - case 0x794f: // answer of a broadcast - if (RFIFOREST (fd) < 4) - return; - if (RFIFOW (fd, 2) == (unsigned short) -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec de l'envoi du message. Aucun server de char en ligne.\n"); - ladmin_log - ("Echec de l'envoi du message. Aucun server de char en ligne.\n"); - } - else - { - printf - ("Message sending failed. No online char-server.\n"); - ladmin_log - ("Message sending failed. No online char-server.\n"); - } - } - else - { - if (defaultlanguage == 'F') - { - printf - ("Message transmis au server de logins avec succès.\n"); - ladmin_log - ("Message transmis au server de logins avec succès.\n"); - } - else - { - printf - ("Message successfully sended to login-server.\n"); - ladmin_log - ("Message successfully sended to login-server.\n"); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 4); - break; - - case 0x7951: // answer of an account validity limit changing - if (RFIFOREST (fd) < 34) - return; - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", - RFIFOP (fd, 6)); - } - else - { - printf - ("Account [%s] validity limit changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - ladmin_log - ("Account [%s] validity limit changing failed. Account doesn't exist.\n", - RFIFOP (fd, 6)); - } - } - else - { - time_t timestamp = RFIFOL (fd, 30); - if (timestamp == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Limite de validité du compte [%s][id: %d] inchangée.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - printf - ("Le compte a une validité illimitée ou\n"); - printf - ("la modification est impossible avec les ajustements demandés.\n"); - ladmin_log - ("Limite de validité du compte [%s][id: %d] inchangée. Le compte a une validité illimitée ou la modification est impossible avec les ajustements demandés.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - else - { - printf - ("Validity limit of the account [%s][id: %d] unchanged.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - printf - ("The account have an unlimited validity limit or\n"); - printf - ("the changing is impossible with the proposed adjustments.\n"); - ladmin_log - ("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2)); - } - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (×tamp)); - if (defaultlanguage == 'F') - { - printf - ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - else - { - printf - ("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); - ladmin_log - ("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", - RFIFOP (fd, 6), RFIFOL (fd, 2), - tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 34); - break; - - case 0x7953: // answer of a request about informations of an account (by account name/id) - if (RFIFOREST (fd) < 150 - || RFIFOREST (fd) < (150 + RFIFOW (fd, 148))) - return; - { - char userid[24], error_message[20], lastlogin[24], - last_ip[16], email[40], memo[255]; - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - memcpy (userid, RFIFOP (fd, 7), sizeof (userid)); - userid[sizeof (userid) - 1] = '\0'; - memcpy (error_message, RFIFOP (fd, 40), - sizeof (error_message)); - error_message[sizeof (error_message) - 1] = '\0'; - memcpy (lastlogin, RFIFOP (fd, 60), sizeof (lastlogin)); - lastlogin[sizeof (lastlogin) - 1] = '\0'; - memcpy (last_ip, RFIFOP (fd, 84), sizeof (last_ip)); - last_ip[sizeof (last_ip) - 1] = '\0'; - memcpy (email, RFIFOP (fd, 100), sizeof (email)); - email[sizeof (email) - 1] = '\0'; - connect_until_time = (time_t) RFIFOL (fd, 140); - ban_until_time = (time_t) RFIFOL (fd, 144); - memset (memo, '\0', sizeof (memo)); - strncpy (memo, RFIFOP (fd, 150), RFIFOW (fd, 148)); - if (RFIFOL (fd, 2) == -1) - { - if (defaultlanguage == 'F') - { - printf - ("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", - parameters); - ladmin_log - ("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", - parameters); - } - else - { - printf - ("Unabled to find the account [%s]. Account doesn't exist.\n", - parameters); - ladmin_log - ("Unabled to find the account [%s]. Account doesn't exist.\n", - parameters); - } - } - else if (strlen (userid) == 0) - { - if (defaultlanguage == 'F') - { - printf - ("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", - parameters); - ladmin_log - ("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", - parameters); - } - else - { - printf - ("Unabled to find the account [id: %s]. Account doesn't exist.\n", - parameters); - ladmin_log - ("Unabled to find the account [id: %s]. Account doesn't exist.\n", - parameters); - } - } - else - { - if (defaultlanguage == 'F') - { - ladmin_log - ("Réception d'information concernant un compte.\n"); - printf - ("Le compte a les caractéristiques suivantes:\n"); - } - else - { - ladmin_log - ("Receiving information about an account.\n"); - printf ("The account is set with:\n"); - } - if (RFIFOB (fd, 6) == 0) - { - printf (" Id: %d (non-GM)\n", RFIFOL (fd, 2)); - } - else - { - if (defaultlanguage == 'F') - { - printf (" Id: %d (GM niveau %d)\n", - RFIFOL (fd, 2), (int) RFIFOB (fd, 6)); - } - else - { - printf (" Id: %d (GM level %d)\n", - RFIFOL (fd, 2), (int) RFIFOB (fd, 6)); - } - } - if (defaultlanguage == 'F') - { - printf (" Nom: '%s'\n", userid); - if (RFIFOB (fd, 31) == 0) - printf (" Sexe: Femme\n"); - else if (RFIFOB (fd, 31) == 1) - printf (" Sexe: Male\n"); - else - printf (" Sexe: Serveur\n"); - } - else - { - printf (" Name: '%s'\n", userid); - if (RFIFOB (fd, 31) == 0) - printf (" Sex: Female\n"); - else if (RFIFOB (fd, 31) == 1) - printf (" Sex: Male\n"); - else - printf (" Sex: Server\n"); - } - printf (" E-mail: %s\n", email); - switch (RFIFOL (fd, 36)) - { - case 0: - if (defaultlanguage == 'F') - printf (" Statut: 0 [Compte Ok]\n"); - else - printf (" Statut: 0 [Account OK]\n"); - break; - case 1: - printf (" Statut: 1 [Unregistered ID]\n"); - break; - case 2: - printf (" Statut: 2 [Incorrect Password]\n"); - break; - case 3: - printf (" Statut: 3 [This ID is expired]\n"); - break; - case 4: - printf - (" Statut: 4 [Rejected from Server]\n"); - break; - case 5: - printf - (" Statut: 5 [You have been blocked by the GM Team]\n"); - break; - case 6: - printf - (" Statut: 6 [Your Game's EXE file is not the latest version]\n"); - break; - case 7: - printf - (" Statut: 7 [You are Prohibited to log in until %s]\n", - error_message); - break; - case 8: - printf - (" Statut: 8 [Server is jammed due to over populated]\n"); - break; - case 9: - printf (" Statut: 9 [No MSG]\n"); - break; - default: // 100 - printf - (" Statut: %d [This ID is totally erased]\n", - RFIFOL (fd, 36)); - break; - } - if (defaultlanguage == 'F') - { - if (ban_until_time == 0) - { - printf (" Banissement: non banni.\n"); - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (&ban_until_time)); - printf (" Banissement: jusqu'au %s.\n", - tmpstr); - } - if (RFIFOL (fd, 32) > 1) - printf (" Compteur: %d connexions.\n", - RFIFOL (fd, 32)); - else - printf (" Compteur: %d connexion.\n", - RFIFOL (fd, 32)); - printf (" Dernière connexion le: %s (ip: %s)\n", - lastlogin, last_ip); - if (connect_until_time == 0) - { - printf (" Limite de validité: illimité.\n"); - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (&connect_until_time)); - printf (" Limite de validité: jusqu'au %s.\n", - tmpstr); - } - } - else - { - if (ban_until_time == 0) - { - printf (" Banishment: not banished.\n"); - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (&ban_until_time)); - printf (" Banishment: until %s.\n", tmpstr); - } - if (RFIFOL (fd, 32) > 1) - printf (" Count: %d connections.\n", - RFIFOL (fd, 32)); - else - printf (" Count: %d connection.\n", - RFIFOL (fd, 32)); - printf (" Last connection at: %s (ip: %s)\n", - lastlogin, last_ip); - if (connect_until_time == 0) - { - printf (" Validity limit: unlimited.\n"); - } - else - { - char tmpstr[128]; - strftime (tmpstr, 24, date_format, - localtime (&connect_until_time)); - printf (" Validity limit: until %s.\n", - tmpstr); - } - } - printf (" Memo: '%s'\n", memo); - } - } - bytes_to_read = 0; - RFIFOSKIP (fd, 150 + RFIFOW (fd, 148)); - break; - - default: - printf - ("Remote administration has been disconnected (unknown packet).\n"); - ladmin_log ("'End of connection, unknown packet.\n"); - session[fd]->eof = 1; - return; - } - } - - // if we don't wait new packets, do the prompt - prompt (); -} - -//------------------------------------ -// Function to connect to login-server -//------------------------------------ -int Connect_login_server (void) -{ - if (defaultlanguage == 'F') - { - Iprintf ("Essai de connection au server de logins...\n"); - ladmin_log ("Essai de connection au server de logins...\n"); - } - else - { - Iprintf ("Attempt to connect to login-server...\n"); - ladmin_log ("Attempt to connect to login-server...\n"); - } - - if ((login_fd = make_connection (login_ip, loginserverport)) < 0) - return 0; - -#ifdef PASSWORDENC - if (passenc == 0) - { -#endif - WFIFOW (login_fd, 0) = 0x7918; // Request for administation login - WFIFOW (login_fd, 2) = 0; // no encrypted - memcpy (WFIFOP (login_fd, 4), loginserveradminpassword, 24); - WFIFOSET (login_fd, 28); - bytes_to_read = 1; - - if (defaultlanguage == 'F') - { - Iprintf ("Envoi du mot de passe...\n"); - ladmin_log ("Envoi du mot de passe...\n"); - } - else - { - Iprintf ("Sending of the password...\n"); - ladmin_log ("Sending of the password...\n"); - } -#ifdef PASSWORDENC - } - else - { - WFIFOW (login_fd, 0) = 0x791a; // Sending request about the coding key - WFIFOSET (login_fd, 2); - bytes_to_read = 1; - if (defaultlanguage == 'F') - { - Iprintf ("Demande de la clef MD5...\n"); - ladmin_log ("Demande de la clef MD5...\n"); - } - else - { - Iprintf ("Request about the MD5 key...\n"); - ladmin_log ("Request about the MD5 key...\n"); - } - } -#endif - - return 0; -} - -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, français, deutsch, español -//------------------------------------------------- -int config_switch (const char *str) -{ - if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 - || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 - || strcasecmp (str, "si") == 0) - return 1; - if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 - || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) - return 0; - - return atoi (str); -} - -//----------------------------------- -// Reading general configuration file -//----------------------------------- -int ladmin_config_read (const char *cfgName) -{ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen_ (cfgName, "r"); - if (fp == NULL) - { - if (defaultlanguage == 'F') - { - printf ("\033[0mFichier de configuration (%s) non trouvé.\n", - cfgName); - } - else - { - printf ("\033[0mConfiguration file (%s) not found.\n", cfgName); - } - return 1; - } - - if (defaultlanguage == 'F') - { - Iprintf - ("\033[0m---Début de lecture du fichier de configuration Ladmin (%s)\n", - cfgName); - } - else - { - Iprintf - ("\033[0m---Start reading of Ladmin configuration file (%s)\n", - cfgName); - } - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof (line) - 1] = '\0'; - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) == 2) - { - remove_control_chars (w1); - remove_control_chars (w2); - - if (strcasecmp (w1, "login_ip") == 0) - { - struct hostent *h = gethostbyname (w2); - if (h != NULL) - { - if (defaultlanguage == 'F') - { - Iprintf - ("Adresse du serveur de logins: %s -> %d.%d.%d.%d\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]); - } - else - { - Iprintf - ("Login server IP address: %s -> %d.%d.%d.%d\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 (loginserverip, "%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]); - } - else - memcpy (loginserverip, w2, 16); - } - else if (strcasecmp (w1, "login_port") == 0) - { - loginserverport = atoi (w2); - } - else if (strcasecmp (w1, "admin_pass") == 0) - { - strncpy (loginserveradminpassword, w2, - sizeof (loginserveradminpassword)); - loginserveradminpassword[sizeof (loginserveradminpassword) - - 1] = '\0'; -#ifdef PASSWORDENC - } - else if (strcasecmp (w1, "passenc") == 0) - { - passenc = atoi (w2); - if (passenc < 0 || passenc > 2) - passenc = 0; -#endif - } - else if (strcasecmp (w1, "defaultlanguage") == 0) - { - if (w2[0] == 'F' || w2[0] == 'E') - defaultlanguage = w2[0]; - } - else if (strcasecmp (w1, "ladmin_log_filename") == 0) - { - strncpy (ladmin_log_filename, w2, - sizeof (ladmin_log_filename)); - ladmin_log_filename[sizeof (ladmin_log_filename) - 1] = '\0'; - } - else if (strcasecmp (w1, "date_format") == 0) - { // note: never have more than 19 char for the date! - switch (atoi (w2)) - { - case 0: - strcpy (date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 - break; - case 1: - strcpy (date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 - break; - case 2: - strcpy (date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 - break; - case 3: - strcpy (date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 - break; - } - } - else if (strcasecmp (w1, "import") == 0) - { - ladmin_config_read (w2); - } - } - } - fclose_ (fp); - - login_ip = inet_addr (loginserverip); - - if (defaultlanguage == 'F') - { - Iprintf ("---Lecture du fichier de configuration Ladmin terminée.\n"); - } - else - { - Iprintf ("---End reading of Ladmin configuration file.\n"); - } - - return 0; -} - -//-------------------------------------- -// Function called at exit of the server -//-------------------------------------- -void term_func (void) -{ - - if (already_exit_function == 0) - { - delete_session (login_fd); - - if (defaultlanguage == 'F') - { - Iprintf - ("\033[0m----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); - ladmin_log - ("----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); - } - else - { - Iprintf - ("\033[0m----End of Ladmin (normal end with closing of all files).\n"); - ladmin_log - ("----End of Ladmin (normal end with closing of all files).\n"); - } - - already_exit_function = 1; - } -} - -//------------------------ -// Main function of ladmin -//------------------------ -int do_init (int argc, char **argv) -{ - eathena_interactive_session = isatty (0); - // read ladmin configuration - ladmin_config_read ((argc > 1) ? argv[1] : LADMIN_CONF_NAME); - - ladmin_log (""); - if (defaultlanguage == 'F') - { - ladmin_log ("Fichier de configuration lu.\n"); - } - else - { - ladmin_log ("Configuration file readed.\n"); - } - - srand (time (NULL)); - - set_defaultparse (parse_fromlogin); - - if (defaultlanguage == 'F') - { - Iprintf ("Outil d'administration à distance de eAthena.\n"); - Iprintf ("(pour eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, - ATHENA_MINOR_VERSION, ATHENA_REVISION); - } - else - { - Iprintf ("EAthena login-server administration tool.\n"); - Iprintf ("(for eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, - ATHENA_MINOR_VERSION, ATHENA_REVISION); - } - - if (defaultlanguage == 'F') - { - ladmin_log ("Ladmin est prêt.\n"); - Iprintf ("Ladmin est \033[1;32mprêt\033[0m.\n\n"); - } - else - { - ladmin_log ("Ladmin is ready.\n"); - Iprintf ("Ladmin is \033[1;32mready\033[0m.\n\n"); - } - - Connect_login_server (); - - return 0; -} diff --git a/src/ladmin/ladmin.cpp b/src/ladmin/ladmin.cpp new file mode 100644 index 0000000..01eb244 --- /dev/null +++ b/src/ladmin/ladmin.cpp @@ -0,0 +1,6476 @@ +// $Id: ladmin.c,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ +/////////////////////////////////////////////////////////////////////////// +// EAthena login-server remote administration tool +// Ladamin in C by [Yor] +// if you modify this software, modify ladmin in tool too. +/////////////////////////////////////////////////////////////////////////// + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> // gettimeofday +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> // close +#include <signal.h> +#include <fcntl.h> +#include <string.h> // str* +#include <arpa/inet.h> // inet_addr +#include <netdb.h> // gethostbyname +#include <stdarg.h> // valist +#include <ctype.h> // tolower + +#include "../common/core.hpp" +#include "../common/socket.hpp" +#include "ladmin.hpp" +#include "../common/version.hpp" +#include "../common/mmo.hpp" + +#ifdef PASSWORDENC +#include "../common/md5calc.hpp" +#endif + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +int eathena_interactive_session; // from core.c +#define Iprintf if (eathena_interactive_session) printf + +//-------------------------------INSTRUCTIONS------------------------------ +// Set the variables below: +// IP of the login server. +// Port where the login-server listens incoming packets. +// Password of administration (same of config_athena.conf). +// Displayed language of the sofware (if not correct, english is used). +// IMPORTANT: +// Be sure that you authorize remote administration in login-server +// (see login_athena.conf, 'admin_state' parameter) +//------------------------------------------------------------------------- +char loginserverip[16] = "127.0.0.1"; // IP of login-server +int loginserverport = 6900; // Port of login-server +char loginserveradminpassword[24] = "admin"; // Administration password +#ifdef PASSWORDENC +int passenc = 2; // Encoding type of the password +#else +int passenc = 0; // Encoding type of the password +#endif +char defaultlanguage = 'E'; // Default language (F: Français/E: English) + // (if it's not 'F', default is English) +char ladmin_log_filename[1024] = "log/ladmin.log"; +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +//------------------------------------------------------------------------- +// LIST of COMMANDs that you can type at the prompt: +// To use these commands you can only type only the first letters. +// You must type a minimum of letters (you can not type 'a', +// because ladmin doesn't know if it's for 'aide' or for 'add') +// <Example> q <= quit, li <= list, pass <= passwd, etc. +// +// Note: every time you must give a account_name, you can use "" or '' (spaces can be included) +// +// aide/help/? +// Display the description of the commands +// aide/help/? [command] +// Display the description of the specified command +// +// add <account_name> <sex> <password> +// Create an account with the default email (a@a.com). +// Concerning the sex, only the first letter is used (F or M). +// The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail. +// When the password is omitted, the input is done without displaying of the pressed keys. +// <example> add testname Male testpass +// +// ban/banish yyyy/mm/dd hh:mm:ss <account name> +// Changes the final date of a banishment of an account. +// Like banset, but <account name> is at end. +// +// banadd <account_name> <modifier> +// Adds or substracts time from the final date of a banishment of an account. +// Modifier 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> banadd testname +1m-2mn1s-6y +// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. +// NOTE: If you modify the final date of a non-banished account, +// you fix the final date to (actual time +- adjustments) +// +// banset <account_name> yyyy/mm/dd [hh:mm:ss] +// Changes the final date of a banishment of an account. +// Default time [hh:mm:ss]: 23:59:59. +// banset <account_name> 0 +// Set a non-banished account (0 = unbanished). +// +// block <account name> +// Set state 5 (You have been blocked by the GM Team) to an account. +// Like state <account name> 5. +// +// check <account_name> <password> +// Check the validity of a password for an account +// NOTE: Server will never sends back a password. +// It's the only method you have to know if a password is correct. +// The other method is to have a ('physical') access to the accounts file. +// +// create <account_name> <sex> <email> <password> +// Like the 'add' command, but with e-mail moreover. +// <example> create testname Male my@mail.com testpass +// +// del <account name> +// Remove an account. +// This order requires confirmation. After confirmation, the account is deleted. +// +// email <account_name> <email> +// Modify the e-mail of an account. +// +// getcount +// Give the number of players online on all char-servers. +// +// gm <account_name> [GM_level] +// Modify the GM level of an account. +// Default value remove GM level (GM level = 0). +// <example> gm testname 80 +// +// id <account name> +// Give the id of an account. +// +// info <account_id> +// Display complete information of an account. +// +// kami <message> +// Sends a broadcast message on all map-server (in yellow). +// kamib <message> +// Sends a broadcast message on all map-server (in blue). +// +// language <language> +// Change the language of displaying. +// +// list/ls [start_id [end_id]] +// Display a list of accounts. +// 'start_id', 'end_id': indicate end and start identifiers. +// Research by name is not possible with this command. +// <example> list 10 9999999 +// +// listBan/lsBan [start_id [end_id]] +// Like list/ls, but only for accounts with state or banished +// +// listGM/lsGM [start_id [end_id]] +// Like list/ls, but only for GM accounts +// +// listOK/lsOK [start_id [end_id]] +// Like list/ls, but only for accounts without state and not banished +// +// memo <account_name> <memo> +// Modify the memo of an account. +// 'memo': it can have until 253 characters (with spaces or not). +// +// name <account_id> +// Give the name of an account. +// +// passwd <account_name> <new_password> +// Change the password of an account. +// When new password is omitted, the input is done without displaying of the pressed keys. +// +// quit/end/exit +// End of the program of administration +// +// reloadGM +// Reload GM configuration file +// +// search <expression> +// Seek accounts. +// Displays the accounts whose names correspond. +// search -r/-e/--expr/--regex <expression> +// Seek accounts by regular expression. +// Displays the accounts whose names correspond. +// +// sex <account_name> <sex> +// Modify the sex of an account. +// <example> sex testname Male +// +// state <account_name> <new_state> <error_message_#7> +// Change the state of an account. +// 'new_state': state is the state of the packet 0x006a + 1. The possibilities are: +// 0 = Account ok 6 = Your Game's EXE file is not the latest version +// 1 = Unregistered ID 7 = You are Prohibited to log in until %s +// 2 = Incorrect Password 8 = Server is jammed due to over populated +// 3 = This ID is expired 9 = No MSG +// 4 = Rejected from Server 100 = This ID has been totally erased +// 5 = You have been blocked by the GM Team +// all other values are 'No MSG', then use state 9 please. +// 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a) +// +// timeadd <account_name> <modifier> +// Adds or substracts time from the validity limit of an account. +// Modifier 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> timeadd testname +1m-2mn1s-6y +// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. +// NOTE: You can not modify a unlimited validity limit. +// If you want modify it, you want probably create a limited validity limit. +// So, at first, you must set the validity limit to a date/time. +// +// timeset <account_name> yyyy/mm/dd [hh:mm:ss] +// Changes the validity limit of an account. +// Default time [hh:mm:ss]: 23:59:59. +// timeset <account_name> 0 +// Gives an unlimited validity limit (0 = unlimited). +// +// unban/unbanish <account name> +// Unban an account. +// Like banset <account name> 0. +// +// unblock <account name> +// Set state 0 (Account ok) to an account. +// Like state <account name> 0. +// +// version +// Display the version of the login-server. +// +// who <account name> +// Displays complete information of an account. +// +//------------------------------------------------------------------------- +int login_fd; +int login_ip; +int bytes_to_read = 0; // flag to know if we waiting bytes from login-server +char command[1024]; +char parameters[1024]; +int list_first, list_last, list_type, list_count; // parameter to display a list of accounts +int already_exit_function = 0; // sometimes, the exit function is called twice... so, don't log twice the message + +//------------------------------ +// Writing function of logs file +//------------------------------ +int ladmin_log (const char *fmt, ...) +{ + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start (ap, fmt); + + logfp = fopen_ (ladmin_log_filename, "a"); + if (logfp) + { + if (fmt[0] == '\0') // jump a line if no message + fprintf (logfp, "\n"); + else + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 24, date_format, localtime (&(tv.tv_sec))); + sprintf (tmpstr + strlen (tmpstr), ".%03d: %s", + (int) tv.tv_usec / 1000, fmt); + vfprintf (logfp, tmpstr, ap); + } + fclose_ (logfp); + } + + va_end (ap); + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars (unsigned char *str) +{ + int i; + int change = 0; + + for (i = 0; str[i]; i++) + { + if (str[i] < 32) + { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//--------------------------------------------- +// Function to return ordonal text of a number. +//--------------------------------------------- +const char *makeordinal (int number) +{ + if (defaultlanguage == 'F') + { + if (number == 0) + return ""; + else if (number == 1) + return "er"; + else + return "ème"; + } + else + { + if ((number % 10) < 4 && (number % 10) != 0 + && (number < 10 || number > 20)) + { + if ((number % 10) == 1) + return "st"; + else if ((number % 10) == 2) + return "nd"; + else + return "rd"; + } + else + { + return "th"; + } + } + return ""; +} + +//----------------------------------------------------------------------------------------- +// Function to test of the validity of an account name (return 0 if incorrect, and 1 if ok) +//----------------------------------------------------------------------------------------- +int verify_accountname (char *account_name) +{ + int i; + + for (i = 0; account_name[i]; i++) + { + if (account_name[i] < 32) + { + if (defaultlanguage == 'F') + { + printf + ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", + i + 1, makeordinal (i + 1)); + ladmin_log + ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", + i + 1, makeordinal (i + 1)); + } + else + { + printf + ("Illegal character found in the account name (%d%s character).\n", + i + 1, makeordinal (i + 1)); + ladmin_log + ("Illegal character found in the account name (%d%s character).\n", + i + 1, makeordinal (i + 1)); + } + return 0; + } + } + + if (strlen (account_name) < 4) + { + if (defaultlanguage == 'F') + { + printf + ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); + ladmin_log + ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); + } + else + { + printf + ("Account name is too short. Please input an account name of 4-23 bytes.\n"); + ladmin_log + ("Account name is too short. Please input an account name of 4-23 bytes.\n"); + } + return 0; + } + + if (strlen (account_name) > 23) + { + if (defaultlanguage == 'F') + { + printf + ("Nom du compte trop long. Entrez un nom de compte de 4-23 caractères.\n"); + ladmin_log + ("Nom du compte trop long. Entrez un nom de compte de 4-23 caractères.\n"); + } + else + { + printf + ("Account name is too long. Please input an account name of 4-23 bytes.\n"); + ladmin_log + ("Account name is too long. Please input an account name of 4-23 bytes.\n"); + } + return 0; + } + + return 1; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check (unsigned char *email) +{ + char ch; + unsigned 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; +} + +//---------------------------------- +// Sub-function: Input of a password +//---------------------------------- +int typepasswd (char *password) +{ + char password1[1023], password2[1023]; + int letter; + int i; + + if (defaultlanguage == 'F') + { + ladmin_log + ("Aucun mot de passe n'a été donné. Demande d'un mot de passe.\n"); + } + else + { + ladmin_log ("No password was given. Request to obtain a password.\n"); + } + + memset (password1, '\0', sizeof (password1)); + memset (password2, '\0', sizeof (password2)); + if (defaultlanguage == 'F') + printf ("\033[1;36m Entrez le mot de passe > \033[0;32;42m"); + else + printf ("\033[1;36m Type the password > \033[0;32;42m"); + i = 0; + while ((letter = getchar ()) != '\n') + password1[i++] = letter; + if (defaultlanguage == 'F') + printf + ("\033[0m\033[1;36m Ré-entrez le mot de passe > \033[0;32;42m"); + else + printf ("\033[0m\033[1;36m Verify the password > \033[0;32;42m"); + i = 0; + while ((letter = getchar ()) != '\n') + password2[i++] = letter; + + printf ("\033[0m"); + fflush (stdout); + fflush (stdin); + + if (strcmp (password1, password2) != 0) + { + if (defaultlanguage == 'F') + { + printf + ("Erreur de vérification du mot de passe: Saisissez le même mot de passe svp.\n"); + ladmin_log + ("Erreur de vérification du mot de passe: Saisissez le même mot de passe svp.\n"); + ladmin_log (" Premier mot de passe: %s, second mot de passe: %s.\n", + password1, password2); + } + else + { + printf + ("Password verification failed. Please input same password.\n"); + ladmin_log + ("Password verification failed. Please input same password.\n"); + ladmin_log (" First password: %s, second password: %s.\n", + password1, password2); + } + return 0; + } + if (defaultlanguage == 'F') + { + ladmin_log ("Mot de passe saisi: %s.\n", password1); + } + else + { + ladmin_log ("Typed password: %s.\n", password1); + } + strcpy (password, password1); + return 1; +} + +//------------------------------------------------------------------------------------ +// Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok) +//------------------------------------------------------------------------------------ +int verify_password (char *password) +{ + int i; + + for (i = 0; password[i]; i++) + { + if (password[i] < 32) + { + if (defaultlanguage == 'F') + { + printf + ("Caractère interdit trouvé dans le mot de passe (%d%s caractère).\n", + i + 1, makeordinal (i + 1)); + ladmin_log + ("Caractère interdit trouvé dans le nom du compte (%d%s caractère).\n", + i + 1, makeordinal (i + 1)); + } + else + { + printf + ("Illegal character found in the password (%d%s character).\n", + i + 1, makeordinal (i + 1)); + ladmin_log + ("Illegal character found in the password (%d%s character).\n", + i + 1, makeordinal (i + 1)); + } + return 0; + } + } + + if (strlen (password) < 4) + { + if (defaultlanguage == 'F') + { + printf + ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); + ladmin_log + ("Nom du compte trop court. Entrez un nom de compte de 4-23 caractères.\n"); + } + else + { + printf + ("Account name is too short. Please input an account name of 4-23 bytes.\n"); + ladmin_log + ("Account name is too short. Please input an account name of 4-23 bytes.\n"); + } + return 0; + } + + if (strlen (password) > 23) + { + if (defaultlanguage == 'F') + { + printf + ("Mot de passe trop long. Entrez un mot de passe de 4-23 caractères.\n"); + ladmin_log + ("Mot de passe trop long. Entrez un mot de passe de 4-23 caractères.\n"); + } + else + { + printf + ("Password is too long. Please input a password of 4-23 bytes.\n"); + ladmin_log + ("Password is too long. Please input a password of 4-23 bytes.\n"); + } + return 0; + } + + return 1; +} + +//------------------------------------------------------------------ +// Sub-function: Check the name of a command (return complete name) +//----------------------------------------------------------------- +int check_command (char *command) +{ +// help + if (strncmp (command, "aide", 2) == 0 && strncmp (command, "aide", strlen (command)) == 0) // not 1 letter command: 'aide' or 'add'? + strcpy (command, "aide"); + else if (strncmp (command, "help", 1) == 0 + && strncmp (command, "help", strlen (command)) == 0) + strcpy (command, "help"); +// general commands + else if (strncmp (command, "add", 2) == 0 && strncmp (command, "add", strlen (command)) == 0) // not 1 letter command: 'aide' or 'add'? + strcpy (command, "add"); + else if ((strncmp (command, "ban", 3) == 0 + && strncmp (command, "ban", strlen (command)) == 0) + || (strncmp (command, "banish", 4) == 0 + && strncmp (command, "banish", strlen (command)) == 0)) + strcpy (command, "ban"); + else if ((strncmp (command, "banadd", 4) == 0 && strncmp (command, "banadd", strlen (command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? + strcmp (command, "ba") == 0) + strcpy (command, "banadd"); + else if ((strncmp (command, "banset", 4) == 0 && strncmp (command, "banset", strlen (command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? + strcmp (command, "bs") == 0) + strcpy (command, "banset"); + else if (strncmp (command, "block", 2) == 0 + && strncmp (command, "block", strlen (command)) == 0) + strcpy (command, "block"); + else if (strncmp (command, "check", 2) == 0 && strncmp (command, "check", strlen (command)) == 0) // not 1 letter command: 'check' or 'create'? + strcpy (command, "check"); + else if (strncmp (command, "create", 2) == 0 && strncmp (command, "create", strlen (command)) == 0) // not 1 letter command: 'check' or 'create'? + strcpy (command, "create"); + else if (strncmp (command, "delete", 1) == 0 + && strncmp (command, "delete", strlen (command)) == 0) + strcpy (command, "delete"); + else if ((strncmp (command, "email", 2) == 0 && strncmp (command, "email", strlen (command)) == 0) || // not 1 letter command: 'email', 'end' or 'exit'? + (strncmp (command, "e-mail", 2) == 0 + && strncmp (command, "e-mail", strlen (command)) == 0)) + strcpy (command, "email"); + else if (strncmp (command, "getcount", 2) == 0 && strncmp (command, "getcount", strlen (command)) == 0) // not 1 letter command: 'getcount' or 'gm'? + strcpy (command, "getcount"); +// else if (strncmp(command, "gm", 2) == 0 && strncmp(command, "gm", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? +// strcpy(command, "gm"); +// else if (strncmp(command, "id", 2) == 0 && strncmp(command, "id", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? +// strcpy(command, "id"); + else if (strncmp (command, "info", 2) == 0 && strncmp (command, "info", strlen (command)) == 0) // not 1 letter command: 'id' or 'info'? + strcpy (command, "info"); +// else if (strncmp(command, "kami", 4) == 0 && strncmp(command, "kami", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? +// strcpy(command, "kami"); +// else if (strncmp(command, "kamib", 5) == 0 && strncmp(command, "kamib", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? +// strcpy(command, "kamib"); + else if ((strncmp (command, "language", 2) == 0 && strncmp (command, "language", strlen (command)) == 0)) // not 1 letter command: 'language' or 'list'? + strcpy (command, "language"); + else if ((strncmp (command, "list", 2) == 0 && strncmp (command, "list", strlen (command)) == 0) || // 'list' is default list command // not 1 letter command: 'language' or 'list'? + strcmp (command, "ls") == 0) + strcpy (command, "list"); + else if (strncmp (command, "itemfrob", 6) == 0) + strcpy (command, "itemfrob"); + else if ((strncmp (command, "listban", 5) == 0 + && strncmp (command, "listban", strlen (command)) == 0) + || (strncmp (command, "lsban", 3) == 0 + && strncmp (command, "lsban", strlen (command)) == 0) + || strcmp (command, "lb") == 0) + strcpy (command, "listban"); + else if ((strncmp (command, "listgm", 5) == 0 + && strncmp (command, "listgm", strlen (command)) == 0) + || (strncmp (command, "lsgm", 3) == 0 + && strncmp (command, "lsgm", strlen (command)) == 0) + || strcmp (command, "lg") == 0) + strcpy (command, "listgm"); + else if ((strncmp (command, "listok", 5) == 0 + && strncmp (command, "listok", strlen (command)) == 0) + || (strncmp (command, "lsok", 3) == 0 + && strncmp (command, "lsok", strlen (command)) == 0) + || strcmp (command, "lo") == 0) + strcpy (command, "listok"); + else if (strncmp (command, "memo", 1) == 0 + && strncmp (command, "memo", strlen (command)) == 0) + strcpy (command, "memo"); + else if (strncmp (command, "name", 1) == 0 + && strncmp (command, "name", strlen (command)) == 0) + strcpy (command, "name"); + else if ((strncmp (command, "password", 1) == 0 + && strncmp (command, "password", strlen (command)) == 0) + || strcmp (command, "passwd") == 0) + strcpy (command, "password"); + else if (strncmp (command, "reloadgm", 1) == 0 + && strncmp (command, "reloadgm", strlen (command)) == 0) + strcpy (command, "reloadgm"); + else if (strncmp (command, "search", 3) == 0 && strncmp (command, "search", strlen (command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? + strcpy (command, "search"); // not 2 letters command: 'search' or 'sex'? +// else if (strncmp(command, "sex", 3) == 0 && strncmp(command, "sex", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? +// strcpy(command, "sex"); // not 2 letters command: 'search' or 'sex'? + else if (strncmp (command, "state", 2) == 0 && strncmp (command, "state", strlen (command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? + strcpy (command, "state"); + else if ((strncmp (command, "timeadd", 5) == 0 && strncmp (command, "timeadd", strlen (command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? + strcmp (command, "ta") == 0) + strcpy (command, "timeadd"); + else if ((strncmp (command, "timeset", 5) == 0 && strncmp (command, "timeset", strlen (command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? + strcmp (command, "ts") == 0) + strcpy (command, "timeset"); + else if ((strncmp (command, "unban", 5) == 0 + && strncmp (command, "unban", strlen (command)) == 0) + || (strncmp (command, "unbanish", 4) == 0 + && strncmp (command, "unbanish", strlen (command)) == 0)) + strcpy (command, "unban"); + else if (strncmp (command, "unblock", 4) == 0 + && strncmp (command, "unblock", strlen (command)) == 0) + strcpy (command, "unblock"); + else if (strncmp (command, "version", 1) == 0 + && strncmp (command, "version", strlen (command)) == 0) + strcpy (command, "version"); + else if (strncmp (command, "who", 1) == 0 + && strncmp (command, "who", strlen (command)) == 0) + strcpy (command, "who"); +// quit + else if (strncmp (command, "quit", 1) == 0 + && strncmp (command, "quit", strlen (command)) == 0) + strcpy (command, "quit"); + else if (strncmp (command, "exit", 2) == 0 && strncmp (command, "exit", strlen (command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? + strcpy (command, "exit"); + else if (strncmp (command, "end", 2) == 0 && strncmp (command, "end", strlen (command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? + strcpy (command, "end"); + + return 0; +} + +//----------------------------------------- +// Sub-function: Display commands of ladmin +//----------------------------------------- +void display_help (char *param, int language) +{ + char command[1023]; + int i; + + memset (command, '\0', sizeof (command)); + + if (sscanf (param, "%s ", command) < 1 || strlen (command) == 0) + strcpy (command, ""); // any value that is not a command + + if (command[0] == '?') + { + if (defaultlanguage == 'F') + strcpy (command, "aide"); + else + strcpy (command, "help"); + } + + // lowercase for command + for (i = 0; command[i]; i++) + command[i] = tolower (command[i]); + + // Analyse of the command + check_command (command); // give complete name to the command + + if (defaultlanguage == 'F') + { + ladmin_log ("Affichage des commandes ou d'une commande.\n"); + } + else + { + ladmin_log ("Displaying of the commands or a command.\n"); + } + + if (language == 1) + { + if (strcmp (command, "aide") == 0) + { + printf ("aide/help/?\n"); + printf (" Affiche la description des commandes\n"); + printf ("aide/help/? [commande]\n"); + printf (" Affiche la description de la commande specifiée\n"); + } + else if (strcmp (command, "help") == 0) + { + printf ("aide/help/?\n"); + printf (" Display the description of the commands\n"); + printf ("aide/help/? [command]\n"); + printf (" Display the description of the specified command\n"); +// general commands + } + else if (strcmp (command, "add") == 0) + { + printf ("add <nomcompte> <sexe> <motdepasse>\n"); + printf (" Crée un compte avec l'email par défaut (a@a.com).\n"); + printf + (" Concernant le sexe, seule la première lettre compte (F ou M).\n"); + printf + (" L'e-mail est a@a.com (e-mail par défaut). C'est comme n'avoir aucun e-mail.\n"); + printf + (" Lorsque motdepasse est omis, la saisie se fait sans que la frappe se voit.\n"); + printf (" <exemple> add testname Male testpass\n"); + } + else if (strcmp (command, "ban") == 0) + { + printf ("ban/banish aaaa/mm/jj hh:mm:ss <nom compte>\n"); + printf (" Change la date de fin de bannissement d'un compte.\n"); + printf (" Comme banset, mais <nom compte> est à la fin.\n"); + } + else if (strcmp (command, "banadd") == 0) + { + printf ("banadd <nomcompte> <Modificateur>\n"); + printf + (" Ajoute ou soustrait du temps à la date de banissement d'un compte.\n"); + printf (" Les modificateurs sont construits comme suit:\n"); + printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf (" Elément modifié:\n"); + printf (" a ou y: année\n"); + printf (" m: mois\n"); + printf (" j ou d: jour\n"); + printf (" h: heure\n"); + printf (" mn: minute\n"); + printf (" s: seconde\n"); + printf (" <exemple> banadd testname +1m-2mn1s-6a\n"); + printf + (" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + printf + ("NOTE: Si vous modifez la date de banissement d'un compte non bani,\n"); + printf + (" vous indiquez comme date (le moment actuel +- les ajustements)\n"); + } + else if (strcmp (command, "banset") == 0) + { + printf ("banset <nomcompte> aaaa/mm/jj [hh:mm:ss]\n"); + printf (" Change la date de fin de bannissement d'un compte.\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + printf ("banset <nomcompte> 0\n"); + printf (" Débanni un compte (0 = de-banni).\n"); + } + else if (strcmp (command, "block") == 0) + { + printf ("block <nom compte>\n"); + printf + (" Place le status d'un compte à 5 (You have been blocked by the GM Team).\n"); + printf + (" La commande est l'équivalent de state <nom_compte> 5.\n"); + } + else if (strcmp (command, "check") == 0) + { + printf ("check <nomcompte> <motdepasse>\n"); + printf + (" Vérifie la validité d'un mot de passe pour un compte\n"); + printf (" NOTE: Le serveur n'enverra jamais un mot de passe.\n"); + printf + (" C'est la seule méthode que vous possédez pour savoir\n"); + printf + (" si un mot de passe est le bon. L'autre méthode est\n"); + printf + (" d'avoir un accès ('physique') au fichier des comptes.\n"); + } + else if (strcmp (command, "create") == 0) + { + printf ("create <nomcompte> <sexe> <email> <motdepasse>\n"); + printf (" Comme la commande add, mais avec l'e-mail en plus.\n"); + printf + (" <exemple> create testname Male mon@mail.com testpass\n"); + } + else if (strcmp (command, "delete") == 0) + { + printf ("del <nom compte>\n"); + printf (" Supprime un compte.\n"); + printf + (" La commande demande confirmation. Après confirmation, le compte est détruit.\n"); + } + else if (strcmp (command, "email") == 0) + { + printf ("email <nomcompte> <email>\n"); + printf (" Modifie l'e-mail d'un compte.\n"); + } + else if (strcmp (command, "getcount") == 0) + { + printf ("getcount\n"); + printf + (" Donne le nombre de joueurs en ligne par serveur de char.\n"); + } + else if (strcmp (command, "gm") == 0) + { + printf ("gm <nomcompte> [Niveau_GM]\n"); + printf (" Modifie le niveau de GM d'un compte.\n"); + printf + (" Valeur par défaut: 0 (suppression du niveau de GM).\n"); + printf (" <exemple> gm nomtest 80\n"); + } + else if (strcmp (command, "id") == 0) + { + printf ("id <nom compte>\n"); + printf (" Donne l'id d'un compte.\n"); + } + else if (strcmp (command, "info") == 0) + { + printf ("info <idcompte>\n"); + printf (" Affiche les informations sur un compte.\n"); + } + else if (strcmp (command, "kami") == 0) + { + printf ("kami <message>\n"); + printf + (" Envoi un message général sur tous les serveurs de map (en jaune).\n"); + } + else if (strcmp (command, "kamib") == 0) + { + printf ("kamib <message>\n"); + printf + (" Envoi un message général sur tous les serveurs de map (en bleu).\n"); + } + else if (strcmp (command, "language") == 0) + { + printf ("language <langue>\n"); + printf (" Change la langue d'affichage.\n"); + printf (" Langues possibles: 'Français' ou 'English'.\n"); + } + else if (strcmp (command, "list") == 0) + { + printf ("list/ls [Premier_id [Dernier_id]]\n"); + printf (" Affiche une liste de comptes.\n"); + printf + (" 'Premier_id', 'Dernier_id': indique les identifiants de départ et de fin.\n"); + printf + (" La recherche par nom n'est pas possible avec cette commande.\n"); + printf (" <example> list 10 9999999\n"); + } + else if (strcmp (command, "itemfrob") == 0) + { + printf ("Not localised yet.\n"); + } + else if (strcmp (command, "listban") == 0) + { + printf ("listBan/lsBan [Premier_id [Dernier_id]]\n"); + printf + (" Comme list/ls, mais seulement pour les comptes avec statut ou bannis.\n"); + } + else if (strcmp (command, "listgm") == 0) + { + printf ("listGM/lsGM [Premier_id [Dernier_id]]\n"); + printf (" Comme list/ls, mais seulement pour les comptes GM.\n"); + } + else if (strcmp (command, "listok") == 0) + { + printf ("listOK/lsOK [Premier_id [Dernier_id]]\n"); + printf + (" Comme list/ls, mais seulement pour les comptes sans statut et non bannis.\n"); + } + else if (strcmp (command, "memo") == 0) + { + printf ("memo <nomcompte> <memo>\n"); + printf (" Modifie le mémo d'un compte.\n"); + printf + (" 'memo': Il peut avoir jusqu'à 253 caractères (avec des espaces ou non).\n"); + } + else if (strcmp (command, "name") == 0) + { + printf ("name <idcompte>\n"); + printf (" Donne le nom d'un compte.\n"); + } + else if (strcmp (command, "password") == 0) + { + printf ("passwd <nomcompte> <nouveaumotdepasse>\n"); + printf (" Change le mot de passe d'un compte.\n"); + printf (" Lorsque nouveaumotdepasse est omis,\n"); + printf (" la saisie se fait sans que la frappe ne se voit.\n"); + } + else if (strcmp (command, "reloadgm") == 0) + { + printf ("reloadGM\n"); + printf (" Reload GM configuration file\n"); + } + else if (strcmp (command, "search") == 0) + { + printf ("search <expression>\n"); + printf (" Cherche des comptes.\n"); + printf (" Affiche les comptes dont les noms correspondent.\n"); +// printf("search -r/-e/--expr/--regex <expression>\n"); +// printf(" Cherche des comptes par expression regulière.\n"); +// printf(" Affiche les comptes dont les noms correspondent.\n"); + } + else if (strcmp (command, "sex") == 0) + { + printf ("sex <nomcompte> <sexe>\n"); + printf (" Modifie le sexe d'un compte.\n"); + printf (" <exemple> sex testname Male\n"); + } + else if (strcmp (command, "state") == 0) + { + printf ("state <nomcompte> <nouveaustatut> <message_erreur_7>\n"); + printf (" Change le statut d'un compte.\n"); + printf + (" 'nouveaustatut': Le statut est le même que celui du packet 0x006a + 1.\n"); + printf (" les possibilités sont:\n"); + printf (" 0 = Compte ok\n"); + printf (" 1 = Unregistered ID\n"); + printf (" 2 = Incorrect Password\n"); + printf (" 3 = This ID is expired\n"); + printf (" 4 = Rejected from Server\n"); + printf + (" 5 = You have been blocked by the GM Team\n"); + printf + (" 6 = Your Game's EXE file is not the latest version\n"); + printf + (" 7 = You are Prohibited to log in until...\n"); + printf + (" 8 = Server is jammed due to over populated\n"); + printf (" 9 = No MSG\n"); + printf (" 100 = This ID has been totally erased\n"); + printf + (" all other values are 'No MSG', then use state 9 please.\n"); + printf (" 'message_erreur_7': message du code erreur 6 =\n"); + printf + (" = Your are Prohibited to log in until... (packet 0x006a)\n"); + } + else if (strcmp (command, "timeadd") == 0) + { + printf ("timeadd <nomcompte> <modificateur>\n"); + printf + (" Ajoute/soustrait du temps à la limite de validité d'un compte.\n"); + printf (" Le modificateur est composé comme suit:\n"); + printf (" Valeur modificatrice (-1, 1, +1, etc...)\n"); + printf (" Elément modifié:\n"); + printf (" a ou y: année\n"); + printf (" m: mois\n"); + printf (" j ou d: jour\n"); + printf (" h: heure\n"); + printf (" mn: minute\n"); + printf (" s: seconde\n"); + printf (" <exemple> timeadd testname +1m-2mn1s-6a\n"); + printf + (" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + printf + ("NOTE: Vous ne pouvez pas modifier une limite de validité illimitée. Si vous\n"); + printf + (" désirez le faire, c'est que vous voulez probablement créer un limite de\n"); + printf + (" validité limitée. Donc, en premier, fixé une limite de valitidé.\n"); + } + else if (strcmp (command, "timeadd") == 0) + { + printf ("timeset <nomcompte> aaaa/mm/jj [hh:mm:ss]\n"); + printf (" Change la limite de validité d'un compte.\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + printf ("timeset <nomcompte> 0\n"); + printf + (" Donne une limite de validité illimitée (0 = illimitée).\n"); + } + else if (strcmp (command, "unban") == 0) + { + printf ("unban/unbanish <nom compte>\n"); + printf (" Ote le banissement d'un compte.\n"); + printf + (" La commande est l'équivalent de banset <nom_compte> 0.\n"); + } + else if (strcmp (command, "unblock") == 0) + { + printf ("unblock <nom compte>\n"); + printf (" Place le status d'un compte à 0 (Compte ok).\n"); + printf + (" La commande est l'équivalent de state <nom_compte> 0.\n"); + } + else if (strcmp (command, "version") == 0) + { + printf ("version\n"); + printf (" Affiche la version du login-serveur.\n"); + } + else if (strcmp (command, "who") == 0) + { + printf ("who <nom compte>\n"); + printf (" Affiche les informations sur un compte.\n"); +// quit + } + else if (strcmp (command, "quit") == 0 || + strcmp (command, "exit") == 0 || + strcmp (command, "end") == 0) + { + printf ("quit/end/exit\n"); + printf (" Fin du programme d'administration.\n"); +// unknown command + } + else + { + if (strlen (command) > 0) + printf + ("Commande inconnue [%s] pour l'aide. Affichage de toutes les commandes.\n", + command); + printf + (" aide/help/? -- Affiche cet aide\n"); + printf + (" aide/help/? [commande] -- Affiche l'aide de la commande\n"); + printf + (" add <nomcompte> <sexe> <motdepasse> -- Crée un compte (sans email)\n"); + printf + (" ban/banish aaaa/mm/jj hh:mm:ss <nom compte> -- Fixe la date finale de banismnt\n"); + printf + (" banadd/ba <nomcompte> <modificateur> -- Ajout/soustrait du temps à la\n"); + printf + (" exemple: ba moncompte +1m-2mn1s-2y date finale de banissement\n"); + printf + (" banset/bs <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la date fin de banisemnt\n"); + printf + (" banset/bs <nomcompte> 0 -- Dé-banis un compte.\n"); + printf + (" block <nom compte> -- Mets le status d'un compte à 5 (blocked by the GM Team)\n"); + printf + (" check <nomcompte> <motdepasse> -- Vérifie un mot de passe d'un compte\n"); + printf + (" create <nomcompte> <sexe> <email> <motdepasse> -- Crée un compte (avec email)\n"); + printf + (" del <nom compte> -- Supprime un compte\n"); + printf + (" email <nomcompte> <email> -- Modifie l'e-mail d'un compte\n"); + printf + (" getcount -- Donne le nb de joueurs en ligne\n"); + printf + (" gm <nomcompte> [Niveau_GM] -- Modifie le niveau de GM d'un compte\n"); + printf + (" id <nom compte> -- Donne l'id d'un compte\n"); + printf + (" info <idcompte> -- Affiche les infos sur un compte\n"); + printf + (" kami <message> -- Envoi un message général (en jaune)\n"); + printf + (" kamib <message> -- Envoi un message général (en bleu)\n"); + printf + (" language <langue> -- Change la langue d'affichage.\n"); + printf + (" list/ls [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf + (" listBan/lsBan [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf + (" avec un statut ou bannis\n"); + printf + (" listGM/lsGM [Premier_id [Dernier_id] ] -- Affiche une liste de comptes GM\n"); + printf + (" listOK/lsOK [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf + (" sans status et non bannis\n"); + printf + (" memo <nomcompte> <memo> -- Modifie le memo d'un compte\n"); + printf + (" name <idcompte> -- Donne le nom d'un compte\n"); + printf + (" passwd <nomcompte> <nouveaumotdepasse> -- Change le mot de passe d'un compte\n"); + printf + (" quit/end/exit -- Fin du programme d'administation\n"); + printf + (" reloadGM -- Recharger le fichier de config des GM\n"); + printf + (" search <expression> -- Cherche des comptes\n"); +// printf(" search -e/-r/--expr/--regex <expression> -- Cherche des comptes par REGEX\n"); + printf + (" sex <nomcompte> <sexe> -- Modifie le sexe d'un compte\n"); + printf + (" state <nomcompte> <nouveaustatut> <messageerr7> -- Change le statut d'1 compte\n"); + printf + (" timeadd/ta <nomcompte> <modificateur> -- Ajout/soustrait du temps à la\n"); + printf + (" exemple: ta moncompte +1m-2mn1s-2y limite de validité\n"); + printf + (" timeset/ts <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la limite de validité\n"); + printf + (" timeset/ts <nomcompte> 0 -- limite de validité = illimitée\n"); + printf + (" unban/unbanish <nom compte> -- Ote le banissement d'un compte\n"); + printf + (" unblock <nom compte> -- Mets le status d'un compte à 0 (Compte ok)\n"); + printf + (" version -- Donne la version du login-serveur\n"); + printf + (" who <nom compte> -- Affiche les infos sur un compte\n"); + printf + (" Note: Pour les noms de compte avec des espaces, tapez \"<nom compte>\" (ou ').\n"); + } + } + else + { + if (strcmp (command, "aide") == 0) + { + printf ("aide/help/?\n"); + printf (" Display the description of the commands\n"); + printf ("aide/help/? [command]\n"); + printf (" Display the description of the specified command\n"); + } + else if (strcmp (command, "help") == 0) + { + printf ("aide/help/?\n"); + printf (" Display the description of the commands\n"); + printf ("aide/help/? [command]\n"); + printf (" Display the description of the specified command\n"); +// general commands + } + else if (strcmp (command, "add") == 0) + { + printf ("add <account_name> <sex> <password>\n"); + printf + (" Create an account with the default email (a@a.com).\n"); + printf + (" Concerning the sex, only the first letter is used (F or M).\n"); + printf + (" The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail.\n"); + printf (" When the password is omitted,\n"); + printf + (" the input is done without displaying of the pressed keys.\n"); + printf (" <example> add testname Male testpass\n"); + } + else if (strcmp (command, "ban") == 0) + { + printf ("ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); + printf + (" Changes the final date of a banishment of an account.\n"); + printf (" Like banset, but <account name> is at end.\n"); + } + else if (strcmp (command, "banadd") == 0) + { + printf ("banadd <account_name> <modifier>\n"); + printf + (" Adds or substracts time from the final date of a banishment of an account.\n"); + printf (" Modifier is done as follows:\n"); + printf (" Adjustment value (-1, 1, +1, etc...)\n"); + printf (" Modified element:\n"); + printf (" a or y: year\n"); + printf (" m: month\n"); + printf (" j or d: day\n"); + printf (" h: hour\n"); + printf (" mn: minute\n"); + printf (" s: second\n"); + printf (" <example> banadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + printf + ("NOTE: If you modify the final date of a non-banished account,\n"); + printf + (" you fix the final date to (actual time +- adjustments)\n"); + } + else if (strcmp (command, "banset") == 0) + { + printf ("banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf + (" Changes the final date of a banishment of an account.\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + printf ("banset <account_name> 0\n"); + printf (" Set a non-banished account (0 = unbanished).\n"); + } + else if (strcmp (command, "block") == 0) + { + printf ("block <account name>\n"); + printf + (" Set state 5 (You have been blocked by the GM Team) to an account.\n"); + printf (" This command works like state <account_name> 5.\n"); + } + else if (strcmp (command, "check") == 0) + { + printf ("check <account_name> <password>\n"); + printf (" Check the validity of a password for an account.\n"); + printf (" NOTE: Server will never sends back a password.\n"); + printf + (" It's the only method you have to know if a password is correct.\n"); + printf + (" The other method is to have a ('physical') access to the accounts file.\n"); + } + else if (strcmp (command, "create") == 0) + { + printf ("create <account_name> <sex> <email> <password>\n"); + printf (" Like the 'add' command, but with e-mail moreover.\n"); + printf + (" <example> create testname Male my@mail.com testpass\n"); + } + else if (strcmp (command, "delete") == 0) + { + printf ("del <account name>\n"); + printf (" Remove an account.\n"); + printf + (" This order requires confirmation. After confirmation, the account is deleted.\n"); + } + else if (strcmp (command, "email") == 0) + { + printf ("email <account_name> <email>\n"); + printf (" Modify the e-mail of an account.\n"); + } + else if (strcmp (command, "getcount") == 0) + { + printf ("getcount\n"); + printf + (" Give the number of players online on all char-servers.\n"); + } + else if (strcmp (command, "gm") == 0) + { + printf ("gm <account_name> [GM_level]\n"); + printf (" Modify the GM level of an account.\n"); + printf (" Default value remove GM level (GM level = 0).\n"); + printf (" <example> gm testname 80\n"); + } + else if (strcmp (command, "id") == 0) + { + printf ("id <account name>\n"); + printf (" Give the id of an account.\n"); + } + else if (strcmp (command, "info") == 0) + { + printf ("info <account_id>\n"); + printf (" Display complete information of an account.\n"); + } + else if (strcmp (command, "kami") == 0) + { + printf ("kami <message>\n"); + printf + (" Sends a broadcast message on all map-server (in yellow).\n"); + } + else if (strcmp (command, "kamib") == 0) + { + printf ("kamib <message>\n"); + printf + (" Sends a broadcast message on all map-server (in blue).\n"); + } + else if (strcmp (command, "language") == 0) + { + printf ("language <language>\n"); + printf (" Change the language of displaying.\n"); + printf (" Possible languages: Français or English.\n"); + } + else if (strcmp (command, "list") == 0) + { + printf ("list/ls [start_id [end_id]]\n"); + printf (" Display a list of accounts.\n"); + printf + (" 'start_id', 'end_id': indicate end and start identifiers.\n"); + printf + (" Research by name is not possible with this command.\n"); + printf (" <example> list 10 9999999\n"); + } + else if (strcmp (command, "itemfrob") == 0) + { + printf ("itemfrob <source-id> <dest-id>\n"); + printf (" Translates item IDs for all accounts.\n"); + printf + (" Any items matching the source item ID will be mapped to the dest-id.\n"); + printf (" <example> itemfrob 500 700\n"); + } + else if (strcmp (command, "listban") == 0) + { + printf ("listBan/lsBan [start_id [end_id]]\n"); + printf + (" Like list/ls, but only for accounts with state or banished.\n"); + } + else if (strcmp (command, "listgm") == 0) + { + printf ("listGM/lsGM [start_id [end_id]]\n"); + printf (" Like list/ls, but only for GM accounts.\n"); + } + else if (strcmp (command, "listok") == 0) + { + printf ("listOK/lsOK [start_id [end_id]]\n"); + printf + (" Like list/ls, but only for accounts without state and not banished.\n"); + } + else if (strcmp (command, "memo") == 0) + { + printf ("memo <account_name> <memo>\n"); + printf (" Modify the memo of an account.\n"); + printf + (" 'memo': it can have until 253 characters (with spaces or not).\n"); + } + else if (strcmp (command, "name") == 0) + { + printf ("name <account_id>\n"); + printf (" Give the name of an account.\n"); + } + else if (strcmp (command, "password") == 0) + { + printf ("passwd <account_name> <new_password>\n"); + printf (" Change the password of an account.\n"); + printf (" When new password is omitted,\n"); + printf + (" the input is done without displaying of the pressed keys.\n"); + } + else if (strcmp (command, "reloadgm") == 0) + { + printf ("reloadGM\n"); + printf (" Reload GM configuration file\n"); + } + else if (strcmp (command, "search") == 0) + { + printf ("search <expression>\n"); + printf (" Seek accounts.\n"); + printf (" Displays the accounts whose names correspond.\n"); +// printf("search -r/-e/--expr/--regex <expression>\n"); +// printf(" Seek accounts by regular expression.\n"); +// printf(" Displays the accounts whose names correspond.\n"); + } + else if (strcmp (command, "sex") == 0) + { + printf ("sex <account_name> <sex>\n"); + printf (" Modify the sex of an account.\n"); + printf (" <example> sex testname Male\n"); + } + else if (strcmp (command, "state") == 0) + { + printf ("state <account_name> <new_state> <error_message_#7>\n"); + printf (" Change the state of an account.\n"); + printf + (" 'new_state': state is the state of the packet 0x006a + 1.\n"); + printf (" The possibilities are:\n"); + printf (" 0 = Account ok\n"); + printf (" 1 = Unregistered ID\n"); + printf (" 2 = Incorrect Password\n"); + printf (" 3 = This ID is expired\n"); + printf (" 4 = Rejected from Server\n"); + printf + (" 5 = You have been blocked by the GM Team\n"); + printf + (" 6 = Your Game's EXE file is not the latest version\n"); + printf + (" 7 = You are Prohibited to log in until...\n"); + printf + (" 8 = Server is jammed due to over populated\n"); + printf (" 9 = No MSG\n"); + printf (" 100 = This ID has been totally erased\n"); + printf + (" all other values are 'No MSG', then use state 9 please.\n"); + printf (" 'error_message_#7': message of the code error 6\n"); + printf + (" = Your are Prohibited to log in until... (packet 0x006a)\n"); + } + else if (strcmp (command, "timeadd") == 0) + { + printf ("timeadd <account_name> <modifier>\n"); + printf + (" Adds or substracts time from the validity limit of an account.\n"); + printf (" Modifier is done as follows:\n"); + printf (" Adjustment value (-1, 1, +1, etc...)\n"); + printf (" Modified element:\n"); + printf (" a or y: year\n"); + printf (" m: month\n"); + printf (" j or d: day\n"); + printf (" h: hour\n"); + printf (" mn: minute\n"); + printf (" s: second\n"); + printf (" <example> timeadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + printf ("NOTE: You can not modify a unlimited validity limit.\n"); + printf + (" If you want modify it, you want probably create a limited validity limit.\n"); + printf + (" So, at first, you must set the validity limit to a date/time.\n"); + } + else if (strcmp (command, "timeadd") == 0) + { + printf ("timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf (" Changes the validity limit of an account.\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + printf ("timeset <account_name> 0\n"); + printf (" Gives an unlimited validity limit (0 = unlimited).\n"); + } + else if (strcmp (command, "unban") == 0) + { + printf ("unban/unbanish <account name>\n"); + printf (" Remove the banishment of an account.\n"); + printf (" This command works like banset <account_name> 0.\n"); + } + else if (strcmp (command, "unblock") == 0) + { + printf ("unblock <account name>\n"); + printf (" Set state 0 (Account ok) to an account.\n"); + printf (" This command works like state <account_name> 0.\n"); + } + else if (strcmp (command, "version") == 0) + { + printf ("version\n"); + printf (" Display the version of the login-server.\n"); + } + else if (strcmp (command, "who") == 0) + { + printf ("who <account name>\n"); + printf (" Displays complete information of an account.\n"); +// quit + } + else if (strcmp (command, "quit") == 0 || + strcmp (command, "exit") == 0 || + strcmp (command, "end") == 0) + { + printf ("quit/end/exit\n"); + printf (" End of the program of administration.\n"); +// unknown command + } + else + { + if (strlen (command) > 0) + printf + ("Unknown command [%s] for help. Displaying of all commands.\n", + command); + printf + (" aide/help/? -- Display this help\n"); + printf + (" aide/help/? [command] -- Display the help of the command\n"); + printf + (" add <account_name> <sex> <password> -- Create an account with default email\n"); + printf + (" ban/banish yyyy/mm/dd hh:mm:ss <account name> -- Change final date of a ban\n"); + printf + (" banadd/ba <account_name> <modifier> -- Add or substract time from the final\n"); + printf + (" example: ba apple +1m-2mn1s-2y date of a banishment of an account\n"); + printf + (" banset/bs <account_name> yyyy/mm/dd [hh:mm:ss] -- Change final date of a ban\n"); + printf + (" banset/bs <account_name> 0 -- Un-banish an account\n"); + printf + (" block <account name> -- Set state 5 (blocked by the GM Team) to an account\n"); + printf + (" check <account_name> <password> -- Check the validity of a password\n"); + printf + (" create <account_name> <sex> <email> <passwrd> -- Create an account with email\n"); + printf + (" del <account name> -- Remove an account\n"); + printf + (" email <account_name> <email> -- Modify an email of an account\n"); + printf + (" getcount -- Give the number of players online\n"); + printf + (" gm <account_name> [GM_level] -- Modify the GM level of an account\n"); + printf + (" id <account name> -- Give the id of an account\n"); + printf + (" info <account_id> -- Display all information of an account\n"); + printf + (" itemfrob <source-id> <dest-id> -- Map all items from one item ID to another\n"); + printf + (" kami <message> -- Sends a broadcast message (in yellow)\n"); + printf + (" kamib <message> -- Sends a broadcast message (in blue)\n"); + printf + (" language <language> -- Change the language of displaying.\n"); + printf + (" list/ls [First_id [Last_id]] -- Display a list of accounts\n"); + printf + (" listBan/lsBan [First_id [Last_id] ] -- Display a list of accounts\n"); + printf + (" with state or banished\n"); + printf + (" listGM/lsGM [First_id [Last_id]] -- Display a list of GM accounts\n"); + printf + (" listOK/lsOK [First_id [Last_id] ] -- Display a list of accounts\n"); + printf + (" without state and not banished\n"); + printf + (" memo <account_name> <memo> -- Modify the memo of an account\n"); + printf + (" name <account_id> -- Give the name of an account\n"); + printf + (" passwd <account_name> <new_password> -- Change the password of an account\n"); + printf + (" quit/end/exit -- End of the program of administation\n"); + printf + (" reloadGM -- Reload GM configuration file\n"); + printf + (" search <expression> -- Seek accounts\n"); +// printf(" search -e/-r/--expr/--regex <expressn> -- Seek accounts by regular-expression\n"); + printf + (" sex <nomcompte> <sexe> -- Modify the sex of an account\n"); + printf + (" state <account_name> <new_state> <error_message_#7> -- Change the state\n"); + printf + (" timeadd/ta <account_name> <modifier> -- Add or substract time from the\n"); + printf + (" example: ta apple +1m-2mn1s-2y validity limit of an account\n"); + printf + (" timeset/ts <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"); + printf + (" timeset/ts <account_name> 0 -- Give a unlimited validity limit\n"); + printf + (" unban/unbanish <account name> -- Remove the banishment of an account\n"); + printf + (" unblock <account name> -- Set state 0 (Account ok) to an account\n"); + printf + (" version -- Gives the version of the login-server\n"); + printf + (" who <account name> -- Display all information of an account\n"); + printf + (" who <account name> -- Display all information of an account\n"); + printf + (" Note: To use spaces in an account name, type \"<account name>\" (or ').\n"); + } + } +} + +//----------------------------- +// Sub-function: add an account +//----------------------------- +int addaccount (char *param, int emailflag) +{ + char name[1023], sex[1023], email[1023], password[1023]; +// int i; + + memset (name, '\0', sizeof (name)); + memset (sex, '\0', sizeof (sex)); + memset (email, '\0', sizeof (email)); + memset (password, '\0', sizeof (password)); + + if (emailflag == 0) + { // add command + if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, sex, password) < 2 && // password can be void + sscanf (param, "'%[^']' %s %[^\r\n]", name, sex, password) < 2 && // password can be void + sscanf (param, "%s %s %[^\r\n]", name, sex, password) < 2) + { // password can be void + if (defaultlanguage == 'F') + { + printf + ("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); + printf ("<exemple> add nomtest Male motdepassetest\n"); + ladmin_log + ("Nombre incorrect de paramètres pour créer un compte (commande 'add').\n"); + } + else + { + printf + ("Please input an account name, a sex and a password.\n"); + printf ("<example> add testname Male testpass\n"); + ladmin_log + ("Incomplete parameters to create an account ('add' command).\n"); + } + return 136; + } + strcpy (email, "a@a.com"); // default email + } + else + { // 1: create command + if (sscanf (param, "\"%[^\"]\" %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void + sscanf (param, "'%[^']' %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void + sscanf (param, "%s %s %s %[^\r\n]", name, sex, email, + password) < 3) + { // password can be void + if (defaultlanguage == 'F') + { + printf + ("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); + printf + ("<exemple> create nomtest Male mo@mail.com motdepassetest\n"); + ladmin_log + ("Nombre incorrect de paramètres pour créer un compte (commande 'create').\n"); + } + else + { + printf + ("Please input an account name, a sex and a password.\n"); + printf + ("<example> create testname Male my@mail.com testpass\n"); + ladmin_log + ("Incomplete parameters to create an account ('create' command).\n"); + } + return 136; + } + } + if (verify_accountname (name) == 0) + { + return 102; + } + +/* for(i = 0; name[i]; i++) { + if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_", name[i]) == NULL) { + if (defaultlanguage == 'F') { + printf("Caractère interdit (%c) trouvé dans le nom du compte (%d%s caractère).\n", name[i], i+1, makeordinal(i+1)); + ladmin_log("Caractère interdit (%c) trouvé dans le nom du compte (%d%s caractère).\n", name[i], i+1, makeordinal(i+1)); + } else { + printf("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); + ladmin_log("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); + } + return 101; + } + }*/ + + sex[0] = toupper (sex[0]); + if (strchr ("MF", sex[0]) == NULL) + { + if (defaultlanguage == 'F') + { + printf ("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); + ladmin_log ("Sexe incorrect [%s]. Entrez M ou F svp.\n", + sex); + } + else + { + printf ("Illegal gender [%s]. Please input M or F.\n", sex); + ladmin_log ("Illegal gender [%s]. Please input M or F.\n", + sex); + } + return 103; + } + + if (strlen (email) < 3) + { + if (defaultlanguage == 'F') + { + printf ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", + email); + ladmin_log + ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", + email); + } + else + { + printf ("Email is too short [%s]. Please input a valid e-mail.\n", + email); + ladmin_log + ("Email is too short [%s]. Please input a valid e-mail.\n", + email); + } + return 109; + } + if (strlen (email) > 39) + { + if (defaultlanguage == 'F') + { + printf + ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", + email); + ladmin_log + ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", + email); + } + else + { + printf + ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", + email); + ladmin_log + ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", + email); + } + return 109; + } + if (e_mail_check (email) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", + email); + ladmin_log ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", + email); + } + else + { + printf ("Invalid email [%s]. Please input a valid e-mail.\n", + email); + ladmin_log ("Invalid email [%s]. Please input a valid e-mail.\n", + email); + } + return 109; + } + + if (strlen (password) == 0) + { + if (typepasswd (password) == 0) + return 108; + } + if (verify_password (password) == 0) + return 104; + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour créer un compte.\n"); + } + else + { + ladmin_log ("Request to login-server to create an account.\n"); + } + + WFIFOW (login_fd, 0) = 0x7930; + memcpy (WFIFOP (login_fd, 2), name, 24); + memcpy (WFIFOP (login_fd, 26), password, 24); + WFIFOB (login_fd, 50) = sex[0]; + memcpy (WFIFOP (login_fd, 51), email, 40); + WFIFOSET (login_fd, 91); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------------------------------------------- +// Sub-function: Add/substract time to the final date of a banishment of an account +//--------------------------------------------------------------------------------- +int banaddaccount (char *param) +{ + char name[1023], modif[1023]; + int year, month, day, hour, minute, second; + char *p_modif; + int value, i; + + memset (name, '\0', sizeof (name)); + memset (modif, '\0', sizeof (modif)); + year = month = day = hour = minute = second = 0; + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && + sscanf (param, "'%[^']' %[^\r\n]", name, modif) < 2 && + sscanf (param, "%s %[^\r\n]", name, modif) < 2) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un modificateur svp.\n"); + printf (" <exemple> banadd nomtest +1m-2mn1s-6y\n"); + printf + (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour modifier la fin de ban d'un compte (commande 'banadd').\n"); + } + else + { + printf ("Please input an account name and a modifier.\n"); + printf (" <example>: banadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + ladmin_log + ("Incomplete parameters to modify the ban date/time of an account ('banadd' command).\n"); + } + return 136; + } + if (verify_accountname (name) == 0) + { + return 102; + } + + // lowercase for modif + for (i = 0; modif[i]; i++) + modif[i] = tolower (modif[i]); + p_modif = modif; + while (strlen (p_modif) > 0) + { + value = atoi (p_modif); + if (value == 0) + { + p_modif++; + } + else + { + if (p_modif[0] == '-' || p_modif[0] == '+') + p_modif++; + while (strlen (p_modif) > 0 && p_modif[0] >= '0' + && p_modif[0] <= '9') + { + p_modif++; + } + if (p_modif[0] == 's') + { + second = value; + p_modif++; + } + else if (p_modif[0] == 'm' && p_modif[1] == 'n') + { + minute = value; + p_modif += 2; + } + else if (p_modif[0] == 'h') + { + hour = value; + p_modif++; + } + else if (p_modif[0] == 'd' || p_modif[0] == 'j') + { + day = value; + p_modif += 2; + } + else if (p_modif[0] == 'm') + { + month = value; + p_modif++; + } + else if (p_modif[0] == 'y' || p_modif[0] == 'a') + { + year = value; + p_modif++; + } + else + { + p_modif++; + } + } + } + + if (defaultlanguage == 'F') + { + printf (" année: %d\n", year); + printf (" mois: %d\n", month); + printf (" jour: %d\n", day); + printf (" heure: %d\n", hour); + printf (" minute: %d\n", minute); + printf (" seconde: %d\n", second); + } + else + { + printf (" year: %d\n", year); + printf (" month: %d\n", month); + printf (" day: %d\n", day); + printf (" hour: %d\n", hour); + printf (" minute: %d\n", minute); + printf (" second: %d\n", second); + } + + if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 + && second == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Vous devez entrer un ajustement avec cette commande, svp:\n"); + printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf (" Element modifié:\n"); + printf (" a ou y: année\n"); + printf (" m: mois\n"); + printf (" j ou d: jour\n"); + printf (" h: heure\n"); + printf (" mn: minute\n"); + printf (" s: seconde\n"); + printf (" <exemple> banadd nomtest +1m-2mn1s-6y\n"); + printf + (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + ladmin_log + ("Aucun ajustement n'est pas un ajustement (commande 'banadd').\n"); + } + else + { + printf ("Please give an adjustment with this command:\n"); + printf (" Adjustment value (-1, 1, +1, etc...)\n"); + printf (" Modified element:\n"); + printf (" a or y: year\n"); + printf (" m: month\n"); + printf (" j or d: day\n"); + printf (" h: hour\n"); + printf (" mn: minute\n"); + printf (" s: second\n"); + printf (" <example> banadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + ladmin_log + ("No adjustment isn't an adjustment ('banadd' command).\n"); + } + return 137; + } + if (year > 127 || year < -127) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement d'années correct (de -127 à 127), svp.\n"); + ladmin_log + ("Ajustement de l'année hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the years (from -127 to 127).\n"); + ladmin_log + ("Abnormal adjustement for the year ('banadd' command).\n"); + } + return 137; + } + if (month > 255 || month < -255) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de mois correct (de -255 à 255), svp.\n"); + ladmin_log ("Ajustement du mois hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the months (from -255 to 255).\n"); + ladmin_log + ("Abnormal adjustement for the month ('banadd' command).\n"); + } + return 137; + } + if (day > 32767 || day < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de jours correct (de -32767 à 32767), svp.\n"); + ladmin_log ("Ajustement des jours hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the days (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the days ('banadd' command).\n"); + } + return 137; + } + if (hour > 32767 || hour < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement d'heures correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des heures hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the hours ('banadd' command).\n"); + } + return 137; + } + if (minute > 32767 || minute < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de minutes correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des minutes hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the minutes ('banadd' command).\n"); + } + return 137; + } + if (second > 32767 || second < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de secondes correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des secondes hors norme (commande 'banadd').\n"); + } + else + { + printf + ("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the seconds ('banadd' command).\n"); + } + return 137; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour modifier la date d'un bannissement.\n"); + } + else + { + ladmin_log ("Request to login-server to modify a ban date/time.\n"); + } + + WFIFOW (login_fd, 0) = 0x794c; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOW (login_fd, 26) = (short) year; + WFIFOW (login_fd, 28) = (short) month; + WFIFOW (login_fd, 30) = (short) day; + WFIFOW (login_fd, 32) = (short) hour; + WFIFOW (login_fd, 34) = (short) minute; + WFIFOW (login_fd, 36) = (short) second; + WFIFOSET (login_fd, 38); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------------------------------- +// Sub-function of sub-function banaccount, unbanaccount or bansetaccount +// Set the final date of a banishment of an account +//----------------------------------------------------------------------- +int bansetaccountsub (char *name, char *date, char *time) +{ + int year, month, day, hour, minute, second; + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + struct tm *tmtime; + + year = month = day = hour = minute = second = 0; + ban_until_time = 0; + tmtime = localtime (&ban_until_time); // initialize + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (atoi (date) != 0 && + ((sscanf (date, "%d/%d/%d", &year, &month, &day) < 3 && + sscanf (date, "%d-%d-%d", &year, &month, &day) < 3 && + sscanf (date, "%d.%d.%d", &year, &month, &day) < 3) || + sscanf (time, "%d:%d:%d", &hour, &minute, &second) < 3)) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); + printf + ("Vous pouvez aussi mettre 0 à la place si vous utilisez la commande 'banset'.\n"); + ladmin_log + ("Format incorrect pour la date/heure (commande'banset' ou 'ban').\n"); + } + else + { + printf + ("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); + printf + ("You can imput 0 instead of if you use 'banset' command.\n"); + ladmin_log + ("Invalid format for the date/time ('banset' or 'ban' command).\n"); + } + return 102; + } + + if (atoi (date) == 0) + { + ban_until_time = 0; + } + else + { + if (year < 70) + { + year = year + 100; + } + if (year >= 1900) + { + year = year - 1900; + } + if (month < 1 || month > 12) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un mois correct svp (entre 1 et 12).\n"); + ladmin_log + ("Mois incorrect pour la date (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for the month (from 1 to 12).\n"); + ladmin_log + ("Invalid month for the date ('banset' or 'ban' command).\n"); + } + return 102; + } + month = month - 1; + if (day < 1 || day > 31) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un jour correct svp (entre 1 et 31).\n"); + ladmin_log + ("Jour incorrect pour la date (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for the day (from 1 to 31).\n"); + ladmin_log + ("Invalid day for the date ('banset' or 'ban' command).\n"); + } + return 102; + } + if (((month == 3 || month == 5 || month == 8 || month == 10) + && day > 30) || (month == 1 && day > 29)) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un jour correct en fonction du mois (%d) svp.\n", + month); + ladmin_log + ("Jour incorrect pour ce mois correspondant (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for a day of this month (%d).\n", + month); + ladmin_log + ("Invalid day for this month ('banset' or 'ban' command).\n"); + } + return 102; + } + if (hour < 0 || hour > 23) + { + if (defaultlanguage == 'F') + { + printf ("Entrez une heure correcte svp (entre 0 et 23).\n"); + ladmin_log + ("Heure incorrecte pour l'heure (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for the hour (from 0 to 23).\n"); + ladmin_log + ("Invalid hour for the time ('banset' or 'ban' command).\n"); + } + return 102; + } + if (minute < 0 || minute > 59) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez des minutes correctes svp (entre 0 et 59).\n"); + ladmin_log + ("Minute incorrecte pour l'heure (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for the minutes (from 0 to 59).\n"); + ladmin_log + ("Invalid minute for the time ('banset' or 'ban' command).\n"); + } + return 102; + } + if (second < 0 || second > 59) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez des secondes correctes svp (entre 0 et 59).\n"); + ladmin_log + ("Seconde incorrecte pour l'heure (command 'banset' ou 'ban').\n"); + } + else + { + printf + ("Please give a correct value for the seconds (from 0 to 59).\n"); + ladmin_log + ("Invalid second for the time ('banset' or 'ban' command).\n"); + } + return 102; + } + tmtime->tm_year = year; + tmtime->tm_mon = month; + tmtime->tm_mday = day; + tmtime->tm_hour = hour; + tmtime->tm_min = minute; + tmtime->tm_sec = second; + tmtime->tm_isdst = -1; // -1: no winter/summer time modification + ban_until_time = timegm (tmtime); + if (ban_until_time == -1) + { + if (defaultlanguage == 'F') + { + printf ("Date incorrecte.\n"); + printf + ("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); + printf + ("Vous pouvez aussi mettre 0 à la place si vous utilisez la commande 'banset'.\n"); + ladmin_log ("Date incorrecte. (command 'banset' ou 'ban').\n"); + } + else + { + printf ("Invalid date.\n"); + printf + ("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); + printf + ("You can imput 0 instead of if you use 'banset' command.\n"); + ladmin_log ("Invalid date. ('banset' or 'ban' command).\n"); + } + return 102; + } + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour fixer un ban.\n"); + } + else + { + ladmin_log ("Request to login-server to set a ban.\n"); + } + + WFIFOW (login_fd, 0) = 0x794a; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOL (login_fd, 26) = (int) ban_until_time; + WFIFOSET (login_fd, 30); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------------------------------- +// Sub-function: Set the final date of a banishment of an account (ban) +//--------------------------------------------------------------------- +int banaccount (char *param) +{ + char name[1023], date[1023], time[1023]; + + memset (name, '\0', sizeof (name)); + memset (date, '\0', sizeof (date)); + memset (time, '\0', sizeof (time)); + + if (sscanf (param, "%s %s \"%[^\"]\"", date, time, name) < 3 && + sscanf (param, "%s %s '%[^']'", date, time, name) < 3 && + sscanf (param, "%s %s %[^\r\n]", date, time, name) < 3) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte, une date et une heure svp.\n"); + printf + ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); + printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); + printf + (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); + printf (" unban/unbanish <nom du compte>\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour fixer un ban (commande 'banset' ou 'ban').\n"); + } + else + { + printf ("Please input an account name, a date and a hour.\n"); + printf + ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf + (" banset <account_name> 0 (0 = un-banished)\n"); + printf + (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); + printf (" unban/unbanish <account name>\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); + } + return 136; + } + + return bansetaccountsub (name, date, time); +} + +//------------------------------------------------------------------------ +// Sub-function: Set the final date of a banishment of an account (banset) +//------------------------------------------------------------------------ +int bansetaccount (char *param) +{ + char name[1023], date[1023], time[1023]; + + memset (name, '\0', sizeof (name)); + memset (date, '\0', sizeof (date)); + memset (time, '\0', sizeof (time)); + + if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf (param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf (param, "%s %s %[^\r\n]", name, date, time) < 2) + { // if date = 0, time can be void + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte, une date et une heure svp.\n"); + printf + ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); + printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); + printf + (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); + printf (" unban/unbanish <nom du compte>\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour fixer un ban (commande 'banset' ou 'ban').\n"); + } + else + { + printf ("Please input an account name, a date and a hour.\n"); + printf + ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf + (" banset <account_name> 0 (0 = un-banished)\n"); + printf + (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); + printf (" unban/unbanish <account name>\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); + } + return 136; + } + + if (time[0] == '\0') + strcpy (time, "23:59:59"); + + return bansetaccountsub (name, date, time); +} + +//------------------------------------------------- +// Sub-function: unbanishment of an account (unban) +//------------------------------------------------- +int unbanaccount (char *param) +{ + char name[1023]; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf + ("<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); + printf (" banset <nom_du_compte> 0 (0 = dé-bani)\n"); + printf + (" ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n"); + printf (" unban/unbanish <nom du compte>\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour fixer un ban (commande 'unban').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf + ("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf + (" banset <account_name> 0 (0 = un-banished)\n"); + printf + (" ban/banish yyyy/mm/dd hh:mm:ss <account name>\n"); + printf (" unban/unbanish <account name>\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Incomplete parameters to set a ban ('unban' command).\n"); + } + return 136; + } + + return bansetaccountsub (name, "0", ""); +} + +//--------------------------------------------------------- +// Sub-function: Asking to check the validity of a password +// (Note: never send back a password with login-server!! security of passwords) +//--------------------------------------------------------- +int checkaccount (char *param) +{ + char name[1023], password[1023]; + + memset (name, '\0', sizeof (name)); + memset (password, '\0', sizeof (password)); + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && // password can be void + sscanf (param, "'%[^']' %[^\r\n]", name, password) < 1 && // password can be void + sscanf (param, "%s %[^\r\n]", name, password) < 1) + { // password can be void + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemple> check testname motdepasse\n"); + ladmin_log + ("Nombre incorrect de paramètres pour tester le mot d'un passe d'un compte (commande 'check').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<example> check testname password\n"); + ladmin_log + ("Incomplete parameters to check the password of an account ('check' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (strlen (password) == 0) + { + if (typepasswd (password) == 0) + return 134; + } + if (verify_password (password) == 0) + return 131; + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour test un mot de passe.\n"); + } + else + { + ladmin_log ("Request to login-server to check a password.\n"); + } + + WFIFOW (login_fd, 0) = 0x793a; + memcpy (WFIFOP (login_fd, 2), name, 24); + memcpy (WFIFOP (login_fd, 26), password, 24); + WFIFOSET (login_fd, 50); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------ +// Sub-function: Asking for deletion of an account +//------------------------------------------------ +int delaccount (char *param) +{ + char name[1023]; + char letter; + char confirm[1023]; + int i; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemple> del nomtestasupprimer\n"); + ladmin_log + ("Aucun nom donné pour supprimer un compte (commande 'delete').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<example> del testnametodelete\n"); + ladmin_log + ("No name given to delete an account ('delete' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + memset (confirm, '\0', sizeof (confirm)); + while ((confirm[0] != 'o' || defaultlanguage != 'F') && confirm[0] != 'n' + && (confirm[0] != 'y' || defaultlanguage == 'F')) + { + if (defaultlanguage == 'F') + printf + ("\033[1;36m ** Etes-vous vraiment sûr de vouloir SUPPRIMER le compte [$userid]? (o/n) > \033[0m"); + else + printf + ("\033[1;36m ** Are you really sure to DELETE account [$userid]? (y/n) > \033[0m"); + fflush (stdout); + memset (confirm, '\0', sizeof (confirm)); + i = 0; + while ((letter = getchar ()) != '\n') + confirm[i++] = letter; + } + + if (confirm[0] == 'n') + { + if (defaultlanguage == 'F') + { + printf ("Suppression annulée.\n"); + ladmin_log + ("Suppression annulée par l'utilisateur (commande 'delete').\n"); + } + else + { + printf ("Deletion canceled.\n"); + ladmin_log ("Deletion canceled by user ('delete' command).\n"); + } + return 121; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour détruire un compte.\n"); + } + else + { + ladmin_log ("Request to login-server to delete an acount.\n"); + } + + WFIFOW (login_fd, 0) = 0x7932; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOSET (login_fd, 26); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------- +// Sub-function: Asking to modification of an account e-mail +//---------------------------------------------------------- +int changeemail (char *param) +{ + char name[1023], email[1023]; + + memset (name, '\0', sizeof (name)); + memset (email, '\0', sizeof (email)); + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, email) < 2 && + sscanf (param, "'%[^']' %[^\r\n]", name, email) < 2 && + sscanf (param, "%s %[^\r\n]", name, email) < 2) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et une email svp.\n"); + printf ("<exemple> email testname nouveauemail\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer l'email d'un compte (commande 'email').\n"); + } + else + { + printf ("Please input an account name and an email.\n"); + printf ("<example> email testname newemail\n"); + ladmin_log + ("Incomplete parameters to change the email of an account ('email' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (strlen (email) < 3) + { + if (defaultlanguage == 'F') + { + printf ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", + email); + ladmin_log + ("Email trop courte [%s]. Entrez une e-mail valide svp.\n", + email); + } + else + { + printf ("Email is too short [%s]. Please input a valid e-mail.\n", + email); + ladmin_log + ("Email is too short [%s]. Please input a valid e-mail.\n", + email); + } + return 109; + } + if (strlen (email) > 39) + { + if (defaultlanguage == 'F') + { + printf + ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", + email); + ladmin_log + ("Email trop longue [%s]. Entrez une e-mail de 39 caractères maximum svp.\n", + email); + } + else + { + printf + ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", + email); + ladmin_log + ("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", + email); + } + return 109; + } + if (e_mail_check (email) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", + email); + ladmin_log ("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", + email); + } + else + { + printf ("Invalid email [%s]. Please input a valid e-mail.\n", + email); + ladmin_log ("Invalid email [%s]. Please input a valid e-mail.\n", + email); + } + return 109; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer une email.\n"); + } + else + { + ladmin_log ("Request to login-server to change an email.\n"); + } + + WFIFOW (login_fd, 0) = 0x7940; + memcpy (WFIFOP (login_fd, 2), name, 24); + memcpy (WFIFOP (login_fd, 26), email, 40); + WFIFOSET (login_fd, 66); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------------- +// Sub-function: Asking of the number of online players +//----------------------------------------------------- +int getlogincount (void) +{ + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir le nombre de joueurs en jeu.\n"); + } + else + { + ladmin_log + ("Request to login-server to obtain the # of online players.\n"); + } + + WFIFOW (login_fd, 0) = 0x7938; + WFIFOSET (login_fd, 2); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------- +// Sub-function: Asking to modify the GM level of an account +//---------------------------------------------------------- +int changegmlevel (char *param) +{ + char name[1023]; + int GM_level; + + memset (name, '\0', sizeof (name)); + GM_level = 0; + + if (sscanf (param, "\"%[^\"]\" %d", name, &GM_level) < 1 && + sscanf (param, "'%[^']' %d", name, &GM_level) < 1 && + sscanf (param, "%s %d", name, &GM_level) < 1) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un niveau de GM svp.\n"); + printf ("<exemple> gm nomtest 80\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le Niveau de GM d'un compte (commande 'gm').\n"); + } + else + { + printf ("Please input an account name and a GM level.\n"); + printf ("<example> gm testname 80\n"); + ladmin_log + ("Incomplete parameters to change the GM level of an account ('gm' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (GM_level < 0 || GM_level > 99) + { + if (defaultlanguage == 'F') + { + printf + ("Niveau de GM incorrect [%d]. Entrez une valeur de 0 à 99 svp.\n", + GM_level); + ladmin_log + ("Niveau de GM incorrect [%d]. La valeur peut être de 0 à 99.\n", + GM_level); + } + else + { + printf + ("Illegal GM level [%d]. Please input a value from 0 to 99.\n", + GM_level); + ladmin_log + ("Illegal GM level [%d]. The value can be from 0 to 99.\n", + GM_level); + } + return 103; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer un niveau de GM.\n"); + } + else + { + ladmin_log ("Request to login-server to change a GM level.\n"); + } + + WFIFOW (login_fd, 0) = 0x793e; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOB (login_fd, 26) = GM_level; + WFIFOSET (login_fd, 27); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------- +// Sub-function: Asking to obtain an account id +//--------------------------------------------- +int idaccount (char *param) +{ + char name[1023]; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemple> id nomtest\n"); + ladmin_log + ("Aucun nom donné pour rechecher l'id d'un compte (commande 'id').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<example> id testname\n"); + ladmin_log + ("No name given to search an account id ('id' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour connaître l'id d'un compte.\n"); + } + else + { + ladmin_log ("Request to login-server to know an account id.\n"); + } + + WFIFOW (login_fd, 0) = 0x7944; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOSET (login_fd, 26); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------------------------- +// Sub-function: Asking to displaying information about an account (by its id) +//---------------------------------------------------------------------------- +int infoaccount (int account_id) +{ + if (account_id < 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un id ayant une valeur positive svp.\n"); + ladmin_log + ("Une valeur négative a été donné pour trouver le compte.\n"); + } + else + { + printf ("Please input a positive value for the id.\n"); + ladmin_log ("Negative value was given to found the account.\n"); + } + return 136; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir le information d'un compte (par l'id).\n"); + } + else + { + ladmin_log + ("Request to login-server to obtain information about an account (by its id).\n"); + } + + WFIFOW (login_fd, 0) = 0x7954; + WFIFOL (login_fd, 2) = account_id; + WFIFOSET (login_fd, 6); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------- +// Sub-function: Send a broadcast message +//--------------------------------------- +int sendbroadcast (short type, char *message) +{ + if (strlen (message) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un message svp.\n"); + if (type == 0) + { + printf ("<exemple> kami un message\n"); + } + else + { + printf ("<exemple> kamib un message\n"); + } + ladmin_log ("Le message est vide (commande 'kami(b)').\n"); + } + else + { + printf ("Please input a message.\n"); + if (type == 0) + { + printf ("<example> kami a message\n"); + } + else + { + printf ("<example> kamib a message\n"); + } + ladmin_log ("The message is void ('kami(b)' command).\n"); + } + return 136; + } + + WFIFOW (login_fd, 0) = 0x794e; + WFIFOW (login_fd, 2) = type; + WFIFOL (login_fd, 4) = strlen (message) + 1; + memcpy (WFIFOP (login_fd, 8), message, strlen (message) + 1); + WFIFOSET (login_fd, 8 + strlen (message) + 1); + bytes_to_read = 1; + + return 0; +} + +//-------------------------------------------- +// Sub-function: Change language of displaying +//-------------------------------------------- +int changelanguage (char *language) +{ + if (strlen (language) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez une langue svp.\n"); + printf ("<exemple> language english\n"); + printf (" language français\n"); + ladmin_log ("La langue est vide (commande 'language').\n"); + } + else + { + printf ("Please input a language.\n"); + printf ("<example> language english\n"); + printf (" language français\n"); + ladmin_log ("The language is void ('language' command).\n"); + } + return 136; + } + + language[0] = toupper (language[0]); + if (language[0] == 'F' || language[0] == 'E') + { + defaultlanguage = language[0]; + if (defaultlanguage == 'F') + { + printf ("Changement de la langue d'affichage en Français.\n"); + ladmin_log ("Changement de la langue d'affichage en Français.\n"); + } + else + { + printf ("Displaying language changed to English.\n"); + ladmin_log ("Displaying language changed to English.\n"); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Langue non paramétrée (langues possibles: 'Français' ou 'English').\n"); + ladmin_log + ("Langue non paramétrée (Français ou English nécessaire).\n"); + } + else + { + printf + ("Undefined language (possible languages: Français or English).\n"); + ladmin_log ("Undefined language (must be Français or English).\n"); + } + } + + return 0; +} + +//-------------------------------------------------------- +// Sub-function: Asking to Displaying of the accounts list +//-------------------------------------------------------- +int listaccount (char *param, int type) +{ +//int list_first, list_last, list_type; // parameter to display a list of accounts + int i; + + list_type = type; + + // set default values + list_first = 0; + list_last = 0; + + if (list_type == 1) + { // if listgm + // get all accounts = use default + } + else if (list_type == 2) + { // if search + for (i = 0; param[i]; i++) + param[i] = tolower (param[i]); + // get all accounts = use default + } + else if (list_type == 3) + { // if listban + // get all accounts = use default + } + else if (list_type == 4) + { // if listok + // get all accounts = use default + } + else + { // if list (list_type == 0) + switch (sscanf (param, "%d %d", &list_first, &list_last)) + { + case 0: + // get all accounts = use default + break; + case 1: + list_last = 0; + // use tests of the following value + default: + if (list_first < 0) + list_first = 0; + if (list_last < list_first || list_last < 0) + list_last = 0; + break; + } + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir la liste des comptes de %d à %d.\n", + list_first, list_last); + } + else + { + ladmin_log + ("Request to login-server to obtain the list of accounts from %d to %d.\n", + list_first, list_last); + } + + WFIFOW (login_fd, 0) = 0x7920; + WFIFOL (login_fd, 2) = list_first; + WFIFOL (login_fd, 6) = list_last; + WFIFOSET (login_fd, 10); + bytes_to_read = 1; + + // 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567 + if (defaultlanguage == 'F') + { + Iprintf + (" id_compte GM nom_utilisateur sexe count statut\n"); + } + else + { + Iprintf + ("account_id GM user_name sex count state\n"); + } + Iprintf + ("-------------------------------------------------------------------------------\n"); + list_count = 0; + + return 0; +} + +//-------------------------------------------------------- +// Sub-function: Frobnicate items +//-------------------------------------------------------- +int itemfrob (char *param) +{ + int source_id, dest_id; + + if (sscanf (param, "%d %d", &source_id, &dest_id) < 2) + { + printf ("You must provide the source and destination item IDs.\n"); + return 1; + } + + WFIFOW (login_fd, 0) = 0x7924; + WFIFOL (login_fd, 2) = source_id; + WFIFOL (login_fd, 6) = dest_id; + WFIFOSET (login_fd, 10); + bytes_to_read = 1; // all logging is done to the three main servers + + return 0; +} + +//-------------------------------------------- +// Sub-function: Asking to modify a memo field +//-------------------------------------------- +int changememo (char *param) +{ + char name[1023], memo[1023]; + + memset (name, '\0', sizeof (name)); + memset (memo, '\0', sizeof (memo)); + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, memo) < 1 && // memo can be void + sscanf (param, "'%[^']' %[^\r\n]", name, memo) < 1 && // memo can be void + sscanf (param, "%s %[^\r\n]", name, memo) < 1) + { // memo can be void + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un mémo svp.\n"); + printf ("<exemple> memo nomtest nouveau memo\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le mémo d'un compte (commande 'email').\n"); + } + else + { + printf ("Please input an account name and a memo.\n"); + printf ("<example> memo testname new memo\n"); + ladmin_log + ("Incomplete parameters to change the memo of an account ('email' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (strlen (memo) > 254) + { + if (defaultlanguage == 'F') + { + printf ("Mémo trop long (%d caractères).\n", strlen (memo)); + printf ("Entrez un mémo de 254 caractères maximum svp.\n"); + ladmin_log + ("Mémo trop long (%d caractères). Entrez un mémo de 254 caractères maximum svp.\n", + strlen (memo)); + } + else + { + printf ("Memo is too long (%d characters).\n", strlen (memo)); + printf ("Please input a memo of 254 bytes at the maximum.\n"); + ladmin_log + ("Email is too long (%d characters). Please input a memo of 254 bytes at the maximum.\n", + strlen (memo)); + } + return 102; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer un mémo.\n"); + } + else + { + ladmin_log ("Request to login-server to change a memo.\n"); + } + + WFIFOW (login_fd, 0) = 0x7942; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOW (login_fd, 26) = strlen (memo); + if (strlen (memo) > 0) + memcpy (WFIFOP (login_fd, 28), memo, strlen (memo)); + WFIFOSET (login_fd, 28 + strlen (memo)); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------- +// Sub-function: Asking to obtain an account name +//----------------------------------------------- +int nameaccount (int id) +{ + if (id < 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un id ayant une valeur positive svp.\n"); + ladmin_log + ("Id négatif donné pour rechecher le nom d'un compte (commande 'name').\n"); + } + else + { + printf ("Please input a positive value for the id.\n"); + ladmin_log + ("Negativ id given to search an account name ('name' command).\n"); + } + return 136; + } + + if (defaultlanguage == 'F') + ladmin_log + ("Envoi d'un requête au serveur de logins pour connaître le nom d'un compte.\n"); + else + ladmin_log ("Request to login-server to know an account name.\n"); + + WFIFOW (login_fd, 0) = 0x7946; + WFIFOL (login_fd, 2) = id; + WFIFOSET (login_fd, 6); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------ +// Sub-function: Asking to modify a password +// (Note: never send back a password with login-server!! security of passwords) +//------------------------------------------ +int changepasswd (char *param) +{ + char name[1023], password[1023]; + + memset (name, '\0', sizeof (name)); + memset (password, '\0', sizeof (password)); + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && + sscanf (param, "'%[^']' %[^\r\n]", name, password) < 1 && + sscanf (param, "%s %[^\r\n]", name, password) < 1) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemple> passwd nomtest nouveaumotdepasse\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le mot d'un passe d'un compte (commande 'password').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<example> passwd testname newpassword\n"); + ladmin_log + ("Incomplete parameters to change the password of an account ('password' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (strlen (password) == 0) + { + if (typepasswd (password) == 0) + return 134; + } + if (verify_password (password) == 0) + return 131; + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer un mot de passe.\n"); + } + else + { + ladmin_log ("Request to login-server to change a password.\n"); + } + + WFIFOW (login_fd, 0) = 0x7934; + memcpy (WFIFOP (login_fd, 2), name, 24); + memcpy (WFIFOP (login_fd, 26), password, 24); + WFIFOSET (login_fd, 50); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------------------- +// Sub-function: Request to login-server to reload GM configuration file +// this function have no answer +//---------------------------------------------------------------------- +int reloadGM (void) +{ + WFIFOW (login_fd, 0) = 0x7955; + WFIFOSET (login_fd, 2); + bytes_to_read = 0; + + if (defaultlanguage == 'F') + { + ladmin_log + ("Demande de recharger le fichier de configuration des GM envoyée.\n"); + printf + ("Demande de recharger le fichier de configuration des GM envoyée.\n"); + printf ("Vérifiez les comptes GM actuels (après rechargement):\n"); + } + else + { + ladmin_log ("Request to reload the GM configuration file sended.\n"); + printf ("Request to reload the GM configuration file sended.\n"); + printf ("Check the actual GM accounts (after reloading):\n"); + } + listaccount (parameters, 1); // 1: to list only GM + + return 180; +} + +//----------------------------------------------------- +// Sub-function: Asking to modify the sex of an account +//----------------------------------------------------- +int changesex (char *param) +{ + char name[1023], sex[1023]; + + memset (name, '\0', sizeof (name)); + memset (sex, '\0', sizeof (sex)); + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, sex) < 2 && + sscanf (param, "'%[^']' %[^\r\n]", name, sex) < 2 && + sscanf (param, "%s %[^\r\n]", name, sex) < 2) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un sexe svp.\n"); + printf ("<exemple> sex nomtest Male\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le sexe d'un compte (commande 'sex').\n"); + } + else + { + printf ("Please input an account name and a sex.\n"); + printf ("<example> sex testname Male\n"); + ladmin_log + ("Incomplete parameters to change the sex of an account ('sex' command).\n"); + } + return 136; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + sex[0] = toupper (sex[0]); + if (strchr ("MF", sex[0]) == NULL) + { + if (defaultlanguage == 'F') + { + printf ("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); + ladmin_log ("Sexe incorrect [%s]. Entrez M ou F svp.\n", + sex); + } + else + { + printf ("Illegal gender [%s]. Please input M or F.\n", sex); + ladmin_log ("Illegal gender [%s]. Please input M or F.\n", + sex); + } + return 103; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer un sexe.\n"); + } + else + { + ladmin_log ("Request to login-server to change a sex.\n"); + } + + WFIFOW (login_fd, 0) = 0x793c; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOB (login_fd, 26) = sex[0]; + WFIFOSET (login_fd, 27); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------------------------- +// Sub-function of sub-function changestate, blockaccount or unblockaccount +// Asking to modify the state of an account +//------------------------------------------------------------------------- +int changestatesub (char *name, int state, char *error_message7) +{ + char error_message[1023]; // need to use, because we can modify error_message7 + + memset (error_message, '\0', sizeof (error_message)); + strncpy (error_message, error_message7, sizeof (error_message) - 1); + + if ((state < 0 || state > 9) && state != 100) + { // Valid values: 0: ok, or value of the 0x006a packet + 1 + if (defaultlanguage == 'F') + { + printf ("Entrez une des statuts suivantes svp:\n"); + printf + (" 0 = Compte ok 6 = Your Game's EXE file is not the latest version\n"); + } + else + { + printf ("Please input one of these states:\n"); + printf + (" 0 = Account ok 6 = Your Game's EXE file is not the latest version\n"); + } + printf + (" 1 = Unregistered ID 7 = You are Prohibited to log in until + message\n"); + printf + (" 2 = Incorrect Password 8 = Server is jammed due to over populated\n"); + printf (" 3 = This ID is expired 9 = No MSG\n"); + printf + (" 4 = Rejected from Server 100 = This ID has been totally erased\n"); + printf (" 5 = You have been blocked by the GM Team\n"); + if (defaultlanguage == 'F') + { + printf ("<exemples> state nomtest 5\n"); + printf (" state nomtest 7 fin de votre ban\n"); + printf (" block <nom compte>\n"); + printf (" unblock <nom compte>\n"); + ladmin_log + ("Valeur incorrecte pour le statut d'un compte (commande 'state', 'block' ou 'unblock').\n"); + } + else + { + printf ("<examples> state testname 5\n"); + printf (" state testname 7 end of your ban\n"); + printf (" block <account name>\n"); + printf (" unblock <account name>\n"); + ladmin_log + ("Invalid value for the state of an account ('state', 'block' or 'unblock' command).\n"); + } + return 151; + } + + if (verify_accountname (name) == 0) + { + return 102; + } + + if (state != 7) + { + strcpy (error_message, "-"); + } + else + { + if (strlen (error_message) < 1) + { + if (defaultlanguage == 'F') + { + printf + ("Message d'erreur trop court. Entrez un message de 1-19 caractères.\n"); + ladmin_log + ("Message d'erreur trop court. Entrez un message de 1-19 caractères.\n"); + } + else + { + printf + ("Error message is too short. Please input a message of 1-19 bytes.\n"); + ladmin_log + ("Error message is too short. Please input a message of 1-19 bytes.\n"); + } + return 102; + } + if (strlen (error_message) > 19) + { + if (defaultlanguage == 'F') + { + printf + ("Message d'erreur trop long. Entrez un message de 1-19 caractères.\n"); + ladmin_log + ("Message d'erreur trop long. Entrez un message de 1-19 caractères.\n"); + } + else + { + printf + ("Error message is too long. Please input a message of 1-19 bytes.\n"); + ladmin_log + ("Error message is too long. Please input a message of 1-19 bytes.\n"); + } + return 102; + } + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour changer un statut.\n"); + } + else + { + ladmin_log ("Request to login-server to change a state.\n"); + } + + WFIFOW (login_fd, 0) = 0x7936; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOL (login_fd, 26) = state; + memcpy (WFIFOP (login_fd, 30), error_message, 20); + WFIFOSET (login_fd, 50); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------- +// Sub-function: Asking to modify the state of an account +//------------------------------------------------------- +int changestate (char *param) +{ + char name[1023], error_message[1023]; + int state; + + memset (name, '\0', sizeof (name)); + memset (error_message, '\0', sizeof (error_message)); + + if (sscanf (param, "\"%[^\"]\" %d %[^\r\n]", name, &state, error_message) + < 2 + && sscanf (param, "'%[^']' %d %[^\r\n]", name, &state, + error_message) < 2 + && sscanf (param, "%s %d %[^\r\n]", name, &state, error_message) < 2) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un statut svp.\n"); + printf ("<exemples> state nomtest 5\n"); + printf (" state nomtest 7 fin de votre ban\n"); + printf (" block <nom compte>\n"); + printf (" unblock <nom compte>\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'state').\n"); + } + else + { + printf ("Please input an account name and a state.\n"); + printf ("<examples> state testname 5\n"); + printf (" state testname 7 end of your ban\n"); + printf (" block <account name>\n"); + printf (" unblock <account name>\n"); + ladmin_log + ("Incomplete parameters to change the state of an account ('state' command).\n"); + } + return 136; + } + + return changestatesub (name, state, error_message); +} + +//------------------------------------------- +// Sub-function: Asking to unblock an account +//------------------------------------------- +int unblockaccount (char *param) +{ + char name[1023]; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemples> state nomtest 5\n"); + printf (" state nomtest 7 fin de votre ban\n"); + printf (" block <nom compte>\n"); + printf (" unblock <nom compte>\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'unblock').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<examples> state testname 5\n"); + printf (" state testname 7 end of your ban\n"); + printf (" block <account name>\n"); + printf (" unblock <account name>\n"); + ladmin_log + ("Incomplete parameters to change the state of an account ('unblock' command).\n"); + } + return 136; + } + + return changestatesub (name, 0, "-"); // state 0, no error message +} + +//------------------------------------------- +// Sub-function: Asking to unblock an account +//------------------------------------------- +int blockaccount (char *param) +{ + char name[1023]; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemples> state nomtest 5\n"); + printf (" state nomtest 7 fin de votre ban\n"); + printf (" block <nom compte>\n"); + printf (" unblock <nom compte>\n"); + ladmin_log + ("Nombre incorrect de paramètres pour changer le statut d'un compte (commande 'block').\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<examples> state testname 5\n"); + printf (" state testname 7 end of your ban\n"); + printf (" block <account name>\n"); + printf (" unblock <account name>\n"); + ladmin_log + ("Incomplete parameters to change the state of an account ('block' command).\n"); + } + return 136; + } + + return changestatesub (name, 5, "-"); // state 5, no error message +} + +//--------------------------------------------------------------------- +// Sub-function: Add/substract time to the validity limit of an account +//--------------------------------------------------------------------- +int timeaddaccount (char *param) +{ + char name[1023], modif[1023]; + int year, month, day, hour, minute, second; + char *p_modif; + int value, i; + + memset (name, '\0', sizeof (name)); + memset (modif, '\0', sizeof (modif)); + year = month = day = hour = minute = second = 0; + + if (sscanf (param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && + sscanf (param, "'%[^']' %[^\r\n]", name, modif) < 2 && + sscanf (param, "%s %[^\r\n]", name, modif) < 2) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte et un modificateur svp.\n"); + printf (" <exemple> timeadd nomtest +1m-2mn1s-6y\n"); + printf + (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour modifier une date limite d'utilisation (commande 'timeadd').\n"); + } + else + { + printf ("Please input an account name and a modifier.\n"); + printf (" <example>: timeadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + ladmin_log + ("Incomplete parameters to modify a limit time ('timeadd' command).\n"); + } + return 136; + } + if (verify_accountname (name) == 0) + { + return 102; + } + + // lowercase for modif + for (i = 0; modif[i]; i++) + modif[i] = tolower (modif[i]); + p_modif = modif; + while (strlen (p_modif) > 0) + { + value = atoi (p_modif); + if (value == 0) + { + p_modif++; + } + else + { + if (p_modif[0] == '-' || p_modif[0] == '+') + p_modif++; + while (strlen (p_modif) > 0 && p_modif[0] >= '0' + && p_modif[0] <= '9') + { + p_modif++; + } + if (p_modif[0] == 's') + { + second = value; + p_modif++; + } + else if (p_modif[0] == 'm' && p_modif[1] == 'n') + { + minute = value; + p_modif += 2; + } + else if (p_modif[0] == 'h') + { + hour = value; + p_modif++; + } + else if (p_modif[0] == 'd' || p_modif[0] == 'j') + { + day = value; + p_modif += 2; + } + else if (p_modif[0] == 'm') + { + month = value; + p_modif++; + } + else if (p_modif[0] == 'y' || p_modif[0] == 'a') + { + year = value; + p_modif++; + } + else + { + p_modif++; + } + } + } + + if (defaultlanguage == 'F') + { + printf (" année: %d\n", year); + printf (" mois: %d\n", month); + printf (" jour: %d\n", day); + printf (" heure: %d\n", hour); + printf (" minute: %d\n", minute); + printf (" seconde: %d\n", second); + } + else + { + printf (" year: %d\n", year); + printf (" month: %d\n", month); + printf (" day: %d\n", day); + printf (" hour: %d\n", hour); + printf (" minute: %d\n", minute); + printf (" second: %d\n", second); + } + + if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 + && second == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Vous devez entrer un ajustement avec cette commande, svp:\n"); + printf (" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf (" Elément modifié:\n"); + printf (" a ou y: année\n"); + printf (" m: mois\n"); + printf (" j ou d: jour\n"); + printf (" h: heure\n"); + printf (" mn: minute\n"); + printf (" s: seconde\n"); + printf (" <exemple> timeadd nomtest +1m-2mn1s-6y\n"); + printf + (" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf (" et 6 ans dans le même temps.\n"); + ladmin_log + ("Aucun ajustement n'est pas un ajustement (commande 'timeadd').\n"); + } + else + { + printf ("Please give an adjustment with this command:\n"); + printf (" Adjustment value (-1, 1, +1, etc...)\n"); + printf (" Modified element:\n"); + printf (" a or y: year\n"); + printf (" m: month\n"); + printf (" j or d: day\n"); + printf (" h: hour\n"); + printf (" mn: minute\n"); + printf (" s: second\n"); + printf (" <example> timeadd testname +1m-2mn1s-6y\n"); + printf + (" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf (" and 6 years at the same time.\n"); + ladmin_log + ("No adjustment isn't an adjustment ('timeadd' command).\n"); + } + return 137; + } + if (year > 127 || year < -127) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement d'années correct (de -127 à 127), svp.\n"); + ladmin_log + ("Ajustement de l'année hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the years (from -127 to 127).\n"); + ladmin_log + ("Abnormal adjustement for the year ('timeadd' command).\n"); + } + return 137; + } + if (month > 255 || month < -255) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de mois correct (de -255 à 255), svp.\n"); + ladmin_log ("Ajustement du mois hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the months (from -255 to 255).\n"); + ladmin_log + ("Abnormal adjustement for the month ('timeadd' command).\n"); + } + return 137; + } + if (day > 32767 || day < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de jours correct (de -32767 à 32767), svp.\n"); + ladmin_log ("Ajustement des jours hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the days (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the days ('timeadd' command).\n"); + } + return 137; + } + if (hour > 32767 || hour < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement d'heures correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des heures hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the hours ('timeadd' command).\n"); + } + return 137; + } + if (minute > 32767 || minute < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de minutes correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des minutes hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the minutes ('timeadd' command).\n"); + } + return 137; + } + if (second > 32767 || second < -32767) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un ajustement de secondes correct (de -32767 à 32767), svp.\n"); + ladmin_log + ("Ajustement des secondes hors norme ('timeadd' command).\n"); + } + else + { + printf + ("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); + ladmin_log + ("Abnormal adjustement for the seconds ('timeadd' command).\n"); + } + return 137; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour modifier une date limite d'utilisation.\n"); + } + else + { + ladmin_log ("Request to login-server to modify a time limit.\n"); + } + + WFIFOW (login_fd, 0) = 0x7950; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOW (login_fd, 26) = (short) year; + WFIFOW (login_fd, 28) = (short) month; + WFIFOW (login_fd, 30) = (short) day; + WFIFOW (login_fd, 32) = (short) hour; + WFIFOW (login_fd, 34) = (short) minute; + WFIFOW (login_fd, 36) = (short) second; + WFIFOSET (login_fd, 38); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------- +// Sub-function: Set a validity limit of an account +//------------------------------------------------- +int timesetaccount (char *param) +{ + char name[1023], date[1023], time[1023]; + int year, month, day, hour, minute, second; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + struct tm *tmtime; + + memset (name, '\0', sizeof (name)); + memset (date, '\0', sizeof (date)); + memset (time, '\0', sizeof (time)); + year = month = day = hour = minute = second = 0; + connect_until_time = 0; + tmtime = localtime (&connect_until_time); // initialize + + if (sscanf (param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf (param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf (param, "%s %s %[^\r\n]", name, date, time) < 2) + { // if date = 0, time can be void + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte, une date et une heure svp.\n"); + printf + ("<exemple>: timeset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n"); + printf + (" timeset <nom_du_compte> 0 (0 = illimité)\n"); + printf (" Heure par défaut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Nombre incorrect de paramètres pour fixer une date limite d'utilisation (commande 'timeset').\n"); + } + else + { + printf ("Please input an account name, a date and a hour.\n"); + printf + ("<example>: timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n"); + printf + (" timeset <account_name> 0 (0 = unlimited)\n"); + printf (" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log + ("Incomplete parameters to set a limit time ('timeset' command).\n"); + } + return 136; + } + if (verify_accountname (name) == 0) + { + return 102; + } + + if (time[0] == '\0') + strcpy (time, "23:59:59"); + + if (atoi (date) != 0 && + ((sscanf (date, "%d/%d/%d", &year, &month, &day) < 3 && + sscanf (date, "%d-%d-%d", &year, &month, &day) < 3 && + sscanf (date, "%d.%d.%d", &year, &month, &day) < 3 && + sscanf (date, "%d'%d'%d", &year, &month, &day) < 3) || + sscanf (time, "%d:%d:%d", &hour, &minute, &second) < 3)) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); + ladmin_log + ("Format incorrect pour la date/heure ('timeset' command).\n"); + } + else + { + printf + ("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); + ladmin_log + ("Invalid format for the date/time ('timeset' command).\n"); + } + return 102; + } + + if (atoi (date) == 0) + { + connect_until_time = 0; + } + else + { + if (year < 70) + { + year = year + 100; + } + if (year >= 1900) + { + year = year - 1900; + } + if (month < 1 || month > 12) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un mois correct svp (entre 1 et 12).\n"); + ladmin_log ("Mois incorrect pour la date ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for the month (from 1 to 12).\n"); + ladmin_log ("Invalid month for the date ('timeset' command).\n"); + } + return 102; + } + month = month - 1; + if (day < 1 || day > 31) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un jour correct svp (entre 1 et 31).\n"); + ladmin_log ("Jour incorrect pour la date ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for the day (from 1 to 31).\n"); + ladmin_log ("Invalid day for the date ('timeset' command).\n"); + } + return 102; + } + if (((month == 3 || month == 5 || month == 8 || month == 10) + && day > 30) || (month == 1 && day > 29)) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez un jour correct en fonction du mois (%d) svp.\n", + month); + ladmin_log + ("Jour incorrect pour ce mois correspondant ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for a day of this month (%d).\n", + month); + ladmin_log ("Invalid day for this month ('timeset' command).\n"); + } + return 102; + } + if (hour < 0 || hour > 23) + { + if (defaultlanguage == 'F') + { + printf ("Entrez une heure correcte svp (entre 0 et 23).\n"); + ladmin_log + ("Heure incorrecte pour l'heure ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for the hour (from 0 to 23).\n"); + ladmin_log ("Invalid hour for the time ('timeset' command).\n"); + } + return 102; + } + if (minute < 0 || minute > 59) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez des minutes correctes svp (entre 0 et 59).\n"); + ladmin_log + ("Minute incorrecte pour l'heure ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for the minutes (from 0 to 59).\n"); + ladmin_log ("Invalid minute for the time ('timeset' command).\n"); + } + return 102; + } + if (second < 0 || second > 59) + { + if (defaultlanguage == 'F') + { + printf + ("Entrez des secondes correctes svp (entre 0 et 59).\n"); + ladmin_log + ("Seconde incorrecte pour l'heure ('timeset' command).\n"); + } + else + { + printf + ("Please give a correct value for the seconds (from 0 to 59).\n"); + ladmin_log ("Invalid second for the time ('timeset' command).\n"); + } + return 102; + } + tmtime->tm_year = year; + tmtime->tm_mon = month; + tmtime->tm_mday = day; + tmtime->tm_hour = hour; + tmtime->tm_min = minute; + tmtime->tm_sec = second; + tmtime->tm_isdst = -1; // -1: no winter/summer time modification + connect_until_time = timegm (tmtime); + if (connect_until_time == -1) + { + if (defaultlanguage == 'F') + { + printf ("Date incorrecte.\n"); + printf + ("Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); + ladmin_log ("Date incorrecte. ('timeset' command).\n"); + } + else + { + printf ("Invalid date.\n"); + printf + ("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); + ladmin_log ("Invalid date. ('timeset' command).\n"); + } + return 102; + } + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour fixer une date limite d'utilisation.\n"); + } + else + { + ladmin_log ("Request to login-server to set a time limit.\n"); + } + + WFIFOW (login_fd, 0) = 0x7948; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOL (login_fd, 26) = (int) connect_until_time; + WFIFOSET (login_fd, 30); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +// Sub-function: Asking to displaying information about an account (by its name) +//------------------------------------------------------------------------------ +int whoaccount (char *param) +{ + char name[1023]; + + memset (name, '\0', sizeof (name)); + + if (strlen (param) == 0 || + (sscanf (param, "\"%[^\"]\"", name) < 1 && + sscanf (param, "'%[^']'", name) < 1 && + sscanf (param, "%[^\r\n]", name) < 1) || strlen (name) == 0) + { + if (defaultlanguage == 'F') + { + printf ("Entrez un nom de compte svp.\n"); + printf ("<exemple> who nomtest\n"); + ladmin_log ("Aucun nom n'a été donné pour trouver le compte.\n"); + } + else + { + printf ("Please input an account name.\n"); + printf ("<example> who testname\n"); + ladmin_log ("No name was given to found the account.\n"); + } + return 136; + } + if (verify_accountname (name) == 0) + { + return 102; + } + + if (defaultlanguage == 'F') + { + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir le information d'un compte (par le nom).\n"); + } + else + { + ladmin_log + ("Request to login-server to obtain information about an account (by its name).\n"); + } + + WFIFOW (login_fd, 0) = 0x7952; + memcpy (WFIFOP (login_fd, 2), name, 24); + WFIFOSET (login_fd, 26); + bytes_to_read = 1; + + return 0; +} + +//-------------------------------------------------------- +// Sub-function: Asking of the version of the login-server +//-------------------------------------------------------- +int checkloginversion (void) +{ + if (defaultlanguage == 'F') + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir sa version.\n"); + else + ladmin_log ("Request to login-server to obtain its version.\n"); + + WFIFOW (login_fd, 0) = 0x7530; + WFIFOSET (login_fd, 2); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------- +// Prompt function +// this function wait until user type a command +// and analyse the command. +//--------------------------------------------- +int prompt (void) +{ + int i, j; + char buf[1024]; + char *p; + + // while we don't wait new packets + while (bytes_to_read == 0) + { + // for help with the console colors look here: + // http://www.edoceo.com/liberum/?doc=printf-with-color + // some code explanation (used here): + // \033[2J : clear screen and go up/left (0, 0 position) + // \033[K : clear line from actual position to end of the line + // \033[0m : reset color parameter + // \033[1m : use bold for font + Iprintf ("\n"); + if (defaultlanguage == 'F') + { + Iprintf + ("\033[32mPour afficher les commandes, tapez 'Entrée'.\033[0m\n"); + } + else + Iprintf ("\033[32mTo list the commands, type 'enter'.\033[0m\n"); + Iprintf ("\033[0;36mLadmin-> \033[0m"); + Iprintf ("\033[1m"); + fflush (stdout); + + // get command and parameter + memset (buf, '\0', sizeof (buf)); + fflush (stdin); + if (!fgets (buf, 1023, stdin)) + exit (0); + buf[1023] = '\0'; + + Iprintf ("\033[0m"); + fflush (stdout); + + if (!eathena_interactive_session && !strlen (buf)) + exit (0); + + // remove final \n + if ((p = strrchr (buf, '\n')) != NULL) + p[0] = '\0'; + // remove all control char + for (i = 0; buf[i]; i++) + if (buf[i] < 32) + { + // remove cursor control. + if (buf[i] == 27 && buf[i + 1] == '[' && (buf[i + 2] == 'H' || // home position (cursor) + buf[i + 2] == 'J' || // clear screen + buf[i + 2] == 'A' || // up 1 line + buf[i + 2] == 'B' || // down 1 line + buf[i + 2] == 'C' || // right 1 position + buf[i + 2] == 'D' || // left 1 position + buf[i + 2] == 'G')) + { // center cursor (windows) + for (j = i; buf[j]; j++) + buf[j] = buf[j + 3]; + } + else if (buf[i] == 27 && buf[i + 1] == '[' + && buf[i + 2] == '2' && buf[i + 3] == 'J') + { // clear screen + for (j = i; buf[j]; j++) + buf[j] = buf[j + 4]; + } + else if (buf[i] == 27 && buf[i + 1] == '[' && buf[i + 3] == '~' && (buf[i + 2] == '1' || // home (windows) + buf[i + 2] == '2' || // insert (windows) + buf[i + 2] == '3' || // del (windows) + buf[i + 2] == '4' || // end (windows) + buf[i + 2] == '5' || // pgup (windows) + buf + [i + + + 2] + == + '6')) + { // pgdown (windows) + for (j = i; buf[j]; j++) + buf[j] = buf[j + 4]; + } + else + { + // remove other control char. + for (j = i; buf[j]; j++) + buf[j] = buf[j + 1]; + } + i--; + } + + // extract command name and parameters + memset (command, '\0', sizeof (command)); + memset (parameters, '\0', sizeof (parameters)); + sscanf (buf, "%1023s %[^\n]", command, parameters); + command[1023] = '\0'; + parameters[1023] = '\0'; + + // lowercase for command line + for (i = 0; command[i]; i++) + command[i] = tolower (command[i]); + + if (command[0] == '?' || strlen (command) == 0) + { + if (defaultlanguage == 'F') + { + strcpy (buf, "aide"); + strcpy (command, "aide"); + } + else + { + strcpy (buf, "help"); + strcpy (command, "help"); + } + } + + // Analyse of the command + check_command (command); // give complete name to the command + + if (strlen (parameters) == 0) + { + if (defaultlanguage == 'F') + { + ladmin_log ("Commande: '%s' (sans paramètre)\n", + command, parameters); + } + else + { + ladmin_log ("Command: '%s' (without parameters)\n", + command, parameters); + } + } + else + { + if (defaultlanguage == 'F') + { + ladmin_log ("Commande: '%s', paramètres: '%s'\n", + command, parameters); + } + else + { + ladmin_log ("Command: '%s', parameters: '%s'\n", + command, parameters); + } + } + + // Analyse of the command +// help + if (strcmp (command, "aide") == 0) + { + display_help (parameters, 1); // 1: french + } + else if (strcmp (command, "help") == 0) + { + display_help (parameters, 0); // 0: english +// general commands + } + else if (strcmp (command, "add") == 0) + { + addaccount (parameters, 0); // 0: no email + } + else if (strcmp (command, "ban") == 0) + { + banaccount (parameters); + } + else if (strcmp (command, "banadd") == 0) + { + banaddaccount (parameters); + } + else if (strcmp (command, "banset") == 0) + { + bansetaccount (parameters); + } + else if (strcmp (command, "block") == 0) + { + blockaccount (parameters); + } + else if (strcmp (command, "check") == 0) + { + checkaccount (parameters); + } + else if (strcmp (command, "create") == 0) + { + addaccount (parameters, 1); // 1: with email + } + else if (strcmp (command, "delete") == 0) + { + delaccount (parameters); + } + else if (strcmp (command, "email") == 0) + { + changeemail (parameters); + } + else if (strcmp (command, "getcount") == 0) + { + getlogincount (); + } + else if (strcmp (command, "gm") == 0) + { + changegmlevel (parameters); + } + else if (strcmp (command, "id") == 0) + { + idaccount (parameters); + } + else if (strcmp (command, "info") == 0) + { + infoaccount (atoi (parameters)); + } + else if (strcmp (command, "kami") == 0) + { + sendbroadcast (0, parameters); // flag for normal + } + else if (strcmp (command, "kamib") == 0) + { + sendbroadcast (0x10, parameters); // flag for blue + } + else if (strcmp (command, "language") == 0) + { + changelanguage (parameters); + } + else if (strcmp (command, "itemfrob") == 0) + { + itemfrob (parameters); // 0: to list all + } + else if (strcmp (command, "list") == 0) + { + listaccount (parameters, 0); // 0: to list all + } + else if (strcmp (command, "listban") == 0) + { + listaccount (parameters, 3); // 3: to list only accounts with state or bannished + } + else if (strcmp (command, "listgm") == 0) + { + listaccount (parameters, 1); // 1: to list only GM + } + else if (strcmp (command, "listok") == 0) + { + listaccount (parameters, 4); // 4: to list only accounts without state and not bannished + } + else if (strcmp (command, "memo") == 0) + { + changememo (parameters); + } + else if (strcmp (command, "name") == 0) + { + nameaccount (atoi (parameters)); + } + else if (strcmp (command, "password") == 0) + { + changepasswd (parameters); + } + else if (strcmp (command, "reloadgm") == 0) + { + reloadGM (); + } + else if (strcmp (command, "search") == 0) + { // no regex in C version + listaccount (parameters, 2); // 2: to list with pattern + } + else if (strcmp (command, "sex") == 0) + { + changesex (parameters); + } + else if (strcmp (command, "state") == 0) + { + changestate (parameters); + } + else if (strcmp (command, "timeadd") == 0) + { + timeaddaccount (parameters); + } + else if (strcmp (command, "timeset") == 0) + { + timesetaccount (parameters); + } + else if (strcmp (command, "unban") == 0) + { + unbanaccount (parameters); + } + else if (strcmp (command, "unblock") == 0) + { + unblockaccount (parameters); + } + else if (strcmp (command, "version") == 0) + { + checkloginversion (); + } + else if (strcmp (command, "who") == 0) + { + whoaccount (parameters); +// quit + } + else if (strcmp (command, "quit") == 0 || + strcmp (command, "exit") == 0 || + strcmp (command, "end") == 0) + { + if (defaultlanguage == 'F') + { + printf ("Au revoir.\n"); + } + else + { + printf ("Bye.\n"); + } + exit (0); +// unknown command + } + else + { + if (defaultlanguage == 'F') + { + printf ("Commande inconnue [%s].\n", buf); + ladmin_log ("Commande inconnue [%s].\n", buf); + } + else + { + printf ("Unknown command [%s].\n", buf); + ladmin_log ("Unknown command [%s].\n", buf); + } + } + } + + return 0; +} + +//------------------------------------------------------------- +// Function: Parse receiving informations from the login-server +//------------------------------------------------------------- +void parse_fromlogin (int fd) +{ + struct char_session_data *sd; + + if (session[fd]->eof) + { + if (defaultlanguage == 'F') + { + printf + ("Impossible de se connecter au serveur de login [%s:%d] !\n", + loginserverip, loginserverport); + ladmin_log + ("Impossible de se connecter au serveur de login [%s:%d] !\n", + loginserverip, loginserverport); + } + else + { + printf + ("Impossible to have a connection with the login-server [%s:%d] !\n", + loginserverip, loginserverport); + ladmin_log + ("Impossible to have a connection with the login-server [%s:%d] !\n", + loginserverip, loginserverport); + } + close (fd); + delete_session (fd); + exit (0); + } + +// printf("parse_fromlogin : %d %d %d\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); + sd = (struct char_session_data *)session[fd]->session_data; + + while (RFIFOREST (fd) >= 2) + { + switch (RFIFOW (fd, 0)) + { + case 0x7919: // answer of a connection request + if (RFIFOREST (fd) < 3) + return; + if (RFIFOB (fd, 2) != 0) + { + if (defaultlanguage == 'F') + { + printf ("Erreur de login:\n"); + printf (" - mot de passe incorrect,\n"); + printf + (" - système d'administration non activé, ou\n"); + printf (" - IP non autorisée.\n"); + ladmin_log + ("Erreur de login: mot de passe incorrect, système d'administration non activé, ou IP non autorisée.\n"); + } + else + { + printf ("Error at login:\n"); + printf (" - incorrect password,\n"); + printf + (" - administration system not activated, or\n"); + printf (" - unauthorised IP.\n"); + ladmin_log + ("Error at login: incorrect password, administration system not activated, or unauthorised IP.\n"); + } + session[fd]->eof = 1; + //bytes_to_read = 1; // not stop at prompt + } + else + { + if (defaultlanguage == 'F') + { + printf ("Connexion établie.\n"); + ladmin_log ("Connexion établie.\n"); + printf + ("Lecture de la version du serveur de login...\n"); + ladmin_log + ("Lecture de la version du serveur de login...\n"); + } + else + { + Iprintf ("Established connection.\n"); + ladmin_log ("Established connection.\n"); + Iprintf + ("Reading of the version of the login-server...\n"); + ladmin_log + ("Reading of the version of the login-server...\n"); + } + //bytes_to_read = 1; // unchanged + checkloginversion (); + } + RFIFOSKIP (fd, 3); + break; + +#ifdef PASSWORDENC + case 0x01dc: // answer of a coding key request + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + char md5str[64] = + "", md5bin[32], md5key[RFIFOW (fd, 2) - 4 + 1]; + memcpy (md5key, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); + md5key[sizeof (md5key) - 1] = '0'; + if (passenc == 1) + { + strncpy (md5str, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); + strcat (md5str, loginserveradminpassword); + } + else if (passenc == 2) + { + strncpy (md5str, loginserveradminpassword, + sizeof (loginserveradminpassword)); + strcat (md5str, RFIFOP (fd, 4)); + } + MD5_to_bin(MD5_from_cstring(md5str), md5bin); + WFIFOW (login_fd, 0) = 0x7918; // Request for administation login (encrypted password) + WFIFOW (login_fd, 2) = passenc; // Encrypted type + memcpy (WFIFOP (login_fd, 4), md5bin, 16); + WFIFOSET (login_fd, 20); + if (defaultlanguage == 'F') + { + Iprintf ("Réception de la clef MD5.\n"); + ladmin_log ("Réception de la clef MD5.\n"); + Iprintf ("Envoi du mot de passe crypté...\n"); + ladmin_log ("Envoi du mot de passe crypté...\n"); + } + else + { + Iprintf ("Receiving of the MD5 key.\n"); + ladmin_log ("Receiving of the MD5 key.\n"); + Iprintf ("Sending of the encrypted password...\n"); + ladmin_log ("Sending of the encrypted password...\n"); + } + } + bytes_to_read = 1; + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; +#endif + + case 0x7531: // Displaying of the version of the login-server + if (RFIFOREST (fd) < 10) + return; + Iprintf (" Login-Server [%s:%d]\n", loginserverip, + loginserverport); + if (((int) RFIFOB (login_fd, 5)) == 0) + { + Iprintf (" eAthena version stable-%d.%d", + (int) RFIFOB (login_fd, 2), + (int) RFIFOB (login_fd, 3)); + } + else + { + Iprintf (" eAthena version dev-%d.%d", + (int) RFIFOB (login_fd, 2), + (int) RFIFOB (login_fd, 3)); + } + if (((int) RFIFOB (login_fd, 4)) == 0) + Iprintf (" revision %d", (int) RFIFOB (login_fd, 4)); + if (((int) RFIFOB (login_fd, 6)) == 0) + { + Iprintf ("%d.\n", RFIFOW (login_fd, 8)); + } + else + Iprintf ("-mod%d.\n", RFIFOW (login_fd, 8)); + bytes_to_read = 0; + RFIFOSKIP (fd, 10); + break; + + case 0x7925: // Itemfrob-OK + RFIFOSKIP (fd, 2); + bytes_to_read = 0; + break; + + case 0x7921: // Displaying of the list of accounts + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + if (RFIFOW (fd, 2) < 5) + { + if (defaultlanguage == 'F') + { + ladmin_log + (" Réception d'une liste des comptes vide.\n"); + if (list_count == 0) + printf ("Aucun compte trouvé.\n"); + else if (list_count == 1) + printf ("1 compte trouvé.\n"); + else + printf ("%d comptes trouvés.\n", list_count); + } + else + { + ladmin_log (" Receiving of a void accounts list.\n"); + if (list_count == 0) + { + Iprintf ("No account found.\n"); + } + else if (list_count == 1) + { + Iprintf ("1 account found.\n"); + } + else + Iprintf ("%d accounts found.\n", list_count); + } + bytes_to_read = 0; + } + else + { + int i; + if (defaultlanguage == 'F') + ladmin_log (" Réception d'une liste des comptes.\n"); + else + ladmin_log (" Receiving of a accounts list.\n"); + for (i = 4; i < RFIFOW (fd, 2); i += 38) + { + int j; + char userid[24]; + char lower_userid[24]; + memcpy (userid, RFIFOP (fd, i + 5), sizeof (userid)); + userid[sizeof (userid) - 1] = '\0'; + memset (lower_userid, '\0', sizeof (lower_userid)); + for (j = 0; userid[j]; j++) + lower_userid[j] = tolower (userid[j]); + list_first = RFIFOL (fd, i) + 1; + // here are checks... + if (list_type == 0 || + (list_type == 1 && RFIFOB (fd, i + 4) > 0) || + (list_type == 2 + && strstr (lower_userid, parameters) != NULL) + || (list_type == 3 && RFIFOL (fd, i + 34) != 0) + || (list_type == 4 && RFIFOL (fd, i + 34) == 0)) + { + printf ("%10d ", RFIFOL (fd, i)); + if (RFIFOB (fd, i + 4) == 0) + printf (" "); + else + printf ("%2d ", (int) RFIFOB (fd, i + 4)); + printf ("%-24s", userid); + if (defaultlanguage == 'F') + { + if (RFIFOB (fd, i + 29) == 0) + printf ("%-5s ", "Femme"); + else if (RFIFOB (fd, i + 29) == 1) + printf ("%-5s ", "Male"); + else + printf ("%-5s ", "Servr"); + } + else + { + if (RFIFOB (fd, i + 29) == 0) + printf ("%-5s ", "Femal"); + else if (RFIFOB (fd, i + 29) == 1) + printf ("%-5s ", "Male"); + else + printf ("%-5s ", "Servr"); + } + printf ("%6d ", RFIFOL (fd, i + 30)); + switch (RFIFOL (fd, i + 34)) + { + case 0: + if (defaultlanguage == 'F') + printf ("%-27s\n", "Compte Ok"); + else + printf ("%-27s\n", "Account OK"); + break; + case 1: + printf ("%-27s\n", "Unregistered ID"); + break; + case 2: + printf ("%-27s\n", "Incorrect Password"); + break; + case 3: + printf ("%-27s\n", "This ID is expired"); + break; + case 4: + printf ("%-27s\n", + "Rejected from Server"); + break; + case 5: + printf ("%-27s\n", "Blocked by the GM Team"); // You have been blocked by the GM Team + break; + case 6: + printf ("%-27s\n", "Your EXE file is too old"); // Your Game's EXE file is not the latest version + break; + case 7: + printf ("%-27s\n", "Banishement or"); + printf (" Prohibited to login until...\n"); // You are Prohibited to log in until %s + break; + case 8: + printf ("%-27s\n", + "Server is over populated"); + break; + case 9: + printf ("%-27s\n", "No MSG"); + break; + default: // 100 + printf ("%-27s\n", "This ID is totally erased"); // This ID has been totally erased + break; + } + list_count++; + } + } + // asking of the following acounts + if (defaultlanguage == 'F') + ladmin_log + ("Envoi d'un requête au serveur de logins pour obtenir la liste des comptes de %d à %d (complément).\n", + list_first, list_last); + else + ladmin_log + ("Request to login-server to obtain the list of accounts from %d to %d (complement).\n", + list_first, list_last); + WFIFOW (login_fd, 0) = 0x7920; + WFIFOL (login_fd, 2) = list_first; + WFIFOL (login_fd, 6) = list_last; + WFIFOSET (login_fd, 10); + bytes_to_read = 1; + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + case 0x7931: // Answer of login-server about an account creation + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec à la création du compte [%s]. Un compte identique existe déjà.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec à la création du compte [%s]. Un compte identique existe déjà.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] creation failed. Same account already exists.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] creation failed. Same account already exists.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf ("Compte [%s] créé avec succès [id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log ("Compte [%s] créé avec succès [id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s] is successfully created [id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s] is successfully created [id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7933: // Answer of login-server about an account deletion + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] deletion failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] deletion failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf ("Compte [%s][id: %d] SUPPRIME avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Compte [%s][id: %d] SUPPRIME avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] is successfully DELETED.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] is successfully DELETED.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7935: // answer of the change of an account password + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de la modification du mot de passe du compte [%s].\n", + RFIFOP (fd, 6)); + printf ("Le compte [%s] n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec de la modification du mot de passe du compte. Le compte [%s] n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf ("Account [%s] password changing failed.\n", + RFIFOP (fd, 6)); + printf ("Account [%s] doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account password changing failed. The compte [%s] doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Modification du mot de passe du compte [%s][id: %d] réussie.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Modification du mot de passe du compte [%s][id: %d] réussie.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] password successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] password successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7937: // answer of the change of an account state + if (RFIFOREST (fd) < 34) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] state changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] state changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + char tmpstr[256]; + if (defaultlanguage == 'F') + { + sprintf (tmpstr, + "Statut du compte [%s] changé avec succès en [", + RFIFOP (fd, 6)); + } + else + { + sprintf (tmpstr, + "Account [%s] state successfully changed in [", + RFIFOP (fd, 6)); + } + switch (RFIFOL (fd, 30)) + { + case 0: + if (defaultlanguage == 'F') + strcat (tmpstr, "0: Compte Ok"); + else + strcat (tmpstr, "0: Account OK"); + break; + case 1: + strcat (tmpstr, "1: Unregistered ID"); + break; + case 2: + strcat (tmpstr, "2: Incorrect Password"); + break; + case 3: + strcat (tmpstr, "3: This ID is expired"); + break; + case 4: + strcat (tmpstr, "4: Rejected from Server"); + break; + case 5: + strcat (tmpstr, + "5: You have been blocked by the GM Team"); + break; + case 6: + strcat (tmpstr, + "6: [Your Game's EXE file is not the latest version"); + break; + case 7: + strcat (tmpstr, + "7: You are Prohibited to log in until..."); + break; + case 8: + strcat (tmpstr, + "8: Server is jammed due to over populated"); + break; + case 9: + strcat (tmpstr, "9: No MSG"); + break; + default: // 100 + strcat (tmpstr, "100: This ID is totally erased"); + break; + } + strcat (tmpstr, "]"); + printf ("%s\n", tmpstr); + ladmin_log ("%s%s", tmpstr, "\n"); + } + bytes_to_read = 0; + RFIFOSKIP (fd, 34); + break; + + case 0x7939: // answer of the number of online players + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + // Get length of the received packet + int i; + char name[20]; + if (defaultlanguage == 'F') + { + ladmin_log + (" Réception du nombre de joueurs en ligne.\n"); + } + else + { + ladmin_log + (" Receiving of the number of online players.\n"); + } + // Read information of the servers + if (RFIFOW (fd, 2) < 5) + { + if (defaultlanguage == 'F') + { + printf + (" Aucun serveur n'est connecté au login serveur.\n"); + } + else + { + printf + (" No server is connected to the login-server.\n"); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + (" Nombre de joueurs en ligne (serveur: nb):\n"); + } + else + { + printf + (" Number of online players (server: number).\n"); + } + // Displaying of result + for (i = 4; i < RFIFOW (fd, 2); i += 32) + { + memcpy (name, RFIFOP (fd, i + 6), sizeof (name)); + name[sizeof (name) - 1] = '\0'; + printf (" %-20s : %5d\n", name, + RFIFOW (fd, i + 26)); + } + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + case 0x793b: // answer of the check of a password + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("The account [%s] doesn't exist or the password is incorrect.\n", + RFIFOP (fd, 6)); + ladmin_log + ("The account [%s] doesn't exist or the password is incorrect.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Le mot de passe donné correspond bien au compte [%s][id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Le mot de passe donné correspond bien au compte [%s][id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("The proposed password is correct for the account [%s][id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("The proposed password is correct for the account [%s][id: %d].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x793d: // answer of the change of an account sex + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de la modification du sexe du compte [%s].\n", + RFIFOP (fd, 6)); + printf + ("Le compte [%s] n'existe pas ou le sexe est déjà celui demandé.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec de la modification du sexe du compte. Le compte [%s] n'existe pas ou le sexe est déjà celui demandé.\n", + RFIFOP (fd, 6)); + } + else + { + printf ("Account [%s] sex changing failed.\n", + RFIFOP (fd, 6)); + printf + ("Account [%s] doesn't exist or the sex is already the good sex.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account sex changing failed. The compte [%s] doesn't exist or the sex is already the good sex.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Sexe du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Sexe du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] sex successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] sex successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x793f: // answer of the change of an account GM level + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de la modification du niveau de GM du compte [%s].\n", + RFIFOP (fd, 6)); + printf + ("Le compte [%s] n'existe pas, le niveau de GM est déjà celui demandé\n", + RFIFOP (fd, 6)); + printf + ("ou il est impossible de modifier le fichier des comptes GM.\n"); + ladmin_log + ("Echec de la modification du niveau de GM du compte. Le compte [%s] n'existe pas, le niveau de GM est déjà celui demandé ou il est impossible de modifier le fichier des comptes GM.\n", + RFIFOP (fd, 6)); + } + else + { + printf ("Account [%s] GM level changing failed.\n", + RFIFOP (fd, 6)); + printf + ("Account [%s] doesn't exist, the GM level is already the good GM level\n", + RFIFOP (fd, 6)); + printf + ("or it's impossible to modify the GM accounts file.\n"); + ladmin_log + ("Account GM level changing failed. The compte [%s] doesn't exist, the GM level is already the good sex or it's impossible to modify the GM accounts file.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Niveau de GM du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Niveau de GM du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] GM level successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] GM level successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7941: // answer of the change of an account email + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de la modification de l'e-mail du compte [%s].\n", + RFIFOP (fd, 6)); + printf ("Le compte [%s] n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec de la modification de l'e-mail du compte. Le compte [%s] n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf ("Account [%s] e-mail changing failed.\n", + RFIFOP (fd, 6)); + printf ("Account [%s] doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account e-mail changing failed. The compte [%s] doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Modification de l'e-mail du compte [%s][id: %d] réussie.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Modification de l'e-mail du compte [%s][id: %d] réussie.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] e-mail successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] e-mail successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7943: // answer of the change of an account memo + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement du mémo du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement du mémo du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] memo changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] memo changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Mémo du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Mémo du compte [%s][id: %d] changé avec succès.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Account [%s][id: %d] memo successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Account [%s][id: %d] memo successfully changed.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7945: // answer of an account id search + if (RFIFOREST (fd) < 30) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Unable to find the account [%s] id. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Unable to find the account [%s] id. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf ("Le compte [%s] a pour id: %d.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log ("Le compte [%s] a pour id: %d.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf ("The account [%s] have the id: %d.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log ("The account [%s] have the id: %d.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7947: // answer of an account name search + if (RFIFOREST (fd) < 30) + return; + if (strcmp (RFIFOP (fd, 6), "") == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", + RFIFOL (fd, 2)); + ladmin_log + ("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", + RFIFOL (fd, 2)); + } + else + { + printf + ("Unable to find the account [%d] name. Account doesn't exist.\n", + RFIFOL (fd, 2)); + ladmin_log + ("Unable to find the account [%d] name. Account doesn't exist.\n", + RFIFOL (fd, 2)); + } + } + else + { + if (defaultlanguage == 'F') + { + printf ("Le compte [id: %d] a pour nom: %s.\n", + RFIFOL (fd, 2), RFIFOP (fd, 6)); + ladmin_log ("Le compte [id: %d] a pour nom: %s.\n", + RFIFOL (fd, 2), RFIFOP (fd, 6)); + } + else + { + printf ("The account [id: %d] have the name: %s.\n", + RFIFOL (fd, 2), RFIFOP (fd, 6)); + ladmin_log ("The account [id: %d] have the name: %s.\n", + RFIFOL (fd, 2), RFIFOP (fd, 6)); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 30); + break; + + case 0x7949: // answer of an account validity limit set + if (RFIFOREST (fd) < 34) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] validity limit changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] validity limit changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + time_t timestamp = RFIFOL (fd, 30); + if (timestamp == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Limite de validité du compte [%s][id: %d] changée avec succès en [illimité].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Limite de validité du compte [%s][id: %d] changée avec succès en [illimité].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (×tamp)); + if (defaultlanguage == 'F') + { + printf + ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + else + { + printf + ("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 34); + break; + + case 0x794b: // answer of an account ban set + if (RFIFOREST (fd) < 34) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + time_t timestamp = RFIFOL (fd, 30); + if (timestamp == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (×tamp)); + if (defaultlanguage == 'F') + { + printf + ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + else + { + printf + ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 34); + break; + + case 0x794d: // answer of an account ban date/time changing + if (RFIFOREST (fd) < 34) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + time_t timestamp = RFIFOL (fd, 30); + if (timestamp == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + ladmin_log + ("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (×tamp)); + if (defaultlanguage == 'F') + { + printf + ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + else + { + printf + ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 34); + break; + + case 0x794f: // answer of a broadcast + if (RFIFOREST (fd) < 4) + return; + if (RFIFOW (fd, 2) == (unsigned short) -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec de l'envoi du message. Aucun server de char en ligne.\n"); + ladmin_log + ("Echec de l'envoi du message. Aucun server de char en ligne.\n"); + } + else + { + printf + ("Message sending failed. No online char-server.\n"); + ladmin_log + ("Message sending failed. No online char-server.\n"); + } + } + else + { + if (defaultlanguage == 'F') + { + printf + ("Message transmis au server de logins avec succès.\n"); + ladmin_log + ("Message transmis au server de logins avec succès.\n"); + } + else + { + printf + ("Message successfully sended to login-server.\n"); + ladmin_log + ("Message successfully sended to login-server.\n"); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 4); + break; + + case 0x7951: // answer of an account validity limit changing + if (RFIFOREST (fd) < 34) + return; + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Echec du changement de la validité du compte [%s]. Le compte n'existe pas.\n", + RFIFOP (fd, 6)); + } + else + { + printf + ("Account [%s] validity limit changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + ladmin_log + ("Account [%s] validity limit changing failed. Account doesn't exist.\n", + RFIFOP (fd, 6)); + } + } + else + { + time_t timestamp = RFIFOL (fd, 30); + if (timestamp == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Limite de validité du compte [%s][id: %d] inchangée.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + printf + ("Le compte a une validité illimitée ou\n"); + printf + ("la modification est impossible avec les ajustements demandés.\n"); + ladmin_log + ("Limite de validité du compte [%s][id: %d] inchangée. Le compte a une validité illimitée ou la modification est impossible avec les ajustements demandés.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + else + { + printf + ("Validity limit of the account [%s][id: %d] unchanged.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + printf + ("The account have an unlimited validity limit or\n"); + printf + ("the changing is impossible with the proposed adjustments.\n"); + ladmin_log + ("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2)); + } + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (×tamp)); + if (defaultlanguage == 'F') + { + printf + ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + else + { + printf + ("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), tmpstr); + ladmin_log + ("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", + RFIFOP (fd, 6), RFIFOL (fd, 2), + tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 34); + break; + + case 0x7953: // answer of a request about informations of an account (by account name/id) + if (RFIFOREST (fd) < 150 + || RFIFOREST (fd) < (150 + RFIFOW (fd, 148))) + return; + { + char userid[24], error_message[20], lastlogin[24], + last_ip[16], email[40], memo[255]; + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + memcpy (userid, RFIFOP (fd, 7), sizeof (userid)); + userid[sizeof (userid) - 1] = '\0'; + memcpy (error_message, RFIFOP (fd, 40), + sizeof (error_message)); + error_message[sizeof (error_message) - 1] = '\0'; + memcpy (lastlogin, RFIFOP (fd, 60), sizeof (lastlogin)); + lastlogin[sizeof (lastlogin) - 1] = '\0'; + memcpy (last_ip, RFIFOP (fd, 84), sizeof (last_ip)); + last_ip[sizeof (last_ip) - 1] = '\0'; + memcpy (email, RFIFOP (fd, 100), sizeof (email)); + email[sizeof (email) - 1] = '\0'; + connect_until_time = (time_t) RFIFOL (fd, 140); + ban_until_time = (time_t) RFIFOL (fd, 144); + memset (memo, '\0', sizeof (memo)); + strncpy (memo, RFIFOP (fd, 150), RFIFOW (fd, 148)); + if (RFIFOL (fd, 2) == -1) + { + if (defaultlanguage == 'F') + { + printf + ("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", + parameters); + ladmin_log + ("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", + parameters); + } + else + { + printf + ("Unabled to find the account [%s]. Account doesn't exist.\n", + parameters); + ladmin_log + ("Unabled to find the account [%s]. Account doesn't exist.\n", + parameters); + } + } + else if (strlen (userid) == 0) + { + if (defaultlanguage == 'F') + { + printf + ("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", + parameters); + ladmin_log + ("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", + parameters); + } + else + { + printf + ("Unabled to find the account [id: %s]. Account doesn't exist.\n", + parameters); + ladmin_log + ("Unabled to find the account [id: %s]. Account doesn't exist.\n", + parameters); + } + } + else + { + if (defaultlanguage == 'F') + { + ladmin_log + ("Réception d'information concernant un compte.\n"); + printf + ("Le compte a les caractéristiques suivantes:\n"); + } + else + { + ladmin_log + ("Receiving information about an account.\n"); + printf ("The account is set with:\n"); + } + if (RFIFOB (fd, 6) == 0) + { + printf (" Id: %d (non-GM)\n", RFIFOL (fd, 2)); + } + else + { + if (defaultlanguage == 'F') + { + printf (" Id: %d (GM niveau %d)\n", + RFIFOL (fd, 2), (int) RFIFOB (fd, 6)); + } + else + { + printf (" Id: %d (GM level %d)\n", + RFIFOL (fd, 2), (int) RFIFOB (fd, 6)); + } + } + if (defaultlanguage == 'F') + { + printf (" Nom: '%s'\n", userid); + if (RFIFOB (fd, 31) == 0) + printf (" Sexe: Femme\n"); + else if (RFIFOB (fd, 31) == 1) + printf (" Sexe: Male\n"); + else + printf (" Sexe: Serveur\n"); + } + else + { + printf (" Name: '%s'\n", userid); + if (RFIFOB (fd, 31) == 0) + printf (" Sex: Female\n"); + else if (RFIFOB (fd, 31) == 1) + printf (" Sex: Male\n"); + else + printf (" Sex: Server\n"); + } + printf (" E-mail: %s\n", email); + switch (RFIFOL (fd, 36)) + { + case 0: + if (defaultlanguage == 'F') + printf (" Statut: 0 [Compte Ok]\n"); + else + printf (" Statut: 0 [Account OK]\n"); + break; + case 1: + printf (" Statut: 1 [Unregistered ID]\n"); + break; + case 2: + printf (" Statut: 2 [Incorrect Password]\n"); + break; + case 3: + printf (" Statut: 3 [This ID is expired]\n"); + break; + case 4: + printf + (" Statut: 4 [Rejected from Server]\n"); + break; + case 5: + printf + (" Statut: 5 [You have been blocked by the GM Team]\n"); + break; + case 6: + printf + (" Statut: 6 [Your Game's EXE file is not the latest version]\n"); + break; + case 7: + printf + (" Statut: 7 [You are Prohibited to log in until %s]\n", + error_message); + break; + case 8: + printf + (" Statut: 8 [Server is jammed due to over populated]\n"); + break; + case 9: + printf (" Statut: 9 [No MSG]\n"); + break; + default: // 100 + printf + (" Statut: %d [This ID is totally erased]\n", + RFIFOL (fd, 36)); + break; + } + if (defaultlanguage == 'F') + { + if (ban_until_time == 0) + { + printf (" Banissement: non banni.\n"); + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (&ban_until_time)); + printf (" Banissement: jusqu'au %s.\n", + tmpstr); + } + if (RFIFOL (fd, 32) > 1) + printf (" Compteur: %d connexions.\n", + RFIFOL (fd, 32)); + else + printf (" Compteur: %d connexion.\n", + RFIFOL (fd, 32)); + printf (" Dernière connexion le: %s (ip: %s)\n", + lastlogin, last_ip); + if (connect_until_time == 0) + { + printf (" Limite de validité: illimité.\n"); + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (&connect_until_time)); + printf (" Limite de validité: jusqu'au %s.\n", + tmpstr); + } + } + else + { + if (ban_until_time == 0) + { + printf (" Banishment: not banished.\n"); + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (&ban_until_time)); + printf (" Banishment: until %s.\n", tmpstr); + } + if (RFIFOL (fd, 32) > 1) + printf (" Count: %d connections.\n", + RFIFOL (fd, 32)); + else + printf (" Count: %d connection.\n", + RFIFOL (fd, 32)); + printf (" Last connection at: %s (ip: %s)\n", + lastlogin, last_ip); + if (connect_until_time == 0) + { + printf (" Validity limit: unlimited.\n"); + } + else + { + char tmpstr[128]; + strftime (tmpstr, 24, date_format, + localtime (&connect_until_time)); + printf (" Validity limit: until %s.\n", + tmpstr); + } + } + printf (" Memo: '%s'\n", memo); + } + } + bytes_to_read = 0; + RFIFOSKIP (fd, 150 + RFIFOW (fd, 148)); + break; + + default: + printf + ("Remote administration has been disconnected (unknown packet).\n"); + ladmin_log ("'End of connection, unknown packet.\n"); + session[fd]->eof = 1; + return; + } + } + + // if we don't wait new packets, do the prompt + prompt (); +} + +//------------------------------------ +// Function to connect to login-server +//------------------------------------ +int Connect_login_server (void) +{ + if (defaultlanguage == 'F') + { + Iprintf ("Essai de connection au server de logins...\n"); + ladmin_log ("Essai de connection au server de logins...\n"); + } + else + { + Iprintf ("Attempt to connect to login-server...\n"); + ladmin_log ("Attempt to connect to login-server...\n"); + } + + if ((login_fd = make_connection (login_ip, loginserverport)) < 0) + return 0; + +#ifdef PASSWORDENC + if (passenc == 0) + { +#endif + WFIFOW (login_fd, 0) = 0x7918; // Request for administation login + WFIFOW (login_fd, 2) = 0; // no encrypted + memcpy (WFIFOP (login_fd, 4), loginserveradminpassword, 24); + WFIFOSET (login_fd, 28); + bytes_to_read = 1; + + if (defaultlanguage == 'F') + { + Iprintf ("Envoi du mot de passe...\n"); + ladmin_log ("Envoi du mot de passe...\n"); + } + else + { + Iprintf ("Sending of the password...\n"); + ladmin_log ("Sending of the password...\n"); + } +#ifdef PASSWORDENC + } + else + { + WFIFOW (login_fd, 0) = 0x791a; // Sending request about the coding key + WFIFOSET (login_fd, 2); + bytes_to_read = 1; + if (defaultlanguage == 'F') + { + Iprintf ("Demande de la clef MD5...\n"); + ladmin_log ("Demande de la clef MD5...\n"); + } + else + { + Iprintf ("Request about the MD5 key...\n"); + ladmin_log ("Request about the MD5 key...\n"); + } + } +#endif + + return 0; +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, français, deutsch, español +//------------------------------------------------- +int config_switch (const char *str) +{ + if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 + || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 + || strcasecmp (str, "si") == 0) + return 1; + if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 + || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) + return 0; + + return atoi (str); +} + +//----------------------------------- +// Reading general configuration file +//----------------------------------- +int ladmin_config_read (const char *cfgName) +{ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen_ (cfgName, "r"); + if (fp == NULL) + { + if (defaultlanguage == 'F') + { + printf ("\033[0mFichier de configuration (%s) non trouvé.\n", + cfgName); + } + else + { + printf ("\033[0mConfiguration file (%s) not found.\n", cfgName); + } + return 1; + } + + if (defaultlanguage == 'F') + { + Iprintf + ("\033[0m---Début de lecture du fichier de configuration Ladmin (%s)\n", + cfgName); + } + else + { + Iprintf + ("\033[0m---Start reading of Ladmin configuration file (%s)\n", + cfgName); + } + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof (line) - 1] = '\0'; + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) == 2) + { + remove_control_chars (w1); + remove_control_chars (w2); + + if (strcasecmp (w1, "login_ip") == 0) + { + struct hostent *h = gethostbyname (w2); + if (h != NULL) + { + if (defaultlanguage == 'F') + { + Iprintf + ("Adresse du serveur de logins: %s -> %d.%d.%d.%d\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]); + } + else + { + Iprintf + ("Login server IP address: %s -> %d.%d.%d.%d\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 (loginserverip, "%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]); + } + else + memcpy (loginserverip, w2, 16); + } + else if (strcasecmp (w1, "login_port") == 0) + { + loginserverport = atoi (w2); + } + else if (strcasecmp (w1, "admin_pass") == 0) + { + strncpy (loginserveradminpassword, w2, + sizeof (loginserveradminpassword)); + loginserveradminpassword[sizeof (loginserveradminpassword) - + 1] = '\0'; +#ifdef PASSWORDENC + } + else if (strcasecmp (w1, "passenc") == 0) + { + passenc = atoi (w2); + if (passenc < 0 || passenc > 2) + passenc = 0; +#endif + } + else if (strcasecmp (w1, "defaultlanguage") == 0) + { + if (w2[0] == 'F' || w2[0] == 'E') + defaultlanguage = w2[0]; + } + else if (strcasecmp (w1, "ladmin_log_filename") == 0) + { + strncpy (ladmin_log_filename, w2, + sizeof (ladmin_log_filename)); + ladmin_log_filename[sizeof (ladmin_log_filename) - 1] = '\0'; + } + else if (strcasecmp (w1, "date_format") == 0) + { // note: never have more than 19 char for the date! + switch (atoi (w2)) + { + case 0: + strcpy (date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 + break; + case 1: + strcpy (date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 + break; + case 2: + strcpy (date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 + break; + case 3: + strcpy (date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 + break; + } + } + else if (strcasecmp (w1, "import") == 0) + { + ladmin_config_read (w2); + } + } + } + fclose_ (fp); + + login_ip = inet_addr (loginserverip); + + if (defaultlanguage == 'F') + { + Iprintf ("---Lecture du fichier de configuration Ladmin terminée.\n"); + } + else + { + Iprintf ("---End reading of Ladmin configuration file.\n"); + } + + return 0; +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void term_func (void) +{ + + if (already_exit_function == 0) + { + delete_session (login_fd); + + if (defaultlanguage == 'F') + { + Iprintf + ("\033[0m----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); + ladmin_log + ("----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); + } + else + { + Iprintf + ("\033[0m----End of Ladmin (normal end with closing of all files).\n"); + ladmin_log + ("----End of Ladmin (normal end with closing of all files).\n"); + } + + already_exit_function = 1; + } +} + +//------------------------ +// Main function of ladmin +//------------------------ +int do_init (int argc, char **argv) +{ + eathena_interactive_session = isatty (0); + // read ladmin configuration + ladmin_config_read ((argc > 1) ? argv[1] : LADMIN_CONF_NAME); + + ladmin_log (""); + if (defaultlanguage == 'F') + { + ladmin_log ("Fichier de configuration lu.\n"); + } + else + { + ladmin_log ("Configuration file readed.\n"); + } + + srand (time (NULL)); + + set_defaultparse (parse_fromlogin); + + if (defaultlanguage == 'F') + { + Iprintf ("Outil d'administration à distance de eAthena.\n"); + Iprintf ("(pour eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, + ATHENA_MINOR_VERSION, ATHENA_REVISION); + } + else + { + Iprintf ("EAthena login-server administration tool.\n"); + Iprintf ("(for eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, + ATHENA_MINOR_VERSION, ATHENA_REVISION); + } + + if (defaultlanguage == 'F') + { + ladmin_log ("Ladmin est prêt.\n"); + Iprintf ("Ladmin est \033[1;32mprêt\033[0m.\n\n"); + } + else + { + ladmin_log ("Ladmin is ready.\n"); + Iprintf ("Ladmin is \033[1;32mready\033[0m.\n\n"); + } + + Connect_login_server (); + + return 0; +} diff --git a/src/ladmin/ladmin.h b/src/ladmin/ladmin.h deleted file mode 100644 index dc1efe6..0000000 --- a/src/ladmin/ladmin.h +++ /dev/null @@ -1,11 +0,0 @@ -// $Id: ladmin.h,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ -#ifndef _LADMIN_H_ -#define _LADMIN_H_ - -#define LADMIN_CONF_NAME "conf/ladmin_athena.conf" -#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. - // It is 1 at the time of passwordencrypt. - // It is made into 2 at the time of passwordencrypt2. - // When it is made 3, it corresponds to both. - -#endif diff --git a/src/ladmin/ladmin.hpp b/src/ladmin/ladmin.hpp new file mode 100644 index 0000000..77d7fe4 --- /dev/null +++ b/src/ladmin/ladmin.hpp @@ -0,0 +1,11 @@ +// $Id: ladmin.h,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ +#ifndef LADMIN_HPP +#define LADMIN_HPP + +#define LADMIN_CONF_NAME "conf/ladmin_athena.conf" +#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. + +#endif diff --git a/src/login/login.c b/src/login/login.c deleted file mode 100644 index cd46049..0000000 --- a/src/login/login.c +++ /dev/null @@ -1,5038 +0,0 @@ -// $Id: login.c,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ -// new version of the login-server by [Yor] - -#include <sys/types.h> -#include <sys/socket.h> -#include <stdio.h> -#include <stdlib.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> -#include <sys/ioctl.h> -#include <sys/stat.h> // for stat/lstat/fstat -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <string.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <sys/wait.h> - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "login.h" -#include "../common/mmo.h" -#include "../common/version.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/mt_rand.h" - -#include "../common/md5calc.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -int account_id_count = START_ACCOUNT_NUM; -int server_num; -int new_account_flag = 0; -int login_port = 6900; -char lan_char_ip[16]; -int subneti[4]; -int subnetmaski[4]; -char update_host[128] = ""; -char main_server[20] = ""; - -char account_filename[1024] = "save/account.txt"; -char GM_account_filename[1024] = "conf/GM_account.txt"; -char login_log_filename[1024] = "log/login.log"; -char login_log_unknown_packets_filename[1024] = - "log/login_unknown_packets.log"; -char date_format[32] = "%Y-%m-%d %H:%M:%S"; -int save_unknown_packets = 0; -long creation_time_GM_account_file; -int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15) - -int display_parse_login = 0; // 0: no, 1: yes -int display_parse_admin = 0; // 0: no, 1: yes -int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets - -struct mmo_char_server server[MAX_SERVERS]; -int server_fd[MAX_SERVERS]; -int server_freezeflag[MAX_SERVERS]; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed -int anti_freeze_enable = 0; -int ANTI_FREEZE_INTERVAL = 15; - -int login_fd; - -enum -{ - ACO_DENY_ALLOW = 0, - ACO_ALLOW_DENY, - ACO_MUTUAL_FAILTURE, - ACO_STRSIZE = 128, -}; - -int access_order = ACO_DENY_ALLOW; -int access_allownum = 0; -int access_denynum = 0; -char *access_allow = NULL; -char *access_deny = NULL; - -int access_ladmin_allownum = 0; -char *access_ladmin_allow = NULL; - -int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server -int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. -int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) -int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) - -struct login_session_data -{ - int md5keylen; - char md5key[20]; -}; - -#define AUTH_FIFO_SIZE 256 -struct -{ - int account_id, login_id1, login_id2; - int ip, sex, delflag; -} auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - -struct auth_dat -{ - int account_id, sex; - char userid[24], pass[40], lastlogin[24]; - int logincount; - int state; // packet 0x006a value + 1 (0: compte OK) - char email[40]; // e-mail (by default: a@a.com) - char error_message[20]; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - char last_ip[16]; // save of last IP of connection - char memo[255]; // a memo field - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -} *auth_dat; - -int auth_num = 0, auth_max = 0; - -int admin_state = 0; -char admin_pass[24] = ""; -char gm_pass[64] = ""; -int level_new_gm = 60; - -static struct dbt *gm_account_db; - -pid_t pid = 0; // For forked DB writes - - -#define VERSION_2_UPDATEHOST 0x01 // client supports updatehost -#define VERSION_2_SERVERORDER 0x02 // send servers in forward order -//------------------------------ -// Writing function of logs file -//------------------------------ -int login_log (char *fmt, ...) -{ - FILE *logfp; - va_list ap; - struct timeval tv; - char tmpstr[2048]; - - va_start (ap, fmt); - - logfp = fopen_ (login_log_filename, "a"); - if (logfp) - { - if (fmt[0] == '\0') // jump a line if no message - fprintf (logfp, "\n"); - else - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 24, date_format, gmtime (&(tv.tv_sec))); - sprintf (tmpstr + strlen (tmpstr), ".%03d: %s", - (int) tv.tv_usec / 1000, fmt); - vfprintf (logfp, tmpstr, ap); - } - fclose_ (logfp); - } - - va_end (ap); - return 0; -} - -//---------------------------------------------------------------------- -// Determine if an account (id) is a GM account -// and returns its level (or 0 if it isn't a GM account or if not found) -//---------------------------------------------------------------------- -int isGM (int account_id) -{ - struct gm_account *p = (struct gm_account*) numdb_search (gm_account_db, account_id); - if (p == NULL) - return 0; - return p->level; -} - -//------------------------------------------------------- -// Reading function of GM accounts file (and their level) -//------------------------------------------------------- -int read_gm_account (void) -{ - char line[512]; - struct gm_account *p; - FILE *fp; - int c = 0; - int GM_level; - struct stat file_stat; - - free (gm_account_db); - gm_account_db = numdb_init (); - - // get last modify time/date - if (stat (GM_account_filename, &file_stat)) - creation_time_GM_account_file = 0; // error - else - creation_time_GM_account_file = file_stat.st_mtime; - - if ((fp = fopen_ (GM_account_filename, "r")) == NULL) - { - printf ("read_gm_account: GM accounts file [%s] not found.\n", - GM_account_filename); - printf - (" Actually, there is no GM accounts on the server.\n"); - login_log ("read_gm_account: GM accounts file [%s] not found.\n", - GM_account_filename); - login_log - (" Actually, there is no GM accounts on the server.\n"); - return 1; - } - // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) - // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - while (fgets (line, sizeof (line) - 1, fp) && c < 4000) - { - if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' - || line[0] == '\n' || line[0] == '\r') - continue; - CREATE (p, struct gm_account, 1); - if (sscanf (line, "%d %d", &p->account_id, &p->level) != 2 - && sscanf (line, "%d: %d", &p->account_id, &p->level) != 2) - printf - ("read_gm_account: file [%s], invalid 'id_acount level' format.\n", - GM_account_filename); - else if (p->level <= 0) - printf - ("read_gm_account: file [%s] %dth account (invalid level [0 or negative]: %d).\n", - GM_account_filename, c + 1, p->level); - else - { - if (p->level > 99) - { - printf - ("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", - GM_account_filename, c + 1, p->level); - p->level = 99; - } - if ((GM_level = isGM (p->account_id)) > 0) - { // if it's not a new account - if (GM_level == p->level) - printf - ("read_gm_account: GM account %d defined twice (same level: %d).\n", - p->account_id, p->level); - else - printf - ("read_gm_account: GM account %d defined twice (levels: %d and %d).\n", - p->account_id, GM_level, p->level); - } - if (GM_level != p->level) - { // if new account or new level - numdb_insert (gm_account_db, p->account_id, p); - //printf("GM account:%d, level: %d->%d\n", p->account_id, GM_level, p->level); - if (GM_level == 0) - { // if new account - c++; - if (c >= 4000) - { - printf - ("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); - login_log - ("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); - } - } - } - } - } - fclose_ (fp); - - printf ("read_gm_account: file '%s' readed (%d GM accounts found).\n", - GM_account_filename, c); - login_log ("read_gm_account: file '%s' readed (%d GM accounts found).\n", - GM_account_filename, c); - - return 0; -} - -//-------------------------------------------------------------- -// Test of the IP mask -// (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) -//-------------------------------------------------------------- -int check_ipmask (unsigned int ip, const unsigned char *str) -{ - unsigned int mask = 0, i = 0, m, ip2, a0, a1, a2, a3; - unsigned char *p = (unsigned char *) &ip2, *p2 = (unsigned char *) &mask; - - if (sscanf (str, "%d.%d.%d.%d/%n", &a0, &a1, &a2, &a3, &i) != 4 || i == 0) - return 0; - p[0] = a0; - p[1] = a1; - p[2] = a2; - p[3] = a3; - - if (sscanf (str + i, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) == 4) - { - p2[0] = a0; - p2[1] = a1; - p2[2] = a2; - p2[3] = a3; - mask = ntohl (mask); - } - else if (sscanf (str + i, "%d", &m) == 1 && m >= 0 && m <= 32) - { - for (i = 0; i < m && i < 32; i++) - mask = (mask >> 1) | 0x80000000; - } - else - { - printf ("check_ipmask: invalid mask [%s].\n", str); - return 0; - } - -// printf("Tested IP: %08x, network: %08x, network mask: %08x\n", -// (unsigned int)ntohl(ip), (unsigned int)ntohl(ip2), (unsigned int)mask); - return ((ntohl (ip) & mask) == (ntohl (ip2) & mask)); -} - -//--------------------- -// Access control by IP -//--------------------- -int check_ip (unsigned int ip) -{ - int i; - unsigned char *p = (unsigned char *) &ip; - char buf[32]; - enum - { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; - - if (access_allownum == 0 && access_denynum == 0) - return 1; // When there is no restriction, all IP are authorised. - -// + 012.345.: front match form, or -// all: all IP are matched, or -// 012.345.678.901/24: network form (mask with # of bits), or -// 012.345.678.901/255.255.255.0: network form (mask with ip mask) -// + Note about the DNS resolution (like www.ne.jp, etc.): -// There is no guarantee to have an answer. -// If we have an answer, there is no guarantee to have a 100% correct value. -// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. -// So, DNS notation isn't authorised for ip checking. - sprintf (buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); - - for (i = 0; i < access_allownum; i++) - { - const char *p = access_allow + i * ACO_STRSIZE; - if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) - { - flag = ACF_ALLOW; - if (access_order == ACO_ALLOW_DENY) - return 1; // With 'allow, deny' (deny if not allow), allow has priority - break; - } - } - - for (i = 0; i < access_denynum; i++) - { - const char *p = access_deny + i * ACO_STRSIZE; - if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) - { - flag = ACF_DENY; - return 0; // At this point, if it's 'deny', we refuse connection. - break; - } - } - - return (flag == ACF_ALLOW || access_order == ACO_DENY_ALLOW) ? 1 : 0; - // With 'mutual-failture', only 'allow' and non 'deny' IP are authorised. - // A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised. - // So, it's disapproval if you have no description at the time of 'mutual-failture'. - // With 'deny,allow' (allow if not deny), because here it's not deny, we authorise. -} - -//-------------------------------- -// Access control by IP for ladmin -//-------------------------------- -int check_ladminip (unsigned int ip) -{ - int i; - unsigned char *p = (unsigned char *) &ip; - char buf[32]; - - if (access_ladmin_allownum == 0) - return 1; // When there is no restriction, all IP are authorised. - -// + 012.345.: front match form, or -// all: all IP are matched, or -// 012.345.678.901/24: network form (mask with # of bits), or -// 012.345.678.901/255.255.255.0: network form (mask with ip mask) -// + Note about the DNS resolution (like www.ne.jp, etc.): -// There is no guarantee to have an answer. -// If we have an answer, there is no guarantee to have a 100% correct value. -// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. -// So, DNS notation isn't authorised for ip checking. - sprintf (buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); - - for (i = 0; i < access_ladmin_allownum; i++) - { - const char *p = access_ladmin_allow + i * ACO_STRSIZE; - if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) - { - return 1; - } - } - - return 0; -} - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars (unsigned char *str) -{ - int i; - int change = 0; - - for (i = 0; str[i]; i++) - { - if (str[i] < 32) - { - str[i] = '_'; - change = 1; - } - } - - return change; -} - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -int e_mail_check (unsigned char *email) -{ - char ch; - unsigned 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; -} - -//----------------------------------------------- -// Search an account id -// (return account index or -1 (if not found)) -// If exact account name is not found, -// the function checks without case sensitive -// and returns index if only 1 account is found -// and similar to the searched name. -//----------------------------------------------- -int search_account_index (char *account_name) -{ - int i, quantity, index; - - quantity = 0; - index = -1; - for (i = 0; i < auth_num; i++) - { - // Without case sensitive check (increase the number of similar account names found) - if (strcasecmp (auth_dat[i].userid, account_name) == 0) - { - // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp (auth_dat[i].userid, account_name) == 0) - return i; - quantity++; - index = i; - } - } - // Here, the exact account name is not found - // We return the found index of a similar account ONLY if there is 1 similar account - if (quantity == 1) - return index; - - // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found - return -1; -} - -//-------------------------------------------------------- -// Create a string to save the account in the account file -//-------------------------------------------------------- -int mmo_auth_tostr (char *str, struct auth_dat *p) -{ - int i; - char *str_p = str; - - str_p += sprintf (str_p, "%d\t%s\t%s\t%s\t%c\t%d\t%d\t" - "%s\t%s\t%ld\t%s\t%s\t%ld\t", - p->account_id, p->userid, p->pass, p->lastlogin, - (p->sex == 2) ? 'S' : (p->sex ? 'M' : 'F'), - p->logincount, p->state, - p->email, p->error_message, - p->connect_until_time, p->last_ip, p->memo, - p->ban_until_time); - - for (i = 0; i < p->account_reg2_num; i++) - if (p->account_reg2[i].str[0]) - str_p += - sprintf (str_p, "%s,%d ", p->account_reg2[i].str, - p->account_reg2[i].value); - - return 0; -} - -//--------------------------------- -// Reading of the accounts database -//--------------------------------- -int mmo_auth_init (void) -{ - FILE *fp; - int account_id, logincount, state, n, i, j, v; - char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, - email[2048], error_message[2048], last_ip[2048], memo[2048]; - time_t ban_until_time; - time_t connect_until_time; - char str[2048]; - int GM_count = 0; - int server_count = 0; - - CREATE (auth_dat, struct auth_dat, 256); - auth_max = 256; - - fp = fopen_ (account_filename, "r"); - if (fp == NULL) - { - // no account file -> no account -> no login, including char-server (ERROR) - printf - ("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", - account_filename); - return 0; - } - - while (fgets (line, sizeof (line) - 1, fp) != NULL) - { - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof (line) - 1] = '\0'; - p = line; - - // database version reading (v2) - if (((i = sscanf (line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", - &account_id, userid, pass, lastlogin, &sex, - &logincount, &state, email, error_message, - &connect_until_time, last_ip, memo, &ban_until_time, - &n)) == 13 && line[n] == '\t') - || - ((i = - sscanf (line, - "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", &account_id, - userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, - memo, &n)) == 12 && line[n] == '\t')) - { - n = n + 1; - - // Some checks - if (account_id > END_ACCOUNT_NUM) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", - END_ACCOUNT_NUM); - printf - (" account id #%d -> account not read (saved in log file).\033[0m\n", - account_id); - login_log - ("mmmo_auth_init: ******Error: an account has an id higher than %d.\n", - END_ACCOUNT_NUM); - login_log - (" account id #%d -> account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - continue; - } - userid[23] = '\0'; - remove_control_chars (userid); - for (j = 0; j < auth_num; j++) - { - if (auth_dat[j].account_id == account_id) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - printf - (" account id #%d -> new account not read (saved in log file).\033[0m\n", - account_id); - login_log - ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - login_log - (" account id #%d -> new account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - break; - } - else if (strcmp (auth_dat[j].userid, userid) == 0) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); - printf (" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. - printf - (" Account saved in log file.\033[0m\n"); - login_log - ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - login_log - (" account id #%d -> new account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) - { - auth_max += 256; - RECREATE (auth_dat, struct auth_dat, auth_max); - } - - memset (&auth_dat[auth_num], '\0', sizeof (struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy (auth_dat[auth_num].userid, userid, 24); - - memo[254] = '\0'; - remove_control_chars (memo); - strncpy (auth_dat[auth_num].memo, memo, 255); - - pass[39] = '\0'; - remove_control_chars (pass); - // If a password is not encrypted, we encrypt it now. - // A password beginning with ! and - in the memo field is our magic - if (pass[0] != '!' && memo[0] == '-') { - strcpy(auth_dat[auth_num].pass, MD5_saltcrypt(pass, make_salt())); - auth_dat[auth_num].memo[0] = '!'; - printf("encrypting pass: %s %s\n", pass, auth_dat[auth_num].pass); - } - else - strcpy(auth_dat[auth_num].pass, pass); - - lastlogin[23] = '\0'; - remove_control_chars (lastlogin); - strncpy (auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' - || sex == 's') ? 2 : (sex == 'M' - || sex == 'm'); - - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - - if (e_mail_check (email) == 0) - { - printf - ("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", - auth_dat[auth_num].userid, - auth_dat[auth_num].account_id); - strncpy (auth_dat[auth_num].email, "a@a.com", 40); - } - else - { - remove_control_chars (email); - strncpy (auth_dat[auth_num].email, email, 40); - } - - error_message[19] = '\0'; - remove_control_chars (error_message); - if (error_message[0] == '\0' || state != 7) - { // 7, because state is packet 0x006a value + 1 - strncpy (auth_dat[auth_num].error_message, "-", 20); - } - else - { - strncpy (auth_dat[auth_num].error_message, error_message, 20); - } - - if (i == 13) - auth_dat[auth_num].ban_until_time = ban_until_time; - else - auth_dat[auth_num].ban_until_time = 0; - - auth_dat[auth_num].connect_until_time = connect_until_time; - - last_ip[15] = '\0'; - remove_control_chars (last_ip); - strncpy (auth_dat[auth_num].last_ip, last_ip, 16); - - for (j = 0; j < ACCOUNT_REG2_NUM; j++) - { - p += n; - if (sscanf (p, "%[^\t,],%d %n", str, &v, &n) != 2) - { - // We must check if a str is void. If it's, we can continue to read other REG2. - // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) - if (p[0] == ',' && sscanf (p, ",%d %n", &v, &n) == 1) - { - j--; - continue; - } - else - break; - } - str[31] = '\0'; - remove_control_chars (str); - strncpy (auth_dat[auth_num].account_reg2[j].str, str, 32); - auth_dat[auth_num].account_reg2[j].value = v; - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM (account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - // Old athena database version reading (v1) - } - else if ((i = - sscanf (line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", - &account_id, userid, pass, lastlogin, &sex, - &logincount, &state, &n)) >= 5) - { - if (account_id > END_ACCOUNT_NUM) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", - END_ACCOUNT_NUM); - printf - (" account id #%d -> account not read (saved in log file).\033[0m\n", - account_id); - login_log - ("mmmo_auth_init: ******Error: an account has an id higher than %d.\n", - END_ACCOUNT_NUM); - login_log - (" account id #%d -> account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - continue; - } - userid[23] = '\0'; - remove_control_chars (userid); - for (j = 0; j < auth_num; j++) - { - if (auth_dat[j].account_id == account_id) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - printf - (" account id #%d -> new account not read (saved in log file).\033[0m\n", - account_id); - login_log - ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - login_log - (" account id #%d -> new account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - break; - } - else if (strcmp (auth_dat[j].userid, userid) == 0) - { - printf - ("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); - printf (" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. - printf - (" Account saved in log file.\033[0m\n"); - login_log - ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); - login_log - (" account id #%d -> new account not read (saved in next line):\n", - account_id); - login_log ("%s", line); - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) - { - auth_max += 256; - RECREATE (auth_dat, struct auth_dat, auth_max); - } - - memset (&auth_dat[auth_num], '\0', sizeof (struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy (auth_dat[auth_num].userid, userid, 24); - - lastlogin[23] = '\0'; - remove_control_chars (lastlogin); - strncpy (auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' - || sex == 's') ? 2 : (sex == 'M' - || sex == 'm'); - - if (i >= 6) - { - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - } - else - auth_dat[auth_num].logincount = 0; - - if (i >= 7) - { - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - } - else - auth_dat[auth_num].state = 0; - - // Initialization of new data - strncpy (auth_dat[auth_num].email, "a@a.com", 40); - strncpy (auth_dat[auth_num].error_message, "-", 20); - auth_dat[auth_num].ban_until_time = 0; - auth_dat[auth_num].connect_until_time = 0; - strncpy (auth_dat[auth_num].last_ip, "-", 16); - strncpy (auth_dat[auth_num].memo, "!", 255); - - for (j = 0; j < ACCOUNT_REG2_NUM; j++) - { - p += n; - if (sscanf (p, "%[^\t,],%d %n", str, &v, &n) != 2) - { - // We must check if a str is void. If it's, we can continue to read other REG2. - // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) - if (p[0] == ',' && sscanf (p, ",%d %n", &v, &n) == 1) - { - j--; - continue; - } - else - break; - } - str[31] = '\0'; - remove_control_chars (str); - strncpy (auth_dat[auth_num].account_reg2[j].str, str, 32); - auth_dat[auth_num].account_reg2[j].value = v; - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM (account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - } - else - { - i = 0; - if (sscanf (line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && - i > 0 && account_id > account_id_count) - account_id_count = account_id; - } - } - fclose_ (fp); - - if (auth_num == 0) - { - printf ("mmo_auth_init: No account found in %s.\n", account_filename); - sprintf (line, "No account found in %s.", account_filename); - } - else - { - if (auth_num == 1) - { - printf ("mmo_auth_init: 1 account read in %s,\n", - account_filename); - sprintf (line, "1 account read in %s,", account_filename); - } - else - { - printf ("mmo_auth_init: %d accounts read in %s,\n", auth_num, - account_filename); - sprintf (line, "%d accounts read in %s,", auth_num, - account_filename); - } - if (GM_count == 0) - { - printf (" of which is no GM account, and "); - sprintf (str, "%s of which is no GM account and", line); - } - else if (GM_count == 1) - { - printf (" of which is 1 GM account, and "); - sprintf (str, "%s of which is 1 GM account and", line); - } - else - { - printf (" of which is %d GM accounts, and ", - GM_count); - sprintf (str, "%s of which is %d GM accounts and", line, - GM_count); - } - if (server_count == 0) - { - printf ("no server account ('S').\n"); - sprintf (line, "%s no server account ('S').", str); - } - else if (server_count == 1) - { - printf ("1 server account ('S').\n"); - sprintf (line, "%s 1 server account ('S').", str); - } - else - { - printf ("%d server accounts ('S').\n", server_count); - sprintf (line, "%s %d server accounts ('S').", str, server_count); - } - } - login_log ("%s\n", line); - - return 0; -} - -//------------------------------------------ -// Writing of the accounts database file -// (accounts are sorted by id before save) -//------------------------------------------ -void mmo_auth_sync (void) -{ - FILE *fp; - int i, j, k, lock; - int id[auth_num]; - char line[65536]; - - // Sorting before save - for (i = 0; i < auth_num; i++) - { - id[i] = i; - for (j = 0; j < i; j++) - { - if (auth_dat[i].account_id < auth_dat[id[j]].account_id) - { - for (k = i; k > j; k--) - id[k] = id[k - 1]; - id[j] = i; // id[i] - break; - } - } - } - - // Data save - fp = lock_fopen (account_filename, &lock); - if (fp == NULL) - return; - fprintf (fp, - "// Accounts file: here are saved all information about the accounts.\n"); - fprintf (fp, - "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); - fprintf (fp, "// Some explanations:\n"); - fprintf (fp, - "// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"); - fprintf (fp, "// account password: between 4 to 23 char\n"); - fprintf (fp, - "// sex : M or F for normal accounts, S for server accounts\n"); - fprintf (fp, - "// state : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n"); - fprintf (fp, - "// email : between 3 to 39 char (a@a.com is like no email)\n"); - fprintf (fp, - "// error message : text for the state 7: 'Your are Prohibited to login until <text>'. Max 19 char\n"); - fprintf (fp, - "// valitidy time : 0: unlimited account, <other value>: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); - fprintf (fp, "// memo field : max 254 char\n"); - fprintf (fp, - "// ban time : 0: no ban, <other value>: banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); - for (i = 0; i < auth_num; i++) - { - k = id[i]; // use of sorted index - if (auth_dat[k].account_id < 0) - continue; - - mmo_auth_tostr (line, &auth_dat[k]); - fprintf (fp, "%s\n", line); - } - fprintf (fp, "%d\t%%newid%%\n", account_id_count); - - lock_fclose (fp, account_filename, &lock); - - return; -} - -// We want to sync the DB to disk as little as possible as it's fairly -// resource intensive. therefore most player-triggerable events that -// update the account DB will not immideately trigger a save. Instead -// we save periodicly on a timer. -//----------------------------------------------------- -void check_auth_sync (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - if (pid != 0) - { - int status; - pid_t temp = waitpid (pid, &status, WNOHANG); - - // Need to check status too? - if (temp == 0) - { - return; - } - } - - // This can take a lot of time. Fork a child to handle the work and return at once - // If we're unable to fork just continue running the function normally - if ((pid = fork ()) > 0) - return; - - mmo_auth_sync (); - - // If we're a child we should suicide now. - if (pid == 0) - _exit (0); - - return; -} - -//-------------------------------------------------------------------- -// Packet send to all char-servers, except one (wos: without our self) -//-------------------------------------------------------------------- -int charif_sendallwos (int sfd, unsigned char *buf, unsigned int len) -{ - int i, c; - - for (i = 0, c = 0; i < MAX_SERVERS; i++) - { - int fd; - if ((fd = server_fd[i]) >= 0 && fd != sfd) - { - memcpy (WFIFOP (fd, 0), buf, len); - WFIFOSET (fd, len); - c++; - } - } - return c; -} - -//----------------------------------------------------- -// Send GM accounts to all char-server -//----------------------------------------------------- -void send_GM_accounts (void) -{ - int i; - char buf[32000]; - int GM_value; - int len; - - len = 4; - WBUFW (buf, 0) = 0x2732; - for (i = 0; i < auth_num; i++) - // send only existing accounts. We can not create a GM account when server is online. - if ((GM_value = isGM (auth_dat[i].account_id)) > 0) - { - WBUFL (buf, len) = auth_dat[i].account_id; - WBUFB (buf, len + 4) = (unsigned char) GM_value; - len += 5; - } - WBUFW (buf, 2) = len; - charif_sendallwos (-1, buf, len); - - return; -} - -//----------------------------------------------------- -// Check if GM file account have been changed -//----------------------------------------------------- -void check_GM_file (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct stat file_stat; - long new_time; - - // if we would not check - if (gm_account_filename_check_timer < 1) - return; - - // get last modify time/date - if (stat (GM_account_filename, &file_stat)) - new_time = 0; // error - else - new_time = file_stat.st_mtime; - - if (new_time != creation_time_GM_account_file) - { - read_gm_account (); - send_GM_accounts (); - } -} - -//------------------------------------- -// Account creation (with e-mail check) -//------------------------------------- -int mmo_auth_new (struct mmo_account *account, char sex, char *email) -{ - time_t timestamp, timestamp_temp; - struct tm *tmtime; - int i = auth_num; - - if (auth_num >= auth_max) - { - auth_max += 256; - RECREATE (auth_dat, struct auth_dat, auth_max); - } - - memset (&auth_dat[i], '\0', sizeof (struct auth_dat)); - - while (isGM (account_id_count) > 0) - account_id_count++; - - auth_dat[i].account_id = account_id_count++; - - strncpy (auth_dat[i].userid, account->userid, 24); - auth_dat[i].userid[23] = '\0'; - - strcpy(auth_dat[i].pass, MD5_saltcrypt(account->passwd, make_salt())); - auth_dat[i].pass[39] = '\0'; - - memcpy (auth_dat[i].lastlogin, "-", 2); - - auth_dat[i].sex = (sex == 'M'); - - auth_dat[i].logincount = 0; - - auth_dat[i].state = 0; - - if (e_mail_check (email) == 0) - strncpy (auth_dat[i].email, "a@a.com", 40); - else - strncpy (auth_dat[i].email, email, 40); - - strncpy (auth_dat[i].error_message, "-", 20); - - auth_dat[i].ban_until_time = 0; - - if (start_limited_time < 0) - auth_dat[i].connect_until_time = 0; // unlimited - else - { // limited time - timestamp = time (NULL) + start_limited_time; - // double conversion to be sure that it is possible - tmtime = gmtime (×tamp); - timestamp_temp = mktime (tmtime); - if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour) - auth_dat[i].connect_until_time = timestamp_temp; - else - auth_dat[i].connect_until_time = 0; // unlimited - } - - strncpy (auth_dat[i].last_ip, "-", 16); - - strncpy (auth_dat[i].memo, "!", 255); - - auth_dat[i].account_reg2_num = 0; - - auth_num++; - - return (account_id_count - 1); -} - -//--------------------------------------- -// Check/authentification of a connection -//--------------------------------------- -int mmo_auth (struct mmo_account *account, int fd) -{ - int i; - struct timeval tv; - char tmpstr[256]; - int len, newaccount = 0; -#ifdef PASSWDENC - char md5str[64], md5bin[32]; -#endif - char ip[16]; - unsigned char *sin_addr = - (unsigned char *) &session[fd]->client_addr.sin_addr; - - sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], - sin_addr[3]); - - len = strlen (account->userid) - 2; - // Account creation with _M/_F - if (account->passwdenc == 0 && account->userid[len] == '_' && - (account->userid[len + 1] == 'F' || account->userid[len + 1] == 'M') - && new_account_flag == 1 && account_id_count <= END_ACCOUNT_NUM - && len >= 4 && strlen (account->passwd) >= 4) - { - if (new_account_flag == 1) - newaccount = 1; - account->userid[len] = '\0'; - } - - // Strict account search - for (i = 0; i < auth_num; i++) - { - if (strcmp (account->userid, auth_dat[i].userid) == 0) - break; - } - // if there is no creation request and strict account search fails, we do a no sensitive case research for index - if (newaccount == 0 && i == auth_num) - { - i = search_account_index (account->userid); - if (i == -1) - i = auth_num; - else - memcpy (account->userid, auth_dat[i].userid, 24); // for the possible tests/checks afterwards (copy correcte sensitive case). - } - - if (i != auth_num) - { - int encpasswdok = 0; - struct login_session_data *ld; - if (newaccount) - { - login_log - ("Attempt of creation of an already existant account (account: %s_%c, ip: %s)\n", - account->userid, account->userid[len + 1], ip); - return 9; // 9 = Account already exists - } - ld = (struct login_session_data*) session[fd]->session_data; -#ifdef PASSWORDENC - if (account->passwdenc > 0) - { - int j = account->passwdenc; - if (!ld) - { - login_log ("Md5 key not created (account: %s, ip: %s)\n", - account->userid, ip); - return 1; // 1 = Incorrect Password - } - if (j > 2) - j = 1; - do - { - if (j == 1) - { - strncpy (md5str, ld->md5key, sizeof (ld->md5key)); // 20 - strcat (md5str, auth_dat[i].pass); // 24 - } - else if (j == 2) - { - strncpy (md5str, auth_dat[i].pass, sizeof (auth_dat[i].pass)); // 24 - strcat (md5str, ld->md5key); // 20 - } - else - md5str[0] = '\0'; - md5str[sizeof (md5str) - 1] = '\0'; // 64 - MD5_String2binary (md5str, md5bin); - encpasswdok = (memcmp (account->passwd, md5bin, 16) == 0); - } - while (j < 2 && !encpasswdok && (j++) != account->passwdenc); -// printf("key[%s] md5 [%s] ", md5key, md5); -// printf("client [%s] accountpass [%s]\n", account->passwd, auth_dat[i].pass); - } -#endif - if ((!pass_ok (account->passwd, auth_dat[i].pass)) && !encpasswdok) - { - if (account->passwdenc == 0) - login_log - ("Invalid password (account: %s, ip: %s)\n", - account->userid, ip); - -#ifdef PASSWORDENC - else - { - char logbuf[512], *p = logbuf; - int j; - p += sprintf (p, - "Invalid password (account: %s, received md5[", - account->userid); - for (j = 0; j < 16; j++) - p += sprintf (p, "%02x", - ((unsigned char *) account->passwd)[j]); - p += sprintf (p, "] calculated md5["); - for (j = 0; j < 16; j++) - p += sprintf (p, "%02x", ((unsigned char *) md5bin)[j]); - p += sprintf (p, "] md5 key["); - for (j = 0; j < ld->md5keylen; j++) - p += sprintf (p, "%02x", - ((unsigned char *) ld->md5key)[j]); - p += sprintf (p, "], ip: %s)\n", ip); - login_log (logbuf); - } -#endif - return 1; // 1 = Incorrect Password - } - - if (auth_dat[i].state) - { - login_log - ("Connection refused (account: %s, state: %d, ip: %s)\n", - account->userid, auth_dat[i].state, - ip); - switch (auth_dat[i].state) - { // packet 0x006a value + 1 - case 1: // 0 = Unregistered ID - case 2: // 1 = Incorrect Password - case 3: // 2 = This ID is expired - case 4: // 3 = Rejected from Server - case 5: // 4 = You have been blocked by the GM Team - case 6: // 5 = Your Game's EXE file is not the latest version - case 7: // 6 = Your are Prohibited to log in until %s - case 8: // 7 = Server is jammed due to over populated - case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) - case 100: // 99 = This ID has been totally erased - return auth_dat[i].state - 1; - break; - default: - return 99; // 99 = ID has been totally erased - break; - } - } - - if (auth_dat[i].ban_until_time != 0) - { // if account is banned - strftime (tmpstr, 20, date_format, - gmtime (&auth_dat[i].ban_until_time)); - tmpstr[19] = '\0'; - if (auth_dat[i].ban_until_time > time (NULL)) - { // always banned - login_log - ("Connection refused (account: %s, banned until %s, ip: %s)\n", - account->userid, tmpstr, ip); - return 6; // 6 = Your are Prohibited to log in until %s - } - else - { // ban is finished - login_log - ("End of ban (account: %s, previously banned until %s -> not more banned, ip: %s)\n", - account->userid, tmpstr, ip); - auth_dat[i].ban_until_time = 0; // reset the ban time - } - } - - if (auth_dat[i].connect_until_time != 0 - && auth_dat[i].connect_until_time < time (NULL)) - { - login_log - ("Connection refused (account: %s, expired ID, ip: %s)\n", - account->userid, ip); - return 2; // 2 = This ID is expired - } - - login_log ("Authentification accepted (account: %s (id: %d), ip: %s)\n", - account->userid, auth_dat[i].account_id, ip); - } - else - { - if (newaccount == 0) - { - login_log - ("Unknown account (account: %s, ip: %s)\n", - account->userid, ip); - return 0; // 0 = Unregistered ID - } - else - { - int new_id = - mmo_auth_new (account, account->userid[len + 1], "a@a.com"); - login_log - ("Account creation and authentification accepted (account %s (id: %d), sex: %c, connection with _F/_M, ip: %s)\n", - account->userid, new_id, - account->userid[len + 1], ip); - } - } - - gettimeofday (&tv, NULL); - strftime (tmpstr, 24, date_format, gmtime (&(tv.tv_sec))); - sprintf (tmpstr + strlen (tmpstr), ".%03d", (int) tv.tv_usec / 1000); - - account->account_id = auth_dat[i].account_id; - account->login_id1 = mt_random (); - account->login_id2 = mt_random (); - memcpy (account->lastlogin, auth_dat[i].lastlogin, 24); - memcpy (auth_dat[i].lastlogin, tmpstr, 24); - account->sex = auth_dat[i].sex; - strncpy (auth_dat[i].last_ip, ip, 16); - auth_dat[i].logincount++; - - return -1; // account OK -} - -//------------------------------- -// Char-server anti-freeze system -//------------------------------- -void char_anti_freeze_system (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int i; - - //printf("Entering in char_anti_freeze_system function to check freeze of servers.\n"); - for (i = 0; i < MAX_SERVERS; i++) - { - if (server_fd[i] >= 0) - { // if char-server is online - //printf("char_anti_freeze_system: server #%d '%s', flag: %d.\n", i, server[i].name, server_freezeflag[i]); - if (server_freezeflag[i]-- < 1) - { // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed - printf - ("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection.\n", - i, server[i].name); - login_log - ("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection.\n", - i, server[i].name); - session[server_fd[i]]->eof = 1; - } - } - } -} - -//-------------------------------- -// Packet parsing for char-servers -//-------------------------------- -void parse_fromchar (int fd) -{ - int i, j, id; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - char ip[16]; - - sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - for (id = 0; id < MAX_SERVERS; id++) - if (server_fd[id] == fd) - break; - if (id == MAX_SERVERS || session[fd]->eof) - { - if (id < MAX_SERVERS) - { - printf ("Char-server '%s' has disconnected.\n", server[id].name); - login_log ("Char-server '%s' has disconnected (ip: %s).\n", - server[id].name, ip); - server_fd[id] = -1; - memset (&server[id], 0, sizeof (struct mmo_char_server)); - } - close (fd); - delete_session (fd); - return; - } - - while (RFIFOREST (fd) >= 2) - { - if (display_parse_fromchar == 2 || (display_parse_fromchar == 1 && RFIFOW (fd, 0) != 0x2714)) // 0x2714 is done very often (number of players) - printf - ("parse_fromchar: connection #%d, packet: 0x%x (with being read: %d bytes).\n", - fd, RFIFOW (fd, 0), RFIFOREST (fd)); - - switch (RFIFOW (fd, 0)) - { - // request from map-server via char-server to reload GM accounts (by Yor). - case 0x2709: - login_log - ("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n", - server[id].name, ip); - read_gm_account (); - // send GM accounts to all char-servers - send_GM_accounts (); - RFIFOSKIP (fd, 2); - break; - - case 0x2712: // request from char-server to authentify an account - if (RFIFOREST (fd) < 19) - return; - { - int acc; - acc = RFIFOL (fd, 2); // speed up - for (i = 0; i < AUTH_FIFO_SIZE; i++) - { - if (auth_fifo[i].account_id == acc && - auth_fifo[i].login_id1 == RFIFOL (fd, 6) && -#if CMP_AUTHFIFO_LOGIN2 != 0 - auth_fifo[i].login_id2 == RFIFOL (fd, 10) && // relate to the versions higher than 18 -#endif - auth_fifo[i].sex == RFIFOB (fd, 14) && - (!check_ip_flag - || auth_fifo[i].ip == RFIFOL (fd, 15)) - && !auth_fifo[i].delflag) - { - int p, k; - auth_fifo[i].delflag = 1; - login_log - ("Char-server '%s': authentification of the account %d accepted (ip: %s).\n", - server[id].name, acc, ip); -// printf("%d\n", i); - for (k = 0; k < auth_num; k++) - { - if (auth_dat[k].account_id == acc) - { - WFIFOW (fd, 0) = 0x2729; // Sending of the account_reg2 - WFIFOL (fd, 4) = acc; - for (p = 8, j = 0; - j < auth_dat[k].account_reg2_num; - p += 36, j++) - { - memcpy (WFIFOP (fd, p), - auth_dat[k]. - account_reg2[j].str, 32); - WFIFOL (fd, p + 32) = - auth_dat[k].account_reg2[j].value; - } - WFIFOW (fd, 2) = p; - WFIFOSET (fd, p); -// printf("parse_fromchar: Sending of account_reg2: login->char (auth fifo)\n"); - WFIFOW (fd, 0) = 0x2713; - WFIFOL (fd, 2) = acc; - WFIFOB (fd, 6) = 0; - memcpy (WFIFOP (fd, 7), auth_dat[k].email, - 40); - WFIFOL (fd, 47) = - (unsigned long) - auth_dat[k].connect_until_time; - WFIFOSET (fd, 51); - break; - } - } - break; - } - } - // authentification not found - if (i == AUTH_FIFO_SIZE) - { - login_log - ("Char-server '%s': authentification of the account %d REFUSED (ip: %s).\n", - server[id].name, acc, ip); - WFIFOW (fd, 0) = 0x2713; - WFIFOL (fd, 2) = acc; - WFIFOB (fd, 6) = 1; - // It is unnecessary to send email - // It is unnecessary to send validity date of the account - WFIFOSET (fd, 51); - } - } - RFIFOSKIP (fd, 19); - break; - - case 0x2714: - if (RFIFOREST (fd) < 6) - return; - //printf("parse_fromchar: Receiving of the users number of the server '%s': %d\n", server[id].name, RFIFOL(fd,2)); - server[id].users = RFIFOL (fd, 2); - if (anti_freeze_enable) - server_freezeflag[id] = 5; // Char anti-freeze system. Counter. 5 ok, 4...0 freezed - RFIFOSKIP (fd, 6); - break; - - // we receive a e-mail creation of an account with a default e-mail (no answer) - case 0x2715: - { - int acc; - char email[40]; - if (RFIFOREST (fd) < 46) - return; - acc = RFIFOL (fd, 2); // speed up - memcpy (email, RFIFOP (fd, 6), 40); - email[39] = '\0'; - remove_control_chars (email); - //printf("parse_fromchar: an e-mail creation of an account with a default e-mail: server '%s', account: %d, e-mail: '%s'.\n", server[id].name, acc, RFIFOP(fd,6)); - if (e_mail_check (email) == 0) - login_log - ("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", - server[id].name, acc, ip); - else - { - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc - && (strcmp (auth_dat[i].email, "a@a.com") == 0 - || auth_dat[i].email[0] == '\0')) - { - memcpy (auth_dat[i].email, email, 40); - login_log - ("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", - server[id].name, acc, email, ip); - break; - } - } - if (i == auth_num) - login_log - ("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", - server[id].name, acc, ip); - } - RFIFOSKIP (fd, 46); - break; - - // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server - } - case 0x2716: - if (RFIFOREST (fd) < 6) - return; - //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == RFIFOL (fd, 2)) - { - login_log - ("Char-server '%s': e-mail of the account %d found (ip: %s).\n", - server[id].name, RFIFOL (fd, 2), ip); - WFIFOW (fd, 0) = 0x2717; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - memcpy (WFIFOP (fd, 6), auth_dat[i].email, 40); - WFIFOL (fd, 46) = - (unsigned long) auth_dat[i].connect_until_time; - WFIFOSET (fd, 50); - break; - } - } - if (i == auth_num) - { - login_log - ("Char-server '%s': e-mail of the account %d NOT found (ip: %s).\n", - server[id].name, RFIFOL (fd, 2), ip); - } - RFIFOSKIP (fd, 6); - break; - - case 0x2720: // To become GM request - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - int acc; - unsigned char buf[10]; - FILE *fp; - acc = RFIFOL (fd, 4); - //printf("parse_fromchar: Request to become a GM acount from %d account.\n", acc); - WBUFW (buf, 0) = 0x2721; - WBUFL (buf, 2) = acc; - WBUFL (buf, 6) = 0; - if (strcmp (RFIFOP (fd, 8), gm_pass) == 0) - { - // only non-GM can become GM - if (isGM (acc) == 0) - { - // if we autorise creation - if (level_new_gm > 0) - { - // if we can open the file to add the new GM - if ((fp = - fopen_ (GM_account_filename, - "a")) != NULL) - { - char tmpstr[24]; - struct timeval tv; - gettimeofday (&tv, NULL); - strftime (tmpstr, 23, date_format, - gmtime (&(tv.tv_sec))); - fprintf (fp, - "\n// %s: @GM command on account %d\n%d %d\n", - tmpstr, - acc, acc, level_new_gm); - fclose_ (fp); - WBUFL (buf, 6) = level_new_gm; - read_gm_account (); - send_GM_accounts (); - printf - ("GM Change of the account %d: level 0 -> %d.\n", - acc, level_new_gm); - login_log - ("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s).\n", - server[id].name, acc, - level_new_gm, ip); - } - else - { - printf - ("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n", - acc); - login_log - ("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s).\n", - server[id].name, acc, ip); - } - } - else - { - printf - ("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n", - acc); - login_log - ("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s).\n", - server[id].name, acc, ip); - } - } - else - { - printf - ("Error of GM change (suggested account: %d (already GM), correct password).\n", - acc); - login_log - ("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s).\n", - server[id].name, acc, ip); - } - } - else - { - printf - ("Error of GM change (suggested account: %d, invalid password).\n", - acc); - login_log - ("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s).\n", - server[id].name, acc, ip); - } - charif_sendallwos (-1, buf, 10); - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - return; - - // Map server send information to change an email of an account via char-server - case 0x2722: // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B - if (RFIFOREST (fd) < 86) - return; - { - int acc; - char actual_email[40], new_email[40]; - acc = RFIFOL (fd, 2); - memcpy (actual_email, RFIFOP (fd, 6), 40); - actual_email[39] = '\0'; - remove_control_chars (actual_email); - memcpy (new_email, RFIFOP (fd, 46), 40); - new_email[39] = '\0'; - remove_control_chars (new_email); - if (e_mail_check (actual_email) == 0) - login_log - ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", - server[id].name, acc, ip); - else if (e_mail_check (new_email) == 0) - login_log - ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", - server[id].name, acc, ip); - else if (strcasecmp (new_email, "a@a.com") == 0) - login_log - ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", - server[id].name, acc, ip); - else - { - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - if (strcasecmp (auth_dat[i].email, actual_email) - == 0) - { - memcpy (auth_dat[i].email, new_email, 40); - login_log - ("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", - server[id].name, acc, - auth_dat[i].userid, new_email, ip); - } - else - login_log - ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", - server[id].name, acc, - auth_dat[i].userid, - auth_dat[i].email, actual_email, ip); - break; - } - } - if (i == auth_num) - login_log - ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", - server[id].name, acc, ip); - } - } - RFIFOSKIP (fd, 86); - break; - - // Receiving of map-server via char-server a status change resquest (by Yor) - case 0x2724: - if (RFIFOREST (fd) < 10) - return; - { - int acc, statut; - acc = RFIFOL (fd, 2); - statut = RFIFOL (fd, 6); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - if (auth_dat[i].state != statut) - { - login_log - ("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", - server[id].name, acc, statut, - ip); - if (statut != 0) - { - unsigned char buf[16]; - WBUFW (buf, 0) = 0x2731; - WBUFL (buf, 2) = acc; - WBUFB (buf, 6) = 0; // 0: change of statut, 1: ban - WBUFL (buf, 7) = statut; // status or final date of a banishment - charif_sendallwos (-1, buf, 11); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].state = statut; - } - else - login_log - ("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", - server[id].name, acc, statut, - ip); - break; - } - } - if (i == auth_num) - { - login_log - ("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", - server[id].name, acc, statut, ip); - } - RFIFOSKIP (fd, 10); - } - return; - - case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) - if (RFIFOREST (fd) < 18) - return; - { - int acc; - acc = RFIFOL (fd, 2); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - time_t timestamp; - struct tm *tmtime; - if (auth_dat[i].ban_until_time == 0 - || auth_dat[i].ban_until_time < time (NULL)) - timestamp = time (NULL); - else - timestamp = auth_dat[i].ban_until_time; - tmtime = gmtime (×tamp); - tmtime->tm_year = - tmtime->tm_year + (short) RFIFOW (fd, 6); - tmtime->tm_mon = - tmtime->tm_mon + (short) RFIFOW (fd, 8); - tmtime->tm_mday = - tmtime->tm_mday + (short) RFIFOW (fd, 10); - tmtime->tm_hour = - tmtime->tm_hour + (short) RFIFOW (fd, 12); - tmtime->tm_min = - tmtime->tm_min + (short) RFIFOW (fd, 14); - tmtime->tm_sec = - tmtime->tm_sec + (short) RFIFOW (fd, 16); - timestamp = timegm (tmtime); - if (timestamp != -1) - { - if (timestamp <= time (NULL)) - timestamp = 0; - if (auth_dat[i].ban_until_time != timestamp) - { - if (timestamp != 0) - { - unsigned char buf[16]; - char tmpstr[2048]; - strftime (tmpstr, 24, date_format, - gmtime (×tamp)); - login_log - ("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", - server[id].name, acc, - timestamp, - (timestamp == - 0 ? "no banishment" : tmpstr), - ip); - WBUFW (buf, 0) = 0x2731; - WBUFL (buf, 2) = - auth_dat[i].account_id; - WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban - WBUFL (buf, 7) = timestamp; // status or final date of a banishment - charif_sendallwos (-1, buf, 11); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == - acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - else - { - login_log - ("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", - server[id].name, acc, - ip); - } - auth_dat[i].ban_until_time = timestamp; - } - else - { - login_log - ("Char-server '%s': Error of ban request (account: %d, no change for ban date, ip: %s).\n", - server[id].name, acc, ip); - } - } - else - { - login_log - ("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", - server[id].name, acc, ip); - } - break; - } - } - if (i == auth_num) - { - login_log - ("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", - server[id].name, acc, ip); - } - RFIFOSKIP (fd, 18); - } - return; - - case 0x2727: // Change of sex (sex is reversed) - if (RFIFOREST (fd) < 6) - return; - { - int acc, sex; - acc = RFIFOL (fd, 2); - for (i = 0; i < auth_num; i++) - { -// printf("%d,", auth_dat[i].account_id); - if (auth_dat[i].account_id == acc) - { - if (auth_dat[i].sex == 2) - login_log - ("Char-server '%s': Error of sex change - Server account (suggested account: %d, actual sex %d (Server), ip: %s).\n", - server[id].name, acc, - auth_dat[i].sex, ip); - else - { - unsigned char buf[16]; - if (auth_dat[i].sex == 0) - sex = 1; - else - sex = 0; - login_log - ("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", - server[id].name, acc, - (sex == 2) ? 'S' : (sex ? 'M' : 'F'), - ip); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - auth_dat[i].sex = sex; - WBUFW (buf, 0) = 0x2723; - WBUFL (buf, 2) = acc; - WBUFB (buf, 6) = sex; - charif_sendallwos (-1, buf, 7); - } - break; - } - } - if (i == auth_num) - { - login_log - ("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s).\n", - server[id].name, acc, ip); - } - RFIFOSKIP (fd, 6); - } - return; - - case 0x2728: // We receive account_reg2 from a char-server, and we send them to other char-servers. - if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) - return; - { - int acc, p; - acc = RFIFOL (fd, 4); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - unsigned char buf[RFIFOW (fd, 2) + 1]; - login_log - ("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", - server[id].name, acc, ip); - for (p = 8, j = 0; - p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) - { - memcpy (auth_dat[i].account_reg2[j].str, - RFIFOP (fd, p), 32); - auth_dat[i].account_reg2[j].str[31] = '\0'; - remove_control_chars (auth_dat[i].account_reg2 - [j].str); - auth_dat[i].account_reg2[j].value = - RFIFOL (fd, p + 32); - } - auth_dat[i].account_reg2_num = j; - // Sending information towards the other char-servers. - memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), - RFIFOW (fd, 2)); - WBUFW (buf, 0) = 0x2729; - charif_sendallwos (fd, buf, WBUFW (buf, 2)); -// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (account id: %d).\n", acc); - break; - } - } - if (i == auth_num) - { -// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (unknwon account id: %d).\n", acc); - login_log - ("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", - server[id].name, acc, ip); - } - } - RFIFOSKIP (fd, RFIFOW (fd, 2)); - break; - - case 0x272a: // Receiving of map-server via char-server a unban resquest (by Yor) - if (RFIFOREST (fd) < 6) - return; - { - int acc; - acc = RFIFOL (fd, 2); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - if (auth_dat[i].ban_until_time != 0) - { - auth_dat[i].ban_until_time = 0; - login_log - ("Char-server '%s': UnBan request (account: %d, ip: %s).\n", - server[id].name, acc, ip); - } - else - { - login_log - ("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", - server[id].name, acc, ip); - } - break; - } - } - if (i == auth_num) - { - login_log - ("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", - server[id].name, acc, ip); - } - RFIFOSKIP (fd, 6); - } - return; - - // request from char-server to change account password - case 0x2740: // 0x2740 <account_id>.L <actual_password>.24B <new_password>.24B - if (RFIFOREST (fd) < 54) - return; - { - int acc; - char actual_pass[24], new_pass[24]; - acc = RFIFOL (fd, 2); - memcpy (actual_pass, RFIFOP (fd, 6), 24); - actual_pass[23] = '\0'; - remove_control_chars (actual_pass); - memcpy (new_pass, RFIFOP (fd, 30), 24); - new_pass[23] = '\0'; - remove_control_chars (new_pass); - - int status = 0; - - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == acc) - { - if (pass_ok (actual_pass, auth_dat[i].pass)) - { - if (strlen (new_pass) < 4) - status = 3; - else - { - status = 1; - strcpy (auth_dat[i].pass, MD5_saltcrypt(new_pass, make_salt())); - login_log - ("Char-server '%s': Change pass success (account: %d (%s), ip: %s.\n", - server[id].name, acc, - auth_dat[i].userid, ip); - } - } - else - { - status = 2; - login_log - ("Char-server '%s': Attempt to modify a pass failed, wrong password. (account: %d (%s), ip: %s).\n", - server[id].name, acc, - auth_dat[i].userid, ip); - } - break; - } - } - WFIFOW (fd, 0) = 0x2741; - WFIFOL (fd, 2) = acc; - WFIFOB (fd, 6) = status; // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short - WFIFOSET (fd, 7); - } - - RFIFOSKIP (fd, 54); - break; - - default: - { - FILE *logfp; - char tmpstr[24]; - struct timeval tv; - logfp = fopen_ (login_log_unknown_packets_filename, "a"); - if (logfp) - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 23, date_format, gmtime (&(tv.tv_sec))); - fprintf (logfp, - "%s.%03d: receiving of an unknown packet -> disconnection\n", - tmpstr, (int) tv.tv_usec / 1000); - fprintf (logfp, - "parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", - fd, ip, RFIFOW (fd, 0), RFIFOREST (fd)); - fprintf (logfp, "Detail (in hex):\n"); - fprintf (logfp, - "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); - memset (tmpstr, '\0', sizeof (tmpstr)); - for (i = 0; i < RFIFOREST (fd); i++) - { - if ((i & 15) == 0) - fprintf (logfp, "%04X ", i); - fprintf (logfp, "%02x ", RFIFOB (fd, i)); - if (RFIFOB (fd, i) > 0x1f) - tmpstr[i % 16] = RFIFOB (fd, i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - else if ((i + 1) % 16 == 0) - { - fprintf (logfp, " %s\n", tmpstr); - memset (tmpstr, '\0', sizeof (tmpstr)); - } - } - if (i % 16 != 0) - { - for (j = i; j % 16 != 0; j++) - { - fprintf (logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - } - fprintf (logfp, " %s\n", tmpstr); - } - fprintf (logfp, "\n"); - fclose_ (logfp); - } - } - printf - ("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", - RFIFOW (fd, 0)); - session[fd]->eof = 1; - printf - ("Char-server has been disconnected (unknown packet).\n"); - return; - } - } - return; -} - -//--------------------------------------- -// Packet parsing for administation login -//--------------------------------------- -void parse_admin (int fd) -{ - int i, j; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - char *account_name; - char ip[16]; - - sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - if (session[fd]->eof) - { - close (fd); - delete_session (fd); - printf ("Remote administration has disconnected (session #%d).\n", - fd); - return; - } - - while (RFIFOREST (fd) >= 2) - { - if (display_parse_admin == 1) - printf - ("parse_admin: connection #%d, packet: 0x%x (with being read: %d).\n", - fd, RFIFOW (fd, 0), RFIFOREST (fd)); - - switch (RFIFOW (fd, 0)) - { - case 0x7530: // Request of the server version - login_log ("'ladmin': Sending of the server version (ip: %s)\n", - ip); - 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_LOGIN; - WFIFOW (fd, 8) = ATHENA_MOD_VERSION; - WFIFOSET (fd, 10); - RFIFOSKIP (fd, 2); - break; - - case 0x7532: // Request of end of connection - login_log ("'ladmin': End of connection (ip: %s)\n", - ip); - RFIFOSKIP (fd, 2); - session[fd]->eof = 1; - break; - - case 0x7920: // Request of an accounts list - if (RFIFOREST (fd) < 10) - return; - { - int st, ed, len; - int id[auth_num]; - st = RFIFOL (fd, 2); - ed = RFIFOL (fd, 6); - RFIFOSKIP (fd, 10); - WFIFOW (fd, 0) = 0x7921; - if (st < 0) - st = 0; - if (ed > END_ACCOUNT_NUM || ed < st || ed <= 0) - ed = END_ACCOUNT_NUM; - login_log - ("'ladmin': Sending an accounts list (ask: from %d to %d, ip: %s)\n", - st, ed, ip); - // Sort before send - for (i = 0; i < auth_num; i++) - { - int k; - id[i] = i; - for (j = 0; j < i; j++) - { - if (auth_dat[id[i]].account_id < - auth_dat[id[j]].account_id) - { - for (k = i; k > j; k--) - { - id[k] = id[k - 1]; - } - id[j] = i; // id[i] - break; - } - } - } - // Sending accounts information - len = 4; - for (i = 0; i < auth_num && len < 30000; i++) - { - int account_id = auth_dat[id[i]].account_id; // use sorted index - if (account_id >= st && account_id <= ed) - { - j = id[i]; - WFIFOL (fd, len) = account_id; - WFIFOB (fd, len + 4) = - (unsigned char) isGM (account_id); - memcpy (WFIFOP (fd, len + 5), auth_dat[j].userid, - 24); - WFIFOB (fd, len + 29) = auth_dat[j].sex; - WFIFOL (fd, len + 30) = auth_dat[j].logincount; - if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0) // if no state and banished - WFIFOL (fd, len + 34) = 7; // 6 = Your are Prohibited to log in until %s - else - WFIFOL (fd, len + 34) = auth_dat[j].state; - len += 38; - } - } - WFIFOW (fd, 2) = len; - WFIFOSET (fd, len); - } - break; - - case 0x7924: - { // [Fate] Itemfrob package: change item IDs - if (RFIFOREST (fd) < 10) - return; - charif_sendallwos (-1, RFIFOP (fd, 0), 10); // forward package to char servers - RFIFOSKIP (fd, 10); - WFIFOW (fd, 0) = 0x7925; - WFIFOSET (fd, 2); - break; - } - - case 0x7930: // Request for an account creation - if (RFIFOREST (fd) < 91) - return; - { - struct mmo_account ma; - ma.userid = RFIFOP (fd, 2); - ma.passwd = RFIFOP (fd, 26); - memcpy (ma.lastlogin, "-", 2); - ma.sex = RFIFOB (fd, 50); - WFIFOW (fd, 0) = 0x7931; - WFIFOL (fd, 2) = -1; - memcpy (WFIFOP (fd, 6), RFIFOP (fd, 2), 24); - if (strlen (ma.userid) > 23 || strlen (ma.passwd) > 23) - { - login_log - ("'ladmin': Attempt to create an invalid account (account or pass is too long, ip: %s)\n", - ip); - } - else if (strlen (ma.userid) < 4 || strlen (ma.passwd) < 4) - { - login_log - ("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)\n", - ip); - } - else if (ma.sex != 'F' && ma.sex != 'M') - { - login_log - ("'ladmin': Attempt to create an invalid account (account: %s, invalid sex, ip: %s)\n", - ma.userid, ip); - } - else if (account_id_count > END_ACCOUNT_NUM) - { - login_log - ("'ladmin': Attempt to create an account, but there is no more available id number (account: %s, sex: %c, ip: %s)\n", - ma.userid, ma.sex, ip); - } - else - { - remove_control_chars (ma.userid); - remove_control_chars (ma.passwd); - for (i = 0; i < auth_num; i++) - { - if (strncmp (auth_dat[i].userid, ma.userid, 24) == - 0) - { - login_log - ("'ladmin': Attempt to create an already existing account (account: %s ip: %s)\n", - auth_dat[i].userid, ip); - break; - } - } - if (i == auth_num) - { - int new_id; - char email[40]; - memcpy (email, RFIFOP (fd, 51), 40); - email[39] = '\0'; - remove_control_chars (email); - new_id = mmo_auth_new (&ma, ma.sex, email); - login_log - ("'ladmin': Account creation (account: %s (id: %d), sex: %c, email: %s, ip: %s)\n", - ma.userid, new_id, - ma.sex, auth_dat[i].email, ip); - WFIFOL (fd, 2) = new_id; - } - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 91); - } - break; - - case 0x7932: // Request for an account deletion - if (RFIFOREST (fd) < 26) - return; - WFIFOW (fd, 0) = 0x7933; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - // Char-server is notified of deletion (for characters deletion). - unsigned char buf[65535]; - WBUFW (buf, 0) = 0x2730; - WBUFL (buf, 2) = auth_dat[i].account_id; - charif_sendallwos (-1, buf, 6); - // send answer - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - WFIFOL (fd, 2) = auth_dat[i].account_id; - // save deleted account in log file - login_log - ("'ladmin': Account deletion (account: %s, id: %d, ip: %s) - saved in next line:\n", - auth_dat[i].userid, auth_dat[i].account_id, - ip); - mmo_auth_tostr (buf, &auth_dat[i]); - login_log ("%s\n", buf); - // delete account - memset (auth_dat[i].userid, '\0', - sizeof (auth_dat[i].userid)); - auth_dat[i].account_id = -1; - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to delete an unknown account (account: %s, ip: %s)\n", - account_name, ip); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 26); - break; - - case 0x7934: // Request to change a password - if (RFIFOREST (fd) < 50) - return; - WFIFOW (fd, 0) = 0x7935; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - strcpy (auth_dat[i].pass, MD5_saltcrypt(RFIFOP (fd, 26), make_salt())); - auth_dat[i].pass[39] = '\0'; - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)\n", - auth_dat[i].userid, auth_dat[i].pass, ip); - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to modify the password of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 50); - break; - - case 0x7936: // Request to modify a state - if (RFIFOREST (fd) < 50) - return; - { - char error_message[20]; - int statut; - WFIFOW (fd, 0) = 0x7937; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - statut = RFIFOL (fd, 26); - memcpy (error_message, RFIFOP (fd, 30), 20); - error_message[19] = '\0'; - remove_control_chars (error_message); - if (statut != 7 || error_message[0] == '\0') - { // 7: // 6 = Your are Prohibited to log in until %s - strcpy (error_message, "-"); - } - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - WFIFOL (fd, 2) = auth_dat[i].account_id; - if (auth_dat[i].state == statut - && strcmp (auth_dat[i].error_message, - error_message) == 0) - login_log - ("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)\n", - account_name, statut, ip); - else - { - if (statut == 7) - login_log - ("'ladmin': Modification of a state (account: %s, new state: %d - prohibited to login until '%s', ip: %s)\n", - auth_dat[i].userid, statut, - error_message, ip); - else - login_log - ("'ladmin': Modification of a state (account: %s, new state: %d, ip: %s)\n", - auth_dat[i].userid, statut, ip); - if (auth_dat[i].state == 0) - { - unsigned char buf[16]; - WBUFW (buf, 0) = 0x2731; - WBUFL (buf, 2) = auth_dat[i].account_id; - WBUFB (buf, 6) = 0; // 0: change of statut, 1: ban - WBUFL (buf, 7) = statut; // status or final date of a banishment - charif_sendallwos (-1, buf, 11); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == - auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].state = statut; - memcpy (auth_dat[i].error_message, error_message, - 20); - } - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to modify the state of an unknown account (account: %s, received state: %d, ip: %s)\n", - account_name, statut, ip); - } - WFIFOL (fd, 30) = statut; - } - WFIFOSET (fd, 34); - RFIFOSKIP (fd, 50); - break; - - case 0x7938: // Request for servers list and # of online players - login_log ("'ladmin': Sending of servers list (ip: %s)\n", ip); - server_num = 0; - for (i = 0; i < MAX_SERVERS; i++) - { - if (server_fd[i] >= 0) - { - WFIFOL (fd, 4 + server_num * 32) = server[i].ip; - WFIFOW (fd, 4 + server_num * 32 + 4) = server[i].port; - memcpy (WFIFOP (fd, 4 + server_num * 32 + 6), - server[i].name, 20); - WFIFOW (fd, 4 + server_num * 32 + 26) = - server[i].users; - WFIFOW (fd, 4 + server_num * 32 + 28) = - server[i].maintenance; - WFIFOW (fd, 4 + server_num * 32 + 30) = server[i].is_new; - server_num++; - } - } - WFIFOW (fd, 0) = 0x7939; - WFIFOW (fd, 2) = 4 + 32 * server_num; - WFIFOSET (fd, 4 + 32 * server_num); - RFIFOSKIP (fd, 2); - break; - - case 0x793a: // Request to password check - if (RFIFOREST (fd) < 50) - return; - WFIFOW (fd, 0) = 0x793b; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - if ( pass_ok(RFIFOP (fd, 26), auth_dat[i].pass) ) - { - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)\n", - auth_dat[i].userid, auth_dat[i].pass, - ip); - } - else - { - char pass[24]; - memcpy (pass, RFIFOP (fd, 26), 24); - pass[23] = '\0'; - remove_control_chars (pass); - login_log - ("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)\n", - auth_dat[i].userid, pass, ip); - } - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to check the password of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 50); - break; - - case 0x793c: // Request to modify sex - if (RFIFOREST (fd) < 27) - return; - WFIFOW (fd, 0) = 0x793d; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - memcpy (WFIFOP (fd, 6), account_name, 24); - { - char sex; - sex = RFIFOB (fd, 26); - if (sex != 'F' && sex != 'M') - { - if (sex > 31) - login_log - ("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)\n", - account_name, sex, ip); - else - login_log - ("'ladmin': Attempt to give an invalid sex (account: %s, received sex: 'control char', ip: %s)\n", - account_name, ip); - } - else - { - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - if (auth_dat[i].sex != - ((sex == 'S' || sex == 's') ? 2 : (sex == 'M' - || sex == - 'm'))) - { - unsigned char buf[16]; - WFIFOL (fd, 2) = auth_dat[i].account_id; - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == - auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - auth_dat[i].sex = (sex == 'S' - || sex == - 's') ? 2 : (sex == 'M' - || sex == 'm'); - login_log - ("'ladmin': Modification of a sex (account: %s, new sex: %c, ip: %s)\n", - auth_dat[i].userid, sex, ip); - // send to all char-server the change - WBUFW (buf, 0) = 0x2723; - WBUFL (buf, 2) = auth_dat[i].account_id; - WBUFB (buf, 6) = auth_dat[i].sex; - charif_sendallwos (-1, buf, 7); - } - else - { - login_log - ("'ladmin': Modification of a sex, but the sex is already the good sex (account: %s, sex: %c, ip: %s)\n", - auth_dat[i].userid, sex, ip); - } - } - else - { - login_log - ("'ladmin': Attempt to modify the sex of an unknown account (account: %s, received sex: %c, ip: %s)\n", - account_name, sex, ip); - } - } - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 27); - break; - - case 0x793e: // Request to modify GM level - if (RFIFOREST (fd) < 27) - return; - WFIFOW (fd, 0) = 0x793f; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - memcpy (WFIFOP (fd, 6), account_name, 24); - { - char new_gm_level; - new_gm_level = RFIFOB (fd, 26); - if (new_gm_level < 0 || new_gm_level > 99) - { - login_log - ("'ladmin': Attempt to give an invalid GM level (account: %s, received GM level: %d, ip: %s)\n", - account_name, (int) new_gm_level, ip); - } - else - { - i = search_account_index (account_name); - if (i != -1) - { - int acc = auth_dat[i].account_id; - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - if (isGM (acc) != new_gm_level) - { - // modification of the file - FILE *fp, *fp2; - int lock; - char line[512]; - int GM_account, GM_level; - int modify_flag; - char tmpstr[24]; - struct timeval tv; - if ((fp2 = - lock_fopen (GM_account_filename, - &lock)) != NULL) - { - if ((fp = - fopen_ (GM_account_filename, - "r")) != NULL) - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 23, date_format, - gmtime (&(tv.tv_sec))); - modify_flag = 0; - // read/write GM file - while (fgets - (line, sizeof (line) - 1, fp)) - { - while (line[0] != '\0' - && (line[strlen (line) - 1] - == '\n' - || line[strlen (line) - - 1] == '\r')) - line[strlen (line) - 1] = - '\0'; - if ((line[0] == '/' - && line[1] == '/') - || line[0] == '\0') - fprintf (fp2, "%s\n", - line); - else - { - if (sscanf - (line, "%d %d", - &GM_account, - &GM_level) != 2 - && sscanf (line, "%d: %d", - &GM_account, - &GM_level) != - 2) - fprintf (fp2, - "%s\n", - line); - else if (GM_account != acc) - fprintf (fp2, - "%s\n", - line); - else if (new_gm_level < 1) - { - fprintf (fp2, - "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)\n//%d %d\n", - tmpstr, - acc, - auth_dat - [i].userid, - GM_level, acc, - new_gm_level); - modify_flag = 1; - } - else - { - fprintf (fp2, - "// %s: 'ladmin' GM level on account %d '%s' (previous level: %d)\n%d %d\n", - tmpstr, - acc, - auth_dat - [i].userid, - GM_level, acc, - new_gm_level); - modify_flag = 1; - } - } - } - if (modify_flag == 0) - fprintf (fp2, - "// %s: 'ladmin' GM level on account %d '%s' (previous level: 0)\n%d %d\n", - tmpstr, acc, - auth_dat[i].userid, acc, - new_gm_level); - fclose_ (fp); - } - else - { - login_log - ("'ladmin': Attempt to modify of a GM level - impossible to read GM accounts file (account: %s (%d), received GM level: %d, ip: %s)\n", - auth_dat[i].userid, acc, - (int) new_gm_level, ip); - } - lock_fclose(fp2, GM_account_filename, &lock); - WFIFOL (fd, 2) = acc; - login_log - ("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)\n", - auth_dat[i].userid, acc, - (int) new_gm_level, ip); - // read and send new GM informations - read_gm_account (); - send_GM_accounts (); - } - else - { - login_log - ("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)\n", - auth_dat[i].userid, acc, - (int) new_gm_level, ip); - } - } - else - { - login_log - ("'ladmin': Attempt to modify of a GM level, but the GM level is already the good GM level (account: %s (%d), GM level: %d, ip: %s)\n", - auth_dat[i].userid, acc, - (int) new_gm_level, ip); - } - } - else - { - login_log - ("'ladmin': Attempt to modify the GM level of an unknown account (account: %s, received GM level: %d, ip: %s)\n", - account_name, (int) new_gm_level, - ip); - } - } - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 27); - break; - - case 0x7940: // Request to modify e-mail - if (RFIFOREST (fd) < 66) - return; - WFIFOW (fd, 0) = 0x7941; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - memcpy (WFIFOP (fd, 6), account_name, 24); - { - char email[40]; - memcpy (email, RFIFOP (fd, 26), 40); - if (e_mail_check (email) == 0) - { - login_log - ("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)\n", - account_name, ip); - } - else - { - remove_control_chars (email); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - memcpy (auth_dat[i].email, email, 40); - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)\n", - auth_dat[i].userid, email, ip); - } - else - { - login_log - ("'ladmin': Attempt to modify the e-mail of an unknown account (account: %s, received e-mail: %s, ip: %s)\n", - account_name, email, ip); - } - } - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 66); - break; - - case 0x7942: // Request to modify memo field - if (RFIFOREST (fd) < 28 - || RFIFOREST (fd) < (28 + RFIFOW (fd, 26))) - return; - WFIFOW (fd, 0) = 0x7943; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - int size_of_memo = sizeof (auth_dat[i].memo); - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - memset (auth_dat[i].memo, '\0', size_of_memo); - if (RFIFOW (fd, 26) == 0) - { - strncpy (auth_dat[i].memo, "!", size_of_memo); - } - else if (RFIFOW (fd, 26) > size_of_memo - 1) - { - memcpy (auth_dat[i].memo, RFIFOP (fd, 28), - size_of_memo - 1); - } - else - { - memcpy (auth_dat[i].memo, RFIFOP (fd, 28), - RFIFOW (fd, 26)); - } - auth_dat[i].memo[size_of_memo - 1] = '\0'; - remove_control_chars (auth_dat[i].memo); - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)\n", - auth_dat[i].userid, auth_dat[i].memo, ip); - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to modify the memo field of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 28 + RFIFOW (fd, 26)); - break; - - case 0x7944: // Request to found an account id - if (RFIFOREST (fd) < 26) - return; - WFIFOW (fd, 0) = 0x7945; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)\n", - auth_dat[i].userid, auth_dat[i].account_id, - ip); - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': ID request (by the name) of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 26); - break; - - case 0x7946: // Request to found an account name - if (RFIFOREST (fd) < 6) - return; - WFIFOW (fd, 0) = 0x7947; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - memset (WFIFOP (fd, 6), '\0', 24); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == RFIFOL (fd, 2)) - { - strncpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - login_log - ("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)\n", - auth_dat[i].userid, RFIFOL (fd, 2), ip); - break; - } - } - if (i == auth_num) - { - login_log - ("'ladmin': Name request (by id) of an unknown account (id: %d, ip: %s)\n", - RFIFOL (fd, 2), ip); - strncpy (WFIFOP (fd, 6), "", 24); - } - WFIFOSET (fd, 30); - RFIFOSKIP (fd, 6); - break; - - case 0x7948: // Request to change the validity limit (timestamp) (absolute value) - if (RFIFOREST (fd) < 30) - return; - { - time_t timestamp; - char tmpstr[2048]; - WFIFOW (fd, 0) = 0x7949; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - timestamp = (time_t) RFIFOL (fd, 26); - strftime (tmpstr, 24, date_format, gmtime (×tamp)); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - login_log - ("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)\n", - auth_dat[i].userid, timestamp, - (timestamp == 0 ? "unlimited" : tmpstr), ip); - auth_dat[i].connect_until_time = timestamp; - WFIFOL (fd, 2) = auth_dat[i].account_id; - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %d (%s), ip: %s)\n", account_name, timestamp, - (timestamp == 0 ? "unlimited" : tmpstr), ip); - } - WFIFOL (fd, 30) = timestamp; - } - WFIFOSET (fd, 34); - RFIFOSKIP (fd, 30); - break; - - case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value) - if (RFIFOREST (fd) < 30) - return; - { - time_t timestamp; - char tmpstr[2048]; - WFIFOW (fd, 0) = 0x794b; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - timestamp = (time_t) RFIFOL (fd, 26); - if (timestamp <= time (NULL)) - timestamp = 0; - strftime (tmpstr, 24, date_format, gmtime (×tamp)); - i = search_account_index (account_name); - if (i != -1) - { - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - WFIFOL (fd, 2) = auth_dat[i].account_id; - login_log - ("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)\n", - auth_dat[i].userid, timestamp, - (timestamp == 0 ? "no banishment" : tmpstr), ip); - if (auth_dat[i].ban_until_time != timestamp) - { - if (timestamp != 0) - { - unsigned char buf[16]; - WBUFW (buf, 0) = 0x2731; - WBUFL (buf, 2) = auth_dat[i].account_id; - WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban - WBUFL (buf, 7) = timestamp; // status or final date of a banishment - charif_sendallwos (-1, buf, 11); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == - auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].ban_until_time = timestamp; - } - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %d (%s), ip: %s)\n", - account_name, timestamp, - (timestamp == 0 ? "no banishment" : tmpstr), ip); - } - WFIFOL (fd, 30) = timestamp; - } - WFIFOSET (fd, 34); - RFIFOSKIP (fd, 30); - break; - - case 0x794c: // Request to change the final date of a banishment (timestamp) (relative change) - if (RFIFOREST (fd) < 38) - return; - { - time_t timestamp; - struct tm *tmtime; - char tmpstr[2048]; - WFIFOW (fd, 0) = 0x794d; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - WFIFOL (fd, 2) = auth_dat[i].account_id; - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - if (auth_dat[i].ban_until_time == 0 - || auth_dat[i].ban_until_time < time (NULL)) - timestamp = time (NULL); - else - timestamp = auth_dat[i].ban_until_time; - tmtime = gmtime (×tamp); - tmtime->tm_year = - tmtime->tm_year + (short) RFIFOW (fd, 26); - tmtime->tm_mon = - tmtime->tm_mon + (short) RFIFOW (fd, 28); - tmtime->tm_mday = - tmtime->tm_mday + (short) RFIFOW (fd, 30); - tmtime->tm_hour = - tmtime->tm_hour + (short) RFIFOW (fd, 32); - tmtime->tm_min = - tmtime->tm_min + (short) RFIFOW (fd, 34); - tmtime->tm_sec = - tmtime->tm_sec + (short) RFIFOW (fd, 36); - timestamp = mktime (tmtime); - if (timestamp != -1) - { - if (timestamp <= time (NULL)) - timestamp = 0; - strftime (tmpstr, 24, date_format, - gmtime (×tamp)); - login_log - ("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)\n", - auth_dat[i].userid, - (short) RFIFOW (fd, 26), (short) RFIFOW (fd, - 28), - (short) RFIFOW (fd, 30), (short) RFIFOW (fd, - 32), - (short) RFIFOW (fd, 34), (short) RFIFOW (fd, - 36), - timestamp, - (timestamp == 0 ? "no banishment" : tmpstr), - ip); - if (auth_dat[i].ban_until_time != timestamp) - { - if (timestamp != 0) - { - unsigned char buf[16]; - WBUFW (buf, 0) = 0x2731; - WBUFL (buf, 2) = auth_dat[i].account_id; - WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban - WBUFL (buf, 7) = timestamp; // status or final date of a banishment - charif_sendallwos (-1, buf, 11); - for (j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == - auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].ban_until_time = timestamp; - } - } - else - { - strftime (tmpstr, 24, date_format, - gmtime (&auth_dat[i].ban_until_time)); - login_log - ("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", - auth_dat[i].userid, - auth_dat[i].ban_until_time, - (auth_dat[i].ban_until_time == - 0 ? "no banishment" : tmpstr), - (short) RFIFOW (fd, 26), (short) RFIFOW (fd, - 28), - (short) RFIFOW (fd, 30), (short) RFIFOW (fd, - 32), - (short) RFIFOW (fd, 34), (short) RFIFOW (fd, - 36), - ip); - } - WFIFOL (fd, 30) = - (unsigned long) auth_dat[i].ban_until_time; - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to adjust the final date of a banishment of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - WFIFOL (fd, 30) = 0; - } - } - WFIFOSET (fd, 34); - RFIFOSKIP (fd, 38); - break; - - case 0x794e: // Request to send a broadcast message - if (RFIFOREST (fd) < 8 - || RFIFOREST (fd) < (8 + RFIFOL (fd, 4))) - return; - WFIFOW (fd, 0) = 0x794f; - WFIFOW (fd, 2) = -1; - if (RFIFOL (fd, 4) < 1) - { - login_log - ("'ladmin': Receiving a message for broadcast, but message is void (ip: %s)\n", - ip); - } - else - { - // at least 1 char-server - for (i = 0; i < MAX_SERVERS; i++) - if (server_fd[i] >= 0) - break; - if (i == MAX_SERVERS) - { - login_log - ("'ladmin': Receiving a message for broadcast, but no char-server is online (ip: %s)\n", - ip); - } - else - { - char buf[32000]; - char message[32000]; - WFIFOW (fd, 2) = 0; - memset (message, '\0', sizeof (message)); - memcpy (message, RFIFOP (fd, 8), RFIFOL (fd, 4)); - message[sizeof (message) - 1] = '\0'; - remove_control_chars (message); - if (RFIFOW (fd, 2) == 0) - login_log - ("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)\n", - message, ip); - else - login_log - ("'ladmin': Receiving a message for broadcast (message (in blue): %s, ip: %s)\n", - message, ip); - // send same message to all char-servers (no answer) - memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), - 8 + RFIFOL (fd, 4)); - WBUFW (buf, 0) = 0x2726; - charif_sendallwos (-1, buf, 8 + RFIFOL (fd, 4)); - } - } - WFIFOSET (fd, 4); - RFIFOSKIP (fd, 8 + RFIFOL (fd, 4)); - break; - - case 0x7950: // Request to change the validity limite (timestamp) (relative change) - if (RFIFOREST (fd) < 38) - return; - { - time_t timestamp; - struct tm *tmtime; - char tmpstr[2048]; - char tmpstr2[2048]; - WFIFOW (fd, 0) = 0x7951; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - WFIFOL (fd, 2) = auth_dat[i].account_id; - memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); - timestamp = auth_dat[i].connect_until_time; - if (add_to_unlimited_account == 0 && timestamp == 0) - { - login_log - ("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)\n", - auth_dat[i].userid, ip); - WFIFOL (fd, 30) = 0; - } - else - { - if (timestamp == 0 || timestamp < time (NULL)) - timestamp = time (NULL); - tmtime = gmtime (×tamp); - tmtime->tm_year = - tmtime->tm_year + (short) RFIFOW (fd, 26); - tmtime->tm_mon = - tmtime->tm_mon + (short) RFIFOW (fd, 28); - tmtime->tm_mday = - tmtime->tm_mday + (short) RFIFOW (fd, 30); - tmtime->tm_hour = - tmtime->tm_hour + (short) RFIFOW (fd, 32); - tmtime->tm_min = - tmtime->tm_min + (short) RFIFOW (fd, 34); - tmtime->tm_sec = - tmtime->tm_sec + (short) RFIFOW (fd, 36); - timestamp = mktime (tmtime); - if (timestamp != -1) - { - strftime (tmpstr, 24, date_format, - gmtime (&auth_dat - [i].connect_until_time)); - strftime (tmpstr2, 24, date_format, - gmtime (×tamp)); - login_log - ("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)\n", - auth_dat[i].userid, - auth_dat[i].connect_until_time, - (auth_dat[i].connect_until_time == - 0 ? "unlimited" : tmpstr), - (short) RFIFOW (fd, 26), - (short) RFIFOW (fd, 28), - (short) RFIFOW (fd, 30), - (short) RFIFOW (fd, 32), - (short) RFIFOW (fd, 34), - (short) RFIFOW (fd, 36), timestamp, - (timestamp == 0 ? "unlimited" : tmpstr2), - ip); - auth_dat[i].connect_until_time = timestamp; - WFIFOL (fd, 30) = - (unsigned long) - auth_dat[i].connect_until_time; - } - else - { - strftime (tmpstr, 24, date_format, - gmtime (&auth_dat - [i].connect_until_time)); - login_log - ("'ladmin': Impossible to adjust a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", - auth_dat[i].userid, - auth_dat[i].connect_until_time, - (auth_dat[i].connect_until_time == - 0 ? "unlimited" : tmpstr), - (short) RFIFOW (fd, 26), - (short) RFIFOW (fd, 28), - (short) RFIFOW (fd, 30), - (short) RFIFOW (fd, 32), - (short) RFIFOW (fd, 34), - (short) RFIFOW (fd, 36), ip); - WFIFOL (fd, 30) = 0; - } - } - } - else - { - memcpy (WFIFOP (fd, 6), account_name, 24); - login_log - ("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - WFIFOL (fd, 30) = 0; - } - } - WFIFOSET (fd, 34); - RFIFOSKIP (fd, 38); - break; - - case 0x7952: // Request about informations of an account (by account name) - if (RFIFOREST (fd) < 26) - return; - WFIFOW (fd, 0) = 0x7953; - WFIFOL (fd, 2) = -1; - account_name = RFIFOP (fd, 2); - account_name[23] = '\0'; - remove_control_chars (account_name); - i = search_account_index (account_name); - if (i != -1) - { - WFIFOL (fd, 2) = auth_dat[i].account_id; - WFIFOB (fd, 6) = - (unsigned char) isGM (auth_dat[i].account_id); - memcpy (WFIFOP (fd, 7), auth_dat[i].userid, 24); - WFIFOB (fd, 31) = auth_dat[i].sex; - WFIFOL (fd, 32) = auth_dat[i].logincount; - WFIFOL (fd, 36) = auth_dat[i].state; - memcpy (WFIFOP (fd, 40), auth_dat[i].error_message, 20); - memcpy (WFIFOP (fd, 60), auth_dat[i].lastlogin, 24); - memcpy (WFIFOP (fd, 84), auth_dat[i].last_ip, 16); - memcpy (WFIFOP (fd, 100), auth_dat[i].email, 40); - WFIFOL (fd, 140) = - (unsigned long) auth_dat[i].connect_until_time; - WFIFOL (fd, 144) = - (unsigned long) auth_dat[i].ban_until_time; - WFIFOW (fd, 148) = strlen (auth_dat[i].memo); - if (auth_dat[i].memo[0]) - { - memcpy (WFIFOP (fd, 150), auth_dat[i].memo, - strlen (auth_dat[i].memo)); - } - login_log - ("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)\n", - auth_dat[i].userid, auth_dat[i].account_id, - ip); - WFIFOSET (fd, 150 + strlen (auth_dat[i].memo)); - } - else - { - memcpy (WFIFOP (fd, 7), account_name, 24); - WFIFOW (fd, 148) = 0; - login_log - ("'ladmin': Attempt to obtain information (by the name) of an unknown account (account: %s, ip: %s)\n", - account_name, ip); - WFIFOSET (fd, 150); - } - RFIFOSKIP (fd, 26); - break; - - case 0x7954: // Request about information of an account (by account id) - if (RFIFOREST (fd) < 6) - return; - WFIFOW (fd, 0) = 0x7953; - WFIFOL (fd, 2) = RFIFOL (fd, 2); - memset (WFIFOP (fd, 7), '\0', 24); - for (i = 0; i < auth_num; i++) - { - if (auth_dat[i].account_id == RFIFOL (fd, 2)) - { - login_log - ("'ladmin': Sending information of an account (request by the id; account: %s, id: %d, ip: %s)\n", - auth_dat[i].userid, RFIFOL (fd, 2), ip); - WFIFOB (fd, 6) = - (unsigned char) isGM (auth_dat[i].account_id); - memcpy (WFIFOP (fd, 7), auth_dat[i].userid, 24); - WFIFOB (fd, 31) = auth_dat[i].sex; - WFIFOL (fd, 32) = auth_dat[i].logincount; - WFIFOL (fd, 36) = auth_dat[i].state; - memcpy (WFIFOP (fd, 40), auth_dat[i].error_message, - 20); - memcpy (WFIFOP (fd, 60), auth_dat[i].lastlogin, 24); - memcpy (WFIFOP (fd, 84), auth_dat[i].last_ip, 16); - memcpy (WFIFOP (fd, 100), auth_dat[i].email, 40); - WFIFOL (fd, 140) = - (unsigned long) auth_dat[i].connect_until_time; - WFIFOL (fd, 144) = - (unsigned long) auth_dat[i].ban_until_time; - WFIFOW (fd, 148) = strlen (auth_dat[i].memo); - if (auth_dat[i].memo[0]) - { - memcpy (WFIFOP (fd, 150), auth_dat[i].memo, - strlen (auth_dat[i].memo)); - } - WFIFOSET (fd, 150 + strlen (auth_dat[i].memo)); - break; - } - } - if (i == auth_num) - { - login_log - ("'ladmin': Attempt to obtain information (by the id) of an unknown account (id: %d, ip: %s)\n", - RFIFOL (fd, 2), ip); - strncpy (WFIFOP (fd, 7), "", 24); - WFIFOW (fd, 148) = 0; - WFIFOSET (fd, 150); - } - RFIFOSKIP (fd, 6); - break; - - case 0x7955: // Request to reload GM file (no answer) - login_log - ("'ladmin': Request to re-load GM configuration file (ip: %s).\n", - ip); - read_gm_account (); - // send GM accounts to all char-servers - send_GM_accounts (); - RFIFOSKIP (fd, 2); - break; - - default: - { - FILE *logfp; - char tmpstr[24]; - struct timeval tv; - logfp = fopen_ (login_log_unknown_packets_filename, "a"); - if (logfp) - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 23, date_format, gmtime (&(tv.tv_sec))); - fprintf (logfp, - "%s.%03d: receiving of an unknown packet -> disconnection\n", - tmpstr, (int) tv.tv_usec / 1000); - fprintf (logfp, - "parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", - fd, ip, RFIFOW (fd, 0), RFIFOREST (fd)); - fprintf (logfp, "Detail (in hex):\n"); - fprintf (logfp, - "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); - memset (tmpstr, '\0', sizeof (tmpstr)); - for (i = 0; i < RFIFOREST (fd); i++) - { - if ((i & 15) == 0) - fprintf (logfp, "%04X ", i); - fprintf (logfp, "%02x ", RFIFOB (fd, i)); - if (RFIFOB (fd, i) > 0x1f) - tmpstr[i % 16] = RFIFOB (fd, i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - else if ((i + 1) % 16 == 0) - { - fprintf (logfp, " %s\n", tmpstr); - memset (tmpstr, '\0', sizeof (tmpstr)); - } - } - if (i % 16 != 0) - { - for (j = i; j % 16 != 0; j++) - { - fprintf (logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - } - fprintf (logfp, " %s\n", tmpstr); - } - fprintf (logfp, "\n"); - fclose_ (logfp); - } - } - login_log - ("'ladmin': End of connection, unknown packet (ip: %s)\n", - ip); - session[fd]->eof = 1; - printf - ("Remote administration has been disconnected (unknown packet).\n"); - return; - } - //WFIFOW(fd,0) = 0x791f; - //WFIFOSET(fd,2); - } - return; -} - -//-------------------------------------------- -// Test to know if an IP come from LAN or WAN. -//-------------------------------------------- -int lan_ip_check (unsigned char *p) -{ - int i; - int lancheck = 1; - -// printf("lan_ip_check: to compare: %d.%d.%d.%d, network: %d.%d.%d.%d/%d.%d.%d.%d\n", -// p[0], p[1], p[2], p[3], -// subneti[0], subneti[1], subneti[2], subneti[3], -// subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); - for (i = 0; i < 4; i++) - { - if ((subneti[i] & subnetmaski[i]) != (p[i] & subnetmaski[i])) - { - lancheck = 0; - break; - } - } - printf ("LAN test (result): %s source\033[0m.\n", - (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); - return lancheck; -} - -//---------------------------------------------------------------------------------------- -// Default packet parsing (normal players or administation/char-server connexion requests) -//---------------------------------------------------------------------------------------- -void parse_login (int fd) -{ - struct mmo_account account; - int result, i, j; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - char ip[16]; - int host_len; - - sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - if (session[fd]->eof) - { - close (fd); - delete_session (fd); - return; - } - - while (RFIFOREST (fd) >= 2) - { - if (display_parse_login == 1) - { - if (RFIFOW (fd, 0) == 0x64 || RFIFOW (fd, 0) == 0x01dd) - { - if (RFIFOREST (fd) >= ((RFIFOW (fd, 0) == 0x64) ? 55 : 47)) - printf - ("parse_login: connection #%d, packet: 0x%x (with being read: %d), account: %s.\n", - fd, RFIFOW (fd, 0), RFIFOREST (fd), RFIFOP (fd, 6)); - } - else if (RFIFOW (fd, 0) == 0x2710) - { - if (RFIFOREST (fd) >= 86) - printf - ("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", - fd, RFIFOW (fd, 0), RFIFOREST (fd), RFIFOP (fd, 60)); - } - else - printf - ("parse_login: connection #%d, packet: 0x%x (with being read: %d).\n", - fd, RFIFOW (fd, 0), RFIFOREST (fd)); - } - - switch (RFIFOW (fd, 0)) - { - case 0x200: // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive. - if (RFIFOREST (fd) < 26) - return; - RFIFOSKIP (fd, 26); - break; - - case 0x204: // New alive packet: structure: 0x204 <encrypted.account.userid>.16B. (new ragexe from 22 june 2004) - if (RFIFOREST (fd) < 18) - return; - RFIFOSKIP (fd, 18); - break; - - case 0x64: // Ask connection of a client - case 0x01dd: // Ask connection of a client (encryption mode) - if (RFIFOREST (fd) < ((RFIFOW (fd, 0) == 0x64) ? 55 : 47)) - return; - - account.userid = RFIFOP (fd, 6); - account.userid[23] = '\0'; - remove_control_chars (account.userid); - account.passwd = RFIFOP (fd, 30); - if (RFIFOW (fd, 0) == 0x64) - { - account.passwd[23] = '\0'; - remove_control_chars (account.passwd); - } -#ifdef PASSWORDENC - account.passwdenc = - (RFIFOW (fd, 0) == 0x64) ? 0 : PASSWORDENC; -#else - account.passwdenc = 0; -#endif - - if (RFIFOW (fd, 0) == 0x64) - { - login_log - ("Request for connection (non encryption mode) of %s (ip: %s).\n", - account.userid, ip); - } - else - { - login_log - ("Request for connection (encryption mode) of %s (ip: %s).\n", - account.userid, ip); - } - - if (!check_ip (session[fd]->client_addr.sin_addr.s_addr)) - { - login_log - ("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", - ip); - WFIFOW (fd, 0) = 0x6a; - WFIFOB (fd, 2) = 0x03; - WFIFOSET (fd, 3); - RFIFOSKIP (fd, (RFIFOW (fd, 0) == 0x64) ? 55 : 47); - break; - } - - result = mmo_auth (&account, fd); - if (result == -1) - { - int gm_level = isGM (account.account_id); - if (min_level_to_connect > gm_level) - { - login_log - ("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s).\n", - min_level_to_connect, account.userid, - gm_level, ip); - WFIFOW (fd, 0) = 0x81; - WFIFOL (fd, 2) = 1; // 01 = Server closed - WFIFOSET (fd, 3); - } - else - { - int version_2 = RFIFOB (fd, 54); // version 2 - - if (gm_level) - printf - ("Connection of the GM (level:%d) account '%s' accepted.\n", - gm_level, account.userid); - else - printf - ("Connection of the account '%s' accepted.\n", - account.userid); - - /* - * Add a 0x0063 packet, which contains the name of the update host. The packet will only - * be sent if login_athena.conf contains a non-null entry for "update_host:" - * - * Because older clients cannot handle the 0x63 packet, we check the "version 2" value - * from the incoming 0x64 packet (the byte at offset 54). If bit 0 of this is set, - * then the client can safely accept the 0x63 packet. The "version 2" value is not - * otherwise used by eAthena. - */ - if ((RFIFOW (fd, 0) == 0x64) - && (version_2 & VERSION_2_UPDATEHOST)) - { - host_len = (int) strlen (update_host); - if (host_len > 0) - { - WFIFOW (fd, 0) = 0x63; - WFIFOW (fd, 2) = 4 + host_len; - memcpy (WFIFOP (fd, 4), update_host, - host_len); - WFIFOSET (fd, 4 + host_len); - } - } - - // Load list of char servers into outbound packet - server_num = 0; - if (version_2 && VERSION_2_SERVERORDER) - for (i = 0; i < MAX_SERVERS; i++) - { - if (server_fd[i] >= 0) - { - if (lan_ip_check (p)) - WFIFOL (fd, 47 + server_num * 32) = - inet_addr (lan_char_ip); - else - WFIFOL (fd, 47 + server_num * 32) = - server[i].ip; - WFIFOW (fd, 47 + server_num * 32 + 4) = - server[i].port; - memcpy (WFIFOP - (fd, 47 + server_num * 32 + 6), - server[i].name, 20); - WFIFOW (fd, 47 + server_num * 32 + 26) = - server[i].users; - WFIFOW (fd, 47 + server_num * 32 + 28) = - server[i].maintenance; - WFIFOW (fd, 47 + server_num * 32 + 30) = - server[i].is_new; - server_num++; - } - } - else // Send them in reverse, as the client defaults to the second (!) one - for (i = MAX_SERVERS - 1; i >= 0; i--) - { - if (server_fd[i] >= 0) - { - if (lan_ip_check (p)) - WFIFOL (fd, 47 + server_num * 32) = - inet_addr (lan_char_ip); - else - WFIFOL (fd, 47 + server_num * 32) = - server[i].ip; - WFIFOW (fd, 47 + server_num * 32 + 4) = - server[i].port; - memcpy (WFIFOP - (fd, 47 + server_num * 32 + 6), - server[i].name, 20); - WFIFOW (fd, 47 + server_num * 32 + 26) = - server[i].users; - WFIFOW (fd, 47 + server_num * 32 + 28) = - server[i].maintenance; - WFIFOW (fd, 47 + server_num * 32 + 30) = - server[i].is_new; - server_num++; - } - } - // if at least 1 char-server - if (server_num > 0) - { - WFIFOW (fd, 0) = 0x69; - WFIFOW (fd, 2) = 47 + 32 * server_num; - WFIFOL (fd, 4) = account.login_id1; - WFIFOL (fd, 8) = account.account_id; - WFIFOL (fd, 12) = account.login_id2; - WFIFOL (fd, 16) = 0; // in old version, that was for ip (not more used) - memcpy (WFIFOP (fd, 20), account.lastlogin, 24); // in old version, that was for name (not more used) - WFIFOB (fd, 46) = account.sex; - WFIFOSET (fd, 47 + 32 * server_num); - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = - account.account_id; - auth_fifo[auth_fifo_pos].login_id1 = - account.login_id1; - auth_fifo[auth_fifo_pos].login_id2 = - account.login_id2; - auth_fifo[auth_fifo_pos].sex = account.sex; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].ip = - session[fd]->client_addr.sin_addr.s_addr; - auth_fifo_pos++; - // if no char-server, don't send void list of servers, just disconnect the player with proper message - } - else - { - login_log - ("Connection refused: there is no char-server online (account: %s, ip: %s).\n", - account.userid, ip); - WFIFOW (fd, 0) = 0x81; - WFIFOL (fd, 2) = 1; // 01 = Server closed - WFIFOSET (fd, 3); - } - } - } - else - { - memset (WFIFOP (fd, 0), '\0', 23); - WFIFOW (fd, 0) = 0x6a; - WFIFOB (fd, 2) = result; - if (result == 6) - { // 6 = Your are Prohibited to log in until %s - i = search_account_index (account.userid); - if (i != -1) - { - if (auth_dat[i].ban_until_time != 0) - { // if account is banned, we send ban timestamp - char tmpstr[256]; - strftime (tmpstr, 20, date_format, - gmtime (&auth_dat - [i].ban_until_time)); - tmpstr[19] = '\0'; - memcpy (WFIFOP (fd, 3), tmpstr, 20); - } - else - { // we send error message - memcpy (WFIFOP (fd, 3), - auth_dat[i].error_message, 20); - } - } - } - WFIFOSET (fd, 23); - } - RFIFOSKIP (fd, (RFIFOW (fd, 0) == 0x64) ? 55 : 47); - break; - - case 0x01db: // Sending request of the coding key - case 0x791a: // Sending request of the coding key (administration packet) - { - struct login_session_data *ld; - if (session[fd]->session_data) - { - printf - ("login: abnormal request of MD5 key (already opened session).\n"); - session[fd]->eof = 1; - return; - } - CREATE (ld, struct login_session_data, 1); - session[fd]->session_data = ld; - if (!ld) - { - printf - ("login: Request for md5 key: memory allocation failure (malloc)!\n"); - session[fd]->eof = 1; - return; - } - if (RFIFOW (fd, 0) == 0x01db) - { - login_log ("Sending request of the coding key (ip: %s)\n", - ip); - } - else - { - login_log - ("'ladmin': Sending request of the coding key (ip: %s)\n", - ip); - } - // Creation of the coding key - memset (ld->md5key, '\0', sizeof (ld->md5key)); - ld->md5keylen = rand () % 4 + 12; - for (i = 0; i < ld->md5keylen; i++) - ld->md5key[i] = rand () % 255 + 1; - - RFIFOSKIP (fd, 2); - WFIFOW (fd, 0) = 0x01dc; - WFIFOW (fd, 2) = 4 + ld->md5keylen; - memcpy (WFIFOP (fd, 4), ld->md5key, ld->md5keylen); - WFIFOSET (fd, WFIFOW (fd, 2)); - } - break; - - case 0x2710: // Connection request of a char-server - if (RFIFOREST (fd) < 86) - return; - { - int GM_value, len; - unsigned char *server_name; - account.userid = RFIFOP (fd, 2); - account.userid[23] = '\0'; - remove_control_chars (account.userid); - account.passwd = RFIFOP (fd, 26); - account.passwd[23] = '\0'; - remove_control_chars (account.passwd); - account.passwdenc = 0; - server_name = RFIFOP (fd, 60); - server_name[19] = '\0'; - remove_control_chars (server_name); - login_log - ("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)\n", - server_name, RFIFOB (fd, 54), RFIFOB (fd, 55), - RFIFOB (fd, 56), RFIFOB (fd, 57), RFIFOW (fd, 58), - ip); - result = mmo_auth (&account, fd); - - if (result == -1 && account.sex == 2) - { - // If this is the main server, and we don't already have a main server - if (server_fd[0] <= 0 - && strcasecmp (server_name, main_server) == 0) - { - account.account_id = 0; - } - else - { - int i; - for (i = 1; i < MAX_SERVERS; i++) - { - if (server_fd[i] <= 0) - { - account.account_id = i; - break; - } - } - } - } - - if (result == -1 && account.sex == 2 - && account.account_id < MAX_SERVERS - && server_fd[account.account_id] == -1) - { - login_log - ("Connection of the char-server '%s' accepted (account: %s, pass: %s, ip: %s)\n", - server_name, account.userid, - account.passwd, ip); - printf - ("Connection of the char-server '%s' accepted.\n", - server_name); - memset (&server[account.account_id], 0, - sizeof (struct mmo_char_server)); - server[account.account_id].ip = RFIFOL (fd, 54); - server[account.account_id].port = RFIFOW (fd, 58); - memcpy (server[account.account_id].name, server_name, - 20); - server[account.account_id].users = 0; - server[account.account_id].maintenance = - RFIFOW (fd, 82); - server[account.account_id].is_new = RFIFOW (fd, 84); - server_fd[account.account_id] = fd; - if (anti_freeze_enable) - server_freezeflag[account.account_id] = 5; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed - WFIFOW (fd, 0) = 0x2711; - WFIFOB (fd, 2) = 0; - WFIFOSET (fd, 3); - session[fd]->func_parse = parse_fromchar; - realloc_fifo (fd, FIFOSIZE_SERVERLINK, - FIFOSIZE_SERVERLINK); - // send GM account to char-server - len = 4; - WFIFOW (fd, 0) = 0x2732; - for (i = 0; i < auth_num; i++) - // send only existing accounts. We can not create a GM account when server is online. - if ((GM_value = - isGM (auth_dat[i].account_id)) > 0) - { - WFIFOL (fd, len) = auth_dat[i].account_id; - WFIFOB (fd, len + 4) = - (unsigned char) GM_value; - len += 5; - } - WFIFOW (fd, 2) = len; - WFIFOSET (fd, len); - } - else - { - login_log - ("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)\n", - server_name, account.userid, - account.passwd, ip); - WFIFOW (fd, 0) = 0x2711; - WFIFOB (fd, 2) = 3; - WFIFOSET (fd, 3); - } - } - RFIFOSKIP (fd, 86); - return; - - case 0x7530: // Request of the server version - login_log ("Sending of the server version (ip: %s)\n", - ip); - WFIFOW (fd, 0) = 0x7531; - WFIFOB (fd, 2) = -1; - WFIFOB (fd, 3) = 'T'; - WFIFOB (fd, 4) = 'M'; - WFIFOB (fd, 5) = 'W'; - WFIFOL (fd, 6) = new_account_flag ? 1 : 0; - WFIFOSET (fd, 10); - RFIFOSKIP (fd, 2); - break; - - case 0x7532: // Request to end connection - login_log ("End of connection (ip: %s)\n", ip); - session[fd]->eof = 1; - return; - - case 0x7918: // Request for administation login - if (RFIFOREST (fd) < 4 - || RFIFOREST (fd) < ((RFIFOW (fd, 2) == 0) ? 28 : 20)) - return; - WFIFOW (fd, 0) = 0x7919; - WFIFOB (fd, 2) = 1; - if (!check_ladminip - (session[fd]->client_addr.sin_addr.s_addr)) - { - login_log - ("'ladmin'-login: Connection in administration mode refused: IP isn't authorised (ladmin_allow, ip: %s).\n", - ip); - } - else - { - struct login_session_data *ld = (struct login_session_data *)session[fd]->session_data; - if (RFIFOW (fd, 2) == 0) - { // non encrypted password - unsigned char *password; - password = RFIFOP (fd, 4); - password[23] = '\0'; - remove_control_chars (password); - // If remote administration is enabled and password sent by client matches password read from login server configuration file - if ((admin_state == 1) - && (strcmp (password, admin_pass) == 0)) - { - login_log - ("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)\n", - password, ip); - printf - ("Connection of a remote administration accepted (non encrypted password).\n"); - WFIFOB (fd, 2) = 0; - session[fd]->func_parse = parse_admin; - } - else if (admin_state != 1) - login_log - ("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)\n", - password, ip); - else - login_log - ("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)\n", - password, ip); - } - else - { // encrypted password - if (!ld) - printf - ("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); - else - { - char md5str[64] = "", md5bin[32]; - if (RFIFOW (fd, 2) == 1) - { - strncpy (md5str, ld->md5key, sizeof (ld->md5key)); // 20 - strcat (md5str, admin_pass); // 24 - } - else if (RFIFOW (fd, 2) == 2) - { - strncpy (md5str, admin_pass, sizeof (admin_pass)); // 24 - strcat (md5str, ld->md5key); // 20 - } - MD5_to_bin(MD5_from_cstring(md5str), md5bin); - // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file - if ((admin_state == 1) - && (memcmp (md5bin, RFIFOP (fd, 4), 16) == 0)) - { - login_log - ("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)\n", - ip); - printf - ("Connection of a remote administration accepted (encrypted password).\n"); - WFIFOB (fd, 2) = 0; - session[fd]->func_parse = parse_admin; - } - else if (admin_state != 1) - login_log - ("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)\n", - ip); - else - login_log - ("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)\n", - ip); - } - } - } - WFIFOSET (fd, 3); - RFIFOSKIP (fd, (RFIFOW (fd, 2) == 0) ? 28 : 20); - break; - - default: - if (save_unknown_packets) - { - FILE *logfp; - char tmpstr[24]; - struct timeval tv; - logfp = fopen_ (login_log_unknown_packets_filename, "a"); - if (logfp) - { - gettimeofday (&tv, NULL); - strftime (tmpstr, 23, date_format, - gmtime (&(tv.tv_sec))); - fprintf (logfp, - "%s.%03d: receiving of an unknown packet -> disconnection\n", - tmpstr, (int) tv.tv_usec / 1000); - fprintf (logfp, - "parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", - fd, ip, RFIFOW (fd, 0), - RFIFOREST (fd)); - fprintf (logfp, "Detail (in hex):\n"); - fprintf (logfp, - "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); - memset (tmpstr, '\0', sizeof (tmpstr)); - for (i = 0; i < RFIFOREST (fd); i++) - { - if ((i & 15) == 0) - fprintf (logfp, "%04X ", i); - fprintf (logfp, "%02x ", RFIFOB (fd, i)); - if (RFIFOB (fd, i) > 0x1f) - tmpstr[i % 16] = RFIFOB (fd, i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - else if ((i + 1) % 16 == 0) - { - fprintf (logfp, " %s\n", tmpstr); - memset (tmpstr, '\0', sizeof (tmpstr)); - } - } - if (i % 16 != 0) - { - for (j = i; j % 16 != 0; j++) - { - fprintf (logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf (logfp, " "); - } - fprintf (logfp, " %s\n", tmpstr); - } - fprintf (logfp, "\n"); - fclose_ (logfp); - } - } - login_log ("End of connection, unknown packet (ip: %s)\n", ip); - session[fd]->eof = 1; - return; - } - } - return; -} - -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, français, deutsch, español -//------------------------------------------------- -int config_switch (const char *str) -{ - if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 - || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 - || strcasecmp (str, "si") == 0) - return 1; - if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 - || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) - return 0; - - return atoi (str); -} - -//---------------------------------- -// Reading Lan Support configuration -//---------------------------------- -int login_lan_config_read (const char *lancfgName) -{ - int j; - struct hostent *h = NULL; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - // set default configuration - strncpy (lan_char_ip, "127.0.0.1", sizeof (lan_char_ip)); - subneti[0] = 127; - subneti[1] = 0; - subneti[2] = 0; - subneti[3] = 1; - for (j = 0; j < 4; j++) - subnetmaski[j] = 255; - - fp = fopen_ (lancfgName, "r"); - - if (fp == NULL) - { - printf - ("***WARNING: LAN Support configuration file is not found: %s\n", - lancfgName); - return 1; - } - - printf ("---Start reading Lan Support configuration file\n"); - - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof (line) - 1] = '\0'; - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - remove_control_chars (w1); - remove_control_chars (w2); - if (strcasecmp (w1, "lan_char_ip") == 0) - { // Read Char-Server Lan IP Address - h = gethostbyname (w2); - if (h != NULL) - { - sprintf (lan_char_ip, "%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]); - } - else - { - strncpy (lan_char_ip, w2, sizeof (lan_char_ip)); - lan_char_ip[sizeof (lan_char_ip) - 1] = '\0'; - } - printf ("LAN IP of char-server: %s.\n", lan_char_ip); - } - else if (strcasecmp (w1, "subnet") == 0) - { // Read Subnetwork - for (j = 0; j < 4; j++) - subneti[j] = 0; - h = gethostbyname (w2); - if (h != NULL) - { - for (j = 0; j < 4; j++) - subneti[j] = (unsigned char) h->h_addr[j]; - } - else - { - sscanf (w2, "%d.%d.%d.%d", &subneti[0], &subneti[1], - &subneti[2], &subneti[3]); - } - printf ("Sub-network of the char-server: %d.%d.%d.%d.\n", - subneti[0], subneti[1], subneti[2], subneti[3]); - } - else if (strcasecmp (w1, "subnetmask") == 0) - { // Read Subnetwork Mask - for (j = 0; j < 4; j++) - subnetmaski[j] = 255; - h = gethostbyname (w2); - if (h != NULL) - { - for (j = 0; j < 4; j++) - subnetmaski[j] = (unsigned char) h->h_addr[j]; - } - else - { - sscanf (w2, "%d.%d.%d.%d", &subnetmaski[0], &subnetmaski[1], - &subnetmaski[2], &subnetmaski[3]); - } - printf ("Sub-network mask of the char-server: %d.%d.%d.%d.\n", - subnetmaski[0], subnetmaski[1], subnetmaski[2], - subnetmaski[3]); - } - } - fclose_ (fp); - - // log the LAN configuration - login_log ("The LAN configuration of the server is set:\n"); - login_log ("- with LAN IP of char-server: %s.\n", lan_char_ip); - login_log - ("- with the sub-network of the char-server: %d.%d.%d.%d/%d.%d.%d.%d.\n", - subneti[0], subneti[1], subneti[2], subneti[3], - subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); - - // sub-network check of the char-server - { - unsigned int a0, a1, a2, a3; - unsigned char p[4]; - sscanf (lan_char_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); - p[0] = a0; - p[1] = a1; - p[2] = a2; - p[3] = a3; - printf ("LAN test of LAN IP of the char-server: "); - if (lan_ip_check (p) == 0) - { - printf - ("\033[1;31m***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network\033[0m\n"); - login_log - ("***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network.\n"); - } - } - - printf ("---End reading of Lan Support configuration file\n"); - - return 0; -} - -//----------------------------------- -// Reading general configuration file -//----------------------------------- -int login_config_read (const char *cfgName) -{ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen_ (cfgName, "r"); - if (fp == NULL) - { - printf ("Configuration file (%s) not found.\n", cfgName); - return 1; - } - - printf ("---Start reading of Login Server configuration file (%s)\n", - cfgName); - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof (line) - 1] = '\0'; - if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) == 2) - { - remove_control_chars (w1); - remove_control_chars (w2); - - if (strcasecmp (w1, "admin_state") == 0) - { - admin_state = config_switch (w2); - } - else if (strcasecmp (w1, "admin_pass") == 0) - { - strncpy (admin_pass, w2, sizeof (admin_pass)); - admin_pass[sizeof (admin_pass) - 1] = '\0'; - } - else if (strcasecmp (w1, "ladminallowip") == 0) - { - if (strcasecmp (w2, "clear") == 0) - { - if (access_ladmin_allow) - free (access_ladmin_allow); - access_ladmin_allow = NULL; - access_ladmin_allownum = 0; - } - else - { - if (strcasecmp (w2, "all") == 0) - { - // reset all previous values - if (access_ladmin_allow) - free (access_ladmin_allow); - // set to all - CREATE (access_ladmin_allow, char, ACO_STRSIZE); - access_ladmin_allownum = 1; - } - else if (w2[0] - && !(access_ladmin_allownum == 1 - && access_ladmin_allow[0] == '\0')) - { // don't add IP if already 'all' - if (access_ladmin_allow) - RECREATE (access_ladmin_allow, char, (access_ladmin_allownum + 1) * ACO_STRSIZE); - else - CREATE (access_ladmin_allow, char, ACO_STRSIZE); - strncpy (access_ladmin_allow + - (access_ladmin_allownum++) * ACO_STRSIZE, w2, - ACO_STRSIZE); - access_ladmin_allow[access_ladmin_allownum * - ACO_STRSIZE - 1] = '\0'; - } - } - } - else if (strcasecmp (w1, "gm_pass") == 0) - { - strncpy (gm_pass, w2, sizeof (gm_pass)); - gm_pass[sizeof (gm_pass) - 1] = '\0'; - } - else if (strcasecmp (w1, "level_new_gm") == 0) - { - level_new_gm = atoi (w2); - } - else if (strcasecmp (w1, "new_account") == 0) - { - new_account_flag = config_switch (w2); - } - else if (strcasecmp (w1, "login_port") == 0) - { - login_port = atoi (w2); - } - else if (strcasecmp (w1, "account_filename") == 0) - { - strncpy (account_filename, w2, sizeof (account_filename)); - account_filename[sizeof (account_filename) - 1] = '\0'; - } - else if (strcasecmp (w1, "gm_account_filename") == 0) - { - strncpy (GM_account_filename, w2, - sizeof (GM_account_filename)); - GM_account_filename[sizeof (GM_account_filename) - 1] = '\0'; - } - else if (strcasecmp (w1, "gm_account_filename_check_timer") == 0) - { - gm_account_filename_check_timer = atoi (w2); - } - else if (strcasecmp (w1, "login_log_filename") == 0) - { - strncpy (login_log_filename, w2, sizeof (login_log_filename)); - login_log_filename[sizeof (login_log_filename) - 1] = '\0'; - } - else if (strcasecmp (w1, "login_log_unknown_packets_filename") == 0) - { - strncpy (login_log_unknown_packets_filename, w2, - sizeof (login_log_unknown_packets_filename)); - login_log_unknown_packets_filename[sizeof - (login_log_unknown_packets_filename) - - 1] = '\0'; - } - else if (strcasecmp (w1, "save_unknown_packets") == 0) - { - save_unknown_packets = config_switch (w2); - } - else if (strcasecmp (w1, "display_parse_login") == 0) - { - display_parse_login = config_switch (w2); // 0: no, 1: yes - } - else if (strcasecmp (w1, "display_parse_admin") == 0) - { - display_parse_admin = config_switch (w2); // 0: no, 1: yes - } - else if (strcasecmp (w1, "display_parse_fromchar") == 0) - { - display_parse_fromchar = config_switch (w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets - } - else if (strcasecmp (w1, "date_format") == 0) - { // note: never have more than 19 char for the date! - switch (atoi (w2)) - { - case 0: - strcpy (date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 - break; - case 1: - strcpy (date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 - break; - case 2: - strcpy (date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 - break; - case 3: - strcpy (date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 - break; - } - } - else if (strcasecmp (w1, "min_level_to_connect") == 0) - { - min_level_to_connect = atoi (w2); - } - else if (strcasecmp (w1, "add_to_unlimited_account") == 0) - { - add_to_unlimited_account = config_switch (w2); - } - else if (strcasecmp (w1, "start_limited_time") == 0) - { - start_limited_time = atoi (w2); - } - else if (strcasecmp (w1, "check_ip_flag") == 0) - { - check_ip_flag = config_switch (w2); - } - else if (strcasecmp (w1, "order") == 0) - { - access_order = atoi (w2); - if (strcasecmp (w2, "deny,allow") == 0 || - strcasecmp (w2, "deny, allow") == 0) - access_order = ACO_DENY_ALLOW; - if (strcasecmp (w2, "allow,deny") == 0 || - strcasecmp (w2, "allow, deny") == 0) - access_order = ACO_ALLOW_DENY; - if (strcasecmp (w2, "mutual-failture") == 0 || - strcasecmp (w2, "mutual-failure") == 0) - access_order = ACO_MUTUAL_FAILTURE; - } - else if (strcasecmp (w1, "allow") == 0) - { - if (strcasecmp (w2, "clear") == 0) - { - if (access_allow) - free (access_allow); - access_allow = NULL; - access_allownum = 0; - } - else - { - if (strcasecmp (w2, "all") == 0) - { - // reset all previous values - if (access_allow) - free (access_allow); - // set to all - CREATE (access_allow, char, ACO_STRSIZE); - access_allownum = 1; - } - else if (w2[0] - && !(access_allownum == 1 - && access_allow[0] == '\0')) - { // don't add IP if already 'all' - if (access_allow) - RECREATE (access_allow, char, (access_allownum + 1) * ACO_STRSIZE); - else - CREATE (access_allow, char, ACO_STRSIZE); - strncpy (access_allow + - (access_allownum++) * ACO_STRSIZE, w2, - ACO_STRSIZE); - access_allow[access_allownum * ACO_STRSIZE - 1] = - '\0'; - } - } - } - else if (strcasecmp (w1, "deny") == 0) - { - if (strcasecmp (w2, "clear") == 0) - { - if (access_deny) - free (access_deny); - access_deny = NULL; - access_denynum = 0; - } - else - { - if (strcasecmp (w2, "all") == 0) - { - // reset all previous values - if (access_deny) - free (access_deny); - // set to all - CREATE (access_deny, char, ACO_STRSIZE); - access_denynum = 1; - } - else if (w2[0] - && !(access_denynum == 1 - && access_deny[0] == '\0')) - { // don't add IP if already 'all' - if (access_deny) - RECREATE (access_deny, char, (access_denynum + 1) * ACO_STRSIZE); - else - CREATE (access_deny, char, ACO_STRSIZE); - strncpy (access_deny + - (access_denynum++) * ACO_STRSIZE, w2, - ACO_STRSIZE); - access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; - } - } - } - else if (strcasecmp (w1, "anti_freeze_enable") == 0) - { - anti_freeze_enable = config_switch (w2); - } - else if (strcasecmp (w1, "anti_freeze_interval") == 0) - { - ANTI_FREEZE_INTERVAL = atoi (w2); - if (ANTI_FREEZE_INTERVAL < 5) - ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds - } - else if (strcasecmp (w1, "import") == 0) - { - login_config_read (w2); - } - else if (strcasecmp (w1, "update_host") == 0) - { - strncpy (update_host, w2, sizeof (update_host)); - update_host[sizeof (update_host) - 1] = '\0'; - } - else if (strcasecmp (w1, "main_server") == 0) - { - strncpy (main_server, w2, sizeof (main_server)); - main_server[sizeof (main_server) - 1] = '\0'; - } - } - } - fclose_ (fp); - - printf ("---End reading of Login Server configuration file.\n"); - - return 0; -} - -//------------------------------------- -// Displaying of configuration warnings -//------------------------------------- -void display_conf_warnings (void) -{ - if (admin_state != 0 && admin_state != 1) - { - printf - ("***WARNING: Invalid value for admin_state parameter -> set to 0 (no remote admin).\n"); - admin_state = 0; - } - - if (admin_state == 1) - { - if (admin_pass[0] == '\0') - { - printf - ("***WARNING: Administrator password is void (admin_pass).\n"); - } - else if (strcmp (admin_pass, "admin") == 0) - { - printf - ("***WARNING: You are using the default administrator password (admin_pass).\n"); - printf (" We highly recommend that you change it.\n"); - } - } - - if (gm_pass[0] == '\0') - { - printf ("***WARNING: 'To GM become' password is void (gm_pass).\n"); - printf - (" We highly recommend that you set one password.\n"); - } - else if (strcmp (gm_pass, "gm") == 0) - { - printf - ("***WARNING: You are using the default GM password (gm_pass).\n"); - printf (" We highly recommend that you change it.\n"); - } - - if (level_new_gm < 0 || level_new_gm > 99) - { - printf - ("***WARNING: Invalid value for level_new_gm parameter -> set to 60 (default).\n"); - level_new_gm = 60; - } - - if (new_account_flag != 0 && new_account_flag != 1) - { - printf - ("***WARNING: Invalid value for new_account parameter -> set to 0 (no new account).\n"); - new_account_flag = 0; - } - - if (login_port < 1024 || login_port > 65535) - { - printf - ("***WARNING: Invalid value for login_port parameter -> set to 6900 (default).\n"); - login_port = 6900; - } - - if (gm_account_filename_check_timer < 0) - { - printf - ("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); - printf (" -> set to 15 sec (default).\n"); - gm_account_filename_check_timer = 15; - } - else if (gm_account_filename_check_timer == 1) - { - printf - ("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); - printf (" -> set to 2 sec (minimum value).\n"); - gm_account_filename_check_timer = 2; - } - - if (save_unknown_packets != 0 && save_unknown_packets != 1) - { - printf - ("WARNING: Invalid value for save_unknown_packets parameter -> set to 0-no save.\n"); - save_unknown_packets = 0; - } - - if (display_parse_login != 0 && display_parse_login != 1) - { // 0: no, 1: yes - printf - ("***WARNING: Invalid value for display_parse_login parameter\n"); - printf (" -> set to 0 (no display).\n"); - display_parse_login = 0; - } - - if (display_parse_admin != 0 && display_parse_admin != 1) - { // 0: no, 1: yes - printf - ("***WARNING: Invalid value for display_parse_admin parameter\n"); - printf (" -> set to 0 (no display).\n"); - display_parse_admin = 0; - } - - if (display_parse_fromchar < 0 || display_parse_fromchar > 2) - { // 0: no, 1: yes (without packet 0x2714), 2: all packets - printf - ("***WARNING: Invalid value for display_parse_fromchar parameter\n"); - printf (" -> set to 0 (no display).\n"); - display_parse_fromchar = 0; - } - - if (min_level_to_connect < 0) - { // 0: all players, 1-99 at least gm level x - printf - ("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", - min_level_to_connect); - printf (" -> set to 0 (any player).\n"); - min_level_to_connect = 0; - } - else if (min_level_to_connect > 99) - { // 0: all players, 1-99 at least gm level x - printf - ("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", - min_level_to_connect); - printf (" -> set to 99 (only GM level 99).\n"); - min_level_to_connect = 99; - } - - if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) - { // 0: no, 1: yes - printf - ("***WARNING: Invalid value for add_to_unlimited_account parameter\n"); - printf - (" -> set to 0 (impossible to add a time to an unlimited account).\n"); - add_to_unlimited_account = 0; - } - - if (start_limited_time < -1) - { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time - printf - ("***WARNING: Invalid value for start_limited_time parameter\n"); - printf - (" -> set to -1 (new accounts are created with unlimited time).\n"); - start_limited_time = -1; - } - - if (check_ip_flag != 0 && check_ip_flag != 1) - { // 0: no, 1: yes - printf ("***WARNING: Invalid value for check_ip_flag parameter\n"); - printf - (" -> set to 1 (check players ip between login-server & char-server).\n"); - check_ip_flag = 1; - } - - if (access_order == ACO_DENY_ALLOW) - { - if (access_denynum == 1 && access_deny[0] == '\0') - { - printf - ("***WARNING: The IP security order is 'deny,allow' (allow if not deny).\n"); - printf (" And you refuse ALL IP.\n"); - } - } - else if (access_order == ACO_ALLOW_DENY) - { - if (access_allownum == 0) - { - printf - ("***WARNING: The IP security order is 'allow,deny' (deny if not allow).\n"); - printf (" But, NO IP IS AUTHORISED!\n"); - } - } - else - { // ACO_MUTUAL_FAILTURE - if (access_allownum == 0) - { - printf - ("***WARNING: The IP security order is 'mutual-failture'\n"); - printf - (" (allow if in the allow list and not in the deny list).\n"); - printf (" But, NO IP IS AUTHORISED!\n"); - } - else if (access_denynum == 1 && access_deny[0] == '\0') - { - printf ("***WARNING: The IP security order is mutual-failture\n"); - printf - (" (allow if in the allow list and not in the deny list).\n"); - printf (" But, you refuse ALL IP!\n"); - } - } - - return; -} - -//------------------------------- -// Save configuration in log file -//------------------------------- -void save_config_in_log (void) -{ - int i; - - // a newline in the log... - login_log (""); - login_log ("The login-server starting...\n"); - - // save configuration in log file - login_log ("The configuration of the server is set:\n"); - - if (admin_state != 1) - login_log ("- with no remote administration.\n"); - else if (admin_pass[0] == '\0') - login_log ("- with a remote administration with a VOID password.\n"); - else if (strcmp (admin_pass, "admin") == 0) - login_log ("- with a remote administration with the DEFAULT password.\n"); - else - login_log - ("- with a remote administration with the password of %d character(s).\n", - strlen (admin_pass)); - if (access_ladmin_allownum == 0 - || (access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) - { - login_log ("- to accept any IP for remote administration\n"); - } - else - { - login_log ("- to accept following IP for remote administration:\n"); - for (i = 0; i < access_ladmin_allownum; i++) - login_log (" %s\n", - (char *) (access_ladmin_allow + i * ACO_STRSIZE)); - } - - if (gm_pass[0] == '\0') - login_log ("- with a VOID 'To GM become' password (gm_pass).\n"); - else if (strcmp (gm_pass, "gm") == 0) - login_log ("- with the DEFAULT 'To GM become' password (gm_pass).\n"); - else - login_log - ("- with a 'To GM become' password (gm_pass) of %d character(s).\n", - strlen (gm_pass)); - if (level_new_gm == 0) - login_log ("- to refuse any creation of GM with @gm.\n"); - else - login_log ("- to create GM with level '%d' when @gm is used.\n", - level_new_gm); - - if (new_account_flag == 1) - login_log ("- to ALLOW new users (with _F/_M).\n"); - else - login_log ("- to NOT ALLOW new users (with _F/_M).\n"); - login_log ("- with port: %d.\n", login_port); - login_log ("- with the accounts file name: '%s'.\n", - account_filename); - login_log ("- with the GM accounts file name: '%s'.\n", - GM_account_filename); - if (gm_account_filename_check_timer == 0) - login_log ("- to NOT check GM accounts file modifications.\n"); - else - login_log - ("- to check GM accounts file modifications every %d seconds.\n", - gm_account_filename_check_timer); - - // not necessary to log the 'login_log_filename', we are inside :) - - login_log ("- with the unknown packets file name: '%s'.\n", - login_log_unknown_packets_filename); - if (save_unknown_packets) - login_log ("- to SAVE all unkown packets.\n"); - else - login_log - ("- to SAVE only unkown packets sending by a char-server or a remote administration.\n"); - if (display_parse_login) - login_log ("- to display normal parse packets on console.\n"); - else - login_log ("- to NOT display normal parse packets on console.\n"); - if (display_parse_admin) - login_log ("- to display administration parse packets on console.\n"); - else - login_log ("- to NOT display administration parse packets on console.\n"); - if (display_parse_fromchar) - login_log ("- to display char-server parse packets on console.\n"); - else - login_log ("- to NOT display char-server parse packets on console.\n"); - - if (min_level_to_connect == 0) // 0: all players, 1-99 at least gm level x - login_log ("- with no minimum level for connection.\n"); - else if (min_level_to_connect == 99) - login_log ("- to accept only GM with level 99.\n"); - else - login_log ("- to accept only GM with level %d or more.\n", - min_level_to_connect); - - if (add_to_unlimited_account) - login_log - ("- to authorize adjustment (with timeadd ladmin) on an unlimited account.\n"); - else - login_log - ("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before.\n"); - - if (start_limited_time < 0) - login_log ("- to create new accounts with an unlimited time.\n"); - else if (start_limited_time == 0) - login_log - ("- to create new accounts with a limited time: time of creation.\n"); - else - login_log - ("- to create new accounts with a limited time: time of creation + %d second(s).\n", - start_limited_time); - - if (check_ip_flag) - login_log - ("- with control of players IP between login-server and char-server.\n"); - else - login_log - ("- to not check players IP between login-server and char-server.\n"); - - if (access_order == ACO_DENY_ALLOW) - { - if (access_denynum == 0) - { - login_log - ("- with the IP security order: 'deny,allow' (allow if not deny). You refuse no IP.\n"); - } - else if (access_denynum == 1 && access_deny[0] == '\0') - { - login_log - ("- with the IP security order: 'deny,allow' (allow if not deny). You refuse ALL IP.\n"); - } - else - { - login_log - ("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:\n"); - for (i = 0; i < access_denynum; i++) - login_log (" %s\n", - (char *) (access_deny + i * ACO_STRSIZE)); - } - } - else if (access_order == ACO_ALLOW_DENY) - { - if (access_allownum == 0) - { - login_log - ("- with the IP security order: 'allow,deny' (deny if not allow). But, NO IP IS AUTHORISED!\n"); - } - else if (access_allownum == 1 && access_allow[0] == '\0') - { - login_log - ("- with the IP security order: 'allow,deny' (deny if not allow). You authorise ALL IP.\n"); - } - else - { - login_log - ("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:\n"); - for (i = 0; i < access_allownum; i++) - login_log (" %s\n", - (char *) (access_allow + i * ACO_STRSIZE)); - } - } - else - { // ACO_MUTUAL_FAILTURE - login_log - ("- with the IP security order: 'mutual-failture' (allow if in the allow list and not in the deny list).\n"); - if (access_allownum == 0) - { - login_log (" But, NO IP IS AUTHORISED!\n"); - } - else if (access_denynum == 1 && access_deny[0] == '\0') - { - login_log (" But, you refuse ALL IP!\n"); - } - else - { - if (access_allownum == 1 && access_allow[0] == '\0') - { - login_log (" You authorise ALL IP.\n"); - } - else - { - login_log (" Authorised IP are:\n"); - for (i = 0; i < access_allownum; i++) - login_log (" %s\n", - (char *) (access_allow + i * ACO_STRSIZE)); - } - login_log (" Refused IP are:\n"); - for (i = 0; i < access_denynum; i++) - login_log (" %s\n", - (char *) (access_deny + i * ACO_STRSIZE)); - } - } -} - -//-------------------------------------- -// Function called at exit of the server -//-------------------------------------- -void term_func (void) -{ - int i, fd; - - mmo_auth_sync (); - - free (auth_dat); - free (gm_account_db); - for (i = 0; i < MAX_SERVERS; i++) - { - if ((fd = server_fd[i]) >= 0) - delete_session (fd); - } - delete_session (login_fd); - - login_log - ("----End of login-server (normal end with closing of all files).\n"); -} - -//------------------------------ -// Main function of login-server -//------------------------------ -int do_init (int argc, char **argv) -{ - int i, j; - - // read login-server configuration - login_config_read ((argc > 1) ? argv[1] : LOGIN_CONF_NAME); - display_conf_warnings (); // not in login_config_read, because we can use 'import' option, and display same message twice or more - save_config_in_log (); // not before, because log file name can be changed - login_lan_config_read ((argc > 1) ? argv[1] : LAN_CONF_NAME); - - for (i = 0; i < AUTH_FIFO_SIZE; i++) - auth_fifo[i].delflag = 1; - for (i = 0; i < MAX_SERVERS; i++) - server_fd[i] = -1; - - gm_account_db = numdb_init (); - - read_gm_account (); - mmo_auth_init (); -// set_termfunc (mmo_auth_sync); - set_defaultparse (parse_login); - login_fd = make_listen_port (login_port); - -// add_timer_func_list (check_auth_sync, "check_auth_sync"); - - // Trigger auth sync every 5 minutes - i = add_timer_interval (gettick () + 300000, check_auth_sync, 0, 0, 300000); - - if (anti_freeze_enable > 0) - { -// add_timer_func_list (char_anti_freeze_system, "char_anti_freeze_system"); - i = add_timer_interval (gettick () + 1000, char_anti_freeze_system, 0, - 0, ANTI_FREEZE_INTERVAL * 1000); - } - - // add timer to check GM accounts file modification - j = gm_account_filename_check_timer; - if (j == 0) // if we would not to check, we check every 60 sec, just to have timer (if we change timer, is was not necessary to check if timer already exists) - j = 60; -// add_timer_func_list (check_GM_file, "check_GM_file"); - i = add_timer_interval (gettick () + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed - - login_log - ("The login-server is ready (Server is listening on the port %d).\n", - login_port); - - printf - ("The login-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", - login_port); - - return 0; -} diff --git a/src/login/login.cpp b/src/login/login.cpp new file mode 100644 index 0000000..543f32f --- /dev/null +++ b/src/login/login.cpp @@ -0,0 +1,5038 @@ +// $Id: login.c,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +// new version of the login-server by [Yor] + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> // for stat/lstat/fstat +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/wait.h> + +#include "../common/core.hpp" +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "login.hpp" +#include "../common/mmo.hpp" +#include "../common/version.hpp" +#include "../common/db.hpp" +#include "../common/lock.hpp" +#include "../common/mt_rand.hpp" + +#include "../common/md5calc.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +int account_id_count = START_ACCOUNT_NUM; +int server_num; +int new_account_flag = 0; +int login_port = 6900; +char lan_char_ip[16]; +int subneti[4]; +int subnetmaski[4]; +char update_host[128] = ""; +char main_server[20] = ""; + +char account_filename[1024] = "save/account.txt"; +char GM_account_filename[1024] = "conf/GM_account.txt"; +char login_log_filename[1024] = "log/login.log"; +char login_log_unknown_packets_filename[1024] = + "log/login_unknown_packets.log"; +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +int save_unknown_packets = 0; +long creation_time_GM_account_file; +int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15) + +int display_parse_login = 0; // 0: no, 1: yes +int display_parse_admin = 0; // 0: no, 1: yes +int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets + +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; +int server_freezeflag[MAX_SERVERS]; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 15; + +int login_fd; + +enum +{ + ACO_DENY_ALLOW = 0, + ACO_ALLOW_DENY, + ACO_MUTUAL_FAILTURE, + ACO_STRSIZE = 128, +}; + +int access_order = ACO_DENY_ALLOW; +int access_allownum = 0; +int access_denynum = 0; +char *access_allow = NULL; +char *access_deny = NULL; + +int access_ladmin_allownum = 0; +char *access_ladmin_allow = NULL; + +int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server +int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. +int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) +int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) + +struct login_session_data +{ + int md5keylen; + char md5key[20]; +}; + +#define AUTH_FIFO_SIZE 256 +struct +{ + int account_id, login_id1, login_id2; + int ip, sex, delflag; +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +struct auth_dat +{ + int account_id, sex; + char userid[24], pass[40], lastlogin[24]; + int logincount; + int state; // packet 0x006a value + 1 (0: compte OK) + char email[40]; // e-mail (by default: a@a.com) + char error_message[20]; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + char last_ip[16]; // save of last IP of connection + char memo[255]; // a memo field + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat; + +int auth_num = 0, auth_max = 0; + +int admin_state = 0; +char admin_pass[24] = ""; +char gm_pass[64] = ""; +int level_new_gm = 60; + +static struct dbt *gm_account_db; + +pid_t pid = 0; // For forked DB writes + + +#define VERSION_2_UPDATEHOST 0x01 // client supports updatehost +#define VERSION_2_SERVERORDER 0x02 // send servers in forward order +//------------------------------ +// Writing function of logs file +//------------------------------ +int login_log (char *fmt, ...) +{ + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start (ap, fmt); + + logfp = fopen_ (login_log_filename, "a"); + if (logfp) + { + if (fmt[0] == '\0') // jump a line if no message + fprintf (logfp, "\n"); + else + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 24, date_format, gmtime (&(tv.tv_sec))); + sprintf (tmpstr + strlen (tmpstr), ".%03d: %s", + (int) tv.tv_usec / 1000, fmt); + vfprintf (logfp, tmpstr, ap); + } + fclose_ (logfp); + } + + va_end (ap); + return 0; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM (int account_id) +{ + struct gm_account *p = (struct gm_account*) numdb_search (gm_account_db, account_id); + if (p == NULL) + return 0; + return p->level; +} + +//------------------------------------------------------- +// Reading function of GM accounts file (and their level) +//------------------------------------------------------- +int read_gm_account (void) +{ + char line[512]; + struct gm_account *p; + FILE *fp; + int c = 0; + int GM_level; + struct stat file_stat; + + free (gm_account_db); + gm_account_db = numdb_init (); + + // get last modify time/date + if (stat (GM_account_filename, &file_stat)) + creation_time_GM_account_file = 0; // error + else + creation_time_GM_account_file = file_stat.st_mtime; + + if ((fp = fopen_ (GM_account_filename, "r")) == NULL) + { + printf ("read_gm_account: GM accounts file [%s] not found.\n", + GM_account_filename); + printf + (" Actually, there is no GM accounts on the server.\n"); + login_log ("read_gm_account: GM accounts file [%s] not found.\n", + GM_account_filename); + login_log + (" Actually, there is no GM accounts on the server.\n"); + return 1; + } + // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) + // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) + while (fgets (line, sizeof (line) - 1, fp) && c < 4000) + { + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' + || line[0] == '\n' || line[0] == '\r') + continue; + CREATE (p, struct gm_account, 1); + if (sscanf (line, "%d %d", &p->account_id, &p->level) != 2 + && sscanf (line, "%d: %d", &p->account_id, &p->level) != 2) + printf + ("read_gm_account: file [%s], invalid 'id_acount level' format.\n", + GM_account_filename); + else if (p->level <= 0) + printf + ("read_gm_account: file [%s] %dth account (invalid level [0 or negative]: %d).\n", + GM_account_filename, c + 1, p->level); + else + { + if (p->level > 99) + { + printf + ("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", + GM_account_filename, c + 1, p->level); + p->level = 99; + } + if ((GM_level = isGM (p->account_id)) > 0) + { // if it's not a new account + if (GM_level == p->level) + printf + ("read_gm_account: GM account %d defined twice (same level: %d).\n", + p->account_id, p->level); + else + printf + ("read_gm_account: GM account %d defined twice (levels: %d and %d).\n", + p->account_id, GM_level, p->level); + } + if (GM_level != p->level) + { // if new account or new level + numdb_insert (gm_account_db, p->account_id, p); + //printf("GM account:%d, level: %d->%d\n", p->account_id, GM_level, p->level); + if (GM_level == 0) + { // if new account + c++; + if (c >= 4000) + { + printf + ("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); + login_log + ("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); + } + } + } + } + } + fclose_ (fp); + + printf ("read_gm_account: file '%s' readed (%d GM accounts found).\n", + GM_account_filename, c); + login_log ("read_gm_account: file '%s' readed (%d GM accounts found).\n", + GM_account_filename, c); + + return 0; +} + +//-------------------------------------------------------------- +// Test of the IP mask +// (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) +//-------------------------------------------------------------- +int check_ipmask (unsigned int ip, const unsigned char *str) +{ + unsigned int mask = 0, i = 0, m, ip2, a0, a1, a2, a3; + unsigned char *p = (unsigned char *) &ip2, *p2 = (unsigned char *) &mask; + + if (sscanf (str, "%d.%d.%d.%d/%n", &a0, &a1, &a2, &a3, &i) != 4 || i == 0) + return 0; + p[0] = a0; + p[1] = a1; + p[2] = a2; + p[3] = a3; + + if (sscanf (str + i, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) == 4) + { + p2[0] = a0; + p2[1] = a1; + p2[2] = a2; + p2[3] = a3; + mask = ntohl (mask); + } + else if (sscanf (str + i, "%d", &m) == 1 && m >= 0 && m <= 32) + { + for (i = 0; i < m && i < 32; i++) + mask = (mask >> 1) | 0x80000000; + } + else + { + printf ("check_ipmask: invalid mask [%s].\n", str); + return 0; + } + +// printf("Tested IP: %08x, network: %08x, network mask: %08x\n", +// (unsigned int)ntohl(ip), (unsigned int)ntohl(ip2), (unsigned int)mask); + return ((ntohl (ip) & mask) == (ntohl (ip2) & mask)); +} + +//--------------------- +// Access control by IP +//--------------------- +int check_ip (unsigned int ip) +{ + int i; + unsigned char *p = (unsigned char *) &ip; + char buf[32]; + enum + { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; + + if (access_allownum == 0 && access_denynum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf (buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for (i = 0; i < access_allownum; i++) + { + const char *p = access_allow + i * ACO_STRSIZE; + if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) + { + flag = ACF_ALLOW; + if (access_order == ACO_ALLOW_DENY) + return 1; // With 'allow, deny' (deny if not allow), allow has priority + break; + } + } + + for (i = 0; i < access_denynum; i++) + { + const char *p = access_deny + i * ACO_STRSIZE; + if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) + { + flag = ACF_DENY; + return 0; // At this point, if it's 'deny', we refuse connection. + break; + } + } + + return (flag == ACF_ALLOW || access_order == ACO_DENY_ALLOW) ? 1 : 0; + // With 'mutual-failture', only 'allow' and non 'deny' IP are authorised. + // A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised. + // So, it's disapproval if you have no description at the time of 'mutual-failture'. + // With 'deny,allow' (allow if not deny), because here it's not deny, we authorise. +} + +//-------------------------------- +// Access control by IP for ladmin +//-------------------------------- +int check_ladminip (unsigned int ip) +{ + int i; + unsigned char *p = (unsigned char *) &ip; + char buf[32]; + + if (access_ladmin_allownum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf (buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for (i = 0; i < access_ladmin_allownum; i++) + { + const char *p = access_ladmin_allow + i * ACO_STRSIZE; + if (memcmp (p, buf, strlen (p)) == 0 || check_ipmask (ip, p)) + { + return 1; + } + } + + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars (unsigned char *str) +{ + int i; + int change = 0; + + for (i = 0; str[i]; i++) + { + if (str[i] < 32) + { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check (unsigned char *email) +{ + char ch; + unsigned 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; +} + +//----------------------------------------------- +// Search an account id +// (return account index or -1 (if not found)) +// If exact account name is not found, +// the function checks without case sensitive +// and returns index if only 1 account is found +// and similar to the searched name. +//----------------------------------------------- +int search_account_index (char *account_name) +{ + int i, quantity, index; + + quantity = 0; + index = -1; + for (i = 0; i < auth_num; i++) + { + // Without case sensitive check (increase the number of similar account names found) + if (strcasecmp (auth_dat[i].userid, account_name) == 0) + { + // Strict comparison (if found, we finish the function immediatly with correct value) + if (strcmp (auth_dat[i].userid, account_name) == 0) + return i; + quantity++; + index = i; + } + } + // Here, the exact account name is not found + // We return the found index of a similar account ONLY if there is 1 similar account + if (quantity == 1) + return index; + + // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found + return -1; +} + +//-------------------------------------------------------- +// Create a string to save the account in the account file +//-------------------------------------------------------- +int mmo_auth_tostr (char *str, struct auth_dat *p) +{ + int i; + char *str_p = str; + + str_p += sprintf (str_p, "%d\t%s\t%s\t%s\t%c\t%d\t%d\t" + "%s\t%s\t%ld\t%s\t%s\t%ld\t", + p->account_id, p->userid, p->pass, p->lastlogin, + (p->sex == 2) ? 'S' : (p->sex ? 'M' : 'F'), + p->logincount, p->state, + p->email, p->error_message, + p->connect_until_time, p->last_ip, p->memo, + p->ban_until_time); + + for (i = 0; i < p->account_reg2_num; i++) + if (p->account_reg2[i].str[0]) + str_p += + sprintf (str_p, "%s,%d ", p->account_reg2[i].str, + p->account_reg2[i].value); + + return 0; +} + +//--------------------------------- +// Reading of the accounts database +//--------------------------------- +int mmo_auth_init (void) +{ + FILE *fp; + int account_id, logincount, state, n, i, j, v; + char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, + email[2048], error_message[2048], last_ip[2048], memo[2048]; + time_t ban_until_time; + time_t connect_until_time; + char str[2048]; + int GM_count = 0; + int server_count = 0; + + CREATE (auth_dat, struct auth_dat, 256); + auth_max = 256; + + fp = fopen_ (account_filename, "r"); + if (fp == NULL) + { + // no account file -> no account -> no login, including char-server (ERROR) + printf + ("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", + account_filename); + return 0; + } + + while (fgets (line, sizeof (line) - 1, fp) != NULL) + { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof (line) - 1] = '\0'; + p = line; + + // database version reading (v2) + if (((i = sscanf (line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", + &account_id, userid, pass, lastlogin, &sex, + &logincount, &state, email, error_message, + &connect_until_time, last_ip, memo, &ban_until_time, + &n)) == 13 && line[n] == '\t') + || + ((i = + sscanf (line, + "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", &account_id, + userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, + memo, &n)) == 12 && line[n] == '\t')) + { + n = n + 1; + + // Some checks + if (account_id > END_ACCOUNT_NUM) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", + END_ACCOUNT_NUM); + printf + (" account id #%d -> account not read (saved in log file).\033[0m\n", + account_id); + login_log + ("mmmo_auth_init: ******Error: an account has an id higher than %d.\n", + END_ACCOUNT_NUM); + login_log + (" account id #%d -> account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + continue; + } + userid[23] = '\0'; + remove_control_chars (userid); + for (j = 0; j < auth_num; j++) + { + if (auth_dat[j].account_id == account_id) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + printf + (" account id #%d -> new account not read (saved in log file).\033[0m\n", + account_id); + login_log + ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + login_log + (" account id #%d -> new account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + break; + } + else if (strcmp (auth_dat[j].userid, userid) == 0) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); + printf (" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. + printf + (" Account saved in log file.\033[0m\n"); + login_log + ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + login_log + (" account id #%d -> new account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) + { + auth_max += 256; + RECREATE (auth_dat, struct auth_dat, auth_max); + } + + memset (&auth_dat[auth_num], '\0', sizeof (struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy (auth_dat[auth_num].userid, userid, 24); + + memo[254] = '\0'; + remove_control_chars (memo); + strncpy (auth_dat[auth_num].memo, memo, 255); + + pass[39] = '\0'; + remove_control_chars (pass); + // If a password is not encrypted, we encrypt it now. + // A password beginning with ! and - in the memo field is our magic + if (pass[0] != '!' && memo[0] == '-') { + strcpy(auth_dat[auth_num].pass, MD5_saltcrypt(pass, make_salt())); + auth_dat[auth_num].memo[0] = '!'; + printf("encrypting pass: %s %s\n", pass, auth_dat[auth_num].pass); + } + else + strcpy(auth_dat[auth_num].pass, pass); + + lastlogin[23] = '\0'; + remove_control_chars (lastlogin); + strncpy (auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' + || sex == 's') ? 2 : (sex == 'M' + || sex == 'm'); + + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + + if (e_mail_check (email) == 0) + { + printf + ("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", + auth_dat[auth_num].userid, + auth_dat[auth_num].account_id); + strncpy (auth_dat[auth_num].email, "a@a.com", 40); + } + else + { + remove_control_chars (email); + strncpy (auth_dat[auth_num].email, email, 40); + } + + error_message[19] = '\0'; + remove_control_chars (error_message); + if (error_message[0] == '\0' || state != 7) + { // 7, because state is packet 0x006a value + 1 + strncpy (auth_dat[auth_num].error_message, "-", 20); + } + else + { + strncpy (auth_dat[auth_num].error_message, error_message, 20); + } + + if (i == 13) + auth_dat[auth_num].ban_until_time = ban_until_time; + else + auth_dat[auth_num].ban_until_time = 0; + + auth_dat[auth_num].connect_until_time = connect_until_time; + + last_ip[15] = '\0'; + remove_control_chars (last_ip); + strncpy (auth_dat[auth_num].last_ip, last_ip, 16); + + for (j = 0; j < ACCOUNT_REG2_NUM; j++) + { + p += n; + if (sscanf (p, "%[^\t,],%d %n", str, &v, &n) != 2) + { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf (p, ",%d %n", &v, &n) == 1) + { + j--; + continue; + } + else + break; + } + str[31] = '\0'; + remove_control_chars (str); + strncpy (auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM (account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + // Old athena database version reading (v1) + } + else if ((i = + sscanf (line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", + &account_id, userid, pass, lastlogin, &sex, + &logincount, &state, &n)) >= 5) + { + if (account_id > END_ACCOUNT_NUM) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", + END_ACCOUNT_NUM); + printf + (" account id #%d -> account not read (saved in log file).\033[0m\n", + account_id); + login_log + ("mmmo_auth_init: ******Error: an account has an id higher than %d.\n", + END_ACCOUNT_NUM); + login_log + (" account id #%d -> account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + continue; + } + userid[23] = '\0'; + remove_control_chars (userid); + for (j = 0; j < auth_num; j++) + { + if (auth_dat[j].account_id == account_id) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + printf + (" account id #%d -> new account not read (saved in log file).\033[0m\n", + account_id); + login_log + ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + login_log + (" account id #%d -> new account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + break; + } + else if (strcmp (auth_dat[j].userid, userid) == 0) + { + printf + ("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); + printf (" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. + printf + (" Account saved in log file.\033[0m\n"); + login_log + ("mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + login_log + (" account id #%d -> new account not read (saved in next line):\n", + account_id); + login_log ("%s", line); + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) + { + auth_max += 256; + RECREATE (auth_dat, struct auth_dat, auth_max); + } + + memset (&auth_dat[auth_num], '\0', sizeof (struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy (auth_dat[auth_num].userid, userid, 24); + + lastlogin[23] = '\0'; + remove_control_chars (lastlogin); + strncpy (auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' + || sex == 's') ? 2 : (sex == 'M' + || sex == 'm'); + + if (i >= 6) + { + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + } + else + auth_dat[auth_num].logincount = 0; + + if (i >= 7) + { + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + } + else + auth_dat[auth_num].state = 0; + + // Initialization of new data + strncpy (auth_dat[auth_num].email, "a@a.com", 40); + strncpy (auth_dat[auth_num].error_message, "-", 20); + auth_dat[auth_num].ban_until_time = 0; + auth_dat[auth_num].connect_until_time = 0; + strncpy (auth_dat[auth_num].last_ip, "-", 16); + strncpy (auth_dat[auth_num].memo, "!", 255); + + for (j = 0; j < ACCOUNT_REG2_NUM; j++) + { + p += n; + if (sscanf (p, "%[^\t,],%d %n", str, &v, &n) != 2) + { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf (p, ",%d %n", &v, &n) == 1) + { + j--; + continue; + } + else + break; + } + str[31] = '\0'; + remove_control_chars (str); + strncpy (auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM (account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + } + else + { + i = 0; + if (sscanf (line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && + i > 0 && account_id > account_id_count) + account_id_count = account_id; + } + } + fclose_ (fp); + + if (auth_num == 0) + { + printf ("mmo_auth_init: No account found in %s.\n", account_filename); + sprintf (line, "No account found in %s.", account_filename); + } + else + { + if (auth_num == 1) + { + printf ("mmo_auth_init: 1 account read in %s,\n", + account_filename); + sprintf (line, "1 account read in %s,", account_filename); + } + else + { + printf ("mmo_auth_init: %d accounts read in %s,\n", auth_num, + account_filename); + sprintf (line, "%d accounts read in %s,", auth_num, + account_filename); + } + if (GM_count == 0) + { + printf (" of which is no GM account, and "); + sprintf (str, "%s of which is no GM account and", line); + } + else if (GM_count == 1) + { + printf (" of which is 1 GM account, and "); + sprintf (str, "%s of which is 1 GM account and", line); + } + else + { + printf (" of which is %d GM accounts, and ", + GM_count); + sprintf (str, "%s of which is %d GM accounts and", line, + GM_count); + } + if (server_count == 0) + { + printf ("no server account ('S').\n"); + sprintf (line, "%s no server account ('S').", str); + } + else if (server_count == 1) + { + printf ("1 server account ('S').\n"); + sprintf (line, "%s 1 server account ('S').", str); + } + else + { + printf ("%d server accounts ('S').\n", server_count); + sprintf (line, "%s %d server accounts ('S').", str, server_count); + } + } + login_log ("%s\n", line); + + return 0; +} + +//------------------------------------------ +// Writing of the accounts database file +// (accounts are sorted by id before save) +//------------------------------------------ +void mmo_auth_sync (void) +{ + FILE *fp; + int i, j, k, lock; + int id[auth_num]; + char line[65536]; + + // Sorting before save + for (i = 0; i < auth_num; i++) + { + id[i] = i; + for (j = 0; j < i; j++) + { + if (auth_dat[i].account_id < auth_dat[id[j]].account_id) + { + for (k = i; k > j; k--) + id[k] = id[k - 1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + fp = lock_fopen (account_filename, &lock); + if (fp == NULL) + return; + fprintf (fp, + "// Accounts file: here are saved all information about the accounts.\n"); + fprintf (fp, + "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); + fprintf (fp, "// Some explanations:\n"); + fprintf (fp, + "// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"); + fprintf (fp, "// account password: between 4 to 23 char\n"); + fprintf (fp, + "// sex : M or F for normal accounts, S for server accounts\n"); + fprintf (fp, + "// state : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n"); + fprintf (fp, + "// email : between 3 to 39 char (a@a.com is like no email)\n"); + fprintf (fp, + "// error message : text for the state 7: 'Your are Prohibited to login until <text>'. Max 19 char\n"); + fprintf (fp, + "// valitidy time : 0: unlimited account, <other value>: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + fprintf (fp, "// memo field : max 254 char\n"); + fprintf (fp, + "// ban time : 0: no ban, <other value>: banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + for (i = 0; i < auth_num; i++) + { + k = id[i]; // use of sorted index + if (auth_dat[k].account_id < 0) + continue; + + mmo_auth_tostr (line, &auth_dat[k]); + fprintf (fp, "%s\n", line); + } + fprintf (fp, "%d\t%%newid%%\n", account_id_count); + + lock_fclose (fp, account_filename, &lock); + + return; +} + +// We want to sync the DB to disk as little as possible as it's fairly +// resource intensive. therefore most player-triggerable events that +// update the account DB will not immideately trigger a save. Instead +// we save periodicly on a timer. +//----------------------------------------------------- +void check_auth_sync (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + if (pid != 0) + { + int status; + pid_t temp = waitpid (pid, &status, WNOHANG); + + // Need to check status too? + if (temp == 0) + { + return; + } + } + + // This can take a lot of time. Fork a child to handle the work and return at once + // If we're unable to fork just continue running the function normally + if ((pid = fork ()) > 0) + return; + + mmo_auth_sync (); + + // If we're a child we should suicide now. + if (pid == 0) + _exit (0); + + return; +} + +//-------------------------------------------------------------------- +// Packet send to all char-servers, except one (wos: without our self) +//-------------------------------------------------------------------- +int charif_sendallwos (int sfd, unsigned char *buf, unsigned int len) +{ + int i, c; + + for (i = 0, c = 0; i < MAX_SERVERS; i++) + { + int fd; + if ((fd = server_fd[i]) >= 0 && fd != sfd) + { + memcpy (WFIFOP (fd, 0), buf, len); + WFIFOSET (fd, len); + c++; + } + } + return c; +} + +//----------------------------------------------------- +// Send GM accounts to all char-server +//----------------------------------------------------- +void send_GM_accounts (void) +{ + int i; + char buf[32000]; + int GM_value; + int len; + + len = 4; + WBUFW (buf, 0) = 0x2732; + for (i = 0; i < auth_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if ((GM_value = isGM (auth_dat[i].account_id)) > 0) + { + WBUFL (buf, len) = auth_dat[i].account_id; + WBUFB (buf, len + 4) = (unsigned char) GM_value; + len += 5; + } + WBUFW (buf, 2) = len; + charif_sendallwos (-1, buf, len); + + return; +} + +//----------------------------------------------------- +// Check if GM file account have been changed +//----------------------------------------------------- +void check_GM_file (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct stat file_stat; + long new_time; + + // if we would not check + if (gm_account_filename_check_timer < 1) + return; + + // get last modify time/date + if (stat (GM_account_filename, &file_stat)) + new_time = 0; // error + else + new_time = file_stat.st_mtime; + + if (new_time != creation_time_GM_account_file) + { + read_gm_account (); + send_GM_accounts (); + } +} + +//------------------------------------- +// Account creation (with e-mail check) +//------------------------------------- +int mmo_auth_new (struct mmo_account *account, char sex, char *email) +{ + time_t timestamp, timestamp_temp; + struct tm *tmtime; + int i = auth_num; + + if (auth_num >= auth_max) + { + auth_max += 256; + RECREATE (auth_dat, struct auth_dat, auth_max); + } + + memset (&auth_dat[i], '\0', sizeof (struct auth_dat)); + + while (isGM (account_id_count) > 0) + account_id_count++; + + auth_dat[i].account_id = account_id_count++; + + strncpy (auth_dat[i].userid, account->userid, 24); + auth_dat[i].userid[23] = '\0'; + + strcpy(auth_dat[i].pass, MD5_saltcrypt(account->passwd, make_salt())); + auth_dat[i].pass[39] = '\0'; + + memcpy (auth_dat[i].lastlogin, "-", 2); + + auth_dat[i].sex = (sex == 'M'); + + auth_dat[i].logincount = 0; + + auth_dat[i].state = 0; + + if (e_mail_check (email) == 0) + strncpy (auth_dat[i].email, "a@a.com", 40); + else + strncpy (auth_dat[i].email, email, 40); + + strncpy (auth_dat[i].error_message, "-", 20); + + auth_dat[i].ban_until_time = 0; + + if (start_limited_time < 0) + auth_dat[i].connect_until_time = 0; // unlimited + else + { // limited time + timestamp = time (NULL) + start_limited_time; + // double conversion to be sure that it is possible + tmtime = gmtime (×tamp); + timestamp_temp = mktime (tmtime); + if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour) + auth_dat[i].connect_until_time = timestamp_temp; + else + auth_dat[i].connect_until_time = 0; // unlimited + } + + strncpy (auth_dat[i].last_ip, "-", 16); + + strncpy (auth_dat[i].memo, "!", 255); + + auth_dat[i].account_reg2_num = 0; + + auth_num++; + + return (account_id_count - 1); +} + +//--------------------------------------- +// Check/authentification of a connection +//--------------------------------------- +int mmo_auth (struct mmo_account *account, int fd) +{ + int i; + struct timeval tv; + char tmpstr[256]; + int len, newaccount = 0; +#ifdef PASSWDENC + char md5str[64], md5bin[32]; +#endif + char ip[16]; + unsigned char *sin_addr = + (unsigned char *) &session[fd]->client_addr.sin_addr; + + sprintf (ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], + sin_addr[3]); + + len = strlen (account->userid) - 2; + // Account creation with _M/_F + if (account->passwdenc == 0 && account->userid[len] == '_' && + (account->userid[len + 1] == 'F' || account->userid[len + 1] == 'M') + && new_account_flag == 1 && account_id_count <= END_ACCOUNT_NUM + && len >= 4 && strlen (account->passwd) >= 4) + { + if (new_account_flag == 1) + newaccount = 1; + account->userid[len] = '\0'; + } + + // Strict account search + for (i = 0; i < auth_num; i++) + { + if (strcmp (account->userid, auth_dat[i].userid) == 0) + break; + } + // if there is no creation request and strict account search fails, we do a no sensitive case research for index + if (newaccount == 0 && i == auth_num) + { + i = search_account_index (account->userid); + if (i == -1) + i = auth_num; + else + memcpy (account->userid, auth_dat[i].userid, 24); // for the possible tests/checks afterwards (copy correcte sensitive case). + } + + if (i != auth_num) + { + int encpasswdok = 0; + struct login_session_data *ld; + if (newaccount) + { + login_log + ("Attempt of creation of an already existant account (account: %s_%c, ip: %s)\n", + account->userid, account->userid[len + 1], ip); + return 9; // 9 = Account already exists + } + ld = (struct login_session_data*) session[fd]->session_data; +#ifdef PASSWORDENC + if (account->passwdenc > 0) + { + int j = account->passwdenc; + if (!ld) + { + login_log ("Md5 key not created (account: %s, ip: %s)\n", + account->userid, ip); + return 1; // 1 = Incorrect Password + } + if (j > 2) + j = 1; + do + { + if (j == 1) + { + strncpy (md5str, ld->md5key, sizeof (ld->md5key)); // 20 + strcat (md5str, auth_dat[i].pass); // 24 + } + else if (j == 2) + { + strncpy (md5str, auth_dat[i].pass, sizeof (auth_dat[i].pass)); // 24 + strcat (md5str, ld->md5key); // 20 + } + else + md5str[0] = '\0'; + md5str[sizeof (md5str) - 1] = '\0'; // 64 + MD5_String2binary (md5str, md5bin); + encpasswdok = (memcmp (account->passwd, md5bin, 16) == 0); + } + while (j < 2 && !encpasswdok && (j++) != account->passwdenc); +// printf("key[%s] md5 [%s] ", md5key, md5); +// printf("client [%s] accountpass [%s]\n", account->passwd, auth_dat[i].pass); + } +#endif + if ((!pass_ok (account->passwd, auth_dat[i].pass)) && !encpasswdok) + { + if (account->passwdenc == 0) + login_log + ("Invalid password (account: %s, ip: %s)\n", + account->userid, ip); + +#ifdef PASSWORDENC + else + { + char logbuf[512], *p = logbuf; + int j; + p += sprintf (p, + "Invalid password (account: %s, received md5[", + account->userid); + for (j = 0; j < 16; j++) + p += sprintf (p, "%02x", + ((unsigned char *) account->passwd)[j]); + p += sprintf (p, "] calculated md5["); + for (j = 0; j < 16; j++) + p += sprintf (p, "%02x", ((unsigned char *) md5bin)[j]); + p += sprintf (p, "] md5 key["); + for (j = 0; j < ld->md5keylen; j++) + p += sprintf (p, "%02x", + ((unsigned char *) ld->md5key)[j]); + p += sprintf (p, "], ip: %s)\n", ip); + login_log (logbuf); + } +#endif + return 1; // 1 = Incorrect Password + } + + if (auth_dat[i].state) + { + login_log + ("Connection refused (account: %s, state: %d, ip: %s)\n", + account->userid, auth_dat[i].state, + ip); + switch (auth_dat[i].state) + { // packet 0x006a value + 1 + case 1: // 0 = Unregistered ID + case 2: // 1 = Incorrect Password + case 3: // 2 = This ID is expired + case 4: // 3 = Rejected from Server + case 5: // 4 = You have been blocked by the GM Team + case 6: // 5 = Your Game's EXE file is not the latest version + case 7: // 6 = Your are Prohibited to log in until %s + case 8: // 7 = Server is jammed due to over populated + case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) + case 100: // 99 = This ID has been totally erased + return auth_dat[i].state - 1; + break; + default: + return 99; // 99 = ID has been totally erased + break; + } + } + + if (auth_dat[i].ban_until_time != 0) + { // if account is banned + strftime (tmpstr, 20, date_format, + gmtime (&auth_dat[i].ban_until_time)); + tmpstr[19] = '\0'; + if (auth_dat[i].ban_until_time > time (NULL)) + { // always banned + login_log + ("Connection refused (account: %s, banned until %s, ip: %s)\n", + account->userid, tmpstr, ip); + return 6; // 6 = Your are Prohibited to log in until %s + } + else + { // ban is finished + login_log + ("End of ban (account: %s, previously banned until %s -> not more banned, ip: %s)\n", + account->userid, tmpstr, ip); + auth_dat[i].ban_until_time = 0; // reset the ban time + } + } + + if (auth_dat[i].connect_until_time != 0 + && auth_dat[i].connect_until_time < time (NULL)) + { + login_log + ("Connection refused (account: %s, expired ID, ip: %s)\n", + account->userid, ip); + return 2; // 2 = This ID is expired + } + + login_log ("Authentification accepted (account: %s (id: %d), ip: %s)\n", + account->userid, auth_dat[i].account_id, ip); + } + else + { + if (newaccount == 0) + { + login_log + ("Unknown account (account: %s, ip: %s)\n", + account->userid, ip); + return 0; // 0 = Unregistered ID + } + else + { + int new_id = + mmo_auth_new (account, account->userid[len + 1], "a@a.com"); + login_log + ("Account creation and authentification accepted (account %s (id: %d), sex: %c, connection with _F/_M, ip: %s)\n", + account->userid, new_id, + account->userid[len + 1], ip); + } + } + + gettimeofday (&tv, NULL); + strftime (tmpstr, 24, date_format, gmtime (&(tv.tv_sec))); + sprintf (tmpstr + strlen (tmpstr), ".%03d", (int) tv.tv_usec / 1000); + + account->account_id = auth_dat[i].account_id; + account->login_id1 = mt_random (); + account->login_id2 = mt_random (); + memcpy (account->lastlogin, auth_dat[i].lastlogin, 24); + memcpy (auth_dat[i].lastlogin, tmpstr, 24); + account->sex = auth_dat[i].sex; + strncpy (auth_dat[i].last_ip, ip, 16); + auth_dat[i].logincount++; + + return -1; // account OK +} + +//------------------------------- +// Char-server anti-freeze system +//------------------------------- +void char_anti_freeze_system (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int i; + + //printf("Entering in char_anti_freeze_system function to check freeze of servers.\n"); + for (i = 0; i < MAX_SERVERS; i++) + { + if (server_fd[i] >= 0) + { // if char-server is online + //printf("char_anti_freeze_system: server #%d '%s', flag: %d.\n", i, server[i].name, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) + { // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + printf + ("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection.\n", + i, server[i].name); + login_log + ("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection.\n", + i, server[i].name); + session[server_fd[i]]->eof = 1; + } + } + } +} + +//-------------------------------- +// Packet parsing for char-servers +//-------------------------------- +void parse_fromchar (int fd) +{ + int i, j, id; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char ip[16]; + + sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + for (id = 0; id < MAX_SERVERS; id++) + if (server_fd[id] == fd) + break; + if (id == MAX_SERVERS || session[fd]->eof) + { + if (id < MAX_SERVERS) + { + printf ("Char-server '%s' has disconnected.\n", server[id].name); + login_log ("Char-server '%s' has disconnected (ip: %s).\n", + server[id].name, ip); + server_fd[id] = -1; + memset (&server[id], 0, sizeof (struct mmo_char_server)); + } + close (fd); + delete_session (fd); + return; + } + + while (RFIFOREST (fd) >= 2) + { + if (display_parse_fromchar == 2 || (display_parse_fromchar == 1 && RFIFOW (fd, 0) != 0x2714)) // 0x2714 is done very often (number of players) + printf + ("parse_fromchar: connection #%d, packet: 0x%x (with being read: %d bytes).\n", + fd, RFIFOW (fd, 0), RFIFOREST (fd)); + + switch (RFIFOW (fd, 0)) + { + // request from map-server via char-server to reload GM accounts (by Yor). + case 0x2709: + login_log + ("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n", + server[id].name, ip); + read_gm_account (); + // send GM accounts to all char-servers + send_GM_accounts (); + RFIFOSKIP (fd, 2); + break; + + case 0x2712: // request from char-server to authentify an account + if (RFIFOREST (fd) < 19) + return; + { + int acc; + acc = RFIFOL (fd, 2); // speed up + for (i = 0; i < AUTH_FIFO_SIZE; i++) + { + if (auth_fifo[i].account_id == acc && + auth_fifo[i].login_id1 == RFIFOL (fd, 6) && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == RFIFOL (fd, 10) && // relate to the versions higher than 18 +#endif + auth_fifo[i].sex == RFIFOB (fd, 14) && + (!check_ip_flag + || auth_fifo[i].ip == RFIFOL (fd, 15)) + && !auth_fifo[i].delflag) + { + int p, k; + auth_fifo[i].delflag = 1; + login_log + ("Char-server '%s': authentification of the account %d accepted (ip: %s).\n", + server[id].name, acc, ip); +// printf("%d\n", i); + for (k = 0; k < auth_num; k++) + { + if (auth_dat[k].account_id == acc) + { + WFIFOW (fd, 0) = 0x2729; // Sending of the account_reg2 + WFIFOL (fd, 4) = acc; + for (p = 8, j = 0; + j < auth_dat[k].account_reg2_num; + p += 36, j++) + { + memcpy (WFIFOP (fd, p), + auth_dat[k]. + account_reg2[j].str, 32); + WFIFOL (fd, p + 32) = + auth_dat[k].account_reg2[j].value; + } + WFIFOW (fd, 2) = p; + WFIFOSET (fd, p); +// printf("parse_fromchar: Sending of account_reg2: login->char (auth fifo)\n"); + WFIFOW (fd, 0) = 0x2713; + WFIFOL (fd, 2) = acc; + WFIFOB (fd, 6) = 0; + memcpy (WFIFOP (fd, 7), auth_dat[k].email, + 40); + WFIFOL (fd, 47) = + (unsigned long) + auth_dat[k].connect_until_time; + WFIFOSET (fd, 51); + break; + } + } + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) + { + login_log + ("Char-server '%s': authentification of the account %d REFUSED (ip: %s).\n", + server[id].name, acc, ip); + WFIFOW (fd, 0) = 0x2713; + WFIFOL (fd, 2) = acc; + WFIFOB (fd, 6) = 1; + // It is unnecessary to send email + // It is unnecessary to send validity date of the account + WFIFOSET (fd, 51); + } + } + RFIFOSKIP (fd, 19); + break; + + case 0x2714: + if (RFIFOREST (fd) < 6) + return; + //printf("parse_fromchar: Receiving of the users number of the server '%s': %d\n", server[id].name, RFIFOL(fd,2)); + server[id].users = RFIFOL (fd, 2); + if (anti_freeze_enable) + server_freezeflag[id] = 5; // Char anti-freeze system. Counter. 5 ok, 4...0 freezed + RFIFOSKIP (fd, 6); + break; + + // we receive a e-mail creation of an account with a default e-mail (no answer) + case 0x2715: + { + int acc; + char email[40]; + if (RFIFOREST (fd) < 46) + return; + acc = RFIFOL (fd, 2); // speed up + memcpy (email, RFIFOP (fd, 6), 40); + email[39] = '\0'; + remove_control_chars (email); + //printf("parse_fromchar: an e-mail creation of an account with a default e-mail: server '%s', account: %d, e-mail: '%s'.\n", server[id].name, acc, RFIFOP(fd,6)); + if (e_mail_check (email) == 0) + login_log + ("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", + server[id].name, acc, ip); + else + { + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc + && (strcmp (auth_dat[i].email, "a@a.com") == 0 + || auth_dat[i].email[0] == '\0')) + { + memcpy (auth_dat[i].email, email, 40); + login_log + ("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", + server[id].name, acc, email, ip); + break; + } + } + if (i == auth_num) + login_log + ("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", + server[id].name, acc, ip); + } + RFIFOSKIP (fd, 46); + break; + + // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server + } + case 0x2716: + if (RFIFOREST (fd) < 6) + return; + //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == RFIFOL (fd, 2)) + { + login_log + ("Char-server '%s': e-mail of the account %d found (ip: %s).\n", + server[id].name, RFIFOL (fd, 2), ip); + WFIFOW (fd, 0) = 0x2717; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + memcpy (WFIFOP (fd, 6), auth_dat[i].email, 40); + WFIFOL (fd, 46) = + (unsigned long) auth_dat[i].connect_until_time; + WFIFOSET (fd, 50); + break; + } + } + if (i == auth_num) + { + login_log + ("Char-server '%s': e-mail of the account %d NOT found (ip: %s).\n", + server[id].name, RFIFOL (fd, 2), ip); + } + RFIFOSKIP (fd, 6); + break; + + case 0x2720: // To become GM request + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + int acc; + unsigned char buf[10]; + FILE *fp; + acc = RFIFOL (fd, 4); + //printf("parse_fromchar: Request to become a GM acount from %d account.\n", acc); + WBUFW (buf, 0) = 0x2721; + WBUFL (buf, 2) = acc; + WBUFL (buf, 6) = 0; + if (strcmp (RFIFOP (fd, 8), gm_pass) == 0) + { + // only non-GM can become GM + if (isGM (acc) == 0) + { + // if we autorise creation + if (level_new_gm > 0) + { + // if we can open the file to add the new GM + if ((fp = + fopen_ (GM_account_filename, + "a")) != NULL) + { + char tmpstr[24]; + struct timeval tv; + gettimeofday (&tv, NULL); + strftime (tmpstr, 23, date_format, + gmtime (&(tv.tv_sec))); + fprintf (fp, + "\n// %s: @GM command on account %d\n%d %d\n", + tmpstr, + acc, acc, level_new_gm); + fclose_ (fp); + WBUFL (buf, 6) = level_new_gm; + read_gm_account (); + send_GM_accounts (); + printf + ("GM Change of the account %d: level 0 -> %d.\n", + acc, level_new_gm); + login_log + ("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s).\n", + server[id].name, acc, + level_new_gm, ip); + } + else + { + printf + ("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n", + acc); + login_log + ("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s).\n", + server[id].name, acc, ip); + } + } + else + { + printf + ("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n", + acc); + login_log + ("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s).\n", + server[id].name, acc, ip); + } + } + else + { + printf + ("Error of GM change (suggested account: %d (already GM), correct password).\n", + acc); + login_log + ("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s).\n", + server[id].name, acc, ip); + } + } + else + { + printf + ("Error of GM change (suggested account: %d, invalid password).\n", + acc); + login_log + ("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s).\n", + server[id].name, acc, ip); + } + charif_sendallwos (-1, buf, 10); + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + return; + + // Map server send information to change an email of an account via char-server + case 0x2722: // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B + if (RFIFOREST (fd) < 86) + return; + { + int acc; + char actual_email[40], new_email[40]; + acc = RFIFOL (fd, 2); + memcpy (actual_email, RFIFOP (fd, 6), 40); + actual_email[39] = '\0'; + remove_control_chars (actual_email); + memcpy (new_email, RFIFOP (fd, 46), 40); + new_email[39] = '\0'; + remove_control_chars (new_email); + if (e_mail_check (actual_email) == 0) + login_log + ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", + server[id].name, acc, ip); + else if (e_mail_check (new_email) == 0) + login_log + ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", + server[id].name, acc, ip); + else if (strcasecmp (new_email, "a@a.com") == 0) + login_log + ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", + server[id].name, acc, ip); + else + { + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + if (strcasecmp (auth_dat[i].email, actual_email) + == 0) + { + memcpy (auth_dat[i].email, new_email, 40); + login_log + ("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", + server[id].name, acc, + auth_dat[i].userid, new_email, ip); + } + else + login_log + ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", + server[id].name, acc, + auth_dat[i].userid, + auth_dat[i].email, actual_email, ip); + break; + } + } + if (i == auth_num) + login_log + ("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", + server[id].name, acc, ip); + } + } + RFIFOSKIP (fd, 86); + break; + + // Receiving of map-server via char-server a status change resquest (by Yor) + case 0x2724: + if (RFIFOREST (fd) < 10) + return; + { + int acc, statut; + acc = RFIFOL (fd, 2); + statut = RFIFOL (fd, 6); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + if (auth_dat[i].state != statut) + { + login_log + ("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", + server[id].name, acc, statut, + ip); + if (statut != 0) + { + unsigned char buf[16]; + WBUFW (buf, 0) = 0x2731; + WBUFL (buf, 2) = acc; + WBUFB (buf, 6) = 0; // 0: change of statut, 1: ban + WBUFL (buf, 7) = statut; // status or final date of a banishment + charif_sendallwos (-1, buf, 11); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].state = statut; + } + else + login_log + ("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", + server[id].name, acc, statut, + ip); + break; + } + } + if (i == auth_num) + { + login_log + ("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", + server[id].name, acc, statut, ip); + } + RFIFOSKIP (fd, 10); + } + return; + + case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) + if (RFIFOREST (fd) < 18) + return; + { + int acc; + acc = RFIFOL (fd, 2); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + time_t timestamp; + struct tm *tmtime; + if (auth_dat[i].ban_until_time == 0 + || auth_dat[i].ban_until_time < time (NULL)) + timestamp = time (NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = gmtime (×tamp); + tmtime->tm_year = + tmtime->tm_year + (short) RFIFOW (fd, 6); + tmtime->tm_mon = + tmtime->tm_mon + (short) RFIFOW (fd, 8); + tmtime->tm_mday = + tmtime->tm_mday + (short) RFIFOW (fd, 10); + tmtime->tm_hour = + tmtime->tm_hour + (short) RFIFOW (fd, 12); + tmtime->tm_min = + tmtime->tm_min + (short) RFIFOW (fd, 14); + tmtime->tm_sec = + tmtime->tm_sec + (short) RFIFOW (fd, 16); + timestamp = timegm (tmtime); + if (timestamp != -1) + { + if (timestamp <= time (NULL)) + timestamp = 0; + if (auth_dat[i].ban_until_time != timestamp) + { + if (timestamp != 0) + { + unsigned char buf[16]; + char tmpstr[2048]; + strftime (tmpstr, 24, date_format, + gmtime (×tamp)); + login_log + ("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", + server[id].name, acc, + timestamp, + (timestamp == + 0 ? "no banishment" : tmpstr), + ip); + WBUFW (buf, 0) = 0x2731; + WBUFL (buf, 2) = + auth_dat[i].account_id; + WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban + WBUFL (buf, 7) = timestamp; // status or final date of a banishment + charif_sendallwos (-1, buf, 11); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == + acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + else + { + login_log + ("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", + server[id].name, acc, + ip); + } + auth_dat[i].ban_until_time = timestamp; + } + else + { + login_log + ("Char-server '%s': Error of ban request (account: %d, no change for ban date, ip: %s).\n", + server[id].name, acc, ip); + } + } + else + { + login_log + ("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", + server[id].name, acc, ip); + } + break; + } + } + if (i == auth_num) + { + login_log + ("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", + server[id].name, acc, ip); + } + RFIFOSKIP (fd, 18); + } + return; + + case 0x2727: // Change of sex (sex is reversed) + if (RFIFOREST (fd) < 6) + return; + { + int acc, sex; + acc = RFIFOL (fd, 2); + for (i = 0; i < auth_num; i++) + { +// printf("%d,", auth_dat[i].account_id); + if (auth_dat[i].account_id == acc) + { + if (auth_dat[i].sex == 2) + login_log + ("Char-server '%s': Error of sex change - Server account (suggested account: %d, actual sex %d (Server), ip: %s).\n", + server[id].name, acc, + auth_dat[i].sex, ip); + else + { + unsigned char buf[16]; + if (auth_dat[i].sex == 0) + sex = 1; + else + sex = 0; + login_log + ("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", + server[id].name, acc, + (sex == 2) ? 'S' : (sex ? 'M' : 'F'), + ip); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + auth_dat[i].sex = sex; + WBUFW (buf, 0) = 0x2723; + WBUFL (buf, 2) = acc; + WBUFB (buf, 6) = sex; + charif_sendallwos (-1, buf, 7); + } + break; + } + } + if (i == auth_num) + { + login_log + ("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s).\n", + server[id].name, acc, ip); + } + RFIFOSKIP (fd, 6); + } + return; + + case 0x2728: // We receive account_reg2 from a char-server, and we send them to other char-servers. + if (RFIFOREST (fd) < 4 || RFIFOREST (fd) < RFIFOW (fd, 2)) + return; + { + int acc, p; + acc = RFIFOL (fd, 4); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + unsigned char buf[RFIFOW (fd, 2) + 1]; + login_log + ("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", + server[id].name, acc, ip); + for (p = 8, j = 0; + p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; + p += 36, j++) + { + memcpy (auth_dat[i].account_reg2[j].str, + RFIFOP (fd, p), 32); + auth_dat[i].account_reg2[j].str[31] = '\0'; + remove_control_chars (auth_dat[i].account_reg2 + [j].str); + auth_dat[i].account_reg2[j].value = + RFIFOL (fd, p + 32); + } + auth_dat[i].account_reg2_num = j; + // Sending information towards the other char-servers. + memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), + RFIFOW (fd, 2)); + WBUFW (buf, 0) = 0x2729; + charif_sendallwos (fd, buf, WBUFW (buf, 2)); +// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (account id: %d).\n", acc); + break; + } + } + if (i == auth_num) + { +// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (unknwon account id: %d).\n", acc); + login_log + ("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", + server[id].name, acc, ip); + } + } + RFIFOSKIP (fd, RFIFOW (fd, 2)); + break; + + case 0x272a: // Receiving of map-server via char-server a unban resquest (by Yor) + if (RFIFOREST (fd) < 6) + return; + { + int acc; + acc = RFIFOL (fd, 2); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + if (auth_dat[i].ban_until_time != 0) + { + auth_dat[i].ban_until_time = 0; + login_log + ("Char-server '%s': UnBan request (account: %d, ip: %s).\n", + server[id].name, acc, ip); + } + else + { + login_log + ("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", + server[id].name, acc, ip); + } + break; + } + } + if (i == auth_num) + { + login_log + ("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", + server[id].name, acc, ip); + } + RFIFOSKIP (fd, 6); + } + return; + + // request from char-server to change account password + case 0x2740: // 0x2740 <account_id>.L <actual_password>.24B <new_password>.24B + if (RFIFOREST (fd) < 54) + return; + { + int acc; + char actual_pass[24], new_pass[24]; + acc = RFIFOL (fd, 2); + memcpy (actual_pass, RFIFOP (fd, 6), 24); + actual_pass[23] = '\0'; + remove_control_chars (actual_pass); + memcpy (new_pass, RFIFOP (fd, 30), 24); + new_pass[23] = '\0'; + remove_control_chars (new_pass); + + int status = 0; + + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == acc) + { + if (pass_ok (actual_pass, auth_dat[i].pass)) + { + if (strlen (new_pass) < 4) + status = 3; + else + { + status = 1; + strcpy (auth_dat[i].pass, MD5_saltcrypt(new_pass, make_salt())); + login_log + ("Char-server '%s': Change pass success (account: %d (%s), ip: %s.\n", + server[id].name, acc, + auth_dat[i].userid, ip); + } + } + else + { + status = 2; + login_log + ("Char-server '%s': Attempt to modify a pass failed, wrong password. (account: %d (%s), ip: %s).\n", + server[id].name, acc, + auth_dat[i].userid, ip); + } + break; + } + } + WFIFOW (fd, 0) = 0x2741; + WFIFOL (fd, 2) = acc; + WFIFOB (fd, 6) = status; // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short + WFIFOSET (fd, 7); + } + + RFIFOSKIP (fd, 54); + break; + + default: + { + FILE *logfp; + char tmpstr[24]; + struct timeval tv; + logfp = fopen_ (login_log_unknown_packets_filename, "a"); + if (logfp) + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 23, date_format, gmtime (&(tv.tv_sec))); + fprintf (logfp, + "%s.%03d: receiving of an unknown packet -> disconnection\n", + tmpstr, (int) tv.tv_usec / 1000); + fprintf (logfp, + "parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", + fd, ip, RFIFOW (fd, 0), RFIFOREST (fd)); + fprintf (logfp, "Detail (in hex):\n"); + fprintf (logfp, + "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); + memset (tmpstr, '\0', sizeof (tmpstr)); + for (i = 0; i < RFIFOREST (fd); i++) + { + if ((i & 15) == 0) + fprintf (logfp, "%04X ", i); + fprintf (logfp, "%02x ", RFIFOB (fd, i)); + if (RFIFOB (fd, i) > 0x1f) + tmpstr[i % 16] = RFIFOB (fd, i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + else if ((i + 1) % 16 == 0) + { + fprintf (logfp, " %s\n", tmpstr); + memset (tmpstr, '\0', sizeof (tmpstr)); + } + } + if (i % 16 != 0) + { + for (j = i; j % 16 != 0; j++) + { + fprintf (logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + } + fprintf (logfp, " %s\n", tmpstr); + } + fprintf (logfp, "\n"); + fclose_ (logfp); + } + } + printf + ("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", + RFIFOW (fd, 0)); + session[fd]->eof = 1; + printf + ("Char-server has been disconnected (unknown packet).\n"); + return; + } + } + return; +} + +//--------------------------------------- +// Packet parsing for administation login +//--------------------------------------- +void parse_admin (int fd) +{ + int i, j; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char *account_name; + char ip[16]; + + sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (session[fd]->eof) + { + close (fd); + delete_session (fd); + printf ("Remote administration has disconnected (session #%d).\n", + fd); + return; + } + + while (RFIFOREST (fd) >= 2) + { + if (display_parse_admin == 1) + printf + ("parse_admin: connection #%d, packet: 0x%x (with being read: %d).\n", + fd, RFIFOW (fd, 0), RFIFOREST (fd)); + + switch (RFIFOW (fd, 0)) + { + case 0x7530: // Request of the server version + login_log ("'ladmin': Sending of the server version (ip: %s)\n", + ip); + 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_LOGIN; + WFIFOW (fd, 8) = ATHENA_MOD_VERSION; + WFIFOSET (fd, 10); + RFIFOSKIP (fd, 2); + break; + + case 0x7532: // Request of end of connection + login_log ("'ladmin': End of connection (ip: %s)\n", + ip); + RFIFOSKIP (fd, 2); + session[fd]->eof = 1; + break; + + case 0x7920: // Request of an accounts list + if (RFIFOREST (fd) < 10) + return; + { + int st, ed, len; + int id[auth_num]; + st = RFIFOL (fd, 2); + ed = RFIFOL (fd, 6); + RFIFOSKIP (fd, 10); + WFIFOW (fd, 0) = 0x7921; + if (st < 0) + st = 0; + if (ed > END_ACCOUNT_NUM || ed < st || ed <= 0) + ed = END_ACCOUNT_NUM; + login_log + ("'ladmin': Sending an accounts list (ask: from %d to %d, ip: %s)\n", + st, ed, ip); + // Sort before send + for (i = 0; i < auth_num; i++) + { + int k; + id[i] = i; + for (j = 0; j < i; j++) + { + if (auth_dat[id[i]].account_id < + auth_dat[id[j]].account_id) + { + for (k = i; k > j; k--) + { + id[k] = id[k - 1]; + } + id[j] = i; // id[i] + break; + } + } + } + // Sending accounts information + len = 4; + for (i = 0; i < auth_num && len < 30000; i++) + { + int account_id = auth_dat[id[i]].account_id; // use sorted index + if (account_id >= st && account_id <= ed) + { + j = id[i]; + WFIFOL (fd, len) = account_id; + WFIFOB (fd, len + 4) = + (unsigned char) isGM (account_id); + memcpy (WFIFOP (fd, len + 5), auth_dat[j].userid, + 24); + WFIFOB (fd, len + 29) = auth_dat[j].sex; + WFIFOL (fd, len + 30) = auth_dat[j].logincount; + if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0) // if no state and banished + WFIFOL (fd, len + 34) = 7; // 6 = Your are Prohibited to log in until %s + else + WFIFOL (fd, len + 34) = auth_dat[j].state; + len += 38; + } + } + WFIFOW (fd, 2) = len; + WFIFOSET (fd, len); + } + break; + + case 0x7924: + { // [Fate] Itemfrob package: change item IDs + if (RFIFOREST (fd) < 10) + return; + charif_sendallwos (-1, RFIFOP (fd, 0), 10); // forward package to char servers + RFIFOSKIP (fd, 10); + WFIFOW (fd, 0) = 0x7925; + WFIFOSET (fd, 2); + break; + } + + case 0x7930: // Request for an account creation + if (RFIFOREST (fd) < 91) + return; + { + struct mmo_account ma; + ma.userid = RFIFOP (fd, 2); + ma.passwd = RFIFOP (fd, 26); + memcpy (ma.lastlogin, "-", 2); + ma.sex = RFIFOB (fd, 50); + WFIFOW (fd, 0) = 0x7931; + WFIFOL (fd, 2) = -1; + memcpy (WFIFOP (fd, 6), RFIFOP (fd, 2), 24); + if (strlen (ma.userid) > 23 || strlen (ma.passwd) > 23) + { + login_log + ("'ladmin': Attempt to create an invalid account (account or pass is too long, ip: %s)\n", + ip); + } + else if (strlen (ma.userid) < 4 || strlen (ma.passwd) < 4) + { + login_log + ("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)\n", + ip); + } + else if (ma.sex != 'F' && ma.sex != 'M') + { + login_log + ("'ladmin': Attempt to create an invalid account (account: %s, invalid sex, ip: %s)\n", + ma.userid, ip); + } + else if (account_id_count > END_ACCOUNT_NUM) + { + login_log + ("'ladmin': Attempt to create an account, but there is no more available id number (account: %s, sex: %c, ip: %s)\n", + ma.userid, ma.sex, ip); + } + else + { + remove_control_chars (ma.userid); + remove_control_chars (ma.passwd); + for (i = 0; i < auth_num; i++) + { + if (strncmp (auth_dat[i].userid, ma.userid, 24) == + 0) + { + login_log + ("'ladmin': Attempt to create an already existing account (account: %s ip: %s)\n", + auth_dat[i].userid, ip); + break; + } + } + if (i == auth_num) + { + int new_id; + char email[40]; + memcpy (email, RFIFOP (fd, 51), 40); + email[39] = '\0'; + remove_control_chars (email); + new_id = mmo_auth_new (&ma, ma.sex, email); + login_log + ("'ladmin': Account creation (account: %s (id: %d), sex: %c, email: %s, ip: %s)\n", + ma.userid, new_id, + ma.sex, auth_dat[i].email, ip); + WFIFOL (fd, 2) = new_id; + } + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 91); + } + break; + + case 0x7932: // Request for an account deletion + if (RFIFOREST (fd) < 26) + return; + WFIFOW (fd, 0) = 0x7933; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + // Char-server is notified of deletion (for characters deletion). + unsigned char buf[65535]; + WBUFW (buf, 0) = 0x2730; + WBUFL (buf, 2) = auth_dat[i].account_id; + charif_sendallwos (-1, buf, 6); + // send answer + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + WFIFOL (fd, 2) = auth_dat[i].account_id; + // save deleted account in log file + login_log + ("'ladmin': Account deletion (account: %s, id: %d, ip: %s) - saved in next line:\n", + auth_dat[i].userid, auth_dat[i].account_id, + ip); + mmo_auth_tostr (buf, &auth_dat[i]); + login_log ("%s\n", buf); + // delete account + memset (auth_dat[i].userid, '\0', + sizeof (auth_dat[i].userid)); + auth_dat[i].account_id = -1; + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to delete an unknown account (account: %s, ip: %s)\n", + account_name, ip); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 26); + break; + + case 0x7934: // Request to change a password + if (RFIFOREST (fd) < 50) + return; + WFIFOW (fd, 0) = 0x7935; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + strcpy (auth_dat[i].pass, MD5_saltcrypt(RFIFOP (fd, 26), make_salt())); + auth_dat[i].pass[39] = '\0'; + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)\n", + auth_dat[i].userid, auth_dat[i].pass, ip); + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to modify the password of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 50); + break; + + case 0x7936: // Request to modify a state + if (RFIFOREST (fd) < 50) + return; + { + char error_message[20]; + int statut; + WFIFOW (fd, 0) = 0x7937; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + statut = RFIFOL (fd, 26); + memcpy (error_message, RFIFOP (fd, 30), 20); + error_message[19] = '\0'; + remove_control_chars (error_message); + if (statut != 7 || error_message[0] == '\0') + { // 7: // 6 = Your are Prohibited to log in until %s + strcpy (error_message, "-"); + } + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + WFIFOL (fd, 2) = auth_dat[i].account_id; + if (auth_dat[i].state == statut + && strcmp (auth_dat[i].error_message, + error_message) == 0) + login_log + ("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)\n", + account_name, statut, ip); + else + { + if (statut == 7) + login_log + ("'ladmin': Modification of a state (account: %s, new state: %d - prohibited to login until '%s', ip: %s)\n", + auth_dat[i].userid, statut, + error_message, ip); + else + login_log + ("'ladmin': Modification of a state (account: %s, new state: %d, ip: %s)\n", + auth_dat[i].userid, statut, ip); + if (auth_dat[i].state == 0) + { + unsigned char buf[16]; + WBUFW (buf, 0) = 0x2731; + WBUFL (buf, 2) = auth_dat[i].account_id; + WBUFB (buf, 6) = 0; // 0: change of statut, 1: ban + WBUFL (buf, 7) = statut; // status or final date of a banishment + charif_sendallwos (-1, buf, 11); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == + auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].state = statut; + memcpy (auth_dat[i].error_message, error_message, + 20); + } + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to modify the state of an unknown account (account: %s, received state: %d, ip: %s)\n", + account_name, statut, ip); + } + WFIFOL (fd, 30) = statut; + } + WFIFOSET (fd, 34); + RFIFOSKIP (fd, 50); + break; + + case 0x7938: // Request for servers list and # of online players + login_log ("'ladmin': Sending of servers list (ip: %s)\n", ip); + server_num = 0; + for (i = 0; i < MAX_SERVERS; i++) + { + if (server_fd[i] >= 0) + { + WFIFOL (fd, 4 + server_num * 32) = server[i].ip; + WFIFOW (fd, 4 + server_num * 32 + 4) = server[i].port; + memcpy (WFIFOP (fd, 4 + server_num * 32 + 6), + server[i].name, 20); + WFIFOW (fd, 4 + server_num * 32 + 26) = + server[i].users; + WFIFOW (fd, 4 + server_num * 32 + 28) = + server[i].maintenance; + WFIFOW (fd, 4 + server_num * 32 + 30) = server[i].is_new; + server_num++; + } + } + WFIFOW (fd, 0) = 0x7939; + WFIFOW (fd, 2) = 4 + 32 * server_num; + WFIFOSET (fd, 4 + 32 * server_num); + RFIFOSKIP (fd, 2); + break; + + case 0x793a: // Request to password check + if (RFIFOREST (fd) < 50) + return; + WFIFOW (fd, 0) = 0x793b; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + if ( pass_ok(RFIFOP (fd, 26), auth_dat[i].pass) ) + { + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)\n", + auth_dat[i].userid, auth_dat[i].pass, + ip); + } + else + { + char pass[24]; + memcpy (pass, RFIFOP (fd, 26), 24); + pass[23] = '\0'; + remove_control_chars (pass); + login_log + ("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)\n", + auth_dat[i].userid, pass, ip); + } + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to check the password of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 50); + break; + + case 0x793c: // Request to modify sex + if (RFIFOREST (fd) < 27) + return; + WFIFOW (fd, 0) = 0x793d; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + memcpy (WFIFOP (fd, 6), account_name, 24); + { + char sex; + sex = RFIFOB (fd, 26); + if (sex != 'F' && sex != 'M') + { + if (sex > 31) + login_log + ("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)\n", + account_name, sex, ip); + else + login_log + ("'ladmin': Attempt to give an invalid sex (account: %s, received sex: 'control char', ip: %s)\n", + account_name, ip); + } + else + { + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + if (auth_dat[i].sex != + ((sex == 'S' || sex == 's') ? 2 : (sex == 'M' + || sex == + 'm'))) + { + unsigned char buf[16]; + WFIFOL (fd, 2) = auth_dat[i].account_id; + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == + auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + auth_dat[i].sex = (sex == 'S' + || sex == + 's') ? 2 : (sex == 'M' + || sex == 'm'); + login_log + ("'ladmin': Modification of a sex (account: %s, new sex: %c, ip: %s)\n", + auth_dat[i].userid, sex, ip); + // send to all char-server the change + WBUFW (buf, 0) = 0x2723; + WBUFL (buf, 2) = auth_dat[i].account_id; + WBUFB (buf, 6) = auth_dat[i].sex; + charif_sendallwos (-1, buf, 7); + } + else + { + login_log + ("'ladmin': Modification of a sex, but the sex is already the good sex (account: %s, sex: %c, ip: %s)\n", + auth_dat[i].userid, sex, ip); + } + } + else + { + login_log + ("'ladmin': Attempt to modify the sex of an unknown account (account: %s, received sex: %c, ip: %s)\n", + account_name, sex, ip); + } + } + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 27); + break; + + case 0x793e: // Request to modify GM level + if (RFIFOREST (fd) < 27) + return; + WFIFOW (fd, 0) = 0x793f; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + memcpy (WFIFOP (fd, 6), account_name, 24); + { + char new_gm_level; + new_gm_level = RFIFOB (fd, 26); + if (new_gm_level < 0 || new_gm_level > 99) + { + login_log + ("'ladmin': Attempt to give an invalid GM level (account: %s, received GM level: %d, ip: %s)\n", + account_name, (int) new_gm_level, ip); + } + else + { + i = search_account_index (account_name); + if (i != -1) + { + int acc = auth_dat[i].account_id; + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + if (isGM (acc) != new_gm_level) + { + // modification of the file + FILE *fp, *fp2; + int lock; + char line[512]; + int GM_account, GM_level; + int modify_flag; + char tmpstr[24]; + struct timeval tv; + if ((fp2 = + lock_fopen (GM_account_filename, + &lock)) != NULL) + { + if ((fp = + fopen_ (GM_account_filename, + "r")) != NULL) + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 23, date_format, + gmtime (&(tv.tv_sec))); + modify_flag = 0; + // read/write GM file + while (fgets + (line, sizeof (line) - 1, fp)) + { + while (line[0] != '\0' + && (line[strlen (line) - 1] + == '\n' + || line[strlen (line) - + 1] == '\r')) + line[strlen (line) - 1] = + '\0'; + if ((line[0] == '/' + && line[1] == '/') + || line[0] == '\0') + fprintf (fp2, "%s\n", + line); + else + { + if (sscanf + (line, "%d %d", + &GM_account, + &GM_level) != 2 + && sscanf (line, "%d: %d", + &GM_account, + &GM_level) != + 2) + fprintf (fp2, + "%s\n", + line); + else if (GM_account != acc) + fprintf (fp2, + "%s\n", + line); + else if (new_gm_level < 1) + { + fprintf (fp2, + "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)\n//%d %d\n", + tmpstr, + acc, + auth_dat + [i].userid, + GM_level, acc, + new_gm_level); + modify_flag = 1; + } + else + { + fprintf (fp2, + "// %s: 'ladmin' GM level on account %d '%s' (previous level: %d)\n%d %d\n", + tmpstr, + acc, + auth_dat + [i].userid, + GM_level, acc, + new_gm_level); + modify_flag = 1; + } + } + } + if (modify_flag == 0) + fprintf (fp2, + "// %s: 'ladmin' GM level on account %d '%s' (previous level: 0)\n%d %d\n", + tmpstr, acc, + auth_dat[i].userid, acc, + new_gm_level); + fclose_ (fp); + } + else + { + login_log + ("'ladmin': Attempt to modify of a GM level - impossible to read GM accounts file (account: %s (%d), received GM level: %d, ip: %s)\n", + auth_dat[i].userid, acc, + (int) new_gm_level, ip); + } + lock_fclose(fp2, GM_account_filename, &lock); + WFIFOL (fd, 2) = acc; + login_log + ("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)\n", + auth_dat[i].userid, acc, + (int) new_gm_level, ip); + // read and send new GM informations + read_gm_account (); + send_GM_accounts (); + } + else + { + login_log + ("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)\n", + auth_dat[i].userid, acc, + (int) new_gm_level, ip); + } + } + else + { + login_log + ("'ladmin': Attempt to modify of a GM level, but the GM level is already the good GM level (account: %s (%d), GM level: %d, ip: %s)\n", + auth_dat[i].userid, acc, + (int) new_gm_level, ip); + } + } + else + { + login_log + ("'ladmin': Attempt to modify the GM level of an unknown account (account: %s, received GM level: %d, ip: %s)\n", + account_name, (int) new_gm_level, + ip); + } + } + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 27); + break; + + case 0x7940: // Request to modify e-mail + if (RFIFOREST (fd) < 66) + return; + WFIFOW (fd, 0) = 0x7941; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + memcpy (WFIFOP (fd, 6), account_name, 24); + { + char email[40]; + memcpy (email, RFIFOP (fd, 26), 40); + if (e_mail_check (email) == 0) + { + login_log + ("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)\n", + account_name, ip); + } + else + { + remove_control_chars (email); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + memcpy (auth_dat[i].email, email, 40); + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)\n", + auth_dat[i].userid, email, ip); + } + else + { + login_log + ("'ladmin': Attempt to modify the e-mail of an unknown account (account: %s, received e-mail: %s, ip: %s)\n", + account_name, email, ip); + } + } + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 66); + break; + + case 0x7942: // Request to modify memo field + if (RFIFOREST (fd) < 28 + || RFIFOREST (fd) < (28 + RFIFOW (fd, 26))) + return; + WFIFOW (fd, 0) = 0x7943; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + int size_of_memo = sizeof (auth_dat[i].memo); + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + memset (auth_dat[i].memo, '\0', size_of_memo); + if (RFIFOW (fd, 26) == 0) + { + strncpy (auth_dat[i].memo, "!", size_of_memo); + } + else if (RFIFOW (fd, 26) > size_of_memo - 1) + { + memcpy (auth_dat[i].memo, RFIFOP (fd, 28), + size_of_memo - 1); + } + else + { + memcpy (auth_dat[i].memo, RFIFOP (fd, 28), + RFIFOW (fd, 26)); + } + auth_dat[i].memo[size_of_memo - 1] = '\0'; + remove_control_chars (auth_dat[i].memo); + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)\n", + auth_dat[i].userid, auth_dat[i].memo, ip); + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to modify the memo field of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 28 + RFIFOW (fd, 26)); + break; + + case 0x7944: // Request to found an account id + if (RFIFOREST (fd) < 26) + return; + WFIFOW (fd, 0) = 0x7945; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)\n", + auth_dat[i].userid, auth_dat[i].account_id, + ip); + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': ID request (by the name) of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 26); + break; + + case 0x7946: // Request to found an account name + if (RFIFOREST (fd) < 6) + return; + WFIFOW (fd, 0) = 0x7947; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + memset (WFIFOP (fd, 6), '\0', 24); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == RFIFOL (fd, 2)) + { + strncpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + login_log + ("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)\n", + auth_dat[i].userid, RFIFOL (fd, 2), ip); + break; + } + } + if (i == auth_num) + { + login_log + ("'ladmin': Name request (by id) of an unknown account (id: %d, ip: %s)\n", + RFIFOL (fd, 2), ip); + strncpy (WFIFOP (fd, 6), "", 24); + } + WFIFOSET (fd, 30); + RFIFOSKIP (fd, 6); + break; + + case 0x7948: // Request to change the validity limit (timestamp) (absolute value) + if (RFIFOREST (fd) < 30) + return; + { + time_t timestamp; + char tmpstr[2048]; + WFIFOW (fd, 0) = 0x7949; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + timestamp = (time_t) RFIFOL (fd, 26); + strftime (tmpstr, 24, date_format, gmtime (×tamp)); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + login_log + ("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)\n", + auth_dat[i].userid, timestamp, + (timestamp == 0 ? "unlimited" : tmpstr), ip); + auth_dat[i].connect_until_time = timestamp; + WFIFOL (fd, 2) = auth_dat[i].account_id; + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %d (%s), ip: %s)\n", account_name, timestamp, + (timestamp == 0 ? "unlimited" : tmpstr), ip); + } + WFIFOL (fd, 30) = timestamp; + } + WFIFOSET (fd, 34); + RFIFOSKIP (fd, 30); + break; + + case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value) + if (RFIFOREST (fd) < 30) + return; + { + time_t timestamp; + char tmpstr[2048]; + WFIFOW (fd, 0) = 0x794b; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + timestamp = (time_t) RFIFOL (fd, 26); + if (timestamp <= time (NULL)) + timestamp = 0; + strftime (tmpstr, 24, date_format, gmtime (×tamp)); + i = search_account_index (account_name); + if (i != -1) + { + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + WFIFOL (fd, 2) = auth_dat[i].account_id; + login_log + ("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)\n", + auth_dat[i].userid, timestamp, + (timestamp == 0 ? "no banishment" : tmpstr), ip); + if (auth_dat[i].ban_until_time != timestamp) + { + if (timestamp != 0) + { + unsigned char buf[16]; + WBUFW (buf, 0) = 0x2731; + WBUFL (buf, 2) = auth_dat[i].account_id; + WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban + WBUFL (buf, 7) = timestamp; // status or final date of a banishment + charif_sendallwos (-1, buf, 11); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == + auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].ban_until_time = timestamp; + } + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %d (%s), ip: %s)\n", + account_name, timestamp, + (timestamp == 0 ? "no banishment" : tmpstr), ip); + } + WFIFOL (fd, 30) = timestamp; + } + WFIFOSET (fd, 34); + RFIFOSKIP (fd, 30); + break; + + case 0x794c: // Request to change the final date of a banishment (timestamp) (relative change) + if (RFIFOREST (fd) < 38) + return; + { + time_t timestamp; + struct tm *tmtime; + char tmpstr[2048]; + WFIFOW (fd, 0) = 0x794d; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + WFIFOL (fd, 2) = auth_dat[i].account_id; + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + if (auth_dat[i].ban_until_time == 0 + || auth_dat[i].ban_until_time < time (NULL)) + timestamp = time (NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = gmtime (×tamp); + tmtime->tm_year = + tmtime->tm_year + (short) RFIFOW (fd, 26); + tmtime->tm_mon = + tmtime->tm_mon + (short) RFIFOW (fd, 28); + tmtime->tm_mday = + tmtime->tm_mday + (short) RFIFOW (fd, 30); + tmtime->tm_hour = + tmtime->tm_hour + (short) RFIFOW (fd, 32); + tmtime->tm_min = + tmtime->tm_min + (short) RFIFOW (fd, 34); + tmtime->tm_sec = + tmtime->tm_sec + (short) RFIFOW (fd, 36); + timestamp = mktime (tmtime); + if (timestamp != -1) + { + if (timestamp <= time (NULL)) + timestamp = 0; + strftime (tmpstr, 24, date_format, + gmtime (×tamp)); + login_log + ("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)\n", + auth_dat[i].userid, + (short) RFIFOW (fd, 26), (short) RFIFOW (fd, + 28), + (short) RFIFOW (fd, 30), (short) RFIFOW (fd, + 32), + (short) RFIFOW (fd, 34), (short) RFIFOW (fd, + 36), + timestamp, + (timestamp == 0 ? "no banishment" : tmpstr), + ip); + if (auth_dat[i].ban_until_time != timestamp) + { + if (timestamp != 0) + { + unsigned char buf[16]; + WBUFW (buf, 0) = 0x2731; + WBUFL (buf, 2) = auth_dat[i].account_id; + WBUFB (buf, 6) = 1; // 0: change of statut, 1: ban + WBUFL (buf, 7) = timestamp; // status or final date of a banishment + charif_sendallwos (-1, buf, 11); + for (j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == + auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].ban_until_time = timestamp; + } + } + else + { + strftime (tmpstr, 24, date_format, + gmtime (&auth_dat[i].ban_until_time)); + login_log + ("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", + auth_dat[i].userid, + auth_dat[i].ban_until_time, + (auth_dat[i].ban_until_time == + 0 ? "no banishment" : tmpstr), + (short) RFIFOW (fd, 26), (short) RFIFOW (fd, + 28), + (short) RFIFOW (fd, 30), (short) RFIFOW (fd, + 32), + (short) RFIFOW (fd, 34), (short) RFIFOW (fd, + 36), + ip); + } + WFIFOL (fd, 30) = + (unsigned long) auth_dat[i].ban_until_time; + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to adjust the final date of a banishment of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + WFIFOL (fd, 30) = 0; + } + } + WFIFOSET (fd, 34); + RFIFOSKIP (fd, 38); + break; + + case 0x794e: // Request to send a broadcast message + if (RFIFOREST (fd) < 8 + || RFIFOREST (fd) < (8 + RFIFOL (fd, 4))) + return; + WFIFOW (fd, 0) = 0x794f; + WFIFOW (fd, 2) = -1; + if (RFIFOL (fd, 4) < 1) + { + login_log + ("'ladmin': Receiving a message for broadcast, but message is void (ip: %s)\n", + ip); + } + else + { + // at least 1 char-server + for (i = 0; i < MAX_SERVERS; i++) + if (server_fd[i] >= 0) + break; + if (i == MAX_SERVERS) + { + login_log + ("'ladmin': Receiving a message for broadcast, but no char-server is online (ip: %s)\n", + ip); + } + else + { + char buf[32000]; + char message[32000]; + WFIFOW (fd, 2) = 0; + memset (message, '\0', sizeof (message)); + memcpy (message, RFIFOP (fd, 8), RFIFOL (fd, 4)); + message[sizeof (message) - 1] = '\0'; + remove_control_chars (message); + if (RFIFOW (fd, 2) == 0) + login_log + ("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)\n", + message, ip); + else + login_log + ("'ladmin': Receiving a message for broadcast (message (in blue): %s, ip: %s)\n", + message, ip); + // send same message to all char-servers (no answer) + memcpy (WBUFP (buf, 0), RFIFOP (fd, 0), + 8 + RFIFOL (fd, 4)); + WBUFW (buf, 0) = 0x2726; + charif_sendallwos (-1, buf, 8 + RFIFOL (fd, 4)); + } + } + WFIFOSET (fd, 4); + RFIFOSKIP (fd, 8 + RFIFOL (fd, 4)); + break; + + case 0x7950: // Request to change the validity limite (timestamp) (relative change) + if (RFIFOREST (fd) < 38) + return; + { + time_t timestamp; + struct tm *tmtime; + char tmpstr[2048]; + char tmpstr2[2048]; + WFIFOW (fd, 0) = 0x7951; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + WFIFOL (fd, 2) = auth_dat[i].account_id; + memcpy (WFIFOP (fd, 6), auth_dat[i].userid, 24); + timestamp = auth_dat[i].connect_until_time; + if (add_to_unlimited_account == 0 && timestamp == 0) + { + login_log + ("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)\n", + auth_dat[i].userid, ip); + WFIFOL (fd, 30) = 0; + } + else + { + if (timestamp == 0 || timestamp < time (NULL)) + timestamp = time (NULL); + tmtime = gmtime (×tamp); + tmtime->tm_year = + tmtime->tm_year + (short) RFIFOW (fd, 26); + tmtime->tm_mon = + tmtime->tm_mon + (short) RFIFOW (fd, 28); + tmtime->tm_mday = + tmtime->tm_mday + (short) RFIFOW (fd, 30); + tmtime->tm_hour = + tmtime->tm_hour + (short) RFIFOW (fd, 32); + tmtime->tm_min = + tmtime->tm_min + (short) RFIFOW (fd, 34); + tmtime->tm_sec = + tmtime->tm_sec + (short) RFIFOW (fd, 36); + timestamp = mktime (tmtime); + if (timestamp != -1) + { + strftime (tmpstr, 24, date_format, + gmtime (&auth_dat + [i].connect_until_time)); + strftime (tmpstr2, 24, date_format, + gmtime (×tamp)); + login_log + ("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)\n", + auth_dat[i].userid, + auth_dat[i].connect_until_time, + (auth_dat[i].connect_until_time == + 0 ? "unlimited" : tmpstr), + (short) RFIFOW (fd, 26), + (short) RFIFOW (fd, 28), + (short) RFIFOW (fd, 30), + (short) RFIFOW (fd, 32), + (short) RFIFOW (fd, 34), + (short) RFIFOW (fd, 36), timestamp, + (timestamp == 0 ? "unlimited" : tmpstr2), + ip); + auth_dat[i].connect_until_time = timestamp; + WFIFOL (fd, 30) = + (unsigned long) + auth_dat[i].connect_until_time; + } + else + { + strftime (tmpstr, 24, date_format, + gmtime (&auth_dat + [i].connect_until_time)); + login_log + ("'ladmin': Impossible to adjust a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", + auth_dat[i].userid, + auth_dat[i].connect_until_time, + (auth_dat[i].connect_until_time == + 0 ? "unlimited" : tmpstr), + (short) RFIFOW (fd, 26), + (short) RFIFOW (fd, 28), + (short) RFIFOW (fd, 30), + (short) RFIFOW (fd, 32), + (short) RFIFOW (fd, 34), + (short) RFIFOW (fd, 36), ip); + WFIFOL (fd, 30) = 0; + } + } + } + else + { + memcpy (WFIFOP (fd, 6), account_name, 24); + login_log + ("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + WFIFOL (fd, 30) = 0; + } + } + WFIFOSET (fd, 34); + RFIFOSKIP (fd, 38); + break; + + case 0x7952: // Request about informations of an account (by account name) + if (RFIFOREST (fd) < 26) + return; + WFIFOW (fd, 0) = 0x7953; + WFIFOL (fd, 2) = -1; + account_name = RFIFOP (fd, 2); + account_name[23] = '\0'; + remove_control_chars (account_name); + i = search_account_index (account_name); + if (i != -1) + { + WFIFOL (fd, 2) = auth_dat[i].account_id; + WFIFOB (fd, 6) = + (unsigned char) isGM (auth_dat[i].account_id); + memcpy (WFIFOP (fd, 7), auth_dat[i].userid, 24); + WFIFOB (fd, 31) = auth_dat[i].sex; + WFIFOL (fd, 32) = auth_dat[i].logincount; + WFIFOL (fd, 36) = auth_dat[i].state; + memcpy (WFIFOP (fd, 40), auth_dat[i].error_message, 20); + memcpy (WFIFOP (fd, 60), auth_dat[i].lastlogin, 24); + memcpy (WFIFOP (fd, 84), auth_dat[i].last_ip, 16); + memcpy (WFIFOP (fd, 100), auth_dat[i].email, 40); + WFIFOL (fd, 140) = + (unsigned long) auth_dat[i].connect_until_time; + WFIFOL (fd, 144) = + (unsigned long) auth_dat[i].ban_until_time; + WFIFOW (fd, 148) = strlen (auth_dat[i].memo); + if (auth_dat[i].memo[0]) + { + memcpy (WFIFOP (fd, 150), auth_dat[i].memo, + strlen (auth_dat[i].memo)); + } + login_log + ("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)\n", + auth_dat[i].userid, auth_dat[i].account_id, + ip); + WFIFOSET (fd, 150 + strlen (auth_dat[i].memo)); + } + else + { + memcpy (WFIFOP (fd, 7), account_name, 24); + WFIFOW (fd, 148) = 0; + login_log + ("'ladmin': Attempt to obtain information (by the name) of an unknown account (account: %s, ip: %s)\n", + account_name, ip); + WFIFOSET (fd, 150); + } + RFIFOSKIP (fd, 26); + break; + + case 0x7954: // Request about information of an account (by account id) + if (RFIFOREST (fd) < 6) + return; + WFIFOW (fd, 0) = 0x7953; + WFIFOL (fd, 2) = RFIFOL (fd, 2); + memset (WFIFOP (fd, 7), '\0', 24); + for (i = 0; i < auth_num; i++) + { + if (auth_dat[i].account_id == RFIFOL (fd, 2)) + { + login_log + ("'ladmin': Sending information of an account (request by the id; account: %s, id: %d, ip: %s)\n", + auth_dat[i].userid, RFIFOL (fd, 2), ip); + WFIFOB (fd, 6) = + (unsigned char) isGM (auth_dat[i].account_id); + memcpy (WFIFOP (fd, 7), auth_dat[i].userid, 24); + WFIFOB (fd, 31) = auth_dat[i].sex; + WFIFOL (fd, 32) = auth_dat[i].logincount; + WFIFOL (fd, 36) = auth_dat[i].state; + memcpy (WFIFOP (fd, 40), auth_dat[i].error_message, + 20); + memcpy (WFIFOP (fd, 60), auth_dat[i].lastlogin, 24); + memcpy (WFIFOP (fd, 84), auth_dat[i].last_ip, 16); + memcpy (WFIFOP (fd, 100), auth_dat[i].email, 40); + WFIFOL (fd, 140) = + (unsigned long) auth_dat[i].connect_until_time; + WFIFOL (fd, 144) = + (unsigned long) auth_dat[i].ban_until_time; + WFIFOW (fd, 148) = strlen (auth_dat[i].memo); + if (auth_dat[i].memo[0]) + { + memcpy (WFIFOP (fd, 150), auth_dat[i].memo, + strlen (auth_dat[i].memo)); + } + WFIFOSET (fd, 150 + strlen (auth_dat[i].memo)); + break; + } + } + if (i == auth_num) + { + login_log + ("'ladmin': Attempt to obtain information (by the id) of an unknown account (id: %d, ip: %s)\n", + RFIFOL (fd, 2), ip); + strncpy (WFIFOP (fd, 7), "", 24); + WFIFOW (fd, 148) = 0; + WFIFOSET (fd, 150); + } + RFIFOSKIP (fd, 6); + break; + + case 0x7955: // Request to reload GM file (no answer) + login_log + ("'ladmin': Request to re-load GM configuration file (ip: %s).\n", + ip); + read_gm_account (); + // send GM accounts to all char-servers + send_GM_accounts (); + RFIFOSKIP (fd, 2); + break; + + default: + { + FILE *logfp; + char tmpstr[24]; + struct timeval tv; + logfp = fopen_ (login_log_unknown_packets_filename, "a"); + if (logfp) + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 23, date_format, gmtime (&(tv.tv_sec))); + fprintf (logfp, + "%s.%03d: receiving of an unknown packet -> disconnection\n", + tmpstr, (int) tv.tv_usec / 1000); + fprintf (logfp, + "parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", + fd, ip, RFIFOW (fd, 0), RFIFOREST (fd)); + fprintf (logfp, "Detail (in hex):\n"); + fprintf (logfp, + "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); + memset (tmpstr, '\0', sizeof (tmpstr)); + for (i = 0; i < RFIFOREST (fd); i++) + { + if ((i & 15) == 0) + fprintf (logfp, "%04X ", i); + fprintf (logfp, "%02x ", RFIFOB (fd, i)); + if (RFIFOB (fd, i) > 0x1f) + tmpstr[i % 16] = RFIFOB (fd, i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + else if ((i + 1) % 16 == 0) + { + fprintf (logfp, " %s\n", tmpstr); + memset (tmpstr, '\0', sizeof (tmpstr)); + } + } + if (i % 16 != 0) + { + for (j = i; j % 16 != 0; j++) + { + fprintf (logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + } + fprintf (logfp, " %s\n", tmpstr); + } + fprintf (logfp, "\n"); + fclose_ (logfp); + } + } + login_log + ("'ladmin': End of connection, unknown packet (ip: %s)\n", + ip); + session[fd]->eof = 1; + printf + ("Remote administration has been disconnected (unknown packet).\n"); + return; + } + //WFIFOW(fd,0) = 0x791f; + //WFIFOSET(fd,2); + } + return; +} + +//-------------------------------------------- +// Test to know if an IP come from LAN or WAN. +//-------------------------------------------- +int lan_ip_check (unsigned char *p) +{ + int i; + int lancheck = 1; + +// printf("lan_ip_check: to compare: %d.%d.%d.%d, network: %d.%d.%d.%d/%d.%d.%d.%d\n", +// p[0], p[1], p[2], p[3], +// subneti[0], subneti[1], subneti[2], subneti[3], +// subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + for (i = 0; i < 4; i++) + { + if ((subneti[i] & subnetmaski[i]) != (p[i] & subnetmaski[i])) + { + lancheck = 0; + break; + } + } + printf ("LAN test (result): %s source\033[0m.\n", + (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); + return lancheck; +} + +//---------------------------------------------------------------------------------------- +// Default packet parsing (normal players or administation/char-server connexion requests) +//---------------------------------------------------------------------------------------- +void parse_login (int fd) +{ + struct mmo_account account; + int result, i, j; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char ip[16]; + int host_len; + + sprintf (ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (session[fd]->eof) + { + close (fd); + delete_session (fd); + return; + } + + while (RFIFOREST (fd) >= 2) + { + if (display_parse_login == 1) + { + if (RFIFOW (fd, 0) == 0x64 || RFIFOW (fd, 0) == 0x01dd) + { + if (RFIFOREST (fd) >= ((RFIFOW (fd, 0) == 0x64) ? 55 : 47)) + printf + ("parse_login: connection #%d, packet: 0x%x (with being read: %d), account: %s.\n", + fd, RFIFOW (fd, 0), RFIFOREST (fd), RFIFOP (fd, 6)); + } + else if (RFIFOW (fd, 0) == 0x2710) + { + if (RFIFOREST (fd) >= 86) + printf + ("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", + fd, RFIFOW (fd, 0), RFIFOREST (fd), RFIFOP (fd, 60)); + } + else + printf + ("parse_login: connection #%d, packet: 0x%x (with being read: %d).\n", + fd, RFIFOW (fd, 0), RFIFOREST (fd)); + } + + switch (RFIFOW (fd, 0)) + { + case 0x200: // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive. + if (RFIFOREST (fd) < 26) + return; + RFIFOSKIP (fd, 26); + break; + + case 0x204: // New alive packet: structure: 0x204 <encrypted.account.userid>.16B. (new ragexe from 22 june 2004) + if (RFIFOREST (fd) < 18) + return; + RFIFOSKIP (fd, 18); + break; + + case 0x64: // Ask connection of a client + case 0x01dd: // Ask connection of a client (encryption mode) + if (RFIFOREST (fd) < ((RFIFOW (fd, 0) == 0x64) ? 55 : 47)) + return; + + account.userid = RFIFOP (fd, 6); + account.userid[23] = '\0'; + remove_control_chars (account.userid); + account.passwd = RFIFOP (fd, 30); + if (RFIFOW (fd, 0) == 0x64) + { + account.passwd[23] = '\0'; + remove_control_chars (account.passwd); + } +#ifdef PASSWORDENC + account.passwdenc = + (RFIFOW (fd, 0) == 0x64) ? 0 : PASSWORDENC; +#else + account.passwdenc = 0; +#endif + + if (RFIFOW (fd, 0) == 0x64) + { + login_log + ("Request for connection (non encryption mode) of %s (ip: %s).\n", + account.userid, ip); + } + else + { + login_log + ("Request for connection (encryption mode) of %s (ip: %s).\n", + account.userid, ip); + } + + if (!check_ip (session[fd]->client_addr.sin_addr.s_addr)) + { + login_log + ("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", + ip); + WFIFOW (fd, 0) = 0x6a; + WFIFOB (fd, 2) = 0x03; + WFIFOSET (fd, 3); + RFIFOSKIP (fd, (RFIFOW (fd, 0) == 0x64) ? 55 : 47); + break; + } + + result = mmo_auth (&account, fd); + if (result == -1) + { + int gm_level = isGM (account.account_id); + if (min_level_to_connect > gm_level) + { + login_log + ("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s).\n", + min_level_to_connect, account.userid, + gm_level, ip); + WFIFOW (fd, 0) = 0x81; + WFIFOL (fd, 2) = 1; // 01 = Server closed + WFIFOSET (fd, 3); + } + else + { + int version_2 = RFIFOB (fd, 54); // version 2 + + if (gm_level) + printf + ("Connection of the GM (level:%d) account '%s' accepted.\n", + gm_level, account.userid); + else + printf + ("Connection of the account '%s' accepted.\n", + account.userid); + + /* + * Add a 0x0063 packet, which contains the name of the update host. The packet will only + * be sent if login_athena.conf contains a non-null entry for "update_host:" + * + * Because older clients cannot handle the 0x63 packet, we check the "version 2" value + * from the incoming 0x64 packet (the byte at offset 54). If bit 0 of this is set, + * then the client can safely accept the 0x63 packet. The "version 2" value is not + * otherwise used by eAthena. + */ + if ((RFIFOW (fd, 0) == 0x64) + && (version_2 & VERSION_2_UPDATEHOST)) + { + host_len = (int) strlen (update_host); + if (host_len > 0) + { + WFIFOW (fd, 0) = 0x63; + WFIFOW (fd, 2) = 4 + host_len; + memcpy (WFIFOP (fd, 4), update_host, + host_len); + WFIFOSET (fd, 4 + host_len); + } + } + + // Load list of char servers into outbound packet + server_num = 0; + if (version_2 && VERSION_2_SERVERORDER) + for (i = 0; i < MAX_SERVERS; i++) + { + if (server_fd[i] >= 0) + { + if (lan_ip_check (p)) + WFIFOL (fd, 47 + server_num * 32) = + inet_addr (lan_char_ip); + else + WFIFOL (fd, 47 + server_num * 32) = + server[i].ip; + WFIFOW (fd, 47 + server_num * 32 + 4) = + server[i].port; + memcpy (WFIFOP + (fd, 47 + server_num * 32 + 6), + server[i].name, 20); + WFIFOW (fd, 47 + server_num * 32 + 26) = + server[i].users; + WFIFOW (fd, 47 + server_num * 32 + 28) = + server[i].maintenance; + WFIFOW (fd, 47 + server_num * 32 + 30) = + server[i].is_new; + server_num++; + } + } + else // Send them in reverse, as the client defaults to the second (!) one + for (i = MAX_SERVERS - 1; i >= 0; i--) + { + if (server_fd[i] >= 0) + { + if (lan_ip_check (p)) + WFIFOL (fd, 47 + server_num * 32) = + inet_addr (lan_char_ip); + else + WFIFOL (fd, 47 + server_num * 32) = + server[i].ip; + WFIFOW (fd, 47 + server_num * 32 + 4) = + server[i].port; + memcpy (WFIFOP + (fd, 47 + server_num * 32 + 6), + server[i].name, 20); + WFIFOW (fd, 47 + server_num * 32 + 26) = + server[i].users; + WFIFOW (fd, 47 + server_num * 32 + 28) = + server[i].maintenance; + WFIFOW (fd, 47 + server_num * 32 + 30) = + server[i].is_new; + server_num++; + } + } + // if at least 1 char-server + if (server_num > 0) + { + WFIFOW (fd, 0) = 0x69; + WFIFOW (fd, 2) = 47 + 32 * server_num; + WFIFOL (fd, 4) = account.login_id1; + WFIFOL (fd, 8) = account.account_id; + WFIFOL (fd, 12) = account.login_id2; + WFIFOL (fd, 16) = 0; // in old version, that was for ip (not more used) + memcpy (WFIFOP (fd, 20), account.lastlogin, 24); // in old version, that was for name (not more used) + WFIFOB (fd, 46) = account.sex; + WFIFOSET (fd, 47 + 32 * server_num); + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + auth_fifo[auth_fifo_pos].account_id = + account.account_id; + auth_fifo[auth_fifo_pos].login_id1 = + account.login_id1; + auth_fifo[auth_fifo_pos].login_id2 = + account.login_id2; + auth_fifo[auth_fifo_pos].sex = account.sex; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].ip = + session[fd]->client_addr.sin_addr.s_addr; + auth_fifo_pos++; + // if no char-server, don't send void list of servers, just disconnect the player with proper message + } + else + { + login_log + ("Connection refused: there is no char-server online (account: %s, ip: %s).\n", + account.userid, ip); + WFIFOW (fd, 0) = 0x81; + WFIFOL (fd, 2) = 1; // 01 = Server closed + WFIFOSET (fd, 3); + } + } + } + else + { + memset (WFIFOP (fd, 0), '\0', 23); + WFIFOW (fd, 0) = 0x6a; + WFIFOB (fd, 2) = result; + if (result == 6) + { // 6 = Your are Prohibited to log in until %s + i = search_account_index (account.userid); + if (i != -1) + { + if (auth_dat[i].ban_until_time != 0) + { // if account is banned, we send ban timestamp + char tmpstr[256]; + strftime (tmpstr, 20, date_format, + gmtime (&auth_dat + [i].ban_until_time)); + tmpstr[19] = '\0'; + memcpy (WFIFOP (fd, 3), tmpstr, 20); + } + else + { // we send error message + memcpy (WFIFOP (fd, 3), + auth_dat[i].error_message, 20); + } + } + } + WFIFOSET (fd, 23); + } + RFIFOSKIP (fd, (RFIFOW (fd, 0) == 0x64) ? 55 : 47); + break; + + case 0x01db: // Sending request of the coding key + case 0x791a: // Sending request of the coding key (administration packet) + { + struct login_session_data *ld; + if (session[fd]->session_data) + { + printf + ("login: abnormal request of MD5 key (already opened session).\n"); + session[fd]->eof = 1; + return; + } + CREATE (ld, struct login_session_data, 1); + session[fd]->session_data = ld; + if (!ld) + { + printf + ("login: Request for md5 key: memory allocation failure (malloc)!\n"); + session[fd]->eof = 1; + return; + } + if (RFIFOW (fd, 0) == 0x01db) + { + login_log ("Sending request of the coding key (ip: %s)\n", + ip); + } + else + { + login_log + ("'ladmin': Sending request of the coding key (ip: %s)\n", + ip); + } + // Creation of the coding key + memset (ld->md5key, '\0', sizeof (ld->md5key)); + ld->md5keylen = rand () % 4 + 12; + for (i = 0; i < ld->md5keylen; i++) + ld->md5key[i] = rand () % 255 + 1; + + RFIFOSKIP (fd, 2); + WFIFOW (fd, 0) = 0x01dc; + WFIFOW (fd, 2) = 4 + ld->md5keylen; + memcpy (WFIFOP (fd, 4), ld->md5key, ld->md5keylen); + WFIFOSET (fd, WFIFOW (fd, 2)); + } + break; + + case 0x2710: // Connection request of a char-server + if (RFIFOREST (fd) < 86) + return; + { + int GM_value, len; + unsigned char *server_name; + account.userid = RFIFOP (fd, 2); + account.userid[23] = '\0'; + remove_control_chars (account.userid); + account.passwd = RFIFOP (fd, 26); + account.passwd[23] = '\0'; + remove_control_chars (account.passwd); + account.passwdenc = 0; + server_name = RFIFOP (fd, 60); + server_name[19] = '\0'; + remove_control_chars (server_name); + login_log + ("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)\n", + server_name, RFIFOB (fd, 54), RFIFOB (fd, 55), + RFIFOB (fd, 56), RFIFOB (fd, 57), RFIFOW (fd, 58), + ip); + result = mmo_auth (&account, fd); + + if (result == -1 && account.sex == 2) + { + // If this is the main server, and we don't already have a main server + if (server_fd[0] <= 0 + && strcasecmp (server_name, main_server) == 0) + { + account.account_id = 0; + } + else + { + int i; + for (i = 1; i < MAX_SERVERS; i++) + { + if (server_fd[i] <= 0) + { + account.account_id = i; + break; + } + } + } + } + + if (result == -1 && account.sex == 2 + && account.account_id < MAX_SERVERS + && server_fd[account.account_id] == -1) + { + login_log + ("Connection of the char-server '%s' accepted (account: %s, pass: %s, ip: %s)\n", + server_name, account.userid, + account.passwd, ip); + printf + ("Connection of the char-server '%s' accepted.\n", + server_name); + memset (&server[account.account_id], 0, + sizeof (struct mmo_char_server)); + server[account.account_id].ip = RFIFOL (fd, 54); + server[account.account_id].port = RFIFOW (fd, 58); + memcpy (server[account.account_id].name, server_name, + 20); + server[account.account_id].users = 0; + server[account.account_id].maintenance = + RFIFOW (fd, 82); + server[account.account_id].is_new = RFIFOW (fd, 84); + server_fd[account.account_id] = fd; + if (anti_freeze_enable) + server_freezeflag[account.account_id] = 5; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + WFIFOW (fd, 0) = 0x2711; + WFIFOB (fd, 2) = 0; + WFIFOSET (fd, 3); + session[fd]->func_parse = parse_fromchar; + realloc_fifo (fd, FIFOSIZE_SERVERLINK, + FIFOSIZE_SERVERLINK); + // send GM account to char-server + len = 4; + WFIFOW (fd, 0) = 0x2732; + for (i = 0; i < auth_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if ((GM_value = + isGM (auth_dat[i].account_id)) > 0) + { + WFIFOL (fd, len) = auth_dat[i].account_id; + WFIFOB (fd, len + 4) = + (unsigned char) GM_value; + len += 5; + } + WFIFOW (fd, 2) = len; + WFIFOSET (fd, len); + } + else + { + login_log + ("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)\n", + server_name, account.userid, + account.passwd, ip); + WFIFOW (fd, 0) = 0x2711; + WFIFOB (fd, 2) = 3; + WFIFOSET (fd, 3); + } + } + RFIFOSKIP (fd, 86); + return; + + case 0x7530: // Request of the server version + login_log ("Sending of the server version (ip: %s)\n", + ip); + WFIFOW (fd, 0) = 0x7531; + WFIFOB (fd, 2) = -1; + WFIFOB (fd, 3) = 'T'; + WFIFOB (fd, 4) = 'M'; + WFIFOB (fd, 5) = 'W'; + WFIFOL (fd, 6) = new_account_flag ? 1 : 0; + WFIFOSET (fd, 10); + RFIFOSKIP (fd, 2); + break; + + case 0x7532: // Request to end connection + login_log ("End of connection (ip: %s)\n", ip); + session[fd]->eof = 1; + return; + + case 0x7918: // Request for administation login + if (RFIFOREST (fd) < 4 + || RFIFOREST (fd) < ((RFIFOW (fd, 2) == 0) ? 28 : 20)) + return; + WFIFOW (fd, 0) = 0x7919; + WFIFOB (fd, 2) = 1; + if (!check_ladminip + (session[fd]->client_addr.sin_addr.s_addr)) + { + login_log + ("'ladmin'-login: Connection in administration mode refused: IP isn't authorised (ladmin_allow, ip: %s).\n", + ip); + } + else + { + struct login_session_data *ld = (struct login_session_data *)session[fd]->session_data; + if (RFIFOW (fd, 2) == 0) + { // non encrypted password + unsigned char *password; + password = RFIFOP (fd, 4); + password[23] = '\0'; + remove_control_chars (password); + // If remote administration is enabled and password sent by client matches password read from login server configuration file + if ((admin_state == 1) + && (strcmp (password, admin_pass) == 0)) + { + login_log + ("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)\n", + password, ip); + printf + ("Connection of a remote administration accepted (non encrypted password).\n"); + WFIFOB (fd, 2) = 0; + session[fd]->func_parse = parse_admin; + } + else if (admin_state != 1) + login_log + ("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)\n", + password, ip); + else + login_log + ("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)\n", + password, ip); + } + else + { // encrypted password + if (!ld) + printf + ("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); + else + { + char md5str[64] = "", md5bin[32]; + if (RFIFOW (fd, 2) == 1) + { + strncpy (md5str, ld->md5key, sizeof (ld->md5key)); // 20 + strcat (md5str, admin_pass); // 24 + } + else if (RFIFOW (fd, 2) == 2) + { + strncpy (md5str, admin_pass, sizeof (admin_pass)); // 24 + strcat (md5str, ld->md5key); // 20 + } + MD5_to_bin(MD5_from_cstring(md5str), md5bin); + // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file + if ((admin_state == 1) + && (memcmp (md5bin, RFIFOP (fd, 4), 16) == 0)) + { + login_log + ("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)\n", + ip); + printf + ("Connection of a remote administration accepted (encrypted password).\n"); + WFIFOB (fd, 2) = 0; + session[fd]->func_parse = parse_admin; + } + else if (admin_state != 1) + login_log + ("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)\n", + ip); + else + login_log + ("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)\n", + ip); + } + } + } + WFIFOSET (fd, 3); + RFIFOSKIP (fd, (RFIFOW (fd, 2) == 0) ? 28 : 20); + break; + + default: + if (save_unknown_packets) + { + FILE *logfp; + char tmpstr[24]; + struct timeval tv; + logfp = fopen_ (login_log_unknown_packets_filename, "a"); + if (logfp) + { + gettimeofday (&tv, NULL); + strftime (tmpstr, 23, date_format, + gmtime (&(tv.tv_sec))); + fprintf (logfp, + "%s.%03d: receiving of an unknown packet -> disconnection\n", + tmpstr, (int) tv.tv_usec / 1000); + fprintf (logfp, + "parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %d).\n", + fd, ip, RFIFOW (fd, 0), + RFIFOREST (fd)); + fprintf (logfp, "Detail (in hex):\n"); + fprintf (logfp, + "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"); + memset (tmpstr, '\0', sizeof (tmpstr)); + for (i = 0; i < RFIFOREST (fd); i++) + { + if ((i & 15) == 0) + fprintf (logfp, "%04X ", i); + fprintf (logfp, "%02x ", RFIFOB (fd, i)); + if (RFIFOB (fd, i) > 0x1f) + tmpstr[i % 16] = RFIFOB (fd, i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + else if ((i + 1) % 16 == 0) + { + fprintf (logfp, " %s\n", tmpstr); + memset (tmpstr, '\0', sizeof (tmpstr)); + } + } + if (i % 16 != 0) + { + for (j = i; j % 16 != 0; j++) + { + fprintf (logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf (logfp, " "); + } + fprintf (logfp, " %s\n", tmpstr); + } + fprintf (logfp, "\n"); + fclose_ (logfp); + } + } + login_log ("End of connection, unknown packet (ip: %s)\n", ip); + session[fd]->eof = 1; + return; + } + } + return; +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, français, deutsch, español +//------------------------------------------------- +int config_switch (const char *str) +{ + if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 + || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 + || strcasecmp (str, "si") == 0) + return 1; + if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 + || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) + return 0; + + return atoi (str); +} + +//---------------------------------- +// Reading Lan Support configuration +//---------------------------------- +int login_lan_config_read (const char *lancfgName) +{ + int j; + struct hostent *h = NULL; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + // set default configuration + strncpy (lan_char_ip, "127.0.0.1", sizeof (lan_char_ip)); + subneti[0] = 127; + subneti[1] = 0; + subneti[2] = 0; + subneti[3] = 1; + for (j = 0; j < 4; j++) + subnetmaski[j] = 255; + + fp = fopen_ (lancfgName, "r"); + + if (fp == NULL) + { + printf + ("***WARNING: LAN Support configuration file is not found: %s\n", + lancfgName); + return 1; + } + + printf ("---Start reading Lan Support configuration file\n"); + + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof (line) - 1] = '\0'; + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars (w1); + remove_control_chars (w2); + if (strcasecmp (w1, "lan_char_ip") == 0) + { // Read Char-Server Lan IP Address + h = gethostbyname (w2); + if (h != NULL) + { + sprintf (lan_char_ip, "%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]); + } + else + { + strncpy (lan_char_ip, w2, sizeof (lan_char_ip)); + lan_char_ip[sizeof (lan_char_ip) - 1] = '\0'; + } + printf ("LAN IP of char-server: %s.\n", lan_char_ip); + } + else if (strcasecmp (w1, "subnet") == 0) + { // Read Subnetwork + for (j = 0; j < 4; j++) + subneti[j] = 0; + h = gethostbyname (w2); + if (h != NULL) + { + for (j = 0; j < 4; j++) + subneti[j] = (unsigned char) h->h_addr[j]; + } + else + { + sscanf (w2, "%d.%d.%d.%d", &subneti[0], &subneti[1], + &subneti[2], &subneti[3]); + } + printf ("Sub-network of the char-server: %d.%d.%d.%d.\n", + subneti[0], subneti[1], subneti[2], subneti[3]); + } + else if (strcasecmp (w1, "subnetmask") == 0) + { // Read Subnetwork Mask + for (j = 0; j < 4; j++) + subnetmaski[j] = 255; + h = gethostbyname (w2); + if (h != NULL) + { + for (j = 0; j < 4; j++) + subnetmaski[j] = (unsigned char) h->h_addr[j]; + } + else + { + sscanf (w2, "%d.%d.%d.%d", &subnetmaski[0], &subnetmaski[1], + &subnetmaski[2], &subnetmaski[3]); + } + printf ("Sub-network mask of the char-server: %d.%d.%d.%d.\n", + subnetmaski[0], subnetmaski[1], subnetmaski[2], + subnetmaski[3]); + } + } + fclose_ (fp); + + // log the LAN configuration + login_log ("The LAN configuration of the server is set:\n"); + login_log ("- with LAN IP of char-server: %s.\n", lan_char_ip); + login_log + ("- with the sub-network of the char-server: %d.%d.%d.%d/%d.%d.%d.%d.\n", + subneti[0], subneti[1], subneti[2], subneti[3], + subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + + // sub-network check of the char-server + { + unsigned int a0, a1, a2, a3; + unsigned char p[4]; + sscanf (lan_char_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); + p[0] = a0; + p[1] = a1; + p[2] = a2; + p[3] = a3; + printf ("LAN test of LAN IP of the char-server: "); + if (lan_ip_check (p) == 0) + { + printf + ("\033[1;31m***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network\033[0m\n"); + login_log + ("***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network.\n"); + } + } + + printf ("---End reading of Lan Support configuration file\n"); + + return 0; +} + +//----------------------------------- +// Reading general configuration file +//----------------------------------- +int login_config_read (const char *cfgName) +{ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen_ (cfgName, "r"); + if (fp == NULL) + { + printf ("Configuration file (%s) not found.\n", cfgName); + return 1; + } + + printf ("---Start reading of Login Server configuration file (%s)\n", + cfgName); + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof (line) - 1] = '\0'; + if (sscanf (line, "%[^:]: %[^\r\n]", w1, w2) == 2) + { + remove_control_chars (w1); + remove_control_chars (w2); + + if (strcasecmp (w1, "admin_state") == 0) + { + admin_state = config_switch (w2); + } + else if (strcasecmp (w1, "admin_pass") == 0) + { + strncpy (admin_pass, w2, sizeof (admin_pass)); + admin_pass[sizeof (admin_pass) - 1] = '\0'; + } + else if (strcasecmp (w1, "ladminallowip") == 0) + { + if (strcasecmp (w2, "clear") == 0) + { + if (access_ladmin_allow) + free (access_ladmin_allow); + access_ladmin_allow = NULL; + access_ladmin_allownum = 0; + } + else + { + if (strcasecmp (w2, "all") == 0) + { + // reset all previous values + if (access_ladmin_allow) + free (access_ladmin_allow); + // set to all + CREATE (access_ladmin_allow, char, ACO_STRSIZE); + access_ladmin_allownum = 1; + } + else if (w2[0] + && !(access_ladmin_allownum == 1 + && access_ladmin_allow[0] == '\0')) + { // don't add IP if already 'all' + if (access_ladmin_allow) + RECREATE (access_ladmin_allow, char, (access_ladmin_allownum + 1) * ACO_STRSIZE); + else + CREATE (access_ladmin_allow, char, ACO_STRSIZE); + strncpy (access_ladmin_allow + + (access_ladmin_allownum++) * ACO_STRSIZE, w2, + ACO_STRSIZE); + access_ladmin_allow[access_ladmin_allownum * + ACO_STRSIZE - 1] = '\0'; + } + } + } + else if (strcasecmp (w1, "gm_pass") == 0) + { + strncpy (gm_pass, w2, sizeof (gm_pass)); + gm_pass[sizeof (gm_pass) - 1] = '\0'; + } + else if (strcasecmp (w1, "level_new_gm") == 0) + { + level_new_gm = atoi (w2); + } + else if (strcasecmp (w1, "new_account") == 0) + { + new_account_flag = config_switch (w2); + } + else if (strcasecmp (w1, "login_port") == 0) + { + login_port = atoi (w2); + } + else if (strcasecmp (w1, "account_filename") == 0) + { + strncpy (account_filename, w2, sizeof (account_filename)); + account_filename[sizeof (account_filename) - 1] = '\0'; + } + else if (strcasecmp (w1, "gm_account_filename") == 0) + { + strncpy (GM_account_filename, w2, + sizeof (GM_account_filename)); + GM_account_filename[sizeof (GM_account_filename) - 1] = '\0'; + } + else if (strcasecmp (w1, "gm_account_filename_check_timer") == 0) + { + gm_account_filename_check_timer = atoi (w2); + } + else if (strcasecmp (w1, "login_log_filename") == 0) + { + strncpy (login_log_filename, w2, sizeof (login_log_filename)); + login_log_filename[sizeof (login_log_filename) - 1] = '\0'; + } + else if (strcasecmp (w1, "login_log_unknown_packets_filename") == 0) + { + strncpy (login_log_unknown_packets_filename, w2, + sizeof (login_log_unknown_packets_filename)); + login_log_unknown_packets_filename[sizeof + (login_log_unknown_packets_filename) + - 1] = '\0'; + } + else if (strcasecmp (w1, "save_unknown_packets") == 0) + { + save_unknown_packets = config_switch (w2); + } + else if (strcasecmp (w1, "display_parse_login") == 0) + { + display_parse_login = config_switch (w2); // 0: no, 1: yes + } + else if (strcasecmp (w1, "display_parse_admin") == 0) + { + display_parse_admin = config_switch (w2); // 0: no, 1: yes + } + else if (strcasecmp (w1, "display_parse_fromchar") == 0) + { + display_parse_fromchar = config_switch (w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets + } + else if (strcasecmp (w1, "date_format") == 0) + { // note: never have more than 19 char for the date! + switch (atoi (w2)) + { + case 0: + strcpy (date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 + break; + case 1: + strcpy (date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 + break; + case 2: + strcpy (date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 + break; + case 3: + strcpy (date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 + break; + } + } + else if (strcasecmp (w1, "min_level_to_connect") == 0) + { + min_level_to_connect = atoi (w2); + } + else if (strcasecmp (w1, "add_to_unlimited_account") == 0) + { + add_to_unlimited_account = config_switch (w2); + } + else if (strcasecmp (w1, "start_limited_time") == 0) + { + start_limited_time = atoi (w2); + } + else if (strcasecmp (w1, "check_ip_flag") == 0) + { + check_ip_flag = config_switch (w2); + } + else if (strcasecmp (w1, "order") == 0) + { + access_order = atoi (w2); + if (strcasecmp (w2, "deny,allow") == 0 || + strcasecmp (w2, "deny, allow") == 0) + access_order = ACO_DENY_ALLOW; + if (strcasecmp (w2, "allow,deny") == 0 || + strcasecmp (w2, "allow, deny") == 0) + access_order = ACO_ALLOW_DENY; + if (strcasecmp (w2, "mutual-failture") == 0 || + strcasecmp (w2, "mutual-failure") == 0) + access_order = ACO_MUTUAL_FAILTURE; + } + else if (strcasecmp (w1, "allow") == 0) + { + if (strcasecmp (w2, "clear") == 0) + { + if (access_allow) + free (access_allow); + access_allow = NULL; + access_allownum = 0; + } + else + { + if (strcasecmp (w2, "all") == 0) + { + // reset all previous values + if (access_allow) + free (access_allow); + // set to all + CREATE (access_allow, char, ACO_STRSIZE); + access_allownum = 1; + } + else if (w2[0] + && !(access_allownum == 1 + && access_allow[0] == '\0')) + { // don't add IP if already 'all' + if (access_allow) + RECREATE (access_allow, char, (access_allownum + 1) * ACO_STRSIZE); + else + CREATE (access_allow, char, ACO_STRSIZE); + strncpy (access_allow + + (access_allownum++) * ACO_STRSIZE, w2, + ACO_STRSIZE); + access_allow[access_allownum * ACO_STRSIZE - 1] = + '\0'; + } + } + } + else if (strcasecmp (w1, "deny") == 0) + { + if (strcasecmp (w2, "clear") == 0) + { + if (access_deny) + free (access_deny); + access_deny = NULL; + access_denynum = 0; + } + else + { + if (strcasecmp (w2, "all") == 0) + { + // reset all previous values + if (access_deny) + free (access_deny); + // set to all + CREATE (access_deny, char, ACO_STRSIZE); + access_denynum = 1; + } + else if (w2[0] + && !(access_denynum == 1 + && access_deny[0] == '\0')) + { // don't add IP if already 'all' + if (access_deny) + RECREATE (access_deny, char, (access_denynum + 1) * ACO_STRSIZE); + else + CREATE (access_deny, char, ACO_STRSIZE); + strncpy (access_deny + + (access_denynum++) * ACO_STRSIZE, w2, + ACO_STRSIZE); + access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; + } + } + } + else if (strcasecmp (w1, "anti_freeze_enable") == 0) + { + anti_freeze_enable = config_switch (w2); + } + else if (strcasecmp (w1, "anti_freeze_interval") == 0) + { + ANTI_FREEZE_INTERVAL = atoi (w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } + else if (strcasecmp (w1, "import") == 0) + { + login_config_read (w2); + } + else if (strcasecmp (w1, "update_host") == 0) + { + strncpy (update_host, w2, sizeof (update_host)); + update_host[sizeof (update_host) - 1] = '\0'; + } + else if (strcasecmp (w1, "main_server") == 0) + { + strncpy (main_server, w2, sizeof (main_server)); + main_server[sizeof (main_server) - 1] = '\0'; + } + } + } + fclose_ (fp); + + printf ("---End reading of Login Server configuration file.\n"); + + return 0; +} + +//------------------------------------- +// Displaying of configuration warnings +//------------------------------------- +void display_conf_warnings (void) +{ + if (admin_state != 0 && admin_state != 1) + { + printf + ("***WARNING: Invalid value for admin_state parameter -> set to 0 (no remote admin).\n"); + admin_state = 0; + } + + if (admin_state == 1) + { + if (admin_pass[0] == '\0') + { + printf + ("***WARNING: Administrator password is void (admin_pass).\n"); + } + else if (strcmp (admin_pass, "admin") == 0) + { + printf + ("***WARNING: You are using the default administrator password (admin_pass).\n"); + printf (" We highly recommend that you change it.\n"); + } + } + + if (gm_pass[0] == '\0') + { + printf ("***WARNING: 'To GM become' password is void (gm_pass).\n"); + printf + (" We highly recommend that you set one password.\n"); + } + else if (strcmp (gm_pass, "gm") == 0) + { + printf + ("***WARNING: You are using the default GM password (gm_pass).\n"); + printf (" We highly recommend that you change it.\n"); + } + + if (level_new_gm < 0 || level_new_gm > 99) + { + printf + ("***WARNING: Invalid value for level_new_gm parameter -> set to 60 (default).\n"); + level_new_gm = 60; + } + + if (new_account_flag != 0 && new_account_flag != 1) + { + printf + ("***WARNING: Invalid value for new_account parameter -> set to 0 (no new account).\n"); + new_account_flag = 0; + } + + if (login_port < 1024 || login_port > 65535) + { + printf + ("***WARNING: Invalid value for login_port parameter -> set to 6900 (default).\n"); + login_port = 6900; + } + + if (gm_account_filename_check_timer < 0) + { + printf + ("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); + printf (" -> set to 15 sec (default).\n"); + gm_account_filename_check_timer = 15; + } + else if (gm_account_filename_check_timer == 1) + { + printf + ("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); + printf (" -> set to 2 sec (minimum value).\n"); + gm_account_filename_check_timer = 2; + } + + if (save_unknown_packets != 0 && save_unknown_packets != 1) + { + printf + ("WARNING: Invalid value for save_unknown_packets parameter -> set to 0-no save.\n"); + save_unknown_packets = 0; + } + + if (display_parse_login != 0 && display_parse_login != 1) + { // 0: no, 1: yes + printf + ("***WARNING: Invalid value for display_parse_login parameter\n"); + printf (" -> set to 0 (no display).\n"); + display_parse_login = 0; + } + + if (display_parse_admin != 0 && display_parse_admin != 1) + { // 0: no, 1: yes + printf + ("***WARNING: Invalid value for display_parse_admin parameter\n"); + printf (" -> set to 0 (no display).\n"); + display_parse_admin = 0; + } + + if (display_parse_fromchar < 0 || display_parse_fromchar > 2) + { // 0: no, 1: yes (without packet 0x2714), 2: all packets + printf + ("***WARNING: Invalid value for display_parse_fromchar parameter\n"); + printf (" -> set to 0 (no display).\n"); + display_parse_fromchar = 0; + } + + if (min_level_to_connect < 0) + { // 0: all players, 1-99 at least gm level x + printf + ("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", + min_level_to_connect); + printf (" -> set to 0 (any player).\n"); + min_level_to_connect = 0; + } + else if (min_level_to_connect > 99) + { // 0: all players, 1-99 at least gm level x + printf + ("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", + min_level_to_connect); + printf (" -> set to 99 (only GM level 99).\n"); + min_level_to_connect = 99; + } + + if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) + { // 0: no, 1: yes + printf + ("***WARNING: Invalid value for add_to_unlimited_account parameter\n"); + printf + (" -> set to 0 (impossible to add a time to an unlimited account).\n"); + add_to_unlimited_account = 0; + } + + if (start_limited_time < -1) + { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time + printf + ("***WARNING: Invalid value for start_limited_time parameter\n"); + printf + (" -> set to -1 (new accounts are created with unlimited time).\n"); + start_limited_time = -1; + } + + if (check_ip_flag != 0 && check_ip_flag != 1) + { // 0: no, 1: yes + printf ("***WARNING: Invalid value for check_ip_flag parameter\n"); + printf + (" -> set to 1 (check players ip between login-server & char-server).\n"); + check_ip_flag = 1; + } + + if (access_order == ACO_DENY_ALLOW) + { + if (access_denynum == 1 && access_deny[0] == '\0') + { + printf + ("***WARNING: The IP security order is 'deny,allow' (allow if not deny).\n"); + printf (" And you refuse ALL IP.\n"); + } + } + else if (access_order == ACO_ALLOW_DENY) + { + if (access_allownum == 0) + { + printf + ("***WARNING: The IP security order is 'allow,deny' (deny if not allow).\n"); + printf (" But, NO IP IS AUTHORISED!\n"); + } + } + else + { // ACO_MUTUAL_FAILTURE + if (access_allownum == 0) + { + printf + ("***WARNING: The IP security order is 'mutual-failture'\n"); + printf + (" (allow if in the allow list and not in the deny list).\n"); + printf (" But, NO IP IS AUTHORISED!\n"); + } + else if (access_denynum == 1 && access_deny[0] == '\0') + { + printf ("***WARNING: The IP security order is mutual-failture\n"); + printf + (" (allow if in the allow list and not in the deny list).\n"); + printf (" But, you refuse ALL IP!\n"); + } + } + + return; +} + +//------------------------------- +// Save configuration in log file +//------------------------------- +void save_config_in_log (void) +{ + int i; + + // a newline in the log... + login_log (""); + login_log ("The login-server starting...\n"); + + // save configuration in log file + login_log ("The configuration of the server is set:\n"); + + if (admin_state != 1) + login_log ("- with no remote administration.\n"); + else if (admin_pass[0] == '\0') + login_log ("- with a remote administration with a VOID password.\n"); + else if (strcmp (admin_pass, "admin") == 0) + login_log ("- with a remote administration with the DEFAULT password.\n"); + else + login_log + ("- with a remote administration with the password of %d character(s).\n", + strlen (admin_pass)); + if (access_ladmin_allownum == 0 + || (access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) + { + login_log ("- to accept any IP for remote administration\n"); + } + else + { + login_log ("- to accept following IP for remote administration:\n"); + for (i = 0; i < access_ladmin_allownum; i++) + login_log (" %s\n", + (char *) (access_ladmin_allow + i * ACO_STRSIZE)); + } + + if (gm_pass[0] == '\0') + login_log ("- with a VOID 'To GM become' password (gm_pass).\n"); + else if (strcmp (gm_pass, "gm") == 0) + login_log ("- with the DEFAULT 'To GM become' password (gm_pass).\n"); + else + login_log + ("- with a 'To GM become' password (gm_pass) of %d character(s).\n", + strlen (gm_pass)); + if (level_new_gm == 0) + login_log ("- to refuse any creation of GM with @gm.\n"); + else + login_log ("- to create GM with level '%d' when @gm is used.\n", + level_new_gm); + + if (new_account_flag == 1) + login_log ("- to ALLOW new users (with _F/_M).\n"); + else + login_log ("- to NOT ALLOW new users (with _F/_M).\n"); + login_log ("- with port: %d.\n", login_port); + login_log ("- with the accounts file name: '%s'.\n", + account_filename); + login_log ("- with the GM accounts file name: '%s'.\n", + GM_account_filename); + if (gm_account_filename_check_timer == 0) + login_log ("- to NOT check GM accounts file modifications.\n"); + else + login_log + ("- to check GM accounts file modifications every %d seconds.\n", + gm_account_filename_check_timer); + + // not necessary to log the 'login_log_filename', we are inside :) + + login_log ("- with the unknown packets file name: '%s'.\n", + login_log_unknown_packets_filename); + if (save_unknown_packets) + login_log ("- to SAVE all unkown packets.\n"); + else + login_log + ("- to SAVE only unkown packets sending by a char-server or a remote administration.\n"); + if (display_parse_login) + login_log ("- to display normal parse packets on console.\n"); + else + login_log ("- to NOT display normal parse packets on console.\n"); + if (display_parse_admin) + login_log ("- to display administration parse packets on console.\n"); + else + login_log ("- to NOT display administration parse packets on console.\n"); + if (display_parse_fromchar) + login_log ("- to display char-server parse packets on console.\n"); + else + login_log ("- to NOT display char-server parse packets on console.\n"); + + if (min_level_to_connect == 0) // 0: all players, 1-99 at least gm level x + login_log ("- with no minimum level for connection.\n"); + else if (min_level_to_connect == 99) + login_log ("- to accept only GM with level 99.\n"); + else + login_log ("- to accept only GM with level %d or more.\n", + min_level_to_connect); + + if (add_to_unlimited_account) + login_log + ("- to authorize adjustment (with timeadd ladmin) on an unlimited account.\n"); + else + login_log + ("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before.\n"); + + if (start_limited_time < 0) + login_log ("- to create new accounts with an unlimited time.\n"); + else if (start_limited_time == 0) + login_log + ("- to create new accounts with a limited time: time of creation.\n"); + else + login_log + ("- to create new accounts with a limited time: time of creation + %d second(s).\n", + start_limited_time); + + if (check_ip_flag) + login_log + ("- with control of players IP between login-server and char-server.\n"); + else + login_log + ("- to not check players IP between login-server and char-server.\n"); + + if (access_order == ACO_DENY_ALLOW) + { + if (access_denynum == 0) + { + login_log + ("- with the IP security order: 'deny,allow' (allow if not deny). You refuse no IP.\n"); + } + else if (access_denynum == 1 && access_deny[0] == '\0') + { + login_log + ("- with the IP security order: 'deny,allow' (allow if not deny). You refuse ALL IP.\n"); + } + else + { + login_log + ("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:\n"); + for (i = 0; i < access_denynum; i++) + login_log (" %s\n", + (char *) (access_deny + i * ACO_STRSIZE)); + } + } + else if (access_order == ACO_ALLOW_DENY) + { + if (access_allownum == 0) + { + login_log + ("- with the IP security order: 'allow,deny' (deny if not allow). But, NO IP IS AUTHORISED!\n"); + } + else if (access_allownum == 1 && access_allow[0] == '\0') + { + login_log + ("- with the IP security order: 'allow,deny' (deny if not allow). You authorise ALL IP.\n"); + } + else + { + login_log + ("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:\n"); + for (i = 0; i < access_allownum; i++) + login_log (" %s\n", + (char *) (access_allow + i * ACO_STRSIZE)); + } + } + else + { // ACO_MUTUAL_FAILTURE + login_log + ("- with the IP security order: 'mutual-failture' (allow if in the allow list and not in the deny list).\n"); + if (access_allownum == 0) + { + login_log (" But, NO IP IS AUTHORISED!\n"); + } + else if (access_denynum == 1 && access_deny[0] == '\0') + { + login_log (" But, you refuse ALL IP!\n"); + } + else + { + if (access_allownum == 1 && access_allow[0] == '\0') + { + login_log (" You authorise ALL IP.\n"); + } + else + { + login_log (" Authorised IP are:\n"); + for (i = 0; i < access_allownum; i++) + login_log (" %s\n", + (char *) (access_allow + i * ACO_STRSIZE)); + } + login_log (" Refused IP are:\n"); + for (i = 0; i < access_denynum; i++) + login_log (" %s\n", + (char *) (access_deny + i * ACO_STRSIZE)); + } + } +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void term_func (void) +{ + int i, fd; + + mmo_auth_sync (); + + free (auth_dat); + free (gm_account_db); + for (i = 0; i < MAX_SERVERS; i++) + { + if ((fd = server_fd[i]) >= 0) + delete_session (fd); + } + delete_session (login_fd); + + login_log + ("----End of login-server (normal end with closing of all files).\n"); +} + +//------------------------------ +// Main function of login-server +//------------------------------ +int do_init (int argc, char **argv) +{ + int i, j; + + // read login-server configuration + login_config_read ((argc > 1) ? argv[1] : LOGIN_CONF_NAME); + display_conf_warnings (); // not in login_config_read, because we can use 'import' option, and display same message twice or more + save_config_in_log (); // not before, because log file name can be changed + login_lan_config_read ((argc > 1) ? argv[1] : LAN_CONF_NAME); + + for (i = 0; i < AUTH_FIFO_SIZE; i++) + auth_fifo[i].delflag = 1; + for (i = 0; i < MAX_SERVERS; i++) + server_fd[i] = -1; + + gm_account_db = numdb_init (); + + read_gm_account (); + mmo_auth_init (); +// set_termfunc (mmo_auth_sync); + set_defaultparse (parse_login); + login_fd = make_listen_port (login_port); + +// add_timer_func_list (check_auth_sync, "check_auth_sync"); + + // Trigger auth sync every 5 minutes + i = add_timer_interval (gettick () + 300000, check_auth_sync, 0, 0, 300000); + + if (anti_freeze_enable > 0) + { +// add_timer_func_list (char_anti_freeze_system, "char_anti_freeze_system"); + i = add_timer_interval (gettick () + 1000, char_anti_freeze_system, 0, + 0, ANTI_FREEZE_INTERVAL * 1000); + } + + // add timer to check GM accounts file modification + j = gm_account_filename_check_timer; + if (j == 0) // if we would not to check, we check every 60 sec, just to have timer (if we change timer, is was not necessary to check if timer already exists) + j = 60; +// add_timer_func_list (check_GM_file, "check_GM_file"); + i = add_timer_interval (gettick () + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed + + login_log + ("The login-server is ready (Server is listening on the port %d).\n", + login_port); + + printf + ("The login-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", + login_port); + + return 0; +} diff --git a/src/login/login.h b/src/login/login.h deleted file mode 100644 index a1f8fef..0000000 --- a/src/login/login.h +++ /dev/null @@ -1,41 +0,0 @@ -// $Id: login.h,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ -#ifndef _LOGIN_H_ -#define _LOGIN_H_ - -#define MAX_SERVERS 30 - -#define LOGIN_CONF_NAME "conf/login_athena.conf" -#define LAN_CONF_NAME "conf/lan_support.conf" -// It seems we don't need to emulate RO's "password encryption" - MC/TMW -//#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. - // It is 1 at the time of passwordencrypt. - // It is made into 2 at the time of passwordencrypt2. - // When it is made 3, it corresponds to both. -#define START_ACCOUNT_NUM 2000000 -#define END_ACCOUNT_NUM 100000000 - -struct mmo_account -{ - char *userid; - char *passwd; - int passwdenc; - - long account_id; - long login_id1; - long login_id2; - long char_id; - char lastlogin[24]; - int sex; -}; - -struct mmo_char_server -{ - char name[20]; - long ip; - short port; - int users; - int maintenance; - int is_new; -}; - -#endif diff --git a/src/login/login.hpp b/src/login/login.hpp new file mode 100644 index 0000000..49d1c12 --- /dev/null +++ b/src/login/login.hpp @@ -0,0 +1,41 @@ +// $Id: login.h,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +#ifndef LOGIN_HPP +#define LOGIN_HPP + +#define MAX_SERVERS 30 + +#define LOGIN_CONF_NAME "conf/login_athena.conf" +#define LAN_CONF_NAME "conf/lan_support.conf" +// It seems we don't need to emulate RO's "password encryption" - MC/TMW +//#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. +#define START_ACCOUNT_NUM 2000000 +#define END_ACCOUNT_NUM 100000000 + +struct mmo_account +{ + char *userid; + char *passwd; + int passwdenc; + + long account_id; + long login_id1; + long login_id2; + long char_id; + char lastlogin[24]; + int sex; +}; + +struct mmo_char_server +{ + char name[20]; + long ip; + short port; + int users; + int maintenance; + int is_new; +}; + +#endif diff --git a/src/map/atcommand.c b/src/map/atcommand.c deleted file mode 100644 index 88e756d..0000000 --- a/src/map/atcommand.c +++ /dev/null @@ -1,8728 +0,0 @@ -// $Id: atcommand.c 148 2004-09-30 14:05:37Z MouseJstr $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <math.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <time.h> -#include <unistd.h> - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/mt_rand.h" - -#include "atcommand.h" -#include "battle.h" -#include "clif.h" -#include "chrif.h" -#include "guild.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "pc.h" -#include "party.h" -#include "script.h" -#include "skill.h" -#include "trade.h" - -#include "../common/core.h" -#include "tmw.h" - -#define STATE_BLIND 0x10 - -static char command_symbol = '@'; // first char of the commands (by [Yor]) - -static char msg_table[1000][1024]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) - -#define ATCOMMAND_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) -ATCOMMAND_FUNC (setup); -ATCOMMAND_FUNC (broadcast); -ATCOMMAND_FUNC (localbroadcast); -ATCOMMAND_FUNC (charwarp); -ATCOMMAND_FUNC (warp); -ATCOMMAND_FUNC (where); -ATCOMMAND_FUNC (goto); -ATCOMMAND_FUNC (jump); -ATCOMMAND_FUNC (who); -ATCOMMAND_FUNC (whogroup); -ATCOMMAND_FUNC (whomap); -ATCOMMAND_FUNC (whomapgroup); -ATCOMMAND_FUNC (whogm); // by Yor -ATCOMMAND_FUNC (save); -ATCOMMAND_FUNC (load); -ATCOMMAND_FUNC (speed); -ATCOMMAND_FUNC (storage); -ATCOMMAND_FUNC (guildstorage); -ATCOMMAND_FUNC (option); -ATCOMMAND_FUNC (hide); -ATCOMMAND_FUNC (die); -ATCOMMAND_FUNC (kill); -ATCOMMAND_FUNC (alive); -ATCOMMAND_FUNC (kami); -ATCOMMAND_FUNC (heal); -ATCOMMAND_FUNC (item); -ATCOMMAND_FUNC (itemreset); -ATCOMMAND_FUNC (itemcheck); -ATCOMMAND_FUNC (baselevelup); -ATCOMMAND_FUNC (joblevelup); -ATCOMMAND_FUNC (help); -ATCOMMAND_FUNC (gm); -ATCOMMAND_FUNC (pvpoff); -ATCOMMAND_FUNC (pvpon); -ATCOMMAND_FUNC (gvgoff); -ATCOMMAND_FUNC (gvgon); -ATCOMMAND_FUNC (model); -ATCOMMAND_FUNC (go); -ATCOMMAND_FUNC (spawn); -ATCOMMAND_FUNC (killmonster); -ATCOMMAND_FUNC (killmonster2); -ATCOMMAND_FUNC (refine); -ATCOMMAND_FUNC (produce); -ATCOMMAND_FUNC (memo); -ATCOMMAND_FUNC (gat); -ATCOMMAND_FUNC (packet); -ATCOMMAND_FUNC (statuspoint); -ATCOMMAND_FUNC (skillpoint); -ATCOMMAND_FUNC (zeny); -ATCOMMAND_FUNC (param); -ATCOMMAND_FUNC (guildlevelup); -ATCOMMAND_FUNC (recall); -ATCOMMAND_FUNC (recallall); -ATCOMMAND_FUNC (revive); -ATCOMMAND_FUNC (character_stats); -ATCOMMAND_FUNC (character_stats_all); -ATCOMMAND_FUNC (character_option); -ATCOMMAND_FUNC (character_save); -ATCOMMAND_FUNC (night); -ATCOMMAND_FUNC (day); -ATCOMMAND_FUNC (doom); -ATCOMMAND_FUNC (doommap); -ATCOMMAND_FUNC (raise); -ATCOMMAND_FUNC (raisemap); -ATCOMMAND_FUNC (character_baselevel); -ATCOMMAND_FUNC (character_joblevel); -ATCOMMAND_FUNC (kick); -ATCOMMAND_FUNC (kickall); -ATCOMMAND_FUNC (allskills); -ATCOMMAND_FUNC (questskill); -ATCOMMAND_FUNC (charquestskill); -ATCOMMAND_FUNC (lostskill); -ATCOMMAND_FUNC (charlostskill); -ATCOMMAND_FUNC (party); -ATCOMMAND_FUNC (guild); -ATCOMMAND_FUNC (charskreset); -ATCOMMAND_FUNC (charstreset); -ATCOMMAND_FUNC (charreset); -ATCOMMAND_FUNC (charstpoint); -ATCOMMAND_FUNC (charmodel); -ATCOMMAND_FUNC (charskpoint); -ATCOMMAND_FUNC (charzeny); -ATCOMMAND_FUNC (agitstart); -ATCOMMAND_FUNC (agitend); -ATCOMMAND_FUNC (reloaditemdb); -ATCOMMAND_FUNC (reloadmobdb); -ATCOMMAND_FUNC (reloadskilldb); -ATCOMMAND_FUNC (reloadscript); -ATCOMMAND_FUNC (reloadgmdb); // by Yor -ATCOMMAND_FUNC (mapexit); -ATCOMMAND_FUNC (idsearch); -ATCOMMAND_FUNC (mapinfo); -ATCOMMAND_FUNC (dye); //** by fritz -ATCOMMAND_FUNC (hair_style); //** by fritz -ATCOMMAND_FUNC (hair_color); //** by fritz -ATCOMMAND_FUNC (all_stats); //** by fritz -ATCOMMAND_FUNC (char_change_sex); // by Yor -ATCOMMAND_FUNC (char_block); // by Yor -ATCOMMAND_FUNC (char_ban); // by Yor -ATCOMMAND_FUNC (char_unblock); // by Yor -ATCOMMAND_FUNC (char_unban); // by Yor -ATCOMMAND_FUNC (mount_peco); // by Valaris -ATCOMMAND_FUNC (char_mount_peco); // by Yor -ATCOMMAND_FUNC (guildspy); // [Syrus22] -ATCOMMAND_FUNC (partyspy); // [Syrus22] -ATCOMMAND_FUNC (guildrecall); // by Yor -ATCOMMAND_FUNC (partyrecall); // by Yor -ATCOMMAND_FUNC (enablenpc); -ATCOMMAND_FUNC (disablenpc); -ATCOMMAND_FUNC (servertime); // by Yor -ATCOMMAND_FUNC (chardelitem); // by Yor -ATCOMMAND_FUNC (jail); // by Yor -ATCOMMAND_FUNC (unjail); // by Yor -ATCOMMAND_FUNC (disguise); // [Valaris] -ATCOMMAND_FUNC (undisguise); // by Yor -ATCOMMAND_FUNC (ignorelist); // by Yor -ATCOMMAND_FUNC (charignorelist); // by Yor -ATCOMMAND_FUNC (inall); // by Yor -ATCOMMAND_FUNC (exall); // by Yor -ATCOMMAND_FUNC (chardisguise); // Kalaspuff -ATCOMMAND_FUNC (charundisguise); // Kalaspuff -ATCOMMAND_FUNC (email); // by Yor -ATCOMMAND_FUNC (effect); //by Apple -ATCOMMAND_FUNC (character_item_list); // by Yor -ATCOMMAND_FUNC (character_storage_list); // by Yor -ATCOMMAND_FUNC (character_cart_list); // by Yor -ATCOMMAND_FUNC (addwarp); // by MouseJstr -ATCOMMAND_FUNC (follow); // by MouseJstr -ATCOMMAND_FUNC (skillon); // by MouseJstr -ATCOMMAND_FUNC (skilloff); // by MouseJstr -ATCOMMAND_FUNC (killer); // by MouseJstr -ATCOMMAND_FUNC (npcmove); // by MouseJstr -ATCOMMAND_FUNC (killable); // by MouseJstr -ATCOMMAND_FUNC (charkillable); // by MouseJstr -ATCOMMAND_FUNC (chareffect); // by MouseJstr -ATCOMMAND_FUNC (chardye); // by MouseJstr -ATCOMMAND_FUNC (charhairstyle); // by MouseJstr -ATCOMMAND_FUNC (charhaircolor); // by MouseJstr -ATCOMMAND_FUNC (dropall); // by MouseJstr -ATCOMMAND_FUNC (chardropall); // by MouseJstr -ATCOMMAND_FUNC (storeall); // by MouseJstr -ATCOMMAND_FUNC (charstoreall); // by MouseJstr -ATCOMMAND_FUNC (skillid); // by MouseJstr -ATCOMMAND_FUNC (useskill); // by MouseJstr -ATCOMMAND_FUNC (summon); -ATCOMMAND_FUNC (rain); -ATCOMMAND_FUNC (snow); -ATCOMMAND_FUNC (sakura); -ATCOMMAND_FUNC (fog); -ATCOMMAND_FUNC (leaves); -ATCOMMAND_FUNC (adjgmlvl); // by MouseJstr -ATCOMMAND_FUNC (adjcmdlvl); // by MouseJstr -ATCOMMAND_FUNC (trade); // by MouseJstr -ATCOMMAND_FUNC (unmute); // [Valaris] -ATCOMMAND_FUNC (char_wipe); // [Fate] -ATCOMMAND_FUNC (set_magic); // [Fate] -ATCOMMAND_FUNC (magic_info); // [Fate] -ATCOMMAND_FUNC (log); // [Fate] -ATCOMMAND_FUNC (tee); // [Fate] -ATCOMMAND_FUNC (invisible); // [Fate] -ATCOMMAND_FUNC (visible); // [Fate] -ATCOMMAND_FUNC (list_nearby); // [Fate] -ATCOMMAND_FUNC (iterate_forward_over_players); // [Fate] -ATCOMMAND_FUNC (iterate_backwards_over_players); // [Fate] -ATCOMMAND_FUNC (skillpool_info); // [Fate] -ATCOMMAND_FUNC (skillpool_focus); // [Fate] -ATCOMMAND_FUNC (skillpool_unfocus); // [Fate] -ATCOMMAND_FUNC (skill_learn); // [Fate] -ATCOMMAND_FUNC (wgm); -ATCOMMAND_FUNC (ipcheck); -ATCOMMAND_FUNC (doomspot); - -/*========================================== - *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_Setup, "@setup", 40, atcommand_setup}, - {AtCommand_CharWarp, "@charwarp", 60, atcommand_charwarp}, - {AtCommand_Warp, "@warp", 40, atcommand_warp}, - {AtCommand_Where, "@where", 1, atcommand_where}, - {AtCommand_Goto, "@goto", 20, atcommand_goto}, - {AtCommand_Jump, "@jump", 40, atcommand_jump}, - {AtCommand_Who, "@who", 20, atcommand_who}, - {AtCommand_WhoGroup, "@whogroup", 20, atcommand_whogroup}, - {AtCommand_WhoMap, "@whomap", 20, atcommand_whomap}, - {AtCommand_WhoMapGroup, "@whomapgroup", 20, atcommand_whomapgroup}, - {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_Die, "@die", 1, atcommand_die}, - {AtCommand_Kill, "@kill", 60, atcommand_kill}, - {AtCommand_Alive, "@alive", 60, atcommand_alive}, - {AtCommand_Kami, "@kami", 40, atcommand_kami}, - {AtCommand_Heal, "@heal", 40, atcommand_heal}, - {AtCommand_Item, "@item", 60, atcommand_item}, - {AtCommand_ItemReset, "@itemreset", 40, atcommand_itemreset}, - {AtCommand_ItemCheck, "@itemcheck", 60, atcommand_itemcheck}, - {AtCommand_BaseLevelUp, "@blvl", 60, atcommand_baselevelup}, - {AtCommand_JobLevelUp, "@jlvl", 60, atcommand_joblevelup}, - {AtCommand_Help, "@help", 20, atcommand_help}, - {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, "@spawn", 50, atcommand_spawn}, - {AtCommand_KillMonster, "@killmonster", 60, atcommand_killmonster}, - {AtCommand_KillMonster2, "@killmonster2", 40, atcommand_killmonster2}, - {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_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, "@guildlvl", 60, atcommand_guildlevelup}, - {AtCommand_Recall, "@recall", 60, atcommand_recall}, // + /recall - {AtCommand_Revive, "@revive", 60, atcommand_revive}, - {AtCommand_CharacterStats, "@charstats", 40, atcommand_character_stats}, - {AtCommand_CharacterStatsAll, "@charstatsall", 40, - atcommand_character_stats_all}, - {AtCommand_CharacterOption, "@charoption", 60, - atcommand_character_option}, - {AtCommand_CharacterSave, "@charsave", 60, atcommand_character_save}, - {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_CharacterBaseLevel, "@charbaselvl", 60, - atcommand_character_baselevel}, - {AtCommand_CharacterJobLevel, "@charjlvl", 60, - atcommand_character_joblevel}, - {AtCommand_Kick, "@kick", 20, atcommand_kick}, // + right click menu for GM "(name) force to quit" - {AtCommand_KickAll, "@kickall", 99, atcommand_kickall}, - {AtCommand_AllSkills, "@allskills", 60, atcommand_allskills}, - {AtCommand_QuestSkill, "@questskill", 40, atcommand_questskill}, - {AtCommand_CharQuestSkill, "@charquestskill", 60, - atcommand_charquestskill}, - {AtCommand_LostSkill, "@lostskill", 40, atcommand_lostskill}, - {AtCommand_CharLostSkill, "@charlostskill", 60, atcommand_charlostskill}, - {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_warp}, // /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_CharSkReset, "@charskreset", 60, atcommand_charskreset}, - {AtCommand_CharStReset, "@charstreset", 60, atcommand_charstreset}, - {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_CharReset, "@charreset", 60, atcommand_charreset}, - {AtCommand_CharModel, "@charmodel", 50, atcommand_charmodel}, - {AtCommand_CharSKPoint, "@charskpoint", 60, atcommand_charskpoint}, - {AtCommand_CharSTPoint, "@charstpoint", 60, atcommand_charstpoint}, - {AtCommand_CharZeny, "@charzeny", 60, atcommand_charzeny}, - {AtCommand_MapInfo, "@mapinfo", 99, atcommand_mapinfo}, - {AtCommand_Dye, "@dye", 40, atcommand_dye}, // by fritz - {AtCommand_Dye, "@ccolor", 40, atcommand_dye}, // by fritz - {AtCommand_HairStyle, "@hairstyle", 40, atcommand_hair_style}, // by fritz - {AtCommand_HairColor, "@haircolor", 40, atcommand_hair_color}, // by fritz - {AtCommand_AllStats, "@allstats", 60, atcommand_all_stats}, // by fritz - {AtCommand_CharChangeSex, "@charchangesex", 60, atcommand_char_change_sex}, // by Yor - {AtCommand_CharBlock, "@block", 60, atcommand_char_block}, // by Yor - {AtCommand_CharUnBlock, "@unblock", 60, atcommand_char_unblock}, // by Yor - {AtCommand_CharBan, "@ban", 60, atcommand_char_ban}, // by Yor - {AtCommand_CharUnBan, "@unban", 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_GuildRecall, "@guildrecall", 60, atcommand_guildrecall}, // by Yor - {AtCommand_PartyRecall, "@partyrecall", 60, atcommand_partyrecall}, // by Yor - {AtCommand_Enablenpc, "@enablenpc", 80, atcommand_enablenpc}, // [] - {AtCommand_Disablenpc, "@disablenpc", 80, atcommand_disablenpc}, // [] - {AtCommand_ServerTime, "@servertime", 0, atcommand_servertime}, // by Yor - {AtCommand_CharDelItem, "@chardelitem", 60, atcommand_chardelitem}, // by Yor - {AtCommand_ListNearby, "@listnearby", 40, atcommand_list_nearby}, // by Yor - {AtCommand_Jail, "@jail", 60, atcommand_jail}, // by Yor - {AtCommand_UnJail, "@unjail", 60, atcommand_unjail}, // by Yor - {AtCommand_Disguise, "@disguise", 20, atcommand_disguise}, // [Valaris] - {AtCommand_UnDisguise, "@undisguise", 20, atcommand_undisguise}, // by Yor - {AtCommand_IgnoreList, "@ignorelist", 0, atcommand_ignorelist}, // by Yor - {AtCommand_CharIgnoreList, "@charignorelist", 20, atcommand_charignorelist}, // by Yor - {AtCommand_IgnoreList, "@inall", 20, atcommand_inall}, // by Yor - {AtCommand_ExAll, "@exall", 20, atcommand_exall}, // by Yor - {AtCommand_CharDisguise, "@chardisguise", 60, atcommand_chardisguise}, // Kalaspuff - {AtCommand_CharUnDisguise, "@charundisguise", 60, atcommand_charundisguise}, // Kalaspuff - {AtCommand_EMail, "@email", 0, atcommand_email}, // by Yor - {AtCommand_Effect, "@effect", 40, atcommand_effect}, // by Apple - {AtCommand_Char_Item_List, "@charitemlist", 40, atcommand_character_item_list}, // by Yor - {AtCommand_Char_Storage_List, "@charstoragelist", 40, atcommand_character_storage_list}, // by Yor - {AtCommand_Char_Cart_List, "@charcartlist", 40, atcommand_character_cart_list}, // by Yor - {AtCommand_Follow, "@follow", 10, atcommand_follow}, // by MouseJstr - {AtCommand_AddWarp, "@addwarp", 20, atcommand_addwarp}, // by MouseJstr - {AtCommand_SkillOn, "@skillon", 20, atcommand_skillon}, // by MouseJstr - {AtCommand_SkillOff, "@skilloff", 20, 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_Chareffect, "@chareffect", 40, atcommand_chareffect}, // MouseJstr - //{ AtCommand_Chardye, "@chardye", 40, atcommand_chardye }, // MouseJstr - //{ AtCommand_Charhairstyle, "@charhairstyle", 40, atcommand_charhairstyle }, // MouseJstr - //{ AtCommand_Charhaircolor, "@charhaircolor", 40, atcommand_charhaircolor }, // 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_Fog, "@fog", 99, atcommand_fog}, - {AtCommand_Leaves, "@leaves", 99, atcommand_leaves}, - //{ AtCommand_Shuffle, "@shuffle", 99, atcommand_shuffle }, - //{ AtCommand_Maintenance, "@maintenance", 99, atcommand_maintenance }, - //{ AtCommand_Misceffect, "@misceffect", 60, atcommand_misceffect }, - {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_UnMute, "@unmute", 60, atcommand_unmute}, // [Valaris] - {AtCommand_UnMute, "@charwipe", 60, atcommand_char_wipe}, // [Fate] - {AtCommand_SetMagic, "@setmagic", 99, atcommand_set_magic}, // [Fate] - {AtCommand_MagicInfo, "@magicinfo", 60, atcommand_magic_info}, // [Fate] - {AtCommand_Log, "@log", 60, atcommand_log}, // [Fate] - {AtCommand_Log, "@l", 60, atcommand_log}, // [Fate] - {AtCommand_Tee, "@tee", 60, atcommand_tee}, // [Fate] - {AtCommand_Tee, "@t", 60, atcommand_tee}, // [Fate] - {AtCommand_Invisible, "@invisible", 60, atcommand_invisible}, // [Fate] - {AtCommand_Visible, "@visible", 60, atcommand_visible}, // [Fate] - {AtCommand_IterateForward, "@hugo", 60, atcommand_iterate_forward_over_players}, // [Fate] - {AtCommand_IterateBackward, "@linus", 60, atcommand_iterate_backwards_over_players}, // [Fate] - {AtCommand_IterateBackward, "@sp-info", 40, atcommand_skillpool_info}, // [Fate] - {AtCommand_IterateBackward, "@sp-focus", 80, atcommand_skillpool_focus}, // [Fate] - {AtCommand_IterateBackward, "@sp-unfocus", 80, atcommand_skillpool_unfocus}, // [Fate] - {AtCommand_IterateBackward, "@skill-learn", 80, atcommand_skill_learn}, // [Fate] - {AtCommand_Wgm, "@wgm", 0, atcommand_wgm}, - {AtCommand_IpCheck, "@ipcheck", 60, atcommand_ipcheck}, - {AtCommand_DoomSpot, "@doomspot", 60, atcommand_doomspot}, - -// add new commands before this line - {AtCommand_Unknown, NULL, 1, NULL} -}; - -/*==================================================== - * This function return the name of the job (by [Yor]) - *---------------------------------------------------- - */ -const char *job_name (int pc_class) -{ - switch (pc_class) - { - case 0: - return "Novice"; - case 1: - return "Swordsman"; - case 2: - return "Mage"; - case 3: - return "Archer"; - case 4: - return "Acolyte"; - case 5: - return "Merchant"; - case 6: - return "Thief"; - case 7: - return "Knight"; - case 8: - return "Priest"; - case 9: - return "Wizard"; - case 10: - return "Blacksmith"; - case 11: - return "Hunter"; - case 12: - return "Assassin"; - case 13: - return "Knight 2"; - case 14: - return "Crusader"; - case 15: - return "Monk"; - case 16: - return "Sage"; - case 17: - return "Rogue"; - case 18: - return "Alchemist"; - case 19: - return "Bard"; - case 20: - return "Dancer"; - case 21: - return "Crusader 2"; - case 22: - return "Wedding"; - case 23: - return "Super Novice"; - case 4001: - return "Novice High"; - case 4002: - return "Swordsman High"; - case 4003: - return "Mage High"; - case 4004: - return "Archer High"; - case 4005: - return "Acolyte High"; - case 4006: - return "Merchant High"; - case 4007: - return "Thief High"; - case 4008: - return "Lord Knight"; - case 4009: - return "High Priest"; - case 4010: - return "High Wizard"; - case 4011: - return "Whitesmith"; - case 4012: - return "Sniper"; - case 4013: - return "Assassin Cross"; - case 4014: - return "Peko Knight"; - case 4015: - return "Paladin"; - case 4016: - return "Champion"; - case 4017: - return "Professor"; - case 4018: - return "Stalker"; - case 4019: - return "Creator"; - case 4020: - return "Clown"; - case 4021: - return "Gypsy"; - case 4022: - return "Peko Paladin"; - case 4023: - return "Baby Novice"; - case 4024: - return "Baby Swordsman"; - case 4025: - return "Baby Mage"; - case 4026: - return "Baby Archer"; - case 4027: - return "Baby Acolyte"; - case 4028: - return "Baby Merchant"; - case 4029: - return "Baby Thief"; - case 4030: - return "Baby Knight"; - case 4031: - return "Baby Priest"; - case 4032: - return "Baby Wizard"; - case 4033: - return "Baby Blacksmith"; - case 4034: - return "Baby Hunter"; - case 4035: - return "Baby Assassin"; - case 4036: - return "Baby Peco Knight"; - case 4037: - return "Baby Crusader"; - case 4038: - return "Baby Monk"; - case 4039: - return "Baby Sage"; - case 4040: - return "Baby Rogue"; - case 4041: - return "Baby Alchemist"; - case 4042: - return "Baby Bard"; - case 4043: - return "Baby Dancer"; - case 4044: - return "Baby Peco Crusader"; - case 4045: - return "Super Baby"; - } - return "Unknown Job"; -} - -//----------------------------------------------------------- -// Return the message string of the specified number by [Yor] -//----------------------------------------------------------- -char *msg_txt (int msg_number) -{ - if (msg_number >= 0 - && msg_number < (int) (sizeof (msg_table) / sizeof (msg_table[0])) - && msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0') - return msg_table[msg_number]; - - return "??"; -} - -//------------------------------------------------------------ -// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] -//------------------------------------------------------------ -int e_mail_check (unsigned char *email) -{ - char ch; - unsigned 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 -} - -/*======================================== - * At-command logging - */ -void log_atcommand (struct map_session_data *sd, const char *fmt, ...) -{ - char message[512]; - va_list ap; - - va_start (ap, fmt); - vsnprintf (message, 511, fmt, ap); - va_end (ap); - - gm_log ("%s(%d,%d) %s(%d) : %s", map[sd->bl.m].name, sd->bl.x, - sd->bl.y, sd->status.name, sd->status.account_id, message); -} - -char *gm_logfile_name = NULL; -/*========================================== - * Log a timestamped line to GM log file - *------------------------------------------ - */ -void gm_log (const char *fmt, ...) -{ - static int last_logfile_nr = 0; - static FILE *gm_logfile = NULL; - time_t time_v; - struct tm ctime; - int month, year, logfile_nr; - char message[512]; - va_list ap; - - if (!gm_logfile_name) - return; - - va_start (ap, fmt); - vsnprintf (message, 511, fmt, ap); - va_end (ap); - - time (&time_v); - gmtime_r (&time_v, &ctime); - - year = ctime.tm_year + 1900; - month = ctime.tm_mon + 1; - logfile_nr = (year * 12) + month; - - if (logfile_nr != last_logfile_nr) - { - char *fullname = (char *)malloc (strlen (gm_logfile_name) + 10); - sprintf (fullname, "%s.%04d-%02d", gm_logfile_name, year, month); - - if (gm_logfile) - fclose_ (gm_logfile); - - gm_logfile = fopen_ (fullname, "a"); - free (fullname); - - if (!gm_logfile) - { - perror ("GM log file"); - gm_logfile_name = NULL; - } - last_logfile_nr = logfile_nr; - } - - fprintf (gm_logfile, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n", - year, month, ctime.tm_mday, ctime.tm_hour, - ctime.tm_min, ctime.tm_sec, message); - - fflush (gm_logfile); -} - -/*========================================== - *is_atcommand @コマンドに存在するかどうか確認する - *------------------------------------------ - */ -AtCommandType -is_atcommand (const int fd, struct map_session_data *sd, const char *message, - int gmlvl) -{ - AtCommandInfo info; - AtCommandType type; - - nullpo_retr (AtCommand_None, sd); - - if (!message || !*message) - return AtCommand_None; - - memset (&info, 0, sizeof (info)); - - type = atcommand (gmlvl > 0 ? gmlvl : pc_isGM (sd), message, &info); - if (type != AtCommand_None) - { - char command[100]; - char output[200]; - const char *str = message; - const char *p = message; - memset (command, '\0', sizeof (command)); - memset (output, '\0', sizeof (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 (output, msg_table[153], command); // %s is Unknown Command. - clif_displaymessage (fd, output); - } - else - { - if (info.proc (fd, sd, command, p) != 0) - { - // Command can not be executed - sprintf (output, msg_table[154], command); // %s failed. - clif_displaymessage (fd, output); - } - else - { - if (get_atcommand_level (type) != 0) // Don't log level 0 commands - log_atcommand (sd, "%s %s", command, p); - } - } - - return info.type; - } - - return AtCommand_None; -} - -/*========================================== - * - *------------------------------------------ - */ -AtCommandType atcommand (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) - { - fprintf (stderr, "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 (strcasecmp (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; - } - memcpy (info, &atcommand_info[i], sizeof atcommand_info[i]); - } - else - { - return AtCommand_None; - } - - return info->type; -} - -/*========================================== - * - *------------------------------------------ - */ -static int atkillmonster_sub (struct block_list *bl, va_list ap) -{ - int flag = va_arg (ap, int); - - nullpo_retr (0, bl); - - if (flag) - mob_damage (NULL, (struct mob_data *) bl, - ((struct mob_data *) bl)->hp, 2); - else - mob_delete ((struct mob_data *) bl); - - return 0; -} - -/*========================================== - * Read Message Data - *------------------------------------------ - */ -int msg_config_read (const char *cfgName) -{ - int msg_number; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - if ((fp = fopen_ (cfgName, "r")) == NULL) - { - printf ("Messages file not found: %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 (strcasecmp (w1, "import") == 0) - { - msg_config_read (w2); - } - else - { - msg_number = atoi (w1); - if (msg_number >= 0 - && msg_number < - (int) (sizeof (msg_table) / sizeof (msg_table[0]))) - strcpy (msg_table[msg_number], w2); - // printf("message #%d: '%s'.\n", msg_number, msg_table[msg_number]); - } - } - } - fclose_ (fp); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static AtCommandInfo *get_atcommandinfo_byname (const char *name) -{ - int i; - - for (i = 0; atcommand_info[i].type != AtCommand_Unknown; i++) - if (strcasecmp (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) - { - printf ("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 (strcasecmp (w1, "import") == 0) - atcommand_config_read (w2); - else if (strcasecmp (w1, "command_symbol") == 0 && w2[0] > 31 && w2[0] != '/' && // symbol of standard ragnarok GM commands - w2[0] != '%') // symbol of party chat speaking - command_symbol = w2[0]; - } - fclose_ (fp); - - return 0; -} - -/*========================================== -// @ command processing functions - *------------------------------------------ - */ - -/*========================================== - * @setup - Safely set a chars levels and warp them to a special place - * TAW Specific - *------------------------------------------ - */ -int atcommand_setup (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char buf[256]; - char character[100]; - int level = 1; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &level, character) < 2) - { - clif_displaymessage (fd, "Usage: @setup <level> <char name>"); - return -1; - } - level--; - - snprintf (buf, 255, "-255 %s", character); - atcommand_character_baselevel (fd, sd, "@charbaselvl", buf); - - snprintf (buf, 255, "%d %s", level, character); - atcommand_character_baselevel (fd, sd, "@charbaselvl", buf); - - // Emote skill - snprintf (buf, 255, "1 1 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf); - - // Trade skill - snprintf (buf, 255, "2 1 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf); - - // Party skill - snprintf (buf, 255, "2 2 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf); - - snprintf (buf, 255, "018-1.gat 24 98 %s", character); - atcommand_charwarp (fd, sd, "@charwarp", buf); - - return (0); - -} - -/*========================================== - * @rura+ - *------------------------------------------ - */ -int atcommand_charwarp (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char map_name[100]; - char character[100]; - int x = 0, y = 0; - struct map_session_data *pl_sd; - int m; - - memset (map_name, '\0', sizeof (map_name)); - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%99s %d %d %99[^\n]", map_name, &x, &y, - character) < 4) - { - clif_displaymessage (fd, - "Usage: @charwarp/@rura+ <mapname> <x> <y> <char name>"); - return -1; - } - - if (x <= 0) - x = MRAND (399) + 1; - if (y <= 0) - y = MRAND (399) + 1; - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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 < 800 && y > 0 && y < 800) - { - 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_name, 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_warp (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char map_name[100]; - int x = 0, y = 0; - int m; - - memset (map_name, '\0', sizeof (map_name)); - - if (!message || !*message - || sscanf (message, "%99s %d %d", map_name, &x, &y) < 1) - { - clif_displaymessage (fd, - "Please, enter a map (usage: @warp <mapname> <x> <y>)."); - return -1; - } - - if (x <= 0) - x = MRAND (399) + 1; - if (y <= 0) - y = MRAND (399) + 1; - - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) - strcat (map_name, ".gat"); - - if (x > 0 && x < 800 && y > 0 && y < 800) - { - 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 you to this map."); - 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, - "You are not authorised to warp you from your actual map."); - return -1; - } - if (pc_setpos (sd, map_name, 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_where (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (sscanf (message, "%99[^\n]", character) < 1) - strcpy (character, sd->status.name); - - if ((pl_sd = map_nick2sd (character)) != NULL && - !((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (pc_isGM (pl_sd) > pc_isGM (sd)))) - { // you can look only lower or same level - sprintf (output, "%s: %s (%d,%d)", pl_sd->status.name, pl_sd->mapname, - pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, output); - } - else - { - clif_displaymessage (fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_goto (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @jumpto/@warpto/@goto <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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, - "You are not authorised to warp you to the map of this player."); - 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, - "You are not authorised to warp you from your actual map."); - return -1; - } - pc_setpos (sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, 3); - sprintf (output, msg_table[4], character); // Jump to %s - clif_displaymessage (fd, 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) -{ - char output[200]; - int x = 0, y = 0; - - memset (output, '\0', sizeof (output)); - - sscanf (message, "%d %d", &x, &y); - - if (x <= 0) - x = MRAND (399) + 1; - if (y <= 0) - y = MRAND (399) + 1; - if (x > 0 && x < 800 && y > 0 && y < 800) - { - 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 you to your actual map."); - 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, - "You are not authorised to warp you from your actual map."); - return -1; - } - pc_setpos (sd, sd->mapname, x, y, 3); - sprintf (output, msg_table[5], x, y); // Jump to %d %d - clif_displaymessage (fd, output); - } - else - { - clif_displaymessage (fd, msg_table[2]); // Coordinates out of range. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_who (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char output[200]; - struct map_session_data *pl_sd; - int i, j, count; - int pl_GM_level, GM_level; - char match_text[100]; - char player_name[24]; - - memset (output, '\0', sizeof (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); - 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_GM_level = pc_isGM (pl_sd); - if (! - ((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (pl_GM_level > GM_level))) - { // you can look only lower or same level - memcpy (player_name, pl_sd->status.name, 24); - 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 (pl_GM_level > 0) - sprintf (output, - "Name: %s (GM:%d) | Location: %s %d %d", - pl_sd->status.name, pl_GM_level, - pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); - else - sprintf (output, "Name: %s | Location: %s %d %d", - pl_sd->status.name, pl_sd->mapname, - pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, 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 (output, msg_table[30], count); // %d players found. - clif_displaymessage (fd, output); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_whogroup (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char temp0[100]; - char temp1[100]; - char output[200]; - struct map_session_data *pl_sd; - int i, j, count; - int pl_GM_level, GM_level; - char match_text[100]; - char player_name[24]; - struct guild *g; - struct party *p; - - memset (temp0, '\0', sizeof (temp0)); - memset (temp1, '\0', sizeof (temp1)); - memset (output, '\0', sizeof (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); - 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_GM_level = pc_isGM (pl_sd); - if (! - ((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (pl_GM_level > GM_level))) - { // you can look only lower or same level - memcpy (player_name, pl_sd->status.name, 24); - 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); - 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 (output, - "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", - pl_sd->status.name, pl_GM_level, temp0, - temp1); - else - sprintf (output, - "Name: %s | Party: '%s' | Guild: '%s'", - pl_sd->status.name, temp0, temp1); - clif_displaymessage (fd, 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 (output, msg_table[30], count); // %d players found. - clif_displaymessage (fd, output); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_whomap (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char output[200]; - struct map_session_data *pl_sd; - int i, count; - int pl_GM_level, GM_level; - int map_id; - char map_name[100]; - - memset (output, '\0', sizeof (output)); - memset (map_name, '\0', sizeof (map_name)); - - if (!message || !*message) - map_id = sd->bl.m; - else - { - sscanf (message, "%99s", map_name); - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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); - 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_GM_level = pc_isGM (pl_sd); - if (! - ((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (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 (output, - "Name: %s (GM:%d) | Location: %s %d %d", - pl_sd->status.name, pl_GM_level, - pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); - else - sprintf (output, "Name: %s | Location: %s %d %d", - pl_sd->status.name, pl_sd->mapname, - pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, output); - count++; - } - } - } - } - - if (count == 0) - sprintf (output, msg_table[54], map[map_id].name); // No player found in map '%s'. - else if (count == 1) - sprintf (output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. - else - { - sprintf (output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. - } - clif_displaymessage (fd, output); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_whomapgroup (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char temp0[100]; - char temp1[100]; - char output[200]; - struct map_session_data *pl_sd; - int i, count; - int pl_GM_level, GM_level; - int map_id = 0; - char map_name[100]; - struct guild *g; - struct party *p; - - memset (temp0, '\0', sizeof (temp0)); - memset (temp1, '\0', sizeof (temp1)); - memset (output, '\0', sizeof (output)); - memset (map_name, '\0', sizeof (map_name)); - - if (!message || !*message) - map_id = sd->bl.m; - else - { - sscanf (message, "%99s", map_name); - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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); - 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_GM_level = pc_isGM (pl_sd); - if (! - ((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (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 (output, - "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", - pl_sd->status.name, pl_GM_level, temp0, - temp1); - else - sprintf (output, - "Name: %s | Party: '%s' | Guild: '%s'", - pl_sd->status.name, temp0, temp1); - clif_displaymessage (fd, output); - count++; - } - } - } - } - - if (count == 0) - sprintf (output, msg_table[54], map[map_id].name); // No player found in map '%s'. - else if (count == 1) - sprintf (output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. - else - { - sprintf (output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. - } - clif_displaymessage (fd, 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]; - char output[200]; - struct map_session_data *pl_sd; - int i, j, count; - int pl_GM_level, GM_level; - char match_text[100]; - char player_name[24]; - struct guild *g; - struct party *p; - - memset (temp0, '\0', sizeof (temp0)); - memset (temp1, '\0', sizeof (temp1)); - memset (output, '\0', sizeof (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); - 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_GM_level = pc_isGM (pl_sd); - if (pl_GM_level > 0) - { - if (! - ((battle_config.hide_GM_session - || (pl_sd->status.option & OPTION_HIDE)) - && (pl_GM_level > GM_level))) - { // you can look only lower or same level - memcpy (player_name, pl_sd->status.name, 24); - 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 (output, - "Name: %s (GM:%d) | Location: %s %d %d", - pl_sd->status.name, pl_GM_level, - pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, output); - sprintf (output, - " BLvl: %d | Job: %s (Lvl: %d)", - pl_sd->status.base_level, - job_name (pl_sd->status.pc_class), - pl_sd->status.job_level); - clif_displaymessage (fd, 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 (output, " Party: '%s' | Guild: '%s'", - temp0, temp1); - clif_displaymessage (fd, 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 (output, msg_table[152], count); // %d GMs found. - clif_displaymessage (fd, output); - } - - 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->mapname, sd->bl.x, sd->bl.y); - pc_makesavestatus (sd); - chrif_save (sd); - 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; - - m = map_mapname2mapid (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, - "You are not authorised to warp you to your save map."); - 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, - "You are not authorised to warp you from your actual map."); - 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) -{ - char output[200]; - int speed; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message) - { - sprintf (output, - "Please, enter a speed value (usage: @speed <%d-%d>).", - MIN_WALK_SPEED, MAX_WALK_SPEED); - clif_displaymessage (fd, 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 (output, - "Please, enter a valid speed value (usage: @speed <%d-%d>).", - MIN_WALK_SPEED, MAX_WALK_SPEED); - clif_displaymessage (fd, 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", ¶m1, ¶m2, ¶m3) < 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) - { - clif_cart_itemlist (sd); - clif_cart_equiplist (sd); - clif_updatestatus (sd, SP_CARTINFO); - } - sd->status.option = param3; - // fix pecopeco display - if (sd->status.pc_class == 13 || sd->status.pc_class == 21 - || sd->status.pc_class == 4014 || sd->status.pc_class == 4022) - { - if (!pc_isriding (sd)) - { // sd have the new value... - if (sd->status.pc_class == 13) - sd->status.pc_class = sd->view_class = 7; - else if (sd->status.pc_class == 21) - sd->status.pc_class = sd->view_class = 14; - else if (sd->status.pc_class == 4014) - sd->status.pc_class = sd->view_class = 4008; - else if (sd->status.pc_class == 4022) - sd->status.pc_class = sd->view_class = 4015; - } - } - else - { - if (pc_isriding (sd)) - { // sd have the new value... - if (sd->disguise > 0) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) - sd->status.option &= ~0x0020; - } - else - { - if (sd->status.pc_class == 7) - sd->status.pc_class = sd->view_class = 13; - else if (sd->status.pc_class == 14) - sd->status.pc_class = sd->view_class = 21; - else if (sd->status.pc_class == 4008) - sd->status.pc_class = sd->view_class = 4014; - else if (sd->status.pc_class == 4015) - sd->status.pc_class = sd->view_class = 4022; - else - sd->status.option &= ~0x0020; - } - } - } - - clif_changeoption (&sd->bl); - pc_calcstatus (sd, 0); - 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) -{ - if (sd->status.option & OPTION_HIDE) - { - sd->status.option &= ~OPTION_HIDE; - clif_displaymessage (fd, msg_table[10]); // Invisible: Off - } - else - { - sd->status.option |= OPTION_HIDE; - clif_displaymessage (fd, msg_table[11]); // Invisible: On - } - clif_changeoption (&sd->bl); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_die (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - pc_damage (NULL, sd, sd->status.hp + 1); - 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) -{ - char character[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @kill <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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 + 1); - 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) -{ - sd->status.hp = sd->status.max_hp; - sd->status.sp = sd->status.max_sp; - 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_kami (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message) - { - clif_displaymessage (fd, - "Please, enter a message (usage: @kami <message>)."); - return -1; - } - - sscanf (message, "%199[^\n]", output); - intif_GMmessage (output, strlen (output) + 1, 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 - - 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>) - *------------------------------------------ - */ -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; - - 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; - 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) - { - 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); - } - 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; - - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].amount - && sd->status.inventory[i].equip == 0) - 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) -{ - pc_checkitem (sd); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_baselevelup (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int level, i; - - if (!message || !*message || (level = atoi (message)) == 0) - { - clif_displaymessage (fd, - "Please, enter a level adjustement (usage: @blvl <number of levels>)."); - return -1; - } - - if (level > 0) - { - if (sd->status.base_level == battle_config.maximum_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 (level > battle_config.maximum_level || level > (battle_config.maximum_level - sd->status.base_level)) // fix positiv overflow - level = battle_config.maximum_level - sd->status.base_level; - for (i = 1; i <= level; i++) - sd->status.status_point += (sd->status.base_level + i + 14) / 4; - sd->status.base_level += level; - clif_updatestatus (sd, SP_BASELEVEL); - clif_updatestatus (sd, SP_NEXTBASEEXP); - clif_updatestatus (sd, SP_STATUSPOINT); - pc_calcstatus (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 < -battle_config.maximum_level || level < (1 - 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) / 4; - 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_calcstatus (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) -{ - int up_level = 50, level; - - if (!message || !*message || (level = atoi (message)) == 0) - { - clif_displaymessage (fd, - "Please, enter a level adjustement (usage: @jlvl <number of levels>)."); - return -1; - } - - if (sd->status.pc_class == 0 || sd->status.pc_class == 4001) - up_level -= 40; - else if ((sd->status.pc_class > 4007 && sd->status.pc_class < 4024) - || sd->status.pc_class == 23) - up_level += 20; - - 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 (level > up_level || 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); - pc_calcstatus (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 < -up_level || level < (1 - 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 - pc_calcstatus (sd, 0); - clif_displaymessage (fd, msg_table[25]); // Job level lowered. - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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; - - 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_gm (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char password[100]; - - 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; - int i; - - 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); - 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 (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; - int i; - - 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); - 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 (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; - } - } - } - 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) -{ - 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) -{ - 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; - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message - || sscanf (message, "%d %d %d", &hair_style, &hair_color, - &cloth_color) < 1) - { - sprintf (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, 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) - { - //服の色変更 - if (cloth_color != 0 && sd->status.sex == 1 - && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) - { - //服の色未実装職の判定 - 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; - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%d", &cloth_color) < 1) - { - sprintf (output, - "Please, enter a clothes color (usage: @dye/@ccolor <clothes color: %d-%d>).", - MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); - clif_displaymessage (fd, output); - return -1; - } - - if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) - { - if (cloth_color != 0 && sd->status.sex == 1 - && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) - { - clif_displaymessage (fd, msg_table[35]); // You can't use this command with this class. - return -1; - } - else - { - 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; -} - -/*========================================== - * @chardye by [MouseJstr] - *------------------------------------------ - */ -int -atcommand_chardye (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - 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; - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%d", &hair_style) < 1) - { - sprintf (output, - "Please, enter a hair style (usage: @hairstyle/@hstyle <hair ID: %d-%d>).", - MIN_HAIR_STYLE, MAX_HAIR_STYLE); - clif_displaymessage (fd, output); - return -1; - } - - if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) - { - if (hair_style != 0 && sd->status.sex == 1 - && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) - { - 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) -{ - 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; - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%d", &hair_color) < 1) - { - sprintf (output, - "Please, enter a hair color (usage: @haircolor/@hcolor <hair color: %d-%d>).", - MIN_HAIR_COLOR, MAX_HAIR_COLOR); - clif_displaymessage (fd, output); - return -1; - } - - if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) - { - if (hair_color != 0 && sd->status.sex == 1 - && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) - { - 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; -} - -/*========================================== - * @charhaircolor by [MouseJstr] - *------------------------------------------ - */ -int -atcommand_charhaircolor (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - return 0; -} - -/*========================================== - * @go [city_number/city_name]: improved by [yor] to add city names and help - *------------------------------------------ - */ -int atcommand_go (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int i; - int town; - char map_name[100]; - char output[200]; - int m; - - struct - { - char map[16]; - int x, y; - } data[] = - { - { - "prontera.gat", 156, 191}, // 0=Prontera - { - "morocc.gat", 156, 93}, // 1=Morroc - { - "geffen.gat", 119, 59}, // 2=Geffen - { - "payon.gat", 162, 233}, // 3=Payon - { - "alberta.gat", 192, 147}, // 4=Alberta - { - "izlude.gat", 128, 114}, // 5=Izlude - { - "aldebaran.gat", 140, 131}, // 6=Al de Baran - { - "xmas.gat", 147, 134}, // 7=Lutie - { - "comodo.gat", 209, 143}, // 8=Comodo - { - "yuno.gat", 157, 51}, // 9=Yuno - { - "amatsu.gat", 198, 84}, // 10=Amatsu - { - "gonryun.gat", 160, 120}, // 11=Gon Ryun - { - "umbala.gat", 89, 157}, // 12=Umbala - { - "niflheim.gat", 21, 153}, // 13=Niflheim - { - "louyang.gat", 217, 40}, // 14=Lou Yang - { - "new_1-1.gat", 53, 111}, // 15=Start point - { - "sec_pri.gat", 23, 61}, // 16=Prison - }; - - memset (map_name, '\0', sizeof (map_name)); - memset (output, '\0', sizeof (output)); - - // get the number - town = atoi (message); - - // if no value, display all value - if (!message || !*message || sscanf (message, "%99s", 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, - "-3=(Memo point 2) 4=Alberta 11=Gon Ryun"); - clif_displaymessage (fd, - "-2=(Memo point 1) 5=Izlude 12=Umbala"); - clif_displaymessage (fd, - "-1=(Memo point 0) 6=Al de Baran 13=Niflheim"); - clif_displaymessage (fd, - " 0=Prontera 7=Lutie 14=Lou Yang"); - clif_displaymessage (fd, - " 1=Morroc 8=Comodo 15=Start point"); - clif_displaymessage (fd, - " 2=Geffen 9=Yuno 16=Prison"); - clif_displaymessage (fd, " 3=Payon 10=Amatsu"); - return -1; - } - else - { - // get possible name of the city and add .gat if not in the name - map_name[sizeof (map_name) - 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) < 13) // 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; - } - - if (town >= -3 && town <= -1) - { - if (sd->status.memo_point[-town - 1].map[0]) - { - m = map_mapname2mapid (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, - "You are not authorised to warp you to this memo map."); - 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, - "You are not authorised to warp you from your actual map."); - 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 (output, msg_table[164], -town - 1); // Your memo point #%d doesn't exist. - clif_displaymessage (fd, output); - return -1; - } - } - else if (town >= 0 && town < (int) (sizeof (data) / sizeof (data[0]))) - { - m = map_mapname2mapid (data[town].map); - 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 you to this destination map."); - 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, - "You are not authorised to warp you from your actual map."); - return -1; - } - if (pc_setpos (sd, 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_spawn (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char monster[100]; - char output[200]; - int mob_id; - int number = 0; - int x = 0, y = 0; - int count; - int i, j, k; - int mx, my, range; - - memset (monster, '\0', sizeof (monster)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message - || sscanf (message, "%99s %d %d %d", monster, &number, &x, &y) < 1) - { - clif_displaymessage (fd, msg_table[143]); // 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 = mobdb_checkid (atoi (monster)); - - if (mob_id == 0) - { - clif_displaymessage (fd, msg_table[40]); // Invalid monster ID or name. - return -1; - } - - if (mob_id == 1288) - { - clif_displaymessage (fd, msg_table[83]); // Cannot spawn emperium. - return -1; - } - - if (number <= 0) - number = 1; - - // 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) - printf ("%s monster='%s' id=%d count=%d (%d,%d)\n", command, monster, - mob_id, number, x, y); - - count = 0; - range = 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 + (MRAND (range) - (range / 2)); - else - mx = x; - if (y <= 0) - my = sd->bl.y + (MRAND (range) - (range / 2)); - else - my = y; - k = mob_once_spawn ((struct map_session_data *) sd, "this", mx, - my, "", 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 (output, msg_table[240], count); // %d monster(s) summoned! - clif_displaymessage (fd, output); - } - else - { - clif_displaymessage (fd, msg_table[40]); // Invalid monster ID or name. - return -1; - } - - 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[100]; - - memset (map_name, '\0', sizeof (map_name)); - - if (!message || !*message || sscanf (message, "%99s", map_name) < 1) - map_id = sd->bl.m; - else - { - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) - strcat (map_name, ".gat"); - if ((map_id = map_mapname2mapid (map_name)) < 0) - map_id = sd->bl.m; - } - - map_foreachinarea (atkillmonster_sub, map_id, 0, 0, map[map_id].xs, - map[map_id].ys, 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) -{ - atcommand_killmonster_sub (fd, sd, message, 1); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static int atlist_nearby_sub (struct block_list *bl, va_list ap) -{ - char buf[32]; - int fd = va_arg (ap, int); - nullpo_retr (0, bl); - - sprintf (buf, " - \"%s\"", ((struct map_session_data *) bl)->status.name); - clif_displaymessage (fd, buf); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_list_nearby (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - clif_displaymessage (fd, "Nearby players:"); - map_foreachinarea (atlist_nearby_sub, sd->bl.m, sd->bl.x - 1, - sd->bl.y - 1, sd->bl.x + 1, sd->bl.x + 1, BL_PC, fd); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_killmonster2 (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - atcommand_killmonster_sub (fd, sd, message, 0); - - 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; - char output[200]; - - memset (output, '\0', sizeof (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; - *((unsigned long *) (&tmp_item.card[2])) = sd->char_id; - clif_produceeffect (sd, 0, item_id); // 製造エフェクトパケット - clif_misceffect (&sd->bl, 3); // 他人にも成功を通知 - if ((flag = pc_additem (sd, &tmp_item, 1))) - clif_additem (sd, 0, 0, flag); - } - else - { - if (battle_config.error_log) - printf ("@produce NOT WEAPON [%d]\n", item_id); - if (item_id != 0 && itemdb_exists (item_id)) - sprintf (output, msg_table[169], item_id, item_data->name); // This item (%d: '%s') is not an equipment. - else - sprintf (output, "%s", msg_table[170]); // This item is not an equipment. - clif_displaymessage (fd, output); - return -1; - } - - return 0; -} - -/*========================================== - * Sub-function to display actual memo points - *------------------------------------------ - */ -void atcommand_memo_sub (struct map_session_data *sd) -{ - int i; - char output[200]; - - memset (output, '\0', sizeof (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[0]) - sprintf (output, "%d - %s (%d,%d)", i, - sd->status.memo_point[i].map, sd->status.memo_point[i].x, - sd->status.memo_point[i].y); - else - sprintf (output, msg_table[171], i); // %d - void - clif_displaymessage (sd->fd, output); - } - - return; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_memo (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int position = 0; - char output[200]; - - memset (output, '\0', sizeof (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 - && battle_config.any_warp_GM_min_level > pc_isGM (sd)) - { - clif_displaymessage (fd, - "You are not authorised to memo this map."); - return -1; - } - if (sd->status.memo_point[position].map[0]) - { - sprintf (output, msg_table[172], position, 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, output); - } - memcpy (sd->status.memo_point[position].map, map[sd->bl.m].name, - 24); - 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 (output, - "Please, enter a valid position (usage: @memo <memo_position:%d-%d>).", - MIN_PORTAL_MEMO, MAX_PORTAL_MEMO); - clif_displaymessage (fd, 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) -{ - char output[200]; - int y; - - memset (output, '\0', sizeof (output)); - - for (y = 2; y >= -2; y--) - { - sprintf (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), - map_getcell (sd->bl.m, sd->bl.x - 1, sd->bl.y + y), - map_getcell (sd->bl.m, sd->bl.x, sd->bl.y + y), - map_getcell (sd->bl.m, sd->bl.x + 1, sd->bl.y + y), - map_getcell (sd->bl.m, sd->bl.x + 2, sd->bl.y + y)); - clif_displaymessage (fd, output); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_packet (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int x = 0, y = 0; - - if (!message || !*message || sscanf (message, "%d %d", &x, &y) < 2) - { - clif_displaymessage (fd, - "Please, enter a status type/flag (usage: @packet <status type> <flag>)."); - return -1; - } - - clif_status_change (&sd->bl, x, y); - - 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; - - 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; - - 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 - }; - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%d", &value) < 1 - || value == 0) - { - sprintf (output, - "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); - clif_displaymessage (fd, output); - return -1; - } - - index = -1; - for (i = 0; param[i] != NULL; i++) - { - if (strcasecmp (command, param[i]) == 0) - { - index = i; - break; - } - } - if (index < 0 || index > MAX_STATUS_TYPE) - { // normaly impossible... - sprintf (output, - "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); - clif_displaymessage (fd, output); - return -1; - } - - new_value = (int) *status[index] + value; - if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow - new_value = battle_config.max_parameter; - else if (value < 0 && (value < -battle_config.max_parameter || 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); - pc_calcstatus (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_all_stats (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 - }; - - if (!message || !*message || sscanf (message, "%d", &value) < 1 - || value == 0) - value = battle_config.max_parameter; - - count = 0; - for (index = 0; index < (int) (sizeof (status) / sizeof (status[0])); - index++) - { - - new_value = (int) *status[index] + value; - if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow - new_value = battle_config.max_parameter; - else if (value < 0 && (value < -battle_config.max_parameter || 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); - pc_calcstatus (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; - - if (!message || !*message || sscanf (message, "%d", &level) < 1 - || level == 0) - { - clif_displaymessage (fd, - "Please, enter a valid level (usage: @guildlvl <# 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_recall (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @recall <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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 authorised to warp this player from its actual map."); - return -1; - } - pc_setpos (pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); - sprintf (output, msg_table[46], character); // %s recalled! - 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 atcommand_revive (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @revive <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_character_stats (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - 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, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charstats <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.pc_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 Stats All by fritz -int atcommand_character_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; - struct map_session_data *pl_sd; - - memset (output, '\0', sizeof (output)); - memset (gmlevel, '\0', sizeof (gmlevel)); - - count = 0; - 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) > 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.pc_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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_character_option (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - 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 %99[^\n]", &opt1, &opt2, &opt3, - character) < 4 || opt1 < 0 || opt2 < 0 || opt3 < 0) - { - clif_displaymessage (fd, - "Please, enter valid options and a player name (usage: @charoption <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; - pl_sd->status.option = opt3; - // fix pecopeco display - if (pl_sd->status.pc_class == 13 || pl_sd->status.pc_class == 21 - || pl_sd->status.pc_class == 4014 || pl_sd->status.pc_class == 4022) - { - if (!pc_isriding (pl_sd)) - { // pl_sd have the new value... - if (pl_sd->status.pc_class == 13) - pl_sd->status.pc_class = pl_sd->view_class = 7; - else if (pl_sd->status.pc_class == 21) - pl_sd->status.pc_class = pl_sd->view_class = 14; - else if (pl_sd->status.pc_class == 4014) - pl_sd->status.pc_class = pl_sd->view_class = 4008; - else if (pl_sd->status.pc_class == 4022) - pl_sd->status.pc_class = pl_sd->view_class = 4015; - } - } - else - { - if (pc_isriding (pl_sd)) - { // pl_sd have the new value... - if (pl_sd->disguise > 0) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) - pl_sd->status.option &= ~0x0020; - } - else - { - if (pl_sd->status.pc_class == 7) - pl_sd->status.pc_class = pl_sd->view_class = 13; - else if (pl_sd->status.pc_class == 14) - pl_sd->status.pc_class = pl_sd->view_class = 21; - else if (pl_sd->status.pc_class == 4008) - pl_sd->status.pc_class = pl_sd->view_class = 4014; - else if (pl_sd->status.pc_class == 4015) - pl_sd->status.pc_class = pl_sd->view_class = 4022; - else - pl_sd->status.option &= ~0x0020; - } - } - } - clif_changeoption (&pl_sd->bl); - pc_calcstatus (pl_sd, 0); - 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; -} - -/*========================================== - * charchangesex command (usage: charchangesex <player_name>) - *------------------------------------------ - */ -int atcommand_char_change_sex (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charchangesex <name>)."); - return -1; - } - - // check player name - if (strlen (character) < 4) - { - clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } - else if (strlen (character) > 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, character, 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; -} - -/*========================================== - * 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) -{ - char character[100]; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @block <name>)."); - return -1; - } - - // check player name - if (strlen (character) < 4) - { - clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } - else if (strlen (character) > 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, character, 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[100], character[100]; - char *modif_p; - int year, month, day, hour, minute, second, value; - - memset (modif, '\0', sizeof (modif)); - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%s %99[^\n]", modif, character) < 2) - { - clif_displaymessage (fd, - "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish <time> <name>)."); - return -1; - } - - modif[sizeof (modif) - 1] = '\0'; - character[sizeof (character) - 1] = '\0'; - - modif_p = modif; - 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 (character) < 4) - { - clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } - else if (strlen (character) > 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, character, 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) -{ - char character[100]; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charunblock <player_name>)."); - return -1; - } - - // check player name - if (strlen (character) < 4) - { - clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } - else if (strlen (character) > 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, character, 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) -{ - char character[100]; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charunban <player_name>)."); - return -1; - } - - // check player name - if (strlen (character) < 4) - { - clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } - else if (strlen (character) > 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, character, 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_character_save (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char map_name[100]; - char character[100]; - 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, "%99s %d %d %99[^\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: @charsave <map> <x> <y> <charname>)."); - return -1; - } - - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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 (m >= 0 && 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_name, 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_night (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - int i; - - if (night_flag != 1) - { - 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->opt2 |= STATE_BLIND; - clif_changeoption (&pl_sd->bl); - clif_displaymessage (pl_sd->fd, msg_table[59]); // Night has fallen. - } - } - } - 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) -{ - struct map_session_data *pl_sd; - int i; - - if (night_flag != 0) - { - 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->opt2 &= ~STATE_BLIND; - clif_changeoption (&pl_sd->bl); - clif_displaymessage (pl_sd->fd, msg_table[60]); // Day has arrived. - } - } - } - 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; - int i; - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth && i != 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 + 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_doommap (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - int i; - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth && i != 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 + 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)) - { - 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); - 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; - - for (i = 0; i < fd_max; i++) - { - if (session[i]) - atcommand_raise_sub ((struct map_session_data *)session[i]->session_data); - } - 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_sd; - int i; - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth && sd->bl.m == pl_sd->bl.m) - atcommand_raise_sub (pl_sd); - } - clif_displaymessage (fd, msg_table[64]); // Mercy has been granted. - - return 0; -} - -/*========================================== - * atcommand_character_baselevel @charbaselvlで対象キャラのレベルを上げる - *------------------------------------------ -*/ -int atcommand_character_baselevel (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - int level = 0, i; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &level, character) < 2 - || level == 0) - { - clif_displaymessage (fd, - "Please, enter a level adjustement and a player name (usage: @charblvl <#> <nickname>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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.maximum_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 (level > battle_config.maximum_level || level > (battle_config.maximum_level - pl_sd->status.base_level)) // fix positiv overflow - level = - battle_config.maximum_level - - pl_sd->status.base_level; - for (i = 1; i <= level; i++) - pl_sd->status.status_point += - (pl_sd->status.base_level + i + 14) / 4; - pl_sd->status.base_level += level; - clif_updatestatus (pl_sd, SP_BASELEVEL); - clif_updatestatus (pl_sd, SP_NEXTBASEEXP); - clif_updatestatus (pl_sd, SP_STATUSPOINT); - pc_calcstatus (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 < -battle_config.maximum_level || level < (1 - 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) / 4; - 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; - pl_sd->status.base_exp = 0; - clif_updatestatus (pl_sd, SP_BASELEVEL); - clif_updatestatus (pl_sd, SP_NEXTBASEEXP); - clif_updatestatus (pl_sd, SP_BASEEXP); - pc_calcstatus (pl_sd, 0); - clif_displaymessage (fd, msg_table[66]); // Character's base level lowered. - } - // Reset their stat points to prevent extra points from stacking - atcommand_charstreset(fd, sd,"@charstreset", character); - } - 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; //正常終了 -} - -/*========================================== - * atcommand_character_joblevel @charjoblvlで対象キャラのJobレベルを上げる - *------------------------------------------ - */ -int atcommand_character_joblevel (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - int max_level = 50, level = 0; - //転生や養子の場合の元の職業を算出する - struct pc_base_job pl_s_class; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &level, character) < 2 - || level == 0) - { - clif_displaymessage (fd, - "Please, enter a level adjustement and a player name (usage: @charjlvl <#> <nickname>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - pl_s_class = pc_calc_base_job (pl_sd->status.pc_class); - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can change job level only lower or same gm level - if (pl_s_class.job == 0) - max_level -= 40; - if ((pl_s_class.job == 23) || (pl_s_class.upper == 1 && pl_s_class.type == 2)) //スパノビと転生職はJobレベルの最高が70 - max_level += 20; - - 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); - pc_calcstatus (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 - pc_calcstatus (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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_kick (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @kick <charname>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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; - int i; - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth && 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_allskills (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - 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; - - 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) & 0x01) - { - 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_charquestskill (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - struct map_session_data *pl_sd; - int skill_id = 0; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &skill_id, character) < 2 - || skill_id < 0) - { - clif_displaymessage (fd, - "Please, enter a quest skill number and a player name (usage: @charquestskill <#:0+> <char_name>)."); - return -1; - } - - if (skill_id >= 0 && skill_id < MAX_SKILL_DB) - { - if (skill_get_inf2 (skill_id) & 0x01) - { - if ((pl_sd = map_nick2sd (character)) != 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_lostskill (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int skill_id; - - 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) & 0x01) - { - if (pc_checkskill (sd, skill_id) > 0) - { - sd->status.skill[skill_id].lv = 0; - sd->status.skill[skill_id].flags = 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_charlostskill (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - struct map_session_data *pl_sd; - int skill_id = 0; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &skill_id, character) < 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) & 0x01) - { - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_checkskill (pl_sd, skill_id) > 0) - { - pl_sd->status.skill[skill_id].lv = 0; - pl_sd->status.skill[skill_id].flags = 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; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_party (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char party[100]; - - memset (party, '\0', sizeof (party)); - - if (!message || !*message || sscanf (message, "%99[^\n]", party) < 1) - { - clif_displaymessage (fd, - "Please, enter a party name (usage: @party <party_name>)."); - return -1; - } - - party_create (sd, party); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_guild (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char guild[100]; - int prev; - - memset (guild, '\0', sizeof (guild)); - - if (!message || !*message || sscanf (message, "%99[^\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) -{ - 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) -{ - 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; - int i; - - 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 (sd->status.account_id != pl_sd->status.account_id) - clif_GM_kick (sd, pl_sd, 0); - } - } - clif_GM_kick (sd, sd, 0); - - 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]; - char output[200]; - int i, match; - struct item_data *item; - - memset (item_name, '\0', sizeof (item_name)); - memset (output, '\0', sizeof (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 (output, msg_table[77], item_name); // The reference result of '%s' (name: id): - clif_displaymessage (fd, output); - match = 0; - for (i = 0; i < 20000; i++) - { - if ((item = itemdb_exists (i)) != NULL - && strstr (item->jname, item_name) != NULL) - { - match++; - sprintf (output, msg_table[78], item->jname, item->nameid); // %s: %d - clif_displaymessage (fd, output); - } - } - sprintf (output, msg_table[79], match); // It is %d affair above. - clif_displaymessage (fd, output); - - return 0; -} - -/*========================================== - * Character Skill Reset - *------------------------------------------ - */ -int atcommand_charskreset (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charskreset <charname>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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 (output, msg_table[206], character); // '%s' skill 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; -} - -/*========================================== - * Character Stat Reset - *------------------------------------------ - */ -int atcommand_charstreset (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charstreset <charname>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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 (output, msg_table[207], character); // '%s' 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; -} - -/*========================================== - * Character Reset - *------------------------------------------ - */ -int atcommand_charreset (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charreset <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); - pc_setglobalreg (pl_sd, "MAGIC_FLAGS", 0); // [Fate] Reset magic quest variables - pc_setglobalreg (pl_sd, "MAGIC_EXP", 0); // [Fate] Reset magic experience - 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; -} - -/*========================================== - * Character Wipe - *------------------------------------------ - */ -int atcommand_char_wipe (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charwipe <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 - int i; - - // Reset base level - pl_sd->status.base_level = 1; - pl_sd->status.base_exp = 0; - clif_updatestatus (pl_sd, SP_BASELEVEL); - clif_updatestatus (pl_sd, SP_NEXTBASEEXP); - clif_updatestatus (pl_sd, SP_BASEEXP); - - // Reset job level - pl_sd->status.job_level = 1; - pl_sd->status.job_exp = 0; - clif_updatestatus (pl_sd, SP_JOBLEVEL); - clif_updatestatus (pl_sd, SP_NEXTJOBEXP); - clif_updatestatus (pl_sd, SP_JOBEXP); - - // Zeny to 50 - pl_sd->status.zeny = 50; - clif_updatestatus (pl_sd, SP_ZENY); - - // Clear inventory - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].amount) - { - if (sd->status.inventory[i].equip) - pc_unequipitem (pl_sd, i, 0); - pc_delitem (pl_sd, i, sd->status.inventory[i].amount, 0); - } - } - - // Give knife and shirt - struct item item; - item.nameid = 1201; // knife - item.identify = 1; - item.broken = 0; - pc_additem (pl_sd, &item, 1); - item.nameid = 1202; // shirt - pc_additem (pl_sd, &item, 1); - - // Reset stats and skills - pc_calcstatus (pl_sd, 0); - pc_resetstate (pl_sd); - pc_resetskill (pl_sd); - pc_setglobalreg (pl_sd, "MAGIC_FLAGS", 0); // [Fate] Reset magic quest variables - pc_setglobalreg (pl_sd, "MAGIC_EXP", 0); // [Fate] Reset magic experience - - sprintf (output, "%s: wiped.", 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; -} - -/*========================================== - * Character Model by chbrules - *------------------------------------------ - */ -int atcommand_charmodel (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 character[100]; - char output[200]; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message - || sscanf (message, "%d %d %d %99[^\n]", &hair_style, &hair_color, - &cloth_color, character) < 4 || hair_style < 0 - || hair_color < 0 || cloth_color < 0) - { - sprintf (output, - "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, output); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != 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) - { - - if (cloth_color != 0 && - pl_sd->status.sex == 1 && - (pl_sd->status.pc_class == 12 || pl_sd->status.pc_class == 17)) - { - 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 atcommand_charskpoint (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - int new_skill_point; - int point = 0; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &point, character) < 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 (character)) != 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 atcommand_charstpoint (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - int new_status_point; - int point = 0; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &point, character) < 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 (character)) != 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; -} - -/*========================================== - * Character Zeny Point (Rewritten by [Yor]) - *------------------------------------------ - */ -int atcommand_charzeny (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - char character[100]; - int zeny = 0, new_zeny; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &zeny, character) < 2 || zeny == 0) - { - clif_displaymessage (fd, - "Please, enter a number and a player name (usage: @charzeny <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; -} - -/*========================================== - * 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; - int i; - int count; - char output[200]; - - memset (output, '\0', sizeof (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; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && 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 - pc_setpos (pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); - } - } - - clif_displaymessage (fd, msg_table[92]); // All characters recalled! - if (count) - { - sprintf (output, - "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", - count); - clif_displaymessage (fd, 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; - int i; - char guild_name[100]; - char output[200]; - struct guild *g; - int count; - - memset (guild_name, '\0', sizeof (guild_name)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\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; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && 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->mapname, sd->bl.x, sd->bl.y, 2); - } - } - sprintf (output, msg_table[93], g->name); // All online characters of the %s guild are near you. - clif_displaymessage (fd, output); - if (count) - { - sprintf (output, - "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", - count); - clif_displaymessage (fd, 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; - char party_name[100]; - char output[200]; - struct party *p; - int count; - - memset (party_name, '\0', sizeof (party_name)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\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; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && 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->mapname, sd->bl.x, sd->bl.y, 2); - } - } - sprintf (output, msg_table[95], p->name); // All online characters of the %s party are near you. - clif_displaymessage (fd, output); - if (count) - { - sprintf (output, - "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", - count); - clif_displaymessage (fd, 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) -{ - 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) -{ - mob_reload (); - 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) -{ - skill_reload (); - clif_displaymessage (fd, msg_table[99]); // Skill database reloaded. - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_reloadscript (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - do_init_npc (); - do_init_script (); - - 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) -{ - 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; - struct npc_data *nd = NULL; - struct chat_data *cd = NULL; - char output[200], map_name[100]; - char direction[12]; - int m_id, i, chat_num, list = 0; - - memset (output, '\0', sizeof (output)); - memset (map_name, '\0', sizeof (map_name)); - memset (direction, '\0', sizeof (direction)); - - sscanf (message, "%d %99[^\n]", &list, map_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 (map_name[0] == '\0') - strcpy (map_name, sd->mapname); - if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) - strcat (map_name, ".gat"); - - if ((m_id = map_mapname2mapid (map_name)) < 0) - { - clif_displaymessage (fd, msg_table[1]); // Map not found. - return -1; - } - - clif_displaymessage (fd, "------ Map Info ------"); - sprintf (output, "Map Name: %s", map_name); - clif_displaymessage (fd, output); - sprintf (output, "Players In Map: %d", map[m_id].users); - clif_displaymessage (fd, output); - sprintf (output, "NPCs In Map: %d", map[m_id].npc_num); - clif_displaymessage (fd, output); - chat_num = 0; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && (cd = (struct chat_data *) map_id2bl (pl_sd->chatID))) - { - chat_num++; - } - } - sprintf (output, "Chats In Map: %d", chat_num); - clif_displaymessage (fd, output); - clif_displaymessage (fd, "------ Map Flags ------"); - sprintf (output, "Player vs Player: %s | No Guild: %s | No Party: %s", - (map[m_id].flag.pvp) ? "True" : "False", - (map[m_id].flag.pvp_noguild) ? "True" : "False", - (map[m_id].flag.pvp_noparty) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "Guild vs Guild: %s | No Party: %s", - (map[m_id].flag.gvg) ? "True" : "False", - (map[m_id].flag.gvg_noparty) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Dead Branch: %s", - (map[m_id].flag.nobranch) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Memo: %s", - (map[m_id].flag.nomemo) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Penalty: %s", - (map[m_id].flag.nopenalty) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Return: %s", - (map[m_id].flag.noreturn) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Save: %s", - (map[m_id].flag.nosave) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Teleport: %s", - (map[m_id].flag.noteleport) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Monster Teleport: %s", - (map[m_id].flag.monster_noteleport) ? "True" : "False"); - clif_displaymessage (fd, output); - sprintf (output, "No Zeny Penalty: %s", - (map[m_id].flag.nozenypenalty) ? "True" : "False"); - clif_displaymessage (fd, 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 < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && strcmp (pl_sd->mapname, map_name) == 0) - { - sprintf (output, - "Player '%s' (session #%d) | Location: %d,%d", - pl_sd->status.name, i, pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, 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 (output, - "NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d", - ++i, nd->name, direction, nd->npc_class, nd->bl.x, - nd->bl.y); - clif_displaymessage (fd, output); - } - break; - case 3: - clif_displaymessage (fd, "----- Chats in Map -----"); - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth - && (cd = (struct chat_data *) map_id2bl (pl_sd->chatID)) - && strcmp (pl_sd->mapname, map_name) == 0 - && cd->usersd[0] == pl_sd) - { - sprintf (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, output); - sprintf (output, - " Users: %d/%d | Password: %s | Public: %s", - cd->users, cd->limit, cd->pass, - (cd->pub) ? "Yes" : "No"); - clif_displaymessage (fd, 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) -{ - if (sd->disguise > 0) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (fd, msg_table[212]); // Cannot mount a Peco while in disguise. - return -1; - } - - if (!pc_isriding (sd)) - { // if actually no peco - if (sd->status.pc_class == 7 || sd->status.pc_class == 14 - || sd->status.pc_class == 4008 || sd->status.pc_class == 4015) - { - if (sd->status.pc_class == 7) - sd->status.pc_class = sd->view_class = 13; - else if (sd->status.pc_class == 14) - sd->status.pc_class = sd->view_class = 21; - else if (sd->status.pc_class == 4008) - sd->status.pc_class = sd->view_class = 4014; - else if (sd->status.pc_class == 4015) - sd->status.pc_class = sd->view_class = 4022; - 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 - { - if (sd->status.pc_class == 13) - sd->status.pc_class = sd->view_class = 7; - else if (sd->status.pc_class == 21) - sd->status.pc_class = sd->view_class = 14; - else if (sd->status.pc_class == 4014) - sd->status.pc_class = sd->view_class = 4008; - else if (sd->status.pc_class == 4022) - sd->status.pc_class = sd->view_class = 4015; - 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) -{ - char character[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charmountpeco <char_name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pl_sd->disguise > 0) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (fd, msg_table[215]); // This player cannot mount a Peco while in disguise. - return -1; - } - - if (!pc_isriding (pl_sd)) - { // if actually no peco - if (pl_sd->status.pc_class == 7 || pl_sd->status.pc_class == 14 - || pl_sd->status.pc_class == 4008 || pl_sd->status.pc_class == 4015) - { - if (pl_sd->status.pc_class == 7) - pl_sd->status.pc_class = pl_sd->view_class = 13; - else if (pl_sd->status.pc_class == 14) - pl_sd->status.pc_class = pl_sd->view_class = 21; - else if (pl_sd->status.pc_class == 4008) - pl_sd->status.pc_class = pl_sd->view_class = 4014; - else if (pl_sd->status.pc_class == 4015) - pl_sd->status.pc_class = pl_sd->view_class = 4022; - pc_setoption (pl_sd, pl_sd->status.option | 0x0020); - clif_displaymessage (fd, msg_table[216]); // Now, this player mounts a peco. - } - else - { - clif_displaymessage (fd, msg_table[217]); // This player can not mount a peco with his/her job. - return -1; - } - } - else - { - if (pl_sd->status.pc_class == 13) - pl_sd->status.pc_class = pl_sd->view_class = 7; - else if (pl_sd->status.pc_class == 21) - pl_sd->status.pc_class = pl_sd->view_class = 14; - else if (pl_sd->status.pc_class == 4014) - pl_sd->status.pc_class = pl_sd->view_class = 4008; - else if (pl_sd->status.pc_class == 4022) - pl_sd->status.pc_class = pl_sd->view_class = 4015; - pc_setoption (pl_sd, pl_sd->status.option & ~0x0020); - clif_displaymessage (fd, msg_table[218]); // Now, this player has not more 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[100]; - char output[200]; - struct guild *g; - - memset (guild_name, '\0', sizeof (guild_name)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\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 (output, msg_table[103], g->name); // No longer spying on the %s guild. - clif_displaymessage (fd, output); - } - else - { - sd->guildspy = g->guild_id; - sprintf (output, msg_table[104], g->name); // Spying on the %s guild. - clif_displaymessage (fd, 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[100]; - char output[200]; - struct party *p; - - memset (party_name, '\0', sizeof (party_name)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\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 (output, msg_table[105], p->name); // No longer spying on the %s party. - clif_displaymessage (fd, output); - } - else - { - sd->partyspy = p->party_id; - sprintf (output, msg_table[106], p->name); // Spying on the %s party. - clif_displaymessage (fd, 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_enablenpc (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char NPCname[100]; - - memset (NPCname, '\0', sizeof (NPCname)); - - if (!message || !*message || sscanf (message, "%99[^\n]", NPCname) < 1) - { - clif_displaymessage (fd, - "Please, enter a NPC name (usage: @npcon <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_disablenpc (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char NPCname[100]; - - memset (NPCname, '\0', sizeof (NPCname)); - - if (!message || !*message || sscanf (message, "%99[^\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; -} - -/*========================================== - * 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]; - - memset (temp, '\0', sizeof (temp)); - - time (&time_server); // get time in seconds since 1/1/1970 - datetime = gmtime (&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 ((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 ((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 ((timer_data->tick - gettick ()) / 1000)); // Game time: The game is actualy in daylight for %s. - clif_displaymessage (fd, temp); - if (timer_data->tick > timer_data2->tick) - sprintf (temp, msg_table[237], txt_time ((timer_data->interval - abs (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 (abs (timer_data->tick - timer_data2->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 ((timer_data->tick - gettick ()) / 1000)); // Game time: The game is actualy in night for %s. - clif_displaymessage (fd, temp); - if (timer_data->tick > timer_data2->tick) - sprintf (temp, msg_table[239], txt_time ((timer_data->interval - abs (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 (abs (timer_data->tick - timer_data2->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 character[100]; - char item_name[100]; - int i, number = 0, item_id, item_position, count; - char output[200]; - struct item_data *item_data; - - memset (character, '\0', sizeof (character)); - memset (item_name, '\0', sizeof (item_name)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message - || sscanf (message, "%s %d %99[^\n]", item_name, &number, - character) < 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 (character)) != 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++) - { - pc_delitem (pl_sd, item_position, 1, 0); - count++; - item_position = pc_search_inventory (pl_sd, item_id); // for next loop - } - sprintf (output, msg_table[113], count); // %d item(s) removed by a GM. - clif_displaymessage (pl_sd->fd, output); - if (number == count) - sprintf (output, msg_table[114], count); // %d item(s) removed from the player. - else - sprintf (output, msg_table[115], count, count, number); // %d item(s) removed. Player had only %d on %d items. - clif_displaymessage (fd, 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) -{ - char character[100]; - struct map_session_data *pl_sd; - int x, y; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @jail <char_name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can jail only lower or same GM - switch (MRAND (2)) - { - case 0: - x = 24; - y = 75; - break; - default: - x = 49; - y = 75; - break; - } - if (pc_setpos (pl_sd, "sec_pri.gat", x, y, 3) == 0) - { - pc_setsavepoint (pl_sd, "sec_pri.gat", 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) -{ - char character[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @unjail/@discharge <char_name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can jail only lower or same GM - if (pl_sd->bl.m != map_mapname2mapid ("sec_pri.gat")) - { - clif_displaymessage (fd, msg_table[119]); // This player is not in jails. - return -1; - } - else if (pc_setpos (pl_sd, "prontera.gat", 156, 191, 3) == 0) - { - pc_setsavepoint (pl_sd, "prontera.gat", 156, 191); // 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 mob_id; - - if (!message || !*message) - { - clif_displaymessage (fd, - "Please, enter a Monster/NPC name/id (usage: @disguise <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 ((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 <= 834) || // NPC - (mob_id > 1000 && mob_id < 1521)) - { // monsters - if (pc_isriding (sd)) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (fd, msg_table[227]); // Cannot wear disguise while riding a Peco. - return -1; - } - sd->disguiseflag = 1; // set to override items with disguise script [Valaris] - sd->disguise = mob_id; - pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); - clif_displaymessage (fd, msg_table[122]); // Disguise applied. - } - else - { - clif_displaymessage (fd, msg_table[123]); // Monster/NPC name/id hasn't been found. - return -1; - } - - return 0; -} - -/*========================================== - * @undisguise by [Yor] - *------------------------------------------ - */ -int atcommand_undisguise (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - if (sd->disguise) - { - clif_clearchar (&sd->bl, 9); - sd->disguise = 0; - pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); - clif_displaymessage (fd, msg_table[124]); // Undisguise applied. - } - else - { - clif_displaymessage (fd, msg_table[125]); // You're not disguised. - return -1; - } - - return 0; -} - -/*========================================== - * @broadcast by [Valaris] - *------------------------------------------ - */ -int atcommand_broadcast (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message) - { - clif_displaymessage (fd, - "Please, enter a message (usage: @broadcast <message>)."); - return -1; - } - - snprintf (output, 199, "%s : %s", sd->status.name, message); - intif_GMmessage (output, strlen (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) -{ - char output[200]; - - memset (output, '\0', sizeof (output)); - - if (!message || !*message) - { - clif_displaymessage (fd, - "Please, enter a message (usage: @localbroadcast <message>)."); - return -1; - } - - snprintf (output, 199, "%s : %s", sd->status.name, message); - - clif_GMmessage (&sd->bl, output, strlen (output) + 1, 1); // 1: ALL_SAMEMAP - - return 0; -} - -/*========================================== - * @ignorelist by [Yor] - *------------------------------------------ - */ -int atcommand_ignorelist (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char output[200]; - int count; - int i; - - memset (output, '\0', sizeof (output)); - - count = 0; - for (i = 0; i < (int) (sizeof (sd->ignore) / sizeof (sd->ignore[0])); i++) - if (sd->ignore[i].name[0]) - count++; - - if (sd->ignoreAll == 0) - if (count == 0) - clif_displaymessage (fd, msg_table[126]); // You accept any wisp (no wisper is refused). - else - { - sprintf (output, msg_table[127], count); // You accept any wisp, except thoses from %d player(s): - clif_displaymessage (fd, output); - } - else if (count == 0) - clif_displaymessage (fd, msg_table[128]); // You refuse all wisps (no specifical wisper is refused). - else - { - sprintf (output, msg_table[129], count); // You refuse all wisps, AND refuse wisps from %d player(s): - clif_displaymessage (fd, output); - } - - if (count > 0) - for (i = 0; i < (int) (sizeof (sd->ignore) / sizeof (sd->ignore[0])); - i++) - if (sd->ignore[i].name[0]) - clif_displaymessage (fd, sd->ignore[i].name); - - return 0; -} - -/*========================================== - * @charignorelist <player_name> by [Yor] - *------------------------------------------ - */ -int atcommand_charignorelist (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - struct map_session_data *pl_sd; - char output[200]; - int count; - int i; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charignorelist <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - count = 0; - for (i = 0; - i < (int) (sizeof (pl_sd->ignore) / sizeof (pl_sd->ignore[0])); - i++) - if (pl_sd->ignore[i].name[0]) - count++; - - if (pl_sd->ignoreAll == 0) - if (count == 0) - { - sprintf (output, msg_table[130], pl_sd->status.name); // '%s' accept any wisp (no wisper is refused). - clif_displaymessage (fd, output); - } - else - { - sprintf (output, msg_table[131], pl_sd->status.name, count); // '%s' accept any wisp, except thoses from %d player(s): - clif_displaymessage (fd, output); - } - else if (count == 0) - { - sprintf (output, msg_table[132], pl_sd->status.name); // '%s' refuse all wisps (no specifical wisper is refused). - clif_displaymessage (fd, output); - } - else - { - sprintf (output, msg_table[133], pl_sd->status.name, count); // '%s' refuse all wisps, AND refuse wisps from %d player(s): - clif_displaymessage (fd, output); - } - - if (count > 0) - for (i = 0; - i < - (int) (sizeof (pl_sd->ignore) / sizeof (pl_sd->ignore[0])); - i++) - if (pl_sd->ignore[i].name[0]) - clif_displaymessage (fd, pl_sd->ignore[i].name); - - } - else - { - clif_displaymessage (fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * @inall <player_name> by [Yor] - *------------------------------------------ - */ -int atcommand_inall (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @inall <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can change wisp option only to lower or same level - if (pl_sd->ignoreAll == 0) - { - sprintf (output, msg_table[134], pl_sd->status.name); // '%s' already accepts all wispers. - clif_displaymessage (fd, output); - return -1; - } - else - { - pl_sd->ignoreAll = 0; - sprintf (output, msg_table[135], pl_sd->status.name); // '%s' now accepts all wispers. - clif_displaymessage (fd, output); - // message to player - clif_displaymessage (pl_sd->fd, msg_table[136]); // A GM has authorised all wispers for you. - WFIFOW (pl_sd->fd, 0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail - WFIFOB (pl_sd->fd, 2) = 1; - WFIFOB (pl_sd->fd, 3) = 0; // success - WFIFOSET (pl_sd->fd, 4); // packet_len_table[0x0d2] - } - } - 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; -} - -/*========================================== - * @exall <player_name> by [Yor] - *------------------------------------------ - */ -int atcommand_exall (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char output[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @exall <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can change wisp option only to lower or same level - if (pl_sd->ignoreAll == 1) - { - sprintf (output, msg_table[137], pl_sd->status.name); // '%s' already blocks all wispers. - clif_displaymessage (fd, output); - return -1; - } - else - { - pl_sd->ignoreAll = 1; - sprintf (output, msg_table[138], pl_sd->status.name); // '%s' blocks now all wispers. - clif_displaymessage (fd, output); - // message to player - clif_displaymessage (pl_sd->fd, msg_table[139]); // A GM has blocked all wispers for you. - WFIFOW (pl_sd->fd, 0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail - WFIFOB (pl_sd->fd, 2) = 0; - WFIFOB (pl_sd->fd, 3) = 0; // success - WFIFOSET (pl_sd->fd, 4); // packet_len_table[0x0d2] - } - } - 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; -} - -/*========================================== - * @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 character[100]; - char mob_name[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - memset (mob_name, '\0', sizeof (mob_name)); - - if (!message || !*message - || sscanf (message, "%s %99[^\n]", mob_name, character) < 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 = mobdb_searchname (mob_name)) == 0) // check name first (to avoid possible name begining by a number) - mob_id = atoi (mob_name); - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can disguise only lower or same level - 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 <= 834) || // NPC - (mob_id > 1000 && mob_id < 1521)) - { // monsters - if (pc_isriding (pl_sd)) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (fd, msg_table[228]); // Character cannot wear disguise while riding a Peco. - return -1; - } - pl_sd->disguiseflag = 1; // set to override items with disguise script [Valaris] - pl_sd->disguise = mob_id; - pc_setpos (pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, - 3); - clif_displaymessage (fd, msg_table[140]); // Character's disguise applied. - } - else - { - clif_displaymessage (fd, msg_table[123]); // Monster/NPC name/id hasn't been 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; -} - -/*========================================== - * @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) -{ - char character[100]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charundisguise <char name>)."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (pc_isGM (sd) >= pc_isGM (pl_sd)) - { // you can undisguise only lower or same level - if (pl_sd->disguise) - { - clif_clearchar (&pl_sd->bl, 9); - pl_sd->disguise = 0; - pc_setpos (pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, - 3); - 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]; - - 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 (strcasecmp (new_email, "a@a.com") == 0) - { - clif_displaymessage (fd, msg_table[146]); // New email must be a real e-mail. - return -1; - } - else if (strcasecmp (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; - int type = 0, flag = 0, i; - - 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 - { - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth) - { - clif_specialeffect (&pl_sd->bl, type, flag); - clif_displaymessage (pl_sd->fd, msg_table[229]); // Your effect has changed. - } - } - } - - return 0; -} - -/*========================================== - * @charitemlist <character>: Displays the list of a player's items. - *------------------------------------------ - */ -int -atcommand_character_item_list (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[100], output[200], equipstr[100], outputtmp[200]; - - 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, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charitemlist <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; -} - -/*========================================== - * @charstoragelist <character>: Displays the items list of a player's storage. - *------------------------------------------ - */ -int -atcommand_character_storage_list (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[100], output[200], outputtmp[200]; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - memset (outputtmp, '\0', sizeof (outputtmp)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charitemlist <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 -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; -} - -/*========================================== - * @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) -{ - struct map_session_data *pl_sd; - struct item_data *item_data, *item_temp; - int i, j, count, counter, counter2; - char character[100], output[200], outputtmp[200]; - - memset (character, '\0', sizeof (character)); - memset (output, '\0', sizeof (output)); - memset (outputtmp, '\0', sizeof (outputtmp)); - - if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) - { - clif_displaymessage (fd, - "Please, enter a player name (usage: @charitemlist <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_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 (output, - "------ Cart items list of '%s' ------", - pl_sd->status.name); - clif_displaymessage (fd, output); - } - if (pl_sd->status.cart[i].refine) - sprintf (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 (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, output); - memset (output, '\0', sizeof (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 (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 cart 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, 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) -{ - 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) -{ - 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; - - 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) -{ - 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) -{ - 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) -{ - char character[100]; - int x = 0, y = 0; - struct npc_data *nd = 0; - - if (sd == NULL) - return -1; - - if (!message || !*message) - return -1; - - memset (character, '\0', sizeof character); - - if (sscanf (message, "%d %d %99[^\n]", &x, &y, character) < 3) - return -1; - - nd = npc_name2id (character); - if (nd == NULL) - return -1; - - npc_enable (character, 0); - nd->bl.x = x; - nd->bl.y = y; - npc_enable (character, 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]; - char map[30], output[200]; - int x, y, ret; - - if (!message || !*message) - return -1; - - if (sscanf (message, "%99s %d %d[^\n]", map, &x, &y) < 3) - return -1; - - sprintf (w1, "%s,%d,%d", sd->mapname, sd->bl.x, sd->bl.y); - sprintf (w3, "%s%d%d%d%d", map, sd->bl.x, sd->bl.y, x, y); - sprintf (w4, "1,1,%s.gat,%d,%d", map, x, y); - - ret = npc_parse_warp (w1, "warp", w3, w4); - - sprintf (output, "New warp NPC => %s", w3); - - clif_displaymessage (fd, 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) -{ -#if 0 - struct map_session_data *pl_sd = NULL; - - if (!message || !*message) - return -1; - if ((pl_sd = map_nick2sd ((char *) message)) != NULL) - pc_follow (sd, pl_sd->bl.id); - else - return 1; -#endif - - /* - * Command disabled - it's incompatible with the TMW - * client. - */ - clif_displaymessage (fd, "@follow command not available"); - - return 0; - -} - -/*========================================== - * @chareffect by [MouseJstr] - * - * Create a effect localized on another character - *------------------------------------------ - */ -int -atcommand_chareffect (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; - - if (!message || !*message - || sscanf (message, "%d %s", &type, target) != 2) - { - clif_displaymessage (fd, "usage: @chareffect <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; -} - -/*========================================== - * @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; - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].amount) - { - if (sd->status.inventory[i].equip != 0) - pc_unequipitem (sd, i, 0); - 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; - - 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, 0); - 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 (sd->state.storage_flag != 1) - { //Open storage. - switch (storage_storageopen (sd)) - { - case 2: //Try again - clif_displaymessage (fd, "run this command again.."); - return 0; - case 1: //Failure - clif_displaymessage (fd, - "You can't open the storage currently."); - return 1; - } - } - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].amount) - { - if (sd->status.inventory[i].equip != 0) - pc_unequipitem (sd, i, 0); - 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; - - 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, 0); - 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 = 0, idx = 0; - if (!message || !*message) - return -1; - skillen = strlen (message); - while (skill_names[idx].id != 0) - { - if ((strncasecmp (skill_names[idx].name, message, skillen) == 0) || - (strncasecmp (skill_names[idx].desc, message, skillen) == 0)) - { - char output[255]; - sprintf (output, "skill %d: %s", skill_names[idx].id, - skill_names[idx].desc); - clif_displaymessage (fd, output); - } - idx++; - } - 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; - int inf; - char target[255]; - - if (!message || !*message) - return -1; - if (sscanf (message, "%d %d %s", &skillnum, &skilllv, target) != 3) - { - clif_displaymessage (fd, - "Usage: @useskill <skillnum> <skillv> <target>"); - return -1; - } - if ((pl_sd = map_nick2sd (target)) == NULL) - { - return -1; - } - - inf = skill_get_inf (skillnum); - - if ((inf == 2) || (inf == 1)) - 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; -} - -/*========================================== - * It is made to rain. - *------------------------------------------ - */ -int -atcommand_rain (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int effno = 0; - effno = 161; - nullpo_retr (-1, sd); - if (effno < 0 || map[sd->bl.m].flag.rain) - return -1; - - map[sd->bl.m].flag.rain = 1; - clif_specialeffect (&sd->bl, effno, 2); - return 0; -} - -/*========================================== - * It is made to snow. - *------------------------------------------ - */ -int -atcommand_snow (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int effno = 0; - effno = 162; - nullpo_retr (-1, sd); - if (effno < 0 || map[sd->bl.m].flag.snow) - return -1; - - map[sd->bl.m].flag.snow = 1; - clif_specialeffect (&sd->bl, effno, 2); - 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) -{ - int effno = 0; - effno = 163; - nullpo_retr (-1, sd); - if (effno < 0 || map[sd->bl.m].flag.sakura) - return -1; - - map[sd->bl.m].flag.sakura = 1; - clif_specialeffect (&sd->bl, effno, 2); - return 0; -} - -/*========================================== - * Fog hangs over. - *------------------------------------------ - */ -int -atcommand_fog (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int effno = 0; - effno = 233; - nullpo_retr (-1, sd); - if (effno < 0 || map[sd->bl.m].flag.fog) - return -1; - - map[sd->bl.m].flag.fog = 1; - clif_specialeffect (&sd->bl, effno, 2); - - return 0; -} - -/*========================================== - * Fallen leaves fall. - *------------------------------------------ - */ -int -atcommand_leaves (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - int effno = 0; - effno = 333; - nullpo_retr (-1, sd); - if (effno < 0 || map[sd->bl.m].flag.leaves) - return -1; - - map[sd->bl.m].flag.leaves = 1; - clif_specialeffect (&sd->bl, effno, 2); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int atcommand_summon (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char name[100]; - int mob_id = 0; - int x = 0; - int y = 0; - int id = 0; - struct mob_data *md; - unsigned int tick = gettick (); - - nullpo_retr (-1, sd); - - if (!message || !*message) - return -1; - if (sscanf (message, "%99s", name) < 1) - return -1; - - if ((mob_id = atoi (name)) == 0) - mob_id = mobdb_searchname (name); - if (mob_id == 0) - return -1; - - x = sd->bl.x + (MRAND (10) - 5); - y = sd->bl.y + (MRAND (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->state.special_mob_ai = 1; - md->mode = mob_db[md->mob_class].mode | 0x04; - md->deletetimer = add_timer (tick + 60000, mob_timer_delete, id, 0); - clif_misceffect (&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]; - - 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].type != AtCommand_None; i++) - if (strcasecmp (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[100]; - struct map_session_data *pl_sd; - - if (!message || !*message - || sscanf (message, "%d %s", &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; - - if (!message || !*message) - return -1; - if ((pl_sd = map_nick2sd ((char *) message)) != NULL) - { - trade_traderequest (sd, pl_sd->bl.id); - return 0; - } - return -1; -} - -/*=========================== - * @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; - if (!message || !*message) - return -1; - - if ((pl_sd = map_nick2sd ((char *) message)) != NULL) - { - if (pl_sd->sc_data[SC_NOCHAT].timer != -1) - { - skill_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; -} - -/* Magic atcommands by Fate */ - -static int magic_base = TMW_MAGIC; -#define magic_skills_nr 6 -static char *magic_skill_names[magic_skills_nr] = - { "magic", "life", "war", "transmute", "nature", "astral" }; - -int -atcommand_magic_info (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char buf[200]; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message || sscanf (message, "%99s", character) < 1) - { - clif_displaymessage (fd, "Usage: @magicinfo <char_name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - int i; - - sprintf (buf, "`%s' has the following magic skills:", character); - clif_displaymessage (fd, buf); - - for (i = 0; i < magic_skills_nr; i++) - { - sprintf (buf, "%d in %s", pl_sd->status.skill[i + magic_base].lv, - magic_skill_names[i]); - if (pl_sd->status.skill[i + magic_base].id == i + magic_base) - clif_displaymessage (fd, buf); - } - - return 0; - } - else - clif_displaymessage (fd, "Character not found."); - - return -1; -} - -static void set_skill (struct map_session_data *sd, int i, int level) -{ - sd->status.skill[i].id = level ? i : 0; - sd->status.skill[i].lv = level; -} - -int -atcommand_set_magic (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - char magic_type[20]; - int skill_index = -1; // 0: all - int value; - struct map_session_data *pl_sd; - - memset (character, '\0', sizeof (character)); - - if (!message || !*message - || sscanf (message, "%19s %i %99s", magic_type, &value, - character) < 1) - { - clif_displaymessage (fd, - "Usage: @setmagic <school> <value> <char-name>, where <school> is either `magic', one of the school names, or `all'."); - return -1; - } - - if (!strcasecmp ("all", magic_type)) - skill_index = 0; - else - { - int i; - for (i = 0; i < magic_skills_nr; i++) - { - if (!strcasecmp (magic_skill_names[i], magic_type)) - { - skill_index = i + magic_base; - break; - } - } - } - - if (skill_index == -1) - { - clif_displaymessage (fd, - "Incorrect school of magic. Use `magic', `nature', `life', `war', `transmute', `ether', or `all'."); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - int i; - if (skill_index == 0) - for (i = 0; i < magic_skills_nr; i++) - set_skill (pl_sd, i + magic_base, value); - else - set_skill (pl_sd, skill_index, value); - - clif_skillinfoblock (pl_sd); - return 0; - } - else - clif_displaymessage (fd, "Character not found."); - - return -1; -} - -int -atcommand_log (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - return 0; // only used for (implicit) logging -} - -int -atcommand_tee (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char *data = (char *)malloc (strlen (message) + 28); - strcpy (data, sd->status.name); - strcat (data, " : "); - strcat (data, message); - clif_message (&sd->bl, data); - return 0; -} - -int -atcommand_invisible (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - pc_invisibility (sd, 1); - return 0; -} - -int -atcommand_visible (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - pc_invisibility (sd, 0); - return 0; -} - -int atcommand_jump_iterate (const int fd, struct map_session_data *sd, - const char *command, const char *message, - struct map_session_data *(*get_start) (void), - struct map_session_data *(*get_next) (struct - map_session_data - * current)) -{ - char output[200]; - struct map_session_data *pl_sd; - - memset (output, '\0', sizeof (output)); - - pl_sd = (struct map_session_data *) map_id2bl (sd->followtarget); - - if (pl_sd) - pl_sd = get_next (pl_sd); - - if (!pl_sd) - pl_sd = get_start (); - - if (pl_sd == sd) - { - pl_sd = get_next (pl_sd); - if (!pl_sd) - pl_sd = get_start (); - } - - 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, - "You are not authorised to warp you to the map of this player."); - 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, - "You are not authorised to warp you from your actual map."); - return -1; - } - pc_setpos (sd, map[pl_sd->bl.m].name, pl_sd->bl.x, pl_sd->bl.y, 3); - sprintf (output, msg_table[4], pl_sd->status.name); // Jump to %s - clif_displaymessage (fd, output); - - sd->followtarget = pl_sd->bl.id; - - return 0; -} - -int -atcommand_iterate_forward_over_players (const int fd, - struct map_session_data *sd, - const char *command, - const char *message) -{ - return atcommand_jump_iterate (fd, sd, command, message, - map_get_first_session, - map_get_next_session); -} - -int -atcommand_iterate_backwards_over_players (const int fd, - struct map_session_data *sd, - const char *command, - const char *message) -{ - return atcommand_jump_iterate (fd, sd, command, message, - map_get_last_session, - map_get_prev_session); -} - -int atcommand_wgm (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - if (tmw_CheckChatSpam(sd, message)) - return 0; - tmw_GmHackMsg ("%s: %s", sd->status.name, message); - if (!pc_isGM (sd)) - clif_displaymessage (fd, "Message sent."); - - return 0; -} - - -int atcommand_skillpool_info (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - struct map_session_data *pl_sd; - - if (!message || !*message || sscanf (message, "%99s", character) < 1) - { - clif_displaymessage (fd, "Usage: @sp-info <char_name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - char buf[200]; - int pool_skills[MAX_SKILL_POOL]; - int pool_skills_nr = skill_pool (pl_sd, pool_skills); - int i; - - sprintf (buf, "Active skills %d out of %d for %s:", pool_skills_nr, - skill_pool_max (pl_sd), character); - clif_displaymessage (fd, buf); - for (i = 0; i < pool_skills_nr; ++i) - { - sprintf (buf, " - %s [%d]: power %d", skill_name (pool_skills[i]), - pool_skills[i], skill_power (pl_sd, pool_skills[i])); - clif_displaymessage (fd, buf); - } - - sprintf (buf, "Learned skills out of %d for %s:", - skill_pool_skills_size, character); - clif_displaymessage (fd, buf); - - for (i = 0; i < skill_pool_skills_size; ++i) - { - char *name = skill_name (skill_pool_skills[i]); - int lvl = pl_sd->status.skill[skill_pool_skills[i]].lv; - - if (lvl) - { - sprintf (buf, " - %s [%d]: lvl %d", name, - skill_pool_skills[i], lvl); - clif_displaymessage (fd, buf); - } - } - - } - else - clif_displaymessage (fd, "Character not found."); - - return 0; -} - -int atcommand_skillpool_focus (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - int skill; - struct map_session_data *pl_sd; - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &skill, character) < 1) - { - clif_displaymessage (fd, "Usage: @sp-focus <skill-nr> <char_name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (skill_pool_activate (pl_sd, skill)) - clif_displaymessage (fd, "Activation failed."); - else - clif_displaymessage (fd, "Activation successful."); - } - else - clif_displaymessage (fd, "Character not found."); - - return 0; -} - -int atcommand_skillpool_unfocus (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - int skill; - struct map_session_data *pl_sd; - - if (!message || !*message - || sscanf (message, "%d %99[^\n]", &skill, character) < 1) - { - clif_displaymessage (fd, "Usage: @sp-unfocus <skill-nr> <char_name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - if (skill_pool_deactivate (pl_sd, skill)) - clif_displaymessage (fd, "Deactivation failed."); - else - clif_displaymessage (fd, "Deactivation successful."); - } - else - clif_displaymessage (fd, "Character not found."); - - return 0; -} - -int atcommand_skill_learn (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - char character[100]; - int skill, level; - struct map_session_data *pl_sd; - - if (!message || !*message - || sscanf (message, "%d %d %99[^\n]", &skill, &level, character) < 1) - { - clif_displaymessage (fd, - "Usage: @skill-learn <skill-nr> <level> <char_name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) != NULL) - { - set_skill (pl_sd, skill, level); - clif_skillinfoblock (pl_sd); - } - else - clif_displaymessage (fd, "Character not found."); - - return 0; -} - -int atcommand_ipcheck (const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - struct sockaddr_in sai; - char output[200]; - char character[25]; - int i; - socklen_t sa_len = sizeof (struct sockaddr); - unsigned long ip; - - memset(character, '\0', sizeof(character)); - - if (sscanf (message, "%24[^\n]", character) < 1) - { - clif_displaymessage (fd, "Usage: @ipcheck <char name>"); - return -1; - } - - if ((pl_sd = map_nick2sd (character)) == NULL) - { - clif_displaymessage (fd, "Character not found."); - return -1; - } - - if (getpeername (pl_sd->fd, (struct sockaddr *)&sai, &sa_len)) - { - clif_displaymessage (fd, - "Guru Meditation Error: getpeername() failed"); - return -1; - } - - ip = sai.sin_addr.s_addr; - - // We now have the IP address of a character. - // Loop over all logged in sessions looking for matches. - - 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 (getpeername (pl_sd->fd, (struct sockaddr *)&sai, &sa_len)) - continue; - - // Is checking GM levels really needed here? - if (ip == sai.sin_addr.s_addr) - { - snprintf (output, sizeof(output), - "Name: %s | Location: %s %d %d", - pl_sd->status.name, pl_sd->mapname, - pl_sd->bl.x, pl_sd->bl.y); - clif_displaymessage (fd, output); - } - } - } - - clif_displaymessage (fd, "End of list"); - return 0; -} - -int atcommand_doomspot(const int fd, struct map_session_data *sd, - const char *command, const char *message) -{ - struct map_session_data *pl_sd; - int i; - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth && i != fd && sd->bl.m == pl_sd->bl.m - && sd->bl.x == pl_sd->bl.x && sd->bl.y == pl_sd->bl.y - && 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 + 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; -} diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp new file mode 100644 index 0000000..8f62ca2 --- /dev/null +++ b/src/map/atcommand.cpp @@ -0,0 +1,8728 @@ +// $Id: atcommand.c 148 2004-09-30 14:05:37Z MouseJstr $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> + +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/nullpo.hpp" +#include "../common/mt_rand.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "clif.hpp" +#include "chrif.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "party.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "trade.hpp" + +#include "../common/core.hpp" +#include "tmw.hpp" + +#define STATE_BLIND 0x10 + +static char command_symbol = '@'; // first char of the commands (by [Yor]) + +static char msg_table[1000][1024]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) + +#define ATCOMMAND_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) +ATCOMMAND_FUNC (setup); +ATCOMMAND_FUNC (broadcast); +ATCOMMAND_FUNC (localbroadcast); +ATCOMMAND_FUNC (charwarp); +ATCOMMAND_FUNC (warp); +ATCOMMAND_FUNC (where); +ATCOMMAND_FUNC (goto); +ATCOMMAND_FUNC (jump); +ATCOMMAND_FUNC (who); +ATCOMMAND_FUNC (whogroup); +ATCOMMAND_FUNC (whomap); +ATCOMMAND_FUNC (whomapgroup); +ATCOMMAND_FUNC (whogm); // by Yor +ATCOMMAND_FUNC (save); +ATCOMMAND_FUNC (load); +ATCOMMAND_FUNC (speed); +ATCOMMAND_FUNC (storage); +ATCOMMAND_FUNC (guildstorage); +ATCOMMAND_FUNC (option); +ATCOMMAND_FUNC (hide); +ATCOMMAND_FUNC (die); +ATCOMMAND_FUNC (kill); +ATCOMMAND_FUNC (alive); +ATCOMMAND_FUNC (kami); +ATCOMMAND_FUNC (heal); +ATCOMMAND_FUNC (item); +ATCOMMAND_FUNC (itemreset); +ATCOMMAND_FUNC (itemcheck); +ATCOMMAND_FUNC (baselevelup); +ATCOMMAND_FUNC (joblevelup); +ATCOMMAND_FUNC (help); +ATCOMMAND_FUNC (gm); +ATCOMMAND_FUNC (pvpoff); +ATCOMMAND_FUNC (pvpon); +ATCOMMAND_FUNC (gvgoff); +ATCOMMAND_FUNC (gvgon); +ATCOMMAND_FUNC (model); +ATCOMMAND_FUNC (go); +ATCOMMAND_FUNC (spawn); +ATCOMMAND_FUNC (killmonster); +ATCOMMAND_FUNC (killmonster2); +ATCOMMAND_FUNC (refine); +ATCOMMAND_FUNC (produce); +ATCOMMAND_FUNC (memo); +ATCOMMAND_FUNC (gat); +ATCOMMAND_FUNC (packet); +ATCOMMAND_FUNC (statuspoint); +ATCOMMAND_FUNC (skillpoint); +ATCOMMAND_FUNC (zeny); +ATCOMMAND_FUNC (param); +ATCOMMAND_FUNC (guildlevelup); +ATCOMMAND_FUNC (recall); +ATCOMMAND_FUNC (recallall); +ATCOMMAND_FUNC (revive); +ATCOMMAND_FUNC (character_stats); +ATCOMMAND_FUNC (character_stats_all); +ATCOMMAND_FUNC (character_option); +ATCOMMAND_FUNC (character_save); +ATCOMMAND_FUNC (night); +ATCOMMAND_FUNC (day); +ATCOMMAND_FUNC (doom); +ATCOMMAND_FUNC (doommap); +ATCOMMAND_FUNC (raise); +ATCOMMAND_FUNC (raisemap); +ATCOMMAND_FUNC (character_baselevel); +ATCOMMAND_FUNC (character_joblevel); +ATCOMMAND_FUNC (kick); +ATCOMMAND_FUNC (kickall); +ATCOMMAND_FUNC (allskills); +ATCOMMAND_FUNC (questskill); +ATCOMMAND_FUNC (charquestskill); +ATCOMMAND_FUNC (lostskill); +ATCOMMAND_FUNC (charlostskill); +ATCOMMAND_FUNC (party); +ATCOMMAND_FUNC (guild); +ATCOMMAND_FUNC (charskreset); +ATCOMMAND_FUNC (charstreset); +ATCOMMAND_FUNC (charreset); +ATCOMMAND_FUNC (charstpoint); +ATCOMMAND_FUNC (charmodel); +ATCOMMAND_FUNC (charskpoint); +ATCOMMAND_FUNC (charzeny); +ATCOMMAND_FUNC (agitstart); +ATCOMMAND_FUNC (agitend); +ATCOMMAND_FUNC (reloaditemdb); +ATCOMMAND_FUNC (reloadmobdb); +ATCOMMAND_FUNC (reloadskilldb); +ATCOMMAND_FUNC (reloadscript); +ATCOMMAND_FUNC (reloadgmdb); // by Yor +ATCOMMAND_FUNC (mapexit); +ATCOMMAND_FUNC (idsearch); +ATCOMMAND_FUNC (mapinfo); +ATCOMMAND_FUNC (dye); //** by fritz +ATCOMMAND_FUNC (hair_style); //** by fritz +ATCOMMAND_FUNC (hair_color); //** by fritz +ATCOMMAND_FUNC (all_stats); //** by fritz +ATCOMMAND_FUNC (char_change_sex); // by Yor +ATCOMMAND_FUNC (char_block); // by Yor +ATCOMMAND_FUNC (char_ban); // by Yor +ATCOMMAND_FUNC (char_unblock); // by Yor +ATCOMMAND_FUNC (char_unban); // by Yor +ATCOMMAND_FUNC (mount_peco); // by Valaris +ATCOMMAND_FUNC (char_mount_peco); // by Yor +ATCOMMAND_FUNC (guildspy); // [Syrus22] +ATCOMMAND_FUNC (partyspy); // [Syrus22] +ATCOMMAND_FUNC (guildrecall); // by Yor +ATCOMMAND_FUNC (partyrecall); // by Yor +ATCOMMAND_FUNC (enablenpc); +ATCOMMAND_FUNC (disablenpc); +ATCOMMAND_FUNC (servertime); // by Yor +ATCOMMAND_FUNC (chardelitem); // by Yor +ATCOMMAND_FUNC (jail); // by Yor +ATCOMMAND_FUNC (unjail); // by Yor +ATCOMMAND_FUNC (disguise); // [Valaris] +ATCOMMAND_FUNC (undisguise); // by Yor +ATCOMMAND_FUNC (ignorelist); // by Yor +ATCOMMAND_FUNC (charignorelist); // by Yor +ATCOMMAND_FUNC (inall); // by Yor +ATCOMMAND_FUNC (exall); // by Yor +ATCOMMAND_FUNC (chardisguise); // Kalaspuff +ATCOMMAND_FUNC (charundisguise); // Kalaspuff +ATCOMMAND_FUNC (email); // by Yor +ATCOMMAND_FUNC (effect); //by Apple +ATCOMMAND_FUNC (character_item_list); // by Yor +ATCOMMAND_FUNC (character_storage_list); // by Yor +ATCOMMAND_FUNC (character_cart_list); // by Yor +ATCOMMAND_FUNC (addwarp); // by MouseJstr +ATCOMMAND_FUNC (follow); // by MouseJstr +ATCOMMAND_FUNC (skillon); // by MouseJstr +ATCOMMAND_FUNC (skilloff); // by MouseJstr +ATCOMMAND_FUNC (killer); // by MouseJstr +ATCOMMAND_FUNC (npcmove); // by MouseJstr +ATCOMMAND_FUNC (killable); // by MouseJstr +ATCOMMAND_FUNC (charkillable); // by MouseJstr +ATCOMMAND_FUNC (chareffect); // by MouseJstr +ATCOMMAND_FUNC (chardye); // by MouseJstr +ATCOMMAND_FUNC (charhairstyle); // by MouseJstr +ATCOMMAND_FUNC (charhaircolor); // by MouseJstr +ATCOMMAND_FUNC (dropall); // by MouseJstr +ATCOMMAND_FUNC (chardropall); // by MouseJstr +ATCOMMAND_FUNC (storeall); // by MouseJstr +ATCOMMAND_FUNC (charstoreall); // by MouseJstr +ATCOMMAND_FUNC (skillid); // by MouseJstr +ATCOMMAND_FUNC (useskill); // by MouseJstr +ATCOMMAND_FUNC (summon); +ATCOMMAND_FUNC (rain); +ATCOMMAND_FUNC (snow); +ATCOMMAND_FUNC (sakura); +ATCOMMAND_FUNC (fog); +ATCOMMAND_FUNC (leaves); +ATCOMMAND_FUNC (adjgmlvl); // by MouseJstr +ATCOMMAND_FUNC (adjcmdlvl); // by MouseJstr +ATCOMMAND_FUNC (trade); // by MouseJstr +ATCOMMAND_FUNC (unmute); // [Valaris] +ATCOMMAND_FUNC (char_wipe); // [Fate] +ATCOMMAND_FUNC (set_magic); // [Fate] +ATCOMMAND_FUNC (magic_info); // [Fate] +ATCOMMAND_FUNC (log); // [Fate] +ATCOMMAND_FUNC (tee); // [Fate] +ATCOMMAND_FUNC (invisible); // [Fate] +ATCOMMAND_FUNC (visible); // [Fate] +ATCOMMAND_FUNC (list_nearby); // [Fate] +ATCOMMAND_FUNC (iterate_forward_over_players); // [Fate] +ATCOMMAND_FUNC (iterate_backwards_over_players); // [Fate] +ATCOMMAND_FUNC (skillpool_info); // [Fate] +ATCOMMAND_FUNC (skillpool_focus); // [Fate] +ATCOMMAND_FUNC (skillpool_unfocus); // [Fate] +ATCOMMAND_FUNC (skill_learn); // [Fate] +ATCOMMAND_FUNC (wgm); +ATCOMMAND_FUNC (ipcheck); +ATCOMMAND_FUNC (doomspot); + +/*========================================== + *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_Setup, "@setup", 40, atcommand_setup}, + {AtCommand_CharWarp, "@charwarp", 60, atcommand_charwarp}, + {AtCommand_Warp, "@warp", 40, atcommand_warp}, + {AtCommand_Where, "@where", 1, atcommand_where}, + {AtCommand_Goto, "@goto", 20, atcommand_goto}, + {AtCommand_Jump, "@jump", 40, atcommand_jump}, + {AtCommand_Who, "@who", 20, atcommand_who}, + {AtCommand_WhoGroup, "@whogroup", 20, atcommand_whogroup}, + {AtCommand_WhoMap, "@whomap", 20, atcommand_whomap}, + {AtCommand_WhoMapGroup, "@whomapgroup", 20, atcommand_whomapgroup}, + {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_Die, "@die", 1, atcommand_die}, + {AtCommand_Kill, "@kill", 60, atcommand_kill}, + {AtCommand_Alive, "@alive", 60, atcommand_alive}, + {AtCommand_Kami, "@kami", 40, atcommand_kami}, + {AtCommand_Heal, "@heal", 40, atcommand_heal}, + {AtCommand_Item, "@item", 60, atcommand_item}, + {AtCommand_ItemReset, "@itemreset", 40, atcommand_itemreset}, + {AtCommand_ItemCheck, "@itemcheck", 60, atcommand_itemcheck}, + {AtCommand_BaseLevelUp, "@blvl", 60, atcommand_baselevelup}, + {AtCommand_JobLevelUp, "@jlvl", 60, atcommand_joblevelup}, + {AtCommand_Help, "@help", 20, atcommand_help}, + {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, "@spawn", 50, atcommand_spawn}, + {AtCommand_KillMonster, "@killmonster", 60, atcommand_killmonster}, + {AtCommand_KillMonster2, "@killmonster2", 40, atcommand_killmonster2}, + {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_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, "@guildlvl", 60, atcommand_guildlevelup}, + {AtCommand_Recall, "@recall", 60, atcommand_recall}, // + /recall + {AtCommand_Revive, "@revive", 60, atcommand_revive}, + {AtCommand_CharacterStats, "@charstats", 40, atcommand_character_stats}, + {AtCommand_CharacterStatsAll, "@charstatsall", 40, + atcommand_character_stats_all}, + {AtCommand_CharacterOption, "@charoption", 60, + atcommand_character_option}, + {AtCommand_CharacterSave, "@charsave", 60, atcommand_character_save}, + {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_CharacterBaseLevel, "@charbaselvl", 60, + atcommand_character_baselevel}, + {AtCommand_CharacterJobLevel, "@charjlvl", 60, + atcommand_character_joblevel}, + {AtCommand_Kick, "@kick", 20, atcommand_kick}, // + right click menu for GM "(name) force to quit" + {AtCommand_KickAll, "@kickall", 99, atcommand_kickall}, + {AtCommand_AllSkills, "@allskills", 60, atcommand_allskills}, + {AtCommand_QuestSkill, "@questskill", 40, atcommand_questskill}, + {AtCommand_CharQuestSkill, "@charquestskill", 60, + atcommand_charquestskill}, + {AtCommand_LostSkill, "@lostskill", 40, atcommand_lostskill}, + {AtCommand_CharLostSkill, "@charlostskill", 60, atcommand_charlostskill}, + {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_warp}, // /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_CharSkReset, "@charskreset", 60, atcommand_charskreset}, + {AtCommand_CharStReset, "@charstreset", 60, atcommand_charstreset}, + {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_CharReset, "@charreset", 60, atcommand_charreset}, + {AtCommand_CharModel, "@charmodel", 50, atcommand_charmodel}, + {AtCommand_CharSKPoint, "@charskpoint", 60, atcommand_charskpoint}, + {AtCommand_CharSTPoint, "@charstpoint", 60, atcommand_charstpoint}, + {AtCommand_CharZeny, "@charzeny", 60, atcommand_charzeny}, + {AtCommand_MapInfo, "@mapinfo", 99, atcommand_mapinfo}, + {AtCommand_Dye, "@dye", 40, atcommand_dye}, // by fritz + {AtCommand_Dye, "@ccolor", 40, atcommand_dye}, // by fritz + {AtCommand_HairStyle, "@hairstyle", 40, atcommand_hair_style}, // by fritz + {AtCommand_HairColor, "@haircolor", 40, atcommand_hair_color}, // by fritz + {AtCommand_AllStats, "@allstats", 60, atcommand_all_stats}, // by fritz + {AtCommand_CharChangeSex, "@charchangesex", 60, atcommand_char_change_sex}, // by Yor + {AtCommand_CharBlock, "@block", 60, atcommand_char_block}, // by Yor + {AtCommand_CharUnBlock, "@unblock", 60, atcommand_char_unblock}, // by Yor + {AtCommand_CharBan, "@ban", 60, atcommand_char_ban}, // by Yor + {AtCommand_CharUnBan, "@unban", 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_GuildRecall, "@guildrecall", 60, atcommand_guildrecall}, // by Yor + {AtCommand_PartyRecall, "@partyrecall", 60, atcommand_partyrecall}, // by Yor + {AtCommand_Enablenpc, "@enablenpc", 80, atcommand_enablenpc}, // [] + {AtCommand_Disablenpc, "@disablenpc", 80, atcommand_disablenpc}, // [] + {AtCommand_ServerTime, "@servertime", 0, atcommand_servertime}, // by Yor + {AtCommand_CharDelItem, "@chardelitem", 60, atcommand_chardelitem}, // by Yor + {AtCommand_ListNearby, "@listnearby", 40, atcommand_list_nearby}, // by Yor + {AtCommand_Jail, "@jail", 60, atcommand_jail}, // by Yor + {AtCommand_UnJail, "@unjail", 60, atcommand_unjail}, // by Yor + {AtCommand_Disguise, "@disguise", 20, atcommand_disguise}, // [Valaris] + {AtCommand_UnDisguise, "@undisguise", 20, atcommand_undisguise}, // by Yor + {AtCommand_IgnoreList, "@ignorelist", 0, atcommand_ignorelist}, // by Yor + {AtCommand_CharIgnoreList, "@charignorelist", 20, atcommand_charignorelist}, // by Yor + {AtCommand_IgnoreList, "@inall", 20, atcommand_inall}, // by Yor + {AtCommand_ExAll, "@exall", 20, atcommand_exall}, // by Yor + {AtCommand_CharDisguise, "@chardisguise", 60, atcommand_chardisguise}, // Kalaspuff + {AtCommand_CharUnDisguise, "@charundisguise", 60, atcommand_charundisguise}, // Kalaspuff + {AtCommand_EMail, "@email", 0, atcommand_email}, // by Yor + {AtCommand_Effect, "@effect", 40, atcommand_effect}, // by Apple + {AtCommand_Char_Item_List, "@charitemlist", 40, atcommand_character_item_list}, // by Yor + {AtCommand_Char_Storage_List, "@charstoragelist", 40, atcommand_character_storage_list}, // by Yor + {AtCommand_Char_Cart_List, "@charcartlist", 40, atcommand_character_cart_list}, // by Yor + {AtCommand_Follow, "@follow", 10, atcommand_follow}, // by MouseJstr + {AtCommand_AddWarp, "@addwarp", 20, atcommand_addwarp}, // by MouseJstr + {AtCommand_SkillOn, "@skillon", 20, atcommand_skillon}, // by MouseJstr + {AtCommand_SkillOff, "@skilloff", 20, 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_Chareffect, "@chareffect", 40, atcommand_chareffect}, // MouseJstr + //{ AtCommand_Chardye, "@chardye", 40, atcommand_chardye }, // MouseJstr + //{ AtCommand_Charhairstyle, "@charhairstyle", 40, atcommand_charhairstyle }, // MouseJstr + //{ AtCommand_Charhaircolor, "@charhaircolor", 40, atcommand_charhaircolor }, // 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_Fog, "@fog", 99, atcommand_fog}, + {AtCommand_Leaves, "@leaves", 99, atcommand_leaves}, + //{ AtCommand_Shuffle, "@shuffle", 99, atcommand_shuffle }, + //{ AtCommand_Maintenance, "@maintenance", 99, atcommand_maintenance }, + //{ AtCommand_Misceffect, "@misceffect", 60, atcommand_misceffect }, + {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_UnMute, "@unmute", 60, atcommand_unmute}, // [Valaris] + {AtCommand_UnMute, "@charwipe", 60, atcommand_char_wipe}, // [Fate] + {AtCommand_SetMagic, "@setmagic", 99, atcommand_set_magic}, // [Fate] + {AtCommand_MagicInfo, "@magicinfo", 60, atcommand_magic_info}, // [Fate] + {AtCommand_Log, "@log", 60, atcommand_log}, // [Fate] + {AtCommand_Log, "@l", 60, atcommand_log}, // [Fate] + {AtCommand_Tee, "@tee", 60, atcommand_tee}, // [Fate] + {AtCommand_Tee, "@t", 60, atcommand_tee}, // [Fate] + {AtCommand_Invisible, "@invisible", 60, atcommand_invisible}, // [Fate] + {AtCommand_Visible, "@visible", 60, atcommand_visible}, // [Fate] + {AtCommand_IterateForward, "@hugo", 60, atcommand_iterate_forward_over_players}, // [Fate] + {AtCommand_IterateBackward, "@linus", 60, atcommand_iterate_backwards_over_players}, // [Fate] + {AtCommand_IterateBackward, "@sp-info", 40, atcommand_skillpool_info}, // [Fate] + {AtCommand_IterateBackward, "@sp-focus", 80, atcommand_skillpool_focus}, // [Fate] + {AtCommand_IterateBackward, "@sp-unfocus", 80, atcommand_skillpool_unfocus}, // [Fate] + {AtCommand_IterateBackward, "@skill-learn", 80, atcommand_skill_learn}, // [Fate] + {AtCommand_Wgm, "@wgm", 0, atcommand_wgm}, + {AtCommand_IpCheck, "@ipcheck", 60, atcommand_ipcheck}, + {AtCommand_DoomSpot, "@doomspot", 60, atcommand_doomspot}, + +// add new commands before this line + {AtCommand_Unknown, NULL, 1, NULL} +}; + +/*==================================================== + * This function return the name of the job (by [Yor]) + *---------------------------------------------------- + */ +const char *job_name (int pc_class) +{ + switch (pc_class) + { + case 0: + return "Novice"; + case 1: + return "Swordsman"; + case 2: + return "Mage"; + case 3: + return "Archer"; + case 4: + return "Acolyte"; + case 5: + return "Merchant"; + case 6: + return "Thief"; + case 7: + return "Knight"; + case 8: + return "Priest"; + case 9: + return "Wizard"; + case 10: + return "Blacksmith"; + case 11: + return "Hunter"; + case 12: + return "Assassin"; + case 13: + return "Knight 2"; + case 14: + return "Crusader"; + case 15: + return "Monk"; + case 16: + return "Sage"; + case 17: + return "Rogue"; + case 18: + return "Alchemist"; + case 19: + return "Bard"; + case 20: + return "Dancer"; + case 21: + return "Crusader 2"; + case 22: + return "Wedding"; + case 23: + return "Super Novice"; + case 4001: + return "Novice High"; + case 4002: + return "Swordsman High"; + case 4003: + return "Mage High"; + case 4004: + return "Archer High"; + case 4005: + return "Acolyte High"; + case 4006: + return "Merchant High"; + case 4007: + return "Thief High"; + case 4008: + return "Lord Knight"; + case 4009: + return "High Priest"; + case 4010: + return "High Wizard"; + case 4011: + return "Whitesmith"; + case 4012: + return "Sniper"; + case 4013: + return "Assassin Cross"; + case 4014: + return "Peko Knight"; + case 4015: + return "Paladin"; + case 4016: + return "Champion"; + case 4017: + return "Professor"; + case 4018: + return "Stalker"; + case 4019: + return "Creator"; + case 4020: + return "Clown"; + case 4021: + return "Gypsy"; + case 4022: + return "Peko Paladin"; + case 4023: + return "Baby Novice"; + case 4024: + return "Baby Swordsman"; + case 4025: + return "Baby Mage"; + case 4026: + return "Baby Archer"; + case 4027: + return "Baby Acolyte"; + case 4028: + return "Baby Merchant"; + case 4029: + return "Baby Thief"; + case 4030: + return "Baby Knight"; + case 4031: + return "Baby Priest"; + case 4032: + return "Baby Wizard"; + case 4033: + return "Baby Blacksmith"; + case 4034: + return "Baby Hunter"; + case 4035: + return "Baby Assassin"; + case 4036: + return "Baby Peco Knight"; + case 4037: + return "Baby Crusader"; + case 4038: + return "Baby Monk"; + case 4039: + return "Baby Sage"; + case 4040: + return "Baby Rogue"; + case 4041: + return "Baby Alchemist"; + case 4042: + return "Baby Bard"; + case 4043: + return "Baby Dancer"; + case 4044: + return "Baby Peco Crusader"; + case 4045: + return "Super Baby"; + } + return "Unknown Job"; +} + +//----------------------------------------------------------- +// Return the message string of the specified number by [Yor] +//----------------------------------------------------------- +char *msg_txt (int msg_number) +{ + if (msg_number >= 0 + && msg_number < (int) (sizeof (msg_table) / sizeof (msg_table[0])) + && msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0') + return msg_table[msg_number]; + + return "??"; +} + +//------------------------------------------------------------ +// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] +//------------------------------------------------------------ +int e_mail_check (unsigned char *email) +{ + char ch; + unsigned 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 +} + +/*======================================== + * At-command logging + */ +void log_atcommand (struct map_session_data *sd, const char *fmt, ...) +{ + char message[512]; + va_list ap; + + va_start (ap, fmt); + vsnprintf (message, 511, fmt, ap); + va_end (ap); + + gm_log ("%s(%d,%d) %s(%d) : %s", map[sd->bl.m].name, sd->bl.x, + sd->bl.y, sd->status.name, sd->status.account_id, message); +} + +char *gm_logfile_name = NULL; +/*========================================== + * Log a timestamped line to GM log file + *------------------------------------------ + */ +void gm_log (const char *fmt, ...) +{ + static int last_logfile_nr = 0; + static FILE *gm_logfile = NULL; + time_t time_v; + struct tm ctime; + int month, year, logfile_nr; + char message[512]; + va_list ap; + + if (!gm_logfile_name) + return; + + va_start (ap, fmt); + vsnprintf (message, 511, fmt, ap); + va_end (ap); + + time (&time_v); + gmtime_r (&time_v, &ctime); + + year = ctime.tm_year + 1900; + month = ctime.tm_mon + 1; + logfile_nr = (year * 12) + month; + + if (logfile_nr != last_logfile_nr) + { + char *fullname = (char *)malloc (strlen (gm_logfile_name) + 10); + sprintf (fullname, "%s.%04d-%02d", gm_logfile_name, year, month); + + if (gm_logfile) + fclose_ (gm_logfile); + + gm_logfile = fopen_ (fullname, "a"); + free (fullname); + + if (!gm_logfile) + { + perror ("GM log file"); + gm_logfile_name = NULL; + } + last_logfile_nr = logfile_nr; + } + + fprintf (gm_logfile, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n", + year, month, ctime.tm_mday, ctime.tm_hour, + ctime.tm_min, ctime.tm_sec, message); + + fflush (gm_logfile); +} + +/*========================================== + *is_atcommand @コマンドに存在するかどうか確認する + *------------------------------------------ + */ +AtCommandType +is_atcommand (const int fd, struct map_session_data *sd, const char *message, + int gmlvl) +{ + AtCommandInfo info; + AtCommandType type; + + nullpo_retr (AtCommand_None, sd); + + if (!message || !*message) + return AtCommand_None; + + memset (&info, 0, sizeof (info)); + + type = atcommand (gmlvl > 0 ? gmlvl : pc_isGM (sd), message, &info); + if (type != AtCommand_None) + { + char command[100]; + char output[200]; + const char *str = message; + const char *p = message; + memset (command, '\0', sizeof (command)); + memset (output, '\0', sizeof (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 (output, msg_table[153], command); // %s is Unknown Command. + clif_displaymessage (fd, output); + } + else + { + if (info.proc (fd, sd, command, p) != 0) + { + // Command can not be executed + sprintf (output, msg_table[154], command); // %s failed. + clif_displaymessage (fd, output); + } + else + { + if (get_atcommand_level (type) != 0) // Don't log level 0 commands + log_atcommand (sd, "%s %s", command, p); + } + } + + return info.type; + } + + return AtCommand_None; +} + +/*========================================== + * + *------------------------------------------ + */ +AtCommandType atcommand (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) + { + fprintf (stderr, "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 (strcasecmp (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; + } + memcpy (info, &atcommand_info[i], sizeof atcommand_info[i]); + } + else + { + return AtCommand_None; + } + + return info->type; +} + +/*========================================== + * + *------------------------------------------ + */ +static int atkillmonster_sub (struct block_list *bl, va_list ap) +{ + int flag = va_arg (ap, int); + + nullpo_retr (0, bl); + + if (flag) + mob_damage (NULL, (struct mob_data *) bl, + ((struct mob_data *) bl)->hp, 2); + else + mob_delete ((struct mob_data *) bl); + + return 0; +} + +/*========================================== + * Read Message Data + *------------------------------------------ + */ +int msg_config_read (const char *cfgName) +{ + int msg_number; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + if ((fp = fopen_ (cfgName, "r")) == NULL) + { + printf ("Messages file not found: %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 (strcasecmp (w1, "import") == 0) + { + msg_config_read (w2); + } + else + { + msg_number = atoi (w1); + if (msg_number >= 0 + && msg_number < + (int) (sizeof (msg_table) / sizeof (msg_table[0]))) + strcpy (msg_table[msg_number], w2); + // printf("message #%d: '%s'.\n", msg_number, msg_table[msg_number]); + } + } + } + fclose_ (fp); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static AtCommandInfo *get_atcommandinfo_byname (const char *name) +{ + int i; + + for (i = 0; atcommand_info[i].type != AtCommand_Unknown; i++) + if (strcasecmp (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) + { + printf ("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 (strcasecmp (w1, "import") == 0) + atcommand_config_read (w2); + else if (strcasecmp (w1, "command_symbol") == 0 && w2[0] > 31 && w2[0] != '/' && // symbol of standard ragnarok GM commands + w2[0] != '%') // symbol of party chat speaking + command_symbol = w2[0]; + } + fclose_ (fp); + + return 0; +} + +/*========================================== +// @ command processing functions + *------------------------------------------ + */ + +/*========================================== + * @setup - Safely set a chars levels and warp them to a special place + * TAW Specific + *------------------------------------------ + */ +int atcommand_setup (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char buf[256]; + char character[100]; + int level = 1; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &level, character) < 2) + { + clif_displaymessage (fd, "Usage: @setup <level> <char name>"); + return -1; + } + level--; + + snprintf (buf, 255, "-255 %s", character); + atcommand_character_baselevel (fd, sd, "@charbaselvl", buf); + + snprintf (buf, 255, "%d %s", level, character); + atcommand_character_baselevel (fd, sd, "@charbaselvl", buf); + + // Emote skill + snprintf (buf, 255, "1 1 %s", character); + atcommand_skill_learn(fd, sd, "@skill-learn", buf); + + // Trade skill + snprintf (buf, 255, "2 1 %s", character); + atcommand_skill_learn(fd, sd, "@skill-learn", buf); + + // Party skill + snprintf (buf, 255, "2 2 %s", character); + atcommand_skill_learn(fd, sd, "@skill-learn", buf); + + snprintf (buf, 255, "018-1.gat 24 98 %s", character); + atcommand_charwarp (fd, sd, "@charwarp", buf); + + return (0); + +} + +/*========================================== + * @rura+ + *------------------------------------------ + */ +int atcommand_charwarp (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char map_name[100]; + char character[100]; + int x = 0, y = 0; + struct map_session_data *pl_sd; + int m; + + memset (map_name, '\0', sizeof (map_name)); + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%99s %d %d %99[^\n]", map_name, &x, &y, + character) < 4) + { + clif_displaymessage (fd, + "Usage: @charwarp/@rura+ <mapname> <x> <y> <char name>"); + return -1; + } + + if (x <= 0) + x = MRAND (399) + 1; + if (y <= 0) + y = MRAND (399) + 1; + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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 < 800 && y > 0 && y < 800) + { + 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_name, 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_warp (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char map_name[100]; + int x = 0, y = 0; + int m; + + memset (map_name, '\0', sizeof (map_name)); + + if (!message || !*message + || sscanf (message, "%99s %d %d", map_name, &x, &y) < 1) + { + clif_displaymessage (fd, + "Please, enter a map (usage: @warp <mapname> <x> <y>)."); + return -1; + } + + if (x <= 0) + x = MRAND (399) + 1; + if (y <= 0) + y = MRAND (399) + 1; + + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) + strcat (map_name, ".gat"); + + if (x > 0 && x < 800 && y > 0 && y < 800) + { + 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 you to this map."); + 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, + "You are not authorised to warp you from your actual map."); + return -1; + } + if (pc_setpos (sd, map_name, 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_where (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (sscanf (message, "%99[^\n]", character) < 1) + strcpy (character, sd->status.name); + + if ((pl_sd = map_nick2sd (character)) != NULL && + !((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (pc_isGM (pl_sd) > pc_isGM (sd)))) + { // you can look only lower or same level + sprintf (output, "%s: %s (%d,%d)", pl_sd->status.name, pl_sd->mapname, + pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, output); + } + else + { + clif_displaymessage (fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_goto (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @jumpto/@warpto/@goto <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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, + "You are not authorised to warp you to the map of this player."); + 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, + "You are not authorised to warp you from your actual map."); + return -1; + } + pc_setpos (sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, 3); + sprintf (output, msg_table[4], character); // Jump to %s + clif_displaymessage (fd, 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) +{ + char output[200]; + int x = 0, y = 0; + + memset (output, '\0', sizeof (output)); + + sscanf (message, "%d %d", &x, &y); + + if (x <= 0) + x = MRAND (399) + 1; + if (y <= 0) + y = MRAND (399) + 1; + if (x > 0 && x < 800 && y > 0 && y < 800) + { + 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 you to your actual map."); + 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, + "You are not authorised to warp you from your actual map."); + return -1; + } + pc_setpos (sd, sd->mapname, x, y, 3); + sprintf (output, msg_table[5], x, y); // Jump to %d %d + clif_displaymessage (fd, output); + } + else + { + clif_displaymessage (fd, msg_table[2]); // Coordinates out of range. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_who (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + + memset (output, '\0', sizeof (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); + 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_GM_level = pc_isGM (pl_sd); + if (! + ((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (pl_GM_level > GM_level))) + { // you can look only lower or same level + memcpy (player_name, pl_sd->status.name, 24); + 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 (pl_GM_level > 0) + sprintf (output, + "Name: %s (GM:%d) | Location: %s %d %d", + pl_sd->status.name, pl_GM_level, + pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + else + sprintf (output, "Name: %s | Location: %s %d %d", + pl_sd->status.name, pl_sd->mapname, + pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, 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 (output, msg_table[30], count); // %d players found. + clif_displaymessage (fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whogroup (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char temp0[100]; + char temp1[100]; + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + struct guild *g; + struct party *p; + + memset (temp0, '\0', sizeof (temp0)); + memset (temp1, '\0', sizeof (temp1)); + memset (output, '\0', sizeof (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); + 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_GM_level = pc_isGM (pl_sd); + if (! + ((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (pl_GM_level > GM_level))) + { // you can look only lower or same level + memcpy (player_name, pl_sd->status.name, 24); + 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); + 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 (output, + "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", + pl_sd->status.name, pl_GM_level, temp0, + temp1); + else + sprintf (output, + "Name: %s | Party: '%s' | Guild: '%s'", + pl_sd->status.name, temp0, temp1); + clif_displaymessage (fd, 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 (output, msg_table[30], count); // %d players found. + clif_displaymessage (fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whomap (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, count; + int pl_GM_level, GM_level; + int map_id; + char map_name[100]; + + memset (output, '\0', sizeof (output)); + memset (map_name, '\0', sizeof (map_name)); + + if (!message || !*message) + map_id = sd->bl.m; + else + { + sscanf (message, "%99s", map_name); + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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); + 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_GM_level = pc_isGM (pl_sd); + if (! + ((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (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 (output, + "Name: %s (GM:%d) | Location: %s %d %d", + pl_sd->status.name, pl_GM_level, + pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + else + sprintf (output, "Name: %s | Location: %s %d %d", + pl_sd->status.name, pl_sd->mapname, + pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, output); + count++; + } + } + } + } + + if (count == 0) + sprintf (output, msg_table[54], map[map_id].name); // No player found in map '%s'. + else if (count == 1) + sprintf (output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. + else + { + sprintf (output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. + } + clif_displaymessage (fd, output); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whomapgroup (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char temp0[100]; + char temp1[100]; + char output[200]; + struct map_session_data *pl_sd; + int i, count; + int pl_GM_level, GM_level; + int map_id = 0; + char map_name[100]; + struct guild *g; + struct party *p; + + memset (temp0, '\0', sizeof (temp0)); + memset (temp1, '\0', sizeof (temp1)); + memset (output, '\0', sizeof (output)); + memset (map_name, '\0', sizeof (map_name)); + + if (!message || !*message) + map_id = sd->bl.m; + else + { + sscanf (message, "%99s", map_name); + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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); + 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_GM_level = pc_isGM (pl_sd); + if (! + ((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (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 (output, + "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", + pl_sd->status.name, pl_GM_level, temp0, + temp1); + else + sprintf (output, + "Name: %s | Party: '%s' | Guild: '%s'", + pl_sd->status.name, temp0, temp1); + clif_displaymessage (fd, output); + count++; + } + } + } + } + + if (count == 0) + sprintf (output, msg_table[54], map[map_id].name); // No player found in map '%s'. + else if (count == 1) + sprintf (output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. + else + { + sprintf (output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. + } + clif_displaymessage (fd, 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]; + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + struct guild *g; + struct party *p; + + memset (temp0, '\0', sizeof (temp0)); + memset (temp1, '\0', sizeof (temp1)); + memset (output, '\0', sizeof (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); + 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_GM_level = pc_isGM (pl_sd); + if (pl_GM_level > 0) + { + if (! + ((battle_config.hide_GM_session + || (pl_sd->status.option & OPTION_HIDE)) + && (pl_GM_level > GM_level))) + { // you can look only lower or same level + memcpy (player_name, pl_sd->status.name, 24); + 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 (output, + "Name: %s (GM:%d) | Location: %s %d %d", + pl_sd->status.name, pl_GM_level, + pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, output); + sprintf (output, + " BLvl: %d | Job: %s (Lvl: %d)", + pl_sd->status.base_level, + job_name (pl_sd->status.pc_class), + pl_sd->status.job_level); + clif_displaymessage (fd, 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 (output, " Party: '%s' | Guild: '%s'", + temp0, temp1); + clif_displaymessage (fd, 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 (output, msg_table[152], count); // %d GMs found. + clif_displaymessage (fd, output); + } + + 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->mapname, sd->bl.x, sd->bl.y); + pc_makesavestatus (sd); + chrif_save (sd); + 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; + + m = map_mapname2mapid (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, + "You are not authorised to warp you to your save map."); + 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, + "You are not authorised to warp you from your actual map."); + 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) +{ + char output[200]; + int speed; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message) + { + sprintf (output, + "Please, enter a speed value (usage: @speed <%d-%d>).", + MIN_WALK_SPEED, MAX_WALK_SPEED); + clif_displaymessage (fd, 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 (output, + "Please, enter a valid speed value (usage: @speed <%d-%d>).", + MIN_WALK_SPEED, MAX_WALK_SPEED); + clif_displaymessage (fd, 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", ¶m1, ¶m2, ¶m3) < 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) + { + clif_cart_itemlist (sd); + clif_cart_equiplist (sd); + clif_updatestatus (sd, SP_CARTINFO); + } + sd->status.option = param3; + // fix pecopeco display + if (sd->status.pc_class == 13 || sd->status.pc_class == 21 + || sd->status.pc_class == 4014 || sd->status.pc_class == 4022) + { + if (!pc_isriding (sd)) + { // sd have the new value... + if (sd->status.pc_class == 13) + sd->status.pc_class = sd->view_class = 7; + else if (sd->status.pc_class == 21) + sd->status.pc_class = sd->view_class = 14; + else if (sd->status.pc_class == 4014) + sd->status.pc_class = sd->view_class = 4008; + else if (sd->status.pc_class == 4022) + sd->status.pc_class = sd->view_class = 4015; + } + } + else + { + if (pc_isriding (sd)) + { // sd have the new value... + if (sd->disguise > 0) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) + sd->status.option &= ~0x0020; + } + else + { + if (sd->status.pc_class == 7) + sd->status.pc_class = sd->view_class = 13; + else if (sd->status.pc_class == 14) + sd->status.pc_class = sd->view_class = 21; + else if (sd->status.pc_class == 4008) + sd->status.pc_class = sd->view_class = 4014; + else if (sd->status.pc_class == 4015) + sd->status.pc_class = sd->view_class = 4022; + else + sd->status.option &= ~0x0020; + } + } + } + + clif_changeoption (&sd->bl); + pc_calcstatus (sd, 0); + 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) +{ + if (sd->status.option & OPTION_HIDE) + { + sd->status.option &= ~OPTION_HIDE; + clif_displaymessage (fd, msg_table[10]); // Invisible: Off + } + else + { + sd->status.option |= OPTION_HIDE; + clif_displaymessage (fd, msg_table[11]); // Invisible: On + } + clif_changeoption (&sd->bl); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_die (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + pc_damage (NULL, sd, sd->status.hp + 1); + 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) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @kill <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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 + 1); + 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) +{ + sd->status.hp = sd->status.max_hp; + sd->status.sp = sd->status.max_sp; + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kami (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message) + { + clif_displaymessage (fd, + "Please, enter a message (usage: @kami <message>)."); + return -1; + } + + sscanf (message, "%199[^\n]", output); + intif_GMmessage (output, strlen (output) + 1, 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 + + 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>) + *------------------------------------------ + */ +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; + + 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; + 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) + { + 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); + } + 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; + + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].amount + && sd->status.inventory[i].equip == 0) + 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) +{ + pc_checkitem (sd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_baselevelup (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int level, i; + + if (!message || !*message || (level = atoi (message)) == 0) + { + clif_displaymessage (fd, + "Please, enter a level adjustement (usage: @blvl <number of levels>)."); + return -1; + } + + if (level > 0) + { + if (sd->status.base_level == battle_config.maximum_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 (level > battle_config.maximum_level || level > (battle_config.maximum_level - sd->status.base_level)) // fix positiv overflow + level = battle_config.maximum_level - sd->status.base_level; + for (i = 1; i <= level; i++) + sd->status.status_point += (sd->status.base_level + i + 14) / 4; + sd->status.base_level += level; + clif_updatestatus (sd, SP_BASELEVEL); + clif_updatestatus (sd, SP_NEXTBASEEXP); + clif_updatestatus (sd, SP_STATUSPOINT); + pc_calcstatus (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 < -battle_config.maximum_level || level < (1 - 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) / 4; + 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_calcstatus (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) +{ + int up_level = 50, level; + + if (!message || !*message || (level = atoi (message)) == 0) + { + clif_displaymessage (fd, + "Please, enter a level adjustement (usage: @jlvl <number of levels>)."); + return -1; + } + + if (sd->status.pc_class == 0 || sd->status.pc_class == 4001) + up_level -= 40; + else if ((sd->status.pc_class > 4007 && sd->status.pc_class < 4024) + || sd->status.pc_class == 23) + up_level += 20; + + 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 (level > up_level || 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); + pc_calcstatus (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 < -up_level || level < (1 - 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 + pc_calcstatus (sd, 0); + clif_displaymessage (fd, msg_table[25]); // Job level lowered. + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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; + + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_gm (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char password[100]; + + 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; + int i; + + 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); + 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 (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; + int i; + + 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); + 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 (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; + } + } + } + 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) +{ + 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) +{ + 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; + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message + || sscanf (message, "%d %d %d", &hair_style, &hair_color, + &cloth_color) < 1) + { + sprintf (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, 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) + { + //服の色変更 + if (cloth_color != 0 && sd->status.sex == 1 + && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) + { + //服の色未実装職の判定 + 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; + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%d", &cloth_color) < 1) + { + sprintf (output, + "Please, enter a clothes color (usage: @dye/@ccolor <clothes color: %d-%d>).", + MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + clif_displaymessage (fd, output); + return -1; + } + + if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) + { + if (cloth_color != 0 && sd->status.sex == 1 + && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) + { + clif_displaymessage (fd, msg_table[35]); // You can't use this command with this class. + return -1; + } + else + { + 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; +} + +/*========================================== + * @chardye by [MouseJstr] + *------------------------------------------ + */ +int +atcommand_chardye (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + 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; + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%d", &hair_style) < 1) + { + sprintf (output, + "Please, enter a hair style (usage: @hairstyle/@hstyle <hair ID: %d-%d>).", + MIN_HAIR_STYLE, MAX_HAIR_STYLE); + clif_displaymessage (fd, output); + return -1; + } + + if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) + { + if (hair_style != 0 && sd->status.sex == 1 + && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) + { + 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) +{ + 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; + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%d", &hair_color) < 1) + { + sprintf (output, + "Please, enter a hair color (usage: @haircolor/@hcolor <hair color: %d-%d>).", + MIN_HAIR_COLOR, MAX_HAIR_COLOR); + clif_displaymessage (fd, output); + return -1; + } + + if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) + { + if (hair_color != 0 && sd->status.sex == 1 + && (sd->status.pc_class == 12 || sd->status.pc_class == 17)) + { + 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; +} + +/*========================================== + * @charhaircolor by [MouseJstr] + *------------------------------------------ + */ +int +atcommand_charhaircolor (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + return 0; +} + +/*========================================== + * @go [city_number/city_name]: improved by [yor] to add city names and help + *------------------------------------------ + */ +int atcommand_go (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int i; + int town; + char map_name[100]; + char output[200]; + int m; + + struct + { + char map[16]; + int x, y; + } data[] = + { + { + "prontera.gat", 156, 191}, // 0=Prontera + { + "morocc.gat", 156, 93}, // 1=Morroc + { + "geffen.gat", 119, 59}, // 2=Geffen + { + "payon.gat", 162, 233}, // 3=Payon + { + "alberta.gat", 192, 147}, // 4=Alberta + { + "izlude.gat", 128, 114}, // 5=Izlude + { + "aldebaran.gat", 140, 131}, // 6=Al de Baran + { + "xmas.gat", 147, 134}, // 7=Lutie + { + "comodo.gat", 209, 143}, // 8=Comodo + { + "yuno.gat", 157, 51}, // 9=Yuno + { + "amatsu.gat", 198, 84}, // 10=Amatsu + { + "gonryun.gat", 160, 120}, // 11=Gon Ryun + { + "umbala.gat", 89, 157}, // 12=Umbala + { + "niflheim.gat", 21, 153}, // 13=Niflheim + { + "louyang.gat", 217, 40}, // 14=Lou Yang + { + "new_1-1.gat", 53, 111}, // 15=Start point + { + "sec_pri.gat", 23, 61}, // 16=Prison + }; + + memset (map_name, '\0', sizeof (map_name)); + memset (output, '\0', sizeof (output)); + + // get the number + town = atoi (message); + + // if no value, display all value + if (!message || !*message || sscanf (message, "%99s", 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, + "-3=(Memo point 2) 4=Alberta 11=Gon Ryun"); + clif_displaymessage (fd, + "-2=(Memo point 1) 5=Izlude 12=Umbala"); + clif_displaymessage (fd, + "-1=(Memo point 0) 6=Al de Baran 13=Niflheim"); + clif_displaymessage (fd, + " 0=Prontera 7=Lutie 14=Lou Yang"); + clif_displaymessage (fd, + " 1=Morroc 8=Comodo 15=Start point"); + clif_displaymessage (fd, + " 2=Geffen 9=Yuno 16=Prison"); + clif_displaymessage (fd, " 3=Payon 10=Amatsu"); + return -1; + } + else + { + // get possible name of the city and add .gat if not in the name + map_name[sizeof (map_name) - 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) < 13) // 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; + } + + if (town >= -3 && town <= -1) + { + if (sd->status.memo_point[-town - 1].map[0]) + { + m = map_mapname2mapid (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, + "You are not authorised to warp you to this memo map."); + 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, + "You are not authorised to warp you from your actual map."); + 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 (output, msg_table[164], -town - 1); // Your memo point #%d doesn't exist. + clif_displaymessage (fd, output); + return -1; + } + } + else if (town >= 0 && town < (int) (sizeof (data) / sizeof (data[0]))) + { + m = map_mapname2mapid (data[town].map); + 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 you to this destination map."); + 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, + "You are not authorised to warp you from your actual map."); + return -1; + } + if (pc_setpos (sd, 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_spawn (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char monster[100]; + char output[200]; + int mob_id; + int number = 0; + int x = 0, y = 0; + int count; + int i, j, k; + int mx, my, range; + + memset (monster, '\0', sizeof (monster)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message + || sscanf (message, "%99s %d %d %d", monster, &number, &x, &y) < 1) + { + clif_displaymessage (fd, msg_table[143]); // 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 = mobdb_checkid (atoi (monster)); + + if (mob_id == 0) + { + clif_displaymessage (fd, msg_table[40]); // Invalid monster ID or name. + return -1; + } + + if (mob_id == 1288) + { + clif_displaymessage (fd, msg_table[83]); // Cannot spawn emperium. + return -1; + } + + if (number <= 0) + number = 1; + + // 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) + printf ("%s monster='%s' id=%d count=%d (%d,%d)\n", command, monster, + mob_id, number, x, y); + + count = 0; + range = 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 + (MRAND (range) - (range / 2)); + else + mx = x; + if (y <= 0) + my = sd->bl.y + (MRAND (range) - (range / 2)); + else + my = y; + k = mob_once_spawn ((struct map_session_data *) sd, "this", mx, + my, "", 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 (output, msg_table[240], count); // %d monster(s) summoned! + clif_displaymessage (fd, output); + } + else + { + clif_displaymessage (fd, msg_table[40]); // Invalid monster ID or name. + return -1; + } + + 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[100]; + + memset (map_name, '\0', sizeof (map_name)); + + if (!message || !*message || sscanf (message, "%99s", map_name) < 1) + map_id = sd->bl.m; + else + { + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) + strcat (map_name, ".gat"); + if ((map_id = map_mapname2mapid (map_name)) < 0) + map_id = sd->bl.m; + } + + map_foreachinarea (atkillmonster_sub, map_id, 0, 0, map[map_id].xs, + map[map_id].ys, 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) +{ + atcommand_killmonster_sub (fd, sd, message, 1); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int atlist_nearby_sub (struct block_list *bl, va_list ap) +{ + char buf[32]; + int fd = va_arg (ap, int); + nullpo_retr (0, bl); + + sprintf (buf, " - \"%s\"", ((struct map_session_data *) bl)->status.name); + clif_displaymessage (fd, buf); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_list_nearby (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + clif_displaymessage (fd, "Nearby players:"); + map_foreachinarea (atlist_nearby_sub, sd->bl.m, sd->bl.x - 1, + sd->bl.y - 1, sd->bl.x + 1, sd->bl.x + 1, BL_PC, fd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_killmonster2 (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + atcommand_killmonster_sub (fd, sd, message, 0); + + 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; + char output[200]; + + memset (output, '\0', sizeof (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; + *((unsigned long *) (&tmp_item.card[2])) = sd->char_id; + clif_produceeffect (sd, 0, item_id); // 製造エフェクトパケット + clif_misceffect (&sd->bl, 3); // 他人にも成功を通知 + if ((flag = pc_additem (sd, &tmp_item, 1))) + clif_additem (sd, 0, 0, flag); + } + else + { + if (battle_config.error_log) + printf ("@produce NOT WEAPON [%d]\n", item_id); + if (item_id != 0 && itemdb_exists (item_id)) + sprintf (output, msg_table[169], item_id, item_data->name); // This item (%d: '%s') is not an equipment. + else + sprintf (output, "%s", msg_table[170]); // This item is not an equipment. + clif_displaymessage (fd, output); + return -1; + } + + return 0; +} + +/*========================================== + * Sub-function to display actual memo points + *------------------------------------------ + */ +void atcommand_memo_sub (struct map_session_data *sd) +{ + int i; + char output[200]; + + memset (output, '\0', sizeof (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[0]) + sprintf (output, "%d - %s (%d,%d)", i, + sd->status.memo_point[i].map, sd->status.memo_point[i].x, + sd->status.memo_point[i].y); + else + sprintf (output, msg_table[171], i); // %d - void + clif_displaymessage (sd->fd, output); + } + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_memo (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int position = 0; + char output[200]; + + memset (output, '\0', sizeof (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 + && battle_config.any_warp_GM_min_level > pc_isGM (sd)) + { + clif_displaymessage (fd, + "You are not authorised to memo this map."); + return -1; + } + if (sd->status.memo_point[position].map[0]) + { + sprintf (output, msg_table[172], position, 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, output); + } + memcpy (sd->status.memo_point[position].map, map[sd->bl.m].name, + 24); + 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 (output, + "Please, enter a valid position (usage: @memo <memo_position:%d-%d>).", + MIN_PORTAL_MEMO, MAX_PORTAL_MEMO); + clif_displaymessage (fd, 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) +{ + char output[200]; + int y; + + memset (output, '\0', sizeof (output)); + + for (y = 2; y >= -2; y--) + { + sprintf (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), + map_getcell (sd->bl.m, sd->bl.x - 1, sd->bl.y + y), + map_getcell (sd->bl.m, sd->bl.x, sd->bl.y + y), + map_getcell (sd->bl.m, sd->bl.x + 1, sd->bl.y + y), + map_getcell (sd->bl.m, sd->bl.x + 2, sd->bl.y + y)); + clif_displaymessage (fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_packet (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int x = 0, y = 0; + + if (!message || !*message || sscanf (message, "%d %d", &x, &y) < 2) + { + clif_displaymessage (fd, + "Please, enter a status type/flag (usage: @packet <status type> <flag>)."); + return -1; + } + + clif_status_change (&sd->bl, x, y); + + 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; + + 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; + + 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 + }; + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%d", &value) < 1 + || value == 0) + { + sprintf (output, + "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); + clif_displaymessage (fd, output); + return -1; + } + + index = -1; + for (i = 0; param[i] != NULL; i++) + { + if (strcasecmp (command, param[i]) == 0) + { + index = i; + break; + } + } + if (index < 0 || index > MAX_STATUS_TYPE) + { // normaly impossible... + sprintf (output, + "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); + clif_displaymessage (fd, output); + return -1; + } + + new_value = (int) *status[index] + value; + if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow + new_value = battle_config.max_parameter; + else if (value < 0 && (value < -battle_config.max_parameter || 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); + pc_calcstatus (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_all_stats (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 + }; + + if (!message || !*message || sscanf (message, "%d", &value) < 1 + || value == 0) + value = battle_config.max_parameter; + + count = 0; + for (index = 0; index < (int) (sizeof (status) / sizeof (status[0])); + index++) + { + + new_value = (int) *status[index] + value; + if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow + new_value = battle_config.max_parameter; + else if (value < 0 && (value < -battle_config.max_parameter || 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); + pc_calcstatus (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; + + if (!message || !*message || sscanf (message, "%d", &level) < 1 + || level == 0) + { + clif_displaymessage (fd, + "Please, enter a valid level (usage: @guildlvl <# 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_recall (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @recall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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 authorised to warp this player from its actual map."); + return -1; + } + pc_setpos (pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + sprintf (output, msg_table[46], character); // %s recalled! + 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 atcommand_revive (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @revive <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_character_stats (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + 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, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charstats <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.pc_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 Stats All by fritz +int atcommand_character_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; + struct map_session_data *pl_sd; + + memset (output, '\0', sizeof (output)); + memset (gmlevel, '\0', sizeof (gmlevel)); + + count = 0; + 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) > 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.pc_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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_character_option (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + 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 %99[^\n]", &opt1, &opt2, &opt3, + character) < 4 || opt1 < 0 || opt2 < 0 || opt3 < 0) + { + clif_displaymessage (fd, + "Please, enter valid options and a player name (usage: @charoption <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; + pl_sd->status.option = opt3; + // fix pecopeco display + if (pl_sd->status.pc_class == 13 || pl_sd->status.pc_class == 21 + || pl_sd->status.pc_class == 4014 || pl_sd->status.pc_class == 4022) + { + if (!pc_isriding (pl_sd)) + { // pl_sd have the new value... + if (pl_sd->status.pc_class == 13) + pl_sd->status.pc_class = pl_sd->view_class = 7; + else if (pl_sd->status.pc_class == 21) + pl_sd->status.pc_class = pl_sd->view_class = 14; + else if (pl_sd->status.pc_class == 4014) + pl_sd->status.pc_class = pl_sd->view_class = 4008; + else if (pl_sd->status.pc_class == 4022) + pl_sd->status.pc_class = pl_sd->view_class = 4015; + } + } + else + { + if (pc_isriding (pl_sd)) + { // pl_sd have the new value... + if (pl_sd->disguise > 0) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) + pl_sd->status.option &= ~0x0020; + } + else + { + if (pl_sd->status.pc_class == 7) + pl_sd->status.pc_class = pl_sd->view_class = 13; + else if (pl_sd->status.pc_class == 14) + pl_sd->status.pc_class = pl_sd->view_class = 21; + else if (pl_sd->status.pc_class == 4008) + pl_sd->status.pc_class = pl_sd->view_class = 4014; + else if (pl_sd->status.pc_class == 4015) + pl_sd->status.pc_class = pl_sd->view_class = 4022; + else + pl_sd->status.option &= ~0x0020; + } + } + } + clif_changeoption (&pl_sd->bl); + pc_calcstatus (pl_sd, 0); + 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; +} + +/*========================================== + * charchangesex command (usage: charchangesex <player_name>) + *------------------------------------------ + */ +int atcommand_char_change_sex (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charchangesex <name>)."); + return -1; + } + + // check player name + if (strlen (character) < 4) + { + clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } + else if (strlen (character) > 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, character, 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; +} + +/*========================================== + * 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) +{ + char character[100]; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @block <name>)."); + return -1; + } + + // check player name + if (strlen (character) < 4) + { + clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } + else if (strlen (character) > 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, character, 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[100], character[100]; + char *modif_p; + int year, month, day, hour, minute, second, value; + + memset (modif, '\0', sizeof (modif)); + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%s %99[^\n]", modif, character) < 2) + { + clif_displaymessage (fd, + "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish <time> <name>)."); + return -1; + } + + modif[sizeof (modif) - 1] = '\0'; + character[sizeof (character) - 1] = '\0'; + + modif_p = modif; + 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 (character) < 4) + { + clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } + else if (strlen (character) > 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, character, 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) +{ + char character[100]; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charunblock <player_name>)."); + return -1; + } + + // check player name + if (strlen (character) < 4) + { + clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } + else if (strlen (character) > 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, character, 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) +{ + char character[100]; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charunban <player_name>)."); + return -1; + } + + // check player name + if (strlen (character) < 4) + { + clif_displaymessage (fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } + else if (strlen (character) > 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, character, 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_character_save (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char map_name[100]; + char character[100]; + 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, "%99s %d %d %99[^\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: @charsave <map> <x> <y> <charname>)."); + return -1; + } + + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 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 (m >= 0 && 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_name, 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_night (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + int i; + + if (night_flag != 1) + { + 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->opt2 |= STATE_BLIND; + clif_changeoption (&pl_sd->bl); + clif_displaymessage (pl_sd->fd, msg_table[59]); // Night has fallen. + } + } + } + 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) +{ + struct map_session_data *pl_sd; + int i; + + if (night_flag != 0) + { + 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->opt2 &= ~STATE_BLIND; + clif_changeoption (&pl_sd->bl); + clif_displaymessage (pl_sd->fd, msg_table[60]); // Day has arrived. + } + } + } + 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; + int i; + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth && i != 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 + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_doommap (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth && i != 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 + 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)) + { + 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); + 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; + + for (i = 0; i < fd_max; i++) + { + if (session[i]) + atcommand_raise_sub ((struct map_session_data *)session[i]->session_data); + } + 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_sd; + int i; + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth && sd->bl.m == pl_sd->bl.m) + atcommand_raise_sub (pl_sd); + } + clif_displaymessage (fd, msg_table[64]); // Mercy has been granted. + + return 0; +} + +/*========================================== + * atcommand_character_baselevel @charbaselvlで対象キャラのレベルを上げる + *------------------------------------------ +*/ +int atcommand_character_baselevel (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + int level = 0, i; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &level, character) < 2 + || level == 0) + { + clif_displaymessage (fd, + "Please, enter a level adjustement and a player name (usage: @charblvl <#> <nickname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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.maximum_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 (level > battle_config.maximum_level || level > (battle_config.maximum_level - pl_sd->status.base_level)) // fix positiv overflow + level = + battle_config.maximum_level - + pl_sd->status.base_level; + for (i = 1; i <= level; i++) + pl_sd->status.status_point += + (pl_sd->status.base_level + i + 14) / 4; + pl_sd->status.base_level += level; + clif_updatestatus (pl_sd, SP_BASELEVEL); + clif_updatestatus (pl_sd, SP_NEXTBASEEXP); + clif_updatestatus (pl_sd, SP_STATUSPOINT); + pc_calcstatus (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 < -battle_config.maximum_level || level < (1 - 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) / 4; + 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; + pl_sd->status.base_exp = 0; + clif_updatestatus (pl_sd, SP_BASELEVEL); + clif_updatestatus (pl_sd, SP_NEXTBASEEXP); + clif_updatestatus (pl_sd, SP_BASEEXP); + pc_calcstatus (pl_sd, 0); + clif_displaymessage (fd, msg_table[66]); // Character's base level lowered. + } + // Reset their stat points to prevent extra points from stacking + atcommand_charstreset(fd, sd,"@charstreset", character); + } + 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; //正常終了 +} + +/*========================================== + * atcommand_character_joblevel @charjoblvlで対象キャラのJobレベルを上げる + *------------------------------------------ + */ +int atcommand_character_joblevel (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + int max_level = 50, level = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job pl_s_class; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &level, character) < 2 + || level == 0) + { + clif_displaymessage (fd, + "Please, enter a level adjustement and a player name (usage: @charjlvl <#> <nickname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + pl_s_class = pc_calc_base_job (pl_sd->status.pc_class); + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can change job level only lower or same gm level + if (pl_s_class.job == 0) + max_level -= 40; + if ((pl_s_class.job == 23) || (pl_s_class.upper == 1 && pl_s_class.type == 2)) //スパノビと転生職はJobレベルの最高が70 + max_level += 20; + + 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); + pc_calcstatus (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 + pc_calcstatus (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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kick (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @kick <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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; + int i; + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth && 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_allskills (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + 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; + + 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) & 0x01) + { + 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_charquestskill (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + struct map_session_data *pl_sd; + int skill_id = 0; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &skill_id, character) < 2 + || skill_id < 0) + { + clif_displaymessage (fd, + "Please, enter a quest skill number and a player name (usage: @charquestskill <#:0+> <char_name>)."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL_DB) + { + if (skill_get_inf2 (skill_id) & 0x01) + { + if ((pl_sd = map_nick2sd (character)) != 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_lostskill (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int skill_id; + + 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) & 0x01) + { + if (pc_checkskill (sd, skill_id) > 0) + { + sd->status.skill[skill_id].lv = 0; + sd->status.skill[skill_id].flags = 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_charlostskill (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + struct map_session_data *pl_sd; + int skill_id = 0; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &skill_id, character) < 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) & 0x01) + { + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_checkskill (pl_sd, skill_id) > 0) + { + pl_sd->status.skill[skill_id].lv = 0; + pl_sd->status.skill[skill_id].flags = 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_party (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char party[100]; + + memset (party, '\0', sizeof (party)); + + if (!message || !*message || sscanf (message, "%99[^\n]", party) < 1) + { + clif_displaymessage (fd, + "Please, enter a party name (usage: @party <party_name>)."); + return -1; + } + + party_create (sd, party); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_guild (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char guild[100]; + int prev; + + memset (guild, '\0', sizeof (guild)); + + if (!message || !*message || sscanf (message, "%99[^\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) +{ + 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) +{ + 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; + int i; + + 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 (sd->status.account_id != pl_sd->status.account_id) + clif_GM_kick (sd, pl_sd, 0); + } + } + clif_GM_kick (sd, sd, 0); + + 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]; + char output[200]; + int i, match; + struct item_data *item; + + memset (item_name, '\0', sizeof (item_name)); + memset (output, '\0', sizeof (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 (output, msg_table[77], item_name); // The reference result of '%s' (name: id): + clif_displaymessage (fd, output); + match = 0; + for (i = 0; i < 20000; i++) + { + if ((item = itemdb_exists (i)) != NULL + && strstr (item->jname, item_name) != NULL) + { + match++; + sprintf (output, msg_table[78], item->jname, item->nameid); // %s: %d + clif_displaymessage (fd, output); + } + } + sprintf (output, msg_table[79], match); // It is %d affair above. + clif_displaymessage (fd, output); + + return 0; +} + +/*========================================== + * Character Skill Reset + *------------------------------------------ + */ +int atcommand_charskreset (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charskreset <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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 (output, msg_table[206], character); // '%s' skill 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; +} + +/*========================================== + * Character Stat Reset + *------------------------------------------ + */ +int atcommand_charstreset (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charstreset <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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 (output, msg_table[207], character); // '%s' 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; +} + +/*========================================== + * Character Reset + *------------------------------------------ + */ +int atcommand_charreset (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charreset <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); + pc_setglobalreg (pl_sd, "MAGIC_FLAGS", 0); // [Fate] Reset magic quest variables + pc_setglobalreg (pl_sd, "MAGIC_EXP", 0); // [Fate] Reset magic experience + 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; +} + +/*========================================== + * Character Wipe + *------------------------------------------ + */ +int atcommand_char_wipe (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charwipe <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 + int i; + + // Reset base level + pl_sd->status.base_level = 1; + pl_sd->status.base_exp = 0; + clif_updatestatus (pl_sd, SP_BASELEVEL); + clif_updatestatus (pl_sd, SP_NEXTBASEEXP); + clif_updatestatus (pl_sd, SP_BASEEXP); + + // Reset job level + pl_sd->status.job_level = 1; + pl_sd->status.job_exp = 0; + clif_updatestatus (pl_sd, SP_JOBLEVEL); + clif_updatestatus (pl_sd, SP_NEXTJOBEXP); + clif_updatestatus (pl_sd, SP_JOBEXP); + + // Zeny to 50 + pl_sd->status.zeny = 50; + clif_updatestatus (pl_sd, SP_ZENY); + + // Clear inventory + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].amount) + { + if (sd->status.inventory[i].equip) + pc_unequipitem (pl_sd, i, 0); + pc_delitem (pl_sd, i, sd->status.inventory[i].amount, 0); + } + } + + // Give knife and shirt + struct item item; + item.nameid = 1201; // knife + item.identify = 1; + item.broken = 0; + pc_additem (pl_sd, &item, 1); + item.nameid = 1202; // shirt + pc_additem (pl_sd, &item, 1); + + // Reset stats and skills + pc_calcstatus (pl_sd, 0); + pc_resetstate (pl_sd); + pc_resetskill (pl_sd); + pc_setglobalreg (pl_sd, "MAGIC_FLAGS", 0); // [Fate] Reset magic quest variables + pc_setglobalreg (pl_sd, "MAGIC_EXP", 0); // [Fate] Reset magic experience + + sprintf (output, "%s: wiped.", 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; +} + +/*========================================== + * Character Model by chbrules + *------------------------------------------ + */ +int atcommand_charmodel (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 character[100]; + char output[200]; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message + || sscanf (message, "%d %d %d %99[^\n]", &hair_style, &hair_color, + &cloth_color, character) < 4 || hair_style < 0 + || hair_color < 0 || cloth_color < 0) + { + sprintf (output, + "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, output); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != 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) + { + + if (cloth_color != 0 && + pl_sd->status.sex == 1 && + (pl_sd->status.pc_class == 12 || pl_sd->status.pc_class == 17)) + { + 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 atcommand_charskpoint (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + int new_skill_point; + int point = 0; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &point, character) < 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 (character)) != 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 atcommand_charstpoint (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + int new_status_point; + int point = 0; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &point, character) < 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 (character)) != 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; +} + +/*========================================== + * Character Zeny Point (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_charzeny (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + char character[100]; + int zeny = 0, new_zeny; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &zeny, character) < 2 || zeny == 0) + { + clif_displaymessage (fd, + "Please, enter a number and a player name (usage: @charzeny <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; +} + +/*========================================== + * 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; + int i; + int count; + char output[200]; + + memset (output, '\0', sizeof (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; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && 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 + pc_setpos (pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + } + } + + clif_displaymessage (fd, msg_table[92]); // All characters recalled! + if (count) + { + sprintf (output, + "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", + count); + clif_displaymessage (fd, 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; + int i; + char guild_name[100]; + char output[200]; + struct guild *g; + int count; + + memset (guild_name, '\0', sizeof (guild_name)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\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; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && 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->mapname, sd->bl.x, sd->bl.y, 2); + } + } + sprintf (output, msg_table[93], g->name); // All online characters of the %s guild are near you. + clif_displaymessage (fd, output); + if (count) + { + sprintf (output, + "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", + count); + clif_displaymessage (fd, 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; + char party_name[100]; + char output[200]; + struct party *p; + int count; + + memset (party_name, '\0', sizeof (party_name)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\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; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && 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->mapname, sd->bl.x, sd->bl.y, 2); + } + } + sprintf (output, msg_table[95], p->name); // All online characters of the %s party are near you. + clif_displaymessage (fd, output); + if (count) + { + sprintf (output, + "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", + count); + clif_displaymessage (fd, 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) +{ + 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) +{ + mob_reload (); + 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) +{ + skill_reload (); + clif_displaymessage (fd, msg_table[99]); // Skill database reloaded. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_reloadscript (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + do_init_npc (); + do_init_script (); + + 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) +{ + 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; + struct npc_data *nd = NULL; + struct chat_data *cd = NULL; + char output[200], map_name[100]; + char direction[12]; + int m_id, i, chat_num, list = 0; + + memset (output, '\0', sizeof (output)); + memset (map_name, '\0', sizeof (map_name)); + memset (direction, '\0', sizeof (direction)); + + sscanf (message, "%d %99[^\n]", &list, map_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 (map_name[0] == '\0') + strcpy (map_name, sd->mapname); + if (strstr (map_name, ".gat") == NULL && strstr (map_name, ".afm") == NULL && strlen (map_name) < 13) // 16 - 4 (.gat) + strcat (map_name, ".gat"); + + if ((m_id = map_mapname2mapid (map_name)) < 0) + { + clif_displaymessage (fd, msg_table[1]); // Map not found. + return -1; + } + + clif_displaymessage (fd, "------ Map Info ------"); + sprintf (output, "Map Name: %s", map_name); + clif_displaymessage (fd, output); + sprintf (output, "Players In Map: %d", map[m_id].users); + clif_displaymessage (fd, output); + sprintf (output, "NPCs In Map: %d", map[m_id].npc_num); + clif_displaymessage (fd, output); + chat_num = 0; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && (cd = (struct chat_data *) map_id2bl (pl_sd->chatID))) + { + chat_num++; + } + } + sprintf (output, "Chats In Map: %d", chat_num); + clif_displaymessage (fd, output); + clif_displaymessage (fd, "------ Map Flags ------"); + sprintf (output, "Player vs Player: %s | No Guild: %s | No Party: %s", + (map[m_id].flag.pvp) ? "True" : "False", + (map[m_id].flag.pvp_noguild) ? "True" : "False", + (map[m_id].flag.pvp_noparty) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "Guild vs Guild: %s | No Party: %s", + (map[m_id].flag.gvg) ? "True" : "False", + (map[m_id].flag.gvg_noparty) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Dead Branch: %s", + (map[m_id].flag.nobranch) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Memo: %s", + (map[m_id].flag.nomemo) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Penalty: %s", + (map[m_id].flag.nopenalty) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Return: %s", + (map[m_id].flag.noreturn) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Save: %s", + (map[m_id].flag.nosave) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Teleport: %s", + (map[m_id].flag.noteleport) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Monster Teleport: %s", + (map[m_id].flag.monster_noteleport) ? "True" : "False"); + clif_displaymessage (fd, output); + sprintf (output, "No Zeny Penalty: %s", + (map[m_id].flag.nozenypenalty) ? "True" : "False"); + clif_displaymessage (fd, 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 < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && strcmp (pl_sd->mapname, map_name) == 0) + { + sprintf (output, + "Player '%s' (session #%d) | Location: %d,%d", + pl_sd->status.name, i, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, 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 (output, + "NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d", + ++i, nd->name, direction, nd->npc_class, nd->bl.x, + nd->bl.y); + clif_displaymessage (fd, output); + } + break; + case 3: + clif_displaymessage (fd, "----- Chats in Map -----"); + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth + && (cd = (struct chat_data *) map_id2bl (pl_sd->chatID)) + && strcmp (pl_sd->mapname, map_name) == 0 + && cd->usersd[0] == pl_sd) + { + sprintf (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, output); + sprintf (output, + " Users: %d/%d | Password: %s | Public: %s", + cd->users, cd->limit, cd->pass, + (cd->pub) ? "Yes" : "No"); + clif_displaymessage (fd, 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) +{ + if (sd->disguise > 0) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (fd, msg_table[212]); // Cannot mount a Peco while in disguise. + return -1; + } + + if (!pc_isriding (sd)) + { // if actually no peco + if (sd->status.pc_class == 7 || sd->status.pc_class == 14 + || sd->status.pc_class == 4008 || sd->status.pc_class == 4015) + { + if (sd->status.pc_class == 7) + sd->status.pc_class = sd->view_class = 13; + else if (sd->status.pc_class == 14) + sd->status.pc_class = sd->view_class = 21; + else if (sd->status.pc_class == 4008) + sd->status.pc_class = sd->view_class = 4014; + else if (sd->status.pc_class == 4015) + sd->status.pc_class = sd->view_class = 4022; + 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 + { + if (sd->status.pc_class == 13) + sd->status.pc_class = sd->view_class = 7; + else if (sd->status.pc_class == 21) + sd->status.pc_class = sd->view_class = 14; + else if (sd->status.pc_class == 4014) + sd->status.pc_class = sd->view_class = 4008; + else if (sd->status.pc_class == 4022) + sd->status.pc_class = sd->view_class = 4015; + 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) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charmountpeco <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pl_sd->disguise > 0) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (fd, msg_table[215]); // This player cannot mount a Peco while in disguise. + return -1; + } + + if (!pc_isriding (pl_sd)) + { // if actually no peco + if (pl_sd->status.pc_class == 7 || pl_sd->status.pc_class == 14 + || pl_sd->status.pc_class == 4008 || pl_sd->status.pc_class == 4015) + { + if (pl_sd->status.pc_class == 7) + pl_sd->status.pc_class = pl_sd->view_class = 13; + else if (pl_sd->status.pc_class == 14) + pl_sd->status.pc_class = pl_sd->view_class = 21; + else if (pl_sd->status.pc_class == 4008) + pl_sd->status.pc_class = pl_sd->view_class = 4014; + else if (pl_sd->status.pc_class == 4015) + pl_sd->status.pc_class = pl_sd->view_class = 4022; + pc_setoption (pl_sd, pl_sd->status.option | 0x0020); + clif_displaymessage (fd, msg_table[216]); // Now, this player mounts a peco. + } + else + { + clif_displaymessage (fd, msg_table[217]); // This player can not mount a peco with his/her job. + return -1; + } + } + else + { + if (pl_sd->status.pc_class == 13) + pl_sd->status.pc_class = pl_sd->view_class = 7; + else if (pl_sd->status.pc_class == 21) + pl_sd->status.pc_class = pl_sd->view_class = 14; + else if (pl_sd->status.pc_class == 4014) + pl_sd->status.pc_class = pl_sd->view_class = 4008; + else if (pl_sd->status.pc_class == 4022) + pl_sd->status.pc_class = pl_sd->view_class = 4015; + pc_setoption (pl_sd, pl_sd->status.option & ~0x0020); + clif_displaymessage (fd, msg_table[218]); // Now, this player has not more 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[100]; + char output[200]; + struct guild *g; + + memset (guild_name, '\0', sizeof (guild_name)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\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 (output, msg_table[103], g->name); // No longer spying on the %s guild. + clif_displaymessage (fd, output); + } + else + { + sd->guildspy = g->guild_id; + sprintf (output, msg_table[104], g->name); // Spying on the %s guild. + clif_displaymessage (fd, 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[100]; + char output[200]; + struct party *p; + + memset (party_name, '\0', sizeof (party_name)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\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 (output, msg_table[105], p->name); // No longer spying on the %s party. + clif_displaymessage (fd, output); + } + else + { + sd->partyspy = p->party_id; + sprintf (output, msg_table[106], p->name); // Spying on the %s party. + clif_displaymessage (fd, 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_enablenpc (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char NPCname[100]; + + memset (NPCname, '\0', sizeof (NPCname)); + + if (!message || !*message || sscanf (message, "%99[^\n]", NPCname) < 1) + { + clif_displaymessage (fd, + "Please, enter a NPC name (usage: @npcon <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_disablenpc (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char NPCname[100]; + + memset (NPCname, '\0', sizeof (NPCname)); + + if (!message || !*message || sscanf (message, "%99[^\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; +} + +/*========================================== + * 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]; + + memset (temp, '\0', sizeof (temp)); + + time (&time_server); // get time in seconds since 1/1/1970 + datetime = gmtime (&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 ((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 ((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 ((timer_data->tick - gettick ()) / 1000)); // Game time: The game is actualy in daylight for %s. + clif_displaymessage (fd, temp); + if (timer_data->tick > timer_data2->tick) + sprintf (temp, msg_table[237], txt_time ((timer_data->interval - abs (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 (abs (timer_data->tick - timer_data2->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 ((timer_data->tick - gettick ()) / 1000)); // Game time: The game is actualy in night for %s. + clif_displaymessage (fd, temp); + if (timer_data->tick > timer_data2->tick) + sprintf (temp, msg_table[239], txt_time ((timer_data->interval - abs (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 (abs (timer_data->tick - timer_data2->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 character[100]; + char item_name[100]; + int i, number = 0, item_id, item_position, count; + char output[200]; + struct item_data *item_data; + + memset (character, '\0', sizeof (character)); + memset (item_name, '\0', sizeof (item_name)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message + || sscanf (message, "%s %d %99[^\n]", item_name, &number, + character) < 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 (character)) != 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++) + { + pc_delitem (pl_sd, item_position, 1, 0); + count++; + item_position = pc_search_inventory (pl_sd, item_id); // for next loop + } + sprintf (output, msg_table[113], count); // %d item(s) removed by a GM. + clif_displaymessage (pl_sd->fd, output); + if (number == count) + sprintf (output, msg_table[114], count); // %d item(s) removed from the player. + else + sprintf (output, msg_table[115], count, count, number); // %d item(s) removed. Player had only %d on %d items. + clif_displaymessage (fd, 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) +{ + char character[100]; + struct map_session_data *pl_sd; + int x, y; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @jail <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can jail only lower or same GM + switch (MRAND (2)) + { + case 0: + x = 24; + y = 75; + break; + default: + x = 49; + y = 75; + break; + } + if (pc_setpos (pl_sd, "sec_pri.gat", x, y, 3) == 0) + { + pc_setsavepoint (pl_sd, "sec_pri.gat", 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) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @unjail/@discharge <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can jail only lower or same GM + if (pl_sd->bl.m != map_mapname2mapid ("sec_pri.gat")) + { + clif_displaymessage (fd, msg_table[119]); // This player is not in jails. + return -1; + } + else if (pc_setpos (pl_sd, "prontera.gat", 156, 191, 3) == 0) + { + pc_setsavepoint (pl_sd, "prontera.gat", 156, 191); // 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 mob_id; + + if (!message || !*message) + { + clif_displaymessage (fd, + "Please, enter a Monster/NPC name/id (usage: @disguise <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 ((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 <= 834) || // NPC + (mob_id > 1000 && mob_id < 1521)) + { // monsters + if (pc_isriding (sd)) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (fd, msg_table[227]); // Cannot wear disguise while riding a Peco. + return -1; + } + sd->disguiseflag = 1; // set to override items with disguise script [Valaris] + sd->disguise = mob_id; + pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + clif_displaymessage (fd, msg_table[122]); // Disguise applied. + } + else + { + clif_displaymessage (fd, msg_table[123]); // Monster/NPC name/id hasn't been found. + return -1; + } + + return 0; +} + +/*========================================== + * @undisguise by [Yor] + *------------------------------------------ + */ +int atcommand_undisguise (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + if (sd->disguise) + { + clif_clearchar (&sd->bl, 9); + sd->disguise = 0; + pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + clif_displaymessage (fd, msg_table[124]); // Undisguise applied. + } + else + { + clif_displaymessage (fd, msg_table[125]); // You're not disguised. + return -1; + } + + return 0; +} + +/*========================================== + * @broadcast by [Valaris] + *------------------------------------------ + */ +int atcommand_broadcast (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message) + { + clif_displaymessage (fd, + "Please, enter a message (usage: @broadcast <message>)."); + return -1; + } + + snprintf (output, 199, "%s : %s", sd->status.name, message); + intif_GMmessage (output, strlen (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) +{ + char output[200]; + + memset (output, '\0', sizeof (output)); + + if (!message || !*message) + { + clif_displaymessage (fd, + "Please, enter a message (usage: @localbroadcast <message>)."); + return -1; + } + + snprintf (output, 199, "%s : %s", sd->status.name, message); + + clif_GMmessage (&sd->bl, output, strlen (output) + 1, 1); // 1: ALL_SAMEMAP + + return 0; +} + +/*========================================== + * @ignorelist by [Yor] + *------------------------------------------ + */ +int atcommand_ignorelist (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char output[200]; + int count; + int i; + + memset (output, '\0', sizeof (output)); + + count = 0; + for (i = 0; i < (int) (sizeof (sd->ignore) / sizeof (sd->ignore[0])); i++) + if (sd->ignore[i].name[0]) + count++; + + if (sd->ignoreAll == 0) + if (count == 0) + clif_displaymessage (fd, msg_table[126]); // You accept any wisp (no wisper is refused). + else + { + sprintf (output, msg_table[127], count); // You accept any wisp, except thoses from %d player(s): + clif_displaymessage (fd, output); + } + else if (count == 0) + clif_displaymessage (fd, msg_table[128]); // You refuse all wisps (no specifical wisper is refused). + else + { + sprintf (output, msg_table[129], count); // You refuse all wisps, AND refuse wisps from %d player(s): + clif_displaymessage (fd, output); + } + + if (count > 0) + for (i = 0; i < (int) (sizeof (sd->ignore) / sizeof (sd->ignore[0])); + i++) + if (sd->ignore[i].name[0]) + clif_displaymessage (fd, sd->ignore[i].name); + + return 0; +} + +/*========================================== + * @charignorelist <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_charignorelist (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + struct map_session_data *pl_sd; + char output[200]; + int count; + int i; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charignorelist <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + count = 0; + for (i = 0; + i < (int) (sizeof (pl_sd->ignore) / sizeof (pl_sd->ignore[0])); + i++) + if (pl_sd->ignore[i].name[0]) + count++; + + if (pl_sd->ignoreAll == 0) + if (count == 0) + { + sprintf (output, msg_table[130], pl_sd->status.name); // '%s' accept any wisp (no wisper is refused). + clif_displaymessage (fd, output); + } + else + { + sprintf (output, msg_table[131], pl_sd->status.name, count); // '%s' accept any wisp, except thoses from %d player(s): + clif_displaymessage (fd, output); + } + else if (count == 0) + { + sprintf (output, msg_table[132], pl_sd->status.name); // '%s' refuse all wisps (no specifical wisper is refused). + clif_displaymessage (fd, output); + } + else + { + sprintf (output, msg_table[133], pl_sd->status.name, count); // '%s' refuse all wisps, AND refuse wisps from %d player(s): + clif_displaymessage (fd, output); + } + + if (count > 0) + for (i = 0; + i < + (int) (sizeof (pl_sd->ignore) / sizeof (pl_sd->ignore[0])); + i++) + if (pl_sd->ignore[i].name[0]) + clif_displaymessage (fd, pl_sd->ignore[i].name); + + } + else + { + clif_displaymessage (fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @inall <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_inall (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @inall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can change wisp option only to lower or same level + if (pl_sd->ignoreAll == 0) + { + sprintf (output, msg_table[134], pl_sd->status.name); // '%s' already accepts all wispers. + clif_displaymessage (fd, output); + return -1; + } + else + { + pl_sd->ignoreAll = 0; + sprintf (output, msg_table[135], pl_sd->status.name); // '%s' now accepts all wispers. + clif_displaymessage (fd, output); + // message to player + clif_displaymessage (pl_sd->fd, msg_table[136]); // A GM has authorised all wispers for you. + WFIFOW (pl_sd->fd, 0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB (pl_sd->fd, 2) = 1; + WFIFOB (pl_sd->fd, 3) = 0; // success + WFIFOSET (pl_sd->fd, 4); // packet_len_table[0x0d2] + } + } + 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; +} + +/*========================================== + * @exall <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_exall (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @exall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can change wisp option only to lower or same level + if (pl_sd->ignoreAll == 1) + { + sprintf (output, msg_table[137], pl_sd->status.name); // '%s' already blocks all wispers. + clif_displaymessage (fd, output); + return -1; + } + else + { + pl_sd->ignoreAll = 1; + sprintf (output, msg_table[138], pl_sd->status.name); // '%s' blocks now all wispers. + clif_displaymessage (fd, output); + // message to player + clif_displaymessage (pl_sd->fd, msg_table[139]); // A GM has blocked all wispers for you. + WFIFOW (pl_sd->fd, 0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB (pl_sd->fd, 2) = 0; + WFIFOB (pl_sd->fd, 3) = 0; // success + WFIFOSET (pl_sd->fd, 4); // packet_len_table[0x0d2] + } + } + 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; +} + +/*========================================== + * @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 character[100]; + char mob_name[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + memset (mob_name, '\0', sizeof (mob_name)); + + if (!message || !*message + || sscanf (message, "%s %99[^\n]", mob_name, character) < 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 = mobdb_searchname (mob_name)) == 0) // check name first (to avoid possible name begining by a number) + mob_id = atoi (mob_name); + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can disguise only lower or same level + 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 <= 834) || // NPC + (mob_id > 1000 && mob_id < 1521)) + { // monsters + if (pc_isriding (pl_sd)) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (fd, msg_table[228]); // Character cannot wear disguise while riding a Peco. + return -1; + } + pl_sd->disguiseflag = 1; // set to override items with disguise script [Valaris] + pl_sd->disguise = mob_id; + pc_setpos (pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, + 3); + clif_displaymessage (fd, msg_table[140]); // Character's disguise applied. + } + else + { + clif_displaymessage (fd, msg_table[123]); // Monster/NPC name/id hasn't been 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; +} + +/*========================================== + * @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) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charundisguise <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (pc_isGM (sd) >= pc_isGM (pl_sd)) + { // you can undisguise only lower or same level + if (pl_sd->disguise) + { + clif_clearchar (&pl_sd->bl, 9); + pl_sd->disguise = 0; + pc_setpos (pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, + 3); + 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]; + + 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 (strcasecmp (new_email, "a@a.com") == 0) + { + clif_displaymessage (fd, msg_table[146]); // New email must be a real e-mail. + return -1; + } + else if (strcasecmp (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; + int type = 0, flag = 0, i; + + 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 + { + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth) + { + clif_specialeffect (&pl_sd->bl, type, flag); + clif_displaymessage (pl_sd->fd, msg_table[229]); // Your effect has changed. + } + } + } + + return 0; +} + +/*========================================== + * @charitemlist <character>: Displays the list of a player's items. + *------------------------------------------ + */ +int +atcommand_character_item_list (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[100], output[200], equipstr[100], outputtmp[200]; + + 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, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charitemlist <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; +} + +/*========================================== + * @charstoragelist <character>: Displays the items list of a player's storage. + *------------------------------------------ + */ +int +atcommand_character_storage_list (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[100], output[200], outputtmp[200]; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + memset (outputtmp, '\0', sizeof (outputtmp)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charitemlist <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 -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; +} + +/*========================================== + * @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) +{ + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, count, counter, counter2; + char character[100], output[200], outputtmp[200]; + + memset (character, '\0', sizeof (character)); + memset (output, '\0', sizeof (output)); + memset (outputtmp, '\0', sizeof (outputtmp)); + + if (!message || !*message || sscanf (message, "%99[^\n]", character) < 1) + { + clif_displaymessage (fd, + "Please, enter a player name (usage: @charitemlist <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_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 (output, + "------ Cart items list of '%s' ------", + pl_sd->status.name); + clif_displaymessage (fd, output); + } + if (pl_sd->status.cart[i].refine) + sprintf (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 (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, output); + memset (output, '\0', sizeof (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 (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 cart 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, 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) +{ + 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) +{ + 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; + + 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) +{ + 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) +{ + 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) +{ + char character[100]; + int x = 0, y = 0; + struct npc_data *nd = 0; + + if (sd == NULL) + return -1; + + if (!message || !*message) + return -1; + + memset (character, '\0', sizeof character); + + if (sscanf (message, "%d %d %99[^\n]", &x, &y, character) < 3) + return -1; + + nd = npc_name2id (character); + if (nd == NULL) + return -1; + + npc_enable (character, 0); + nd->bl.x = x; + nd->bl.y = y; + npc_enable (character, 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]; + char map[30], output[200]; + int x, y, ret; + + if (!message || !*message) + return -1; + + if (sscanf (message, "%99s %d %d[^\n]", map, &x, &y) < 3) + return -1; + + sprintf (w1, "%s,%d,%d", sd->mapname, sd->bl.x, sd->bl.y); + sprintf (w3, "%s%d%d%d%d", map, sd->bl.x, sd->bl.y, x, y); + sprintf (w4, "1,1,%s.gat,%d,%d", map, x, y); + + ret = npc_parse_warp (w1, "warp", w3, w4); + + sprintf (output, "New warp NPC => %s", w3); + + clif_displaymessage (fd, 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) +{ +#if 0 + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + if ((pl_sd = map_nick2sd ((char *) message)) != NULL) + pc_follow (sd, pl_sd->bl.id); + else + return 1; +#endif + + /* + * Command disabled - it's incompatible with the TMW + * client. + */ + clif_displaymessage (fd, "@follow command not available"); + + return 0; + +} + +/*========================================== + * @chareffect by [MouseJstr] + * + * Create a effect localized on another character + *------------------------------------------ + */ +int +atcommand_chareffect (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; + + if (!message || !*message + || sscanf (message, "%d %s", &type, target) != 2) + { + clif_displaymessage (fd, "usage: @chareffect <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; +} + +/*========================================== + * @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; + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].amount) + { + if (sd->status.inventory[i].equip != 0) + pc_unequipitem (sd, i, 0); + 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; + + 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, 0); + 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 (sd->state.storage_flag != 1) + { //Open storage. + switch (storage_storageopen (sd)) + { + case 2: //Try again + clif_displaymessage (fd, "run this command again.."); + return 0; + case 1: //Failure + clif_displaymessage (fd, + "You can't open the storage currently."); + return 1; + } + } + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].amount) + { + if (sd->status.inventory[i].equip != 0) + pc_unequipitem (sd, i, 0); + 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; + + 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, 0); + 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 = 0, idx = 0; + if (!message || !*message) + return -1; + skillen = strlen (message); + while (skill_names[idx].id != 0) + { + if ((strncasecmp (skill_names[idx].name, message, skillen) == 0) || + (strncasecmp (skill_names[idx].desc, message, skillen) == 0)) + { + char output[255]; + sprintf (output, "skill %d: %s", skill_names[idx].id, + skill_names[idx].desc); + clif_displaymessage (fd, output); + } + idx++; + } + 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; + int inf; + char target[255]; + + if (!message || !*message) + return -1; + if (sscanf (message, "%d %d %s", &skillnum, &skilllv, target) != 3) + { + clif_displaymessage (fd, + "Usage: @useskill <skillnum> <skillv> <target>"); + return -1; + } + if ((pl_sd = map_nick2sd (target)) == NULL) + { + return -1; + } + + inf = skill_get_inf (skillnum); + + if ((inf == 2) || (inf == 1)) + 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; +} + +/*========================================== + * It is made to rain. + *------------------------------------------ + */ +int +atcommand_rain (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int effno = 0; + effno = 161; + nullpo_retr (-1, sd); + if (effno < 0 || map[sd->bl.m].flag.rain) + return -1; + + map[sd->bl.m].flag.rain = 1; + clif_specialeffect (&sd->bl, effno, 2); + return 0; +} + +/*========================================== + * It is made to snow. + *------------------------------------------ + */ +int +atcommand_snow (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int effno = 0; + effno = 162; + nullpo_retr (-1, sd); + if (effno < 0 || map[sd->bl.m].flag.snow) + return -1; + + map[sd->bl.m].flag.snow = 1; + clif_specialeffect (&sd->bl, effno, 2); + 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) +{ + int effno = 0; + effno = 163; + nullpo_retr (-1, sd); + if (effno < 0 || map[sd->bl.m].flag.sakura) + return -1; + + map[sd->bl.m].flag.sakura = 1; + clif_specialeffect (&sd->bl, effno, 2); + return 0; +} + +/*========================================== + * Fog hangs over. + *------------------------------------------ + */ +int +atcommand_fog (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int effno = 0; + effno = 233; + nullpo_retr (-1, sd); + if (effno < 0 || map[sd->bl.m].flag.fog) + return -1; + + map[sd->bl.m].flag.fog = 1; + clif_specialeffect (&sd->bl, effno, 2); + + return 0; +} + +/*========================================== + * Fallen leaves fall. + *------------------------------------------ + */ +int +atcommand_leaves (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + int effno = 0; + effno = 333; + nullpo_retr (-1, sd); + if (effno < 0 || map[sd->bl.m].flag.leaves) + return -1; + + map[sd->bl.m].flag.leaves = 1; + clif_specialeffect (&sd->bl, effno, 2); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_summon (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char name[100]; + int mob_id = 0; + int x = 0; + int y = 0; + int id = 0; + struct mob_data *md; + unsigned int tick = gettick (); + + nullpo_retr (-1, sd); + + if (!message || !*message) + return -1; + if (sscanf (message, "%99s", name) < 1) + return -1; + + if ((mob_id = atoi (name)) == 0) + mob_id = mobdb_searchname (name); + if (mob_id == 0) + return -1; + + x = sd->bl.x + (MRAND (10) - 5); + y = sd->bl.y + (MRAND (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->state.special_mob_ai = 1; + md->mode = mob_db[md->mob_class].mode | 0x04; + md->deletetimer = add_timer (tick + 60000, mob_timer_delete, id, 0); + clif_misceffect (&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]; + + 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].type != AtCommand_None; i++) + if (strcasecmp (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[100]; + struct map_session_data *pl_sd; + + if (!message || !*message + || sscanf (message, "%d %s", &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; + + if (!message || !*message) + return -1; + if ((pl_sd = map_nick2sd ((char *) message)) != NULL) + { + trade_traderequest (sd, pl_sd->bl.id); + return 0; + } + return -1; +} + +/*=========================== + * @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; + if (!message || !*message) + return -1; + + if ((pl_sd = map_nick2sd ((char *) message)) != NULL) + { + if (pl_sd->sc_data[SC_NOCHAT].timer != -1) + { + skill_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; +} + +/* Magic atcommands by Fate */ + +static int magic_base = TMW_MAGIC; +#define magic_skills_nr 6 +static char *magic_skill_names[magic_skills_nr] = + { "magic", "life", "war", "transmute", "nature", "astral" }; + +int +atcommand_magic_info (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char buf[200]; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message || sscanf (message, "%99s", character) < 1) + { + clif_displaymessage (fd, "Usage: @magicinfo <char_name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + int i; + + sprintf (buf, "`%s' has the following magic skills:", character); + clif_displaymessage (fd, buf); + + for (i = 0; i < magic_skills_nr; i++) + { + sprintf (buf, "%d in %s", pl_sd->status.skill[i + magic_base].lv, + magic_skill_names[i]); + if (pl_sd->status.skill[i + magic_base].id == i + magic_base) + clif_displaymessage (fd, buf); + } + + return 0; + } + else + clif_displaymessage (fd, "Character not found."); + + return -1; +} + +static void set_skill (struct map_session_data *sd, int i, int level) +{ + sd->status.skill[i].id = level ? i : 0; + sd->status.skill[i].lv = level; +} + +int +atcommand_set_magic (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + char magic_type[20]; + int skill_index = -1; // 0: all + int value; + struct map_session_data *pl_sd; + + memset (character, '\0', sizeof (character)); + + if (!message || !*message + || sscanf (message, "%19s %i %99s", magic_type, &value, + character) < 1) + { + clif_displaymessage (fd, + "Usage: @setmagic <school> <value> <char-name>, where <school> is either `magic', one of the school names, or `all'."); + return -1; + } + + if (!strcasecmp ("all", magic_type)) + skill_index = 0; + else + { + int i; + for (i = 0; i < magic_skills_nr; i++) + { + if (!strcasecmp (magic_skill_names[i], magic_type)) + { + skill_index = i + magic_base; + break; + } + } + } + + if (skill_index == -1) + { + clif_displaymessage (fd, + "Incorrect school of magic. Use `magic', `nature', `life', `war', `transmute', `ether', or `all'."); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + int i; + if (skill_index == 0) + for (i = 0; i < magic_skills_nr; i++) + set_skill (pl_sd, i + magic_base, value); + else + set_skill (pl_sd, skill_index, value); + + clif_skillinfoblock (pl_sd); + return 0; + } + else + clif_displaymessage (fd, "Character not found."); + + return -1; +} + +int +atcommand_log (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + return 0; // only used for (implicit) logging +} + +int +atcommand_tee (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char *data = (char *)malloc (strlen (message) + 28); + strcpy (data, sd->status.name); + strcat (data, " : "); + strcat (data, message); + clif_message (&sd->bl, data); + return 0; +} + +int +atcommand_invisible (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + pc_invisibility (sd, 1); + return 0; +} + +int +atcommand_visible (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + pc_invisibility (sd, 0); + return 0; +} + +int atcommand_jump_iterate (const int fd, struct map_session_data *sd, + const char *command, const char *message, + struct map_session_data *(*get_start) (void), + struct map_session_data *(*get_next) (struct + map_session_data + * current)) +{ + char output[200]; + struct map_session_data *pl_sd; + + memset (output, '\0', sizeof (output)); + + pl_sd = (struct map_session_data *) map_id2bl (sd->followtarget); + + if (pl_sd) + pl_sd = get_next (pl_sd); + + if (!pl_sd) + pl_sd = get_start (); + + if (pl_sd == sd) + { + pl_sd = get_next (pl_sd); + if (!pl_sd) + pl_sd = get_start (); + } + + 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, + "You are not authorised to warp you to the map of this player."); + 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, + "You are not authorised to warp you from your actual map."); + return -1; + } + pc_setpos (sd, map[pl_sd->bl.m].name, pl_sd->bl.x, pl_sd->bl.y, 3); + sprintf (output, msg_table[4], pl_sd->status.name); // Jump to %s + clif_displaymessage (fd, output); + + sd->followtarget = pl_sd->bl.id; + + return 0; +} + +int +atcommand_iterate_forward_over_players (const int fd, + struct map_session_data *sd, + const char *command, + const char *message) +{ + return atcommand_jump_iterate (fd, sd, command, message, + map_get_first_session, + map_get_next_session); +} + +int +atcommand_iterate_backwards_over_players (const int fd, + struct map_session_data *sd, + const char *command, + const char *message) +{ + return atcommand_jump_iterate (fd, sd, command, message, + map_get_last_session, + map_get_prev_session); +} + +int atcommand_wgm (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + if (tmw_CheckChatSpam(sd, message)) + return 0; + tmw_GmHackMsg ("%s: %s", sd->status.name, message); + if (!pc_isGM (sd)) + clif_displaymessage (fd, "Message sent."); + + return 0; +} + + +int atcommand_skillpool_info (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + struct map_session_data *pl_sd; + + if (!message || !*message || sscanf (message, "%99s", character) < 1) + { + clif_displaymessage (fd, "Usage: @sp-info <char_name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + char buf[200]; + int pool_skills[MAX_SKILL_POOL]; + int pool_skills_nr = skill_pool (pl_sd, pool_skills); + int i; + + sprintf (buf, "Active skills %d out of %d for %s:", pool_skills_nr, + skill_pool_max (pl_sd), character); + clif_displaymessage (fd, buf); + for (i = 0; i < pool_skills_nr; ++i) + { + sprintf (buf, " - %s [%d]: power %d", skill_name (pool_skills[i]), + pool_skills[i], skill_power (pl_sd, pool_skills[i])); + clif_displaymessage (fd, buf); + } + + sprintf (buf, "Learned skills out of %d for %s:", + skill_pool_skills_size, character); + clif_displaymessage (fd, buf); + + for (i = 0; i < skill_pool_skills_size; ++i) + { + char *name = skill_name (skill_pool_skills[i]); + int lvl = pl_sd->status.skill[skill_pool_skills[i]].lv; + + if (lvl) + { + sprintf (buf, " - %s [%d]: lvl %d", name, + skill_pool_skills[i], lvl); + clif_displaymessage (fd, buf); + } + } + + } + else + clif_displaymessage (fd, "Character not found."); + + return 0; +} + +int atcommand_skillpool_focus (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + int skill; + struct map_session_data *pl_sd; + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &skill, character) < 1) + { + clif_displaymessage (fd, "Usage: @sp-focus <skill-nr> <char_name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (skill_pool_activate (pl_sd, skill)) + clif_displaymessage (fd, "Activation failed."); + else + clif_displaymessage (fd, "Activation successful."); + } + else + clif_displaymessage (fd, "Character not found."); + + return 0; +} + +int atcommand_skillpool_unfocus (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + int skill; + struct map_session_data *pl_sd; + + if (!message || !*message + || sscanf (message, "%d %99[^\n]", &skill, character) < 1) + { + clif_displaymessage (fd, "Usage: @sp-unfocus <skill-nr> <char_name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + if (skill_pool_deactivate (pl_sd, skill)) + clif_displaymessage (fd, "Deactivation failed."); + else + clif_displaymessage (fd, "Deactivation successful."); + } + else + clif_displaymessage (fd, "Character not found."); + + return 0; +} + +int atcommand_skill_learn (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + char character[100]; + int skill, level; + struct map_session_data *pl_sd; + + if (!message || !*message + || sscanf (message, "%d %d %99[^\n]", &skill, &level, character) < 1) + { + clif_displaymessage (fd, + "Usage: @skill-learn <skill-nr> <level> <char_name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) != NULL) + { + set_skill (pl_sd, skill, level); + clif_skillinfoblock (pl_sd); + } + else + clif_displaymessage (fd, "Character not found."); + + return 0; +} + +int atcommand_ipcheck (const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + struct sockaddr_in sai; + char output[200]; + char character[25]; + int i; + socklen_t sa_len = sizeof (struct sockaddr); + unsigned long ip; + + memset(character, '\0', sizeof(character)); + + if (sscanf (message, "%24[^\n]", character) < 1) + { + clif_displaymessage (fd, "Usage: @ipcheck <char name>"); + return -1; + } + + if ((pl_sd = map_nick2sd (character)) == NULL) + { + clif_displaymessage (fd, "Character not found."); + return -1; + } + + if (getpeername (pl_sd->fd, (struct sockaddr *)&sai, &sa_len)) + { + clif_displaymessage (fd, + "Guru Meditation Error: getpeername() failed"); + return -1; + } + + ip = sai.sin_addr.s_addr; + + // We now have the IP address of a character. + // Loop over all logged in sessions looking for matches. + + 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 (getpeername (pl_sd->fd, (struct sockaddr *)&sai, &sa_len)) + continue; + + // Is checking GM levels really needed here? + if (ip == sai.sin_addr.s_addr) + { + snprintf (output, sizeof(output), + "Name: %s | Location: %s %d %d", + pl_sd->status.name, pl_sd->mapname, + pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage (fd, output); + } + } + } + + clif_displaymessage (fd, "End of list"); + return 0; +} + +int atcommand_doomspot(const int fd, struct map_session_data *sd, + const char *command, const char *message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth && i != fd && sd->bl.m == pl_sd->bl.m + && sd->bl.x == pl_sd->bl.x && sd->bl.y == pl_sd->bl.y + && 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 + 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; +} diff --git a/src/map/atcommand.h b/src/map/atcommand.h deleted file mode 100644 index 0b141cb..0000000 --- a/src/map/atcommand.h +++ /dev/null @@ -1,226 +0,0 @@ -// $Id: atcommand.h 148 2004-09-30 14:05:37Z MouseJstr $ -#ifndef _ATCOMMAND_H_ -#define _ATCOMMAND_H_ - -#include "map.h" - -enum AtCommandType -{ - AtCommand_None = -1, - AtCommand_Broadcast = 0, - AtCommand_Setup, - AtCommand_LocalBroadcast, - AtCommand_MapMove, - AtCommand_ResetState, - AtCommand_CharWarp, - AtCommand_Warp, - AtCommand_Where, - AtCommand_Goto, - AtCommand_Jump, - AtCommand_Who, - AtCommand_WhoGroup, - AtCommand_WhoMap, - AtCommand_WhoMapGroup, - AtCommand_WhoGM, - AtCommand_Save, - AtCommand_Load, - AtCommand_Speed, - AtCommand_Storage, - AtCommand_GuildStorage, - AtCommand_Option, - AtCommand_Hide, - AtCommand_Die, - AtCommand_Kill, - AtCommand_Alive, - AtCommand_Kami, - AtCommand_KamiB, - AtCommand_Heal, - AtCommand_Item, - AtCommand_ItemReset, - AtCommand_ItemCheck, - AtCommand_BaseLevelUp, - AtCommand_JobLevelUp, - AtCommand_Help, - AtCommand_GM, - AtCommand_PvPOff, - AtCommand_PvPOn, - AtCommand_GvGOff, - AtCommand_GvGOn, - AtCommand_Model, - AtCommand_Go, - AtCommand_Spawn, - AtCommand_Monster, - AtCommand_KillMonster, - AtCommand_KillMonster2, - AtCommand_Produce, - AtCommand_Memo, - AtCommand_GAT, - AtCommand_Packet, - AtCommand_StatusPoint, - AtCommand_SkillPoint, - AtCommand_Zeny, - AtCommand_Param, - AtCommand_Strength, - AtCommand_Agility, - AtCommand_Vitality, - AtCommand_Intelligence, - AtCommand_Dexterity, - AtCommand_Luck, - AtCommand_GuildLevelUp, - AtCommand_Recall, - AtCommand_Revive, - AtCommand_CharacterStats, - AtCommand_CharacterStatsAll, - AtCommand_CharacterOption, - AtCommand_CharacterSave, - AtCommand_CharacterLoad, - AtCommand_Night, - AtCommand_Day, - AtCommand_Doom, - AtCommand_DoomMap, - AtCommand_Raise, - AtCommand_RaiseMap, - AtCommand_CharacterBaseLevel, - AtCommand_CharacterJobLevel, - AtCommand_Kick, - AtCommand_KickAll, - AtCommand_AllSkills, - AtCommand_QuestSkill, - AtCommand_CharQuestSkill, - AtCommand_LostSkill, - AtCommand_CharLostSkill, - AtCommand_Party, - AtCommand_Guild, - AtCommand_AgitStart, - AtCommand_AgitEnd, - AtCommand_MapExit, - AtCommand_IDSearch, - AtCommand_CharSkReset, - AtCommand_CharStReset, - AtCommand_CharReset, - //by chbrules - AtCommand_CharModel, - AtCommand_CharSKPoint, - AtCommand_CharSTPoint, - AtCommand_CharZeny, - AtCommand_RecallAll, - AtCommand_ReloadItemDB, - AtCommand_ReloadMobDB, - AtCommand_ReloadSkillDB, - AtCommand_ReloadScript, - AtCommand_ReloadGMDB, - AtCommand_MapInfo, - AtCommand_Dye, - AtCommand_HairStyle, - AtCommand_HairColor, - AtCommand_AllStats, - AtCommand_CharChangeSex, // by Yor - 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_GuildRecall, // by Yor - AtCommand_PartyRecall, // by Yor - AtCommand_Enablenpc, - AtCommand_Disablenpc, - AtCommand_ServerTime, // by Yor - AtCommand_CharDelItem, // by Yor - AtCommand_Jail, // by Yor - AtCommand_UnJail, // by Yor - AtCommand_Disguise, // [Valaris] - AtCommand_UnDisguise, // by Yor - AtCommand_IgnoreList, // by Yor - AtCommand_CharIgnoreList, // by Yor - AtCommand_InAll, // by Yor - AtCommand_ExAll, // by Yor - AtCommand_CharDisguise, // Kalaspuff - AtCommand_CharUnDisguise, // Kalaspuff - AtCommand_EMail, // by Yor - AtCommand_Hatch, - AtCommand_Effect, // by Apple - AtCommand_Char_Item_List, // by Yor - AtCommand_Char_Storage_List, // by Yor - 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_Chareffect, // by MouseJstr - AtCommand_Chardye, // by MouseJstr - AtCommand_Charhairstyle, // by MouseJstr - AtCommand_Charhaircolor, // 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_Fog, - AtCommand_Leaves, - AtCommand_AdjGmLvl, - AtCommand_AdjCmdLvl, - AtCommand_Trade, - AtCommand_UnMute, - AtCommand_CharWipe, - AtCommand_SetMagic, - AtCommand_MagicInfo, - AtCommand_Log, - AtCommand_Tee, - AtCommand_Invisible, - AtCommand_Visible, - AtCommand_IterateForward, - AtCommand_IterateBackward, - AtCommand_Wgm, - AtCommand_IpCheck, - AtCommand_ListNearby, // [fate] - AtCommand_DoomSpot, - // end - 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 (const int level, const char *message, - AtCommandInfo * info); -int get_atcommand_level (const AtCommandType type); - -char *msg_txt (int msg_number); // [Yor] - -int atcommand_item (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Valaris] -int atcommand_warp (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Yor] -int atcommand_spawn (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Valaris] -int atcommand_goto (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_config_read (const char *cfgName); -int msg_config_read (const char *cfgName); - -void log_atcommand (struct map_session_data *sd, const char *fmt, ...); -void gm_log (const char *fmt, ...); - -#endif diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp new file mode 100644 index 0000000..824a7cf --- /dev/null +++ b/src/map/atcommand.hpp @@ -0,0 +1,226 @@ +// $Id: atcommand.h 148 2004-09-30 14:05:37Z MouseJstr $ +#ifndef ATCOMMAND_HPP +#define ATCOMMAND_HPP + +#include "map.hpp" + +enum AtCommandType +{ + AtCommand_None = -1, + AtCommand_Broadcast = 0, + AtCommand_Setup, + AtCommand_LocalBroadcast, + AtCommand_MapMove, + AtCommand_ResetState, + AtCommand_CharWarp, + AtCommand_Warp, + AtCommand_Where, + AtCommand_Goto, + AtCommand_Jump, + AtCommand_Who, + AtCommand_WhoGroup, + AtCommand_WhoMap, + AtCommand_WhoMapGroup, + AtCommand_WhoGM, + AtCommand_Save, + AtCommand_Load, + AtCommand_Speed, + AtCommand_Storage, + AtCommand_GuildStorage, + AtCommand_Option, + AtCommand_Hide, + AtCommand_Die, + AtCommand_Kill, + AtCommand_Alive, + AtCommand_Kami, + AtCommand_KamiB, + AtCommand_Heal, + AtCommand_Item, + AtCommand_ItemReset, + AtCommand_ItemCheck, + AtCommand_BaseLevelUp, + AtCommand_JobLevelUp, + AtCommand_Help, + AtCommand_GM, + AtCommand_PvPOff, + AtCommand_PvPOn, + AtCommand_GvGOff, + AtCommand_GvGOn, + AtCommand_Model, + AtCommand_Go, + AtCommand_Spawn, + AtCommand_Monster, + AtCommand_KillMonster, + AtCommand_KillMonster2, + AtCommand_Produce, + AtCommand_Memo, + AtCommand_GAT, + AtCommand_Packet, + AtCommand_StatusPoint, + AtCommand_SkillPoint, + AtCommand_Zeny, + AtCommand_Param, + AtCommand_Strength, + AtCommand_Agility, + AtCommand_Vitality, + AtCommand_Intelligence, + AtCommand_Dexterity, + AtCommand_Luck, + AtCommand_GuildLevelUp, + AtCommand_Recall, + AtCommand_Revive, + AtCommand_CharacterStats, + AtCommand_CharacterStatsAll, + AtCommand_CharacterOption, + AtCommand_CharacterSave, + AtCommand_CharacterLoad, + AtCommand_Night, + AtCommand_Day, + AtCommand_Doom, + AtCommand_DoomMap, + AtCommand_Raise, + AtCommand_RaiseMap, + AtCommand_CharacterBaseLevel, + AtCommand_CharacterJobLevel, + AtCommand_Kick, + AtCommand_KickAll, + AtCommand_AllSkills, + AtCommand_QuestSkill, + AtCommand_CharQuestSkill, + AtCommand_LostSkill, + AtCommand_CharLostSkill, + AtCommand_Party, + AtCommand_Guild, + AtCommand_AgitStart, + AtCommand_AgitEnd, + AtCommand_MapExit, + AtCommand_IDSearch, + AtCommand_CharSkReset, + AtCommand_CharStReset, + AtCommand_CharReset, + //by chbrules + AtCommand_CharModel, + AtCommand_CharSKPoint, + AtCommand_CharSTPoint, + AtCommand_CharZeny, + AtCommand_RecallAll, + AtCommand_ReloadItemDB, + AtCommand_ReloadMobDB, + AtCommand_ReloadSkillDB, + AtCommand_ReloadScript, + AtCommand_ReloadGMDB, + AtCommand_MapInfo, + AtCommand_Dye, + AtCommand_HairStyle, + AtCommand_HairColor, + AtCommand_AllStats, + AtCommand_CharChangeSex, // by Yor + 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_GuildRecall, // by Yor + AtCommand_PartyRecall, // by Yor + AtCommand_Enablenpc, + AtCommand_Disablenpc, + AtCommand_ServerTime, // by Yor + AtCommand_CharDelItem, // by Yor + AtCommand_Jail, // by Yor + AtCommand_UnJail, // by Yor + AtCommand_Disguise, // [Valaris] + AtCommand_UnDisguise, // by Yor + AtCommand_IgnoreList, // by Yor + AtCommand_CharIgnoreList, // by Yor + AtCommand_InAll, // by Yor + AtCommand_ExAll, // by Yor + AtCommand_CharDisguise, // Kalaspuff + AtCommand_CharUnDisguise, // Kalaspuff + AtCommand_EMail, // by Yor + AtCommand_Hatch, + AtCommand_Effect, // by Apple + AtCommand_Char_Item_List, // by Yor + AtCommand_Char_Storage_List, // by Yor + 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_Chareffect, // by MouseJstr + AtCommand_Chardye, // by MouseJstr + AtCommand_Charhairstyle, // by MouseJstr + AtCommand_Charhaircolor, // 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_Fog, + AtCommand_Leaves, + AtCommand_AdjGmLvl, + AtCommand_AdjCmdLvl, + AtCommand_Trade, + AtCommand_UnMute, + AtCommand_CharWipe, + AtCommand_SetMagic, + AtCommand_MagicInfo, + AtCommand_Log, + AtCommand_Tee, + AtCommand_Invisible, + AtCommand_Visible, + AtCommand_IterateForward, + AtCommand_IterateBackward, + AtCommand_Wgm, + AtCommand_IpCheck, + AtCommand_ListNearby, // [fate] + AtCommand_DoomSpot, + // end + 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 (const int level, const char *message, + AtCommandInfo * info); +int get_atcommand_level (const AtCommandType type); + +char *msg_txt (int msg_number); // [Yor] + +int atcommand_item (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Valaris] +int atcommand_warp (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Yor] +int atcommand_spawn (const int fd, struct map_session_data *sd, const char *command, const char *message); // [Valaris] +int atcommand_goto (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_config_read (const char *cfgName); +int msg_config_read (const char *cfgName); + +void log_atcommand (struct map_session_data *sd, const char *fmt, ...); +void gm_log (const char *fmt, ...); + +#endif diff --git a/src/map/battle.c b/src/map/battle.c deleted file mode 100644 index 5e87b07..0000000 --- a/src/map/battle.c +++ /dev/null @@ -1,6282 +0,0 @@ -// $Id: battle.c,v 1.10 2004/09/29 21:08:17 Akitasha Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "battle.h" - -#include "../common/timer.h" -#include "../common/nullpo.h" - -#include "clif.h" -#include "guild.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "pc.h" -#include "skill.h" -#include "../common/socket.h" -#include "../common/mt_rand.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -int attr_fix_table[4][10][10]; - -struct Battle_Config battle_config; - -/*========================================== - * 二点間の距離を返す - * 戻りは整数で0以上 - *------------------------------------------ - */ -static int distance (int x0, int y0, int x1, int y1) -{ - int dx, dy; - - dx = abs (x0 - x1); - dy = abs (y0 - y1); - return dx > dy ? dx : dy; -} - -/*========================================== - * 自分をロックしている対象の数を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_counttargeted (struct block_list *bl, struct block_list *src, - int target_lv) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC) - return pc_counttargeted ((struct map_session_data *) bl, src, - target_lv); - else if (bl->type == BL_MOB) - return mob_counttargeted ((struct mob_data *) bl, src, target_lv); - return 0; -} - -/*========================================== - * 対象のClassを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_class (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->mob_class; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->status.pc_class; - else - return 0; -} - -/*========================================== - * 対象の方向を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_dir (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->dir; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->dir; - else - return 0; -} - -/*========================================== - * 対象のレベルを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_lv (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->stats[MOB_LV]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->status.base_level; - else - return 0; -} - -/*========================================== - * 対象の射程を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_range (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return mob_db[((struct mob_data *) bl)->mob_class].range; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->attackrange; - else - return 0; -} - -/*========================================== - * 対象のHPを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_hp (struct block_list *bl) -{ - nullpo_retr (1, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->hp; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->status.hp; - else - return 1; -} - -/*========================================== - * 対象のMHPを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_max_hp (struct block_list *bl) -{ - nullpo_retr (1, bl); - if (bl->type == BL_PC && ((struct map_session_data *) bl)) - return ((struct map_session_data *) bl)->status.max_hp; - else - { - struct status_change *sc_data = battle_get_sc_data (bl); - int max_hp = 1; - if (bl->type == BL_MOB && ((struct mob_data *) bl)) - { - max_hp = ((struct mob_data *) bl)->stats[MOB_MAX_HP]; - if (mob_db[((struct mob_data *) bl)->mob_class].mexp > 0) - { - if (battle_config.mvp_hp_rate != 100) - max_hp = (max_hp * battle_config.mvp_hp_rate) / 100; - } - else - { - if (battle_config.monster_hp_rate != 100) - max_hp = (max_hp * battle_config.monster_hp_rate) / 100; - } - } - if (sc_data) - { - if (sc_data[SC_APPLEIDUN].timer != -1) - max_hp += - ((5 + sc_data[SC_APPLEIDUN].val1 * 2 + - ((sc_data[SC_APPLEIDUN].val2 + 1) >> 1) + - sc_data[SC_APPLEIDUN].val3 / 10) * max_hp) / 100; - } - if (max_hp < 1) - max_hp = 1; - return max_hp; - } - return 1; -} - -/*========================================== - * 対象のStrを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_str (struct block_list *bl) -{ - int str = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && ((struct mob_data *) bl)) - str = ((struct mob_data *) bl)->stats[MOB_STR]; - else if (bl->type == BL_PC && ((struct map_session_data *) bl)) - return ((struct map_session_data *) bl)->paramc[0]; - - if (sc_data) - { - if (sc_data[SC_LOUD].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 - && bl->type != BL_PC) - str += 4; - if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) - { // ブレッシング - int race = battle_get_race (bl); - if (battle_check_undead (race, battle_get_elem_type (bl)) - || race == 6) - str >>= 1; // 悪 魔/不死 - else - str += sc_data[SC_BLESSING].val1; // その他 - } - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - str += 5; - } - if (str < 0) - str = 0; - return str; -} - -/*========================================== - * 対象のAgiを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ - -int battle_get_agi (struct block_list *bl) -{ - int agi = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - agi = ((struct mob_data *) bl)->stats[MOB_AGI]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - agi = ((struct map_session_data *) bl)->paramc[1]; - - if (sc_data) - { - if (sc_data[SC_INCREASEAGI].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1 && bl->type != BL_PC) // 速度増加(PCはpc.cで) - agi += 2 + sc_data[SC_INCREASEAGI].val1; - - if (sc_data[SC_CONCENTRATE].timer != -1 - && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) - agi += agi * (2 + sc_data[SC_CONCENTRATE].val1) / 100; - - if (sc_data[SC_DECREASEAGI].timer != -1) // 速度減少 - agi -= 2 + sc_data[SC_DECREASEAGI].val1; - - if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア - agi >>= 1; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - agi += 5; - } - if (agi < 0) - agi = 0; - return agi; -} - -/*========================================== - * 対象のVitを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_vit (struct block_list *bl) -{ - int vit = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - vit = ((struct mob_data *) bl)->stats[MOB_VIT]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - vit = ((struct map_session_data *) bl)->paramc[2]; - if (sc_data) - { - if (sc_data[SC_STRIPARMOR].timer != -1 && bl->type != BL_PC) - vit = vit * 60 / 100; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - vit += 5; - } - - if (vit < 0) - vit = 0; - return vit; -} - -/*========================================== - * 対象のIntを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_int (struct block_list *bl) -{ - int int_ = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - int_ = ((struct mob_data *) bl)->stats[MOB_INT]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - int_ = ((struct map_session_data *) bl)->paramc[3]; - - if (sc_data) - { - if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) - { // ブレッシング - int race = battle_get_race (bl); - if (battle_check_undead (race, battle_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_ * 60 / 100; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - int_ += 5; - } - if (int_ < 0) - int_ = 0; - return int_; -} - -/*========================================== - * 対象のDexを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_dex (struct block_list *bl) -{ - int dex = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - dex = ((struct mob_data *) bl)->stats[MOB_DEX]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - dex = ((struct map_session_data *) bl)->paramc[4]; - - if (sc_data) - { - if (sc_data[SC_CONCENTRATE].timer != -1 - && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) - dex += dex * (2 + sc_data[SC_CONCENTRATE].val1) / 100; - - if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) - { // ブレッシング - int race = battle_get_race (bl); - if (battle_check_undead (race, battle_get_elem_type (bl)) - || race == 6) - dex >>= 1; // 悪 魔/不死 - else - dex += sc_data[SC_BLESSING].val1; // その他 - } - - if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア - dex >>= 1; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - dex += 5; - } - if (dex < 0) - dex = 0; - return dex; -} - -/*========================================== - * 対象のLukを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_luk (struct block_list *bl) -{ - int luk = 0; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - luk = ((struct mob_data *) bl)->stats[MOB_LUK]; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - luk = ((struct map_session_data *) bl)->paramc[5]; - - if (sc_data) - { - if (sc_data[SC_GLORIA].timer != -1 && bl->type != BL_PC) // グロリア(PCはpc.cで) - luk += 30; - if (sc_data[SC_CURSE].timer != -1) // 呪い - luk = 0; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - luk += 5; - } - if (luk < 0) - luk = 0; - return luk; -} - -/*========================================== - * 対象のFleeを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_flee (struct block_list *bl) -{ - int flee = 1; - struct status_change *sc_data; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - flee = ((struct map_session_data *) bl)->flee; - else - flee = battle_get_agi (bl) + battle_get_lv (bl); - - if (sc_data) - { - if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) - flee += - flee * (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 + - (sc_data[SC_WHISTLE].val3 >> 16)) / 100; - if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) - flee -= flee * 25 / 100; - if (sc_data[SC_WINDWALK].timer != -1 && bl->type != BL_PC) // ウィンドウォーク - flee += flee * (sc_data[SC_WINDWALK].val2) / 100; - if (sc_data[SC_SPIDERWEB].timer != -1 && bl->type != BL_PC) //スパイダーウェブ - flee -= flee * 50 / 100; - - if (battle_is_unarmed (bl)) - flee += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 - flee += skill_power_bl (bl, TMW_SPEED) >> 3; - } - if (flee < 1) - flee = 1; - return flee; -} - -/*========================================== - * 対象のHitを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_hit (struct block_list *bl) -{ - int hit = 1; - struct status_change *sc_data; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - hit = ((struct map_session_data *) bl)->hit; - else - hit = battle_get_dex (bl) + battle_get_lv (bl); - - if (sc_data) - { - if (sc_data[SC_HUMMING].timer != -1 && bl->type != BL_PC) // - hit += - hit * (sc_data[SC_HUMMING].val1 * 2 + - sc_data[SC_HUMMING].val2 + - sc_data[SC_HUMMING].val3) / 100; - if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) // 呪い - hit -= hit * 25 / 100; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト - hit += 3 * (sc_data[SC_TRUESIGHT].val1); - if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション - hit += (hit * (10 * (sc_data[SC_CONCENTRATION].val1))) / 100; - - if (battle_is_unarmed (bl)) - hit += (skill_power_bl (bl, TMW_BRAWLING) >> 4); // +12 for 200 - } - if (hit < 1) - hit = 1; - return hit; -} - -/*========================================== - * 対象の完全回避を返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_flee2 (struct block_list *bl) -{ - int flee2 = 1; - struct status_change *sc_data; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - { - flee2 = battle_get_luk (bl) + 10; - flee2 += - ((struct map_session_data *) bl)->flee2 - - (((struct map_session_data *) bl)->paramc[5] + 10); - } - else - flee2 = battle_get_luk (bl) + 1; - - if (sc_data) - { - if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) - flee2 += (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 - + (sc_data[SC_WHISTLE].val3 & 0xffff)) * 10; - - if (battle_is_unarmed (bl)) - flee2 += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 - flee2 += skill_power_bl (bl, TMW_SPEED) >> 3; - } - if (flee2 < 1) - flee2 = 1; - return flee2; -} - -/*========================================== - * 対象のクリティカルを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_critical (struct block_list *bl) -{ - int critical = 1; - struct status_change *sc_data; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - { - critical = battle_get_luk (bl) * 2 + 10; - critical += - ((struct map_session_data *) bl)->critical - - ((((struct map_session_data *) bl)->paramc[5] * 3) + 10); - } - else - critical = battle_get_luk (bl) * 3 + 1; - - if (sc_data) - { - if (sc_data[SC_FORTUNE].timer != -1 && bl->type != BL_PC) - critical += - (10 + sc_data[SC_FORTUNE].val1 + sc_data[SC_FORTUNE].val2 + - sc_data[SC_FORTUNE].val3) * 10; - if (sc_data[SC_EXPLOSIONSPIRITS].timer != -1 && bl->type != BL_PC) - critical += sc_data[SC_EXPLOSIONSPIRITS].val2; - if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) //トゥルーサイト - critical += critical * sc_data[SC_TRUESIGHT].val1 / 100; - } - if (critical < 1) - critical = 1; - return critical; -} - -/*========================================== - * base_atkの取得 - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_baseatk (struct block_list *bl) -{ - struct status_change *sc_data; - int batk = 1; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - batk = ((struct map_session_data *) bl)->base_atk; //設定されているbase_atk - else - { //それ以外なら - int str, dstr; - str = battle_get_str (bl); //STR - dstr = str / 10; - batk = dstr * dstr + str; //base_atkを計算する - } - if (sc_data) - { //状態異常あり - if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) //PCでプロボック(SM_PROVOKE)状態 - batk = batk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; //base_atk増加 - if (sc_data[SC_CURSE].timer != -1) //呪われていたら - batk -= batk * 25 / 100; //base_atkが25%減少 - if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション - batk += batk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; - } - if (batk < 1) - batk = 1; //base_atkは最低でも1 - return batk; -} - -/*========================================== - * 対象のAtkを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_atk (struct block_list *bl) -{ - struct status_change *sc_data; - int atk = 0; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - atk = ((struct map_session_data *) bl)->watk; - else if (bl->type == BL_MOB && (struct mob_data *) bl) - atk = ((struct mob_data *) bl)->stats[MOB_ATK1]; - - if (sc_data) - { - if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) - atk = atk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; - if (sc_data[SC_CURSE].timer != -1) - atk -= atk * 25 / 100; - if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション - atk += atk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; - } - if (atk < 0) - atk = 0; - return atk; -} - -/*========================================== - * 対象の左手Atkを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_atk_ (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - { - int atk = ((struct map_session_data *) bl)->watk_; - - if (((struct map_session_data *) bl)->sc_data[SC_CURSE].timer != -1) - atk -= atk * 25 / 100; - return atk; - } - else - return 0; -} - -/*========================================== - * 対象のAtk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_atk2 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->watk2; - else - { - struct status_change *sc_data = battle_get_sc_data (bl); - int atk2 = 0; - if (bl->type == BL_MOB && (struct mob_data *) bl) - atk2 = ((struct mob_data *) bl)->stats[MOB_ATK2]; - if (sc_data) - { - if (sc_data[SC_IMPOSITIO].timer != -1) - atk2 += sc_data[SC_IMPOSITIO].val1 * 5; - if (sc_data[SC_PROVOKE].timer != -1) - atk2 = atk2 * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; - if (sc_data[SC_CURSE].timer != -1) - atk2 -= atk2 * 25 / 100; - if (sc_data[SC_DRUMBATTLE].timer != -1) - atk2 += sc_data[SC_DRUMBATTLE].val2; - if (sc_data[SC_NIBELUNGEN].timer != -1 - && (battle_get_element (bl) / 10) >= 8) - atk2 += sc_data[SC_NIBELUNGEN].val2; - if (sc_data[SC_STRIPWEAPON].timer != -1) - atk2 = atk2 * 90 / 100; - if (sc_data[SC_CONCENTRATION].timer != -1) //コンセントレーション - atk2 += atk2 * (5 * sc_data[SC_CONCENTRATION].val1) / 100; - } - - if (atk2 < 0) - atk2 = 0; - return atk2; - } - return 0; -} - -/*========================================== - * 対象の左手Atk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_atk_2 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC) - return ((struct map_session_data *) bl)->watk_2; - else - return 0; -} - -/*========================================== - * 対象のMAtk1を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_matk1 (struct block_list *bl) -{ - struct status_change *sc_data; - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB) - { - int matk, int_ = battle_get_int (bl); - matk = int_ + (int_ / 5) * (int_ / 5); - - if (sc_data) - if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) - matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; - return matk; - } - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->matk1; - else - return 0; -} - -/*========================================== - * 対象のMAtk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_matk2 (struct block_list *bl) -{ - struct status_change *sc_data = battle_get_sc_data (bl); - nullpo_retr (0, bl); - if (bl->type == BL_MOB) - { - int matk, int_ = battle_get_int (bl); - matk = int_ + (int_ / 7) * (int_ / 7); - - if (sc_data) - if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) - matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; - return matk; - } - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->matk2; - else - return 0; -} - -/*========================================== - * 対象のDefを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_def (struct block_list *bl) -{ - struct status_change *sc_data; - int def = 0, skilltimer = -1, skillid = 0; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - { - def = ((struct map_session_data *) bl)->def; - skilltimer = ((struct map_session_data *) bl)->skilltimer; - skillid = ((struct map_session_data *) bl)->skillid; - } - else if (bl->type == BL_MOB && (struct mob_data *) bl) - { - def = ((struct mob_data *) bl)->stats[MOB_DEF]; - skilltimer = ((struct mob_data *) bl)->skilltimer; - skillid = ((struct mob_data *) bl)->skillid; - } - - if (def < 1000000) - { - if (sc_data) - { - //キーピング時はDEF100 - if (sc_data[SC_KEEPING].timer != -1) - def = 100; - //プロボック時は減算 - if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) - def = (def * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; - //戦太鼓の響き時は加算 - if (sc_data[SC_DRUMBATTLE].timer != -1 && bl->type != BL_PC) - def += sc_data[SC_DRUMBATTLE].val3; - //毒にかかっている時は減算 - if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) - def = def * 75 / 100; - //ストリップシールド時は減算 - if (sc_data[SC_STRIPSHIELD].timer != -1 && bl->type != BL_PC) - def = def * 85 / 100; - //シグナムクルシス時は減算 - if (sc_data[SC_SIGNUMCRUCIS].timer != -1 && bl->type != BL_PC) - def = def * (100 - sc_data[SC_SIGNUMCRUCIS].val2) / 100; - //永遠の混沌時はDEF0になる - if (sc_data[SC_ETERNALCHAOS].timer != -1 && bl->type != BL_PC) - def = 0; - //凍結、石化時は右シフト - if (sc_data[SC_FREEZE].timer != -1 - || (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0)) - def >>= 1; - //コンセントレーション時は減算 - if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) - def = - (def * (100 - 5 * sc_data[SC_CONCENTRATION].val1)) / 100; - } - //詠唱中は詠唱時減算率に基づいて減算 - if (skilltimer != -1) - { - int def_rate = skill_get_castdef (skillid); - if (def_rate != 0) - def = (def * (100 - def_rate)) / 100; - } - } - if (def < 0) - def = 0; - return def; -} - -/*========================================== - * 対象のMDefを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_mdef (struct block_list *bl) -{ - struct status_change *sc_data; - int mdef = 0; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - mdef = ((struct map_session_data *) bl)->mdef; - else if (bl->type == BL_MOB && (struct mob_data *) bl) - mdef = ((struct mob_data *) bl)->stats[MOB_MDEF]; - - if (mdef < 1000000) - { - if (sc_data) - { - //バリアー状態時はMDEF100 - if (mdef < 90 && sc_data[SC_MBARRIER].timer != -1) - { - mdef += sc_data[SC_MBARRIER].val1; - if (mdef > 90) - mdef = 90; - } - if (sc_data[SC_BARRIER].timer != -1) - mdef = 100; - //凍結、石化時は1.25倍 - if (sc_data[SC_FREEZE].timer != -1 - || (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0)) - mdef = mdef * 125 / 100; - if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) - mdef -= (mdef * 6 * sc_data[SC_MINDBREAKER].val1) / 100; - } - } - if (mdef < 0) - mdef = 0; - return mdef; -} - -/*========================================== - * 対象のDef2を返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int battle_get_def2 (struct block_list *bl) -{ - struct status_change *sc_data; - int def2 = 1; - - nullpo_retr (1, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_PC) - def2 = ((struct map_session_data *) bl)->def2; - else if (bl->type == BL_MOB) - def2 = ((struct mob_data *) bl)->stats[MOB_VIT]; - - if (sc_data) - { - if (sc_data[SC_ANGELUS].timer != -1 && bl->type != BL_PC) - def2 = def2 * (110 + 5 * sc_data[SC_ANGELUS].val1) / 100; - if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) - def2 = (def2 * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; - if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) - def2 = def2 * 75 / 100; - //コンセントレーション時は減算 - if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) - def2 = def2 * (100 - 5 * sc_data[SC_CONCENTRATION].val1) / 100; - } - if (def2 < 1) - def2 = 1; - return def2; -} - -/*========================================== - * 対象のMDef2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int battle_get_mdef2 (struct block_list *bl) -{ - int mdef2 = 0; - struct status_change *sc_data = battle_get_sc_data (bl); - - nullpo_retr (0, bl); - if (bl->type == BL_MOB) - mdef2 = - ((struct mob_data *) bl)->stats[MOB_INT] + - (((struct mob_data *) bl)->stats[MOB_VIT] >> 1); - else if (bl->type == BL_PC) - mdef2 = - ((struct map_session_data *) bl)->mdef2 + - (((struct map_session_data *) bl)->paramc[2] >> 1); - if (sc_data) - { - if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) - mdef2 -= (mdef2 * 6 * sc_data[SC_MINDBREAKER].val1) / 100; - } - if (mdef2 < 0) - mdef2 = 0; - return mdef2; -} - -/*========================================== - * 対象のSpeed(移動速度)を返す(汎用) - * 戻りは整数で1以上 - * Speedは小さいほうが移動速度が速い - *------------------------------------------ - */ -int battle_get_speed (struct block_list *bl) -{ - nullpo_retr (1000, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->speed; - else - { - struct status_change *sc_data = battle_get_sc_data (bl); - int speed = 1000; - if (bl->type == BL_MOB && (struct mob_data *) bl) - speed = ((struct mob_data *) bl)->stats[MOB_SPEED]; - - if (sc_data) - { - //速度増加時は25%減算 - if (sc_data[SC_INCREASEAGI].timer != -1 - && sc_data[SC_DONTFORGETME].timer == -1) - speed -= speed * 25 / 100; - //速度減少時は25%加算 - if (sc_data[SC_DECREASEAGI].timer != -1) - speed = speed * 125 / 100; - //クァグマイア時は50%加算 - if (sc_data[SC_QUAGMIRE].timer != -1) - speed = speed * 3 / 2; - //私を忘れないで…時は加算 - if (sc_data[SC_DONTFORGETME].timer != -1) - speed = - speed * (100 + sc_data[SC_DONTFORGETME].val1 * 2 + - sc_data[SC_DONTFORGETME].val2 + - (sc_data[SC_DONTFORGETME].val3 & 0xffff)) / 100; - //金剛時は25%加算 - if (sc_data[SC_STEELBODY].timer != -1) - speed = speed * 125 / 100; - //ディフェンダー時は加算 - if (sc_data[SC_DEFENDER].timer != -1) - speed = (speed * (155 - sc_data[SC_DEFENDER].val1 * 5)) / 100; - //踊り状態は4倍遅い - if (sc_data[SC_DANCING].timer != -1) - speed *= 4; - //呪い時は450加算 - if (sc_data[SC_CURSE].timer != -1) - speed = speed + 450; - //ウィンドウォーク時はLv*2%減算 - if (sc_data[SC_WINDWALK].timer != -1) - speed -= (speed * (sc_data[SC_WINDWALK].val1 * 2)) / 100; - } - if (speed < 1) - speed = 1; - return speed; - } - - return 1000; -} - -/*========================================== - * 対象のaDelay(攻撃時ディレイ)を返す(汎用) - * aDelayは小さいほうが攻撃速度が速い - *------------------------------------------ - */ -int battle_get_adelay (struct block_list *bl) -{ - nullpo_retr (4000, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return (((struct map_session_data *) bl)->aspd << 1); - else - { - struct status_change *sc_data = battle_get_sc_data (bl); - int adelay = 4000, aspd_rate = 100, i; - if (bl->type == BL_MOB && (struct mob_data *) bl) - adelay = ((struct mob_data *) bl)->stats[MOB_ADELAY]; - - if (sc_data) - { - //ツーハンドクイッケン使用時でクァグマイアでも私を忘れないで…でもない時は3割減算 - if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ - aspd_rate -= 30; - //アドレナリンラッシュ使用時でツーハンドクイッケンでもクァグマイアでも私を忘れないで…でもない時は - if (sc_data[SC_ADRENALINE].timer != -1 - && sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sc_data[SC_QUAGMIRE].timer == -1 - && sc_data[SC_DONTFORGETME].timer == -1) - { // アドレナリンラッシュ - //使用者とパーティメンバーで格差が出る設定でなければ3割減算 - if (sc_data[SC_ADRENALINE].val2 - || !battle_config.party_skill_penaly) - aspd_rate -= 30; - //そうでなければ2.5割減算 - else - aspd_rate -= 25; - } - //スピアクィッケン時は減算 - if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン - aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; - //夕日のアサシンクロス時は減算 - if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス - sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sc_data[SC_ADRENALINE].timer == -1 - && sc_data[SC_SPEARSQUICKEN].timer == -1 - && sc_data[SC_DONTFORGETME].timer == -1) - aspd_rate -= - 5 + sc_data[SC_ASSNCROS].val1 + - sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; - //私を忘れないで…時は加算 - if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで - aspd_rate += - sc_data[SC_DONTFORGETME].val1 * 3 + - sc_data[SC_DONTFORGETME].val2 + - (sc_data[SC_DONTFORGETME].val3 >> 16); - //金剛時25%加算 - if (sc_data[SC_STEELBODY].timer != -1) // 金剛 - aspd_rate += 25; - //増速ポーション使用時は減算 - if (sc_data[i = SC_SPEEDPOTION2].timer != -1 - || sc_data[i = SC_SPEEDPOTION1].timer != -1 - || sc_data[i = SC_SPEEDPOTION0].timer != -1) - aspd_rate -= sc_data[i].val1; - // Fate's `haste' spell works the same as the above - if (sc_data[SC_HASTE].timer != -1) - aspd_rate -= sc_data[SC_HASTE].val1; - //ディフェンダー時は加算 - if (sc_data[SC_DEFENDER].timer != -1) - adelay += (1100 - sc_data[SC_DEFENDER].val1 * 100); - } - - 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; - } - return 4000; -} - -int battle_get_amotion (struct block_list *bl) -{ - nullpo_retr (2000, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->amotion; - else - { - struct status_change *sc_data = battle_get_sc_data (bl); - int amotion = 2000, aspd_rate = 100, i; - if (bl->type == BL_MOB && (struct mob_data *) bl) - amotion = mob_db[((struct mob_data *) bl)->mob_class].amotion; - - if (sc_data) - { - if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ - aspd_rate -= 30; - if (sc_data[SC_ADRENALINE].timer != -1 - && sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sc_data[SC_QUAGMIRE].timer == -1 - && sc_data[SC_DONTFORGETME].timer == -1) - { // アドレナリンラッシュ - if (sc_data[SC_ADRENALINE].val2 - || !battle_config.party_skill_penaly) - aspd_rate -= 30; - else - aspd_rate -= 25; - } - if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン - aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; - if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス - sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sc_data[SC_ADRENALINE].timer == -1 - && sc_data[SC_SPEARSQUICKEN].timer == -1 - && sc_data[SC_DONTFORGETME].timer == -1) - aspd_rate -= - 5 + sc_data[SC_ASSNCROS].val1 + - sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; - if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで - aspd_rate += - sc_data[SC_DONTFORGETME].val1 * 3 + - sc_data[SC_DONTFORGETME].val2 + - (sc_data[SC_DONTFORGETME].val3 >> 16); - if (sc_data[SC_STEELBODY].timer != -1) // 金剛 - aspd_rate += 25; - if (sc_data[i = SC_SPEEDPOTION2].timer != -1 - || sc_data[i = SC_SPEEDPOTION1].timer != -1 - || sc_data[i = SC_SPEEDPOTION0].timer != -1) - aspd_rate -= sc_data[i].val1; - if (sc_data[SC_HASTE].timer != -1) - aspd_rate -= sc_data[SC_HASTE].val1; - if (sc_data[SC_DEFENDER].timer != -1) - amotion += (550 - sc_data[SC_DEFENDER].val1 * 50); - } - - 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 battle_get_dmotion (struct block_list *bl) -{ - int ret; - struct status_change *sc_data; - - nullpo_retr (0, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - { - ret = mob_db[((struct mob_data *) bl)->mob_class].dmotion; - if (battle_config.monster_damage_delay_rate != 100) - ret = ret * battle_config.monster_damage_delay_rate / 400; - } - else if (bl->type == BL_PC && (struct map_session_data *) bl) - { - ret = ((struct map_session_data *) bl)->dmotion; - if (battle_config.pc_damage_delay_rate != 100) - ret = ret * battle_config.pc_damage_delay_rate / 400; - } - else - return 2000; - - if ((sc_data && sc_data[SC_ENDURE].timer != -1) || - (bl->type == BL_PC - && ((struct map_session_data *) bl)->special_state.infinite_endure)) - ret = 0; - - return ret; -} - -int battle_get_element (struct block_list *bl) -{ - int ret = 20; - struct status_change *sc_data; - - nullpo_retr (ret, bl); - sc_data = battle_get_sc_data (bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) // 10の位=Lv*2、1の位=属性 - ret = ((struct mob_data *) bl)->def_ele; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - ret = 20 + ((struct map_session_data *) bl)->def_ele; // 防御属性Lv1 - - if (sc_data) - { - if (sc_data[SC_BENEDICTIO].timer != -1) // 聖体降福 - ret = 26; - if (sc_data[SC_FREEZE].timer != -1) // 凍結 - ret = 21; - if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) - ret = 22; - } - - return ret; -} - -int battle_get_attack_element (struct block_list *bl) -{ - int ret = 0; - struct status_change *sc_data = battle_get_sc_data (bl); - - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - ret = 0; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - ret = ((struct map_session_data *) bl)->atk_ele; - - if (sc_data) - { - if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン - ret = 1; - if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン - ret = 2; - if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー - ret = 3; - if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー - ret = 4; - if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン - ret = 5; - if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ - ret = 6; - } - - return ret; -} - -int battle_get_attack_element2 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - { - int ret = ((struct map_session_data *) bl)->atk_ele_; - struct status_change *sc_data = - ((struct map_session_data *) bl)->sc_data; - - if (sc_data) - { - if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン - ret = 1; - if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン - ret = 2; - if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー - ret = 3; - if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー - ret = 4; - if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン - ret = 5; - if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ - ret = 6; - } - return ret; - } - return 0; -} - -int battle_get_party_id (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->status.party_id; - else if (bl->type == BL_MOB && (struct mob_data *) bl) - { - struct mob_data *md = (struct mob_data *) bl; - if (md->master_id > 0) - return -md->master_id; - return -md->bl.id; - } - else if (bl->type == BL_SKILL && (struct skill_unit *) bl) - return ((struct skill_unit *) bl)->group->party_id; - else - return 0; -} - -int battle_get_guild_id (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->status.guild_id; - else if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->mob_class; - else if (bl->type == BL_SKILL && (struct skill_unit *) bl) - return ((struct skill_unit *) bl)->group->guild_id; - else - return 0; -} - -int battle_get_race (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return mob_db[((struct mob_data *) bl)->mob_class].race; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return 7; - else - return 0; -} - -int battle_get_size (struct block_list *bl) -{ - nullpo_retr (1, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return mob_db[((struct mob_data *) bl)->mob_class].size; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return 1; - else - return 1; -} - -int battle_get_mode (struct block_list *bl) -{ - nullpo_retr (0x01, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return mob_db[((struct mob_data *) bl)->mob_class].mode; - else - return 0x01; // とりあえず動くということで1 -} - -int battle_get_mexp (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - { - const struct mob_data *mob = (struct mob_data *) bl; - const int retval = - (mob_db[mob->mob_class].mexp * - (int) (mob->stats[MOB_XP_BONUS])) >> MOB_XP_BONUS_SHIFT; - fprintf (stderr, "Modifier of %x: -> %d\n", mob->stats[MOB_XP_BONUS], - retval); - return retval; - } - else - return 0; -} - -int battle_get_stat (int stat_id /* SP_VIT or similar */ , - struct block_list *bl) -{ - switch (stat_id) - { - case SP_STR: - return battle_get_str (bl); - case SP_AGI: - return battle_get_agi (bl); - case SP_DEX: - return battle_get_dex (bl); - case SP_VIT: - return battle_get_vit (bl); - case SP_INT: - return battle_get_int (bl); - case SP_LUK: - return battle_get_luk (bl); - default: - return 0; - } -} - -// StatusChange系の所得 -struct status_change *battle_get_sc_data (struct block_list *bl) -{ - nullpo_retr (NULL, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return ((struct mob_data *) bl)->sc_data; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return ((struct map_session_data *) bl)->sc_data; - return NULL; -} - -short *battle_get_sc_count (struct block_list *bl) -{ - nullpo_retr (NULL, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return &((struct mob_data *) bl)->sc_count; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return &((struct map_session_data *) bl)->sc_count; - return NULL; -} - -short *battle_get_opt1 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return &((struct mob_data *) bl)->opt1; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return &((struct map_session_data *) bl)->opt1; - else if (bl->type == BL_NPC && (struct npc_data *) bl) - return &((struct npc_data *) bl)->opt1; - return 0; -} - -short *battle_get_opt2 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return &((struct mob_data *) bl)->opt2; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return &((struct map_session_data *) bl)->opt2; - else if (bl->type == BL_NPC && (struct npc_data *) bl) - return &((struct npc_data *) bl)->opt2; - return 0; -} - -short *battle_get_opt3 (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return &((struct mob_data *) bl)->opt3; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return &((struct map_session_data *) bl)->opt3; - else if (bl->type == BL_NPC && (struct npc_data *) bl) - return &((struct npc_data *) bl)->opt3; - return 0; -} - -short *battle_get_option (struct block_list *bl) -{ - nullpo_retr (0, bl); - if (bl->type == BL_MOB && (struct mob_data *) bl) - return &((struct mob_data *) bl)->option; - else if (bl->type == BL_PC && (struct map_session_data *) bl) - return &((struct map_session_data *) bl)->status.option; - else if (bl->type == BL_NPC && (struct npc_data *) bl) - return &((struct npc_data *) bl)->option; - return 0; -} - -//------------------------------------------------------------------- - -// ダメージの遅延 -struct battle_delay_damage_ -{ - struct block_list *src, *target; - int damage; - int flag; -}; -void battle_delay_damage_sub (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct battle_delay_damage_ *dat = (struct battle_delay_damage_ *) data; - if (dat && map_id2bl (id) == dat->src && dat->target->prev != NULL) - battle_damage (dat->src, dat->target, dat->damage, dat->flag); - free (dat); -} - -int battle_delay_damage (unsigned int tick, struct block_list *src, - struct block_list *target, int damage, int flag) -{ - struct battle_delay_damage_ *dat; - CREATE (dat, struct battle_delay_damage_, 1); - - nullpo_retr (0, src); - nullpo_retr (0, target); - - dat->src = src; - dat->target = target; - dat->damage = damage; - dat->flag = flag; - 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 = battle_get_sc_data (target); - short *sc_count; - int i; - - nullpo_retr (0, target); //blはNULLで呼ばれることがあるので他でチェック - - if (damage == 0) - return 0; - - if (target->prev == NULL) - return 0; - - if (bl) - { - if (bl->prev == NULL) - return 0; - - if (bl->type == BL_PC) - sd = (struct map_session_data *) bl; - } - - if (damage < 0) - return battle_heal (bl, target, -damage, 0, flag); - - if (!flag && (sc_count = battle_get_sc_count (target)) != NULL - && *sc_count > 0) - { - // 凍結、石化、睡眠を消去 - if (sc_data[SC_FREEZE].timer != -1) - skill_status_change_end (target, SC_FREEZE, -1); - if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) - skill_status_change_end (target, SC_STONE, -1); - if (sc_data[SC_SLEEP].timer != -1) - skill_status_change_end (target, SC_SLEEP, -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 && tsd->sc_data && tsd->sc_data[SC_DEVOTION].val1) - { // ディボーションをかけられている - struct map_session_data *md = - map_id2sd (tsd->sc_data[SC_DEVOTION].val1); - if (md && skill_devotion3 (&md->bl, target->id)) - { - skill_devotion (md, target->id); - } - else if (md && bl) - for (i = 0; i < 5; i++) - if (md->dev.val1[i] == target->id) - { - clif_damage (bl, &md->bl, gettick (), 0, 0, - damage, 0, 0, 0); - pc_damage (&md->bl, md, damage); - - return 0; - } - } - - if (tsd && tsd->skilltimer != -1) - { // 詠唱妨害 - // フェンカードや妨害されないスキルかの検査 - if ((!tsd->special_state.no_castcancel || map[bl->m].flag.gvg) - && 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_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; -} - -// 攻撃停止 -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); - 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); - return 0; -} - -/*========================================== - * ダメージの属性修正 - *------------------------------------------ - */ -int battle_attr_fix (int damage, int atk_elem, int def_elem) -{ - int def_type = def_elem % 10, def_lv = def_elem / 10 / 2; - - if (atk_elem < 0 || atk_elem > 9 || def_type < 0 || def_type > 9 || - def_lv < 1 || def_lv > 4) - { // 属 性値がおかしいのでとりあえずそのまま返す - if (battle_config.error_log) - printf - ("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n", - atk_elem, def_type, def_lv); - return damage; - } - - return damage * attr_fix_table[def_lv - 1][atk_elem][def_type] / 100; -} - -/*========================================== - * ダメージ最終計算 - *------------------------------------------ - */ -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); - - class_ = battle_get_class (bl); - if (bl->type == BL_MOB) - md = (struct mob_data *) bl; - else - sd = (struct map_session_data *) bl; - - sc_data = battle_get_sc_data (bl); - sc_count = battle_get_sc_count (bl); - - if (sc_count != NULL && *sc_count > 0) - { - - if (sc_data[SC_SAFETYWALL].timer != -1 && damage > 0 - && flag & BF_WEAPON && flag & BF_SHORT - && skill_num != NPC_GUIDEDATTACK) - { - // セーフティウォール - struct skill_unit *unit = - (struct skill_unit *) sc_data[SC_SAFETYWALL].val2; - if (unit && unit->alive && (--unit->group->val2) <= 0) - skill_delunit (unit); - skill_unit_move (bl, gettick (), 1); // 重ね掛けチェック - damage = 0; - } - if (sc_data[SC_PNEUMA].timer != -1 && damage > 0 && flag & BF_WEAPON - && flag & BF_LONG && skill_num != NPC_GUIDEDATTACK) - { - // ニューマ - damage = 0; - } - - if (sc_data[SC_ROKISWEIL].timer != -1 && damage > 0 && - flag & BF_MAGIC) - { - // ニューマ - damage = 0; - } - - if (sc_data[SC_AETERNA].timer != -1 && damage > 0) - { // レックスエーテルナ - damage <<= 1; - skill_status_change_end (bl, SC_AETERNA, -1); - } - - //属性場のダメージ増加 - if (sc_data[SC_VOLCANO].timer != -1) - { // ボルケーノ - if (flag & BF_SKILL && skill_get_pl (skill_num) == 3) - damage += damage * sc_data[SC_VOLCANO].val4 / 100; - else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 3)) - damage += damage * sc_data[SC_VOLCANO].val4 / 100; - } - - if (sc_data[SC_VIOLENTGALE].timer != -1) - { // バイオレントゲイル - if (flag & BF_SKILL && skill_get_pl (skill_num) == 4) - damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; - else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 4)) - damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; - } - - if (sc_data[SC_DELUGE].timer != -1) - { // デリュージ - if (flag & BF_SKILL && skill_get_pl (skill_num) == 1) - damage += damage * sc_data[SC_DELUGE].val4 / 100; - else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 1)) - damage += damage * sc_data[SC_DELUGE].val4 / 100; - } - - if (sc_data[SC_ENERGYCOAT].timer != -1 && damage > 0 - && 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) - skill_status_change_end (bl, SC_ENERGYCOAT, -1); - } - else - damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100; - } - - if (sc_data[SC_KYRIE].timer != -1 && damage > 0) - { // キリエエレイソン - sc = &sc_data[SC_KYRIE]; - sc->val2 -= damage; - if (flag & BF_WEAPON) - { - if (sc->val2 >= 0) - damage = 0; - else - damage = -sc->val2; - } - if ((--sc->val3) <= 0 || (sc->val2 <= 0) - || skill_num == AL_HOLYLIGHT) - skill_status_change_end (bl, SC_KYRIE, -1); - } - - if (sc_data[SC_BASILICA].timer != -1 && damage > 0) - { - // ニューマ - damage = 0; - } - if (sc_data[SC_LANDPROTECTOR].timer != -1 && damage > 0 - && flag & BF_MAGIC) - { - // ニューマ - damage = 0; - } - - if (sc_data[SC_AUTOGUARD].timer != -1 && damage > 0 - && flag & BF_WEAPON) - { - if (MRAND (100) < sc_data[SC_AUTOGUARD].val2) - { - damage = 0; - clif_skill_nodamage (bl, bl, CR_AUTOGUARD, - sc_data[SC_AUTOGUARD].val1, 1); - if (sd) - sd->canmove_tick = gettick () + 300; - else if (md) - md->canmove_tick = gettick () + 300; - } - } -// -- moonsoul (chance to block attacks with new Lord Knight skill parrying) -// - if (sc_data[SC_PARRYING].timer != -1 && damage > 0 - && flag & BF_WEAPON) - { - if (MRAND (100) < sc_data[SC_PARRYING].val2) - { - damage = 0; - clif_skill_nodamage (bl, bl, LK_PARRYING, - sc_data[SC_PARRYING].val1, 1); - } - } - // リジェクトソード - if (sc_data[SC_REJECTSWORD].timer != -1 && damage > 0 - && flag & BF_WEAPON - && - ((src->type == BL_PC - && ((struct map_session_data *) src)->status.weapon == (1 || 2 - || 3)) - || src->type == BL_MOB)) - { - if (MRAND (100) < (10 + 5 * sc_data[SC_REJECTSWORD].val1)) - { //反射確率は10+5*Lv - damage = damage * 50 / 100; - 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) - skill_status_change_end (bl, SC_REJECTSWORD, -1); - } - } - } - - if (class_ == 1288 || class_ == 1287 || class_ == 1286 || class_ == 1285) - { -// if(class == 1288) { - if (class_ == 1288 && flag & BF_SKILL) - damage = 0; - if (src->type == BL_PC) - { - struct guild *g = - guild_search (((struct map_session_data *) src)-> - status.guild_id); - struct guild_castle *gc = guild_mapname2gc (map[bl->m].name); - if (!((struct map_session_data *) src)->status.guild_id) - damage = 0; - if (gc && agit_flag == 0 && class_ != 1288) // guardians cannot be damaged during non-woe [Valaris] - damage = 0; // end woe check [Valaris] - if (g == NULL) - damage = 0; //ギルド未加入ならダメージ無し - else if ((gc != NULL) && guild_isallied (g, gc)) - damage = 0; //自占領ギルドのエンペならダメージ無し - else if (g && guild_checkskill (g, GD_APPROVAL) <= 0) - damage = 0; //正規ギルド承認がないとダメージ無し - else if (battle_config.guild_max_castles != 0 - && guild_checkcastles (g) >= - battle_config.guild_max_castles) - damage = 0; // [MouseJstr] - } - else - damage = 0; - } - - if (map[bl->m].flag.gvg && damage > 0) - { //GvG - if (flag & BF_WEAPON) - { - 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; - } - 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; - if (damage < 1) - damage = 1; - } - - if (battle_config.skill_min_damage || flag & BF_MISC) - { - if (div_ < 255) - { - if (damage > 0 && damage < div_) - damage = div_; - } - else if (damage > 0 && damage < 3) - damage = 3; - } - - if (md != NULL && md->hp > 0 && damage > 0) // 反撃などのMOBスキル判定 - mobskill_event (md, flag); - - return damage; -} - -/*========================================== - * 修練ダメージ - *------------------------------------------ - */ -int battle_addmastery (struct map_session_data *sd, struct block_list *target, - int dmg, int type) -{ - int damage, skill; - int race = battle_get_race (target); - int weapon; - damage = 0; - - nullpo_retr (0, sd); - - // デーモンベイン(+3 〜 +30) vs 不死 or 悪魔 (死人は含めない?) - if ((skill = pc_checkskill (sd, AL_DEMONBANE)) > 0 - && (battle_check_undead (race, battle_get_elem_type (target)) - || race == 6)) - damage += (skill * 3); - - // ビーストベイン(+4 〜 +40) vs 動物 or 昆虫 - if ((skill = pc_checkskill (sd, HT_BEASTBANE)) > 0 - && (race == 2 || race == 4)) - damage += (skill * 4); - - if (type == 0) - weapon = sd->weapontype1; - else - weapon = sd->weapontype2; - switch (weapon) - { - case 0x01: // 短剣 (Updated By AppleGirl) - case 0x02: // 1HS - { - // 剣修練(+4 〜 +40) 片手剣 短剣含む - if ((skill = pc_checkskill (sd, SM_SWORD)) > 0) - { - damage += (skill * 4); - } - break; - } - case 0x03: // 2HS - { - // 両手剣修練(+4 〜 +40) 両手剣 - if ((skill = pc_checkskill (sd, SM_TWOHAND)) > 0) - { - damage += (skill * 4); - } - break; - } - case 0x04: // 1HL - { - // 槍修練(+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 0x05: // 2HL - { - // 槍修練(+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: // 片手斧 - { - if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) - { - damage += (skill * 3); - } - break; - } - case 0x07: // Axe by Tato - { - if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) - { - damage += (skill * 3); - } - break; - } - case 0x08: // メイス - { - // メイス修練(+3 〜 +30) メイス - if ((skill = pc_checkskill (sd, PR_MACEMASTERY)) > 0) - { - damage += (skill * 3); - } - break; - } - case 0x09: // なし? - break; - case 0x0a: // 杖 - break; - case 0x0b: // 弓 - break; - case 0x00: // 素手 - case 0x0c: // Knuckles - { - // 鉄拳(+3 〜 +30) 素手,ナックル - if ((skill = pc_checkskill (sd, MO_IRONHAND)) > 0) - { - damage += (skill * 3); - } - break; - } - case 0x0d: // Musical Instrument - { - // 楽器の練習(+3 〜 +30) 楽器 - if ((skill = pc_checkskill (sd, BA_MUSICALLESSON)) > 0) - { - damage += (skill * 3); - } - break; - } - case 0x0e: // Dance Mastery - { - // 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 - { - // カタール修練(+3 〜 +30) カタール - if ((skill = pc_checkskill (sd, AS_KATAR)) > 0) - { - //ソニックブロー時は別処理(1撃に付き1/8適応) - damage += (skill * 3); - } - break; - } - } - damage = dmg + damage; - return (damage); -} - -static struct Damage battle_calc_mob_weapon_attack (struct block_list *src, - struct block_list *target, - int skill_num, - int skill_lv, int wflag) -{ - struct map_session_data *tsd = NULL; - struct mob_data *md = (struct mob_data *) src, *tmd = NULL; - int hitrate, flee, cri = 0, atkmin, atkmax; - int luk, target_count = 1; - int def1 = battle_get_def (target); - int def2 = battle_get_def2 (target); - int t_vit = battle_get_vit (target); - struct Damage wd; - int damage, damage2 = 0, type, div_, blewcount = - skill_get_blewcount (skill_num, skill_lv); - int flag, skill, ac_flag = 0, dmg_lv = 0; - int t_mode = 0, t_race = 0, t_size = 1, s_race = 0, s_ele = 0; - struct status_change *sc_data, *t_sc_data; - short *sc_count; - short *option, *opt1, *opt2; - - //return前の処理があるので情報出力部のみ変更 - if (src == NULL || target == NULL || md == NULL) - { - nullpo_info (NLP_MARK); - memset (&wd, 0, sizeof (wd)); - return wd; - } - - s_race = battle_get_race (src); - s_ele = battle_get_attack_element (src); - sc_data = battle_get_sc_data (src); - sc_count = battle_get_sc_count (src); - option = battle_get_option (src); - opt1 = battle_get_opt1 (src); - opt2 = battle_get_opt2 (src); - - // ターゲット - if (target->type == BL_PC) - tsd = (struct map_session_data *) target; - else if (target->type == BL_MOB) - tmd = (struct mob_data *) target; - t_race = battle_get_race (target); - t_size = battle_get_size (target); - t_mode = battle_get_mode (target); - t_sc_data = battle_get_sc_data (target); - - if ((skill_num == 0 - || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) - || (target->type == BL_MOB - && battle_config.monster_auto_counter_type & 2)) - && skill_lv >= 0) - { - if (skill_num != CR_GRANDCROSS && t_sc_data - && t_sc_data[SC_AUTOCOUNTER].timer != -1) - { - int dir = map_calc_dir (src, target->x, target->y), t_dir = - battle_get_dir (target); - int dist = distance (src->x, src->y, target->x, target->y); - if (dist <= 0 || map_check_dir (dir, t_dir)) - { - memset (&wd, 0, sizeof (wd)); - t_sc_data[SC_AUTOCOUNTER].val3 = 0; - t_sc_data[SC_AUTOCOUNTER].val4 = 1; - if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) - { - int range = battle_get_range (target); - if ((target->type == BL_PC - && ((struct map_session_data *) target)-> - status.weapon != 11 && dist <= range + 1) - || (target->type == BL_MOB && range <= 3 - && dist <= range + 1)) - t_sc_data[SC_AUTOCOUNTER].val3 = src->id; - } - return wd; - } - else - ac_flag = 1; - } - } - flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 - - // 回避率計算、回避判定は後で - flee = battle_get_flee (target); - if (battle_config.agi_penaly_type > 0 - || battle_config.vit_penaly_type > 0) - target_count += - battle_counttargeted (target, src, - battle_config.agi_penaly_count_lv); - if (battle_config.agi_penaly_type > 0) - { - if (target_count >= battle_config.agi_penaly_count) - { - if (battle_config.agi_penaly_type == 1) - flee = - (flee * - (100 - - (target_count - - (battle_config.agi_penaly_count - - 1)) * battle_config.agi_penaly_num)) / 100; - else if (battle_config.agi_penaly_type == 2) - flee -= - (target_count - - (battle_config.agi_penaly_count - - 1)) * battle_config.agi_penaly_num; - if (flee < 1) - flee = 1; - } - } - hitrate = battle_get_hit (src) - flee + 80; - - type = 0; // normal - div_ = 1; // single attack - - luk = battle_get_luk (src); - - if (battle_config.enemy_str) - damage = battle_get_baseatk (src); - else - damage = 0; - if (skill_num == HW_MAGICCRASHER) - { /* マジッククラッシャーはMATKで殴る */ - atkmin = battle_get_matk1 (src); - atkmax = battle_get_matk2 (src); - } - else - { - atkmin = battle_get_atk (src); - atkmax = battle_get_atk2 (src); - } - if (mob_db[md->mob_class].range > 3) - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - - if (atkmin > atkmax) - atkmin = atkmax; - - if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) - { // マキシマイズパワー - atkmin = atkmax; - } - - cri = battle_get_critical (src); - cri -= battle_get_luk (target) * 3; - if (battle_config.enemy_critical_rate != 100) - { - cri = cri * battle_config.enemy_critical_rate / 100; - if (cri < 1) - cri = 1; - } - if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に - cri <<= 1; - - if (ac_flag) - cri = 1000; - - if (skill_num == KN_AUTOCOUNTER) - { - if (!(battle_config.monster_auto_counter_type & 1)) - cri = 1000; - else - cri <<= 1; - } - - if (tsd && tsd->critical_def) - cri = cri * (100 - tsd->critical_def) / 100; - - if ((skill_num == 0 || skill_num == KN_AUTOCOUNTER) && skill_lv >= 0 && battle_config.enemy_critical && (MRAND (1000)) < cri) // 判定(スキルの場合は無視) - // 敵の判定 - { - damage += atkmax; - type = 0x0a; - } - else - { - int vitbonusmax; - - if (atkmax > atkmin) - damage += atkmin + MRAND ((atkmax - atkmin + 1)); - else - damage += atkmin; - // スキル修正1(攻撃力倍化系) - // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 - // バッシュ,マグナムブレイク, - // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, - // メマーナイト,カートレボリューション - // ダブルストレイフィング,アローシャワー,チャージアロー, - // ソニックブロー - if (sc_data) - { //状態異常中のダメージ追加 - if (sc_data[SC_OVERTHRUST].timer != -1) // オーバートラスト - damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; - if (sc_data[SC_TRUESIGHT].timer != -1) // トゥルーサイト - damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; - if (sc_data[SC_BERSERK].timer != -1) // バーサーク - damage += damage * 50 / 100; - } - - if (skill_num > 0) - { - int i; - if ((i = skill_get_pl (skill_num)) > 0) - s_ele = i; - - flag = (flag & ~BF_SKILLMASK) | BF_SKILL; - switch (skill_num) - { - case SM_BASH: // バッシュ - damage = damage * (100 + 30 * skill_lv) / 100; - hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; - break; - case SM_MAGNUM: // マグナムブレイク - damage = - damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; - break; - case MC_MAMMONITE: // メマーナイト - damage = damage * (100 + 50 * skill_lv) / 100; - break; - case AC_DOUBLE: // ダブルストレイフィング - damage = damage * (180 + 20 * skill_lv) / 100; - div_ = 2; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case AC_SHOWER: // アローシャワー - damage = damage * (75 + 5 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case AC_CHARGEARROW: // チャージアロー - damage = damage * 150 / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case KN_PIERCE: // ピアース - damage = damage * (100 + 10 * skill_lv) / 100; - hitrate = hitrate * (100 + 5 * skill_lv) / 100; - div_ = t_size + 1; - damage *= div_; - break; - case KN_SPEARSTAB: // スピアスタブ - damage = damage * (100 + 15 * skill_lv) / 100; - break; - case KN_SPEARBOOMERANG: // スピアブーメラン - damage = damage * (100 + 50 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case KN_BRANDISHSPEAR: // ブランディッシュスピア - damage = damage * (100 + 20 * skill_lv) / 100; - if (skill_lv > 3 && wflag == 1) - damage2 += damage / 2; - if (skill_lv > 6 && wflag == 1) - damage2 += damage / 4; - if (skill_lv > 9 && wflag == 1) - damage2 += damage / 8; - if (skill_lv > 6 && wflag == 2) - damage2 += damage / 2; - if (skill_lv > 9 && wflag == 2) - damage2 += damage / 4; - if (skill_lv > 9 && wflag == 3) - damage2 += damage / 2; - damage += damage2; - blewcount = 0; - break; - case KN_BOWLINGBASH: // ボウリングバッシュ - damage = damage * (100 + 50 * skill_lv) / 100; - blewcount = 0; - break; - case KN_AUTOCOUNTER: - if (battle_config.monster_auto_counter_type & 1) - hitrate += 20; - else - hitrate = 1000000; - flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; - break; - case AS_SONICBLOW: // ソニックブロウ - damage = damage * (300 + 50 * skill_lv) / 100; - div_ = 8; - break; - case TF_SPRINKLESAND: // 砂まき - damage = damage * 125 / 100; - break; - case MC_CARTREVOLUTION: // カートレボリューション - damage = (damage * 150) / 100; - break; - // 以下MOB - case NPC_COMBOATTACK: // 多段攻撃 - div_ = skill_get_num (skill_num, skill_lv); - damage *= div_; - break; - case NPC_RANDOMATTACK: // ランダムATK攻撃 - damage = damage * (MPRAND (50, 150)) / 100; - break; - // 属性攻撃(適当) - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_TELEKINESISATTACK: - damage = damage * (100 + 25 * (skill_lv - 1)) / 100; - break; - case NPC_GUIDEDATTACK: - hitrate = 1000000; - break; - case NPC_RANGEATTACK: - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case NPC_PIERCINGATT: - flag = (flag & ~BF_RANGEMASK) | BF_SHORT; - break; - case RG_BACKSTAP: // バックスタブ - damage = damage * (300 + 40 * skill_lv) / 100; - hitrate = 1000000; - break; - case RG_RAID: // サプライズアタック - damage = damage * (100 + 40 * skill_lv) / 100; - break; - case RG_INTIMIDATE: // インティミデイト - damage = damage * (100 + 30 * skill_lv) / 100; - break; - case CR_SHIELDCHARGE: // シールドチャージ - damage = damage * (100 + 20 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_SHORT; - s_ele = 0; - break; - case CR_SHIELDBOOMERANG: // シールドブーメラン - damage = damage * (100 + 30 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - s_ele = 0; - break; - case CR_HOLYCROSS: // ホーリークロス - damage = damage * (100 + 35 * skill_lv) / 100; - div_ = 2; - break; - case CR_GRANDCROSS: - hitrate = 1000000; - break; - case AM_DEMONSTRATION: // デモンストレーション - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - break; - case AM_ACIDTERROR: // アシッドテラー - damage = damage * (100 + 40 * skill_lv) / 100; - damage2 = damage2 * (100 + 40 * skill_lv) / 100; - break; - case MO_FINGEROFFENSIVE: //指弾 - damage = damage * (100 + 50 * skill_lv) / 100; - div_ = 1; - break; - case MO_INVESTIGATE: // 発 勁 - if (def1 < 1000000) - damage = - damage * (100 + 75 * skill_lv) / 100 * (def1 + - def2) / - 100; - hitrate = 1000000; - s_ele = 0; - break; - case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 - damage = damage * 8 + 250 + (skill_lv * 150); - hitrate = 1000000; - s_ele = 0; - break; - case MO_CHAINCOMBO: // 連打掌 - damage = damage * (150 + 50 * skill_lv) / 100; - div_ = 4; - break; - case BA_MUSICALSTRIKE: // ミュージカルストライク - damage = damage * (100 + 50 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case DC_THROWARROW: // 矢撃ち - damage = damage * (100 + 50 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case MO_COMBOFINISH: // 猛龍拳 - damage = damage * (240 + 60 * skill_lv) / 100; - break; - case CH_TIGERFIST: // 伏虎拳 - damage = damage * (100 + 20 * skill_lv) / 100; - break; - case CH_CHAINCRUSH: // 連柱崩撃 - damage = damage * (100 + 20 * skill_lv) / 100; - div_ = skill_get_num (skill_num, skill_lv); - break; - case CH_PALMSTRIKE: // 猛虎硬派山 - damage = damage * (50 + 100 * skill_lv) / 100; - break; - case LK_SPIRALPIERCE: /* スパイラルピアース */ - damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に - div_ = 5; - if (tsd) - tsd->canmove_tick = gettick () + 1000; - else if (tmd) - tmd->canmove_tick = gettick () + 1000; - break; - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - damage = damage * (100 + 20 * skill_lv) / 100; - break; - case LK_JOINTBEAT: /* ジョイントビート */ - damage = damage * (50 + 10 * skill_lv) / 100; - break; - case ASC_METEORASSAULT: /* メテオアサルト */ - damage = damage * (40 + 40 * skill_lv) / 100; - break; - case SN_SHARPSHOOTING: /* シャープシューティング */ - damage += damage * (30 * skill_lv) / 100; - break; - case CG_ARROWVULCAN: /* アローバルカン */ - damage = damage * (160 + 40 * skill_lv) / 100; - div_ = 9; - break; - case AS_SPLASHER: /* ベナムスプラッシャー */ - damage = damage * (200 + 20 * skill_lv) / 100; - break; - } - } - - if (skill_num != NPC_CRITICALSLASH) - { - // 対 象の防御力によるダメージの減少 - // ディバインプロテクション(ここでいいのかな?) - if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST - && skill_num != KN_AUTOCOUNTER && def1 < 1000000) - { //DEF, VIT無視 - int t_def; - target_count = - 1 + battle_counttargeted (target, src, - battle_config.vit_penaly_count_lv); - if (battle_config.vit_penaly_type > 0) - { - if (target_count >= battle_config.vit_penaly_count) - { - if (battle_config.vit_penaly_type == 1) - { - def1 = - (def1 * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - def2 = - (def2 * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - t_vit = - (t_vit * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - } - else if (battle_config.vit_penaly_type == 2) - { - def1 -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - def2 -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - t_vit -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - } - if (def1 < 0) - def1 = 0; - if (def2 < 1) - def2 = 1; - if (t_vit < 1) - t_vit = 1; - } - } - t_def = def2 * 8 / 10; - if (battle_check_undead (s_race, battle_get_elem_type (src)) - || s_race == 6) - if (tsd && (skill = pc_checkskill (tsd, AL_DP)) > 0) - t_def += skill * 3; - - vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; - if (battle_config.monster_defense_type) - { - damage = - damage - (def1 * battle_config.monster_defense_type) - - t_def - - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - else - { - damage = - damage * (100 - def1) / 100 - t_def - - ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - } - } - } - - // 0未満だった場合1に補正 - if (damage < 1) - damage = 1; - - // 回避修正 - if (hitrate < 1000000) - hitrate = ((hitrate > 95) ? 95 : ((hitrate < 5) ? 5 : hitrate)); - if (hitrate < 1000000 && // 必中攻撃 - (t_sc_data != NULL && (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)))) // 凍結は必中 - hitrate = 1000000; - if (type == 0 && MRAND (100) >= hitrate) - { - damage = damage2 = 0; - dmg_lv = ATK_FLEE; - } - else - { - dmg_lv = ATK_DEF; - } - - if (tsd) - { - int cardfix = 100, i; - cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属 性によるダメージ耐性 - cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 - if (mob_db[md->mob_class].mode & 0x20) - cardfix = cardfix * (100 - tsd->subrace[10]) / 100; - else - cardfix = cardfix * (100 - tsd->subrace[11]) / 100; - for (i = 0; i < tsd->add_def_class_count; i++) - { - if (tsd->add_def_classid[i] == md->mob_class) - { - cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; - break; - } - } - if (flag & BF_LONG) - cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; - if (flag & BF_SHORT) - cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; - damage = damage * cardfix / 100; - } - if (t_sc_data) - { - int cardfix = 100; - if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) - cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; - if (cardfix != 100) - damage = damage * cardfix / 100; - } - if (t_sc_data && t_sc_data[SC_ASSUMPTIO].timer != -1) - { //アシャンプティオ - if (!map[target->m].flag.pvp) - damage = damage / 3; - else - damage = damage / 2; - } - - if (damage < 0) - damage = 0; - - // 属 性の適用 - if (!((battle_config.mob_ghostring_fix == 1) && (battle_get_element (target) == 8) && (target->type == BL_PC))) // [MouseJstr] - if (skill_num != 0 || s_ele != 0 - || !battle_config.mob_attack_attr_none) - damage = - battle_attr_fix (damage, s_ele, battle_get_element (target)); - - if (sc_data && sc_data[SC_AURABLADE].timer != -1) /* オーラブレード 必中 */ - damage += sc_data[SC_AURABLADE].val1 * 10; - if (skill_num == PA_PRESSURE) /* プレッシャー 必中? */ - damage = 700 + 100 * skill_lv; - - // インベナム修正 - if (skill_num == TF_POISON) - { - damage = - battle_attr_fix (damage + 15 * skill_lv, s_ele, - battle_get_element (target)); - } - if (skill_num == MC_CARTREVOLUTION) - { - damage = battle_attr_fix (damage, 0, battle_get_element (target)); - } - - // 完全回避の判定 - if (skill_num == 0 && skill_lv >= 0 && tsd != NULL - && MRAND (1000) < battle_get_flee2 (target)) - { - damage = 0; - type = 0x0b; - dmg_lv = ATK_LUCKY; - } - - if (battle_config.enemy_perfect_flee) - { - if (skill_num == 0 && skill_lv >= 0 && tmd != NULL - && MRAND (1000) < battle_get_flee2 (target)) - { - damage = 0; - type = 0x0b; - dmg_lv = ATK_LUCKY; - } - } - -// if(def1 >= 1000000 && damage > 0) - if (t_mode & 0x40 && damage > 0) - damage = 1; - - if (tsd && tsd->special_state.no_weapon_damage) - damage = 0; - - if (skill_num != CR_GRANDCROSS) - damage = - battle_calc_damage (src, target, damage, div_, skill_num, - skill_lv, flag); - - wd.damage = damage; - wd.damage2 = 0; - wd.type = type; - wd.div_ = div_; - wd.amotion = battle_get_amotion (src); - if (skill_num == KN_AUTOCOUNTER) - wd.amotion >>= 1; - wd.dmotion = battle_get_dmotion (target); - wd.blewcount = blewcount; - wd.flag = flag; - wd.dmg_lv = dmg_lv; - return wd; -} - -int battle_is_unarmed (struct block_list *bl) -{ - if (!bl) - return 0; - if (bl->type == BL_PC) - { - struct map_session_data *sd = (struct map_session_data *) bl; - - return (sd->equip_index[EQUIP_SHIELD] == -1 - && sd->equip_index[EQUIP_WEAPON] == -1); - } - else - return 0; -} - -/* - * ========================================================================= - * PCの武器による攻撃 - *------------------------------------------------------------------------- - */ -static struct Damage battle_calc_pc_weapon_attack (struct block_list *src, - struct block_list *target, - int skill_num, - int skill_lv, int wflag) -{ - struct map_session_data *sd = (struct map_session_data *) src, *tsd = - NULL; - struct mob_data *tmd = NULL; - int hitrate, flee, cri = 0, atkmin, atkmax; - int dex, luk, target_count = 1; - int def1 = battle_get_def (target); - int def2 = battle_get_def2 (target); - int t_vit = battle_get_vit (target); - struct Damage wd; - int damage, damage2, damage3 = 0, damage4 = 0, type, div_, blewcount = - skill_get_blewcount (skill_num, skill_lv); - int flag, skill, dmg_lv = 0; - int t_mode = 0, t_race = 0, t_size = 1, s_race = 7, s_ele = 0; - struct status_change *sc_data, *t_sc_data; - short *sc_count; - short *option, *opt1, *opt2; - int atkmax_ = 0, atkmin_ = 0, s_ele_; //二刀流用 - int watk, watk_, cardfix, t_ele; - int da = 0, i, t_class, ac_flag = 0; - int idef_flag = 0, idef_flag_ = 0; - int target_distance; - - //return前の処理があるので情報出力部のみ変更 - if (src == NULL || target == NULL || sd == NULL) - { - nullpo_info (NLP_MARK); - memset (&wd, 0, sizeof (wd)); - return wd; - } - - // アタッカー - s_race = battle_get_race (src); //種族 - s_ele = battle_get_attack_element (src); //属性 - s_ele_ = battle_get_attack_element2 (src); //左手属性 - sc_data = battle_get_sc_data (src); //ステータス異常 - sc_count = battle_get_sc_count (src); //ステータス異常の数 - option = battle_get_option (src); //鷹とかペコとかカートとか - opt1 = battle_get_opt1 (src); //石化、凍結、スタン、睡眠、暗闇 - opt2 = battle_get_opt2 (src); //毒、呪い、沈黙、暗闇? - - if (skill_num != CR_GRANDCROSS) //グランドクロスでないなら - sd->state.attack_type = BF_WEAPON; //攻撃タイプは武器攻撃 - - // ターゲット - if (target->type == BL_PC) //対象がPCなら - tsd = (struct map_session_data *) target; //tsdに代入(tmdはNULL) - else if (target->type == BL_MOB) //対象がMobなら - tmd = (struct mob_data *) target; //tmdに代入(tsdはNULL) - t_race = battle_get_race (target); //対象の種族 - t_ele = battle_get_elem_type (target); //対象の属性 - t_size = battle_get_size (target); //対象のサイズ - t_mode = battle_get_mode (target); //対象のMode - t_sc_data = battle_get_sc_data (target); //対象のステータス異常 - -//オートカウンター処理ここから - if ((skill_num == 0 - || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) - || (target->type == BL_MOB - && battle_config.monster_auto_counter_type & 2)) - && skill_lv >= 0) - { - if (skill_num != CR_GRANDCROSS && t_sc_data - && t_sc_data[SC_AUTOCOUNTER].timer != -1) - { //グランドクロスでなく、対象がオートカウンター状態の場合 - int dir = map_calc_dir (src, target->x, target->y), t_dir = - battle_get_dir (target); - int dist = distance (src->x, src->y, target->x, target->y); - if (dist <= 0 || map_check_dir (dir, t_dir)) - { //対象との距離が0以下、または対象の正面? - memset (&wd, 0, sizeof (wd)); - t_sc_data[SC_AUTOCOUNTER].val3 = 0; - t_sc_data[SC_AUTOCOUNTER].val4 = 1; - if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) - { //自分がオートカウンター状態 - int range = battle_get_range (target); - if ((target->type == BL_PC && ((struct map_session_data *) target)->status.weapon != 11 && dist <= range + 1) || //対象がPCで武器が弓矢でなく射程内 - (target->type == BL_MOB && range <= 3 && dist <= range + 1)) //または対象がMobで射程が3以下で射程内 - t_sc_data[SC_AUTOCOUNTER].val3 = src->id; - } - return wd; //ダメージ構造体を返して終了 - } - else - ac_flag = 1; - } - } -//オートカウンター処理ここまで - - flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 - - // 回避率計算、回避判定は後で - flee = battle_get_flee (target); - if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効 - target_count += battle_counttargeted (target, src, battle_config.agi_penaly_count_lv); //対象の数を算出 - if (battle_config.agi_penaly_type > 0) - { - if (target_count >= battle_config.agi_penaly_count) - { //ペナルティ設定より対象が多い - if (battle_config.agi_penaly_type == 1) //回避率がagi_penaly_num%ずつ減少 - flee = - (flee * - (100 - - (target_count - - (battle_config.agi_penaly_count - - 1)) * battle_config.agi_penaly_num)) / 100; - else if (battle_config.agi_penaly_type == 2) //回避率がagi_penaly_num分減少 - flee -= - (target_count - - (battle_config.agi_penaly_count - - 1)) * battle_config.agi_penaly_num; - if (flee < 1) - flee = 1; //回避率は最低でも1 - } - } - hitrate = battle_get_hit (src) - flee + 80; //命中率計算 - - { // [fate] Reduce hit chance by distance - int dx = abs (src->x - target->x); - int dy = abs (src->y - target->y); - int malus_dist; - - target_distance = MAX (dx, dy); - malus_dist = - MAX (0, target_distance - (skill_power (sd, AC_OWL) / 75)); - hitrate -= (malus_dist * (malus_dist + 1)); - } - - dex = battle_get_dex (src); //DEX - luk = battle_get_luk (src); //LUK - watk = battle_get_atk (src); //ATK - watk_ = battle_get_atk_ (src); //ATK左手 - - type = 0; // normal - div_ = 1; // single attack - - if (skill_num == HW_MAGICCRASHER) - { /* マジッククラッシャーはMATKで殴る */ - damage = damage2 = battle_get_matk1 (src); //damega,damega2初登場、base_atkの取得 - } - else - { - damage = damage2 = battle_get_baseatk (&sd->bl); //damega,damega2初登場、base_atkの取得 - } - if (sd->attackrange > 2) - { // [fate] ranged weapon? - const int range_damage_bonus = 80; // up to 31.25% bonus for long-range hit - damage = - damage * (256 + - ((range_damage_bonus * target_distance) / - sd->attackrange)) >> 8; - damage2 = - damage2 * (256 + - ((range_damage_bonus * target_distance) / - sd->attackrange)) >> 8; - } - - atkmin = atkmin_ = dex; //最低ATKはDEXで初期化? - sd->state.arrow_atk = 0; //arrow_atk初期化 - 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 (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 (sd->status.weapon == 11) - { //武器が弓矢の場合 - atkmin = watk * ((atkmin < watk) ? atkmin : watk) / 100; //弓用最低ATK計算 - flag = (flag & ~BF_RANGEMASK) | BF_LONG; //遠距離攻撃フラグを有効 - if (sd->arrow_ele > 0) //属性矢なら属性を矢の属性に変更 - s_ele = sd->arrow_ele; - sd->state.arrow_atk = 1; //arrow_atk有効化 - } - - // サイズ修正 - // ペコ騎乗していて、槍で攻撃した場合は中型のサイズ修正を100にする - // ウェポンパーフェクション,ドレイクC - if (((sd->special_state.no_sizefix) - || (pc_isriding (sd) - && (sd->status.weapon == 4 || sd->status.weapon == 5) - && t_size == 1) || skill_num == MO_EXTREMITYFIST)) - { //ペコ騎乗していて、槍で中型を攻撃 - atkmax = watk; - atkmax_ = watk_; - } - else - { - atkmax = (watk * sd->atkmods[t_size]) / 100; - atkmin = (atkmin * sd->atkmods[t_size]) / 100; - atkmax_ = (watk_ * sd->atkmods_[t_size]) / 100; - atkmin_ = (atkmin_ * sd->atkmods[t_size]) / 100; - } - if ((sc_data != NULL && sc_data[SC_WEAPONPERFECTION].timer != -1) - || (sd->special_state.no_sizefix)) - { // ウェポンパーフェクション || ドレイクカード - atkmax = watk; - atkmax_ = watk_; - } - - if (atkmin > atkmax && !(sd->state.arrow_atk)) - atkmin = atkmax; //弓は最低が上回る場合あり - if (atkmin_ > atkmax_) - atkmin_ = atkmax_; - - if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) - { // マキシマイズパワー - atkmin = atkmax; - atkmin_ = atkmax_; - } - - //ダブルアタック判定 - if (sd->weapontype1 == 0x01) - { - if (skill_num == 0 && skill_lv >= 0 - && (skill = pc_checkskill (sd, TF_DOUBLE)) > 0) - da = (MRAND (100) < (skill * 5)) ? 1 : 0; - } - - //三段掌 - if (skill_num == 0 && skill_lv >= 0 - && (skill = pc_checkskill (sd, MO_TRIPLEATTACK)) > 0 - && sd->status.weapon <= 16 && !sd->state.arrow_atk) - { - da = (MRAND (100) < (30 - skill)) ? 2 : 0; - } - - if (sd->double_rate > 0 && da == 0 && skill_num == 0 && skill_lv >= 0) - da = (MRAND (100) < sd->double_rate) ? 1 : 0; - - // 過剰精錬ボーナス - if (sd->overrefine > 0) - damage += MPRAND (1, sd->overrefine); - if (sd->overrefine_ > 0) - damage2 += MPRAND (1, sd->overrefine_); - - if (da == 0) - { //ダブルアタックが発動していない - // クリティカル計算 - cri = battle_get_critical (src); - - if (sd->state.arrow_atk) - cri += sd->arrow_cri; - if (sd->status.weapon == 16) - // カタールの場合、クリティカルを倍に - cri <<= 1; - cri -= battle_get_luk (target) * 3; - if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に - cri <<= 1; - if (ac_flag) - cri = 1000; - - if (skill_num == KN_AUTOCOUNTER) - { - if (!(battle_config.pc_auto_counter_type & 1)) - cri = 1000; - else - cri <<= 1; - } - - if (skill_num == SN_SHARPSHOOTING && MRAND (100) < 50) - cri = 1000; - } - - if (tsd && tsd->critical_def) - cri = cri * (100 - tsd->critical_def) / 100; - - if (da == 0 && (skill_num == 0 || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING) && skill_lv >= 0 && //ダブルアタックが発動していない - (MRAND (1000)) < cri) // 判定(スキルの場合は無視) - { - damage += atkmax; - damage2 += atkmax_; - if (sd->atk_rate != 100) - { - damage = (damage * sd->atk_rate) / 100; - damage2 = (damage2 * sd->atk_rate) / 100; - } - if (sd->state.arrow_atk) - damage += sd->arrow_atk; - type = 0x0a; - -/* if(def1 < 1000000) { - if(sd->def_ratio_atk_ele & (1<<t_ele) || sd->def_ratio_atk_race & (1<<t_race)) { - damage = (damage * (def1 + def2))/100; - idef_flag = 1; - } - if(sd->def_ratio_atk_ele_ & (1<<t_ele) || sd->def_ratio_atk_race_ & (1<<t_race)) { - damage2 = (damage2 * (def1 + def2))/100; - idef_flag_ = 1; - } - if(t_mode & 0x20) { - if(!idef_flag && sd->def_ratio_atk_race & (1<<10)) { - damage = (damage * (def1 + def2))/100; - idef_flag = 1; - } - if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<10)) { - damage2 = (damage2 * (def1 + def2))/100; - idef_flag_ = 1; - } - } - else { - if(!idef_flag && sd->def_ratio_atk_race & (1<<11)) { - damage = (damage * (def1 + def2))/100; - idef_flag = 1; - } - if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<11)) { - damage2 = (damage2 * (def1 + def2))/100; - idef_flag_ = 1; - } - } - }*/ - } - else - { - int vitbonusmax; - - if (atkmax > atkmin) - damage += atkmin + MRAND ((atkmax - atkmin + 1)); - else - damage += atkmin; - if (atkmax_ > atkmin_) - damage2 += atkmin_ + MRAND ((atkmax_ - atkmin_ + 1)); - else - damage2 += atkmin_; - if (sd->atk_rate != 100) - { - damage = (damage * sd->atk_rate) / 100; - damage2 = (damage2 * sd->atk_rate) / 100; - } - - if (sd->state.arrow_atk) - { - if (sd->arrow_atk > 0) - damage += MRAND ((sd->arrow_atk + 1)); - hitrate += sd->arrow_hit; - } - - if (skill_num != MO_INVESTIGATE && def1 < 1000000) - { - if (sd->def_ratio_atk_ele & (1 << t_ele) - || sd->def_ratio_atk_race & (1 << t_race)) - { - damage = (damage * (def1 + def2)) / 100; - idef_flag = 1; - } - if (sd->def_ratio_atk_ele_ & (1 << t_ele) - || sd->def_ratio_atk_race_ & (1 << t_race)) - { - damage2 = (damage2 * (def1 + def2)) / 100; - idef_flag_ = 1; - } - if (t_mode & 0x20) - { - if (!idef_flag && sd->def_ratio_atk_race & (1 << 10)) - { - damage = (damage * (def1 + def2)) / 100; - idef_flag = 1; - } - if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 10)) - { - damage2 = (damage2 * (def1 + def2)) / 100; - idef_flag_ = 1; - } - } - else - { - if (!idef_flag && sd->def_ratio_atk_race & (1 << 11)) - { - damage = (damage * (def1 + def2)) / 100; - idef_flag = 1; - } - if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 11)) - { - damage2 = (damage2 * (def1 + def2)) / 100; - idef_flag_ = 1; - } - } - } - - // スキル修正1(攻撃力倍化系) - // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 - // バッシュ,マグナムブレイク, - // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, - // メマーナイト,カートレボリューション - // ダブルストレイフィング,アローシャワー,チャージアロー, - // ソニックブロー - if (sc_data) - { //状態異常中のダメージ追加 - if (sc_data[SC_OVERTHRUST].timer != -1) - { // オーバートラスト - damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; - damage2 += damage2 * (5 * sc_data[SC_OVERTHRUST].val1) / 100; - } - if (sc_data[SC_TRUESIGHT].timer != -1) - { // トゥルーサイト - damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; - damage2 += damage2 * (2 * sc_data[SC_TRUESIGHT].val1) / 100; - } - if (sc_data[SC_BERSERK].timer != -1) - { // バーサーク - damage += damage * 50 / 100; - damage2 += damage2 * 50 / 100; - } - } - - if (skill_num > 0) - { - int i; - if ((i = skill_get_pl (skill_num)) > 0) - s_ele = s_ele_ = i; - - flag = (flag & ~BF_SKILLMASK) | BF_SKILL; - switch (skill_num) - { - case SM_BASH: // バッシュ - damage = damage * (100 + 30 * skill_lv) / 100; - damage2 = damage2 * (100 + 30 * skill_lv) / 100; - hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; - break; - case SM_MAGNUM: // マグナムブレイク - damage = - damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; - damage2 = - damage2 * (5 * skill_lv + (wflag) ? 65 : 115) / 100; - break; - case MC_MAMMONITE: // メマーナイト - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - break; - case AC_DOUBLE: // ダブルストレイフィング - if (!sd->state.arrow_atk && sd->arrow_atk > 0) - { - int arr = MRAND ((sd->arrow_atk + 1)); - damage += arr; - damage2 += arr; - } - damage = damage * (180 + 20 * skill_lv) / 100; - damage2 = damage2 * (180 + 20 * skill_lv) / 100; - div_ = 2; - if (sd->arrow_ele > 0) - { - s_ele = sd->arrow_ele; - s_ele_ = sd->arrow_ele; - } - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - sd->state.arrow_atk = 1; - break; - case AC_SHOWER: // アローシャワー - if (!sd->state.arrow_atk && sd->arrow_atk > 0) - { - int arr = MRAND ((sd->arrow_atk + 1)); - damage += arr; - damage2 += arr; - } - damage = damage * (75 + 5 * skill_lv) / 100; - damage2 = damage2 * (75 + 5 * skill_lv) / 100; - if (sd->arrow_ele > 0) - { - s_ele = sd->arrow_ele; - s_ele_ = sd->arrow_ele; - } - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - sd->state.arrow_atk = 1; - break; - case AC_CHARGEARROW: // チャージアロー - if (!sd->state.arrow_atk && sd->arrow_atk > 0) - { - int arr = MRAND ((sd->arrow_atk + 1)); - damage += arr; - damage2 += arr; - } - damage = damage * 150 / 100; - damage2 = damage2 * 150 / 100; - if (sd->arrow_ele > 0) - { - s_ele = sd->arrow_ele; - s_ele_ = sd->arrow_ele; - } - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - sd->state.arrow_atk = 1; - break; - case KN_PIERCE: // ピアース - damage = damage * (100 + 10 * skill_lv) / 100; - damage2 = damage2 * (100 + 10 * skill_lv) / 100; - hitrate = hitrate * (100 + 5 * skill_lv) / 100; - div_ = t_size + 1; - damage *= div_; - damage2 *= div_; - break; - case KN_SPEARSTAB: // スピアスタブ - damage = damage * (100 + 15 * skill_lv) / 100; - damage2 = damage2 * (100 + 15 * skill_lv) / 100; - break; - case KN_SPEARBOOMERANG: // スピアブーメラン - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case KN_BRANDISHSPEAR: // ブランディッシュスピア - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - if (skill_lv > 3 && wflag == 1) - damage3 += damage / 2; - if (skill_lv > 6 && wflag == 1) - damage3 += damage / 4; - if (skill_lv > 9 && wflag == 1) - damage3 += damage / 8; - if (skill_lv > 6 && wflag == 2) - damage3 += damage / 2; - if (skill_lv > 9 && wflag == 2) - damage3 += damage / 4; - if (skill_lv > 9 && wflag == 3) - damage3 += damage / 2; - damage += damage3; - if (skill_lv > 3 && wflag == 1) - damage4 += damage2 / 2; - if (skill_lv > 6 && wflag == 1) - damage4 += damage2 / 4; - if (skill_lv > 9 && wflag == 1) - damage4 += damage2 / 8; - if (skill_lv > 6 && wflag == 2) - damage4 += damage2 / 2; - if (skill_lv > 9 && wflag == 2) - damage4 += damage2 / 4; - if (skill_lv > 9 && wflag == 3) - damage4 += damage2 / 2; - damage2 += damage4; - blewcount = 0; - break; - case KN_BOWLINGBASH: // ボウリングバッシュ - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - blewcount = 0; - break; - case KN_AUTOCOUNTER: - if (battle_config.pc_auto_counter_type & 1) - hitrate += 20; - else - hitrate = 1000000; - flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; - break; - case AS_SONICBLOW: // ソニックブロウ - hitrate += 30; // hitrate +30, thanks to midas - damage = damage * (300 + 50 * skill_lv) / 100; - damage2 = damage2 * (300 + 50 * skill_lv) / 100; - div_ = 8; - break; - case TF_SPRINKLESAND: // 砂まき - damage = damage * 125 / 100; - damage2 = damage2 * 125 / 100; - break; - case MC_CARTREVOLUTION: // カートレボリューション - if (sd->cart_max_weight > 0 && sd->cart_weight > 0) - { - damage = - (damage * - (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + - (sd->cart_weight * 100 / - sd->cart_max_weight))) / 100; - damage2 = - (damage2 * - (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + - (sd->cart_weight * 100 / - sd->cart_max_weight))) / 100; - } - else - { - damage = (damage * 150) / 100; - damage2 = (damage2 * 150) / 100; - } - break; - // 以下MOB - case NPC_COMBOATTACK: // 多段攻撃 - div_ = skill_get_num (skill_num, skill_lv); - damage *= div_; - damage2 *= div_; - break; - case NPC_RANDOMATTACK: // ランダムATK攻撃 - damage = damage * (MPRAND (50, 150)) / 100; - damage2 = damage2 * (MPRAND (50, 150)) / 100; - break; - // 属性攻撃(適当) - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_TELEKINESISATTACK: - damage = damage * (100 + 25 * skill_lv) / 100; - damage2 = damage2 * (100 + 25 * skill_lv) / 100; - break; - case NPC_GUIDEDATTACK: - hitrate = 1000000; - break; - case NPC_RANGEATTACK: - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - break; - case NPC_PIERCINGATT: - flag = (flag & ~BF_RANGEMASK) | BF_SHORT; - break; - case RG_BACKSTAP: // バックスタブ - if (battle_config.backstab_bow_penalty == 1 - && sd->status.weapon == 11) - { - damage = (damage * (300 + 40 * skill_lv) / 100) / 2; - damage2 = (damage2 * (300 + 40 * skill_lv) / 100) / 2; - } - else - { - damage = damage * (300 + 40 * skill_lv) / 100; - damage2 = damage2 * (300 + 40 * skill_lv) / 100; - } - hitrate = 1000000; - break; - case RG_RAID: // サプライズアタック - damage = damage * (100 + 40 * skill_lv) / 100; - damage2 = damage2 * (100 + 40 * skill_lv) / 100; - break; - case RG_INTIMIDATE: // インティミデイト - damage = damage * (100 + 30 * skill_lv) / 100; - damage2 = damage2 * (100 + 30 * skill_lv) / 100; - break; - case CR_SHIELDCHARGE: // シールドチャージ - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_SHORT; - s_ele = 0; - break; - case CR_SHIELDBOOMERANG: // シールドブーメラン - damage = damage * (100 + 30 * skill_lv) / 100; - damage2 = damage2 * (100 + 30 * skill_lv) / 100; - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - s_ele = 0; - break; - case CR_HOLYCROSS: // ホーリークロス - damage = damage * (100 + 35 * skill_lv) / 100; - damage2 = damage2 * (100 + 35 * skill_lv) / 100; - div_ = 2; - break; - case CR_GRANDCROSS: - hitrate = 1000000; - break; - case AM_DEMONSTRATION: // デモンストレーション - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - break; - case AM_ACIDTERROR: // アシッドテラー - damage = damage * (100 + 40 * skill_lv) / 100; - damage2 = damage2 * (100 + 40 * skill_lv) / 100; - break; - case MO_FINGEROFFENSIVE: //指弾 - if (battle_config.finger_offensive_type == 0) - { - damage = - damage * (100 + - 50 * skill_lv) / 100 * - sd->spiritball_old; - damage2 = - damage2 * (100 + - 50 * skill_lv) / 100 * - sd->spiritball_old; - div_ = sd->spiritball_old; - } - else - { - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - div_ = 1; - } - break; - case MO_INVESTIGATE: // 発 勁 - if (def1 < 1000000) - { - damage = - damage * (100 + 75 * skill_lv) / 100 * (def1 + - def2) / - 100; - damage2 = - damage2 * (100 + 75 * skill_lv) / 100 * (def1 + - def2) / - 100; - } - hitrate = 1000000; - s_ele = 0; - s_ele_ = 0; - break; - case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 - damage = - damage * (8 + ((sd->status.sp) / 10)) + 250 + - (skill_lv * 150); - damage2 = - damage2 * (8 + ((sd->status.sp) / 10)) + 250 + - (skill_lv * 150); - sd->status.sp = 0; - clif_updatestatus (sd, SP_SP); - hitrate = 1000000; - s_ele = 0; - s_ele_ = 0; - break; - case MO_CHAINCOMBO: // 連打掌 - damage = damage * (150 + 50 * skill_lv) / 100; - damage2 = damage2 * (150 + 50 * skill_lv) / 100; - div_ = 4; - break; - case MO_COMBOFINISH: // 猛龍拳 - damage = damage * (240 + 60 * skill_lv) / 100; - damage2 = damage2 * (240 + 60 * skill_lv) / 100; - break; - case BA_MUSICALSTRIKE: // ミュージカルストライク - if (!sd->state.arrow_atk && sd->arrow_atk > 0) - { - int arr = MRAND ((sd->arrow_atk + 1)); - damage += arr; - damage2 += arr; - } - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - if (sd->arrow_ele > 0) - { - s_ele = sd->arrow_ele; - s_ele_ = sd->arrow_ele; - } - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - sd->state.arrow_atk = 1; - break; - case DC_THROWARROW: // 矢撃ち - if (!sd->state.arrow_atk && sd->arrow_atk > 0) - { - int arr = MRAND ((sd->arrow_atk + 1)); - damage += arr; - damage2 += arr; - } - damage = damage * (100 + 50 * skill_lv) / 100; - damage2 = damage2 * (100 + 50 * skill_lv) / 100; - if (sd->arrow_ele > 0) - { - s_ele = sd->arrow_ele; - s_ele_ = sd->arrow_ele; - } - flag = (flag & ~BF_RANGEMASK) | BF_LONG; - sd->state.arrow_atk = 1; - break; - case CH_TIGERFIST: // 伏虎拳 - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - break; - case CH_CHAINCRUSH: // 連柱崩撃 - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - div_ = skill_get_num (skill_num, skill_lv); - break; - case CH_PALMSTRIKE: // 猛虎硬派山 - damage = damage * (50 + 100 * skill_lv) / 100; - damage2 = damage2 * (50 + 100 * skill_lv) / 100; - break; - case LK_SPIRALPIERCE: /* スパイラルピアース */ - damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に - damage2 = damage2 * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に - div_ = 5; - if (tsd) - tsd->canmove_tick = gettick () + 1000; - else if (tmd) - tmd->canmove_tick = gettick () + 1000; - break; - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - damage = damage * (100 + 20 * skill_lv) / 100; - damage2 = damage2 * (100 + 20 * skill_lv) / 100; - break; - case LK_JOINTBEAT: /* ジョイントビート */ - damage = damage * (50 + 10 * skill_lv) / 100; - damage2 = damage2 * (50 + 10 * skill_lv) / 100; - break; - case ASC_METEORASSAULT: /* メテオアサルト */ - damage = damage * (40 + 40 * skill_lv) / 100; - damage2 = damage2 * (40 + 40 * skill_lv) / 100; - break; - case SN_SHARPSHOOTING: /* シャープシューティング */ - damage += damage * (30 * skill_lv) / 100; - damage2 += damage2 * (30 * skill_lv) / 100; - break; - case CG_ARROWVULCAN: /* アローバルカン */ - damage = damage * (160 + 40 * skill_lv) / 100; - damage2 = damage2 * (160 + 40 * skill_lv) / 100; - div_ = 9; - break; - case AS_SPLASHER: /* ベナムスプラッシャー */ - damage = - damage * (200 + 20 * skill_lv + - 20 * pc_checkskill (sd, - AS_POISONREACT)) / 100; - damage2 = - damage2 * (200 + 20 * skill_lv + - 20 * pc_checkskill (sd, - AS_POISONREACT)) / 100; - break; - case PA_SACRIFICE: - if (sd) - { - int hp, mhp, damage3; - hp = battle_get_hp (src); - mhp = battle_get_max_hp (src); - damage3 = mhp * ((skill_lv / 2) + (50 / 100)) / 100; - damage = - (((skill_lv * 15) + 90) / 100) * damage3 / 100; - damage2 = - (((skill_lv * 15) + 90) / 100) * damage3 / 100; - } - break; - case ASC_BREAKER: // -- moonsoul (special damage for ASC_BREAKER skill) - if (sd) - { - int damage3; - int mdef1 = battle_get_mdef (target); - int mdef2 = battle_get_mdef2 (target); - int imdef_flag = 0; - - damage = - ((damage * 5) + - (skill_lv * battle_get_int (src) * 5) + - MRAND (500) + 500) / 2; - damage2 = - ((damage2 * 5) + - (skill_lv * battle_get_int (src) * 5) + - MRAND (500) + 500) / 2; - damage3 = damage; - hitrate = 1000000; - - if (sd->ignore_mdef_ele & (1 << t_ele) - || sd->ignore_mdef_race & (1 << t_race)) - imdef_flag = 1; - if (t_mode & 0x20) - { - if (sd->ignore_mdef_race & (1 << 10)) - imdef_flag = 1; - } - else - { - if (sd->ignore_mdef_race & (1 << 11)) - imdef_flag = 1; - } - if (!imdef_flag) - { - if (battle_config.magic_defense_type) - { - damage3 = - damage3 - - (mdef1 * - battle_config.magic_defense_type) - - mdef2; - } - else - { - damage3 = - (damage3 * (100 - mdef1)) / 100 - mdef2; - } - } - - if (damage3 < 1) - damage3 = 1; - - damage3 = - battle_attr_fix (damage2, s_ele_, - battle_get_element (target)); - } - break; - } - } - if (da == 2) - { //三段掌が発動しているか - type = 0x08; - div_ = 255; //三段掌用に… - damage = - damage * (100 + - 20 * pc_checkskill (sd, MO_TRIPLEATTACK)) / 100; - } - - if (skill_num != NPC_CRITICALSLASH) - { - // 対 象の防御力によるダメージの減少 - // ディバインプロテクション(ここでいいのかな?) - if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST - && skill_num != KN_AUTOCOUNTER && def1 < 1000000) - { //DEF, VIT無視 - int t_def; - target_count = - 1 + battle_counttargeted (target, src, - battle_config.vit_penaly_count_lv); - if (battle_config.vit_penaly_type > 0) - { - if (target_count >= battle_config.vit_penaly_count) - { - if (battle_config.vit_penaly_type == 1) - { - def1 = - (def1 * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - def2 = - (def2 * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - t_vit = - (t_vit * - (100 - - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num)) / - 100; - } - else if (battle_config.vit_penaly_type == 2) - { - def1 -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - def2 -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - t_vit -= - (target_count - - (battle_config.vit_penaly_count - - 1)) * battle_config.vit_penaly_num; - } - if (def1 < 0) - def1 = 0; - if (def2 < 1) - def2 = 1; - if (t_vit < 1) - t_vit = 1; - } - } - t_def = def2 * 8 / 10; - vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; - if (sd->ignore_def_ele & (1 << t_ele) - || sd->ignore_def_race & (1 << t_race)) - idef_flag = 1; - if (sd->ignore_def_ele_ & (1 << t_ele) - || sd->ignore_def_race_ & (1 << t_race)) - idef_flag_ = 1; - if (t_mode & 0x20) - { - if (sd->ignore_def_race & (1 << 10)) - idef_flag = 1; - if (sd->ignore_def_race_ & (1 << 10)) - idef_flag_ = 1; - } - else - { - if (sd->ignore_def_race & (1 << 11)) - idef_flag = 1; - if (sd->ignore_def_race_ & (1 << 11)) - idef_flag_ = 1; - } - - if (!idef_flag) - { - if (battle_config.player_defense_type) - { - damage = - damage - - (def1 * battle_config.player_defense_type) - - t_def - - ((vitbonusmax < - 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - else - { - damage = - damage * (100 - def1) / 100 - t_def - - ((vitbonusmax < - 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - } - if (!idef_flag_) - { - if (battle_config.player_defense_type) - { - damage2 = - damage2 - - (def1 * battle_config.player_defense_type) - - t_def - - ((vitbonusmax < - 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - else - { - damage2 = - damage2 * (100 - def1) / 100 - t_def - - ((vitbonusmax < - 1) ? 0 : MRAND ((vitbonusmax + 1))); - } - } - } - } - } - // 精錬ダメージの追加 - if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) - { //DEF, VIT無視 - damage += battle_get_atk2 (src); - damage2 += battle_get_atk_2 (src); - } - if (skill_num == CR_SHIELDBOOMERANG) - { - if (sd->equip_index[8] >= 0) - { - int index = sd->equip_index[8]; - if (sd->inventory_data[index] - && sd->inventory_data[index]->type == 5) - { - damage += sd->inventory_data[index]->weight / 10; - damage += - sd->status.inventory[index].refine * pc_getrefinebonus (0, - 1); - } - } - } - if (skill_num == LK_SPIRALPIERCE) - { /* スパイラルピアース */ - if (sd->equip_index[9] >= 0) - { //重量で追加ダメージらしいのでシールドブーメランを参考に追加 - int index = sd->equip_index[9]; - if (sd->inventory_data[index] - && sd->inventory_data[index]->type == 4) - { - damage += - (int) (double) (sd->inventory_data[index]->weight * - (0.8 * skill_lv * 4 / 10)); - damage += - sd->status.inventory[index].refine * pc_getrefinebonus (0, - 1); - } - } - } - - // 0未満だった場合1に補正 - if (damage < 1) - damage = 1; - if (damage2 < 1) - damage2 = 1; - - // スキル修正2(修練系) - // 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応) - if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST - && skill_num != CR_GRANDCROSS) - { //修練ダメージ無視 - damage = battle_addmastery (sd, target, damage, 0); - damage2 = battle_addmastery (sd, target, damage2, 1); - } - - if (sd->perfect_hit > 0) - { - if (MRAND (100) < sd->perfect_hit) - hitrate = 1000000; - } - - // 回避修正 - hitrate = (hitrate < 5) ? 5 : hitrate; - if (hitrate < 1000000 && // 必中攻撃 - (t_sc_data != NULL && (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)))) // 凍結は必中 - hitrate = 1000000; - if (type == 0 && MRAND (100) >= hitrate) - { - damage = damage2 = 0; - dmg_lv = ATK_FLEE; - } - else - { - dmg_lv = ATK_DEF; - } - // スキル修正3(武器研究) - if ((skill = pc_checkskill (sd, BS_WEAPONRESEARCH)) > 0) - { - damage += skill * 2; - damage2 += skill * 2; - } - //Advanced Katar Research by zanetheinsane - if (sd->weapontype1 == 0x10 || sd->weapontype2 == 0x10) - { - if ((skill = pc_checkskill (sd, ASC_KATAR)) > 0) - { - damage += (damage * ((skill * 2) + 10)) / 100; - } - } - -//スキルによるダメージ補正ここまで - -//カードによるダメージ追加処理ここから - cardfix = 100; - if (!sd->state.arrow_atk) - { //弓矢以外 - if (!battle_config.left_cardfix_to_right) - { //左手カード補正設定無し - cardfix = cardfix * (100 + sd->addrace[t_race]) / 100; // 種族によるダメージ修正 - cardfix = cardfix * (100 + sd->addele[t_ele]) / 100; // 属性によるダメージ修正 - cardfix = cardfix * (100 + sd->addsize[t_size]) / 100; // サイズによるダメージ修正 - } - else - { - cardfix = cardfix * (100 + sd->addrace[t_race] + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正(左手による追加あり) - cardfix = cardfix * (100 + sd->addele[t_ele] + sd->addele_[t_ele]) / 100; // 属性によるダメージ修正(左手による追加あり) - cardfix = cardfix * (100 + sd->addsize[t_size] + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正(左手による追加あり) - } - } - else - { //弓矢 - cardfix = cardfix * (100 + sd->addrace[t_race] + sd->arrow_addrace[t_race]) / 100; // 種族によるダメージ修正(弓矢による追加あり) - cardfix = cardfix * (100 + sd->addele[t_ele] + sd->arrow_addele[t_ele]) / 100; // 属性によるダメージ修正(弓矢による追加あり) - cardfix = cardfix * (100 + sd->addsize[t_size] + sd->arrow_addsize[t_size]) / 100; // サイズによるダメージ修正(弓矢による追加あり) - } - if (t_mode & 0x20) - { //ボス - if (!sd->state.arrow_atk) - { //弓矢攻撃以外なら - if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し - cardfix = cardfix * (100 + sd->addrace[10]) / 100; //ボスモンスターに追加ダメージ - else //左手カード補正設定あり - cardfix = cardfix * (100 + sd->addrace[10] + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ(左手による追加あり) - } - else //弓矢攻撃 - cardfix = cardfix * (100 + sd->addrace[10] + sd->arrow_addrace[10]) / 100; //ボスモンスターに追加ダメージ(弓矢による追加あり) - } - else - { //ボスじゃない - if (!sd->state.arrow_atk) - { //弓矢攻撃以外 - if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し - cardfix = cardfix * (100 + sd->addrace[11]) / 100; //ボス以外モンスターに追加ダメージ - else //左手カード補正設定あり - cardfix = cardfix * (100 + sd->addrace[11] + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ(左手による追加あり) - } - else - cardfix = cardfix * (100 + sd->addrace[11] + sd->arrow_addrace[11]) / 100; //ボス以外モンスターに追加ダメージ(弓矢による追加あり) - } - //特定Class用補正処理(少女の日記→ボンゴン用?) - t_class = battle_get_class (target); - for (i = 0; i < sd->add_damage_class_count; i++) - { - if (sd->add_damage_classid[i] == t_class) - { - cardfix = cardfix * (100 + sd->add_damage_classrate[i]) / 100; - break; - } - } - if (skill_num != CR_GRANDCROSS || !battle_config.gx_cardfix) - damage = damage * cardfix / 100; //カード補正によるダメージ増加 -//カードによるダメージ増加処理ここまで - -//カードによるダメージ追加処理(左手)ここから - cardfix = 100; - if (!battle_config.left_cardfix_to_right) - { //左手カード補正設定無し - cardfix = cardfix * (100 + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正左手 - cardfix = cardfix * (100 + sd->addele_[t_ele]) / 100; // 属 性によるダメージ修正左手 - cardfix = cardfix * (100 + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正左手 - if (t_mode & 0x20) //ボス - cardfix = cardfix * (100 + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ左手 - else - cardfix = cardfix * (100 + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ左手 - } - //特定Class用補正処理左手(少女の日記→ボンゴン用?) - for (i = 0; i < sd->add_damage_class_count_; i++) - { - if (sd->add_damage_classid_[i] == t_class) - { - cardfix = cardfix * (100 + sd->add_damage_classrate_[i]) / 100; - break; - } - } - if (skill_num != CR_GRANDCROSS) - damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ増加 -//カードによるダメージ増加処理(左手)ここまで - -// -- moonsoul (cardfix for magic damage portion of ASC_BREAKER) - if (skill_num == ASC_BREAKER) - damage3 = damage3 * cardfix / 100; - -//カードによるダメージ減衰処理ここから - if (tsd) - { //対象がPCの場合 - cardfix = 100; - cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 - cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属性によるダメージ耐性 - if (battle_get_mode (src) & 0x20) - cardfix = cardfix * (100 - tsd->subrace[10]) / 100; //ボスからの攻撃はダメージ減少 - else - cardfix = cardfix * (100 - tsd->subrace[11]) / 100; //ボス以外からの攻撃はダメージ減少 - //特定Class用補正処理左手(少女の日記→ボンゴン用?) - for (i = 0; i < tsd->add_def_class_count; i++) - { - if (tsd->add_def_classid[i] == sd->status.pc_class) - { - cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; - break; - } - } - if (flag & BF_LONG) - cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; //遠距離攻撃はダメージ減少(ホルンCとか) - if (flag & BF_SHORT) - cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; //近距離攻撃はダメージ減少(該当無し?) - damage = damage * cardfix / 100; //カード補正によるダメージ減少 - damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ減少 - } -//カードによるダメージ減衰処理ここまで - -//対象にステータス異常がある場合のダメージ減算処理ここから - if (t_sc_data) - { - cardfix = 100; - if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) //ディフェンダー状態で遠距離攻撃 - cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; //ディフェンダーによる減衰 - if (cardfix != 100) - { - damage = damage * cardfix / 100; //ディフェンダー補正によるダメージ減少 - damage2 = damage2 * cardfix / 100; //ディフェンダー補正による左手ダメージ減少 - } - if (t_sc_data[SC_ASSUMPTIO].timer != -1) - { //アスムプティオ - if (!map[target->m].flag.pvp) - { - damage = damage / 3; - damage2 = damage2 / 3; - } - else - { - damage = damage / 2; - damage2 = damage2 / 2; - } - } - } -//対象にステータス異常がある場合のダメージ減算処理ここまで - - if (damage < 0) - damage = 0; - if (damage2 < 0) - damage2 = 0; - - // 属 性の適用 - damage = battle_attr_fix (damage, s_ele, battle_get_element (target)); - damage2 = battle_attr_fix (damage2, s_ele_, battle_get_element (target)); - - // 星のかけら、気球の適用 - damage += sd->star; - damage2 += sd->star_; - damage += sd->spiritball * 3; - damage2 += sd->spiritball * 3; - - if (sc_data && sc_data[SC_AURABLADE].timer != -1) - { /* オーラブレード 必中 */ - damage += sc_data[SC_AURABLADE].val1 * 10; - damage2 += sc_data[SC_AURABLADE].val1 * 10; - } - if (skill_num == PA_PRESSURE) - { /* プレッシャー 必中? */ - damage = 700 + 100 * skill_lv; - damage2 = 700 + 100 * skill_lv; - } - - // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ! - // >map_session_data に左手ダメージ(atk,atk2)追加して - // >pc_calcstatus()でやるべきかな? - // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して - // pc_calcstatus()でデータを入力しています - - //左手のみ武器装備 - if (sd->weapontype1 == 0 && sd->weapontype2 > 0) - { - damage = damage2; - damage2 = 0; - } - // 右手、左手修練の適用 - if (sd->status.weapon > 16) - { // 二刀流か? - int dmg = damage, dmg2 = damage2; - // 右手修練(60% 〜 100%) 右手全般 - skill = pc_checkskill (sd, AS_RIGHT); - damage = damage * (50 + (skill * 10)) / 100; - if (dmg > 0 && damage < 1) - damage = 1; - // 左手修練(40% 〜 80%) 左手全般 - skill = pc_checkskill (sd, AS_LEFT); - damage2 = damage2 * (30 + (skill * 10)) / 100; - if (dmg2 > 0 && damage2 < 1) - damage2 = 1; - } - else //二刀流でなければ左手ダメージは0 - damage2 = 0; - - // 右手,短剣のみ - if (da == 1) - { //ダブルアタックが発動しているか - div_ = 2; - damage += damage; - type = 0x08; - } - - if (sd->status.weapon == 16) - { - // カタール追撃ダメージ - skill = pc_checkskill (sd, TF_DOUBLE); - damage2 = damage * (1 + (skill * 2)) / 100; - if (damage > 0 && damage2 < 1) - damage2 = 1; - } - - // インベナム修正 - if (skill_num == TF_POISON) - { - damage = - battle_attr_fix (damage + 15 * skill_lv, s_ele, - battle_get_element (target)); - } - if (skill_num == MC_CARTREVOLUTION) - { - damage = battle_attr_fix (damage, 0, battle_get_element (target)); - } - - // 完全回避の判定 - if (skill_num == 0 && skill_lv >= 0 && tsd != NULL && div_ < 255 - && MRAND (1000) < battle_get_flee2 (target)) - { - damage = damage2 = 0; - type = 0x0b; - dmg_lv = ATK_LUCKY; - } - - // 対象が完全回避をする設定がONなら - if (battle_config.enemy_perfect_flee) - { - if (skill_num == 0 && skill_lv >= 0 && tmd != NULL && div_ < 255 - && MRAND (1000) < battle_get_flee2 (target)) - { - damage = damage2 = 0; - type = 0x0b; - dmg_lv = ATK_LUCKY; - } - } - - //MobのModeに頑強フラグが立っているときの処理 - if (t_mode & 0x40) - { - if (damage > 0) - damage = 1; - if (damage2 > 0) - damage2 = 1; - } - - //bNoWeaponDamage(設定アイテム無し?)でグランドクロスじゃない場合はダメージが0 - if (tsd && tsd->special_state.no_weapon_damage - && skill_num != CR_GRANDCROSS) - damage = damage2 = 0; - - if (skill_num != CR_GRANDCROSS && (damage > 0 || damage2 > 0)) - { - if (damage2 < 1) // ダメージ最終修正 - damage = - battle_calc_damage (src, target, damage, div_, skill_num, - skill_lv, flag); - else if (damage < 1) // 右手がミス? - damage2 = - battle_calc_damage (src, target, damage2, div_, skill_num, - skill_lv, flag); - else - { // 両 手/カタールの場合はちょっと計算ややこしい - int d1 = damage + damage2, d2 = damage2; - damage = - battle_calc_damage (src, target, damage + damage2, div_, - skill_num, skill_lv, flag); - damage2 = (d2 * 100 / d1) * damage / 100; - if (damage > 1 && damage2 < 1) - damage2 = 1; - damage -= damage2; - } - } - - /* For executioner card [Valaris] */ - if (src->type == BL_PC && sd->random_attack_increase_add > 0 - && sd->random_attack_increase_per > 0 && skill_num == 0) - { - if (MRAND (100) < sd->random_attack_increase_per) - { - if (damage > 0) - damage *= sd->random_attack_increase_add / 100; - if (damage2 > 0) - damage2 *= sd->random_attack_increase_add / 100; - } - } - /* End addition */ - -// -- moonsoul (final combination of phys, mag damage for ASC_BREAKER) - if (skill_num == ASC_BREAKER) - { - damage += damage3; - damage2 += damage3; - } - - wd.damage = damage; - wd.damage2 = damage2; - wd.type = type; - wd.div_ = div_; - wd.amotion = battle_get_amotion (src); - if (skill_num == KN_AUTOCOUNTER) - wd.amotion >>= 1; - wd.dmotion = battle_get_dmotion (target); - wd.blewcount = blewcount; - wd.flag = flag; - wd.dmg_lv = dmg_lv; - - return wd; -} - -/*========================================== - * 武器ダメージ計算 - *------------------------------------------ - */ -struct Damage battle_calc_weapon_attack (struct block_list *src, - struct block_list *target, - int skill_num, int skill_lv, - int wflag) -{ - struct Damage wd; - - //return前の処理があるので情報出力部のみ変更 - if (src == NULL || target == NULL) - { - nullpo_info (NLP_MARK); - memset (&wd, 0, sizeof (wd)); - return wd; - } - - else if (src->type == BL_PC) - wd = battle_calc_pc_weapon_attack (src, target, skill_num, skill_lv, wflag); // weapon breaking [Valaris] - else if (src->type == BL_MOB) - wd = battle_calc_mob_weapon_attack (src, target, skill_num, skill_lv, - wflag); - else - memset (&wd, 0, sizeof (wd)); - - if (battle_config.equipment_breaking && src->type == BL_PC - && (wd.damage > 0 || wd.damage2 > 0)) - { - struct map_session_data *sd = (struct map_session_data *) src; - if (sd->status.weapon && sd->status.weapon != 11) - { - int breakrate = 1; - if (target->type == BL_PC && sd->sc_data[SC_MELTDOWN].timer != -1) - { - breakrate += 100 * sd->sc_data[SC_MELTDOWN].val1; - if (MRAND (10000) < - breakrate * battle_config.equipment_break_rate / 100 - || breakrate >= 10000) - pc_breakweapon ((struct map_session_data *) target); - } - if (sd->sc_data[SC_OVERTHRUST].timer != -1) - breakrate += 20 * sd->sc_data[SC_OVERTHRUST].val1; - if (wd.type == 0x0a) - breakrate *= 2; - if (MRAND (10000) < - breakrate * battle_config.equipment_break_rate / 100 - || breakrate >= 10000) - { - pc_breakweapon (sd); - memset (&wd, 0, sizeof (wd)); - } - } - } - - if (battle_config.equipment_breaking && target->type == BL_PC - && (wd.damage > 0 || wd.damage2 > 0)) - { - int breakrate = 1; - if (src->type == BL_PC - && ((struct map_session_data *) src)-> - sc_data[SC_MELTDOWN].timer != -1) - breakrate += - 70 * - ((struct map_session_data *) src)->sc_data[SC_MELTDOWN].val1; - if (wd.type == 0x0a) - breakrate *= 2; - if (MRAND (10000) < - breakrate * battle_config.equipment_break_rate / 100 - || breakrate >= 10000) - { - pc_breakarmor ((struct map_session_data *) target); - } - } - - return wd; -} - -/*========================================== - * 魔法ダメージ計算 - *------------------------------------------ - */ -struct Damage battle_calc_magic_attack (struct block_list *bl, - struct block_list *target, - int skill_num, int skill_lv, int flag) -{ - int mdef1 = battle_get_mdef (target); - int mdef2 = battle_get_mdef2 (target); - int matk1, matk2, damage = 0, div_ = 1, blewcount = - skill_get_blewcount (skill_num, skill_lv), rdamage = 0; - struct Damage md; - int aflag; - int normalmagic_flag = 1; - int ele = 0, race = 7, t_ele = 0, t_race = 7, t_mode = - 0, cardfix, t_class, i; - struct map_session_data *sd = NULL, *tsd = NULL; - struct mob_data *tmd = NULL; - - //return前の処理があるので情報出力部のみ変更 - if (bl == NULL || target == NULL) - { - nullpo_info (NLP_MARK); - memset (&md, 0, sizeof (md)); - return md; - } - - matk1 = battle_get_matk1 (bl); - matk2 = battle_get_matk2 (bl); - ele = skill_get_pl (skill_num); - race = battle_get_race (bl); - t_ele = battle_get_elem_type (target); - t_race = battle_get_race (target); - t_mode = battle_get_mode (target); - -#define MATK_FIX( a,b ) { matk1=matk1*(a)/(b); matk2=matk2*(a)/(b); } - - if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) - { - sd->state.attack_type = BF_MAGIC; - if (sd->matk_rate != 100) - MATK_FIX (sd->matk_rate, 100); - sd->state.arrow_atk = 0; - } - if (target->type == BL_PC) - tsd = (struct map_session_data *) target; - else if (target->type == BL_MOB) - tmd = (struct mob_data *) target; - - aflag = BF_MAGIC | BF_LONG | BF_SKILL; - - if (skill_num > 0) - { - switch (skill_num) - { // 基本ダメージ計算(スキルごとに処理) - // ヒールor聖体 - case AL_HEAL: - case PR_BENEDICTIO: - damage = skill_calc_heal (bl, skill_lv) / 2; - normalmagic_flag = 0; - break; - case PR_ASPERSIO: /* アスペルシオ */ - damage = 40; //固定ダメージ - normalmagic_flag = 0; - break; - case PR_SANCTUARY: // サンクチュアリ - damage = (skill_lv > 6) ? 388 : skill_lv * 50; - normalmagic_flag = 0; - blewcount |= 0x10000; - break; - case ALL_RESURRECTION: - case PR_TURNUNDEAD: // 攻撃リザレクションとターンアンデッド - if (target->type != BL_PC - && battle_check_undead (t_race, t_ele)) - { - int hp, mhp, thres; - hp = battle_get_hp (target); - mhp = battle_get_max_hp (target); - thres = (skill_lv * 20) + battle_get_luk (bl) + - battle_get_int (bl) + battle_get_lv (bl) + - ((200 - hp * 200 / mhp)); - if (thres > 700) - thres = 700; -// if(battle_config.battle_log) -// printf("ターンアンデッド! 確率%d ‰(千分率)\n", thres); - if (MRAND (1000) < thres && !(t_mode & 0x20)) // 成功 - damage = hp; - else // 失敗 - damage = - battle_get_lv (bl) + battle_get_int (bl) + - skill_lv * 10; - } - normalmagic_flag = 0; - break; - - case MG_NAPALMBEAT: // ナパームビート(分散計算込み) - MATK_FIX (70 + skill_lv * 10, 100); - if (flag > 0) - { - MATK_FIX (1, flag); - } - else - { - if (battle_config.error_log) - printf - ("battle_calc_magic_attack(): napam enemy count=0 !\n"); - } - break; - case MG_FIREBALL: // ファイヤーボール - { - const int drate[] = { 100, 90, 70 }; - if (flag > 2) - matk1 = matk2 = 0; - else - MATK_FIX ((95 + skill_lv * 5) * drate[flag], 10000); - } - break; - case MG_FIREWALL: // ファイヤーウォール -/* - if( (t_ele!=3 && !battle_check_undead(t_race,t_ele)) || target->type==BL_PC ) //PCは火属性でも飛ぶ?そもそもダメージ受ける? - blewcount |= 0x10000; - else - blewcount = 0; -*/ - if ((t_ele == 3 || battle_check_undead (t_race, t_ele)) - && target->type != BL_PC) - blewcount = 0; - else - blewcount |= 0x10000; - MATK_FIX (1, 2); - break; - case MG_THUNDERSTORM: // サンダーストーム - MATK_FIX (80, 100); - break; - case MG_FROSTDIVER: // フロストダイバ - MATK_FIX (100 + skill_lv * 10, 100); - break; - case WZ_FROSTNOVA: // フロストダイバ - MATK_FIX (((100 + skill_lv * 10) * (2 / 3)), 100); - break; - case WZ_FIREPILLAR: // ファイヤーピラー - if (mdef1 < 1000000) - mdef1 = mdef2 = 0; // MDEF無視 - MATK_FIX (1, 5); - matk1 += 50; - matk2 += 50; - break; - case WZ_SIGHTRASHER: - MATK_FIX (100 + skill_lv * 20, 100); - break; - case WZ_METEOR: - case WZ_JUPITEL: // ユピテルサンダー - break; - case WZ_VERMILION: // ロードオブバーミリオン - MATK_FIX (skill_lv * 20 + 80, 100); - break; - case WZ_WATERBALL: // ウォーターボール - matk1 += skill_lv * 30; - matk2 += skill_lv * 30; - break; - case WZ_STORMGUST: // ストームガスト - MATK_FIX (skill_lv * 40 + 100, 100); - blewcount |= 0x10000; - break; - case AL_HOLYLIGHT: // ホーリーライト - MATK_FIX (125, 100); - break; - case AL_RUWACH: - MATK_FIX (145, 100); - break; - case HW_NAPALMVULCAN: // ナパームビート(分散計算込み) - MATK_FIX (70 + skill_lv * 10, 100); - if (flag > 0) - { - MATK_FIX (1, flag); - } - else - { - if (battle_config.error_log) - printf - ("battle_calc_magic_attack(): napalmvulcan enemy count=0 !\n"); - } - break; - } - } - - if (normalmagic_flag) - { // 一般魔法ダメージ計算 - int imdef_flag = 0; - if (matk1 > matk2) - damage = matk2 + MRAND ((matk1 - matk2 + 1)); - else - damage = matk2; - if (sd) - { - if (sd->ignore_mdef_ele & (1 << t_ele) - || sd->ignore_mdef_race & (1 << t_race)) - imdef_flag = 1; - if (t_mode & 0x20) - { - if (sd->ignore_mdef_race & (1 << 10)) - imdef_flag = 1; - } - else - { - if (sd->ignore_mdef_race & (1 << 11)) - imdef_flag = 1; - } - } - if (!imdef_flag) - { - if (battle_config.magic_defense_type) - { - damage = - damage - (mdef1 * battle_config.magic_defense_type) - - mdef2; - } - else - { - damage = (damage * (100 - mdef1)) / 100 - mdef2; - } - } - - if (damage < 1) - damage = 1; - } - - if (sd) - { - cardfix = 100; - cardfix = cardfix * (100 + sd->magic_addrace[t_race]) / 100; - cardfix = cardfix * (100 + sd->magic_addele[t_ele]) / 100; - if (t_mode & 0x20) - cardfix = cardfix * (100 + sd->magic_addrace[10]) / 100; - else - cardfix = cardfix * (100 + sd->magic_addrace[11]) / 100; - t_class = battle_get_class (target); - for (i = 0; i < sd->add_magic_damage_class_count; i++) - { - if (sd->add_magic_damage_classid[i] == t_class) - { - cardfix = - cardfix * (100 + sd->add_magic_damage_classrate[i]) / 100; - break; - } - } - damage = damage * cardfix / 100; - } - - if (tsd) - { - int s_class = battle_get_class (bl); - cardfix = 100; - cardfix = cardfix * (100 - tsd->subele[ele]) / 100; // 属 性によるダメージ耐性 - cardfix = cardfix * (100 - tsd->subrace[race]) / 100; // 種族によるダメージ耐性 - cardfix = cardfix * (100 - tsd->magic_subrace[race]) / 100; - if (battle_get_mode (bl) & 0x20) - cardfix = cardfix * (100 - tsd->magic_subrace[10]) / 100; - else - cardfix = cardfix * (100 - tsd->magic_subrace[11]) / 100; - for (i = 0; i < tsd->add_mdef_class_count; i++) - { - if (tsd->add_mdef_classid[i] == s_class) - { - cardfix = cardfix * (100 - tsd->add_mdef_classrate[i]) / 100; - break; - } - } - cardfix = cardfix * (100 - tsd->magic_def_rate) / 100; - damage = damage * cardfix / 100; - } - if (damage < 0) - damage = 0; - - damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属 性修正 - - if (skill_num == CR_GRANDCROSS) - { // グランドクロス - struct Damage wd; - wd = battle_calc_weapon_attack (bl, target, skill_num, skill_lv, - flag); - damage = (damage + wd.damage) * (100 + 40 * skill_lv) / 100; - if (battle_config.gx_dupele) - damage = battle_attr_fix (damage, ele, battle_get_element (target)); //属性2回かかる - if (bl == target) - damage = damage / 2; //反動は半分 - } - - div_ = skill_get_num (skill_num, skill_lv); - - if (div_ > 1 && skill_num != WZ_VERMILION) - damage *= div_; - -// if(mdef1 >= 1000000 && damage > 0) - if (t_mode & 0x40 && damage > 0) - damage = 1; - - if (tsd && tsd->special_state.no_magic_damage) - { - if (battle_config.gtb_pvp_only != 0) - { // [MouseJstr] - if ((map[target->m].flag.pvp || map[target->m].flag.gvg) - && target->type == BL_PC) - damage = (damage * (100 - battle_config.gtb_pvp_only)) / 100; - } - else - damage = 0; // 黄 金蟲カード(魔法ダメージ0) - } - - damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 - - /* magic_damage_return by [AppleGirl] and [Valaris] */ - if (target->type == BL_PC && tsd && tsd->magic_damage_return > 0) - { - rdamage += damage * tsd->magic_damage_return / 100; - if (rdamage < 1) - rdamage = 1; - clif_damage (target, bl, gettick (), 0, 0, rdamage, 0, 0, 0); - battle_damage (target, bl, rdamage, 0); - } - /* end magic_damage_return */ - - md.damage = damage; - md.div_ = div_; - md.amotion = battle_get_amotion (bl); - md.dmotion = battle_get_dmotion (target); - md.damage2 = 0; - md.type = 0; - md.blewcount = blewcount; - md.flag = aflag; - - return md; -} - -/*========================================== - * その他ダメージ計算 - *------------------------------------------ - */ -struct Damage battle_calc_misc_attack (struct block_list *bl, - struct block_list *target, - int skill_num, int skill_lv, int flag) -{ - int int_ = battle_get_int (bl); -// int luk=battle_get_luk(bl); - int dex = battle_get_dex (bl); - int skill, ele, race, cardfix; - 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_LONG | BF_SKILL; - - //return前の処理があるので情報出力部のみ変更 - if (bl == NULL || target == NULL) - { - nullpo_info (NLP_MARK); - memset (&md, 0, sizeof (md)); - return md; - } - - if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) - { - sd->state.attack_type = BF_MISC; - sd->state.arrow_atk = 0; - } - - if (target->type == BL_PC) - tsd = (struct map_session_data *) target; - - 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; - break; - - case TF_THROWSTONE: // 石投げ - damage = 30; - damagefix = 0; - break; - - case BA_DISSONANCE: // 不協和音 - damage = - (skill_lv) * 20 + pc_checkskill (sd, BA_MUSICALLESSON) * 3; - break; - - case NPC_SELFDESTRUCTION: // 自爆 - damage = battle_get_hp (bl) - (bl == target ? 1 : 0); - damagefix = 0; - break; - - case NPC_SMOKING: // タバコを吸う - damage = 3; - damagefix = 0; - break; - - case NPC_DARKBREATH: - { - struct status_change *sc_data = battle_get_sc_data (target); - int hitrate = - battle_get_hit (bl) - battle_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 (MRAND (100) < hitrate) - { - damage = 500 + (skill_lv - 1) * 1000 + MRAND (1000); - if (damage > 9999) - damage = 9999; - } - } - break; - case SN_FALCONASSAULT: /* ファルコンアサルト */ - skill = pc_checkskill (sd, HT_BLITZBEAT); - damage = - (100 + 50 * skill_lv + - (dex / 10 + int_ / 2 + skill * 3 + 40) * 2); - break; - } - - ele = skill_get_pl (skill_num); - race = battle_get_race (bl); - - 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->subrace[race]) / 100; // 種族によるダメージ耐性 - cardfix = cardfix * (100 - tsd->misc_def_rate) / 100; - damage = damage * cardfix / 100; - } - if (damage < 0) - damage = 0; - damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属性修正 - } - - div_ = skill_get_num (skill_num, skill_lv); - if (div_ > 1) - damage *= div_; - - if (damage > 0 - && (damage < div_ - || (battle_get_def (target) >= 1000000 - && battle_get_mdef (target) >= 1000000))) - { - damage = div_; - } - - damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 - - md.damage = damage; - md.div_ = div_; - md.amotion = battle_get_amotion (bl); - md.dmotion = battle_get_dmotion (target); - md.damage2 = 0; - md.type = 0; - 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; - memset (&d, 0, sizeof (d)); - - switch (attack_type) - { - case BF_WEAPON: - return battle_calc_weapon_attack (bl, target, skill_num, skill_lv, - flag); - case BF_MAGIC: - return battle_calc_magic_attack (bl, target, skill_num, skill_lv, - flag); - case BF_MISC: - return battle_calc_misc_attack (bl, target, skill_num, skill_lv, - flag); - default: - if (battle_config.error_log) - printf ("battle_calc_attack: unknwon attack type ! %d\n", - attack_type); - break; - } - return d; -} - -/*========================================== - * 通常攻撃処理まとめ - *------------------------------------------ - */ -int battle_weapon_attack (struct block_list *src, struct block_list *target, - unsigned int tick, int flag) -{ - struct map_session_data *sd = NULL; - struct status_change *sc_data = battle_get_sc_data (src), *t_sc_data = - battle_get_sc_data (target); - short *opt1; - int race = 7, ele = 0; - int damage, rdamage = 0; - struct Damage wd; - - nullpo_retr (0, src); - nullpo_retr (0, target); - - if (src->type == BL_PC) - sd = (struct map_session_data *) src; - - if (src->prev == NULL || target->prev == NULL) - return 0; - if (src->type == BL_PC && pc_isdead (sd)) - return 0; - if (target->type == BL_PC - && pc_isdead ((struct map_session_data *) target)) - return 0; - - opt1 = battle_get_opt1 (src); - if (opt1 && *opt1 > 0) - { - battle_stopattack (src); - return 0; - } - if (sc_data && sc_data[SC_BLADESTOP].timer != -1) - { - battle_stopattack (src); - return 0; - } - - race = battle_get_race (target); - ele = battle_get_elem_type (target); - if (battle_check_target (src, target, BCT_ENEMY) > 0 && - battle_check_range (src, target, 0)) - { - // 攻撃対象となりうるので攻撃 - 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; - } - } - if (flag & 0x8000) - { - if (sd && battle_config.pc_attack_direction_change) - sd->dir = sd->head_dir = - map_calc_dir (src, target->x, target->y); - else if (src->type == BL_MOB - && battle_config.monster_attack_direction_change) - ((struct mob_data *) src)->dir = - map_calc_dir (src, target->x, target->y); - wd = battle_calc_weapon_attack (src, target, KN_AUTOCOUNTER, - flag & 0xff, 0); - } - else - wd = battle_calc_weapon_attack (src, target, 0, 0, 0); - - // significantly increase injuries for hasted characters - if (wd.damage > 0 && (t_sc_data[SC_HASTE].timer != -1)) - { - wd.damage = (wd.damage * (16 + t_sc_data[SC_HASTE].val1)) >> 4; - } - - if (wd.damage > 0 - && t_sc_data[SC_PHYS_SHIELD].timer != -1 && target->type == BL_PC) - { - int reduction = t_sc_data[SC_PHYS_SHIELD].val1; - if (reduction > wd.damage) - reduction = wd.damage; - - wd.damage -= reduction; - MAP_LOG_PC (((struct map_session_data *) target), - "MAGIC-ABSORB-DMG %d", reduction); - } - - if ((damage = wd.damage + wd.damage2) > 0 && src != target) - { - if (wd.flag & BF_SHORT) - { - if (target->type == BL_PC) - { - struct map_session_data *tsd = - (struct map_session_data *) target; - if (tsd && tsd->short_weapon_damage_return > 0) - { - rdamage += - damage * tsd->short_weapon_damage_return / 100; - if (rdamage < 1) - rdamage = 1; - } - } - if (t_sc_data && t_sc_data[SC_REFLECTSHIELD].timer != -1) - { - rdamage += - damage * t_sc_data[SC_REFLECTSHIELD].val2 / 100; - if (rdamage < 1) - rdamage = 1; - } - } - else if (wd.flag & BF_LONG) - { - if (target->type == BL_PC) - { - struct map_session_data *tsd = - (struct map_session_data *) target; - if (tsd && tsd->long_weapon_damage_return > 0) - { - rdamage += - damage * tsd->long_weapon_damage_return / 100; - if (rdamage < 1) - rdamage = 1; - } - } - } - - if (rdamage > 0) - clif_damage (src, src, tick, wd.amotion, 0, rdamage, 1, 4, 0); - } - - if (wd.div_ == 255 && sd) - { //三段掌 - int delay = - 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); - int skilllv; - if (wd.damage + wd.damage2 < battle_get_hp (target)) - { - if ((skilllv = pc_checkskill (sd, MO_CHAINCOMBO)) > 0) - delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 - - skill_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); - clif_skill_damage (src, target, tick, wd.amotion, wd.dmotion, - wd.damage, 3, MO_TRIPLEATTACK, - pc_checkskill (sd, MO_TRIPLEATTACK), -1); - } - else - { - 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 (); - - if (src->type == BL_PC) - { - int weapon_index = sd->equip_index[9]; - int weapon = 0; - if (sd->inventory_data[weapon_index] - && sd->status.inventory[weapon_index].equip & 0x2) - weapon = sd->inventory_data[weapon_index]->nameid; - - MAP_LOG ("PC%d %d:%d,%d WPNDMG %s%d %d FOR %d WPN %d", - sd->status.char_id, src->m, src->x, src->y, - (target->type == BL_PC) ? "PC" : "MOB", - (target->type == - BL_PC) ? ((struct map_session_data *) target)-> - status.char_id : target->id, - (target->type == - BL_PC) ? 0 : ((struct mob_data *) target)->mob_class, - wd.damage + wd.damage2, weapon); - } - - if (target->type == BL_PC) - { - struct map_session_data *sd2 = (struct map_session_data *) target; - MAP_LOG ("PC%d %d:%d,%d WPNINJURY %s%d %d FOR %d", - sd2->status.char_id, target->m, target->x, target->y, - (src->type == BL_PC) ? "PC" : "MOB", - (src->type == - BL_PC) ? ((struct map_session_data *) src)-> - status.char_id : src->id, - (src->type == - BL_PC) ? 0 : ((struct mob_data *) src)->mob_class, - wd.damage + wd.damage2); - } - - battle_damage (src, target, (wd.damage + wd.damage2), 0); - if (target->prev != NULL && - (target->type != BL_PC - || (target->type == BL_PC - && !pc_isdead ((struct map_session_data *) target)))) - { - if (wd.damage > 0 || wd.damage2 > 0) - { - skill_additional_effect (src, target, 0, 0, BF_WEAPON, tick); - if (sd) - { - if (sd->weapon_coma_ele[ele] > 0 - && MRAND (10000) < sd->weapon_coma_ele[ele]) - battle_damage (src, target, - battle_get_max_hp (target), 1); - if (sd->weapon_coma_race[race] > 0 - && MRAND (10000) < sd->weapon_coma_race[race]) - battle_damage (src, target, - battle_get_max_hp (target), 1); - if (battle_get_mode (target) & 0x20) - { - if (sd->weapon_coma_race[10] > 0 - && MRAND (10000) < sd->weapon_coma_race[10]) - battle_damage (src, target, - battle_get_max_hp (target), 1); - } - else - { - if (sd->weapon_coma_race[11] > 0 - && MRAND (10000) < sd->weapon_coma_race[11]) - battle_damage (src, target, - battle_get_max_hp (target), 1); - } - } - } - } - if (sc_data && sc_data[SC_AUTOSPELL].timer != -1 - && MRAND (100) < sc_data[SC_AUTOSPELL].val4) - { - int skilllv = sc_data[SC_AUTOSPELL].val3, i, f = 0; - i = MRAND (100); - if (i >= 50) - skilllv -= 2; - else if (i >= 15) - skilllv--; - if (skilllv < 1) - skilllv = 1; - if (sd) - { - int sp = skill_get_sp (sc_data[SC_AUTOSPELL].val2, - skilllv) * 2 / 3; - if (sd->status.sp >= sp) - { - if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) - || i == 32) - f = skill_castend_pos2 (src, target->x, target->y, - sc_data[SC_AUTOSPELL].val2, - skilllv, tick, flag); - else - { - switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) - { - case 0: - case 2: - f = skill_castend_damage_id (src, target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, tick, - flag); - break; - case 1: /* 支援系 */ - if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL - || (sc_data[SC_AUTOSPELL].val2 == - ALL_RESURRECTION - && target->type != BL_PC)) - && battle_check_undead (race, ele)) - f = skill_castend_damage_id (src, target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, - tick, flag); - else - f = skill_castend_nodamage_id (src, - target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, - tick, - flag); - break; - } - } - if (!f) - pc_heal (sd, 0, -sp); - } - } - else - { - if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) - || i == 32) - skill_castend_pos2 (src, target->x, target->y, - sc_data[SC_AUTOSPELL].val2, skilllv, - tick, flag); - else - { - switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) - { - case 0: - case 2: - skill_castend_damage_id (src, target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, tick, flag); - break; - case 1: /* 支援系 */ - if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL - || (sc_data[SC_AUTOSPELL].val2 == - ALL_RESURRECTION - && target->type != BL_PC)) - && battle_check_undead (race, ele)) - skill_castend_damage_id (src, target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, tick, flag); - else - skill_castend_nodamage_id (src, target, - sc_data - [SC_AUTOSPELL].val2, - skilllv, tick, - flag); - break; - } - } - } - } - if (sd) - { - if (sd->autospell_id > 0 && sd->autospell_lv > 0 - && MRAND (100) < sd->autospell_rate) - { - int skilllv = sd->autospell_lv, i, f = 0, sp; - i = MRAND (100); - if (i >= 50) - skilllv -= 2; - else if (i >= 15) - skilllv--; - if (skilllv < 1) - skilllv = 1; - sp = skill_get_sp (sd->autospell_id, skilllv) * 2 / 3; - if (sd->status.sp >= sp) - { - if ((i = skill_get_inf (sd->autospell_id) == 2) - || i == 32) - f = skill_castend_pos2 (src, target->x, target->y, - sd->autospell_id, skilllv, - tick, flag); - else - { - switch (skill_get_nk (sd->autospell_id)) - { - case 0: - case 2: - f = skill_castend_damage_id (src, target, - sd->autospell_id, - skilllv, tick, - flag); - break; - case 1: /* 支援系 */ - if ((sd->autospell_id == AL_HEAL - || (sd->autospell_id == ALL_RESURRECTION - && target->type != BL_PC)) - && battle_check_undead (race, ele)) - f = skill_castend_damage_id (src, target, - sd->autospell_id, - skilllv, - tick, flag); - else - f = skill_castend_nodamage_id (src, - target, - sd->autospell_id, - skilllv, - tick, - flag); - break; - } - } - if (!f) - pc_heal (sd, 0, -sp); - } - } - if (wd.flag & BF_WEAPON && src != target - && (wd.damage > 0 || wd.damage2 > 0)) - { - int hp = 0, sp = 0; - if (sd->hp_drain_rate && sd->hp_drain_per > 0 && wd.damage > 0 - && MRAND (100) < sd->hp_drain_rate) - { - hp += (wd.damage * sd->hp_drain_per) / 100; - if (sd->hp_drain_rate > 0 && hp < 1) - hp = 1; - else if (sd->hp_drain_rate < 0 && hp > -1) - hp = -1; - } - if (sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 - && wd.damage2 > 0 && MRAND (100) < sd->hp_drain_rate_) - { - hp += (wd.damage2 * sd->hp_drain_per_) / 100; - if (sd->hp_drain_rate_ > 0 && hp < 1) - hp = 1; - else if (sd->hp_drain_rate_ < 0 && hp > -1) - hp = -1; - } - if (sd->sp_drain_rate && sd->sp_drain_per > 0 && wd.damage > 0 - && MRAND (100) < sd->sp_drain_rate) - { - sp += (wd.damage * sd->sp_drain_per) / 100; - if (sd->sp_drain_rate > 0 && sp < 1) - sp = 1; - else if (sd->sp_drain_rate < 0 && sp > -1) - sp = -1; - } - if (sd->sp_drain_rate_ && sd->sp_drain_per_ > 0 - && wd.damage2 > 0 && MRAND (100) < sd->sp_drain_rate_) - { - sp += (wd.damage2 * sd->sp_drain_per_) / 100; - if (sd->sp_drain_rate_ > 0 && sp < 1) - sp = 1; - else if (sd->sp_drain_rate_ < 0 && sp > -1) - sp = -1; - } - if (hp || sp) - pc_heal (sd, hp, sp); - } - } - - if (rdamage > 0) - battle_damage (target, src, rdamage, 0); - if (t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1 - && t_sc_data[SC_AUTOCOUNTER].val4 > 0) - { - if (t_sc_data[SC_AUTOCOUNTER].val3 == src->id) - battle_weapon_attack (target, src, tick, - 0x8000 | - t_sc_data[SC_AUTOCOUNTER].val1); - skill_status_change_end (target, SC_AUTOCOUNTER, -1); - } - if (t_sc_data && t_sc_data[SC_BLADESTOP_WAIT].timer != -1) - { - int lv = t_sc_data[SC_BLADESTOP_WAIT].val1; - skill_status_change_end (target, SC_BLADESTOP_WAIT, -1); - skill_status_change_start (src, SC_BLADESTOP, lv, 1, (int) src, - (int) target, - skill_get_time2 (MO_BLADESTOP, lv), 0); - skill_status_change_start (target, SC_BLADESTOP, lv, 2, - (int) target, (int) src, - skill_get_time2 (MO_BLADESTOP, lv), 0); - } - if (t_sc_data && t_sc_data[SC_SPLASHER].timer != -1) //殴ったので対象のベナムスプラッシャー状態を解除 - skill_status_change_end (target, SC_SPLASHER, -1); - - 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; -} - -/*========================================== - * 敵味方判定(1=肯定,0=否定,-1=エラー) - * flag&0xf0000 = 0x00000:敵じゃないか判定(ret:1=敵ではない) - * = 0x10000:パーティー判定(ret:1=パーティーメンバ) - * = 0x20000:全て(ret:1=敵味方両方) - * = 0x40000:敵か判定(ret:1=敵) - * = 0x50000:パーティーじゃないか判定(ret:1=パーティでない) - *------------------------------------------ - */ -int battle_check_target (struct block_list *src, struct block_list *target, - int flag) -{ - int s_p, s_g, t_p, t_g; - struct block_list *ss = src; - - nullpo_retr (0, src); - nullpo_retr (0, target); - - if (flag & 0x40000) - { // 反転フラグ - int ret = battle_check_target (src, target, flag & 0x30000); - if (ret != -1) - return !ret; - return -1; - } - - if (flag & 0x20000) - { - if (target->type == BL_MOB || target->type == BL_PC) - return 1; - else - return -1; - } - - if (src->type == BL_SKILL && target->type == BL_SKILL) // 対象がスキルユニットなら無条件肯定 - return -1; - - if (target->type == BL_PC - && ((struct map_session_data *) target)->invincible_timer != -1) - return -1; - - if (target->type == BL_SKILL) - { - switch (((struct skill_unit *) target)->group->unit_id) - { - case 0x8d: - case 0x8f: - case 0x98: - return 0; - break; - } - } - - // スキルユニットの場合、親を求める - if (src->type == BL_SKILL) - { - int inf2 = - skill_get_inf2 (((struct skill_unit *) src)->group->skill_id); - if ((ss = - map_id2bl (((struct skill_unit *) src)->group->src_id)) == NULL) - return -1; - if (ss->prev == NULL) - return -1; - if (inf2 & 0x80 && (map[src->m].flag.pvp || pc_iskiller ((struct map_session_data *) src, (struct map_session_data *) target)) && // [MouseJstr] - !(target->type == BL_PC - && pc_isinvisible ((struct map_session_data *) target))) - return 0; - if (ss == target) - { - if (inf2 & 0x100) - return 0; - if (inf2 & 0x200) - return -1; - } - } - // Mobでmaster_idがあってspecial_mob_aiなら、召喚主を求める - if (src->type == BL_MOB) - { - struct mob_data *md = (struct mob_data *) src; - if (md && md->master_id > 0) - { - if (md->master_id == target->id) // 主なら肯定 - return 1; - if (md->state.special_mob_ai) - { - if (target->type == BL_MOB) - { //special_mob_aiで対象がMob - struct mob_data *tmd = (struct mob_data *) target; - if (tmd) - { - if (tmd->master_id != md->master_id) //召喚主が一緒でなければ否定 - return 0; - else - { //召喚主が一緒なので肯定したいけど自爆は否定 - if (md->state.special_mob_ai > 2) - return 0; - else - return 1; - } - } - } - } - if ((ss = map_id2bl (md->master_id)) == NULL) - return -1; - } - } - - if (src == target || ss == target) // 同じなら肯定 - return 1; - - if (target->type == BL_PC - && pc_isinvisible ((struct map_session_data *) target)) - return -1; - - if (src->prev == NULL || // 死んでるならエラー - (src->type == BL_PC && pc_isdead ((struct map_session_data *) src))) - return -1; - - if ((ss->type == BL_PC && target->type == BL_MOB) || - (ss->type == BL_MOB && target->type == BL_PC)) - return 0; // PCvsMOBなら否定 - - s_p = battle_get_party_id (ss); - s_g = battle_get_guild_id (ss); - - t_p = battle_get_party_id (target); - t_g = battle_get_guild_id (target); - - if (flag & 0x10000) - { - if (s_p && t_p && s_p == t_p) // 同じパーティなら肯定(味方) - return 1; - else // パーティ検索なら同じパーティじゃない時点で否定 - return 0; - } - - if (ss->type == BL_MOB && s_g > 0 && t_g > 0 && s_g == t_g) // 同じギルド/mobクラスなら肯定(味方) - return 1; - -//printf("ss:%d src:%d target:%d flag:0x%x %d %d ",ss->id,src->id,target->id,flag,src->type,target->type); -//printf("p:%d %d g:%d %d\n",s_p,t_p,s_g,t_g); - - if (ss->type == BL_PC && target->type == BL_PC) - { // 両方PVPモードなら否定(敵) - struct skill_unit *su = NULL; - if (src->type == BL_SKILL) - su = (struct skill_unit *) src; - if (map[ss->m].flag.pvp - || pc_iskiller ((struct map_session_data *) ss, - (struct map_session_data *) target)) - { // [MouseJstr] - if (su && su->group->target_flag == BCT_NOENEMY) - return 1; - else if (battle_config.pk_mode - && (((struct map_session_data *) ss)->status.pc_class == 0 - || ((struct map_session_data *) target)-> - status.pc_class == 0)) - return 1; // prevent novice engagement in pk_mode [Valaris] - else if (map[ss->m].flag.pvp_noparty && s_p > 0 && t_p > 0 - && s_p == t_p) - return 1; - else if (map[ss->m].flag.pvp_noguild && s_g > 0 && t_g > 0 - && s_g == t_g) - return 1; - return 0; - } - if (map[src->m].flag.gvg) - { - struct guild *g = NULL; - if (su && su->group->target_flag == BCT_NOENEMY) - return 1; - if (s_g > 0 && s_g == t_g) - return 1; - if (map[src->m].flag.gvg_noparty && s_p > 0 && t_p > 0 - && s_p == t_p) - return 1; - if ((g = guild_search (s_g))) - { - int i; - for (i = 0; i < MAX_GUILDALLIANCE; i++) - { - if (g->alliance[i].guild_id > 0 - && g->alliance[i].guild_id == t_g) - { - if (g->alliance[i].opposition) - return 0; //敵対ギルドなら無条件に敵 - else - return 1; //同盟ギルドなら無条件に味方 - } - } - } - return 0; - } - } - - return 1; // 該当しないので無関係人物(まあ敵じゃないので味方) -} - -/*========================================== - * 射程判定 - *------------------------------------------ - */ -int battle_check_range (struct block_list *src, struct block_list *bl, - int range) -{ - - int dx, dy; - struct walkpath_data wpd; - int arange; - - nullpo_retr (0, src); - nullpo_retr (0, bl); - - dx = abs (bl->x - src->x); - dy = abs (bl->y - src->y); - arange = ((dx > dy) ? dx : dy); - - if (src->m != bl->m) // 違うマップ - return 0; - - if (range > 0 && range < arange) // 遠すぎる - return 0; - - if (arange < 2) // 同じマスか隣接 - return 1; - -// if(bl->type == BL_SKILL && ((struct skill_unit *)bl)->group->unit_id == 0x8d) -// return 1; - - // 障害物判定 - wpd.path_len = 0; - wpd.path_pos = 0; - wpd.path_half = 0; - if (path_search (&wpd, src->m, src->x, src->y, bl->x, bl->y, 0x10001) != - -1) - return 1; - - dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); - dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); - return (path_search (&wpd, src->m, src->x + dx, src->y + dy, - bl->x - dx, bl->y - dy, 0x10001) != -1) ? 1 : 0; -} - -/*========================================== - * Return numerical value of a switch configuration (modified by [Yor]) - * on/off, english, fran軋is, deutsch, espal - *------------------------------------------ - */ -int battle_config_switch (const char *str) -{ - if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 - || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 - || strcasecmp (str, "si") == 0) - return 1; - if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 - || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) - return 0; - return atoi (str); -} - -/*========================================== - * 設定ファイルを読み込む - *------------------------------------------ - */ -int battle_config_read (const char *cfgName) -{ - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - static int count = 0; - - if ((count++) == 0) - { - battle_config.warp_point_debug = 0; - battle_config.enemy_critical = 0; - battle_config.enemy_critical_rate = 100; - 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.pc_damage_delay = 1; - battle_config.pc_damage_delay_rate = 100; - battle_config.defnotenemy = 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.drop_pickup_safety_zone = 20; - 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_item_rate = 100; - 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.gm_allskill = 0; - battle_config.gm_allequip = 0; - battle_config.gm_skilluncond = 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_count_rate = 100; - 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.pc_invincible_time = 5000; - battle_config.skill_min_damage = 0; - 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.wedding_modifydisplay = 0; - 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.itemheal_regeneration_factor = 1; - battle_config.item_name_override_grffile = 1; - battle_config.arrow_decrement = 1; - battle_config.max_aspd = 199; - battle_config.max_hp = 32500; - battle_config.max_sp = 32500; - battle_config.max_lv = 99; // [MouseJstr] - battle_config.max_parameter = 99; - battle_config.max_cart_weight = 8000; - 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.agi_penaly_type = 0; - battle_config.agi_penaly_count = 3; - battle_config.agi_penaly_num = 0; - battle_config.agi_penaly_count_lv = ATK_FLEE; - battle_config.vit_penaly_type = 0; - battle_config.vit_penaly_count = 3; - battle_config.vit_penaly_num = 0; - battle_config.vit_penaly_count_lv = ATK_DEF; - battle_config.player_defense_type = 0; - battle_config.monster_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 = 0; - battle_config.monster_cloak_check_type = 0; - battle_config.gvg_short_damage_rate = 100; - battle_config.gvg_long_damage_rate = 100; - battle_config.gvg_magic_damage_rate = 100; - battle_config.gvg_misc_damage_rate = 100; - 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_undead_nofreeze = 0; - battle_config.pc_land_skill_limit = 1; - battle_config.monster_land_skill_limit = 1; - battle_config.party_skill_penaly = 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.display_delay_skill_fail = 1; - battle_config.chat_warpportal = 0; - battle_config.mob_warpportal = 0; - battle_config.dead_branch_active = 0; - battle_config.show_steal_in_same_party = 0; - battle_config.enable_upper_class = 0; - battle_config.pc_attack_attr_none = 0; - battle_config.mob_attack_attr_none = 1; - battle_config.mob_ghostring_fix = 0; - battle_config.gx_allhit = 0; - battle_config.gx_cardfix = 0; - battle_config.gx_dupele = 1; - battle_config.gx_disptype = 1; - battle_config.player_skill_partner_check = 1; - battle_config.hide_GM_session = 0; - battle_config.unit_movement_type = 0; - battle_config.invite_request_check = 1; - battle_config.skill_removetrap_type = 0; - battle_config.disp_experience = 0; - 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_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.prevent_logout = 1; // Added by RoVeRT - battle_config.maximum_level = 255; // Added by Valaris - battle_config.drops_by_luk = 0; // [Valaris] - battle_config.equipment_breaking = 0; // [Valaris] - battle_config.equipment_break_rate = 100; // [Valaris] - battle_config.pk_mode = 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.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 = 63; // added by [Yor] - battle_config.min_hair_style = 0; - battle_config.max_hair_style = 20; - 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.castrate_dex_scale = 150; - - battle_config.area_size = 14; - - battle_config.chat_lame_penalty = 2; - battle_config.chat_spam_threshold = 10; - battle_config.chat_spam_flood = 10; - battle_config.chat_spam_ban = 1; - battle_config.chat_spam_warn = 8; - battle_config.chat_maxline = 255; - - battle_config.packet_spam_threshold = 2; - battle_config.packet_spam_flood = 30; - battle_config.packet_spam_kick = 1; - - battle_config.mask_ip_gms = 1; - - battle_config.mob_splash_radius = -1; - } - - fp = fopen_ (cfgName, "r"); - if (fp == NULL) - { - printf ("file not found: %s\n", cfgName); - return 1; - } - while (fgets (line, 1020, fp)) - { - const struct - { - char str[128]; - int *val; - } data[] = - { - { - "warp_point_debug", &battle_config.warp_point_debug}, - { - "enemy_critical", &battle_config.enemy_critical}, - { - "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}, - { - "player_damage_delay", &battle_config.pc_damage_delay}, - { - "player_damage_delay_rate", - &battle_config.pc_damage_delay_rate}, - { - "defunit_not_enemy", &battle_config.defnotenemy}, - { - "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_pickup_safety_zone", - &battle_config.drop_pickup_safety_zone}, - { - "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}, - { - "item_rate", &battle_config.item_rate}, - { - "drop_rate0item", &battle_config.drop_rate0item}, - { - "base_exp_rate", &battle_config.base_exp_rate}, - { - "job_exp_rate", &battle_config.job_exp_rate}, - { - "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}, - { - "zeny_penalty", &battle_config.zeny_penalty}, - { - "restart_hp_rate", &battle_config.restart_hp_rate}, - { - "restart_sp_rate", &battle_config.restart_sp_rate}, - { - "mvp_hp_rate", &battle_config.mvp_hp_rate}, - { - "mvp_item_rate", &battle_config.mvp_item_rate}, - { - "mvp_exp_rate", &battle_config.mvp_exp_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}, - { - "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}, - { - "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}, - { - "mob_count_rate", &battle_config.mob_count_rate}, - { - "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_limit", &battle_config.guild_exp_limit}, - { - "player_invincible_time", &battle_config.pc_invincible_time}, - { - "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}, - { - "wedding_modifydisplay", &battle_config.wedding_modifydisplay}, - { - "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}, - { - "natural_heal_weight_rate", - &battle_config.natural_heal_weight_rate}, - { - "itemheal_regeneration_factor", - &battle_config.itemheal_regeneration_factor}, - { - "item_name_override_grffile", - &battle_config.item_name_override_grffile}, - { - "arrow_decrement", &battle_config.arrow_decrement}, - { - "max_aspd", &battle_config.max_aspd}, - { - "max_hp", &battle_config.max_hp}, - { - "max_sp", &battle_config.max_sp}, - { - "max_lv", &battle_config.max_lv}, - { - "max_parameter", &battle_config.max_parameter}, - { - "max_cart_weight", &battle_config.max_cart_weight}, - { - "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}, - { - "agi_penaly_type", &battle_config.agi_penaly_type}, - { - "agi_penaly_count", &battle_config.agi_penaly_count}, - { - "agi_penaly_num", &battle_config.agi_penaly_num}, - { - "agi_penaly_count_lv", &battle_config.agi_penaly_count_lv}, - { - "vit_penaly_type", &battle_config.vit_penaly_type}, - { - "vit_penaly_count", &battle_config.vit_penaly_count}, - { - "vit_penaly_num", &battle_config.vit_penaly_num}, - { - "vit_penaly_count_lv", &battle_config.vit_penaly_count_lv}, - { - "player_defense_type", &battle_config.player_defense_type}, - { - "monster_defense_type", &battle_config.monster_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}, - { - "gvg_short_attack_damage_rate", - &battle_config.gvg_short_damage_rate}, - { - "gvg_long_attack_damage_rate", - &battle_config.gvg_long_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_eliminate_time", &battle_config.gvg_eliminate_time}, - { - "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_penaly", &battle_config.party_skill_penaly}, - { - "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}, - { - "display_delay_skill_fail", - &battle_config.display_delay_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}, - { - "enable_upper_class", &battle_config.enable_upper_class}, - { - "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_cardfix", &battle_config.gx_cardfix}, - { - "gx_dupele", &battle_config.gx_dupele}, - { - "gx_disptype", &battle_config.gx_disptype}, - { - "player_skill_partner_check", - &battle_config.player_skill_partner_check}, - { - "hide_GM_session", &battle_config.hide_GM_session}, - { - "unit_movement_type", &battle_config.unit_movement_type}, - { - "invite_request_check", &battle_config.invite_request_check}, - { - "skill_removetrap_type", &battle_config.skill_removetrap_type}, - { - "disp_experience", &battle_config.disp_experience}, - { - "castle_defense_rate", &battle_config.castle_defense_rate}, - { - "riding_weight", &battle_config.riding_weight}, - { - "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_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 - { - "prevent_logout", &battle_config.prevent_logout}, // Added by RoVeRT - { - "alchemist_summon_reward", &battle_config.alchemist_summon_reward}, // [Valaris] - { - "maximum_level", &battle_config.maximum_level}, // [Valaris] - { - "drops_by_luk", &battle_config.drops_by_luk}, // [Valaris] - { - "monsters_ignore_gm", &battle_config.monsters_ignore_gm}, // [Valaris] - { - "equipment_breaking", &battle_config.equipment_breaking}, // [Valaris] - { - "equipment_break_rate", &battle_config.equipment_break_rate}, // [Valaris] - { - "pk_mode", &battle_config.pk_mode}, // [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] - { - "day_duration", &battle_config.day_duration}, // added by [Yor] - { - "night_duration", &battle_config.night_duration}, // added by [Yor] - { - "show_mob_hp", &battle_config.show_mob_hp}, // [Valaris] - { - "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] - { - "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] - { - "chat_lame_penalty", &battle_config.chat_lame_penalty}, - { - "chat_spam_threshold", &battle_config.chat_spam_threshold}, - { - "chat_spam_flood", &battle_config.chat_spam_flood}, - { - "chat_spam_ban", &battle_config.chat_spam_ban}, - { - "chat_spam_warn", &battle_config.chat_spam_warn}, - { - "chat_maxline", &battle_config.chat_maxline}, - { - "packet_spam_threshold", &battle_config.packet_spam_threshold}, - { - "packet_spam_flood", &battle_config.packet_spam_flood}, - { - "packet_spam_kick", &battle_config.packet_spam_kick}, - { - "mask_ip_gms", &battle_config.mask_ip_gms}, - { - "mob_splash_radius", &battle_config.mob_splash_radius}, - }; - - if (line[0] == '/' && line[1] == '/') - continue; - if (sscanf (line, "%[^:]:%s", w1, w2) != 2) - continue; - for (i = 0; i < sizeof (data) / (sizeof (data[0])); i++) - if (strcasecmp (w1, data[i].str) == 0) - *data[i].val = battle_config_switch (w2); - - if (strcasecmp (w1, "import") == 0) - battle_config_read (w2); - } - fclose_ (fp); - - if (--count == 0) - { - 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_hp > 1000000) - battle_config.max_hp = 1000000; - if (battle_config.max_hp < 100) - battle_config.max_hp = 100; - if (battle_config.max_sp > 1000000) - battle_config.max_sp = 1000000; - 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_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.agi_penaly_count < 2) - battle_config.agi_penaly_count = 2; - if (battle_config.vit_penaly_count < 2) - battle_config.vit_penaly_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.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) // added by [Yor] - battle_config.day_duration = 0; - if (battle_config.night_duration < 0) // added by [Yor] - battle_config.night_duration = 0; - - 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; - - if (battle_config.chat_spam_ban < 0) - battle_config.chat_spam_ban = 0; - else if (battle_config.chat_spam_ban > 32767) - battle_config.chat_spam_ban = 32767; - - if (battle_config.chat_spam_flood < 0) - battle_config.chat_spam_flood = 0; - else if (battle_config.chat_spam_flood > 32767) - battle_config.chat_spam_flood = 32767; - - if (battle_config.chat_spam_warn < 0) - battle_config.chat_spam_warn = 0; - else if (battle_config.chat_spam_warn > 32767) - battle_config.chat_spam_warn = 32767; - - if (battle_config.chat_spam_threshold < 0) - battle_config.chat_spam_threshold = 0; - else if (battle_config.chat_spam_threshold > 32767) - battle_config.chat_spam_threshold = 32767; - - if (battle_config.chat_maxline < 1) - battle_config.chat_maxline = 1; - else if (battle_config.chat_maxline > 512) - battle_config.chat_maxline = 512; - - if (battle_config.packet_spam_threshold < 0) - battle_config.packet_spam_threshold = 0; - else if (battle_config.packet_spam_threshold > 32767) - battle_config.packet_spam_threshold = 32767; - - if (battle_config.packet_spam_flood < 0) - battle_config.packet_spam_flood = 0; - else if (battle_config.packet_spam_flood > 32767) - battle_config.packet_spam_flood = 32767; - - if (battle_config.packet_spam_kick < 0) - battle_config.packet_spam_kick = 0; - else if (battle_config.packet_spam_kick > 1) - battle_config.packet_spam_kick = 1; - - if (battle_config.mask_ip_gms < 0) - battle_config.mask_ip_gms = 0; - else if (battle_config.mask_ip_gms > 1) - battle_config.mask_ip_gms = 1; - - // at least 1 client must be accepted - if ((battle_config.packet_ver_flag & 63) == 0) // added by [Yor] - battle_config.packet_ver_flag = 63; // accept all clients - - } - - return 0; -} diff --git a/src/map/battle.cpp b/src/map/battle.cpp new file mode 100644 index 0000000..a46db13 --- /dev/null +++ b/src/map/battle.cpp @@ -0,0 +1,6282 @@ +// $Id: battle.c,v 1.10 2004/09/29 21:08:17 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "battle.hpp" + +#include "../common/timer.hpp" +#include "../common/nullpo.hpp" + +#include "clif.hpp" +#include "guild.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "pc.hpp" +#include "skill.hpp" +#include "../common/socket.hpp" +#include "../common/mt_rand.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +int attr_fix_table[4][10][10]; + +struct Battle_Config battle_config; + +/*========================================== + * 二点間の距離を返す + * 戻りは整数で0以上 + *------------------------------------------ + */ +static int distance (int x0, int y0, int x1, int y1) +{ + int dx, dy; + + dx = abs (x0 - x1); + dy = abs (y0 - y1); + return dx > dy ? dx : dy; +} + +/*========================================== + * 自分をロックしている対象の数を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_counttargeted (struct block_list *bl, struct block_list *src, + int target_lv) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC) + return pc_counttargeted ((struct map_session_data *) bl, src, + target_lv); + else if (bl->type == BL_MOB) + return mob_counttargeted ((struct mob_data *) bl, src, target_lv); + return 0; +} + +/*========================================== + * 対象のClassを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_class (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->mob_class; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->status.pc_class; + else + return 0; +} + +/*========================================== + * 対象の方向を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_dir (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->dir; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->dir; + else + return 0; +} + +/*========================================== + * 対象のレベルを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_lv (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->stats[MOB_LV]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->status.base_level; + else + return 0; +} + +/*========================================== + * 対象の射程を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_range (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return mob_db[((struct mob_data *) bl)->mob_class].range; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->attackrange; + else + return 0; +} + +/*========================================== + * 対象のHPを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_hp (struct block_list *bl) +{ + nullpo_retr (1, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->hp; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->status.hp; + else + return 1; +} + +/*========================================== + * 対象のMHPを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_max_hp (struct block_list *bl) +{ + nullpo_retr (1, bl); + if (bl->type == BL_PC && ((struct map_session_data *) bl)) + return ((struct map_session_data *) bl)->status.max_hp; + else + { + struct status_change *sc_data = battle_get_sc_data (bl); + int max_hp = 1; + if (bl->type == BL_MOB && ((struct mob_data *) bl)) + { + max_hp = ((struct mob_data *) bl)->stats[MOB_MAX_HP]; + if (mob_db[((struct mob_data *) bl)->mob_class].mexp > 0) + { + if (battle_config.mvp_hp_rate != 100) + max_hp = (max_hp * battle_config.mvp_hp_rate) / 100; + } + else + { + if (battle_config.monster_hp_rate != 100) + max_hp = (max_hp * battle_config.monster_hp_rate) / 100; + } + } + if (sc_data) + { + if (sc_data[SC_APPLEIDUN].timer != -1) + max_hp += + ((5 + sc_data[SC_APPLEIDUN].val1 * 2 + + ((sc_data[SC_APPLEIDUN].val2 + 1) >> 1) + + sc_data[SC_APPLEIDUN].val3 / 10) * max_hp) / 100; + } + if (max_hp < 1) + max_hp = 1; + return max_hp; + } + return 1; +} + +/*========================================== + * 対象のStrを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_str (struct block_list *bl) +{ + int str = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && ((struct mob_data *) bl)) + str = ((struct mob_data *) bl)->stats[MOB_STR]; + else if (bl->type == BL_PC && ((struct map_session_data *) bl)) + return ((struct map_session_data *) bl)->paramc[0]; + + if (sc_data) + { + if (sc_data[SC_LOUD].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 + && bl->type != BL_PC) + str += 4; + if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) + { // ブレッシング + int race = battle_get_race (bl); + if (battle_check_undead (race, battle_get_elem_type (bl)) + || race == 6) + str >>= 1; // 悪 魔/不死 + else + str += sc_data[SC_BLESSING].val1; // その他 + } + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + str += 5; + } + if (str < 0) + str = 0; + return str; +} + +/*========================================== + * 対象のAgiを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ + +int battle_get_agi (struct block_list *bl) +{ + int agi = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + agi = ((struct mob_data *) bl)->stats[MOB_AGI]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + agi = ((struct map_session_data *) bl)->paramc[1]; + + if (sc_data) + { + if (sc_data[SC_INCREASEAGI].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1 && bl->type != BL_PC) // 速度増加(PCはpc.cで) + agi += 2 + sc_data[SC_INCREASEAGI].val1; + + if (sc_data[SC_CONCENTRATE].timer != -1 + && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) + agi += agi * (2 + sc_data[SC_CONCENTRATE].val1) / 100; + + if (sc_data[SC_DECREASEAGI].timer != -1) // 速度減少 + agi -= 2 + sc_data[SC_DECREASEAGI].val1; + + if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア + agi >>= 1; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + agi += 5; + } + if (agi < 0) + agi = 0; + return agi; +} + +/*========================================== + * 対象のVitを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_vit (struct block_list *bl) +{ + int vit = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + vit = ((struct mob_data *) bl)->stats[MOB_VIT]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + vit = ((struct map_session_data *) bl)->paramc[2]; + if (sc_data) + { + if (sc_data[SC_STRIPARMOR].timer != -1 && bl->type != BL_PC) + vit = vit * 60 / 100; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + vit += 5; + } + + if (vit < 0) + vit = 0; + return vit; +} + +/*========================================== + * 対象のIntを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_int (struct block_list *bl) +{ + int int_ = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + int_ = ((struct mob_data *) bl)->stats[MOB_INT]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + int_ = ((struct map_session_data *) bl)->paramc[3]; + + if (sc_data) + { + if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) + { // ブレッシング + int race = battle_get_race (bl); + if (battle_check_undead (race, battle_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_ * 60 / 100; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + int_ += 5; + } + if (int_ < 0) + int_ = 0; + return int_; +} + +/*========================================== + * 対象のDexを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_dex (struct block_list *bl) +{ + int dex = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + dex = ((struct mob_data *) bl)->stats[MOB_DEX]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + dex = ((struct map_session_data *) bl)->paramc[4]; + + if (sc_data) + { + if (sc_data[SC_CONCENTRATE].timer != -1 + && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) + dex += dex * (2 + sc_data[SC_CONCENTRATE].val1) / 100; + + if (sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC) + { // ブレッシング + int race = battle_get_race (bl); + if (battle_check_undead (race, battle_get_elem_type (bl)) + || race == 6) + dex >>= 1; // 悪 魔/不死 + else + dex += sc_data[SC_BLESSING].val1; // その他 + } + + if (sc_data[SC_QUAGMIRE].timer != -1) // クァグマイア + dex >>= 1; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + dex += 5; + } + if (dex < 0) + dex = 0; + return dex; +} + +/*========================================== + * 対象のLukを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_luk (struct block_list *bl) +{ + int luk = 0; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + luk = ((struct mob_data *) bl)->stats[MOB_LUK]; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + luk = ((struct map_session_data *) bl)->paramc[5]; + + if (sc_data) + { + if (sc_data[SC_GLORIA].timer != -1 && bl->type != BL_PC) // グロリア(PCはpc.cで) + luk += 30; + if (sc_data[SC_CURSE].timer != -1) // 呪い + luk = 0; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + luk += 5; + } + if (luk < 0) + luk = 0; + return luk; +} + +/*========================================== + * 対象のFleeを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_flee (struct block_list *bl) +{ + int flee = 1; + struct status_change *sc_data; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + flee = ((struct map_session_data *) bl)->flee; + else + flee = battle_get_agi (bl) + battle_get_lv (bl); + + if (sc_data) + { + if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) + flee += + flee * (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 + + (sc_data[SC_WHISTLE].val3 >> 16)) / 100; + if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) + flee -= flee * 25 / 100; + if (sc_data[SC_WINDWALK].timer != -1 && bl->type != BL_PC) // ウィンドウォーク + flee += flee * (sc_data[SC_WINDWALK].val2) / 100; + if (sc_data[SC_SPIDERWEB].timer != -1 && bl->type != BL_PC) //スパイダーウェブ + flee -= flee * 50 / 100; + + if (battle_is_unarmed (bl)) + flee += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 + flee += skill_power_bl (bl, TMW_SPEED) >> 3; + } + if (flee < 1) + flee = 1; + return flee; +} + +/*========================================== + * 対象のHitを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_hit (struct block_list *bl) +{ + int hit = 1; + struct status_change *sc_data; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + hit = ((struct map_session_data *) bl)->hit; + else + hit = battle_get_dex (bl) + battle_get_lv (bl); + + if (sc_data) + { + if (sc_data[SC_HUMMING].timer != -1 && bl->type != BL_PC) // + hit += + hit * (sc_data[SC_HUMMING].val1 * 2 + + sc_data[SC_HUMMING].val2 + + sc_data[SC_HUMMING].val3) / 100; + if (sc_data[SC_BLIND].timer != -1 && bl->type != BL_PC) // 呪い + hit -= hit * 25 / 100; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) // トゥルーサイト + hit += 3 * (sc_data[SC_TRUESIGHT].val1); + if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション + hit += (hit * (10 * (sc_data[SC_CONCENTRATION].val1))) / 100; + + if (battle_is_unarmed (bl)) + hit += (skill_power_bl (bl, TMW_BRAWLING) >> 4); // +12 for 200 + } + if (hit < 1) + hit = 1; + return hit; +} + +/*========================================== + * 対象の完全回避を返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_flee2 (struct block_list *bl) +{ + int flee2 = 1; + struct status_change *sc_data; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + { + flee2 = battle_get_luk (bl) + 10; + flee2 += + ((struct map_session_data *) bl)->flee2 - + (((struct map_session_data *) bl)->paramc[5] + 10); + } + else + flee2 = battle_get_luk (bl) + 1; + + if (sc_data) + { + if (sc_data[SC_WHISTLE].timer != -1 && bl->type != BL_PC) + flee2 += (sc_data[SC_WHISTLE].val1 + sc_data[SC_WHISTLE].val2 + + (sc_data[SC_WHISTLE].val3 & 0xffff)) * 10; + + if (battle_is_unarmed (bl)) + flee2 += (skill_power_bl (bl, TMW_BRAWLING) >> 3); // +25 for 200 + flee2 += skill_power_bl (bl, TMW_SPEED) >> 3; + } + if (flee2 < 1) + flee2 = 1; + return flee2; +} + +/*========================================== + * 対象のクリティカルを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_critical (struct block_list *bl) +{ + int critical = 1; + struct status_change *sc_data; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + { + critical = battle_get_luk (bl) * 2 + 10; + critical += + ((struct map_session_data *) bl)->critical - + ((((struct map_session_data *) bl)->paramc[5] * 3) + 10); + } + else + critical = battle_get_luk (bl) * 3 + 1; + + if (sc_data) + { + if (sc_data[SC_FORTUNE].timer != -1 && bl->type != BL_PC) + critical += + (10 + sc_data[SC_FORTUNE].val1 + sc_data[SC_FORTUNE].val2 + + sc_data[SC_FORTUNE].val3) * 10; + if (sc_data[SC_EXPLOSIONSPIRITS].timer != -1 && bl->type != BL_PC) + critical += sc_data[SC_EXPLOSIONSPIRITS].val2; + if (sc_data[SC_TRUESIGHT].timer != -1 && bl->type != BL_PC) //トゥルーサイト + critical += critical * sc_data[SC_TRUESIGHT].val1 / 100; + } + if (critical < 1) + critical = 1; + return critical; +} + +/*========================================== + * base_atkの取得 + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_baseatk (struct block_list *bl) +{ + struct status_change *sc_data; + int batk = 1; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + batk = ((struct map_session_data *) bl)->base_atk; //設定されているbase_atk + else + { //それ以外なら + int str, dstr; + str = battle_get_str (bl); //STR + dstr = str / 10; + batk = dstr * dstr + str; //base_atkを計算する + } + if (sc_data) + { //状態異常あり + if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) //PCでプロボック(SM_PROVOKE)状態 + batk = batk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; //base_atk増加 + if (sc_data[SC_CURSE].timer != -1) //呪われていたら + batk -= batk * 25 / 100; //base_atkが25%減少 + if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション + batk += batk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; + } + if (batk < 1) + batk = 1; //base_atkは最低でも1 + return batk; +} + +/*========================================== + * 対象のAtkを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk (struct block_list *bl) +{ + struct status_change *sc_data; + int atk = 0; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + atk = ((struct map_session_data *) bl)->watk; + else if (bl->type == BL_MOB && (struct mob_data *) bl) + atk = ((struct mob_data *) bl)->stats[MOB_ATK1]; + + if (sc_data) + { + if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) + atk = atk * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; + if (sc_data[SC_CURSE].timer != -1) + atk -= atk * 25 / 100; + if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) //コンセントレーション + atk += atk * (5 * sc_data[SC_CONCENTRATION].val1) / 100; + } + if (atk < 0) + atk = 0; + return atk; +} + +/*========================================== + * 対象の左手Atkを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk_ (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + { + int atk = ((struct map_session_data *) bl)->watk_; + + if (((struct map_session_data *) bl)->sc_data[SC_CURSE].timer != -1) + atk -= atk * 25 / 100; + return atk; + } + else + return 0; +} + +/*========================================== + * 対象のAtk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk2 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->watk2; + else + { + struct status_change *sc_data = battle_get_sc_data (bl); + int atk2 = 0; + if (bl->type == BL_MOB && (struct mob_data *) bl) + atk2 = ((struct mob_data *) bl)->stats[MOB_ATK2]; + if (sc_data) + { + if (sc_data[SC_IMPOSITIO].timer != -1) + atk2 += sc_data[SC_IMPOSITIO].val1 * 5; + if (sc_data[SC_PROVOKE].timer != -1) + atk2 = atk2 * (100 + 2 * sc_data[SC_PROVOKE].val1) / 100; + if (sc_data[SC_CURSE].timer != -1) + atk2 -= atk2 * 25 / 100; + if (sc_data[SC_DRUMBATTLE].timer != -1) + atk2 += sc_data[SC_DRUMBATTLE].val2; + if (sc_data[SC_NIBELUNGEN].timer != -1 + && (battle_get_element (bl) / 10) >= 8) + atk2 += sc_data[SC_NIBELUNGEN].val2; + if (sc_data[SC_STRIPWEAPON].timer != -1) + atk2 = atk2 * 90 / 100; + if (sc_data[SC_CONCENTRATION].timer != -1) //コンセントレーション + atk2 += atk2 * (5 * sc_data[SC_CONCENTRATION].val1) / 100; + } + + if (atk2 < 0) + atk2 = 0; + return atk2; + } + return 0; +} + +/*========================================== + * 対象の左手Atk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk_2 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC) + return ((struct map_session_data *) bl)->watk_2; + else + return 0; +} + +/*========================================== + * 対象のMAtk1を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_matk1 (struct block_list *bl) +{ + struct status_change *sc_data; + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB) + { + int matk, int_ = battle_get_int (bl); + matk = int_ + (int_ / 5) * (int_ / 5); + + if (sc_data) + if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) + matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; + return matk; + } + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->matk1; + else + return 0; +} + +/*========================================== + * 対象のMAtk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_matk2 (struct block_list *bl) +{ + struct status_change *sc_data = battle_get_sc_data (bl); + nullpo_retr (0, bl); + if (bl->type == BL_MOB) + { + int matk, int_ = battle_get_int (bl); + matk = int_ + (int_ / 7) * (int_ / 7); + + if (sc_data) + if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) + matk = matk * (100 + 2 * sc_data[SC_MINDBREAKER].val1) / 100; + return matk; + } + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->matk2; + else + return 0; +} + +/*========================================== + * 対象のDefを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_def (struct block_list *bl) +{ + struct status_change *sc_data; + int def = 0, skilltimer = -1, skillid = 0; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + { + def = ((struct map_session_data *) bl)->def; + skilltimer = ((struct map_session_data *) bl)->skilltimer; + skillid = ((struct map_session_data *) bl)->skillid; + } + else if (bl->type == BL_MOB && (struct mob_data *) bl) + { + def = ((struct mob_data *) bl)->stats[MOB_DEF]; + skilltimer = ((struct mob_data *) bl)->skilltimer; + skillid = ((struct mob_data *) bl)->skillid; + } + + if (def < 1000000) + { + if (sc_data) + { + //キーピング時はDEF100 + if (sc_data[SC_KEEPING].timer != -1) + def = 100; + //プロボック時は減算 + if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) + def = (def * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; + //戦太鼓の響き時は加算 + if (sc_data[SC_DRUMBATTLE].timer != -1 && bl->type != BL_PC) + def += sc_data[SC_DRUMBATTLE].val3; + //毒にかかっている時は減算 + if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) + def = def * 75 / 100; + //ストリップシールド時は減算 + if (sc_data[SC_STRIPSHIELD].timer != -1 && bl->type != BL_PC) + def = def * 85 / 100; + //シグナムクルシス時は減算 + if (sc_data[SC_SIGNUMCRUCIS].timer != -1 && bl->type != BL_PC) + def = def * (100 - sc_data[SC_SIGNUMCRUCIS].val2) / 100; + //永遠の混沌時はDEF0になる + if (sc_data[SC_ETERNALCHAOS].timer != -1 && bl->type != BL_PC) + def = 0; + //凍結、石化時は右シフト + if (sc_data[SC_FREEZE].timer != -1 + || (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0)) + def >>= 1; + //コンセントレーション時は減算 + if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) + def = + (def * (100 - 5 * sc_data[SC_CONCENTRATION].val1)) / 100; + } + //詠唱中は詠唱時減算率に基づいて減算 + if (skilltimer != -1) + { + int def_rate = skill_get_castdef (skillid); + if (def_rate != 0) + def = (def * (100 - def_rate)) / 100; + } + } + if (def < 0) + def = 0; + return def; +} + +/*========================================== + * 対象のMDefを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_mdef (struct block_list *bl) +{ + struct status_change *sc_data; + int mdef = 0; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + mdef = ((struct map_session_data *) bl)->mdef; + else if (bl->type == BL_MOB && (struct mob_data *) bl) + mdef = ((struct mob_data *) bl)->stats[MOB_MDEF]; + + if (mdef < 1000000) + { + if (sc_data) + { + //バリアー状態時はMDEF100 + if (mdef < 90 && sc_data[SC_MBARRIER].timer != -1) + { + mdef += sc_data[SC_MBARRIER].val1; + if (mdef > 90) + mdef = 90; + } + if (sc_data[SC_BARRIER].timer != -1) + mdef = 100; + //凍結、石化時は1.25倍 + if (sc_data[SC_FREEZE].timer != -1 + || (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0)) + mdef = mdef * 125 / 100; + if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) + mdef -= (mdef * 6 * sc_data[SC_MINDBREAKER].val1) / 100; + } + } + if (mdef < 0) + mdef = 0; + return mdef; +} + +/*========================================== + * 対象のDef2を返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_def2 (struct block_list *bl) +{ + struct status_change *sc_data; + int def2 = 1; + + nullpo_retr (1, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_PC) + def2 = ((struct map_session_data *) bl)->def2; + else if (bl->type == BL_MOB) + def2 = ((struct mob_data *) bl)->stats[MOB_VIT]; + + if (sc_data) + { + if (sc_data[SC_ANGELUS].timer != -1 && bl->type != BL_PC) + def2 = def2 * (110 + 5 * sc_data[SC_ANGELUS].val1) / 100; + if (sc_data[SC_PROVOKE].timer != -1 && bl->type != BL_PC) + def2 = (def2 * (100 - 6 * sc_data[SC_PROVOKE].val1) + 50) / 100; + if (sc_data[SC_POISON].timer != -1 && bl->type != BL_PC) + def2 = def2 * 75 / 100; + //コンセントレーション時は減算 + if (sc_data[SC_CONCENTRATION].timer != -1 && bl->type != BL_PC) + def2 = def2 * (100 - 5 * sc_data[SC_CONCENTRATION].val1) / 100; + } + if (def2 < 1) + def2 = 1; + return def2; +} + +/*========================================== + * 対象のMDef2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_mdef2 (struct block_list *bl) +{ + int mdef2 = 0; + struct status_change *sc_data = battle_get_sc_data (bl); + + nullpo_retr (0, bl); + if (bl->type == BL_MOB) + mdef2 = + ((struct mob_data *) bl)->stats[MOB_INT] + + (((struct mob_data *) bl)->stats[MOB_VIT] >> 1); + else if (bl->type == BL_PC) + mdef2 = + ((struct map_session_data *) bl)->mdef2 + + (((struct map_session_data *) bl)->paramc[2] >> 1); + if (sc_data) + { + if (sc_data[SC_MINDBREAKER].timer != -1 && bl->type != BL_PC) + mdef2 -= (mdef2 * 6 * sc_data[SC_MINDBREAKER].val1) / 100; + } + if (mdef2 < 0) + mdef2 = 0; + return mdef2; +} + +/*========================================== + * 対象のSpeed(移動速度)を返す(汎用) + * 戻りは整数で1以上 + * Speedは小さいほうが移動速度が速い + *------------------------------------------ + */ +int battle_get_speed (struct block_list *bl) +{ + nullpo_retr (1000, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->speed; + else + { + struct status_change *sc_data = battle_get_sc_data (bl); + int speed = 1000; + if (bl->type == BL_MOB && (struct mob_data *) bl) + speed = ((struct mob_data *) bl)->stats[MOB_SPEED]; + + if (sc_data) + { + //速度増加時は25%減算 + if (sc_data[SC_INCREASEAGI].timer != -1 + && sc_data[SC_DONTFORGETME].timer == -1) + speed -= speed * 25 / 100; + //速度減少時は25%加算 + if (sc_data[SC_DECREASEAGI].timer != -1) + speed = speed * 125 / 100; + //クァグマイア時は50%加算 + if (sc_data[SC_QUAGMIRE].timer != -1) + speed = speed * 3 / 2; + //私を忘れないで…時は加算 + if (sc_data[SC_DONTFORGETME].timer != -1) + speed = + speed * (100 + sc_data[SC_DONTFORGETME].val1 * 2 + + sc_data[SC_DONTFORGETME].val2 + + (sc_data[SC_DONTFORGETME].val3 & 0xffff)) / 100; + //金剛時は25%加算 + if (sc_data[SC_STEELBODY].timer != -1) + speed = speed * 125 / 100; + //ディフェンダー時は加算 + if (sc_data[SC_DEFENDER].timer != -1) + speed = (speed * (155 - sc_data[SC_DEFENDER].val1 * 5)) / 100; + //踊り状態は4倍遅い + if (sc_data[SC_DANCING].timer != -1) + speed *= 4; + //呪い時は450加算 + if (sc_data[SC_CURSE].timer != -1) + speed = speed + 450; + //ウィンドウォーク時はLv*2%減算 + if (sc_data[SC_WINDWALK].timer != -1) + speed -= (speed * (sc_data[SC_WINDWALK].val1 * 2)) / 100; + } + if (speed < 1) + speed = 1; + return speed; + } + + return 1000; +} + +/*========================================== + * 対象のaDelay(攻撃時ディレイ)を返す(汎用) + * aDelayは小さいほうが攻撃速度が速い + *------------------------------------------ + */ +int battle_get_adelay (struct block_list *bl) +{ + nullpo_retr (4000, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return (((struct map_session_data *) bl)->aspd << 1); + else + { + struct status_change *sc_data = battle_get_sc_data (bl); + int adelay = 4000, aspd_rate = 100, i; + if (bl->type == BL_MOB && (struct mob_data *) bl) + adelay = ((struct mob_data *) bl)->stats[MOB_ADELAY]; + + if (sc_data) + { + //ツーハンドクイッケン使用時でクァグマイアでも私を忘れないで…でもない時は3割減算 + if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + //アドレナリンラッシュ使用時でツーハンドクイッケンでもクァグマイアでも私を忘れないで…でもない時は + if (sc_data[SC_ADRENALINE].timer != -1 + && sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sc_data[SC_QUAGMIRE].timer == -1 + && sc_data[SC_DONTFORGETME].timer == -1) + { // アドレナリンラッシュ + //使用者とパーティメンバーで格差が出る設定でなければ3割減算 + if (sc_data[SC_ADRENALINE].val2 + || !battle_config.party_skill_penaly) + aspd_rate -= 30; + //そうでなければ2.5割減算 + else + aspd_rate -= 25; + } + //スピアクィッケン時は減算 + if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; + //夕日のアサシンクロス時は減算 + if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス + sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sc_data[SC_ADRENALINE].timer == -1 + && sc_data[SC_SPEARSQUICKEN].timer == -1 + && sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= + 5 + sc_data[SC_ASSNCROS].val1 + + sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; + //私を忘れないで…時は加算 + if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで + aspd_rate += + sc_data[SC_DONTFORGETME].val1 * 3 + + sc_data[SC_DONTFORGETME].val2 + + (sc_data[SC_DONTFORGETME].val3 >> 16); + //金剛時25%加算 + if (sc_data[SC_STEELBODY].timer != -1) // 金剛 + aspd_rate += 25; + //増速ポーション使用時は減算 + if (sc_data[i = SC_SPEEDPOTION2].timer != -1 + || sc_data[i = SC_SPEEDPOTION1].timer != -1 + || sc_data[i = SC_SPEEDPOTION0].timer != -1) + aspd_rate -= sc_data[i].val1; + // Fate's `haste' spell works the same as the above + if (sc_data[SC_HASTE].timer != -1) + aspd_rate -= sc_data[SC_HASTE].val1; + //ディフェンダー時は加算 + if (sc_data[SC_DEFENDER].timer != -1) + adelay += (1100 - sc_data[SC_DEFENDER].val1 * 100); + } + + 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; + } + return 4000; +} + +int battle_get_amotion (struct block_list *bl) +{ + nullpo_retr (2000, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->amotion; + else + { + struct status_change *sc_data = battle_get_sc_data (bl); + int amotion = 2000, aspd_rate = 100, i; + if (bl->type == BL_MOB && (struct mob_data *) bl) + amotion = mob_db[((struct mob_data *) bl)->mob_class].amotion; + + if (sc_data) + { + if (sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + if (sc_data[SC_ADRENALINE].timer != -1 + && sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sc_data[SC_QUAGMIRE].timer == -1 + && sc_data[SC_DONTFORGETME].timer == -1) + { // アドレナリンラッシュ + if (sc_data[SC_ADRENALINE].val2 + || !battle_config.party_skill_penaly) + aspd_rate -= 30; + else + aspd_rate -= 25; + } + if (sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; + if (sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス + sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sc_data[SC_ADRENALINE].timer == -1 + && sc_data[SC_SPEARSQUICKEN].timer == -1 + && sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= + 5 + sc_data[SC_ASSNCROS].val1 + + sc_data[SC_ASSNCROS].val2 + sc_data[SC_ASSNCROS].val3; + if (sc_data[SC_DONTFORGETME].timer != -1) // 私を忘れないで + aspd_rate += + sc_data[SC_DONTFORGETME].val1 * 3 + + sc_data[SC_DONTFORGETME].val2 + + (sc_data[SC_DONTFORGETME].val3 >> 16); + if (sc_data[SC_STEELBODY].timer != -1) // 金剛 + aspd_rate += 25; + if (sc_data[i = SC_SPEEDPOTION2].timer != -1 + || sc_data[i = SC_SPEEDPOTION1].timer != -1 + || sc_data[i = SC_SPEEDPOTION0].timer != -1) + aspd_rate -= sc_data[i].val1; + if (sc_data[SC_HASTE].timer != -1) + aspd_rate -= sc_data[SC_HASTE].val1; + if (sc_data[SC_DEFENDER].timer != -1) + amotion += (550 - sc_data[SC_DEFENDER].val1 * 50); + } + + 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 battle_get_dmotion (struct block_list *bl) +{ + int ret; + struct status_change *sc_data; + + nullpo_retr (0, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + { + ret = mob_db[((struct mob_data *) bl)->mob_class].dmotion; + if (battle_config.monster_damage_delay_rate != 100) + ret = ret * battle_config.monster_damage_delay_rate / 400; + } + else if (bl->type == BL_PC && (struct map_session_data *) bl) + { + ret = ((struct map_session_data *) bl)->dmotion; + if (battle_config.pc_damage_delay_rate != 100) + ret = ret * battle_config.pc_damage_delay_rate / 400; + } + else + return 2000; + + if ((sc_data && sc_data[SC_ENDURE].timer != -1) || + (bl->type == BL_PC + && ((struct map_session_data *) bl)->special_state.infinite_endure)) + ret = 0; + + return ret; +} + +int battle_get_element (struct block_list *bl) +{ + int ret = 20; + struct status_change *sc_data; + + nullpo_retr (ret, bl); + sc_data = battle_get_sc_data (bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) // 10の位=Lv*2、1の位=属性 + ret = ((struct mob_data *) bl)->def_ele; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + ret = 20 + ((struct map_session_data *) bl)->def_ele; // 防御属性Lv1 + + if (sc_data) + { + if (sc_data[SC_BENEDICTIO].timer != -1) // 聖体降福 + ret = 26; + if (sc_data[SC_FREEZE].timer != -1) // 凍結 + ret = 21; + if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) + ret = 22; + } + + return ret; +} + +int battle_get_attack_element (struct block_list *bl) +{ + int ret = 0; + struct status_change *sc_data = battle_get_sc_data (bl); + + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + ret = 0; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + ret = ((struct map_session_data *) bl)->atk_ele; + + if (sc_data) + { + if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン + ret = 1; + if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン + ret = 2; + if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー + ret = 3; + if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー + ret = 4; + if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン + ret = 5; + if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ + ret = 6; + } + + return ret; +} + +int battle_get_attack_element2 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + { + int ret = ((struct map_session_data *) bl)->atk_ele_; + struct status_change *sc_data = + ((struct map_session_data *) bl)->sc_data; + + if (sc_data) + { + if (sc_data[SC_FROSTWEAPON].timer != -1) // フロストウェポン + ret = 1; + if (sc_data[SC_SEISMICWEAPON].timer != -1) // サイズミックウェポン + ret = 2; + if (sc_data[SC_FLAMELAUNCHER].timer != -1) // フレームランチャー + ret = 3; + if (sc_data[SC_LIGHTNINGLOADER].timer != -1) // ライトニングローダー + ret = 4; + if (sc_data[SC_ENCPOISON].timer != -1) // エンチャントポイズン + ret = 5; + if (sc_data[SC_ASPERSIO].timer != -1) // アスペルシオ + ret = 6; + } + return ret; + } + return 0; +} + +int battle_get_party_id (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->status.party_id; + else if (bl->type == BL_MOB && (struct mob_data *) bl) + { + struct mob_data *md = (struct mob_data *) bl; + if (md->master_id > 0) + return -md->master_id; + return -md->bl.id; + } + else if (bl->type == BL_SKILL && (struct skill_unit *) bl) + return ((struct skill_unit *) bl)->group->party_id; + else + return 0; +} + +int battle_get_guild_id (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->status.guild_id; + else if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->mob_class; + else if (bl->type == BL_SKILL && (struct skill_unit *) bl) + return ((struct skill_unit *) bl)->group->guild_id; + else + return 0; +} + +int battle_get_race (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return mob_db[((struct mob_data *) bl)->mob_class].race; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return 7; + else + return 0; +} + +int battle_get_size (struct block_list *bl) +{ + nullpo_retr (1, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return mob_db[((struct mob_data *) bl)->mob_class].size; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return 1; + else + return 1; +} + +int battle_get_mode (struct block_list *bl) +{ + nullpo_retr (0x01, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return mob_db[((struct mob_data *) bl)->mob_class].mode; + else + return 0x01; // とりあえず動くということで1 +} + +int battle_get_mexp (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + { + const struct mob_data *mob = (struct mob_data *) bl; + const int retval = + (mob_db[mob->mob_class].mexp * + (int) (mob->stats[MOB_XP_BONUS])) >> MOB_XP_BONUS_SHIFT; + fprintf (stderr, "Modifier of %x: -> %d\n", mob->stats[MOB_XP_BONUS], + retval); + return retval; + } + else + return 0; +} + +int battle_get_stat (int stat_id /* SP_VIT or similar */ , + struct block_list *bl) +{ + switch (stat_id) + { + case SP_STR: + return battle_get_str (bl); + case SP_AGI: + return battle_get_agi (bl); + case SP_DEX: + return battle_get_dex (bl); + case SP_VIT: + return battle_get_vit (bl); + case SP_INT: + return battle_get_int (bl); + case SP_LUK: + return battle_get_luk (bl); + default: + return 0; + } +} + +// StatusChange系の所得 +struct status_change *battle_get_sc_data (struct block_list *bl) +{ + nullpo_retr (NULL, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return ((struct mob_data *) bl)->sc_data; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return ((struct map_session_data *) bl)->sc_data; + return NULL; +} + +short *battle_get_sc_count (struct block_list *bl) +{ + nullpo_retr (NULL, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return &((struct mob_data *) bl)->sc_count; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return &((struct map_session_data *) bl)->sc_count; + return NULL; +} + +short *battle_get_opt1 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return &((struct mob_data *) bl)->opt1; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return &((struct map_session_data *) bl)->opt1; + else if (bl->type == BL_NPC && (struct npc_data *) bl) + return &((struct npc_data *) bl)->opt1; + return 0; +} + +short *battle_get_opt2 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return &((struct mob_data *) bl)->opt2; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return &((struct map_session_data *) bl)->opt2; + else if (bl->type == BL_NPC && (struct npc_data *) bl) + return &((struct npc_data *) bl)->opt2; + return 0; +} + +short *battle_get_opt3 (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return &((struct mob_data *) bl)->opt3; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return &((struct map_session_data *) bl)->opt3; + else if (bl->type == BL_NPC && (struct npc_data *) bl) + return &((struct npc_data *) bl)->opt3; + return 0; +} + +short *battle_get_option (struct block_list *bl) +{ + nullpo_retr (0, bl); + if (bl->type == BL_MOB && (struct mob_data *) bl) + return &((struct mob_data *) bl)->option; + else if (bl->type == BL_PC && (struct map_session_data *) bl) + return &((struct map_session_data *) bl)->status.option; + else if (bl->type == BL_NPC && (struct npc_data *) bl) + return &((struct npc_data *) bl)->option; + return 0; +} + +//------------------------------------------------------------------- + +// ダメージの遅延 +struct battle_delay_damage_ +{ + struct block_list *src, *target; + int damage; + int flag; +}; +void battle_delay_damage_sub (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct battle_delay_damage_ *dat = (struct battle_delay_damage_ *) data; + if (dat && map_id2bl (id) == dat->src && dat->target->prev != NULL) + battle_damage (dat->src, dat->target, dat->damage, dat->flag); + free (dat); +} + +int battle_delay_damage (unsigned int tick, struct block_list *src, + struct block_list *target, int damage, int flag) +{ + struct battle_delay_damage_ *dat; + CREATE (dat, struct battle_delay_damage_, 1); + + nullpo_retr (0, src); + nullpo_retr (0, target); + + dat->src = src; + dat->target = target; + dat->damage = damage; + dat->flag = flag; + 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 = battle_get_sc_data (target); + short *sc_count; + int i; + + nullpo_retr (0, target); //blはNULLで呼ばれることがあるので他でチェック + + if (damage == 0) + return 0; + + if (target->prev == NULL) + return 0; + + if (bl) + { + if (bl->prev == NULL) + return 0; + + if (bl->type == BL_PC) + sd = (struct map_session_data *) bl; + } + + if (damage < 0) + return battle_heal (bl, target, -damage, 0, flag); + + if (!flag && (sc_count = battle_get_sc_count (target)) != NULL + && *sc_count > 0) + { + // 凍結、石化、睡眠を消去 + if (sc_data[SC_FREEZE].timer != -1) + skill_status_change_end (target, SC_FREEZE, -1); + if (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0) + skill_status_change_end (target, SC_STONE, -1); + if (sc_data[SC_SLEEP].timer != -1) + skill_status_change_end (target, SC_SLEEP, -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 && tsd->sc_data && tsd->sc_data[SC_DEVOTION].val1) + { // ディボーションをかけられている + struct map_session_data *md = + map_id2sd (tsd->sc_data[SC_DEVOTION].val1); + if (md && skill_devotion3 (&md->bl, target->id)) + { + skill_devotion (md, target->id); + } + else if (md && bl) + for (i = 0; i < 5; i++) + if (md->dev.val1[i] == target->id) + { + clif_damage (bl, &md->bl, gettick (), 0, 0, + damage, 0, 0, 0); + pc_damage (&md->bl, md, damage); + + return 0; + } + } + + if (tsd && tsd->skilltimer != -1) + { // 詠唱妨害 + // フェンカードや妨害されないスキルかの検査 + if ((!tsd->special_state.no_castcancel || map[bl->m].flag.gvg) + && 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_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; +} + +// 攻撃停止 +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); + 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); + return 0; +} + +/*========================================== + * ダメージの属性修正 + *------------------------------------------ + */ +int battle_attr_fix (int damage, int atk_elem, int def_elem) +{ + int def_type = def_elem % 10, def_lv = def_elem / 10 / 2; + + if (atk_elem < 0 || atk_elem > 9 || def_type < 0 || def_type > 9 || + def_lv < 1 || def_lv > 4) + { // 属 性値がおかしいのでとりあえずそのまま返す + if (battle_config.error_log) + printf + ("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n", + atk_elem, def_type, def_lv); + return damage; + } + + return damage * attr_fix_table[def_lv - 1][atk_elem][def_type] / 100; +} + +/*========================================== + * ダメージ最終計算 + *------------------------------------------ + */ +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); + + class_ = battle_get_class (bl); + if (bl->type == BL_MOB) + md = (struct mob_data *) bl; + else + sd = (struct map_session_data *) bl; + + sc_data = battle_get_sc_data (bl); + sc_count = battle_get_sc_count (bl); + + if (sc_count != NULL && *sc_count > 0) + { + + if (sc_data[SC_SAFETYWALL].timer != -1 && damage > 0 + && flag & BF_WEAPON && flag & BF_SHORT + && skill_num != NPC_GUIDEDATTACK) + { + // セーフティウォール + struct skill_unit *unit = + (struct skill_unit *) sc_data[SC_SAFETYWALL].val2; + if (unit && unit->alive && (--unit->group->val2) <= 0) + skill_delunit (unit); + skill_unit_move (bl, gettick (), 1); // 重ね掛けチェック + damage = 0; + } + if (sc_data[SC_PNEUMA].timer != -1 && damage > 0 && flag & BF_WEAPON + && flag & BF_LONG && skill_num != NPC_GUIDEDATTACK) + { + // ニューマ + damage = 0; + } + + if (sc_data[SC_ROKISWEIL].timer != -1 && damage > 0 && + flag & BF_MAGIC) + { + // ニューマ + damage = 0; + } + + if (sc_data[SC_AETERNA].timer != -1 && damage > 0) + { // レックスエーテルナ + damage <<= 1; + skill_status_change_end (bl, SC_AETERNA, -1); + } + + //属性場のダメージ増加 + if (sc_data[SC_VOLCANO].timer != -1) + { // ボルケーノ + if (flag & BF_SKILL && skill_get_pl (skill_num) == 3) + damage += damage * sc_data[SC_VOLCANO].val4 / 100; + else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 3)) + damage += damage * sc_data[SC_VOLCANO].val4 / 100; + } + + if (sc_data[SC_VIOLENTGALE].timer != -1) + { // バイオレントゲイル + if (flag & BF_SKILL && skill_get_pl (skill_num) == 4) + damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; + else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 4)) + damage += damage * sc_data[SC_VIOLENTGALE].val4 / 100; + } + + if (sc_data[SC_DELUGE].timer != -1) + { // デリュージ + if (flag & BF_SKILL && skill_get_pl (skill_num) == 1) + damage += damage * sc_data[SC_DELUGE].val4 / 100; + else if (!(flag & BF_SKILL) && (battle_get_attack_element (bl) == 1)) + damage += damage * sc_data[SC_DELUGE].val4 / 100; + } + + if (sc_data[SC_ENERGYCOAT].timer != -1 && damage > 0 + && 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) + skill_status_change_end (bl, SC_ENERGYCOAT, -1); + } + else + damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100; + } + + if (sc_data[SC_KYRIE].timer != -1 && damage > 0) + { // キリエエレイソン + sc = &sc_data[SC_KYRIE]; + sc->val2 -= damage; + if (flag & BF_WEAPON) + { + if (sc->val2 >= 0) + damage = 0; + else + damage = -sc->val2; + } + if ((--sc->val3) <= 0 || (sc->val2 <= 0) + || skill_num == AL_HOLYLIGHT) + skill_status_change_end (bl, SC_KYRIE, -1); + } + + if (sc_data[SC_BASILICA].timer != -1 && damage > 0) + { + // ニューマ + damage = 0; + } + if (sc_data[SC_LANDPROTECTOR].timer != -1 && damage > 0 + && flag & BF_MAGIC) + { + // ニューマ + damage = 0; + } + + if (sc_data[SC_AUTOGUARD].timer != -1 && damage > 0 + && flag & BF_WEAPON) + { + if (MRAND (100) < sc_data[SC_AUTOGUARD].val2) + { + damage = 0; + clif_skill_nodamage (bl, bl, CR_AUTOGUARD, + sc_data[SC_AUTOGUARD].val1, 1); + if (sd) + sd->canmove_tick = gettick () + 300; + else if (md) + md->canmove_tick = gettick () + 300; + } + } +// -- moonsoul (chance to block attacks with new Lord Knight skill parrying) +// + if (sc_data[SC_PARRYING].timer != -1 && damage > 0 + && flag & BF_WEAPON) + { + if (MRAND (100) < sc_data[SC_PARRYING].val2) + { + damage = 0; + clif_skill_nodamage (bl, bl, LK_PARRYING, + sc_data[SC_PARRYING].val1, 1); + } + } + // リジェクトソード + if (sc_data[SC_REJECTSWORD].timer != -1 && damage > 0 + && flag & BF_WEAPON + && + ((src->type == BL_PC + && ((struct map_session_data *) src)->status.weapon == (1 || 2 + || 3)) + || src->type == BL_MOB)) + { + if (MRAND (100) < (10 + 5 * sc_data[SC_REJECTSWORD].val1)) + { //反射確率は10+5*Lv + damage = damage * 50 / 100; + 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) + skill_status_change_end (bl, SC_REJECTSWORD, -1); + } + } + } + + if (class_ == 1288 || class_ == 1287 || class_ == 1286 || class_ == 1285) + { +// if(class == 1288) { + if (class_ == 1288 && flag & BF_SKILL) + damage = 0; + if (src->type == BL_PC) + { + struct guild *g = + guild_search (((struct map_session_data *) src)-> + status.guild_id); + struct guild_castle *gc = guild_mapname2gc (map[bl->m].name); + if (!((struct map_session_data *) src)->status.guild_id) + damage = 0; + if (gc && agit_flag == 0 && class_ != 1288) // guardians cannot be damaged during non-woe [Valaris] + damage = 0; // end woe check [Valaris] + if (g == NULL) + damage = 0; //ギルド未加入ならダメージ無し + else if ((gc != NULL) && guild_isallied (g, gc)) + damage = 0; //自占領ギルドのエンペならダメージ無し + else if (g && guild_checkskill (g, GD_APPROVAL) <= 0) + damage = 0; //正規ギルド承認がないとダメージ無し + else if (battle_config.guild_max_castles != 0 + && guild_checkcastles (g) >= + battle_config.guild_max_castles) + damage = 0; // [MouseJstr] + } + else + damage = 0; + } + + if (map[bl->m].flag.gvg && damage > 0) + { //GvG + if (flag & BF_WEAPON) + { + 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; + } + 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; + if (damage < 1) + damage = 1; + } + + if (battle_config.skill_min_damage || flag & BF_MISC) + { + if (div_ < 255) + { + if (damage > 0 && damage < div_) + damage = div_; + } + else if (damage > 0 && damage < 3) + damage = 3; + } + + if (md != NULL && md->hp > 0 && damage > 0) // 反撃などのMOBスキル判定 + mobskill_event (md, flag); + + return damage; +} + +/*========================================== + * 修練ダメージ + *------------------------------------------ + */ +int battle_addmastery (struct map_session_data *sd, struct block_list *target, + int dmg, int type) +{ + int damage, skill; + int race = battle_get_race (target); + int weapon; + damage = 0; + + nullpo_retr (0, sd); + + // デーモンベイン(+3 〜 +30) vs 不死 or 悪魔 (死人は含めない?) + if ((skill = pc_checkskill (sd, AL_DEMONBANE)) > 0 + && (battle_check_undead (race, battle_get_elem_type (target)) + || race == 6)) + damage += (skill * 3); + + // ビーストベイン(+4 〜 +40) vs 動物 or 昆虫 + if ((skill = pc_checkskill (sd, HT_BEASTBANE)) > 0 + && (race == 2 || race == 4)) + damage += (skill * 4); + + if (type == 0) + weapon = sd->weapontype1; + else + weapon = sd->weapontype2; + switch (weapon) + { + case 0x01: // 短剣 (Updated By AppleGirl) + case 0x02: // 1HS + { + // 剣修練(+4 〜 +40) 片手剣 短剣含む + if ((skill = pc_checkskill (sd, SM_SWORD)) > 0) + { + damage += (skill * 4); + } + break; + } + case 0x03: // 2HS + { + // 両手剣修練(+4 〜 +40) 両手剣 + if ((skill = pc_checkskill (sd, SM_TWOHAND)) > 0) + { + damage += (skill * 4); + } + break; + } + case 0x04: // 1HL + { + // 槍修練(+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 0x05: // 2HL + { + // 槍修練(+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: // 片手斧 + { + if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) + { + damage += (skill * 3); + } + break; + } + case 0x07: // Axe by Tato + { + if ((skill = pc_checkskill (sd, AM_AXEMASTERY)) > 0) + { + damage += (skill * 3); + } + break; + } + case 0x08: // メイス + { + // メイス修練(+3 〜 +30) メイス + if ((skill = pc_checkskill (sd, PR_MACEMASTERY)) > 0) + { + damage += (skill * 3); + } + break; + } + case 0x09: // なし? + break; + case 0x0a: // 杖 + break; + case 0x0b: // 弓 + break; + case 0x00: // 素手 + case 0x0c: // Knuckles + { + // 鉄拳(+3 〜 +30) 素手,ナックル + if ((skill = pc_checkskill (sd, MO_IRONHAND)) > 0) + { + damage += (skill * 3); + } + break; + } + case 0x0d: // Musical Instrument + { + // 楽器の練習(+3 〜 +30) 楽器 + if ((skill = pc_checkskill (sd, BA_MUSICALLESSON)) > 0) + { + damage += (skill * 3); + } + break; + } + case 0x0e: // Dance Mastery + { + // 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 + { + // カタール修練(+3 〜 +30) カタール + if ((skill = pc_checkskill (sd, AS_KATAR)) > 0) + { + //ソニックブロー時は別処理(1撃に付き1/8適応) + damage += (skill * 3); + } + break; + } + } + damage = dmg + damage; + return (damage); +} + +static struct Damage battle_calc_mob_weapon_attack (struct block_list *src, + struct block_list *target, + int skill_num, + int skill_lv, int wflag) +{ + struct map_session_data *tsd = NULL; + struct mob_data *md = (struct mob_data *) src, *tmd = NULL; + int hitrate, flee, cri = 0, atkmin, atkmax; + int luk, target_count = 1; + int def1 = battle_get_def (target); + int def2 = battle_get_def2 (target); + int t_vit = battle_get_vit (target); + struct Damage wd; + int damage, damage2 = 0, type, div_, blewcount = + skill_get_blewcount (skill_num, skill_lv); + int flag, skill, ac_flag = 0, dmg_lv = 0; + int t_mode = 0, t_race = 0, t_size = 1, s_race = 0, s_ele = 0; + struct status_change *sc_data, *t_sc_data; + short *sc_count; + short *option, *opt1, *opt2; + + //return前の処理があるので情報出力部のみ変更 + if (src == NULL || target == NULL || md == NULL) + { + nullpo_info (NLP_MARK); + memset (&wd, 0, sizeof (wd)); + return wd; + } + + s_race = battle_get_race (src); + s_ele = battle_get_attack_element (src); + sc_data = battle_get_sc_data (src); + sc_count = battle_get_sc_count (src); + option = battle_get_option (src); + opt1 = battle_get_opt1 (src); + opt2 = battle_get_opt2 (src); + + // ターゲット + if (target->type == BL_PC) + tsd = (struct map_session_data *) target; + else if (target->type == BL_MOB) + tmd = (struct mob_data *) target; + t_race = battle_get_race (target); + t_size = battle_get_size (target); + t_mode = battle_get_mode (target); + t_sc_data = battle_get_sc_data (target); + + if ((skill_num == 0 + || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) + || (target->type == BL_MOB + && battle_config.monster_auto_counter_type & 2)) + && skill_lv >= 0) + { + if (skill_num != CR_GRANDCROSS && t_sc_data + && t_sc_data[SC_AUTOCOUNTER].timer != -1) + { + int dir = map_calc_dir (src, target->x, target->y), t_dir = + battle_get_dir (target); + int dist = distance (src->x, src->y, target->x, target->y); + if (dist <= 0 || map_check_dir (dir, t_dir)) + { + memset (&wd, 0, sizeof (wd)); + t_sc_data[SC_AUTOCOUNTER].val3 = 0; + t_sc_data[SC_AUTOCOUNTER].val4 = 1; + if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) + { + int range = battle_get_range (target); + if ((target->type == BL_PC + && ((struct map_session_data *) target)-> + status.weapon != 11 && dist <= range + 1) + || (target->type == BL_MOB && range <= 3 + && dist <= range + 1)) + t_sc_data[SC_AUTOCOUNTER].val3 = src->id; + } + return wd; + } + else + ac_flag = 1; + } + } + flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 + + // 回避率計算、回避判定は後で + flee = battle_get_flee (target); + if (battle_config.agi_penaly_type > 0 + || battle_config.vit_penaly_type > 0) + target_count += + battle_counttargeted (target, src, + battle_config.agi_penaly_count_lv); + if (battle_config.agi_penaly_type > 0) + { + if (target_count >= battle_config.agi_penaly_count) + { + if (battle_config.agi_penaly_type == 1) + flee = + (flee * + (100 - + (target_count - + (battle_config.agi_penaly_count - + 1)) * battle_config.agi_penaly_num)) / 100; + else if (battle_config.agi_penaly_type == 2) + flee -= + (target_count - + (battle_config.agi_penaly_count - + 1)) * battle_config.agi_penaly_num; + if (flee < 1) + flee = 1; + } + } + hitrate = battle_get_hit (src) - flee + 80; + + type = 0; // normal + div_ = 1; // single attack + + luk = battle_get_luk (src); + + if (battle_config.enemy_str) + damage = battle_get_baseatk (src); + else + damage = 0; + if (skill_num == HW_MAGICCRASHER) + { /* マジッククラッシャーはMATKで殴る */ + atkmin = battle_get_matk1 (src); + atkmax = battle_get_matk2 (src); + } + else + { + atkmin = battle_get_atk (src); + atkmax = battle_get_atk2 (src); + } + if (mob_db[md->mob_class].range > 3) + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + + if (atkmin > atkmax) + atkmin = atkmax; + + if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) + { // マキシマイズパワー + atkmin = atkmax; + } + + cri = battle_get_critical (src); + cri -= battle_get_luk (target) * 3; + if (battle_config.enemy_critical_rate != 100) + { + cri = cri * battle_config.enemy_critical_rate / 100; + if (cri < 1) + cri = 1; + } + if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に + cri <<= 1; + + if (ac_flag) + cri = 1000; + + if (skill_num == KN_AUTOCOUNTER) + { + if (!(battle_config.monster_auto_counter_type & 1)) + cri = 1000; + else + cri <<= 1; + } + + if (tsd && tsd->critical_def) + cri = cri * (100 - tsd->critical_def) / 100; + + if ((skill_num == 0 || skill_num == KN_AUTOCOUNTER) && skill_lv >= 0 && battle_config.enemy_critical && (MRAND (1000)) < cri) // 判定(スキルの場合は無視) + // 敵の判定 + { + damage += atkmax; + type = 0x0a; + } + else + { + int vitbonusmax; + + if (atkmax > atkmin) + damage += atkmin + MRAND ((atkmax - atkmin + 1)); + else + damage += atkmin; + // スキル修正1(攻撃力倍化系) + // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 + // バッシュ,マグナムブレイク, + // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, + // メマーナイト,カートレボリューション + // ダブルストレイフィング,アローシャワー,チャージアロー, + // ソニックブロー + if (sc_data) + { //状態異常中のダメージ追加 + if (sc_data[SC_OVERTHRUST].timer != -1) // オーバートラスト + damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; + if (sc_data[SC_TRUESIGHT].timer != -1) // トゥルーサイト + damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; + if (sc_data[SC_BERSERK].timer != -1) // バーサーク + damage += damage * 50 / 100; + } + + if (skill_num > 0) + { + int i; + if ((i = skill_get_pl (skill_num)) > 0) + s_ele = i; + + flag = (flag & ~BF_SKILLMASK) | BF_SKILL; + switch (skill_num) + { + case SM_BASH: // バッシュ + damage = damage * (100 + 30 * skill_lv) / 100; + hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; + break; + case SM_MAGNUM: // マグナムブレイク + damage = + damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; + break; + case MC_MAMMONITE: // メマーナイト + damage = damage * (100 + 50 * skill_lv) / 100; + break; + case AC_DOUBLE: // ダブルストレイフィング + damage = damage * (180 + 20 * skill_lv) / 100; + div_ = 2; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case AC_SHOWER: // アローシャワー + damage = damage * (75 + 5 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case AC_CHARGEARROW: // チャージアロー + damage = damage * 150 / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case KN_PIERCE: // ピアース + damage = damage * (100 + 10 * skill_lv) / 100; + hitrate = hitrate * (100 + 5 * skill_lv) / 100; + div_ = t_size + 1; + damage *= div_; + break; + case KN_SPEARSTAB: // スピアスタブ + damage = damage * (100 + 15 * skill_lv) / 100; + break; + case KN_SPEARBOOMERANG: // スピアブーメラン + damage = damage * (100 + 50 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case KN_BRANDISHSPEAR: // ブランディッシュスピア + damage = damage * (100 + 20 * skill_lv) / 100; + if (skill_lv > 3 && wflag == 1) + damage2 += damage / 2; + if (skill_lv > 6 && wflag == 1) + damage2 += damage / 4; + if (skill_lv > 9 && wflag == 1) + damage2 += damage / 8; + if (skill_lv > 6 && wflag == 2) + damage2 += damage / 2; + if (skill_lv > 9 && wflag == 2) + damage2 += damage / 4; + if (skill_lv > 9 && wflag == 3) + damage2 += damage / 2; + damage += damage2; + blewcount = 0; + break; + case KN_BOWLINGBASH: // ボウリングバッシュ + damage = damage * (100 + 50 * skill_lv) / 100; + blewcount = 0; + break; + case KN_AUTOCOUNTER: + if (battle_config.monster_auto_counter_type & 1) + hitrate += 20; + else + hitrate = 1000000; + flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; + break; + case AS_SONICBLOW: // ソニックブロウ + damage = damage * (300 + 50 * skill_lv) / 100; + div_ = 8; + break; + case TF_SPRINKLESAND: // 砂まき + damage = damage * 125 / 100; + break; + case MC_CARTREVOLUTION: // カートレボリューション + damage = (damage * 150) / 100; + break; + // 以下MOB + case NPC_COMBOATTACK: // 多段攻撃 + div_ = skill_get_num (skill_num, skill_lv); + damage *= div_; + break; + case NPC_RANDOMATTACK: // ランダムATK攻撃 + damage = damage * (MPRAND (50, 150)) / 100; + break; + // 属性攻撃(適当) + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + damage = damage * (100 + 25 * (skill_lv - 1)) / 100; + break; + case NPC_GUIDEDATTACK: + hitrate = 1000000; + break; + case NPC_RANGEATTACK: + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case NPC_PIERCINGATT: + flag = (flag & ~BF_RANGEMASK) | BF_SHORT; + break; + case RG_BACKSTAP: // バックスタブ + damage = damage * (300 + 40 * skill_lv) / 100; + hitrate = 1000000; + break; + case RG_RAID: // サプライズアタック + damage = damage * (100 + 40 * skill_lv) / 100; + break; + case RG_INTIMIDATE: // インティミデイト + damage = damage * (100 + 30 * skill_lv) / 100; + break; + case CR_SHIELDCHARGE: // シールドチャージ + damage = damage * (100 + 20 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_SHORT; + s_ele = 0; + break; + case CR_SHIELDBOOMERANG: // シールドブーメラン + damage = damage * (100 + 30 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + s_ele = 0; + break; + case CR_HOLYCROSS: // ホーリークロス + damage = damage * (100 + 35 * skill_lv) / 100; + div_ = 2; + break; + case CR_GRANDCROSS: + hitrate = 1000000; + break; + case AM_DEMONSTRATION: // デモンストレーション + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + break; + case AM_ACIDTERROR: // アシッドテラー + damage = damage * (100 + 40 * skill_lv) / 100; + damage2 = damage2 * (100 + 40 * skill_lv) / 100; + break; + case MO_FINGEROFFENSIVE: //指弾 + damage = damage * (100 + 50 * skill_lv) / 100; + div_ = 1; + break; + case MO_INVESTIGATE: // 発 勁 + if (def1 < 1000000) + damage = + damage * (100 + 75 * skill_lv) / 100 * (def1 + + def2) / + 100; + hitrate = 1000000; + s_ele = 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + damage = damage * 8 + 250 + (skill_lv * 150); + hitrate = 1000000; + s_ele = 0; + break; + case MO_CHAINCOMBO: // 連打掌 + damage = damage * (150 + 50 * skill_lv) / 100; + div_ = 4; + break; + case BA_MUSICALSTRIKE: // ミュージカルストライク + damage = damage * (100 + 50 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case DC_THROWARROW: // 矢撃ち + damage = damage * (100 + 50 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case MO_COMBOFINISH: // 猛龍拳 + damage = damage * (240 + 60 * skill_lv) / 100; + break; + case CH_TIGERFIST: // 伏虎拳 + damage = damage * (100 + 20 * skill_lv) / 100; + break; + case CH_CHAINCRUSH: // 連柱崩撃 + damage = damage * (100 + 20 * skill_lv) / 100; + div_ = skill_get_num (skill_num, skill_lv); + break; + case CH_PALMSTRIKE: // 猛虎硬派山 + damage = damage * (50 + 100 * skill_lv) / 100; + break; + case LK_SPIRALPIERCE: /* スパイラルピアース */ + damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に + div_ = 5; + if (tsd) + tsd->canmove_tick = gettick () + 1000; + else if (tmd) + tmd->canmove_tick = gettick () + 1000; + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + damage = damage * (100 + 20 * skill_lv) / 100; + break; + case LK_JOINTBEAT: /* ジョイントビート */ + damage = damage * (50 + 10 * skill_lv) / 100; + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + damage = damage * (40 + 40 * skill_lv) / 100; + break; + case SN_SHARPSHOOTING: /* シャープシューティング */ + damage += damage * (30 * skill_lv) / 100; + break; + case CG_ARROWVULCAN: /* アローバルカン */ + damage = damage * (160 + 40 * skill_lv) / 100; + div_ = 9; + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + damage = damage * (200 + 20 * skill_lv) / 100; + break; + } + } + + if (skill_num != NPC_CRITICALSLASH) + { + // 対 象の防御力によるダメージの減少 + // ディバインプロテクション(ここでいいのかな?) + if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST + && skill_num != KN_AUTOCOUNTER && def1 < 1000000) + { //DEF, VIT無視 + int t_def; + target_count = + 1 + battle_counttargeted (target, src, + battle_config.vit_penaly_count_lv); + if (battle_config.vit_penaly_type > 0) + { + if (target_count >= battle_config.vit_penaly_count) + { + if (battle_config.vit_penaly_type == 1) + { + def1 = + (def1 * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + def2 = + (def2 * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + t_vit = + (t_vit * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + } + else if (battle_config.vit_penaly_type == 2) + { + def1 -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + def2 -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + t_vit -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + } + if (def1 < 0) + def1 = 0; + if (def2 < 1) + def2 = 1; + if (t_vit < 1) + t_vit = 1; + } + } + t_def = def2 * 8 / 10; + if (battle_check_undead (s_race, battle_get_elem_type (src)) + || s_race == 6) + if (tsd && (skill = pc_checkskill (tsd, AL_DP)) > 0) + t_def += skill * 3; + + vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; + if (battle_config.monster_defense_type) + { + damage = + damage - (def1 * battle_config.monster_defense_type) - + t_def - + ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + else + { + damage = + damage * (100 - def1) / 100 - t_def - + ((vitbonusmax < 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + } + } + } + + // 0未満だった場合1に補正 + if (damage < 1) + damage = 1; + + // 回避修正 + if (hitrate < 1000000) + hitrate = ((hitrate > 95) ? 95 : ((hitrate < 5) ? 5 : hitrate)); + if (hitrate < 1000000 && // 必中攻撃 + (t_sc_data != NULL && (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)))) // 凍結は必中 + hitrate = 1000000; + if (type == 0 && MRAND (100) >= hitrate) + { + damage = damage2 = 0; + dmg_lv = ATK_FLEE; + } + else + { + dmg_lv = ATK_DEF; + } + + if (tsd) + { + int cardfix = 100, i; + cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属 性によるダメージ耐性 + cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 + if (mob_db[md->mob_class].mode & 0x20) + cardfix = cardfix * (100 - tsd->subrace[10]) / 100; + else + cardfix = cardfix * (100 - tsd->subrace[11]) / 100; + for (i = 0; i < tsd->add_def_class_count; i++) + { + if (tsd->add_def_classid[i] == md->mob_class) + { + cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; + break; + } + } + if (flag & BF_LONG) + cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; + if (flag & BF_SHORT) + cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; + damage = damage * cardfix / 100; + } + if (t_sc_data) + { + int cardfix = 100; + if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) + cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; + if (cardfix != 100) + damage = damage * cardfix / 100; + } + if (t_sc_data && t_sc_data[SC_ASSUMPTIO].timer != -1) + { //アシャンプティオ + if (!map[target->m].flag.pvp) + damage = damage / 3; + else + damage = damage / 2; + } + + if (damage < 0) + damage = 0; + + // 属 性の適用 + if (!((battle_config.mob_ghostring_fix == 1) && (battle_get_element (target) == 8) && (target->type == BL_PC))) // [MouseJstr] + if (skill_num != 0 || s_ele != 0 + || !battle_config.mob_attack_attr_none) + damage = + battle_attr_fix (damage, s_ele, battle_get_element (target)); + + if (sc_data && sc_data[SC_AURABLADE].timer != -1) /* オーラブレード 必中 */ + damage += sc_data[SC_AURABLADE].val1 * 10; + if (skill_num == PA_PRESSURE) /* プレッシャー 必中? */ + damage = 700 + 100 * skill_lv; + + // インベナム修正 + if (skill_num == TF_POISON) + { + damage = + battle_attr_fix (damage + 15 * skill_lv, s_ele, + battle_get_element (target)); + } + if (skill_num == MC_CARTREVOLUTION) + { + damage = battle_attr_fix (damage, 0, battle_get_element (target)); + } + + // 完全回避の判定 + if (skill_num == 0 && skill_lv >= 0 && tsd != NULL + && MRAND (1000) < battle_get_flee2 (target)) + { + damage = 0; + type = 0x0b; + dmg_lv = ATK_LUCKY; + } + + if (battle_config.enemy_perfect_flee) + { + if (skill_num == 0 && skill_lv >= 0 && tmd != NULL + && MRAND (1000) < battle_get_flee2 (target)) + { + damage = 0; + type = 0x0b; + dmg_lv = ATK_LUCKY; + } + } + +// if(def1 >= 1000000 && damage > 0) + if (t_mode & 0x40 && damage > 0) + damage = 1; + + if (tsd && tsd->special_state.no_weapon_damage) + damage = 0; + + if (skill_num != CR_GRANDCROSS) + damage = + battle_calc_damage (src, target, damage, div_, skill_num, + skill_lv, flag); + + wd.damage = damage; + wd.damage2 = 0; + wd.type = type; + wd.div_ = div_; + wd.amotion = battle_get_amotion (src); + if (skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion = battle_get_dmotion (target); + wd.blewcount = blewcount; + wd.flag = flag; + wd.dmg_lv = dmg_lv; + return wd; +} + +int battle_is_unarmed (struct block_list *bl) +{ + if (!bl) + return 0; + if (bl->type == BL_PC) + { + struct map_session_data *sd = (struct map_session_data *) bl; + + return (sd->equip_index[EQUIP_SHIELD] == -1 + && sd->equip_index[EQUIP_WEAPON] == -1); + } + else + return 0; +} + +/* + * ========================================================================= + * PCの武器による攻撃 + *------------------------------------------------------------------------- + */ +static struct Damage battle_calc_pc_weapon_attack (struct block_list *src, + struct block_list *target, + int skill_num, + int skill_lv, int wflag) +{ + struct map_session_data *sd = (struct map_session_data *) src, *tsd = + NULL; + struct mob_data *tmd = NULL; + int hitrate, flee, cri = 0, atkmin, atkmax; + int dex, luk, target_count = 1; + int def1 = battle_get_def (target); + int def2 = battle_get_def2 (target); + int t_vit = battle_get_vit (target); + struct Damage wd; + int damage, damage2, damage3 = 0, damage4 = 0, type, div_, blewcount = + skill_get_blewcount (skill_num, skill_lv); + int flag, skill, dmg_lv = 0; + int t_mode = 0, t_race = 0, t_size = 1, s_race = 7, s_ele = 0; + struct status_change *sc_data, *t_sc_data; + short *sc_count; + short *option, *opt1, *opt2; + int atkmax_ = 0, atkmin_ = 0, s_ele_; //二刀流用 + int watk, watk_, cardfix, t_ele; + int da = 0, i, t_class, ac_flag = 0; + int idef_flag = 0, idef_flag_ = 0; + int target_distance; + + //return前の処理があるので情報出力部のみ変更 + if (src == NULL || target == NULL || sd == NULL) + { + nullpo_info (NLP_MARK); + memset (&wd, 0, sizeof (wd)); + return wd; + } + + // アタッカー + s_race = battle_get_race (src); //種族 + s_ele = battle_get_attack_element (src); //属性 + s_ele_ = battle_get_attack_element2 (src); //左手属性 + sc_data = battle_get_sc_data (src); //ステータス異常 + sc_count = battle_get_sc_count (src); //ステータス異常の数 + option = battle_get_option (src); //鷹とかペコとかカートとか + opt1 = battle_get_opt1 (src); //石化、凍結、スタン、睡眠、暗闇 + opt2 = battle_get_opt2 (src); //毒、呪い、沈黙、暗闇? + + if (skill_num != CR_GRANDCROSS) //グランドクロスでないなら + sd->state.attack_type = BF_WEAPON; //攻撃タイプは武器攻撃 + + // ターゲット + if (target->type == BL_PC) //対象がPCなら + tsd = (struct map_session_data *) target; //tsdに代入(tmdはNULL) + else if (target->type == BL_MOB) //対象がMobなら + tmd = (struct mob_data *) target; //tmdに代入(tsdはNULL) + t_race = battle_get_race (target); //対象の種族 + t_ele = battle_get_elem_type (target); //対象の属性 + t_size = battle_get_size (target); //対象のサイズ + t_mode = battle_get_mode (target); //対象のMode + t_sc_data = battle_get_sc_data (target); //対象のステータス異常 + +//オートカウンター処理ここから + if ((skill_num == 0 + || (target->type == BL_PC && battle_config.pc_auto_counter_type & 2) + || (target->type == BL_MOB + && battle_config.monster_auto_counter_type & 2)) + && skill_lv >= 0) + { + if (skill_num != CR_GRANDCROSS && t_sc_data + && t_sc_data[SC_AUTOCOUNTER].timer != -1) + { //グランドクロスでなく、対象がオートカウンター状態の場合 + int dir = map_calc_dir (src, target->x, target->y), t_dir = + battle_get_dir (target); + int dist = distance (src->x, src->y, target->x, target->y); + if (dist <= 0 || map_check_dir (dir, t_dir)) + { //対象との距離が0以下、または対象の正面? + memset (&wd, 0, sizeof (wd)); + t_sc_data[SC_AUTOCOUNTER].val3 = 0; + t_sc_data[SC_AUTOCOUNTER].val4 = 1; + if (sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) + { //自分がオートカウンター状態 + int range = battle_get_range (target); + if ((target->type == BL_PC && ((struct map_session_data *) target)->status.weapon != 11 && dist <= range + 1) || //対象がPCで武器が弓矢でなく射程内 + (target->type == BL_MOB && range <= 3 && dist <= range + 1)) //または対象がMobで射程が3以下で射程内 + t_sc_data[SC_AUTOCOUNTER].val3 = src->id; + } + return wd; //ダメージ構造体を返して終了 + } + else + ac_flag = 1; + } + } +//オートカウンター処理ここまで + + flag = BF_SHORT | BF_WEAPON | BF_NORMAL; // 攻撃の種類の設定 + + // 回避率計算、回避判定は後で + flee = battle_get_flee (target); + if (battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効 + target_count += battle_counttargeted (target, src, battle_config.agi_penaly_count_lv); //対象の数を算出 + if (battle_config.agi_penaly_type > 0) + { + if (target_count >= battle_config.agi_penaly_count) + { //ペナルティ設定より対象が多い + if (battle_config.agi_penaly_type == 1) //回避率がagi_penaly_num%ずつ減少 + flee = + (flee * + (100 - + (target_count - + (battle_config.agi_penaly_count - + 1)) * battle_config.agi_penaly_num)) / 100; + else if (battle_config.agi_penaly_type == 2) //回避率がagi_penaly_num分減少 + flee -= + (target_count - + (battle_config.agi_penaly_count - + 1)) * battle_config.agi_penaly_num; + if (flee < 1) + flee = 1; //回避率は最低でも1 + } + } + hitrate = battle_get_hit (src) - flee + 80; //命中率計算 + + { // [fate] Reduce hit chance by distance + int dx = abs (src->x - target->x); + int dy = abs (src->y - target->y); + int malus_dist; + + target_distance = MAX (dx, dy); + malus_dist = + MAX (0, target_distance - (skill_power (sd, AC_OWL) / 75)); + hitrate -= (malus_dist * (malus_dist + 1)); + } + + dex = battle_get_dex (src); //DEX + luk = battle_get_luk (src); //LUK + watk = battle_get_atk (src); //ATK + watk_ = battle_get_atk_ (src); //ATK左手 + + type = 0; // normal + div_ = 1; // single attack + + if (skill_num == HW_MAGICCRASHER) + { /* マジッククラッシャーはMATKで殴る */ + damage = damage2 = battle_get_matk1 (src); //damega,damega2初登場、base_atkの取得 + } + else + { + damage = damage2 = battle_get_baseatk (&sd->bl); //damega,damega2初登場、base_atkの取得 + } + if (sd->attackrange > 2) + { // [fate] ranged weapon? + const int range_damage_bonus = 80; // up to 31.25% bonus for long-range hit + damage = + damage * (256 + + ((range_damage_bonus * target_distance) / + sd->attackrange)) >> 8; + damage2 = + damage2 * (256 + + ((range_damage_bonus * target_distance) / + sd->attackrange)) >> 8; + } + + atkmin = atkmin_ = dex; //最低ATKはDEXで初期化? + sd->state.arrow_atk = 0; //arrow_atk初期化 + 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 (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 (sd->status.weapon == 11) + { //武器が弓矢の場合 + atkmin = watk * ((atkmin < watk) ? atkmin : watk) / 100; //弓用最低ATK計算 + flag = (flag & ~BF_RANGEMASK) | BF_LONG; //遠距離攻撃フラグを有効 + if (sd->arrow_ele > 0) //属性矢なら属性を矢の属性に変更 + s_ele = sd->arrow_ele; + sd->state.arrow_atk = 1; //arrow_atk有効化 + } + + // サイズ修正 + // ペコ騎乗していて、槍で攻撃した場合は中型のサイズ修正を100にする + // ウェポンパーフェクション,ドレイクC + if (((sd->special_state.no_sizefix) + || (pc_isriding (sd) + && (sd->status.weapon == 4 || sd->status.weapon == 5) + && t_size == 1) || skill_num == MO_EXTREMITYFIST)) + { //ペコ騎乗していて、槍で中型を攻撃 + atkmax = watk; + atkmax_ = watk_; + } + else + { + atkmax = (watk * sd->atkmods[t_size]) / 100; + atkmin = (atkmin * sd->atkmods[t_size]) / 100; + atkmax_ = (watk_ * sd->atkmods_[t_size]) / 100; + atkmin_ = (atkmin_ * sd->atkmods[t_size]) / 100; + } + if ((sc_data != NULL && sc_data[SC_WEAPONPERFECTION].timer != -1) + || (sd->special_state.no_sizefix)) + { // ウェポンパーフェクション || ドレイクカード + atkmax = watk; + atkmax_ = watk_; + } + + if (atkmin > atkmax && !(sd->state.arrow_atk)) + atkmin = atkmax; //弓は最低が上回る場合あり + if (atkmin_ > atkmax_) + atkmin_ = atkmax_; + + if (sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer != -1) + { // マキシマイズパワー + atkmin = atkmax; + atkmin_ = atkmax_; + } + + //ダブルアタック判定 + if (sd->weapontype1 == 0x01) + { + if (skill_num == 0 && skill_lv >= 0 + && (skill = pc_checkskill (sd, TF_DOUBLE)) > 0) + da = (MRAND (100) < (skill * 5)) ? 1 : 0; + } + + //三段掌 + if (skill_num == 0 && skill_lv >= 0 + && (skill = pc_checkskill (sd, MO_TRIPLEATTACK)) > 0 + && sd->status.weapon <= 16 && !sd->state.arrow_atk) + { + da = (MRAND (100) < (30 - skill)) ? 2 : 0; + } + + if (sd->double_rate > 0 && da == 0 && skill_num == 0 && skill_lv >= 0) + da = (MRAND (100) < sd->double_rate) ? 1 : 0; + + // 過剰精錬ボーナス + if (sd->overrefine > 0) + damage += MPRAND (1, sd->overrefine); + if (sd->overrefine_ > 0) + damage2 += MPRAND (1, sd->overrefine_); + + if (da == 0) + { //ダブルアタックが発動していない + // クリティカル計算 + cri = battle_get_critical (src); + + if (sd->state.arrow_atk) + cri += sd->arrow_cri; + if (sd->status.weapon == 16) + // カタールの場合、クリティカルを倍に + cri <<= 1; + cri -= battle_get_luk (target) * 3; + if (t_sc_data != NULL && t_sc_data[SC_SLEEP].timer != -1) // 睡眠中はクリティカルが倍に + cri <<= 1; + if (ac_flag) + cri = 1000; + + if (skill_num == KN_AUTOCOUNTER) + { + if (!(battle_config.pc_auto_counter_type & 1)) + cri = 1000; + else + cri <<= 1; + } + + if (skill_num == SN_SHARPSHOOTING && MRAND (100) < 50) + cri = 1000; + } + + if (tsd && tsd->critical_def) + cri = cri * (100 - tsd->critical_def) / 100; + + if (da == 0 && (skill_num == 0 || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING) && skill_lv >= 0 && //ダブルアタックが発動していない + (MRAND (1000)) < cri) // 判定(スキルの場合は無視) + { + damage += atkmax; + damage2 += atkmax_; + if (sd->atk_rate != 100) + { + damage = (damage * sd->atk_rate) / 100; + damage2 = (damage2 * sd->atk_rate) / 100; + } + if (sd->state.arrow_atk) + damage += sd->arrow_atk; + type = 0x0a; + +/* if(def1 < 1000000) { + if(sd->def_ratio_atk_ele & (1<<t_ele) || sd->def_ratio_atk_race & (1<<t_race)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(sd->def_ratio_atk_ele_ & (1<<t_ele) || sd->def_ratio_atk_race_ & (1<<t_race)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + if(t_mode & 0x20) { + if(!idef_flag && sd->def_ratio_atk_race & (1<<10)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<10)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + else { + if(!idef_flag && sd->def_ratio_atk_race & (1<<11)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<11)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + }*/ + } + else + { + int vitbonusmax; + + if (atkmax > atkmin) + damage += atkmin + MRAND ((atkmax - atkmin + 1)); + else + damage += atkmin; + if (atkmax_ > atkmin_) + damage2 += atkmin_ + MRAND ((atkmax_ - atkmin_ + 1)); + else + damage2 += atkmin_; + if (sd->atk_rate != 100) + { + damage = (damage * sd->atk_rate) / 100; + damage2 = (damage2 * sd->atk_rate) / 100; + } + + if (sd->state.arrow_atk) + { + if (sd->arrow_atk > 0) + damage += MRAND ((sd->arrow_atk + 1)); + hitrate += sd->arrow_hit; + } + + if (skill_num != MO_INVESTIGATE && def1 < 1000000) + { + if (sd->def_ratio_atk_ele & (1 << t_ele) + || sd->def_ratio_atk_race & (1 << t_race)) + { + damage = (damage * (def1 + def2)) / 100; + idef_flag = 1; + } + if (sd->def_ratio_atk_ele_ & (1 << t_ele) + || sd->def_ratio_atk_race_ & (1 << t_race)) + { + damage2 = (damage2 * (def1 + def2)) / 100; + idef_flag_ = 1; + } + if (t_mode & 0x20) + { + if (!idef_flag && sd->def_ratio_atk_race & (1 << 10)) + { + damage = (damage * (def1 + def2)) / 100; + idef_flag = 1; + } + if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 10)) + { + damage2 = (damage2 * (def1 + def2)) / 100; + idef_flag_ = 1; + } + } + else + { + if (!idef_flag && sd->def_ratio_atk_race & (1 << 11)) + { + damage = (damage * (def1 + def2)) / 100; + idef_flag = 1; + } + if (!idef_flag_ && sd->def_ratio_atk_race_ & (1 << 11)) + { + damage2 = (damage2 * (def1 + def2)) / 100; + idef_flag_ = 1; + } + } + } + + // スキル修正1(攻撃力倍化系) + // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 + // バッシュ,マグナムブレイク, + // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, + // メマーナイト,カートレボリューション + // ダブルストレイフィング,アローシャワー,チャージアロー, + // ソニックブロー + if (sc_data) + { //状態異常中のダメージ追加 + if (sc_data[SC_OVERTHRUST].timer != -1) + { // オーバートラスト + damage += damage * (5 * sc_data[SC_OVERTHRUST].val1) / 100; + damage2 += damage2 * (5 * sc_data[SC_OVERTHRUST].val1) / 100; + } + if (sc_data[SC_TRUESIGHT].timer != -1) + { // トゥルーサイト + damage += damage * (2 * sc_data[SC_TRUESIGHT].val1) / 100; + damage2 += damage2 * (2 * sc_data[SC_TRUESIGHT].val1) / 100; + } + if (sc_data[SC_BERSERK].timer != -1) + { // バーサーク + damage += damage * 50 / 100; + damage2 += damage2 * 50 / 100; + } + } + + if (skill_num > 0) + { + int i; + if ((i = skill_get_pl (skill_num)) > 0) + s_ele = s_ele_ = i; + + flag = (flag & ~BF_SKILLMASK) | BF_SKILL; + switch (skill_num) + { + case SM_BASH: // バッシュ + damage = damage * (100 + 30 * skill_lv) / 100; + damage2 = damage2 * (100 + 30 * skill_lv) / 100; + hitrate = (hitrate * (100 + 5 * skill_lv)) / 100; + break; + case SM_MAGNUM: // マグナムブレイク + damage = + damage * (5 * skill_lv + (wflag) ? 65 : 115) / 100; + damage2 = + damage2 * (5 * skill_lv + (wflag) ? 65 : 115) / 100; + break; + case MC_MAMMONITE: // メマーナイト + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + break; + case AC_DOUBLE: // ダブルストレイフィング + if (!sd->state.arrow_atk && sd->arrow_atk > 0) + { + int arr = MRAND ((sd->arrow_atk + 1)); + damage += arr; + damage2 += arr; + } + damage = damage * (180 + 20 * skill_lv) / 100; + damage2 = damage2 * (180 + 20 * skill_lv) / 100; + div_ = 2; + if (sd->arrow_ele > 0) + { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + sd->state.arrow_atk = 1; + break; + case AC_SHOWER: // アローシャワー + if (!sd->state.arrow_atk && sd->arrow_atk > 0) + { + int arr = MRAND ((sd->arrow_atk + 1)); + damage += arr; + damage2 += arr; + } + damage = damage * (75 + 5 * skill_lv) / 100; + damage2 = damage2 * (75 + 5 * skill_lv) / 100; + if (sd->arrow_ele > 0) + { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + sd->state.arrow_atk = 1; + break; + case AC_CHARGEARROW: // チャージアロー + if (!sd->state.arrow_atk && sd->arrow_atk > 0) + { + int arr = MRAND ((sd->arrow_atk + 1)); + damage += arr; + damage2 += arr; + } + damage = damage * 150 / 100; + damage2 = damage2 * 150 / 100; + if (sd->arrow_ele > 0) + { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + sd->state.arrow_atk = 1; + break; + case KN_PIERCE: // ピアース + damage = damage * (100 + 10 * skill_lv) / 100; + damage2 = damage2 * (100 + 10 * skill_lv) / 100; + hitrate = hitrate * (100 + 5 * skill_lv) / 100; + div_ = t_size + 1; + damage *= div_; + damage2 *= div_; + break; + case KN_SPEARSTAB: // スピアスタブ + damage = damage * (100 + 15 * skill_lv) / 100; + damage2 = damage2 * (100 + 15 * skill_lv) / 100; + break; + case KN_SPEARBOOMERANG: // スピアブーメラン + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case KN_BRANDISHSPEAR: // ブランディッシュスピア + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + if (skill_lv > 3 && wflag == 1) + damage3 += damage / 2; + if (skill_lv > 6 && wflag == 1) + damage3 += damage / 4; + if (skill_lv > 9 && wflag == 1) + damage3 += damage / 8; + if (skill_lv > 6 && wflag == 2) + damage3 += damage / 2; + if (skill_lv > 9 && wflag == 2) + damage3 += damage / 4; + if (skill_lv > 9 && wflag == 3) + damage3 += damage / 2; + damage += damage3; + if (skill_lv > 3 && wflag == 1) + damage4 += damage2 / 2; + if (skill_lv > 6 && wflag == 1) + damage4 += damage2 / 4; + if (skill_lv > 9 && wflag == 1) + damage4 += damage2 / 8; + if (skill_lv > 6 && wflag == 2) + damage4 += damage2 / 2; + if (skill_lv > 9 && wflag == 2) + damage4 += damage2 / 4; + if (skill_lv > 9 && wflag == 3) + damage4 += damage2 / 2; + damage2 += damage4; + blewcount = 0; + break; + case KN_BOWLINGBASH: // ボウリングバッシュ + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + blewcount = 0; + break; + case KN_AUTOCOUNTER: + if (battle_config.pc_auto_counter_type & 1) + hitrate += 20; + else + hitrate = 1000000; + flag = (flag & ~BF_SKILLMASK) | BF_NORMAL; + break; + case AS_SONICBLOW: // ソニックブロウ + hitrate += 30; // hitrate +30, thanks to midas + damage = damage * (300 + 50 * skill_lv) / 100; + damage2 = damage2 * (300 + 50 * skill_lv) / 100; + div_ = 8; + break; + case TF_SPRINKLESAND: // 砂まき + damage = damage * 125 / 100; + damage2 = damage2 * 125 / 100; + break; + case MC_CARTREVOLUTION: // カートレボリューション + if (sd->cart_max_weight > 0 && sd->cart_weight > 0) + { + damage = + (damage * + (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + + (sd->cart_weight * 100 / + sd->cart_max_weight))) / 100; + damage2 = + (damage2 * + (150 + pc_checkskill (sd, BS_WEAPONRESEARCH) + + (sd->cart_weight * 100 / + sd->cart_max_weight))) / 100; + } + else + { + damage = (damage * 150) / 100; + damage2 = (damage2 * 150) / 100; + } + break; + // 以下MOB + case NPC_COMBOATTACK: // 多段攻撃 + div_ = skill_get_num (skill_num, skill_lv); + damage *= div_; + damage2 *= div_; + break; + case NPC_RANDOMATTACK: // ランダムATK攻撃 + damage = damage * (MPRAND (50, 150)) / 100; + damage2 = damage2 * (MPRAND (50, 150)) / 100; + break; + // 属性攻撃(適当) + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + damage = damage * (100 + 25 * skill_lv) / 100; + damage2 = damage2 * (100 + 25 * skill_lv) / 100; + break; + case NPC_GUIDEDATTACK: + hitrate = 1000000; + break; + case NPC_RANGEATTACK: + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + break; + case NPC_PIERCINGATT: + flag = (flag & ~BF_RANGEMASK) | BF_SHORT; + break; + case RG_BACKSTAP: // バックスタブ + if (battle_config.backstab_bow_penalty == 1 + && sd->status.weapon == 11) + { + damage = (damage * (300 + 40 * skill_lv) / 100) / 2; + damage2 = (damage2 * (300 + 40 * skill_lv) / 100) / 2; + } + else + { + damage = damage * (300 + 40 * skill_lv) / 100; + damage2 = damage2 * (300 + 40 * skill_lv) / 100; + } + hitrate = 1000000; + break; + case RG_RAID: // サプライズアタック + damage = damage * (100 + 40 * skill_lv) / 100; + damage2 = damage2 * (100 + 40 * skill_lv) / 100; + break; + case RG_INTIMIDATE: // インティミデイト + damage = damage * (100 + 30 * skill_lv) / 100; + damage2 = damage2 * (100 + 30 * skill_lv) / 100; + break; + case CR_SHIELDCHARGE: // シールドチャージ + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_SHORT; + s_ele = 0; + break; + case CR_SHIELDBOOMERANG: // シールドブーメラン + damage = damage * (100 + 30 * skill_lv) / 100; + damage2 = damage2 * (100 + 30 * skill_lv) / 100; + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + s_ele = 0; + break; + case CR_HOLYCROSS: // ホーリークロス + damage = damage * (100 + 35 * skill_lv) / 100; + damage2 = damage2 * (100 + 35 * skill_lv) / 100; + div_ = 2; + break; + case CR_GRANDCROSS: + hitrate = 1000000; + break; + case AM_DEMONSTRATION: // デモンストレーション + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + break; + case AM_ACIDTERROR: // アシッドテラー + damage = damage * (100 + 40 * skill_lv) / 100; + damage2 = damage2 * (100 + 40 * skill_lv) / 100; + break; + case MO_FINGEROFFENSIVE: //指弾 + if (battle_config.finger_offensive_type == 0) + { + damage = + damage * (100 + + 50 * skill_lv) / 100 * + sd->spiritball_old; + damage2 = + damage2 * (100 + + 50 * skill_lv) / 100 * + sd->spiritball_old; + div_ = sd->spiritball_old; + } + else + { + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + div_ = 1; + } + break; + case MO_INVESTIGATE: // 発 勁 + if (def1 < 1000000) + { + damage = + damage * (100 + 75 * skill_lv) / 100 * (def1 + + def2) / + 100; + damage2 = + damage2 * (100 + 75 * skill_lv) / 100 * (def1 + + def2) / + 100; + } + hitrate = 1000000; + s_ele = 0; + s_ele_ = 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + damage = + damage * (8 + ((sd->status.sp) / 10)) + 250 + + (skill_lv * 150); + damage2 = + damage2 * (8 + ((sd->status.sp) / 10)) + 250 + + (skill_lv * 150); + sd->status.sp = 0; + clif_updatestatus (sd, SP_SP); + hitrate = 1000000; + s_ele = 0; + s_ele_ = 0; + break; + case MO_CHAINCOMBO: // 連打掌 + damage = damage * (150 + 50 * skill_lv) / 100; + damage2 = damage2 * (150 + 50 * skill_lv) / 100; + div_ = 4; + break; + case MO_COMBOFINISH: // 猛龍拳 + damage = damage * (240 + 60 * skill_lv) / 100; + damage2 = damage2 * (240 + 60 * skill_lv) / 100; + break; + case BA_MUSICALSTRIKE: // ミュージカルストライク + if (!sd->state.arrow_atk && sd->arrow_atk > 0) + { + int arr = MRAND ((sd->arrow_atk + 1)); + damage += arr; + damage2 += arr; + } + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + if (sd->arrow_ele > 0) + { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + sd->state.arrow_atk = 1; + break; + case DC_THROWARROW: // 矢撃ち + if (!sd->state.arrow_atk && sd->arrow_atk > 0) + { + int arr = MRAND ((sd->arrow_atk + 1)); + damage += arr; + damage2 += arr; + } + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + if (sd->arrow_ele > 0) + { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag = (flag & ~BF_RANGEMASK) | BF_LONG; + sd->state.arrow_atk = 1; + break; + case CH_TIGERFIST: // 伏虎拳 + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + break; + case CH_CHAINCRUSH: // 連柱崩撃 + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + div_ = skill_get_num (skill_num, skill_lv); + break; + case CH_PALMSTRIKE: // 猛虎硬派山 + damage = damage * (50 + 100 * skill_lv) / 100; + damage2 = damage2 * (50 + 100 * skill_lv) / 100; + break; + case LK_SPIRALPIERCE: /* スパイラルピアース */ + damage = damage * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に + damage2 = damage2 * (100 + 50 * skill_lv) / 100; //増加量が分からないので適当に + div_ = 5; + if (tsd) + tsd->canmove_tick = gettick () + 1000; + else if (tmd) + tmd->canmove_tick = gettick () + 1000; + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + damage = damage * (100 + 20 * skill_lv) / 100; + damage2 = damage2 * (100 + 20 * skill_lv) / 100; + break; + case LK_JOINTBEAT: /* ジョイントビート */ + damage = damage * (50 + 10 * skill_lv) / 100; + damage2 = damage2 * (50 + 10 * skill_lv) / 100; + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + damage = damage * (40 + 40 * skill_lv) / 100; + damage2 = damage2 * (40 + 40 * skill_lv) / 100; + break; + case SN_SHARPSHOOTING: /* シャープシューティング */ + damage += damage * (30 * skill_lv) / 100; + damage2 += damage2 * (30 * skill_lv) / 100; + break; + case CG_ARROWVULCAN: /* アローバルカン */ + damage = damage * (160 + 40 * skill_lv) / 100; + damage2 = damage2 * (160 + 40 * skill_lv) / 100; + div_ = 9; + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + damage = + damage * (200 + 20 * skill_lv + + 20 * pc_checkskill (sd, + AS_POISONREACT)) / 100; + damage2 = + damage2 * (200 + 20 * skill_lv + + 20 * pc_checkskill (sd, + AS_POISONREACT)) / 100; + break; + case PA_SACRIFICE: + if (sd) + { + int hp, mhp, damage3; + hp = battle_get_hp (src); + mhp = battle_get_max_hp (src); + damage3 = mhp * ((skill_lv / 2) + (50 / 100)) / 100; + damage = + (((skill_lv * 15) + 90) / 100) * damage3 / 100; + damage2 = + (((skill_lv * 15) + 90) / 100) * damage3 / 100; + } + break; + case ASC_BREAKER: // -- moonsoul (special damage for ASC_BREAKER skill) + if (sd) + { + int damage3; + int mdef1 = battle_get_mdef (target); + int mdef2 = battle_get_mdef2 (target); + int imdef_flag = 0; + + damage = + ((damage * 5) + + (skill_lv * battle_get_int (src) * 5) + + MRAND (500) + 500) / 2; + damage2 = + ((damage2 * 5) + + (skill_lv * battle_get_int (src) * 5) + + MRAND (500) + 500) / 2; + damage3 = damage; + hitrate = 1000000; + + if (sd->ignore_mdef_ele & (1 << t_ele) + || sd->ignore_mdef_race & (1 << t_race)) + imdef_flag = 1; + if (t_mode & 0x20) + { + if (sd->ignore_mdef_race & (1 << 10)) + imdef_flag = 1; + } + else + { + if (sd->ignore_mdef_race & (1 << 11)) + imdef_flag = 1; + } + if (!imdef_flag) + { + if (battle_config.magic_defense_type) + { + damage3 = + damage3 - + (mdef1 * + battle_config.magic_defense_type) - + mdef2; + } + else + { + damage3 = + (damage3 * (100 - mdef1)) / 100 - mdef2; + } + } + + if (damage3 < 1) + damage3 = 1; + + damage3 = + battle_attr_fix (damage2, s_ele_, + battle_get_element (target)); + } + break; + } + } + if (da == 2) + { //三段掌が発動しているか + type = 0x08; + div_ = 255; //三段掌用に… + damage = + damage * (100 + + 20 * pc_checkskill (sd, MO_TRIPLEATTACK)) / 100; + } + + if (skill_num != NPC_CRITICALSLASH) + { + // 対 象の防御力によるダメージの減少 + // ディバインプロテクション(ここでいいのかな?) + if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST + && skill_num != KN_AUTOCOUNTER && def1 < 1000000) + { //DEF, VIT無視 + int t_def; + target_count = + 1 + battle_counttargeted (target, src, + battle_config.vit_penaly_count_lv); + if (battle_config.vit_penaly_type > 0) + { + if (target_count >= battle_config.vit_penaly_count) + { + if (battle_config.vit_penaly_type == 1) + { + def1 = + (def1 * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + def2 = + (def2 * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + t_vit = + (t_vit * + (100 - + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num)) / + 100; + } + else if (battle_config.vit_penaly_type == 2) + { + def1 -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + def2 -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + t_vit -= + (target_count - + (battle_config.vit_penaly_count - + 1)) * battle_config.vit_penaly_num; + } + if (def1 < 0) + def1 = 0; + if (def2 < 1) + def2 = 1; + if (t_vit < 1) + t_vit = 1; + } + } + t_def = def2 * 8 / 10; + vitbonusmax = (t_vit / 20) * (t_vit / 20) - 1; + if (sd->ignore_def_ele & (1 << t_ele) + || sd->ignore_def_race & (1 << t_race)) + idef_flag = 1; + if (sd->ignore_def_ele_ & (1 << t_ele) + || sd->ignore_def_race_ & (1 << t_race)) + idef_flag_ = 1; + if (t_mode & 0x20) + { + if (sd->ignore_def_race & (1 << 10)) + idef_flag = 1; + if (sd->ignore_def_race_ & (1 << 10)) + idef_flag_ = 1; + } + else + { + if (sd->ignore_def_race & (1 << 11)) + idef_flag = 1; + if (sd->ignore_def_race_ & (1 << 11)) + idef_flag_ = 1; + } + + if (!idef_flag) + { + if (battle_config.player_defense_type) + { + damage = + damage - + (def1 * battle_config.player_defense_type) - + t_def - + ((vitbonusmax < + 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + else + { + damage = + damage * (100 - def1) / 100 - t_def - + ((vitbonusmax < + 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + } + if (!idef_flag_) + { + if (battle_config.player_defense_type) + { + damage2 = + damage2 - + (def1 * battle_config.player_defense_type) - + t_def - + ((vitbonusmax < + 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + else + { + damage2 = + damage2 * (100 - def1) / 100 - t_def - + ((vitbonusmax < + 1) ? 0 : MRAND ((vitbonusmax + 1))); + } + } + } + } + } + // 精錬ダメージの追加 + if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) + { //DEF, VIT無視 + damage += battle_get_atk2 (src); + damage2 += battle_get_atk_2 (src); + } + if (skill_num == CR_SHIELDBOOMERANG) + { + if (sd->equip_index[8] >= 0) + { + int index = sd->equip_index[8]; + if (sd->inventory_data[index] + && sd->inventory_data[index]->type == 5) + { + damage += sd->inventory_data[index]->weight / 10; + damage += + sd->status.inventory[index].refine * pc_getrefinebonus (0, + 1); + } + } + } + if (skill_num == LK_SPIRALPIERCE) + { /* スパイラルピアース */ + if (sd->equip_index[9] >= 0) + { //重量で追加ダメージらしいのでシールドブーメランを参考に追加 + int index = sd->equip_index[9]; + if (sd->inventory_data[index] + && sd->inventory_data[index]->type == 4) + { + damage += + (int) (double) (sd->inventory_data[index]->weight * + (0.8 * skill_lv * 4 / 10)); + damage += + sd->status.inventory[index].refine * pc_getrefinebonus (0, + 1); + } + } + } + + // 0未満だった場合1に補正 + if (damage < 1) + damage = 1; + if (damage2 < 1) + damage2 = 1; + + // スキル修正2(修練系) + // 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応) + if (skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST + && skill_num != CR_GRANDCROSS) + { //修練ダメージ無視 + damage = battle_addmastery (sd, target, damage, 0); + damage2 = battle_addmastery (sd, target, damage2, 1); + } + + if (sd->perfect_hit > 0) + { + if (MRAND (100) < sd->perfect_hit) + hitrate = 1000000; + } + + // 回避修正 + hitrate = (hitrate < 5) ? 5 : hitrate; + if (hitrate < 1000000 && // 必中攻撃 + (t_sc_data != NULL && (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)))) // 凍結は必中 + hitrate = 1000000; + if (type == 0 && MRAND (100) >= hitrate) + { + damage = damage2 = 0; + dmg_lv = ATK_FLEE; + } + else + { + dmg_lv = ATK_DEF; + } + // スキル修正3(武器研究) + if ((skill = pc_checkskill (sd, BS_WEAPONRESEARCH)) > 0) + { + damage += skill * 2; + damage2 += skill * 2; + } + //Advanced Katar Research by zanetheinsane + if (sd->weapontype1 == 0x10 || sd->weapontype2 == 0x10) + { + if ((skill = pc_checkskill (sd, ASC_KATAR)) > 0) + { + damage += (damage * ((skill * 2) + 10)) / 100; + } + } + +//スキルによるダメージ補正ここまで + +//カードによるダメージ追加処理ここから + cardfix = 100; + if (!sd->state.arrow_atk) + { //弓矢以外 + if (!battle_config.left_cardfix_to_right) + { //左手カード補正設定無し + cardfix = cardfix * (100 + sd->addrace[t_race]) / 100; // 種族によるダメージ修正 + cardfix = cardfix * (100 + sd->addele[t_ele]) / 100; // 属性によるダメージ修正 + cardfix = cardfix * (100 + sd->addsize[t_size]) / 100; // サイズによるダメージ修正 + } + else + { + cardfix = cardfix * (100 + sd->addrace[t_race] + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正(左手による追加あり) + cardfix = cardfix * (100 + sd->addele[t_ele] + sd->addele_[t_ele]) / 100; // 属性によるダメージ修正(左手による追加あり) + cardfix = cardfix * (100 + sd->addsize[t_size] + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正(左手による追加あり) + } + } + else + { //弓矢 + cardfix = cardfix * (100 + sd->addrace[t_race] + sd->arrow_addrace[t_race]) / 100; // 種族によるダメージ修正(弓矢による追加あり) + cardfix = cardfix * (100 + sd->addele[t_ele] + sd->arrow_addele[t_ele]) / 100; // 属性によるダメージ修正(弓矢による追加あり) + cardfix = cardfix * (100 + sd->addsize[t_size] + sd->arrow_addsize[t_size]) / 100; // サイズによるダメージ修正(弓矢による追加あり) + } + if (t_mode & 0x20) + { //ボス + if (!sd->state.arrow_atk) + { //弓矢攻撃以外なら + if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し + cardfix = cardfix * (100 + sd->addrace[10]) / 100; //ボスモンスターに追加ダメージ + else //左手カード補正設定あり + cardfix = cardfix * (100 + sd->addrace[10] + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ(左手による追加あり) + } + else //弓矢攻撃 + cardfix = cardfix * (100 + sd->addrace[10] + sd->arrow_addrace[10]) / 100; //ボスモンスターに追加ダメージ(弓矢による追加あり) + } + else + { //ボスじゃない + if (!sd->state.arrow_atk) + { //弓矢攻撃以外 + if (!battle_config.left_cardfix_to_right) //左手カード補正設定無し + cardfix = cardfix * (100 + sd->addrace[11]) / 100; //ボス以外モンスターに追加ダメージ + else //左手カード補正設定あり + cardfix = cardfix * (100 + sd->addrace[11] + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ(左手による追加あり) + } + else + cardfix = cardfix * (100 + sd->addrace[11] + sd->arrow_addrace[11]) / 100; //ボス以外モンスターに追加ダメージ(弓矢による追加あり) + } + //特定Class用補正処理(少女の日記→ボンゴン用?) + t_class = battle_get_class (target); + for (i = 0; i < sd->add_damage_class_count; i++) + { + if (sd->add_damage_classid[i] == t_class) + { + cardfix = cardfix * (100 + sd->add_damage_classrate[i]) / 100; + break; + } + } + if (skill_num != CR_GRANDCROSS || !battle_config.gx_cardfix) + damage = damage * cardfix / 100; //カード補正によるダメージ増加 +//カードによるダメージ増加処理ここまで + +//カードによるダメージ追加処理(左手)ここから + cardfix = 100; + if (!battle_config.left_cardfix_to_right) + { //左手カード補正設定無し + cardfix = cardfix * (100 + sd->addrace_[t_race]) / 100; // 種族によるダメージ修正左手 + cardfix = cardfix * (100 + sd->addele_[t_ele]) / 100; // 属 性によるダメージ修正左手 + cardfix = cardfix * (100 + sd->addsize_[t_size]) / 100; // サイズによるダメージ修正左手 + if (t_mode & 0x20) //ボス + cardfix = cardfix * (100 + sd->addrace_[10]) / 100; //ボスモンスターに追加ダメージ左手 + else + cardfix = cardfix * (100 + sd->addrace_[11]) / 100; //ボス以外モンスターに追加ダメージ左手 + } + //特定Class用補正処理左手(少女の日記→ボンゴン用?) + for (i = 0; i < sd->add_damage_class_count_; i++) + { + if (sd->add_damage_classid_[i] == t_class) + { + cardfix = cardfix * (100 + sd->add_damage_classrate_[i]) / 100; + break; + } + } + if (skill_num != CR_GRANDCROSS) + damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ増加 +//カードによるダメージ増加処理(左手)ここまで + +// -- moonsoul (cardfix for magic damage portion of ASC_BREAKER) + if (skill_num == ASC_BREAKER) + damage3 = damage3 * cardfix / 100; + +//カードによるダメージ減衰処理ここから + if (tsd) + { //対象がPCの場合 + cardfix = 100; + cardfix = cardfix * (100 - tsd->subrace[s_race]) / 100; // 種族によるダメージ耐性 + cardfix = cardfix * (100 - tsd->subele[s_ele]) / 100; // 属性によるダメージ耐性 + if (battle_get_mode (src) & 0x20) + cardfix = cardfix * (100 - tsd->subrace[10]) / 100; //ボスからの攻撃はダメージ減少 + else + cardfix = cardfix * (100 - tsd->subrace[11]) / 100; //ボス以外からの攻撃はダメージ減少 + //特定Class用補正処理左手(少女の日記→ボンゴン用?) + for (i = 0; i < tsd->add_def_class_count; i++) + { + if (tsd->add_def_classid[i] == sd->status.pc_class) + { + cardfix = cardfix * (100 - tsd->add_def_classrate[i]) / 100; + break; + } + } + if (flag & BF_LONG) + cardfix = cardfix * (100 - tsd->long_attack_def_rate) / 100; //遠距離攻撃はダメージ減少(ホルンCとか) + if (flag & BF_SHORT) + cardfix = cardfix * (100 - tsd->near_attack_def_rate) / 100; //近距離攻撃はダメージ減少(該当無し?) + damage = damage * cardfix / 100; //カード補正によるダメージ減少 + damage2 = damage2 * cardfix / 100; //カード補正による左手ダメージ減少 + } +//カードによるダメージ減衰処理ここまで + +//対象にステータス異常がある場合のダメージ減算処理ここから + if (t_sc_data) + { + cardfix = 100; + if (t_sc_data[SC_DEFENDER].timer != -1 && flag & BF_LONG) //ディフェンダー状態で遠距離攻撃 + cardfix = cardfix * (100 - t_sc_data[SC_DEFENDER].val2) / 100; //ディフェンダーによる減衰 + if (cardfix != 100) + { + damage = damage * cardfix / 100; //ディフェンダー補正によるダメージ減少 + damage2 = damage2 * cardfix / 100; //ディフェンダー補正による左手ダメージ減少 + } + if (t_sc_data[SC_ASSUMPTIO].timer != -1) + { //アスムプティオ + if (!map[target->m].flag.pvp) + { + damage = damage / 3; + damage2 = damage2 / 3; + } + else + { + damage = damage / 2; + damage2 = damage2 / 2; + } + } + } +//対象にステータス異常がある場合のダメージ減算処理ここまで + + if (damage < 0) + damage = 0; + if (damage2 < 0) + damage2 = 0; + + // 属 性の適用 + damage = battle_attr_fix (damage, s_ele, battle_get_element (target)); + damage2 = battle_attr_fix (damage2, s_ele_, battle_get_element (target)); + + // 星のかけら、気球の適用 + damage += sd->star; + damage2 += sd->star_; + damage += sd->spiritball * 3; + damage2 += sd->spiritball * 3; + + if (sc_data && sc_data[SC_AURABLADE].timer != -1) + { /* オーラブレード 必中 */ + damage += sc_data[SC_AURABLADE].val1 * 10; + damage2 += sc_data[SC_AURABLADE].val1 * 10; + } + if (skill_num == PA_PRESSURE) + { /* プレッシャー 必中? */ + damage = 700 + 100 * skill_lv; + damage2 = 700 + 100 * skill_lv; + } + + // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ! + // >map_session_data に左手ダメージ(atk,atk2)追加して + // >pc_calcstatus()でやるべきかな? + // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して + // pc_calcstatus()でデータを入力しています + + //左手のみ武器装備 + if (sd->weapontype1 == 0 && sd->weapontype2 > 0) + { + damage = damage2; + damage2 = 0; + } + // 右手、左手修練の適用 + if (sd->status.weapon > 16) + { // 二刀流か? + int dmg = damage, dmg2 = damage2; + // 右手修練(60% 〜 100%) 右手全般 + skill = pc_checkskill (sd, AS_RIGHT); + damage = damage * (50 + (skill * 10)) / 100; + if (dmg > 0 && damage < 1) + damage = 1; + // 左手修練(40% 〜 80%) 左手全般 + skill = pc_checkskill (sd, AS_LEFT); + damage2 = damage2 * (30 + (skill * 10)) / 100; + if (dmg2 > 0 && damage2 < 1) + damage2 = 1; + } + else //二刀流でなければ左手ダメージは0 + damage2 = 0; + + // 右手,短剣のみ + if (da == 1) + { //ダブルアタックが発動しているか + div_ = 2; + damage += damage; + type = 0x08; + } + + if (sd->status.weapon == 16) + { + // カタール追撃ダメージ + skill = pc_checkskill (sd, TF_DOUBLE); + damage2 = damage * (1 + (skill * 2)) / 100; + if (damage > 0 && damage2 < 1) + damage2 = 1; + } + + // インベナム修正 + if (skill_num == TF_POISON) + { + damage = + battle_attr_fix (damage + 15 * skill_lv, s_ele, + battle_get_element (target)); + } + if (skill_num == MC_CARTREVOLUTION) + { + damage = battle_attr_fix (damage, 0, battle_get_element (target)); + } + + // 完全回避の判定 + if (skill_num == 0 && skill_lv >= 0 && tsd != NULL && div_ < 255 + && MRAND (1000) < battle_get_flee2 (target)) + { + damage = damage2 = 0; + type = 0x0b; + dmg_lv = ATK_LUCKY; + } + + // 対象が完全回避をする設定がONなら + if (battle_config.enemy_perfect_flee) + { + if (skill_num == 0 && skill_lv >= 0 && tmd != NULL && div_ < 255 + && MRAND (1000) < battle_get_flee2 (target)) + { + damage = damage2 = 0; + type = 0x0b; + dmg_lv = ATK_LUCKY; + } + } + + //MobのModeに頑強フラグが立っているときの処理 + if (t_mode & 0x40) + { + if (damage > 0) + damage = 1; + if (damage2 > 0) + damage2 = 1; + } + + //bNoWeaponDamage(設定アイテム無し?)でグランドクロスじゃない場合はダメージが0 + if (tsd && tsd->special_state.no_weapon_damage + && skill_num != CR_GRANDCROSS) + damage = damage2 = 0; + + if (skill_num != CR_GRANDCROSS && (damage > 0 || damage2 > 0)) + { + if (damage2 < 1) // ダメージ最終修正 + damage = + battle_calc_damage (src, target, damage, div_, skill_num, + skill_lv, flag); + else if (damage < 1) // 右手がミス? + damage2 = + battle_calc_damage (src, target, damage2, div_, skill_num, + skill_lv, flag); + else + { // 両 手/カタールの場合はちょっと計算ややこしい + int d1 = damage + damage2, d2 = damage2; + damage = + battle_calc_damage (src, target, damage + damage2, div_, + skill_num, skill_lv, flag); + damage2 = (d2 * 100 / d1) * damage / 100; + if (damage > 1 && damage2 < 1) + damage2 = 1; + damage -= damage2; + } + } + + /* For executioner card [Valaris] */ + if (src->type == BL_PC && sd->random_attack_increase_add > 0 + && sd->random_attack_increase_per > 0 && skill_num == 0) + { + if (MRAND (100) < sd->random_attack_increase_per) + { + if (damage > 0) + damage *= sd->random_attack_increase_add / 100; + if (damage2 > 0) + damage2 *= sd->random_attack_increase_add / 100; + } + } + /* End addition */ + +// -- moonsoul (final combination of phys, mag damage for ASC_BREAKER) + if (skill_num == ASC_BREAKER) + { + damage += damage3; + damage2 += damage3; + } + + wd.damage = damage; + wd.damage2 = damage2; + wd.type = type; + wd.div_ = div_; + wd.amotion = battle_get_amotion (src); + if (skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion = battle_get_dmotion (target); + wd.blewcount = blewcount; + wd.flag = flag; + wd.dmg_lv = dmg_lv; + + return wd; +} + +/*========================================== + * 武器ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_weapon_attack (struct block_list *src, + struct block_list *target, + int skill_num, int skill_lv, + int wflag) +{ + struct Damage wd; + + //return前の処理があるので情報出力部のみ変更 + if (src == NULL || target == NULL) + { + nullpo_info (NLP_MARK); + memset (&wd, 0, sizeof (wd)); + return wd; + } + + else if (src->type == BL_PC) + wd = battle_calc_pc_weapon_attack (src, target, skill_num, skill_lv, wflag); // weapon breaking [Valaris] + else if (src->type == BL_MOB) + wd = battle_calc_mob_weapon_attack (src, target, skill_num, skill_lv, + wflag); + else + memset (&wd, 0, sizeof (wd)); + + if (battle_config.equipment_breaking && src->type == BL_PC + && (wd.damage > 0 || wd.damage2 > 0)) + { + struct map_session_data *sd = (struct map_session_data *) src; + if (sd->status.weapon && sd->status.weapon != 11) + { + int breakrate = 1; + if (target->type == BL_PC && sd->sc_data[SC_MELTDOWN].timer != -1) + { + breakrate += 100 * sd->sc_data[SC_MELTDOWN].val1; + if (MRAND (10000) < + breakrate * battle_config.equipment_break_rate / 100 + || breakrate >= 10000) + pc_breakweapon ((struct map_session_data *) target); + } + if (sd->sc_data[SC_OVERTHRUST].timer != -1) + breakrate += 20 * sd->sc_data[SC_OVERTHRUST].val1; + if (wd.type == 0x0a) + breakrate *= 2; + if (MRAND (10000) < + breakrate * battle_config.equipment_break_rate / 100 + || breakrate >= 10000) + { + pc_breakweapon (sd); + memset (&wd, 0, sizeof (wd)); + } + } + } + + if (battle_config.equipment_breaking && target->type == BL_PC + && (wd.damage > 0 || wd.damage2 > 0)) + { + int breakrate = 1; + if (src->type == BL_PC + && ((struct map_session_data *) src)-> + sc_data[SC_MELTDOWN].timer != -1) + breakrate += + 70 * + ((struct map_session_data *) src)->sc_data[SC_MELTDOWN].val1; + if (wd.type == 0x0a) + breakrate *= 2; + if (MRAND (10000) < + breakrate * battle_config.equipment_break_rate / 100 + || breakrate >= 10000) + { + pc_breakarmor ((struct map_session_data *) target); + } + } + + return wd; +} + +/*========================================== + * 魔法ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_magic_attack (struct block_list *bl, + struct block_list *target, + int skill_num, int skill_lv, int flag) +{ + int mdef1 = battle_get_mdef (target); + int mdef2 = battle_get_mdef2 (target); + int matk1, matk2, damage = 0, div_ = 1, blewcount = + skill_get_blewcount (skill_num, skill_lv), rdamage = 0; + struct Damage md; + int aflag; + int normalmagic_flag = 1; + int ele = 0, race = 7, t_ele = 0, t_race = 7, t_mode = + 0, cardfix, t_class, i; + struct map_session_data *sd = NULL, *tsd = NULL; + struct mob_data *tmd = NULL; + + //return前の処理があるので情報出力部のみ変更 + if (bl == NULL || target == NULL) + { + nullpo_info (NLP_MARK); + memset (&md, 0, sizeof (md)); + return md; + } + + matk1 = battle_get_matk1 (bl); + matk2 = battle_get_matk2 (bl); + ele = skill_get_pl (skill_num); + race = battle_get_race (bl); + t_ele = battle_get_elem_type (target); + t_race = battle_get_race (target); + t_mode = battle_get_mode (target); + +#define MATK_FIX( a,b ) { matk1=matk1*(a)/(b); matk2=matk2*(a)/(b); } + + if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) + { + sd->state.attack_type = BF_MAGIC; + if (sd->matk_rate != 100) + MATK_FIX (sd->matk_rate, 100); + sd->state.arrow_atk = 0; + } + if (target->type == BL_PC) + tsd = (struct map_session_data *) target; + else if (target->type == BL_MOB) + tmd = (struct mob_data *) target; + + aflag = BF_MAGIC | BF_LONG | BF_SKILL; + + if (skill_num > 0) + { + switch (skill_num) + { // 基本ダメージ計算(スキルごとに処理) + // ヒールor聖体 + case AL_HEAL: + case PR_BENEDICTIO: + damage = skill_calc_heal (bl, skill_lv) / 2; + normalmagic_flag = 0; + break; + case PR_ASPERSIO: /* アスペルシオ */ + damage = 40; //固定ダメージ + normalmagic_flag = 0; + break; + case PR_SANCTUARY: // サンクチュアリ + damage = (skill_lv > 6) ? 388 : skill_lv * 50; + normalmagic_flag = 0; + blewcount |= 0x10000; + break; + case ALL_RESURRECTION: + case PR_TURNUNDEAD: // 攻撃リザレクションとターンアンデッド + if (target->type != BL_PC + && battle_check_undead (t_race, t_ele)) + { + int hp, mhp, thres; + hp = battle_get_hp (target); + mhp = battle_get_max_hp (target); + thres = (skill_lv * 20) + battle_get_luk (bl) + + battle_get_int (bl) + battle_get_lv (bl) + + ((200 - hp * 200 / mhp)); + if (thres > 700) + thres = 700; +// if(battle_config.battle_log) +// printf("ターンアンデッド! 確率%d ‰(千分率)\n", thres); + if (MRAND (1000) < thres && !(t_mode & 0x20)) // 成功 + damage = hp; + else // 失敗 + damage = + battle_get_lv (bl) + battle_get_int (bl) + + skill_lv * 10; + } + normalmagic_flag = 0; + break; + + case MG_NAPALMBEAT: // ナパームビート(分散計算込み) + MATK_FIX (70 + skill_lv * 10, 100); + if (flag > 0) + { + MATK_FIX (1, flag); + } + else + { + if (battle_config.error_log) + printf + ("battle_calc_magic_attack(): napam enemy count=0 !\n"); + } + break; + case MG_FIREBALL: // ファイヤーボール + { + const int drate[] = { 100, 90, 70 }; + if (flag > 2) + matk1 = matk2 = 0; + else + MATK_FIX ((95 + skill_lv * 5) * drate[flag], 10000); + } + break; + case MG_FIREWALL: // ファイヤーウォール +/* + if( (t_ele!=3 && !battle_check_undead(t_race,t_ele)) || target->type==BL_PC ) //PCは火属性でも飛ぶ?そもそもダメージ受ける? + blewcount |= 0x10000; + else + blewcount = 0; +*/ + if ((t_ele == 3 || battle_check_undead (t_race, t_ele)) + && target->type != BL_PC) + blewcount = 0; + else + blewcount |= 0x10000; + MATK_FIX (1, 2); + break; + case MG_THUNDERSTORM: // サンダーストーム + MATK_FIX (80, 100); + break; + case MG_FROSTDIVER: // フロストダイバ + MATK_FIX (100 + skill_lv * 10, 100); + break; + case WZ_FROSTNOVA: // フロストダイバ + MATK_FIX (((100 + skill_lv * 10) * (2 / 3)), 100); + break; + case WZ_FIREPILLAR: // ファイヤーピラー + if (mdef1 < 1000000) + mdef1 = mdef2 = 0; // MDEF無視 + MATK_FIX (1, 5); + matk1 += 50; + matk2 += 50; + break; + case WZ_SIGHTRASHER: + MATK_FIX (100 + skill_lv * 20, 100); + break; + case WZ_METEOR: + case WZ_JUPITEL: // ユピテルサンダー + break; + case WZ_VERMILION: // ロードオブバーミリオン + MATK_FIX (skill_lv * 20 + 80, 100); + break; + case WZ_WATERBALL: // ウォーターボール + matk1 += skill_lv * 30; + matk2 += skill_lv * 30; + break; + case WZ_STORMGUST: // ストームガスト + MATK_FIX (skill_lv * 40 + 100, 100); + blewcount |= 0x10000; + break; + case AL_HOLYLIGHT: // ホーリーライト + MATK_FIX (125, 100); + break; + case AL_RUWACH: + MATK_FIX (145, 100); + break; + case HW_NAPALMVULCAN: // ナパームビート(分散計算込み) + MATK_FIX (70 + skill_lv * 10, 100); + if (flag > 0) + { + MATK_FIX (1, flag); + } + else + { + if (battle_config.error_log) + printf + ("battle_calc_magic_attack(): napalmvulcan enemy count=0 !\n"); + } + break; + } + } + + if (normalmagic_flag) + { // 一般魔法ダメージ計算 + int imdef_flag = 0; + if (matk1 > matk2) + damage = matk2 + MRAND ((matk1 - matk2 + 1)); + else + damage = matk2; + if (sd) + { + if (sd->ignore_mdef_ele & (1 << t_ele) + || sd->ignore_mdef_race & (1 << t_race)) + imdef_flag = 1; + if (t_mode & 0x20) + { + if (sd->ignore_mdef_race & (1 << 10)) + imdef_flag = 1; + } + else + { + if (sd->ignore_mdef_race & (1 << 11)) + imdef_flag = 1; + } + } + if (!imdef_flag) + { + if (battle_config.magic_defense_type) + { + damage = + damage - (mdef1 * battle_config.magic_defense_type) - + mdef2; + } + else + { + damage = (damage * (100 - mdef1)) / 100 - mdef2; + } + } + + if (damage < 1) + damage = 1; + } + + if (sd) + { + cardfix = 100; + cardfix = cardfix * (100 + sd->magic_addrace[t_race]) / 100; + cardfix = cardfix * (100 + sd->magic_addele[t_ele]) / 100; + if (t_mode & 0x20) + cardfix = cardfix * (100 + sd->magic_addrace[10]) / 100; + else + cardfix = cardfix * (100 + sd->magic_addrace[11]) / 100; + t_class = battle_get_class (target); + for (i = 0; i < sd->add_magic_damage_class_count; i++) + { + if (sd->add_magic_damage_classid[i] == t_class) + { + cardfix = + cardfix * (100 + sd->add_magic_damage_classrate[i]) / 100; + break; + } + } + damage = damage * cardfix / 100; + } + + if (tsd) + { + int s_class = battle_get_class (bl); + cardfix = 100; + cardfix = cardfix * (100 - tsd->subele[ele]) / 100; // 属 性によるダメージ耐性 + cardfix = cardfix * (100 - tsd->subrace[race]) / 100; // 種族によるダメージ耐性 + cardfix = cardfix * (100 - tsd->magic_subrace[race]) / 100; + if (battle_get_mode (bl) & 0x20) + cardfix = cardfix * (100 - tsd->magic_subrace[10]) / 100; + else + cardfix = cardfix * (100 - tsd->magic_subrace[11]) / 100; + for (i = 0; i < tsd->add_mdef_class_count; i++) + { + if (tsd->add_mdef_classid[i] == s_class) + { + cardfix = cardfix * (100 - tsd->add_mdef_classrate[i]) / 100; + break; + } + } + cardfix = cardfix * (100 - tsd->magic_def_rate) / 100; + damage = damage * cardfix / 100; + } + if (damage < 0) + damage = 0; + + damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属 性修正 + + if (skill_num == CR_GRANDCROSS) + { // グランドクロス + struct Damage wd; + wd = battle_calc_weapon_attack (bl, target, skill_num, skill_lv, + flag); + damage = (damage + wd.damage) * (100 + 40 * skill_lv) / 100; + if (battle_config.gx_dupele) + damage = battle_attr_fix (damage, ele, battle_get_element (target)); //属性2回かかる + if (bl == target) + damage = damage / 2; //反動は半分 + } + + div_ = skill_get_num (skill_num, skill_lv); + + if (div_ > 1 && skill_num != WZ_VERMILION) + damage *= div_; + +// if(mdef1 >= 1000000 && damage > 0) + if (t_mode & 0x40 && damage > 0) + damage = 1; + + if (tsd && tsd->special_state.no_magic_damage) + { + if (battle_config.gtb_pvp_only != 0) + { // [MouseJstr] + if ((map[target->m].flag.pvp || map[target->m].flag.gvg) + && target->type == BL_PC) + damage = (damage * (100 - battle_config.gtb_pvp_only)) / 100; + } + else + damage = 0; // 黄 金蟲カード(魔法ダメージ0) + } + + damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 + + /* magic_damage_return by [AppleGirl] and [Valaris] */ + if (target->type == BL_PC && tsd && tsd->magic_damage_return > 0) + { + rdamage += damage * tsd->magic_damage_return / 100; + if (rdamage < 1) + rdamage = 1; + clif_damage (target, bl, gettick (), 0, 0, rdamage, 0, 0, 0); + battle_damage (target, bl, rdamage, 0); + } + /* end magic_damage_return */ + + md.damage = damage; + md.div_ = div_; + md.amotion = battle_get_amotion (bl); + md.dmotion = battle_get_dmotion (target); + md.damage2 = 0; + md.type = 0; + md.blewcount = blewcount; + md.flag = aflag; + + return md; +} + +/*========================================== + * その他ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_misc_attack (struct block_list *bl, + struct block_list *target, + int skill_num, int skill_lv, int flag) +{ + int int_ = battle_get_int (bl); +// int luk=battle_get_luk(bl); + int dex = battle_get_dex (bl); + int skill, ele, race, cardfix; + 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_LONG | BF_SKILL; + + //return前の処理があるので情報出力部のみ変更 + if (bl == NULL || target == NULL) + { + nullpo_info (NLP_MARK); + memset (&md, 0, sizeof (md)); + return md; + } + + if (bl->type == BL_PC && (sd = (struct map_session_data *) bl)) + { + sd->state.attack_type = BF_MISC; + sd->state.arrow_atk = 0; + } + + if (target->type == BL_PC) + tsd = (struct map_session_data *) target; + + 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; + break; + + case TF_THROWSTONE: // 石投げ + damage = 30; + damagefix = 0; + break; + + case BA_DISSONANCE: // 不協和音 + damage = + (skill_lv) * 20 + pc_checkskill (sd, BA_MUSICALLESSON) * 3; + break; + + case NPC_SELFDESTRUCTION: // 自爆 + damage = battle_get_hp (bl) - (bl == target ? 1 : 0); + damagefix = 0; + break; + + case NPC_SMOKING: // タバコを吸う + damage = 3; + damagefix = 0; + break; + + case NPC_DARKBREATH: + { + struct status_change *sc_data = battle_get_sc_data (target); + int hitrate = + battle_get_hit (bl) - battle_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 (MRAND (100) < hitrate) + { + damage = 500 + (skill_lv - 1) * 1000 + MRAND (1000); + if (damage > 9999) + damage = 9999; + } + } + break; + case SN_FALCONASSAULT: /* ファルコンアサルト */ + skill = pc_checkskill (sd, HT_BLITZBEAT); + damage = + (100 + 50 * skill_lv + + (dex / 10 + int_ / 2 + skill * 3 + 40) * 2); + break; + } + + ele = skill_get_pl (skill_num); + race = battle_get_race (bl); + + 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->subrace[race]) / 100; // 種族によるダメージ耐性 + cardfix = cardfix * (100 - tsd->misc_def_rate) / 100; + damage = damage * cardfix / 100; + } + if (damage < 0) + damage = 0; + damage = battle_attr_fix (damage, ele, battle_get_element (target)); // 属性修正 + } + + div_ = skill_get_num (skill_num, skill_lv); + if (div_ > 1) + damage *= div_; + + if (damage > 0 + && (damage < div_ + || (battle_get_def (target) >= 1000000 + && battle_get_mdef (target) >= 1000000))) + { + damage = div_; + } + + damage = battle_calc_damage (bl, target, damage, div_, skill_num, skill_lv, aflag); // 最終修正 + + md.damage = damage; + md.div_ = div_; + md.amotion = battle_get_amotion (bl); + md.dmotion = battle_get_dmotion (target); + md.damage2 = 0; + md.type = 0; + 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; + memset (&d, 0, sizeof (d)); + + switch (attack_type) + { + case BF_WEAPON: + return battle_calc_weapon_attack (bl, target, skill_num, skill_lv, + flag); + case BF_MAGIC: + return battle_calc_magic_attack (bl, target, skill_num, skill_lv, + flag); + case BF_MISC: + return battle_calc_misc_attack (bl, target, skill_num, skill_lv, + flag); + default: + if (battle_config.error_log) + printf ("battle_calc_attack: unknwon attack type ! %d\n", + attack_type); + break; + } + return d; +} + +/*========================================== + * 通常攻撃処理まとめ + *------------------------------------------ + */ +int battle_weapon_attack (struct block_list *src, struct block_list *target, + unsigned int tick, int flag) +{ + struct map_session_data *sd = NULL; + struct status_change *sc_data = battle_get_sc_data (src), *t_sc_data = + battle_get_sc_data (target); + short *opt1; + int race = 7, ele = 0; + int damage, rdamage = 0; + struct Damage wd; + + nullpo_retr (0, src); + nullpo_retr (0, target); + + if (src->type == BL_PC) + sd = (struct map_session_data *) src; + + if (src->prev == NULL || target->prev == NULL) + return 0; + if (src->type == BL_PC && pc_isdead (sd)) + return 0; + if (target->type == BL_PC + && pc_isdead ((struct map_session_data *) target)) + return 0; + + opt1 = battle_get_opt1 (src); + if (opt1 && *opt1 > 0) + { + battle_stopattack (src); + return 0; + } + if (sc_data && sc_data[SC_BLADESTOP].timer != -1) + { + battle_stopattack (src); + return 0; + } + + race = battle_get_race (target); + ele = battle_get_elem_type (target); + if (battle_check_target (src, target, BCT_ENEMY) > 0 && + battle_check_range (src, target, 0)) + { + // 攻撃対象となりうるので攻撃 + 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; + } + } + if (flag & 0x8000) + { + if (sd && battle_config.pc_attack_direction_change) + sd->dir = sd->head_dir = + map_calc_dir (src, target->x, target->y); + else if (src->type == BL_MOB + && battle_config.monster_attack_direction_change) + ((struct mob_data *) src)->dir = + map_calc_dir (src, target->x, target->y); + wd = battle_calc_weapon_attack (src, target, KN_AUTOCOUNTER, + flag & 0xff, 0); + } + else + wd = battle_calc_weapon_attack (src, target, 0, 0, 0); + + // significantly increase injuries for hasted characters + if (wd.damage > 0 && (t_sc_data[SC_HASTE].timer != -1)) + { + wd.damage = (wd.damage * (16 + t_sc_data[SC_HASTE].val1)) >> 4; + } + + if (wd.damage > 0 + && t_sc_data[SC_PHYS_SHIELD].timer != -1 && target->type == BL_PC) + { + int reduction = t_sc_data[SC_PHYS_SHIELD].val1; + if (reduction > wd.damage) + reduction = wd.damage; + + wd.damage -= reduction; + MAP_LOG_PC (((struct map_session_data *) target), + "MAGIC-ABSORB-DMG %d", reduction); + } + + if ((damage = wd.damage + wd.damage2) > 0 && src != target) + { + if (wd.flag & BF_SHORT) + { + if (target->type == BL_PC) + { + struct map_session_data *tsd = + (struct map_session_data *) target; + if (tsd && tsd->short_weapon_damage_return > 0) + { + rdamage += + damage * tsd->short_weapon_damage_return / 100; + if (rdamage < 1) + rdamage = 1; + } + } + if (t_sc_data && t_sc_data[SC_REFLECTSHIELD].timer != -1) + { + rdamage += + damage * t_sc_data[SC_REFLECTSHIELD].val2 / 100; + if (rdamage < 1) + rdamage = 1; + } + } + else if (wd.flag & BF_LONG) + { + if (target->type == BL_PC) + { + struct map_session_data *tsd = + (struct map_session_data *) target; + if (tsd && tsd->long_weapon_damage_return > 0) + { + rdamage += + damage * tsd->long_weapon_damage_return / 100; + if (rdamage < 1) + rdamage = 1; + } + } + } + + if (rdamage > 0) + clif_damage (src, src, tick, wd.amotion, 0, rdamage, 1, 4, 0); + } + + if (wd.div_ == 255 && sd) + { //三段掌 + int delay = + 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); + int skilllv; + if (wd.damage + wd.damage2 < battle_get_hp (target)) + { + if ((skilllv = pc_checkskill (sd, MO_CHAINCOMBO)) > 0) + delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 + + skill_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); + clif_skill_damage (src, target, tick, wd.amotion, wd.dmotion, + wd.damage, 3, MO_TRIPLEATTACK, + pc_checkskill (sd, MO_TRIPLEATTACK), -1); + } + else + { + 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 (); + + if (src->type == BL_PC) + { + int weapon_index = sd->equip_index[9]; + int weapon = 0; + if (sd->inventory_data[weapon_index] + && sd->status.inventory[weapon_index].equip & 0x2) + weapon = sd->inventory_data[weapon_index]->nameid; + + MAP_LOG ("PC%d %d:%d,%d WPNDMG %s%d %d FOR %d WPN %d", + sd->status.char_id, src->m, src->x, src->y, + (target->type == BL_PC) ? "PC" : "MOB", + (target->type == + BL_PC) ? ((struct map_session_data *) target)-> + status.char_id : target->id, + (target->type == + BL_PC) ? 0 : ((struct mob_data *) target)->mob_class, + wd.damage + wd.damage2, weapon); + } + + if (target->type == BL_PC) + { + struct map_session_data *sd2 = (struct map_session_data *) target; + MAP_LOG ("PC%d %d:%d,%d WPNINJURY %s%d %d FOR %d", + sd2->status.char_id, target->m, target->x, target->y, + (src->type == BL_PC) ? "PC" : "MOB", + (src->type == + BL_PC) ? ((struct map_session_data *) src)-> + status.char_id : src->id, + (src->type == + BL_PC) ? 0 : ((struct mob_data *) src)->mob_class, + wd.damage + wd.damage2); + } + + battle_damage (src, target, (wd.damage + wd.damage2), 0); + if (target->prev != NULL && + (target->type != BL_PC + || (target->type == BL_PC + && !pc_isdead ((struct map_session_data *) target)))) + { + if (wd.damage > 0 || wd.damage2 > 0) + { + skill_additional_effect (src, target, 0, 0, BF_WEAPON, tick); + if (sd) + { + if (sd->weapon_coma_ele[ele] > 0 + && MRAND (10000) < sd->weapon_coma_ele[ele]) + battle_damage (src, target, + battle_get_max_hp (target), 1); + if (sd->weapon_coma_race[race] > 0 + && MRAND (10000) < sd->weapon_coma_race[race]) + battle_damage (src, target, + battle_get_max_hp (target), 1); + if (battle_get_mode (target) & 0x20) + { + if (sd->weapon_coma_race[10] > 0 + && MRAND (10000) < sd->weapon_coma_race[10]) + battle_damage (src, target, + battle_get_max_hp (target), 1); + } + else + { + if (sd->weapon_coma_race[11] > 0 + && MRAND (10000) < sd->weapon_coma_race[11]) + battle_damage (src, target, + battle_get_max_hp (target), 1); + } + } + } + } + if (sc_data && sc_data[SC_AUTOSPELL].timer != -1 + && MRAND (100) < sc_data[SC_AUTOSPELL].val4) + { + int skilllv = sc_data[SC_AUTOSPELL].val3, i, f = 0; + i = MRAND (100); + if (i >= 50) + skilllv -= 2; + else if (i >= 15) + skilllv--; + if (skilllv < 1) + skilllv = 1; + if (sd) + { + int sp = skill_get_sp (sc_data[SC_AUTOSPELL].val2, + skilllv) * 2 / 3; + if (sd->status.sp >= sp) + { + if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) + || i == 32) + f = skill_castend_pos2 (src, target->x, target->y, + sc_data[SC_AUTOSPELL].val2, + skilllv, tick, flag); + else + { + switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) + { + case 0: + case 2: + f = skill_castend_damage_id (src, target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, tick, + flag); + break; + case 1: /* 支援系 */ + if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL + || (sc_data[SC_AUTOSPELL].val2 == + ALL_RESURRECTION + && target->type != BL_PC)) + && battle_check_undead (race, ele)) + f = skill_castend_damage_id (src, target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, + tick, flag); + else + f = skill_castend_nodamage_id (src, + target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, + tick, + flag); + break; + } + } + if (!f) + pc_heal (sd, 0, -sp); + } + } + else + { + if ((i = skill_get_inf (sc_data[SC_AUTOSPELL].val2) == 2) + || i == 32) + skill_castend_pos2 (src, target->x, target->y, + sc_data[SC_AUTOSPELL].val2, skilllv, + tick, flag); + else + { + switch (skill_get_nk (sc_data[SC_AUTOSPELL].val2)) + { + case 0: + case 2: + skill_castend_damage_id (src, target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, tick, flag); + break; + case 1: /* 支援系 */ + if ((sc_data[SC_AUTOSPELL].val2 == AL_HEAL + || (sc_data[SC_AUTOSPELL].val2 == + ALL_RESURRECTION + && target->type != BL_PC)) + && battle_check_undead (race, ele)) + skill_castend_damage_id (src, target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, tick, flag); + else + skill_castend_nodamage_id (src, target, + sc_data + [SC_AUTOSPELL].val2, + skilllv, tick, + flag); + break; + } + } + } + } + if (sd) + { + if (sd->autospell_id > 0 && sd->autospell_lv > 0 + && MRAND (100) < sd->autospell_rate) + { + int skilllv = sd->autospell_lv, i, f = 0, sp; + i = MRAND (100); + if (i >= 50) + skilllv -= 2; + else if (i >= 15) + skilllv--; + if (skilllv < 1) + skilllv = 1; + sp = skill_get_sp (sd->autospell_id, skilllv) * 2 / 3; + if (sd->status.sp >= sp) + { + if ((i = skill_get_inf (sd->autospell_id) == 2) + || i == 32) + f = skill_castend_pos2 (src, target->x, target->y, + sd->autospell_id, skilllv, + tick, flag); + else + { + switch (skill_get_nk (sd->autospell_id)) + { + case 0: + case 2: + f = skill_castend_damage_id (src, target, + sd->autospell_id, + skilllv, tick, + flag); + break; + case 1: /* 支援系 */ + if ((sd->autospell_id == AL_HEAL + || (sd->autospell_id == ALL_RESURRECTION + && target->type != BL_PC)) + && battle_check_undead (race, ele)) + f = skill_castend_damage_id (src, target, + sd->autospell_id, + skilllv, + tick, flag); + else + f = skill_castend_nodamage_id (src, + target, + sd->autospell_id, + skilllv, + tick, + flag); + break; + } + } + if (!f) + pc_heal (sd, 0, -sp); + } + } + if (wd.flag & BF_WEAPON && src != target + && (wd.damage > 0 || wd.damage2 > 0)) + { + int hp = 0, sp = 0; + if (sd->hp_drain_rate && sd->hp_drain_per > 0 && wd.damage > 0 + && MRAND (100) < sd->hp_drain_rate) + { + hp += (wd.damage * sd->hp_drain_per) / 100; + if (sd->hp_drain_rate > 0 && hp < 1) + hp = 1; + else if (sd->hp_drain_rate < 0 && hp > -1) + hp = -1; + } + if (sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 + && wd.damage2 > 0 && MRAND (100) < sd->hp_drain_rate_) + { + hp += (wd.damage2 * sd->hp_drain_per_) / 100; + if (sd->hp_drain_rate_ > 0 && hp < 1) + hp = 1; + else if (sd->hp_drain_rate_ < 0 && hp > -1) + hp = -1; + } + if (sd->sp_drain_rate && sd->sp_drain_per > 0 && wd.damage > 0 + && MRAND (100) < sd->sp_drain_rate) + { + sp += (wd.damage * sd->sp_drain_per) / 100; + if (sd->sp_drain_rate > 0 && sp < 1) + sp = 1; + else if (sd->sp_drain_rate < 0 && sp > -1) + sp = -1; + } + if (sd->sp_drain_rate_ && sd->sp_drain_per_ > 0 + && wd.damage2 > 0 && MRAND (100) < sd->sp_drain_rate_) + { + sp += (wd.damage2 * sd->sp_drain_per_) / 100; + if (sd->sp_drain_rate_ > 0 && sp < 1) + sp = 1; + else if (sd->sp_drain_rate_ < 0 && sp > -1) + sp = -1; + } + if (hp || sp) + pc_heal (sd, hp, sp); + } + } + + if (rdamage > 0) + battle_damage (target, src, rdamage, 0); + if (t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1 + && t_sc_data[SC_AUTOCOUNTER].val4 > 0) + { + if (t_sc_data[SC_AUTOCOUNTER].val3 == src->id) + battle_weapon_attack (target, src, tick, + 0x8000 | + t_sc_data[SC_AUTOCOUNTER].val1); + skill_status_change_end (target, SC_AUTOCOUNTER, -1); + } + if (t_sc_data && t_sc_data[SC_BLADESTOP_WAIT].timer != -1) + { + int lv = t_sc_data[SC_BLADESTOP_WAIT].val1; + skill_status_change_end (target, SC_BLADESTOP_WAIT, -1); + skill_status_change_start (src, SC_BLADESTOP, lv, 1, (int) src, + (int) target, + skill_get_time2 (MO_BLADESTOP, lv), 0); + skill_status_change_start (target, SC_BLADESTOP, lv, 2, + (int) target, (int) src, + skill_get_time2 (MO_BLADESTOP, lv), 0); + } + if (t_sc_data && t_sc_data[SC_SPLASHER].timer != -1) //殴ったので対象のベナムスプラッシャー状態を解除 + skill_status_change_end (target, SC_SPLASHER, -1); + + 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; +} + +/*========================================== + * 敵味方判定(1=肯定,0=否定,-1=エラー) + * flag&0xf0000 = 0x00000:敵じゃないか判定(ret:1=敵ではない) + * = 0x10000:パーティー判定(ret:1=パーティーメンバ) + * = 0x20000:全て(ret:1=敵味方両方) + * = 0x40000:敵か判定(ret:1=敵) + * = 0x50000:パーティーじゃないか判定(ret:1=パーティでない) + *------------------------------------------ + */ +int battle_check_target (struct block_list *src, struct block_list *target, + int flag) +{ + int s_p, s_g, t_p, t_g; + struct block_list *ss = src; + + nullpo_retr (0, src); + nullpo_retr (0, target); + + if (flag & 0x40000) + { // 反転フラグ + int ret = battle_check_target (src, target, flag & 0x30000); + if (ret != -1) + return !ret; + return -1; + } + + if (flag & 0x20000) + { + if (target->type == BL_MOB || target->type == BL_PC) + return 1; + else + return -1; + } + + if (src->type == BL_SKILL && target->type == BL_SKILL) // 対象がスキルユニットなら無条件肯定 + return -1; + + if (target->type == BL_PC + && ((struct map_session_data *) target)->invincible_timer != -1) + return -1; + + if (target->type == BL_SKILL) + { + switch (((struct skill_unit *) target)->group->unit_id) + { + case 0x8d: + case 0x8f: + case 0x98: + return 0; + break; + } + } + + // スキルユニットの場合、親を求める + if (src->type == BL_SKILL) + { + int inf2 = + skill_get_inf2 (((struct skill_unit *) src)->group->skill_id); + if ((ss = + map_id2bl (((struct skill_unit *) src)->group->src_id)) == NULL) + return -1; + if (ss->prev == NULL) + return -1; + if (inf2 & 0x80 && (map[src->m].flag.pvp || pc_iskiller ((struct map_session_data *) src, (struct map_session_data *) target)) && // [MouseJstr] + !(target->type == BL_PC + && pc_isinvisible ((struct map_session_data *) target))) + return 0; + if (ss == target) + { + if (inf2 & 0x100) + return 0; + if (inf2 & 0x200) + return -1; + } + } + // Mobでmaster_idがあってspecial_mob_aiなら、召喚主を求める + if (src->type == BL_MOB) + { + struct mob_data *md = (struct mob_data *) src; + if (md && md->master_id > 0) + { + if (md->master_id == target->id) // 主なら肯定 + return 1; + if (md->state.special_mob_ai) + { + if (target->type == BL_MOB) + { //special_mob_aiで対象がMob + struct mob_data *tmd = (struct mob_data *) target; + if (tmd) + { + if (tmd->master_id != md->master_id) //召喚主が一緒でなければ否定 + return 0; + else + { //召喚主が一緒なので肯定したいけど自爆は否定 + if (md->state.special_mob_ai > 2) + return 0; + else + return 1; + } + } + } + } + if ((ss = map_id2bl (md->master_id)) == NULL) + return -1; + } + } + + if (src == target || ss == target) // 同じなら肯定 + return 1; + + if (target->type == BL_PC + && pc_isinvisible ((struct map_session_data *) target)) + return -1; + + if (src->prev == NULL || // 死んでるならエラー + (src->type == BL_PC && pc_isdead ((struct map_session_data *) src))) + return -1; + + if ((ss->type == BL_PC && target->type == BL_MOB) || + (ss->type == BL_MOB && target->type == BL_PC)) + return 0; // PCvsMOBなら否定 + + s_p = battle_get_party_id (ss); + s_g = battle_get_guild_id (ss); + + t_p = battle_get_party_id (target); + t_g = battle_get_guild_id (target); + + if (flag & 0x10000) + { + if (s_p && t_p && s_p == t_p) // 同じパーティなら肯定(味方) + return 1; + else // パーティ検索なら同じパーティじゃない時点で否定 + return 0; + } + + if (ss->type == BL_MOB && s_g > 0 && t_g > 0 && s_g == t_g) // 同じギルド/mobクラスなら肯定(味方) + return 1; + +//printf("ss:%d src:%d target:%d flag:0x%x %d %d ",ss->id,src->id,target->id,flag,src->type,target->type); +//printf("p:%d %d g:%d %d\n",s_p,t_p,s_g,t_g); + + if (ss->type == BL_PC && target->type == BL_PC) + { // 両方PVPモードなら否定(敵) + struct skill_unit *su = NULL; + if (src->type == BL_SKILL) + su = (struct skill_unit *) src; + if (map[ss->m].flag.pvp + || pc_iskiller ((struct map_session_data *) ss, + (struct map_session_data *) target)) + { // [MouseJstr] + if (su && su->group->target_flag == BCT_NOENEMY) + return 1; + else if (battle_config.pk_mode + && (((struct map_session_data *) ss)->status.pc_class == 0 + || ((struct map_session_data *) target)-> + status.pc_class == 0)) + return 1; // prevent novice engagement in pk_mode [Valaris] + else if (map[ss->m].flag.pvp_noparty && s_p > 0 && t_p > 0 + && s_p == t_p) + return 1; + else if (map[ss->m].flag.pvp_noguild && s_g > 0 && t_g > 0 + && s_g == t_g) + return 1; + return 0; + } + if (map[src->m].flag.gvg) + { + struct guild *g = NULL; + if (su && su->group->target_flag == BCT_NOENEMY) + return 1; + if (s_g > 0 && s_g == t_g) + return 1; + if (map[src->m].flag.gvg_noparty && s_p > 0 && t_p > 0 + && s_p == t_p) + return 1; + if ((g = guild_search (s_g))) + { + int i; + for (i = 0; i < MAX_GUILDALLIANCE; i++) + { + if (g->alliance[i].guild_id > 0 + && g->alliance[i].guild_id == t_g) + { + if (g->alliance[i].opposition) + return 0; //敵対ギルドなら無条件に敵 + else + return 1; //同盟ギルドなら無条件に味方 + } + } + } + return 0; + } + } + + return 1; // 該当しないので無関係人物(まあ敵じゃないので味方) +} + +/*========================================== + * 射程判定 + *------------------------------------------ + */ +int battle_check_range (struct block_list *src, struct block_list *bl, + int range) +{ + + int dx, dy; + struct walkpath_data wpd; + int arange; + + nullpo_retr (0, src); + nullpo_retr (0, bl); + + dx = abs (bl->x - src->x); + dy = abs (bl->y - src->y); + arange = ((dx > dy) ? dx : dy); + + if (src->m != bl->m) // 違うマップ + return 0; + + if (range > 0 && range < arange) // 遠すぎる + return 0; + + if (arange < 2) // 同じマスか隣接 + return 1; + +// if(bl->type == BL_SKILL && ((struct skill_unit *)bl)->group->unit_id == 0x8d) +// return 1; + + // 障害物判定 + wpd.path_len = 0; + wpd.path_pos = 0; + wpd.path_half = 0; + if (path_search (&wpd, src->m, src->x, src->y, bl->x, bl->y, 0x10001) != + -1) + return 1; + + dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); + dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); + return (path_search (&wpd, src->m, src->x + dx, src->y + dy, + bl->x - dx, bl->y - dy, 0x10001) != -1) ? 1 : 0; +} + +/*========================================== + * Return numerical value of a switch configuration (modified by [Yor]) + * on/off, english, fran軋is, deutsch, espal + *------------------------------------------ + */ +int battle_config_switch (const char *str) +{ + if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0 + || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0 + || strcasecmp (str, "si") == 0) + return 1; + if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0 + || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0) + return 0; + return atoi (str); +} + +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +int battle_config_read (const char *cfgName) +{ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + static int count = 0; + + if ((count++) == 0) + { + battle_config.warp_point_debug = 0; + battle_config.enemy_critical = 0; + battle_config.enemy_critical_rate = 100; + 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.pc_damage_delay = 1; + battle_config.pc_damage_delay_rate = 100; + battle_config.defnotenemy = 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.drop_pickup_safety_zone = 20; + 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_item_rate = 100; + 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.gm_allskill = 0; + battle_config.gm_allequip = 0; + battle_config.gm_skilluncond = 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_count_rate = 100; + 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.pc_invincible_time = 5000; + battle_config.skill_min_damage = 0; + 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.wedding_modifydisplay = 0; + 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.itemheal_regeneration_factor = 1; + battle_config.item_name_override_grffile = 1; + battle_config.arrow_decrement = 1; + battle_config.max_aspd = 199; + battle_config.max_hp = 32500; + battle_config.max_sp = 32500; + battle_config.max_lv = 99; // [MouseJstr] + battle_config.max_parameter = 99; + battle_config.max_cart_weight = 8000; + 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.agi_penaly_type = 0; + battle_config.agi_penaly_count = 3; + battle_config.agi_penaly_num = 0; + battle_config.agi_penaly_count_lv = ATK_FLEE; + battle_config.vit_penaly_type = 0; + battle_config.vit_penaly_count = 3; + battle_config.vit_penaly_num = 0; + battle_config.vit_penaly_count_lv = ATK_DEF; + battle_config.player_defense_type = 0; + battle_config.monster_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 = 0; + battle_config.monster_cloak_check_type = 0; + battle_config.gvg_short_damage_rate = 100; + battle_config.gvg_long_damage_rate = 100; + battle_config.gvg_magic_damage_rate = 100; + battle_config.gvg_misc_damage_rate = 100; + 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_undead_nofreeze = 0; + battle_config.pc_land_skill_limit = 1; + battle_config.monster_land_skill_limit = 1; + battle_config.party_skill_penaly = 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.display_delay_skill_fail = 1; + battle_config.chat_warpportal = 0; + battle_config.mob_warpportal = 0; + battle_config.dead_branch_active = 0; + battle_config.show_steal_in_same_party = 0; + battle_config.enable_upper_class = 0; + battle_config.pc_attack_attr_none = 0; + battle_config.mob_attack_attr_none = 1; + battle_config.mob_ghostring_fix = 0; + battle_config.gx_allhit = 0; + battle_config.gx_cardfix = 0; + battle_config.gx_dupele = 1; + battle_config.gx_disptype = 1; + battle_config.player_skill_partner_check = 1; + battle_config.hide_GM_session = 0; + battle_config.unit_movement_type = 0; + battle_config.invite_request_check = 1; + battle_config.skill_removetrap_type = 0; + battle_config.disp_experience = 0; + 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_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.prevent_logout = 1; // Added by RoVeRT + battle_config.maximum_level = 255; // Added by Valaris + battle_config.drops_by_luk = 0; // [Valaris] + battle_config.equipment_breaking = 0; // [Valaris] + battle_config.equipment_break_rate = 100; // [Valaris] + battle_config.pk_mode = 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.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 = 63; // added by [Yor] + battle_config.min_hair_style = 0; + battle_config.max_hair_style = 20; + 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.castrate_dex_scale = 150; + + battle_config.area_size = 14; + + battle_config.chat_lame_penalty = 2; + battle_config.chat_spam_threshold = 10; + battle_config.chat_spam_flood = 10; + battle_config.chat_spam_ban = 1; + battle_config.chat_spam_warn = 8; + battle_config.chat_maxline = 255; + + battle_config.packet_spam_threshold = 2; + battle_config.packet_spam_flood = 30; + battle_config.packet_spam_kick = 1; + + battle_config.mask_ip_gms = 1; + + battle_config.mob_splash_radius = -1; + } + + fp = fopen_ (cfgName, "r"); + if (fp == NULL) + { + printf ("file not found: %s\n", cfgName); + return 1; + } + while (fgets (line, 1020, fp)) + { + const struct + { + char str[128]; + int *val; + } data[] = + { + { + "warp_point_debug", &battle_config.warp_point_debug}, + { + "enemy_critical", &battle_config.enemy_critical}, + { + "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}, + { + "player_damage_delay", &battle_config.pc_damage_delay}, + { + "player_damage_delay_rate", + &battle_config.pc_damage_delay_rate}, + { + "defunit_not_enemy", &battle_config.defnotenemy}, + { + "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_pickup_safety_zone", + &battle_config.drop_pickup_safety_zone}, + { + "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}, + { + "item_rate", &battle_config.item_rate}, + { + "drop_rate0item", &battle_config.drop_rate0item}, + { + "base_exp_rate", &battle_config.base_exp_rate}, + { + "job_exp_rate", &battle_config.job_exp_rate}, + { + "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}, + { + "zeny_penalty", &battle_config.zeny_penalty}, + { + "restart_hp_rate", &battle_config.restart_hp_rate}, + { + "restart_sp_rate", &battle_config.restart_sp_rate}, + { + "mvp_hp_rate", &battle_config.mvp_hp_rate}, + { + "mvp_item_rate", &battle_config.mvp_item_rate}, + { + "mvp_exp_rate", &battle_config.mvp_exp_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}, + { + "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}, + { + "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}, + { + "mob_count_rate", &battle_config.mob_count_rate}, + { + "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_limit", &battle_config.guild_exp_limit}, + { + "player_invincible_time", &battle_config.pc_invincible_time}, + { + "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}, + { + "wedding_modifydisplay", &battle_config.wedding_modifydisplay}, + { + "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}, + { + "natural_heal_weight_rate", + &battle_config.natural_heal_weight_rate}, + { + "itemheal_regeneration_factor", + &battle_config.itemheal_regeneration_factor}, + { + "item_name_override_grffile", + &battle_config.item_name_override_grffile}, + { + "arrow_decrement", &battle_config.arrow_decrement}, + { + "max_aspd", &battle_config.max_aspd}, + { + "max_hp", &battle_config.max_hp}, + { + "max_sp", &battle_config.max_sp}, + { + "max_lv", &battle_config.max_lv}, + { + "max_parameter", &battle_config.max_parameter}, + { + "max_cart_weight", &battle_config.max_cart_weight}, + { + "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}, + { + "agi_penaly_type", &battle_config.agi_penaly_type}, + { + "agi_penaly_count", &battle_config.agi_penaly_count}, + { + "agi_penaly_num", &battle_config.agi_penaly_num}, + { + "agi_penaly_count_lv", &battle_config.agi_penaly_count_lv}, + { + "vit_penaly_type", &battle_config.vit_penaly_type}, + { + "vit_penaly_count", &battle_config.vit_penaly_count}, + { + "vit_penaly_num", &battle_config.vit_penaly_num}, + { + "vit_penaly_count_lv", &battle_config.vit_penaly_count_lv}, + { + "player_defense_type", &battle_config.player_defense_type}, + { + "monster_defense_type", &battle_config.monster_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}, + { + "gvg_short_attack_damage_rate", + &battle_config.gvg_short_damage_rate}, + { + "gvg_long_attack_damage_rate", + &battle_config.gvg_long_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_eliminate_time", &battle_config.gvg_eliminate_time}, + { + "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_penaly", &battle_config.party_skill_penaly}, + { + "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}, + { + "display_delay_skill_fail", + &battle_config.display_delay_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}, + { + "enable_upper_class", &battle_config.enable_upper_class}, + { + "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_cardfix", &battle_config.gx_cardfix}, + { + "gx_dupele", &battle_config.gx_dupele}, + { + "gx_disptype", &battle_config.gx_disptype}, + { + "player_skill_partner_check", + &battle_config.player_skill_partner_check}, + { + "hide_GM_session", &battle_config.hide_GM_session}, + { + "unit_movement_type", &battle_config.unit_movement_type}, + { + "invite_request_check", &battle_config.invite_request_check}, + { + "skill_removetrap_type", &battle_config.skill_removetrap_type}, + { + "disp_experience", &battle_config.disp_experience}, + { + "castle_defense_rate", &battle_config.castle_defense_rate}, + { + "riding_weight", &battle_config.riding_weight}, + { + "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_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 + { + "prevent_logout", &battle_config.prevent_logout}, // Added by RoVeRT + { + "alchemist_summon_reward", &battle_config.alchemist_summon_reward}, // [Valaris] + { + "maximum_level", &battle_config.maximum_level}, // [Valaris] + { + "drops_by_luk", &battle_config.drops_by_luk}, // [Valaris] + { + "monsters_ignore_gm", &battle_config.monsters_ignore_gm}, // [Valaris] + { + "equipment_breaking", &battle_config.equipment_breaking}, // [Valaris] + { + "equipment_break_rate", &battle_config.equipment_break_rate}, // [Valaris] + { + "pk_mode", &battle_config.pk_mode}, // [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] + { + "day_duration", &battle_config.day_duration}, // added by [Yor] + { + "night_duration", &battle_config.night_duration}, // added by [Yor] + { + "show_mob_hp", &battle_config.show_mob_hp}, // [Valaris] + { + "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] + { + "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] + { + "chat_lame_penalty", &battle_config.chat_lame_penalty}, + { + "chat_spam_threshold", &battle_config.chat_spam_threshold}, + { + "chat_spam_flood", &battle_config.chat_spam_flood}, + { + "chat_spam_ban", &battle_config.chat_spam_ban}, + { + "chat_spam_warn", &battle_config.chat_spam_warn}, + { + "chat_maxline", &battle_config.chat_maxline}, + { + "packet_spam_threshold", &battle_config.packet_spam_threshold}, + { + "packet_spam_flood", &battle_config.packet_spam_flood}, + { + "packet_spam_kick", &battle_config.packet_spam_kick}, + { + "mask_ip_gms", &battle_config.mask_ip_gms}, + { + "mob_splash_radius", &battle_config.mob_splash_radius}, + }; + + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf (line, "%[^:]:%s", w1, w2) != 2) + continue; + for (i = 0; i < sizeof (data) / (sizeof (data[0])); i++) + if (strcasecmp (w1, data[i].str) == 0) + *data[i].val = battle_config_switch (w2); + + if (strcasecmp (w1, "import") == 0) + battle_config_read (w2); + } + fclose_ (fp); + + if (--count == 0) + { + 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_hp > 1000000) + battle_config.max_hp = 1000000; + if (battle_config.max_hp < 100) + battle_config.max_hp = 100; + if (battle_config.max_sp > 1000000) + battle_config.max_sp = 1000000; + 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_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.agi_penaly_count < 2) + battle_config.agi_penaly_count = 2; + if (battle_config.vit_penaly_count < 2) + battle_config.vit_penaly_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.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) // added by [Yor] + battle_config.day_duration = 0; + if (battle_config.night_duration < 0) // added by [Yor] + battle_config.night_duration = 0; + + 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; + + if (battle_config.chat_spam_ban < 0) + battle_config.chat_spam_ban = 0; + else if (battle_config.chat_spam_ban > 32767) + battle_config.chat_spam_ban = 32767; + + if (battle_config.chat_spam_flood < 0) + battle_config.chat_spam_flood = 0; + else if (battle_config.chat_spam_flood > 32767) + battle_config.chat_spam_flood = 32767; + + if (battle_config.chat_spam_warn < 0) + battle_config.chat_spam_warn = 0; + else if (battle_config.chat_spam_warn > 32767) + battle_config.chat_spam_warn = 32767; + + if (battle_config.chat_spam_threshold < 0) + battle_config.chat_spam_threshold = 0; + else if (battle_config.chat_spam_threshold > 32767) + battle_config.chat_spam_threshold = 32767; + + if (battle_config.chat_maxline < 1) + battle_config.chat_maxline = 1; + else if (battle_config.chat_maxline > 512) + battle_config.chat_maxline = 512; + + if (battle_config.packet_spam_threshold < 0) + battle_config.packet_spam_threshold = 0; + else if (battle_config.packet_spam_threshold > 32767) + battle_config.packet_spam_threshold = 32767; + + if (battle_config.packet_spam_flood < 0) + battle_config.packet_spam_flood = 0; + else if (battle_config.packet_spam_flood > 32767) + battle_config.packet_spam_flood = 32767; + + if (battle_config.packet_spam_kick < 0) + battle_config.packet_spam_kick = 0; + else if (battle_config.packet_spam_kick > 1) + battle_config.packet_spam_kick = 1; + + if (battle_config.mask_ip_gms < 0) + battle_config.mask_ip_gms = 0; + else if (battle_config.mask_ip_gms > 1) + battle_config.mask_ip_gms = 1; + + // at least 1 client must be accepted + if ((battle_config.packet_ver_flag & 63) == 0) // added by [Yor] + battle_config.packet_ver_flag = 63; // accept all clients + + } + + return 0; +} diff --git a/src/map/battle.h b/src/map/battle.h deleted file mode 100644 index 66ca3d4..0000000 --- a/src/map/battle.h +++ /dev/null @@ -1,357 +0,0 @@ -// $Id: battle.h,v 1.6 2004/09/29 21:08:17 Akitasha Exp $ -#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); -struct Damage battle_calc_weapon_attack (struct block_list *bl, - struct block_list *target, - int skill_num, int skill_lv, - int flag); -struct Damage battle_calc_magic_attack (struct block_list *bl, - struct block_list *target, - int skill_num, int skill_lv, - int flag); -struct Damage battle_calc_misc_attack (struct block_list *bl, - struct block_list *target, - int skill_num, int skill_lv, int flag); - -// 属性修正計算 -int battle_attr_fix (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_delay_damage (unsigned int tick, struct block_list *src, - struct block_list *target, int damage, 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_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); -int battle_is_unarmed (struct block_list *bl); -int battle_get_class (struct block_list *bl); -int battle_get_dir (struct block_list *bl); -int battle_get_lv (struct block_list *bl); -int battle_get_range (struct block_list *bl); -int battle_get_hp (struct block_list *bl); -int battle_get_max_hp (struct block_list *bl); -int battle_get_str (struct block_list *bl); -int battle_get_agi (struct block_list *bl); -int battle_get_vit (struct block_list *bl); -int battle_get_int (struct block_list *bl); -int battle_get_dex (struct block_list *bl); -int battle_get_luk (struct block_list *bl); -int battle_get_hit (struct block_list *bl); -int battle_get_flee (struct block_list *bl); -int battle_get_def (struct block_list *bl); -int battle_get_mdef (struct block_list *bl); -int battle_get_flee2 (struct block_list *bl); -int battle_get_def2 (struct block_list *bl); -int battle_get_mdef2 (struct block_list *bl); -int battle_get_baseatk (struct block_list *bl); -int battle_get_atk (struct block_list *bl); -int battle_get_atk2 (struct block_list *bl); -int battle_get_speed (struct block_list *bl); -int battle_get_adelay (struct block_list *bl); -int battle_get_amotion (struct block_list *bl); -int battle_get_dmotion (struct block_list *bl); -int battle_get_element (struct block_list *bl); -int battle_get_attack_element (struct block_list *bl); -int battle_get_attack_element2 (struct block_list *bl); //左手武器属性取得 -#define battle_get_elem_type(bl) (battle_get_element(bl)%10) -#define battle_get_elem_level(bl) (battle_get_element(bl)/10/2) -int battle_get_party_id (struct block_list *bl); -int battle_get_guild_id (struct block_list *bl); -int battle_get_race (struct block_list *bl); -int battle_get_size (struct block_list *bl); -int battle_get_mode (struct block_list *bl); -int battle_get_mexp (struct block_list *bl); -int battle_get_stat (int stat_id /* SP_VIT or similar */ , - struct block_list *bl); - -struct status_change *battle_get_sc_data (struct block_list *bl); -short *battle_get_sc_count (struct block_list *bl); -short *battle_get_opt1 (struct block_list *bl); -short *battle_get_opt2 (struct block_list *bl); -short *battle_get_opt3 (struct block_list *bl); -short *battle_get_option (struct block_list *bl); - -enum -{ - BCT_NOENEMY = 0x00000, - BCT_PARTY = 0x10000, - BCT_ENEMY = 0x40000, - BCT_NOPARTY = 0x50000, - BCT_ALL = 0x20000, - BCT_NOONE = 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 -{ - int warp_point_debug; - int enemy_critical; - int enemy_critical_rate; - int enemy_str; - int enemy_perfect_flee; - int cast_rate, delay_rate, delay_dependon_dex; - int sdelay_attack_enable; - int left_cardfix_to_right; - int pc_skill_add_range; - int skill_out_range_consume; - int mob_skill_add_range; - int pc_damage_delay; - int pc_damage_delay_rate; - int defnotenemy; - int random_monster_checklv; - int attr_recover; - int flooritem_lifetime; - int 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 item_rate, base_exp_rate, job_exp_rate; // removed item rate, depreciated - int drop_rate0item; - int death_penalty_type; - int death_penalty_base, death_penalty_job; - int pvp_exp; // [MouseJstr] - int gtb_pvp_only; // [MouseJstr] - int zeny_penalty; - int restart_hp_rate; - int restart_sp_rate; - int mvp_item_rate, mvp_exp_rate; - int mvp_hp_rate; - int monster_hp_rate; - int monster_max_aspd; - int atc_gmonly; - int atc_spawn_quantity_limit; - int gm_allskill; - int gm_allskill_addabra; - int gm_allequip; - int gm_skilluncond; - int skillfree; - int skillup_limit; - int wp_rate; - int pp_rate; - int monster_active_enable; - int monster_damage_delay_rate; - int monster_loot_type; - int mob_skill_use; - int mob_count_rate; - int quest_skill_learn; - int quest_skill_reset; - int basic_skill_check; - int guild_emperium_check; - int guild_exp_limit; - int guild_max_castles; - int pc_invincible_time; - int skill_min_damage; - int finger_offensive_type; - int heal_exp; - int resurrection_exp; - int shop_exp; - int combo_delay_rate; - int item_check; - int wedding_modifydisplay; - int natural_healhp_interval; - int natural_healsp_interval; - int natural_heal_skill_interval; - int natural_heal_weight_rate; - int item_name_override_grffile; - int arrow_decrement; - int max_aspd; - int max_hp; - int max_sp; - int max_lv; - int max_parameter; - int max_cart_weight; - int pc_skill_log; - int mob_skill_log; - int battle_log; - int save_log; - int error_log; - int etc_log; - int save_clothcolor; - int undead_detect_type; - int pc_auto_counter_type; - int monster_auto_counter_type; - int agi_penaly_type; - int agi_penaly_count; - int agi_penaly_num; - int vit_penaly_type; - int vit_penaly_count; - int vit_penaly_num; - int player_defense_type; - int monster_defense_type; - int magic_defense_type; - int pc_skill_reiteration; - int monster_skill_reiteration; - int pc_skill_nofootset; - int monster_skill_nofootset; - int pc_cloak_check_type; - int monster_cloak_check_type; - int gvg_short_damage_rate; - int gvg_long_damage_rate; - int gvg_magic_damage_rate; - int gvg_misc_damage_rate; - int gvg_eliminate_time; - int mob_changetarget_byskill; - int pc_attack_direction_change; - int monster_attack_direction_change; - int pc_undead_nofreeze; - int pc_land_skill_limit; - int monster_land_skill_limit; - int party_skill_penaly; - int monster_class_change_full_recover; - int produce_item_name_input; - int produce_potion_name_input; - int making_arrow_name_input; - int holywater_name_input; - int display_delay_skill_fail; - int chat_warpportal; - int mob_warpportal; - int dead_branch_active; - int show_steal_in_same_party; - int enable_upper_class; - int mob_attack_attr_none; - int mob_ghostring_fix; - int pc_attack_attr_none; - int item_rate_common, item_rate_card, item_rate_equip, item_rate_heal, item_rate_use; // Added by RoVeRT, Additional Heal and Usable item rate by Val - int item_drop_common_min, item_drop_common_max; // Added by TyrNemesis^ - int item_drop_card_min, item_drop_card_max; - int item_drop_equip_min, item_drop_equip_max; - int item_drop_mvp_min, item_drop_mvp_max; // End Addition - int item_drop_heal_min, item_drop_heal_max; // Added by Valatris - int item_drop_use_min, item_drop_use_max; //End - - int prevent_logout; // Added by RoVeRT - - int alchemist_summon_reward; // [Valaris] - int maximum_level; - int drops_by_luk; - int monsters_ignore_gm; - int equipment_breaking; - int equipment_break_rate; - int multi_level_up; - int pk_mode; - int show_mob_hp; // end additions [Valaris] - - int agi_penaly_count_lv; - int vit_penaly_count_lv; - - int gx_allhit; - int gx_cardfix; - int gx_dupele; - int gx_disptype; - int player_skill_partner_check; - int hide_GM_session; - int unit_movement_type; - int invite_request_check; - int skill_removetrap_type; - int disp_experience; - int castle_defense_rate; - int riding_weight; - int backstab_bow_penalty; - - int night_at_start; // added by [Yor] - int day_duration; // added by [Yor] - int night_duration; // added by [Yor] - int hack_info_GM_level; // added by [Yor] - int any_warp_GM_min_level; // added by [Yor] - int packet_ver_flag; // added by [Yor] - int muting_players; // added by [Apple] - - int min_hair_style; // added by [MouseJstr] - int max_hair_style; // added by [MouseJstr] - int min_hair_color; // added by [MouseJstr] - int max_hair_color; // added by [MouseJstr] - int min_cloth_color; // added by [MouseJstr] - int max_cloth_color; // added by [MouseJstr] - - int castrate_dex_scale; // added by [MouseJstr] - int area_size; // added by [MouseJstr] - - int chat_lame_penalty; - int chat_spam_threshold; - int chat_spam_flood; - int chat_spam_ban; - int chat_spam_warn; - int chat_maxline; - - int packet_spam_threshold; - int packet_spam_flood; - int packet_spam_kick; - - int mask_ip_gms; - - int drop_pickup_safety_zone; // [Fate] Max. distance to an object dropped by a kill by self in which dropsteal protection works - int itemheal_regeneration_factor; // [Fate] itemheal speed factor - - int mob_splash_radius; -} battle_config; - -int battle_config_read (const char *cfgName); - -#endif diff --git a/src/map/battle.hpp b/src/map/battle.hpp new file mode 100644 index 0000000..5f2a22f --- /dev/null +++ b/src/map/battle.hpp @@ -0,0 +1,357 @@ +// $Id: battle.h,v 1.6 2004/09/29 21:08:17 Akitasha Exp $ +#ifndef BATTLE_HPP +#define BATTLE_HPP + +// ダメージ +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); +struct Damage battle_calc_weapon_attack (struct block_list *bl, + struct block_list *target, + int skill_num, int skill_lv, + int flag); +struct Damage battle_calc_magic_attack (struct block_list *bl, + struct block_list *target, + int skill_num, int skill_lv, + int flag); +struct Damage battle_calc_misc_attack (struct block_list *bl, + struct block_list *target, + int skill_num, int skill_lv, int flag); + +// 属性修正計算 +int battle_attr_fix (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_delay_damage (unsigned int tick, struct block_list *src, + struct block_list *target, int damage, 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_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); +int battle_is_unarmed (struct block_list *bl); +int battle_get_class (struct block_list *bl); +int battle_get_dir (struct block_list *bl); +int battle_get_lv (struct block_list *bl); +int battle_get_range (struct block_list *bl); +int battle_get_hp (struct block_list *bl); +int battle_get_max_hp (struct block_list *bl); +int battle_get_str (struct block_list *bl); +int battle_get_agi (struct block_list *bl); +int battle_get_vit (struct block_list *bl); +int battle_get_int (struct block_list *bl); +int battle_get_dex (struct block_list *bl); +int battle_get_luk (struct block_list *bl); +int battle_get_hit (struct block_list *bl); +int battle_get_flee (struct block_list *bl); +int battle_get_def (struct block_list *bl); +int battle_get_mdef (struct block_list *bl); +int battle_get_flee2 (struct block_list *bl); +int battle_get_def2 (struct block_list *bl); +int battle_get_mdef2 (struct block_list *bl); +int battle_get_baseatk (struct block_list *bl); +int battle_get_atk (struct block_list *bl); +int battle_get_atk2 (struct block_list *bl); +int battle_get_speed (struct block_list *bl); +int battle_get_adelay (struct block_list *bl); +int battle_get_amotion (struct block_list *bl); +int battle_get_dmotion (struct block_list *bl); +int battle_get_element (struct block_list *bl); +int battle_get_attack_element (struct block_list *bl); +int battle_get_attack_element2 (struct block_list *bl); //左手武器属性取得 +#define battle_get_elem_type(bl) (battle_get_element(bl)%10) +#define battle_get_elem_level(bl) (battle_get_element(bl)/10/2) +int battle_get_party_id (struct block_list *bl); +int battle_get_guild_id (struct block_list *bl); +int battle_get_race (struct block_list *bl); +int battle_get_size (struct block_list *bl); +int battle_get_mode (struct block_list *bl); +int battle_get_mexp (struct block_list *bl); +int battle_get_stat (int stat_id /* SP_VIT or similar */ , + struct block_list *bl); + +struct status_change *battle_get_sc_data (struct block_list *bl); +short *battle_get_sc_count (struct block_list *bl); +short *battle_get_opt1 (struct block_list *bl); +short *battle_get_opt2 (struct block_list *bl); +short *battle_get_opt3 (struct block_list *bl); +short *battle_get_option (struct block_list *bl); + +enum +{ + BCT_NOENEMY = 0x00000, + BCT_PARTY = 0x10000, + BCT_ENEMY = 0x40000, + BCT_NOPARTY = 0x50000, + BCT_ALL = 0x20000, + BCT_NOONE = 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 +{ + int warp_point_debug; + int enemy_critical; + int enemy_critical_rate; + int enemy_str; + int enemy_perfect_flee; + int cast_rate, delay_rate, delay_dependon_dex; + int sdelay_attack_enable; + int left_cardfix_to_right; + int pc_skill_add_range; + int skill_out_range_consume; + int mob_skill_add_range; + int pc_damage_delay; + int pc_damage_delay_rate; + int defnotenemy; + int random_monster_checklv; + int attr_recover; + int flooritem_lifetime; + int 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 item_rate, base_exp_rate, job_exp_rate; // removed item rate, depreciated + int drop_rate0item; + int death_penalty_type; + int death_penalty_base, death_penalty_job; + int pvp_exp; // [MouseJstr] + int gtb_pvp_only; // [MouseJstr] + int zeny_penalty; + int restart_hp_rate; + int restart_sp_rate; + int mvp_item_rate, mvp_exp_rate; + int mvp_hp_rate; + int monster_hp_rate; + int monster_max_aspd; + int atc_gmonly; + int atc_spawn_quantity_limit; + int gm_allskill; + int gm_allskill_addabra; + int gm_allequip; + int gm_skilluncond; + int skillfree; + int skillup_limit; + int wp_rate; + int pp_rate; + int monster_active_enable; + int monster_damage_delay_rate; + int monster_loot_type; + int mob_skill_use; + int mob_count_rate; + int quest_skill_learn; + int quest_skill_reset; + int basic_skill_check; + int guild_emperium_check; + int guild_exp_limit; + int guild_max_castles; + int pc_invincible_time; + int skill_min_damage; + int finger_offensive_type; + int heal_exp; + int resurrection_exp; + int shop_exp; + int combo_delay_rate; + int item_check; + int wedding_modifydisplay; + int natural_healhp_interval; + int natural_healsp_interval; + int natural_heal_skill_interval; + int natural_heal_weight_rate; + int item_name_override_grffile; + int arrow_decrement; + int max_aspd; + int max_hp; + int max_sp; + int max_lv; + int max_parameter; + int max_cart_weight; + int pc_skill_log; + int mob_skill_log; + int battle_log; + int save_log; + int error_log; + int etc_log; + int save_clothcolor; + int undead_detect_type; + int pc_auto_counter_type; + int monster_auto_counter_type; + int agi_penaly_type; + int agi_penaly_count; + int agi_penaly_num; + int vit_penaly_type; + int vit_penaly_count; + int vit_penaly_num; + int player_defense_type; + int monster_defense_type; + int magic_defense_type; + int pc_skill_reiteration; + int monster_skill_reiteration; + int pc_skill_nofootset; + int monster_skill_nofootset; + int pc_cloak_check_type; + int monster_cloak_check_type; + int gvg_short_damage_rate; + int gvg_long_damage_rate; + int gvg_magic_damage_rate; + int gvg_misc_damage_rate; + int gvg_eliminate_time; + int mob_changetarget_byskill; + int pc_attack_direction_change; + int monster_attack_direction_change; + int pc_undead_nofreeze; + int pc_land_skill_limit; + int monster_land_skill_limit; + int party_skill_penaly; + int monster_class_change_full_recover; + int produce_item_name_input; + int produce_potion_name_input; + int making_arrow_name_input; + int holywater_name_input; + int display_delay_skill_fail; + int chat_warpportal; + int mob_warpportal; + int dead_branch_active; + int show_steal_in_same_party; + int enable_upper_class; + int mob_attack_attr_none; + int mob_ghostring_fix; + int pc_attack_attr_none; + int item_rate_common, item_rate_card, item_rate_equip, item_rate_heal, item_rate_use; // Added by RoVeRT, Additional Heal and Usable item rate by Val + int item_drop_common_min, item_drop_common_max; // Added by TyrNemesis^ + int item_drop_card_min, item_drop_card_max; + int item_drop_equip_min, item_drop_equip_max; + int item_drop_mvp_min, item_drop_mvp_max; // End Addition + int item_drop_heal_min, item_drop_heal_max; // Added by Valatris + int item_drop_use_min, item_drop_use_max; //End + + int prevent_logout; // Added by RoVeRT + + int alchemist_summon_reward; // [Valaris] + int maximum_level; + int drops_by_luk; + int monsters_ignore_gm; + int equipment_breaking; + int equipment_break_rate; + int multi_level_up; + int pk_mode; + int show_mob_hp; // end additions [Valaris] + + int agi_penaly_count_lv; + int vit_penaly_count_lv; + + int gx_allhit; + int gx_cardfix; + int gx_dupele; + int gx_disptype; + int player_skill_partner_check; + int hide_GM_session; + int unit_movement_type; + int invite_request_check; + int skill_removetrap_type; + int disp_experience; + int castle_defense_rate; + int riding_weight; + int backstab_bow_penalty; + + int night_at_start; // added by [Yor] + int day_duration; // added by [Yor] + int night_duration; // added by [Yor] + int hack_info_GM_level; // added by [Yor] + int any_warp_GM_min_level; // added by [Yor] + int packet_ver_flag; // added by [Yor] + int muting_players; // added by [Apple] + + int min_hair_style; // added by [MouseJstr] + int max_hair_style; // added by [MouseJstr] + int min_hair_color; // added by [MouseJstr] + int max_hair_color; // added by [MouseJstr] + int min_cloth_color; // added by [MouseJstr] + int max_cloth_color; // added by [MouseJstr] + + int castrate_dex_scale; // added by [MouseJstr] + int area_size; // added by [MouseJstr] + + int chat_lame_penalty; + int chat_spam_threshold; + int chat_spam_flood; + int chat_spam_ban; + int chat_spam_warn; + int chat_maxline; + + int packet_spam_threshold; + int packet_spam_flood; + int packet_spam_kick; + + int mask_ip_gms; + + int drop_pickup_safety_zone; // [Fate] Max. distance to an object dropped by a kill by self in which dropsteal protection works + int itemheal_regeneration_factor; // [Fate] itemheal speed factor + + int mob_splash_radius; +} battle_config; + +int battle_config_read (const char *cfgName); + +#endif diff --git a/src/map/chat.c b/src/map/chat.c deleted file mode 100644 index ccb75df..0000000 --- a/src/map/chat.c +++ /dev/null @@ -1,395 +0,0 @@ -// $Id: chat.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "../common/db.h" -#include "../common/nullpo.h" -#include "map.h" -#include "clif.h" -#include "pc.h" -#include "chat.h" -#include "npc.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -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); - - CREATE(cd, struct chat_data, 1); - - cd->limit = limit; - cd->pub = pub; - cd->users = 1; - memcpy (cd->pass, pass, 8); - 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); - free (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); - if (cd == NULL) - return 1; - - if (cd->bl.m != sd->bl.m || cd->limit <= cd->users) - { - clif_joinchatfail (sd, 0); - return 0; - } - if (cd->pub == 0 && strncmp (pass, cd->pass, 8)) - { - 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, BL_CHAT); // 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); - 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, kickuser; - - 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 = 0, kickuser = -1; i < cd->users; i++) - { - if (strcmp (cd->usersd[i]->status.name, kickusername) == 0) - { - kickuser = i; - break; - } - } - if (kickuser < 0) // そんな人は居ない - return -1; - - chat_leavechat (cd->usersd[kickuser]); - - return 0; -} - -/*========================================== - * 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); - - CREATE (cd, struct chat_data, 1); - - cd->limit = cd->trigger = limit; - if (trigger > 0) - cd->trigger = trigger; - cd->pub = pub; - cd->users = 0; - memcpy (cd->pass, "", 8); - 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_; - memcpy (cd->npc_event, ev, sizeof (cd->npc_event)); - - cd->bl.id = map_addobject (&cd->bl); - if (cd->bl.id == 0) - { - free (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, BL_CHAT); // 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; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -int do_final_chat (void) -{ - return 0; -} diff --git a/src/map/chat.cpp b/src/map/chat.cpp new file mode 100644 index 0000000..0615b41 --- /dev/null +++ b/src/map/chat.cpp @@ -0,0 +1,395 @@ +// $Id: chat.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../common/db.hpp" +#include "../common/nullpo.hpp" +#include "map.hpp" +#include "clif.hpp" +#include "pc.hpp" +#include "chat.hpp" +#include "npc.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +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); + + CREATE(cd, struct chat_data, 1); + + cd->limit = limit; + cd->pub = pub; + cd->users = 1; + memcpy (cd->pass, pass, 8); + 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); + free (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); + if (cd == NULL) + return 1; + + if (cd->bl.m != sd->bl.m || cd->limit <= cd->users) + { + clif_joinchatfail (sd, 0); + return 0; + } + if (cd->pub == 0 && strncmp (pass, cd->pass, 8)) + { + 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, BL_CHAT); // 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); + 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, kickuser; + + 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 = 0, kickuser = -1; i < cd->users; i++) + { + if (strcmp (cd->usersd[i]->status.name, kickusername) == 0) + { + kickuser = i; + break; + } + } + if (kickuser < 0) // そんな人は居ない + return -1; + + chat_leavechat (cd->usersd[kickuser]); + + return 0; +} + +/*========================================== + * 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); + + CREATE (cd, struct chat_data, 1); + + cd->limit = cd->trigger = limit; + if (trigger > 0) + cd->trigger = trigger; + cd->pub = pub; + cd->users = 0; + memcpy (cd->pass, "", 8); + 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_; + memcpy (cd->npc_event, ev, sizeof (cd->npc_event)); + + cd->bl.id = map_addobject (&cd->bl); + if (cd->bl.id == 0) + { + free (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, BL_CHAT); // 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; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_chat (void) +{ + return 0; +} diff --git a/src/map/chat.h b/src/map/chat.h deleted file mode 100644 index 7f61f54..0000000 --- a/src/map/chat.h +++ /dev/null @@ -1,25 +0,0 @@ -// $Id: chat.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ -#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); - -int do_final_chat (void); - -#endif diff --git a/src/map/chat.hpp b/src/map/chat.hpp new file mode 100644 index 0000000..3a9ccb6 --- /dev/null +++ b/src/map/chat.hpp @@ -0,0 +1,25 @@ +// $Id: chat.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef CHAT_HPP +#define CHAT_HPP + +#include "map.hpp" + +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); + +int do_final_chat (void); + +#endif diff --git a/src/map/chrif.c b/src/map/chrif.c deleted file mode 100644 index 5e2cd4c..0000000 --- a/src/map/chrif.c +++ /dev/null @@ -1,1307 +0,0 @@ -// $Id: chrif.c,v 1.6 2004/09/25 11:39:17 MouseJstr Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#ifdef LCCWIN32 -#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/socket.h" -#include "../common/timer.h" -#include "map.h" -#include "battle.h" -#include "chrif.h" -#include "clif.h" -#include "intif.h" -#include "npc.h" -#include "pc.h" -#include "../common/nullpo.h" -#include "itemdb.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -static const int packet_len_table[0x20] = { - 60, 3, 10, 27, 22, -1, 6, -1, // 2af8-2aff - 6, -1, 18, 7, -1, 49, 44, 0, // 2b00-2b07 - 6, 30, -1, 10, 86, 7, 44, 34, // 2b08-2b0f - -1, -1, 10, 6, 11, -1, 0, 0, // 2b10-2b17 -}; - -int char_fd; -int srvinfo; -static char char_ip_str[16]; -static int char_ip; -static int char_port = 6121; -static char userid[24], passwd[24]; -static int chrif_state; - -// 設定ファイル読み込み関係 -/*========================================== - * - *------------------------------------------ - */ -void chrif_setuserid (char *id) -{ - strncpy (userid, id, sizeof(userid)-1); - userid[sizeof(userid)-1] = '\0'; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setpasswd (char *pwd) -{ - strncpy (passwd, pwd, sizeof(passwd)-1); - passwd[sizeof(passwd)-1] = '\0'; -} - -char *chrif_getpasswd (void) -{ - return passwd; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setip (char *ip) -{ - strncpy (char_ip_str, ip, sizeof(char_ip_str)-1); - char_ip_str[sizeof(char_ip_str)-1] = '\0'; - char_ip = inet_addr (char_ip_str); -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setport (int port) -{ - char_port = port; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_isconnect (void) -{ - return chrif_state == 2; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_save (struct map_session_data *sd) -{ - nullpo_retr (-1, sd); - - if (char_fd < 0) - return -1; - - pc_makesavestatus (sd); - - WFIFOW (char_fd, 0) = 0x2b01; - WFIFOW (char_fd, 2) = sizeof (sd->status) + 12; - WFIFOL (char_fd, 4) = sd->bl.id; - WFIFOL (char_fd, 8) = sd->char_id; - memcpy (WFIFOP (char_fd, 12), &sd->status, sizeof (sd->status)); - WFIFOSET (char_fd, WFIFOW (char_fd, 2)); - - //For data sync - if (sd->state.storage_flag == 1) - storage_storage_save (sd->status.account_id, 0); - else if (sd->state.storage_flag == 2) - storage_guild_storagesave (sd->status.account_id, sd->status.guild_id, - 0); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_connect (int fd) -{ - WFIFOW (fd, 0) = 0x2af8; - memcpy (WFIFOP (fd, 2), userid, 24); - memcpy (WFIFOP (fd, 26), passwd, 24); - 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; - - WFIFOW (fd, 0) = 0x2afa; - for (i = 0; i < map_num; i++) - if (map[i].alias[0] != '\0') // [MouseJstr] map aliasing - memcpy (WFIFOP (fd, 4 + i * 16), map[i].alias, 16); - else - memcpy (WFIFOP (fd, 4 + i * 16), map[i].name, 16); - WFIFOW (fd, 2) = 4 + i * 16; - WFIFOSET (fd, WFIFOW (fd, 2)); - - return 0; -} - -/*========================================== - * マップ受信 - *------------------------------------------ - */ -int chrif_recvmap (int fd) -{ - int i, j, ip, port; - unsigned char *p = (unsigned char *) &ip; - - if (chrif_state < 2) // まだ準備中 - return -1; - - ip = RFIFOL (fd, 4); - port = RFIFOW (fd, 8); - for (i = 10, j = 0; i < RFIFOW (fd, 2); i += 16, j++) - { - map_setipport (RFIFOP (fd, i), ip, port); -// if (battle_config.etc_log) -// printf("recv map %d %s\n", j, RFIFOP(fd,i)); - } - if (battle_config.etc_log) - printf ("recv map on %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, char *name, int x, - int y, int ip, short port) -{ - int i, s_ip; - - nullpo_retr (-1, sd); - - 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; - } - - 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; - memcpy (WFIFOP (char_fd, 18), name, 16); - WFIFOW (char_fd, 34) = x; - WFIFOW (char_fd, 36) = y; - WFIFOL (char_fd, 38) = ip; - WFIFOL (char_fd, 42) = port; - WFIFOB (char_fd, 44) = sd->status.sex; - WFIFOL (char_fd, 45) = s_ip; - WFIFOSET (char_fd, 49); - - return 0; -} - -/*========================================== - * マップ鯖間移動ack - *------------------------------------------ - */ -int chrif_changemapserverack (int fd) -{ - struct map_session_data *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) - printf ("map server change failed.\n"); - pc_authfail (sd->fd); - return 0; - } - clif_changemapserver (sd, RFIFOP (fd, 18), RFIFOW (fd, 34), - RFIFOW (fd, 36), RFIFOL (fd, 38), RFIFOW (fd, 42)); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_connectack (int fd) -{ - if (RFIFOB (fd, 2)) - { - printf ("Connected to char-server failed %d.\n", RFIFOB (fd, 2)); - exit (1); - } - printf ("Connected to char-server (connection #%d).\n", fd); - chrif_state = 1; - - chrif_sendmap (fd); - - printf ("chrif: OnCharIfInit event done. (%d events)\n", - npc_event_doall ("OnCharIfInit")); - printf ("chrif: OnInterIfInit event done. (%d events)\n", - npc_event_doall ("OnInterIfInit")); - - // <Agit> Run Event [AgitInit] -// printf("NPC_Event:[OnAgitInit] do (%d) events (Agit Initialize).\n", npc_event_doall("OnAgitInit")); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_sendmapack (int fd) -{ - if (RFIFOB (fd, 2)) - { - printf ("chrif : send map list to char server failed %d\n", - RFIFOB (fd, 2)); - exit (1); - } - - memcpy (wisp_server_name, RFIFOP (fd, 3), 24); - - chrif_state = 2; - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_authreq (struct map_session_data *sd) -{ - int i; - - nullpo_retr (-1, sd); - - if (!sd || !char_fd || !sd->bl.id || !sd->login_id1) - return -1; - - for (i = 0; i < fd_max; i++) - if (session[i] && session[i]->session_data == sd) - { - WFIFOW (char_fd, 0) = 0x2afc; - WFIFOL (char_fd, 2) = sd->bl.id; - WFIFOL (char_fd, 6) = sd->char_id; - WFIFOL (char_fd, 10) = sd->login_id1; - WFIFOL (char_fd, 14) = sd->login_id2; - WFIFOL (char_fd, 18) = session[i]->client_addr.sin_addr.s_addr; - WFIFOSET (char_fd, 22); - break; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_charselectreq (struct map_session_data *sd) -{ - int i, s_ip; - - nullpo_retr (-1, sd); - - if (!sd || !char_fd || !sd->bl.id || !sd->login_id1) - return -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; - } - - 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; - - 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) - printf ("chrif_changegm: account: %d, password: '%s'.\n", id, pass); - - 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) - printf - ("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", - id, actual_email, new_email); - - 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) -{ - 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, 24); - 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; - } - printf ("chrif : sended 0x2b0e\n"); - WFIFOSET (char_fd, 44); - - 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[24]; - - acc = RFIFOL (fd, 2); // account_id of who has asked (-1 if nobody) - memcpy (player_name, RFIFOP (fd, 6), sizeof (player_name)); - player_name[sizeof (player_name) - 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 - printf ("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; - - acc = RFIFOL (fd, 2); - level = RFIFOL (fd, 6); - - sd = map_id2sd (acc); - - if (battle_config.etc_log) - printf ("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; - struct pc_base_job s_class; - - acc = RFIFOL (fd, 2); - sex = RFIFOL (fd, 6); - if (battle_config.etc_log) - printf ("chrif_changedsex %d.\n", acc); - sd = map_id2sd (acc); - if (acc > 0) - { - if (sd != NULL && sd->status.sex != sex) - { - s_class = pc_calc_base_job (sd->status.pc_class); - 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, 0); - } - // reset skill of some job - if (s_class.job == 19 || s_class.job == 4020 - || s_class.job == 4042 || s_class.job == 20 - || s_class.job == 4021 || s_class.job == 4043) - { - - clif_updatestatus (sd, SP_SKILLPOINT); - // change job if necessary - if (s_class.job == 20 || s_class.job == 4021 - || s_class.job == 4043) - sd->status.pc_class -= 1; - else if (s_class.job == 19 || s_class.job == 4020 - || s_class.job == 4042) - sd->status.pc_class += 1; - } - // save character - chrif_save (sd); - 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 disconexion by the server)..."); - clif_setwaitclose (sd->fd); // forced to disconnect for the change - } - } - else - { - if (sd != NULL) - { - printf ("chrif_changedsex failed.\n"); - } - } - - return 0; -} - -/*========================================== - * アカウント変数保存要求 - *------------------------------------------ - */ -int chrif_saveaccountreg2 (struct map_session_data *sd) -{ - int p, j; - nullpo_retr (-1, sd); - - p = 8; - for (j = 0; j < sd->status.account_reg2_num; j++) - { - struct global_reg *reg = &sd->status.account_reg2[j]; - if (reg->str[0] && reg->value != 0) - { - memcpy (WFIFOP (char_fd, p), reg->str, 32); - WFIFOL (char_fd, p + 32) = reg->value; - p += 36; - } - } - WFIFOW (char_fd, 0) = 0x2b10; - WFIFOW (char_fd, 2) = p; - WFIFOL (char_fd, 4) = sd->bl.id; - WFIFOSET (char_fd, p); - - return 0; -} - -/*========================================== - * アカウント変数通知 - *------------------------------------------ - */ -int chrif_accountreg2 (int fd) -{ - int j, p; - struct map_session_data *sd; - - if ((sd = map_id2sd (RFIFOL (fd, 4))) == NULL) - return 1; - - for (p = 8, j = 0; p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) - { - memcpy (sd->status.account_reg2[j].str, RFIFOP (fd, p), 32); - sd->status.account_reg2[j].value = RFIFOL (fd, p + 32); - } - sd->status.account_reg2_num = j; -// printf("chrif: accountreg2\n"); - - return 0; -} - -/*========================================== - * Divorce request from char server - * triggered on account deletion or as an - * ack from a map-server divorce request - *------------------------------------------ - */ -int chrif_divorce (int char_id, int partner_id) -{ - struct map_session_data *sd = NULL; - - if (!char_id || !partner_id) - return 0; - - sd = map_nick2sd (map_charid2nick (char_id)); - if (sd && sd->status.partner_id == partner_id) - { - sd->status.partner_id = 0; - - if (sd->npc_flags.divorce) - { - sd->npc_flags.divorce = 0; - map_scriptcont (sd, sd->npc_id); - } - } - - nullpo_retr (0, sd = map_nick2sd (map_charid2nick (partner_id))); - if (sd->status.partner_id == char_id) - sd->status.partner_id = 0; - - return 0; -} - -/*========================================== - * Tell character server someone is divorced - * Needed to divorce when partner is not connected to map server - *------------------------------------- - */ -int chrif_send_divorce (int char_id) -{ - if (char_fd < 0) - return -1; - - WFIFOW (char_fd, 0) = 0x2b16; - WFIFOL (char_fd, 2) = char_id; - WFIFOSET (char_fd, 6); - 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; - - acc = RFIFOL (fd, 2); - if (battle_config.etc_log) - printf ("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) - printf ("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; - - acc = RFIFOL (fd, 2); - if (battle_config.etc_log) - printf ("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", - gmtime (×tamp)); - clif_displaymessage (sd->fd, tmpstr); - } - clif_setwaitclose (sd->fd); // forced to disconnect for the change - } - } - else - { - if (sd != NULL) - printf ("chrif_accountban failed - player not online.\n"); - } - - return 0; -} - -/*========================================== - * Receiving GM accounts and their levels from char-server by [Yor] - *------------------------------------------ - */ -int chrif_recvgmaccounts (int fd) -{ - printf ("From login-server: receiving of %d GM accounts information.\n", - pc_read_gm_account (fd)); - - return 0; -} - -/*========================================== - * Request to reload GM accounts and their levels: send to char-server by [Yor] - *------------------------------------------ - */ -int chrif_reloadGMdb (void) -{ - - WFIFOW (char_fd, 0) = 0x2af7; - WFIFOSET (char_fd, 2); - - 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; - - 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 - { - 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) -{ - if (char_fd < 0) - return -1; - - WFIFOW (char_fd, 0) = 0x2b17; - WFIFOL (char_fd, 2) = sd->status.char_id; - WFIFOSET (char_fd, 6); - - return 0; -} - -/*======================================== - * Map item IDs - *---------------------------------------- - */ - -static void ladmin_itemfrob_fix_item (int source, int dest, struct item *item) -{ - if (item && item->nameid == source) - { - item->nameid = dest; - item->equip = 0; - } -} - -static int ladmin_itemfrob_c2 (struct block_list *bl, int source_id, - int dest_id) -{ -#define IFIX(v) if (v == source_id) {v = dest_id; } -#define FIX(item) ladmin_itemfrob_fix_item(source_id, dest_id, &item) - - if (!bl) - return 0; - - switch (bl->type) - { - case BL_PC: - { - struct map_session_data *pc = (struct map_session_data *) bl; - struct storage *stor = account2storage2 (pc->status.account_id); - int j; - - for (j = 0; j < MAX_INVENTORY; j++) - IFIX (pc->status.inventory[j].nameid); - for (j = 0; j < MAX_CART; j++) - IFIX (pc->status.cart[j].nameid); - IFIX (pc->status.weapon); - IFIX (pc->status.shield); - IFIX (pc->status.head_top); - IFIX (pc->status.head_mid); - IFIX (pc->status.head_bottom); - - if (stor) - for (j = 0; j < stor->storage_amount; j++) - FIX (stor->storage_[j]); - - for (j = 0; j < MAX_INVENTORY; j++) - { - struct item_data *item = pc->inventory_data[j]; - if (item && item->nameid == source_id) - { - item->nameid = dest_id; - if (item->equip) - pc_unequipitem (pc, j, 0); - item->nameid = dest_id; - } - } - - break; - } - - case BL_MOB: - { - struct mob_data *mob = (struct mob_data *) bl; - int i; - for (i = 0; i < mob->lootitem_count; i++) - FIX (mob->lootitem[i]); - break; - } - - case BL_ITEM: - { - struct flooritem_data *item = (struct flooritem_data *) bl; - FIX (item->item_data); - break; - } - } -#undef FIX -#undef IFIX - - return 0; -} - -int ladmin_itemfrob_c (struct block_list *bl, va_list va_args) -{ - int source_id = va_arg (va_args, int); - int dest_id = va_arg (va_args, int); - return ladmin_itemfrob_c2 (bl, source_id, dest_id); -} - -void ladmin_itemfrob (int fd) -{ - int source_id = RFIFOL (fd, 2); - int dest_id = RFIFOL (fd, 6); - struct block_list *bl = (struct block_list *) map_get_first_session (); - - // flooritems - map_foreachobject (ladmin_itemfrob_c, 0 /* any object */ , source_id, - dest_id); - - // player characters (and, hopefully, mobs) - while (bl->next) - { - ladmin_itemfrob_c2 (bl, source_id, dest_id); - bl = bl->next; - } -} - -/*========================================== - * - *------------------------------------------ - */ -void 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) - { - printf - ("Map-server can't connect to char-server (connection #%d).\n", - fd); - char_fd = -1; - } - close (fd); - delete_session (fd); - return; - } - - while (RFIFOREST (fd) >= 2) - { - 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; // intifで処理したが、データが足りない - - session[fd]->eof = 1; - return; - } - packet_len = packet_len_table[cmd - 0x2af8]; - if (packet_len == -1) - { - if (RFIFOREST (fd) < 4) - return; - packet_len = RFIFOW (fd, 2); - } - if (RFIFOREST (fd) < packet_len) - return; - - switch (cmd) - { - case 0x2af9: - chrif_connectack (fd); - break; - case 0x2afa: - ladmin_itemfrob (fd); - break; - case 0x2afb: - chrif_sendmapack (fd); - break; - case 0x2afd: - pc_authok (RFIFOL (fd, 4), RFIFOL (fd, 8), - (time_t) RFIFOL (fd, 12), RFIFOW (fd, 16), - (struct mmo_charstatus *) RFIFOP (fd, 18)); - break; - case 0x2afe: - pc_authfail (RFIFOL (fd, 2)); - break; - case 0x2b00: - map_setusers (RFIFOL (fd, 2)); - break; - case 0x2b03: - clif_charselectok (RFIFOL (fd, 2)); - break; - case 0x2b04: - chrif_recvmap (fd); - break; - case 0x2b06: - chrif_changemapserverack (fd); - break; - case 0x2b09: - map_addchariddb (RFIFOL (fd, 2), 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 0x2b11: - chrif_accountreg2 (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; - - default: - if (battle_config.error_log) - printf ("chrif_parse : unknown packet %d %d\n", fd, - RFIFOW (fd, 0)); - session[fd]->eof = 1; - return; - } - RFIFOSKIP (fd, packet_len); - } -} - -/*========================================== - * timer関数 - * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る - *------------------------------------------ - */ -void send_users_tochar (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int users = 0, i; - struct map_session_data *sd; - - if (char_fd <= 0 || session[char_fd] == NULL) - return; - - WFIFOW (char_fd, 0) = 0x2aff; - 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 - || sd->state.shroud_active - || (sd->status.option & OPTION_HIDE)) && pc_isGM (sd))) - { - WFIFOL (char_fd, 6 + 4 * users) = sd->status.char_id; - users++; - } - } - WFIFOW (char_fd, 2) = 6 + 4 * users; - WFIFOW (char_fd, 4) = users; - WFIFOSET (char_fd, 6 + 4 * users); -} - -/*========================================== - * timer関数 - * char鯖との接続を確認し、もし切れていたら再度接続する - *------------------------------------------ - */ -void check_connect_char_server (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - if (char_fd <= 0 || session[char_fd] == NULL) - { - printf ("Attempt to connect to char-server...\n"); - chrif_state = 0; - if ((char_fd = make_connection (char_ip, char_port)) < 0) - return; - session[char_fd]->func_parse = chrif_parse; - realloc_fifo (char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - - chrif_connect (char_fd); - } -} - -/*========================================== - * - *------------------------------------------ - */ -int do_init_chrif (void) -{ -// add_timer_func_list (check_connect_char_server, "check_connect_char_server"); -// add_timer_func_list (send_users_tochar, "send_users_tochar"); - add_timer_interval (gettick () + 1000, check_connect_char_server, 0, 0, - 10 * 1000); - add_timer_interval (gettick () + 1000, send_users_tochar, 0, 0, 5 * 1000); - - return 0; -} diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp new file mode 100644 index 0000000..0223e67 --- /dev/null +++ b/src/map/chrif.cpp @@ -0,0 +1,1307 @@ +// $Id: chrif.c,v 1.6 2004/09/25 11:39:17 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef LCCWIN32 +#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/socket.hpp" +#include "../common/timer.hpp" +#include "map.hpp" +#include "battle.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "../common/nullpo.hpp" +#include "itemdb.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +static const int packet_len_table[0x20] = { + 60, 3, 10, 27, 22, -1, 6, -1, // 2af8-2aff + 6, -1, 18, 7, -1, 49, 44, 0, // 2b00-2b07 + 6, 30, -1, 10, 86, 7, 44, 34, // 2b08-2b0f + -1, -1, 10, 6, 11, -1, 0, 0, // 2b10-2b17 +}; + +int char_fd; +int srvinfo; +static char char_ip_str[16]; +static int char_ip; +static int char_port = 6121; +static char userid[24], passwd[24]; +static int chrif_state; + +// 設定ファイル読み込み関係 +/*========================================== + * + *------------------------------------------ + */ +void chrif_setuserid (char *id) +{ + strncpy (userid, id, sizeof(userid)-1); + userid[sizeof(userid)-1] = '\0'; +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setpasswd (char *pwd) +{ + strncpy (passwd, pwd, sizeof(passwd)-1); + passwd[sizeof(passwd)-1] = '\0'; +} + +char *chrif_getpasswd (void) +{ + return passwd; +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setip (char *ip) +{ + strncpy (char_ip_str, ip, sizeof(char_ip_str)-1); + char_ip_str[sizeof(char_ip_str)-1] = '\0'; + char_ip = inet_addr (char_ip_str); +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setport (int port) +{ + char_port = port; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_isconnect (void) +{ + return chrif_state == 2; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_save (struct map_session_data *sd) +{ + nullpo_retr (-1, sd); + + if (char_fd < 0) + return -1; + + pc_makesavestatus (sd); + + WFIFOW (char_fd, 0) = 0x2b01; + WFIFOW (char_fd, 2) = sizeof (sd->status) + 12; + WFIFOL (char_fd, 4) = sd->bl.id; + WFIFOL (char_fd, 8) = sd->char_id; + memcpy (WFIFOP (char_fd, 12), &sd->status, sizeof (sd->status)); + WFIFOSET (char_fd, WFIFOW (char_fd, 2)); + + //For data sync + if (sd->state.storage_flag == 1) + storage_storage_save (sd->status.account_id, 0); + else if (sd->state.storage_flag == 2) + storage_guild_storagesave (sd->status.account_id, sd->status.guild_id, + 0); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connect (int fd) +{ + WFIFOW (fd, 0) = 0x2af8; + memcpy (WFIFOP (fd, 2), userid, 24); + memcpy (WFIFOP (fd, 26), passwd, 24); + 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; + + WFIFOW (fd, 0) = 0x2afa; + for (i = 0; i < map_num; i++) + if (map[i].alias[0] != '\0') // [MouseJstr] map aliasing + memcpy (WFIFOP (fd, 4 + i * 16), map[i].alias, 16); + else + memcpy (WFIFOP (fd, 4 + i * 16), map[i].name, 16); + WFIFOW (fd, 2) = 4 + i * 16; + WFIFOSET (fd, WFIFOW (fd, 2)); + + return 0; +} + +/*========================================== + * マップ受信 + *------------------------------------------ + */ +int chrif_recvmap (int fd) +{ + int i, j, ip, port; + unsigned char *p = (unsigned char *) &ip; + + if (chrif_state < 2) // まだ準備中 + return -1; + + ip = RFIFOL (fd, 4); + port = RFIFOW (fd, 8); + for (i = 10, j = 0; i < RFIFOW (fd, 2); i += 16, j++) + { + map_setipport (RFIFOP (fd, i), ip, port); +// if (battle_config.etc_log) +// printf("recv map %d %s\n", j, RFIFOP(fd,i)); + } + if (battle_config.etc_log) + printf ("recv map on %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, char *name, int x, + int y, int ip, short port) +{ + int i, s_ip; + + nullpo_retr (-1, sd); + + 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; + } + + 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; + memcpy (WFIFOP (char_fd, 18), name, 16); + WFIFOW (char_fd, 34) = x; + WFIFOW (char_fd, 36) = y; + WFIFOL (char_fd, 38) = ip; + WFIFOL (char_fd, 42) = port; + WFIFOB (char_fd, 44) = sd->status.sex; + WFIFOL (char_fd, 45) = s_ip; + WFIFOSET (char_fd, 49); + + return 0; +} + +/*========================================== + * マップ鯖間移動ack + *------------------------------------------ + */ +int chrif_changemapserverack (int fd) +{ + struct map_session_data *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) + printf ("map server change failed.\n"); + pc_authfail (sd->fd); + return 0; + } + clif_changemapserver (sd, RFIFOP (fd, 18), RFIFOW (fd, 34), + RFIFOW (fd, 36), RFIFOL (fd, 38), RFIFOW (fd, 42)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connectack (int fd) +{ + if (RFIFOB (fd, 2)) + { + printf ("Connected to char-server failed %d.\n", RFIFOB (fd, 2)); + exit (1); + } + printf ("Connected to char-server (connection #%d).\n", fd); + chrif_state = 1; + + chrif_sendmap (fd); + + printf ("chrif: OnCharIfInit event done. (%d events)\n", + npc_event_doall ("OnCharIfInit")); + printf ("chrif: OnInterIfInit event done. (%d events)\n", + npc_event_doall ("OnInterIfInit")); + + // <Agit> Run Event [AgitInit] +// printf("NPC_Event:[OnAgitInit] do (%d) events (Agit Initialize).\n", npc_event_doall("OnAgitInit")); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_sendmapack (int fd) +{ + if (RFIFOB (fd, 2)) + { + printf ("chrif : send map list to char server failed %d\n", + RFIFOB (fd, 2)); + exit (1); + } + + memcpy (wisp_server_name, RFIFOP (fd, 3), 24); + + chrif_state = 2; + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_authreq (struct map_session_data *sd) +{ + int i; + + nullpo_retr (-1, sd); + + if (!sd || !char_fd || !sd->bl.id || !sd->login_id1) + return -1; + + for (i = 0; i < fd_max; i++) + if (session[i] && session[i]->session_data == sd) + { + WFIFOW (char_fd, 0) = 0x2afc; + WFIFOL (char_fd, 2) = sd->bl.id; + WFIFOL (char_fd, 6) = sd->char_id; + WFIFOL (char_fd, 10) = sd->login_id1; + WFIFOL (char_fd, 14) = sd->login_id2; + WFIFOL (char_fd, 18) = session[i]->client_addr.sin_addr.s_addr; + WFIFOSET (char_fd, 22); + break; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_charselectreq (struct map_session_data *sd) +{ + int i, s_ip; + + nullpo_retr (-1, sd); + + if (!sd || !char_fd || !sd->bl.id || !sd->login_id1) + return -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; + } + + 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; + + 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) + printf ("chrif_changegm: account: %d, password: '%s'.\n", id, pass); + + 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) + printf + ("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", + id, actual_email, new_email); + + 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) +{ + 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, 24); + 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; + } + printf ("chrif : sended 0x2b0e\n"); + WFIFOSET (char_fd, 44); + + 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[24]; + + acc = RFIFOL (fd, 2); // account_id of who has asked (-1 if nobody) + memcpy (player_name, RFIFOP (fd, 6), sizeof (player_name)); + player_name[sizeof (player_name) - 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 + printf ("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; + + acc = RFIFOL (fd, 2); + level = RFIFOL (fd, 6); + + sd = map_id2sd (acc); + + if (battle_config.etc_log) + printf ("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; + struct pc_base_job s_class; + + acc = RFIFOL (fd, 2); + sex = RFIFOL (fd, 6); + if (battle_config.etc_log) + printf ("chrif_changedsex %d.\n", acc); + sd = map_id2sd (acc); + if (acc > 0) + { + if (sd != NULL && sd->status.sex != sex) + { + s_class = pc_calc_base_job (sd->status.pc_class); + 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, 0); + } + // reset skill of some job + if (s_class.job == 19 || s_class.job == 4020 + || s_class.job == 4042 || s_class.job == 20 + || s_class.job == 4021 || s_class.job == 4043) + { + + clif_updatestatus (sd, SP_SKILLPOINT); + // change job if necessary + if (s_class.job == 20 || s_class.job == 4021 + || s_class.job == 4043) + sd->status.pc_class -= 1; + else if (s_class.job == 19 || s_class.job == 4020 + || s_class.job == 4042) + sd->status.pc_class += 1; + } + // save character + chrif_save (sd); + 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 disconexion by the server)..."); + clif_setwaitclose (sd->fd); // forced to disconnect for the change + } + } + else + { + if (sd != NULL) + { + printf ("chrif_changedsex failed.\n"); + } + } + + return 0; +} + +/*========================================== + * アカウント変数保存要求 + *------------------------------------------ + */ +int chrif_saveaccountreg2 (struct map_session_data *sd) +{ + int p, j; + nullpo_retr (-1, sd); + + p = 8; + for (j = 0; j < sd->status.account_reg2_num; j++) + { + struct global_reg *reg = &sd->status.account_reg2[j]; + if (reg->str[0] && reg->value != 0) + { + memcpy (WFIFOP (char_fd, p), reg->str, 32); + WFIFOL (char_fd, p + 32) = reg->value; + p += 36; + } + } + WFIFOW (char_fd, 0) = 0x2b10; + WFIFOW (char_fd, 2) = p; + WFIFOL (char_fd, 4) = sd->bl.id; + WFIFOSET (char_fd, p); + + return 0; +} + +/*========================================== + * アカウント変数通知 + *------------------------------------------ + */ +int chrif_accountreg2 (int fd) +{ + int j, p; + struct map_session_data *sd; + + if ((sd = map_id2sd (RFIFOL (fd, 4))) == NULL) + return 1; + + for (p = 8, j = 0; p < RFIFOW (fd, 2) && j < ACCOUNT_REG2_NUM; + p += 36, j++) + { + memcpy (sd->status.account_reg2[j].str, RFIFOP (fd, p), 32); + sd->status.account_reg2[j].value = RFIFOL (fd, p + 32); + } + sd->status.account_reg2_num = j; +// printf("chrif: accountreg2\n"); + + return 0; +} + +/*========================================== + * Divorce request from char server + * triggered on account deletion or as an + * ack from a map-server divorce request + *------------------------------------------ + */ +int chrif_divorce (int char_id, int partner_id) +{ + struct map_session_data *sd = NULL; + + if (!char_id || !partner_id) + return 0; + + sd = map_nick2sd (map_charid2nick (char_id)); + if (sd && sd->status.partner_id == partner_id) + { + sd->status.partner_id = 0; + + if (sd->npc_flags.divorce) + { + sd->npc_flags.divorce = 0; + map_scriptcont (sd, sd->npc_id); + } + } + + nullpo_retr (0, sd = map_nick2sd (map_charid2nick (partner_id))); + if (sd->status.partner_id == char_id) + sd->status.partner_id = 0; + + return 0; +} + +/*========================================== + * Tell character server someone is divorced + * Needed to divorce when partner is not connected to map server + *------------------------------------- + */ +int chrif_send_divorce (int char_id) +{ + if (char_fd < 0) + return -1; + + WFIFOW (char_fd, 0) = 0x2b16; + WFIFOL (char_fd, 2) = char_id; + WFIFOSET (char_fd, 6); + 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; + + acc = RFIFOL (fd, 2); + if (battle_config.etc_log) + printf ("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) + printf ("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; + + acc = RFIFOL (fd, 2); + if (battle_config.etc_log) + printf ("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", + gmtime (×tamp)); + clif_displaymessage (sd->fd, tmpstr); + } + clif_setwaitclose (sd->fd); // forced to disconnect for the change + } + } + else + { + if (sd != NULL) + printf ("chrif_accountban failed - player not online.\n"); + } + + return 0; +} + +/*========================================== + * Receiving GM accounts and their levels from char-server by [Yor] + *------------------------------------------ + */ +int chrif_recvgmaccounts (int fd) +{ + printf ("From login-server: receiving of %d GM accounts information.\n", + pc_read_gm_account (fd)); + + return 0; +} + +/*========================================== + * Request to reload GM accounts and their levels: send to char-server by [Yor] + *------------------------------------------ + */ +int chrif_reloadGMdb (void) +{ + + WFIFOW (char_fd, 0) = 0x2af7; + WFIFOSET (char_fd, 2); + + 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; + + 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 + { + 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) +{ + if (char_fd < 0) + return -1; + + WFIFOW (char_fd, 0) = 0x2b17; + WFIFOL (char_fd, 2) = sd->status.char_id; + WFIFOSET (char_fd, 6); + + return 0; +} + +/*======================================== + * Map item IDs + *---------------------------------------- + */ + +static void ladmin_itemfrob_fix_item (int source, int dest, struct item *item) +{ + if (item && item->nameid == source) + { + item->nameid = dest; + item->equip = 0; + } +} + +static int ladmin_itemfrob_c2 (struct block_list *bl, int source_id, + int dest_id) +{ +#define IFIX(v) if (v == source_id) {v = dest_id; } +#define FIX(item) ladmin_itemfrob_fix_item(source_id, dest_id, &item) + + if (!bl) + return 0; + + switch (bl->type) + { + case BL_PC: + { + struct map_session_data *pc = (struct map_session_data *) bl; + struct storage *stor = account2storage2 (pc->status.account_id); + int j; + + for (j = 0; j < MAX_INVENTORY; j++) + IFIX (pc->status.inventory[j].nameid); + for (j = 0; j < MAX_CART; j++) + IFIX (pc->status.cart[j].nameid); + IFIX (pc->status.weapon); + IFIX (pc->status.shield); + IFIX (pc->status.head_top); + IFIX (pc->status.head_mid); + IFIX (pc->status.head_bottom); + + if (stor) + for (j = 0; j < stor->storage_amount; j++) + FIX (stor->storage_[j]); + + for (j = 0; j < MAX_INVENTORY; j++) + { + struct item_data *item = pc->inventory_data[j]; + if (item && item->nameid == source_id) + { + item->nameid = dest_id; + if (item->equip) + pc_unequipitem (pc, j, 0); + item->nameid = dest_id; + } + } + + break; + } + + case BL_MOB: + { + struct mob_data *mob = (struct mob_data *) bl; + int i; + for (i = 0; i < mob->lootitem_count; i++) + FIX (mob->lootitem[i]); + break; + } + + case BL_ITEM: + { + struct flooritem_data *item = (struct flooritem_data *) bl; + FIX (item->item_data); + break; + } + } +#undef FIX +#undef IFIX + + return 0; +} + +int ladmin_itemfrob_c (struct block_list *bl, va_list va_args) +{ + int source_id = va_arg (va_args, int); + int dest_id = va_arg (va_args, int); + return ladmin_itemfrob_c2 (bl, source_id, dest_id); +} + +void ladmin_itemfrob (int fd) +{ + int source_id = RFIFOL (fd, 2); + int dest_id = RFIFOL (fd, 6); + struct block_list *bl = (struct block_list *) map_get_first_session (); + + // flooritems + map_foreachobject (ladmin_itemfrob_c, 0 /* any object */ , source_id, + dest_id); + + // player characters (and, hopefully, mobs) + while (bl->next) + { + ladmin_itemfrob_c2 (bl, source_id, dest_id); + bl = bl->next; + } +} + +/*========================================== + * + *------------------------------------------ + */ +void 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) + { + printf + ("Map-server can't connect to char-server (connection #%d).\n", + fd); + char_fd = -1; + } + close (fd); + delete_session (fd); + return; + } + + while (RFIFOREST (fd) >= 2) + { + 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; // intifで処理したが、データが足りない + + session[fd]->eof = 1; + return; + } + packet_len = packet_len_table[cmd - 0x2af8]; + if (packet_len == -1) + { + if (RFIFOREST (fd) < 4) + return; + packet_len = RFIFOW (fd, 2); + } + if (RFIFOREST (fd) < packet_len) + return; + + switch (cmd) + { + case 0x2af9: + chrif_connectack (fd); + break; + case 0x2afa: + ladmin_itemfrob (fd); + break; + case 0x2afb: + chrif_sendmapack (fd); + break; + case 0x2afd: + pc_authok (RFIFOL (fd, 4), RFIFOL (fd, 8), + (time_t) RFIFOL (fd, 12), RFIFOW (fd, 16), + (struct mmo_charstatus *) RFIFOP (fd, 18)); + break; + case 0x2afe: + pc_authfail (RFIFOL (fd, 2)); + break; + case 0x2b00: + map_setusers (RFIFOL (fd, 2)); + break; + case 0x2b03: + clif_charselectok (RFIFOL (fd, 2)); + break; + case 0x2b04: + chrif_recvmap (fd); + break; + case 0x2b06: + chrif_changemapserverack (fd); + break; + case 0x2b09: + map_addchariddb (RFIFOL (fd, 2), 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 0x2b11: + chrif_accountreg2 (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; + + default: + if (battle_config.error_log) + printf ("chrif_parse : unknown packet %d %d\n", fd, + RFIFOW (fd, 0)); + session[fd]->eof = 1; + return; + } + RFIFOSKIP (fd, packet_len); + } +} + +/*========================================== + * timer関数 + * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る + *------------------------------------------ + */ +void send_users_tochar (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int users = 0, i; + struct map_session_data *sd; + + if (char_fd <= 0 || session[char_fd] == NULL) + return; + + WFIFOW (char_fd, 0) = 0x2aff; + 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 + || sd->state.shroud_active + || (sd->status.option & OPTION_HIDE)) && pc_isGM (sd))) + { + WFIFOL (char_fd, 6 + 4 * users) = sd->status.char_id; + users++; + } + } + WFIFOW (char_fd, 2) = 6 + 4 * users; + WFIFOW (char_fd, 4) = users; + WFIFOSET (char_fd, 6 + 4 * users); +} + +/*========================================== + * timer関数 + * char鯖との接続を確認し、もし切れていたら再度接続する + *------------------------------------------ + */ +void check_connect_char_server (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + if (char_fd <= 0 || session[char_fd] == NULL) + { + printf ("Attempt to connect to char-server...\n"); + chrif_state = 0; + if ((char_fd = make_connection (char_ip, char_port)) < 0) + return; + session[char_fd]->func_parse = chrif_parse; + realloc_fifo (char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + + chrif_connect (char_fd); + } +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_chrif (void) +{ +// add_timer_func_list (check_connect_char_server, "check_connect_char_server"); +// add_timer_func_list (send_users_tochar, "send_users_tochar"); + add_timer_interval (gettick () + 1000, check_connect_char_server, 0, 0, + 10 * 1000); + add_timer_interval (gettick () + 1000, send_users_tochar, 0, 0, 5 * 1000); + + return 0; +} diff --git a/src/map/chrif.h b/src/map/chrif.h deleted file mode 100644 index e891be7..0000000 --- a/src/map/chrif.h +++ /dev/null @@ -1,36 +0,0 @@ -// $Id: chrif.h,v 1.3 2004/09/25 11:39:17 MouseJstr Exp $ -#ifndef _CHRIF_H_ -#define _CHRIF_H_ - -void chrif_setuserid (char *); -void chrif_setpasswd (char *); -char *chrif_getpasswd (void); - -void chrif_setip (char *); -void chrif_setport (int); - -int chrif_isconnect (void); - -int chrif_authreq (struct map_session_data *); -int chrif_save (struct map_session_data *); -int chrif_charselectreq (struct map_session_data *); - -int chrif_changemapserver (struct map_session_data *sd, char *name, 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_saveaccountreg2 (struct map_session_data *sd); -int chrif_reloadGMdb (void); -int chrif_ragsrvinfo (int base_rate, int job_rate, int drop_rate); -int chrif_char_offline (struct map_session_data *sd); -int chrif_send_divorce (int char_id); - -int do_init_chrif (void); - -#endif diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp new file mode 100644 index 0000000..5b0341e --- /dev/null +++ b/src/map/chrif.hpp @@ -0,0 +1,36 @@ +// $Id: chrif.h,v 1.3 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef CHRIF_HPP +#define CHRIF_HPP + +void chrif_setuserid (char *); +void chrif_setpasswd (char *); +char *chrif_getpasswd (void); + +void chrif_setip (char *); +void chrif_setport (int); + +int chrif_isconnect (void); + +int chrif_authreq (struct map_session_data *); +int chrif_save (struct map_session_data *); +int chrif_charselectreq (struct map_session_data *); + +int chrif_changemapserver (struct map_session_data *sd, char *name, 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_saveaccountreg2 (struct map_session_data *sd); +int chrif_reloadGMdb (void); +int chrif_ragsrvinfo (int base_rate, int job_rate, int drop_rate); +int chrif_char_offline (struct map_session_data *sd); +int chrif_send_divorce (int char_id); + +int do_init_chrif (void); + +#endif diff --git a/src/map/clif.c b/src/map/clif.c deleted file mode 100644 index 30348fe..0000000 --- a/src/map/clif.c +++ /dev/null @@ -1,10332 +0,0 @@ -// $Id: clif.c 164 2004-10-01 16:46:58Z $ - -#define DUMP_UNKNOWN_PACKET 1 - -#include <stdio.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <sys/types.h> -#ifdef LCCWIN32 -#include <winsock.h> -#else -#include <unistd.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/version.h" -#include "../common/nullpo.h" -#include "../common/md5calc.h" -#include "../common/mt_rand.h" - -#include "atcommand.h" -#include "battle.h" -#include "chat.h" -#include "chrif.h" -#include "clif.h" -#include "guild.h" -#include "intif.h" -#include "itemdb.h" -#include "magic.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "storage.h" -#include "tmw.h" -#include "trade.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define STATE_BLIND 0x10 -#define EMOTE_IGNORED 0x0e - -static const int packet_len_table[0x220] = { - 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, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3, 108, 3, 2, - 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, -//#0x0080 - 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, // 0x8b unknown... size 2 or 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, 6, 2, 27, - 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, - 90, 86, 24, 6, 30, 102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6, - 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, 0, 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, 19, 10, -1, 24, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// local define -enum -{ - ALL_CLIENT, - ALL_SAMEMAP, - AREA, - AREA_WOS, - AREA_WOC, - AREA_WOSC, - AREA_CHAT_WOC, - CHAT, - CHAT_WOS, - 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 -}; - -#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] = (x0)>>2; __p[1] = ((x0)<<6) | (((y0)>>4)&0x3f); __p[2] = ((y0)<<4) | (((x1)>>6)&0x0f); __p[3]=((x1)<<2) | (((y1)>>8)&0x03); __p[4]=(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); } - -static char map_ip_str[16]; -static in_addr_t map_ip; -static int map_port = 5121; -int map_fd; -char talkie_mes[80]; - -/*========================================== - * map鯖のip設定 - *------------------------------------------ - */ -void clif_setip (char *ip) -{ - memcpy (map_ip_str, ip, 16); - map_ip = inet_addr (map_ip_str); -} - -/*========================================== - * 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; -} - -/*========================================== - * - *------------------------------------------ - */ -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 - && 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), ...) -{ - 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) && sd - && sd->state.auth) - func (sd, ap); - } - va_end (ap); - return 0; -} - -static int is_deaf (struct block_list *bl) -{ - struct map_session_data *sd = (struct map_session_data *) bl; - if (!bl || bl->type != BL_PC) - return 0; - return sd->special_state.deaf; -} - -static void clif_emotion_towards (struct block_list *bl, - struct block_list *target, int type); - -static char *clif_validate_chat (struct map_session_data *sd, int type, - char **message, size_t *message_len); - -/*========================================== - * clif_sendでAREA*指定時用 - *------------------------------------------ - */ -int clif_send_sub (struct block_list *bl, va_list ap) -{ - unsigned char *buf; - int len; - struct block_list *src_bl; - int type; - struct map_session_data *sd; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, sd = (struct map_session_data *) bl); - - 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 && bl == src_bl) - return 0; - break; - - case AREA_CHAT_WOC: - if (is_deaf (bl) - && !(bl->type == BL_PC - && pc_isGM ((struct map_session_data *) src_bl))) - { - clif_emotion_towards (src_bl, bl, EMOTE_IGNORED); - return 0; - } - /* fall through... */ - case AREA_WOC: - if ((sd && sd->chatID) || (bl && bl == src_bl)) - return 0; - - break; - case AREA_WOSC: - if ((sd) && sd->chatID - && sd->chatID == ((struct map_session_data *) src_bl)->chatID) - return 0; - break; - } - - if (session[sd->fd] != NULL) - { - 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_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - 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; - struct chat_data *cd; - struct party *p = NULL; - struct guild *g = NULL; - int x0 = 0, x1 = 0, y0 = 0, y1 = 0; - - if (type != ALL_CLIENT) - { - nullpo_retr (0, bl); - - if (bl->type == BL_PC) - { - struct map_session_data *sd = (struct map_session_data *) bl; - if (sd->status.option & OPTION_INVISIBILITY) - { - // Obscure hidden GMs - - switch (type) - { - case AREA: - case AREA_WOC: - type = SELF; - break; - - case AREA_WOS: - case AREA_WOSC: - return 1; - - default: - break; - } - } - } - } - - 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_len_table[RBUFW (buf, 0)]) - { // packet must exist - 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_len_table[RBUFW (buf, 0)]) - { // packet must exist - 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), - bl->y - (AREA_SIZE), - bl->x + (AREA_SIZE), - bl->y + (AREA_SIZE), BL_PC, buf, len, bl, - AREA_CHAT_WOC); - break; - case CHAT: - case CHAT_WOS: - cd = (struct chat_data *) bl; - if (bl->type == BL_PC) - { - sd = (struct map_session_data *) bl; - cd = (struct chat_data *) map_id2bl (sd->chatID); - } - else if (bl->type != BL_CHAT) - break; - if (cd == NULL) - break; - for (i = 0; i < cd->users; i++) - { - if (type == CHAT_WOS - && cd->usersd[i] == (struct map_session_data *) bl) - continue; - if (packet_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (cd->usersd[i]->fd, 0), buf, len); - WFIFOSET (cd->usersd[i]->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 (bl->type == BL_PC) - { - sd = (struct map_session_data *) bl; - if (sd->partyspy > 0) - { - p = party_search (sd->partyspy); - } - else - { - if (sd->status.party_id > 0) - 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->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_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - } - } - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL - && sd->state.auth) - { - if (sd->partyspy == p->party_id) - { - if (packet_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - } - } - } - } - break; - case SELF: - sd = (struct map_session_data *) bl; - if (packet_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - break; - -/* New definitions for guilds [Valaris] */ - - 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: - case GUILD_WOS: - if (bl && bl->type == BL_PC) - { // guildspy [Syrus22] - sd = (struct map_session_data *) bl; - if (sd->guildspy > 0) - { - g = guild_search (sd->guildspy); - } - else - { - if (sd->status.guild_id > 0) - 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 (type == GUILD_WOS && sd->bl.id == bl->id) - continue; - if (packet_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - } - } - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL - && sd->state.auth) - { - if (sd->guildspy == g->guild_id) - { - if (packet_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - } - } - } - } - break; - case GUILD_SAMEMAP: - case GUILD_SAMEMAP_WOS: - if (bl->type == BL_PC) - { - sd = (struct map_session_data *) bl; - if (sd->status.guild_id > 0) - 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->bl.id == bl->id && (type == GUILD_WOS || - type == GUILD_SAMEMAP_WOS - || type == - GUILD_AREA_WOS)) - continue; - if (type != GUILD && type != GUILD_WOS && bl->m != sd->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_len_table[RBUFW (buf, 0)]) - { // packet must exist - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); - } - } - } - } - break; - - default: - if (battle_config.error_log) - printf ("clif_send まだ作ってないよー\n"); - return -1; - } - - return 0; -} - -// -// パケット作って送信 -// -/*========================================== - * - *------------------------------------------ - */ -int clif_authok (struct map_session_data *sd) -{ - int fd; - - nullpo_retr (0, sd); - - if (!sd) - return 0; - - if (!sd->fd) - return 0; - - fd = sd->fd; - - 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]) - return 0; - - WFIFOW (fd, 0) = 0x81; - WFIFOL (fd, 2) = type; - WFIFOSET (fd, packet_len_table[0x81]); - - clif_setwaitclose (fd); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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; - WFIFOW (fd, 0) = 0xb3; - WFIFOB (fd, 2) = 1; - WFIFOSET (fd, packet_len_table[0xb3]); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static int clif_set009e (struct flooritem_data *fitem, 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) -{ - 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 - { - 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; - if (type == 9) - { - WBUFB (buf, 6) = 0; - clif_send (buf, packet_len_table[0x80], bl, AREA); - } - else - { - WBUFB (buf, 6) = type; - clif_send (buf, packet_len_table[0x80], bl, - type == 1 ? AREA : AREA_WOS); - } - - return 0; -} - -static void clif_clearchar_delay_sub (timer_id tid, tick_t tick, custom_id_t id, - custom_data_t data) -{ - struct block_list *bl = (struct block_list *) id; - - clif_clearchar (bl, data); - map_freeblock (bl); -} - -int clif_clearchar_delay (unsigned int tick, struct block_list *bl, int type) -{ - struct block_list *tmpbl; - CREATE (tmpbl, struct block_list, 1); - - memcpy (tmpbl, bl, sizeof (struct block_list)); - add_timer (tick, clif_clearchar_delay_sub, (custom_id_t) tmpbl, 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; - memcpy (WFIFOP (fd, 0), buf, 7); - WFIFOSET (fd, packet_len_table[0x80]); - - return 0; -} - -/* -static int current_weapon(struct map_session_data *sd) -{ - if (sd->attack_spell_override) - return sd->attack_spell_look_override; - else { - return sd->status.weapon; - } -} -*/ - -/*========================================== - * - *------------------------------------------ - */ -static int clif_set0078 (struct map_session_data *sd, unsigned char *buf) -{ - int level = 0; - - nullpo_retr (0, sd); - - if (sd->disguise > 23 && sd->disguise < 4001) - { // mob disguises [Valaris] - WBUFW (buf, 0) = 0x78; - WBUFL (buf, 2) = sd->bl.id; - WBUFW (buf, 6) = battle_get_speed (&sd->bl); - WBUFW (buf, 8) = sd->opt1; - WBUFW (buf, 10) = sd->opt2; - WBUFW (buf, 12) = sd->status.option; - WBUFW (buf, 14) = sd->disguise; - 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) = 0; - WBUFW (buf, 52) = - ((level = - battle_get_lv (&sd->bl)) > - battle_config.max_lv) ? battle_config.max_lv : level; - - return packet_len_table[0x78]; - } - - 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) = sd->status.option; - WBUFW (buf, 14) = sd->view_class; - WBUFW (buf, 16) = sd->status.hair; - if (sd->attack_spell_override) - WBUFB (buf, 18) = sd->attack_spell_look_override; - else - { - if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] - && sd->view_class != 22) - { - if (sd->inventory_data[sd->equip_index[9]]->view_id > 0) - WBUFW (buf, 18) = - sd->inventory_data[sd->equip_index[9]]->view_id; - else - WBUFW (buf, 18) = - sd->status.inventory[sd->equip_index[9]].nameid; - } - else - WBUFW (buf, 18) = 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 != 22) - { - if (sd->inventory_data[sd->equip_index[8]]->view_id > 0) - WBUFW (buf, 20) = sd->inventory_data[sd->equip_index[8]]->view_id; - else - WBUFW (buf, 20) = sd->status.inventory[sd->equip_index[8]].nameid; - } - 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; - WBUFW (buf, 49) = (pc_isGM (sd) == 60 || pc_isGM (sd) == 99) ? 0x80 : 0; - WBUFB (buf, 51) = sd->state.dead_sit; - WBUFW (buf, 52) = 0; - - return packet_len_table[0x1d8]; -} - -/*========================================== - * - *------------------------------------------ - */ -static int clif_set007b (struct map_session_data *sd, unsigned char *buf) -{ - int level = 0; - nullpo_retr (0, sd); - - if (sd->disguise > 23 && sd->disguise < 4001) - { // mob disguises [Valaris] - WBUFW (buf, 0) = 0x7b; - WBUFL (buf, 2) = sd->bl.id; - WBUFW (buf, 6) = battle_get_speed (&sd->bl); - WBUFW (buf, 8) = sd->opt1; - WBUFW (buf, 10) = sd->opt2; - WBUFW (buf, 12) = sd->status.option; - WBUFW (buf, 14) = sd->disguise; - WBUFL (buf, 22) = gettick (); - WBUFW (buf, 46) = 0; - WBUFB (buf, 48) = 0; - WBUFPOS2 (buf, 50, sd->bl.x, sd->bl.y, sd->to_x, sd->to_y); - WBUFB (buf, 55) = 0; - WBUFB (buf, 56) = 5; - WBUFB (buf, 57) = 5; - WBUFW (buf, 58) = - ((level = - battle_get_lv (&sd->bl)) > - battle_config.max_lv) ? battle_config.max_lv : level; - - return packet_len_table[0x7b]; - } - - 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) = sd->status.option; - WBUFW (buf, 14) = sd->view_class; - WBUFW (buf, 16) = sd->status.hair; - if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] - && sd->view_class != 22) - { - if (sd->inventory_data[sd->equip_index[9]]->view_id > 0) - WBUFW (buf, 18) = sd->inventory_data[sd->equip_index[9]]->view_id; - else - WBUFW (buf, 18) = sd->status.inventory[sd->equip_index[9]].nameid; - } - else - WBUFW (buf, 18) = 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 != 22) - { - if (sd->inventory_data[sd->equip_index[8]]->view_id > 0) - WBUFW (buf, 20) = sd->inventory_data[sd->equip_index[8]]->view_id; - else - WBUFW (buf, 20) = sd->status.inventory[sd->equip_index[8]].nameid; - } - 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); - WBUFW (buf, 55) = pc_isGM (sd) == 60 ? 0x80 : 0; - WBUFB (buf, 57) = 5; - WBUFW (buf, 58) = 0; - - return packet_len_table[0x1da]; -} - -/*========================================== - * クラスチェンジ typeはMobの場合は1で他は0? - *------------------------------------------ - */ -int clif_npc_class_change (struct block_list *bl, int npc_class, int type) -{ - char buf[16]; - - nullpo_retr (0, bl); - - if (npc_class >= MAX_PC_CLASS) - { - WBUFW (buf, 0) = 0x1b0; - WBUFL (buf, 2) = bl->id; - WBUFB (buf, 6) = type; - WBUFL (buf, 7) = npc_class; - - clif_send (buf, packet_len_table[0x1b0], bl, AREA); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_mob_class_change (struct mob_data *md, int class_) -{ - 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; - - memset (buf, 0, packet_len_table[0x78]); - - nullpo_retr (0, md); - - WBUFW (buf, 0) = 0x78; - WBUFL (buf, 2) = md->bl.id; - WBUFW (buf, 6) = battle_get_speed (&md->bl); - WBUFW (buf, 8) = md->opt1; - WBUFW (buf, 10) = md->opt2; - WBUFW (buf, 12) = md->option; - WBUFW (buf, 14) = mob_get_viewclass (md->mob_class); - if ((mob_get_viewclass (md->mob_class) <= 23) - || (mob_get_viewclass (md->mob_class) == 812) - || (mob_get_viewclass (md->mob_class) >= 4001)) - { - WBUFW (buf, 12) |= mob_db[md->mob_class].option; - WBUFW (buf, 16) = mob_get_hair (md->mob_class); - WBUFW (buf, 18) = mob_get_weapon (md->mob_class); - WBUFW (buf, 20) = mob_get_head_buttom (md->mob_class); - WBUFW (buf, 22) = mob_get_shield (md->mob_class); - WBUFW (buf, 24) = mob_get_head_top (md->mob_class); - WBUFW (buf, 26) = mob_get_head_mid (md->mob_class); - WBUFW (buf, 28) = mob_get_hair_color (md->mob_class); - WBUFW (buf, 30) = mob_get_clothes_color (md->mob_class); //Add for player monster dye - Valaris - WBUFB (buf, 45) = mob_get_sex (md->mob_class); - } - - if (md->mob_class >= 1285 && md->mob_class <= 1287) - { // Added guardian emblems [Valaris] - struct guild *g; - struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); - if (gc && gc->guild_id > 0) - { - g = guild_search (gc->guild_id); - if (g) - { - WBUFL (buf, 26) = gc->guild_id; - WBUFL (buf, 22) = g->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) = - ((level = - battle_get_lv (&md->bl)) > - battle_config.max_lv) ? battle_config.max_lv : level; - - return packet_len_table[0x78]; -} - -/*========================================== - * MOB表示2 - *------------------------------------------ - */ -static int clif_mob007b (struct mob_data *md, unsigned char *buf) -{ - int level; - - memset (buf, 0, packet_len_table[0x7b]); - - nullpo_retr (0, md); - - WBUFW (buf, 0) = 0x7b; - WBUFL (buf, 2) = md->bl.id; - WBUFW (buf, 6) = battle_get_speed (&md->bl); - WBUFW (buf, 8) = md->opt1; - WBUFW (buf, 10) = md->opt2; - WBUFW (buf, 12) = md->option; - WBUFW (buf, 14) = mob_get_viewclass (md->mob_class); - if ((mob_get_viewclass (md->mob_class) < 24) - || (mob_get_viewclass (md->mob_class) > 4000)) - { - WBUFW (buf, 12) |= mob_db[md->mob_class].option; - WBUFW (buf, 16) = mob_get_hair (md->mob_class); - WBUFW (buf, 18) = mob_get_weapon (md->mob_class); - WBUFW (buf, 20) = mob_get_head_buttom (md->mob_class); - WBUFL (buf, 22) = gettick (); - WBUFW (buf, 26) = mob_get_shield (md->mob_class); - WBUFW (buf, 28) = mob_get_head_top (md->mob_class); - WBUFW (buf, 30) = mob_get_head_mid (md->mob_class); - WBUFW (buf, 32) = mob_get_hair_color (md->mob_class); - WBUFW (buf, 34) = mob_get_clothes_color (md->mob_class); //Add for player monster dye - Valaris - WBUFB (buf, 49) = mob_get_sex (md->mob_class); - } - else - WBUFL (buf, 22) = gettick (); - - if (md->mob_class >= 1285 && md->mob_class <= 1287) - { // Added guardian emblems [Valaris] - struct guild *g; - struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); - if (gc && gc->guild_id > 0) - { - g = guild_search (gc->guild_id); - if (g) - { - WBUFL (buf, 28) = gc->guild_id; - WBUFL (buf, 24) = g->emblem_id; - } - } - } // End addition - - WBUFPOS2 (buf, 50, md->bl.x, md->bl.y, md->to_x, md->to_y); - WBUFB (buf, 56) = 5; - WBUFB (buf, 57) = 5; - WBUFW (buf, 58) = - ((level = - battle_get_lv (&md->bl)) > - battle_config.max_lv) ? battle_config.max_lv : level; - - return packet_len_table[0x7b]; -} - -/*========================================== - * - *------------------------------------------ - */ -static int clif_npc0078 (struct npc_data *nd, unsigned char *buf) -{ - struct guild *g; - - nullpo_retr (0, nd); - - memset (buf, 0, packet_len_table[0x78]); - - WBUFW (buf, 0) = 0x78; - WBUFL (buf, 2) = nd->bl.id; - WBUFW (buf, 6) = nd->speed; - WBUFW (buf, 14) = nd->npc_class; - if ((nd->npc_class == 722) && (nd->u.scr.guild_id > 0) - && ((g = guild_search (nd->u.scr.guild_id)) != NULL)) - { - WBUFL (buf, 22) = g->emblem_id; - WBUFL (buf, 26) = 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]; -} - -/*========================================== - * - *------------------------------------------ - */ -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) -{ - WFIFOW (fd, 0) = 0x192; - WFIFOW (fd, 2) = x; - WFIFOW (fd, 4) = y; - WFIFOW (fd, 6) = type; - memcpy (WFIFOP (fd, 8), map[m].name, 16); - WFIFOSET (fd, packet_len_table[0x192]); - - return 0; -} - -/* These indices are derived from equip_pos in pc.c and some guesswork */ -static int equip_points[LOOK_LAST + 1] = { - -1, /* 0: base */ - -1, /* 1: hair */ - 9, /* 2: weapon */ - 4, /* 3: head botom -- leg armour */ - 6, /* 4: head top -- hat */ - 5, /* 5: head mid -- torso armour */ - -1, /* 6: hair colour */ - -1, /* 6: clothes colour */ - 8, /* 6: shield */ - 2, /* 9: shoes */ - 3, /* gloves */ - 1, /* cape */ - 7, /* misc1 */ - 0, /* misc2 */ -}; - -/*========================================== - * - *------------------------------------------ - */ -int clif_spawnpc (struct map_session_data *sd) -{ - unsigned char buf[128]; - - nullpo_retr (0, sd); - - if (sd->disguise > 23 && sd->disguise < 4001) - { // mob disguises [Valaris] - clif_clearchar (&sd->bl, 9); - - memset (buf, 0, packet_len_table[0x119]); - - WBUFW (buf, 0) = 0x119; - WBUFL (buf, 2) = sd->bl.id; - WBUFW (buf, 6) = 0; - WBUFW (buf, 8) = 0; - WBUFW (buf, 10) = 0x40; - WBUFB (buf, 12) = 0; - - clif_send (buf, packet_len_table[0x119], &sd->bl, SELF); - - memset (buf, 0, packet_len_table[0x7c]); - - WBUFW (buf, 0) = 0x7c; - WBUFL (buf, 2) = sd->bl.id; - WBUFW (buf, 6) = sd->speed; - WBUFW (buf, 8) = sd->opt1; - WBUFW (buf, 10) = sd->opt2; - 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); - } - - clif_set0078 (sd, buf); - - WBUFW (buf, 0) = 0x1d9; - WBUFW (buf, 51) = 0; - clif_send (buf, packet_len_table[0x1d9], &sd->bl, AREA_WOS); - - 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 (sd->status.pc_class == 13 || sd->status.pc_class == 21 - || sd->status.pc_class == 4014 || sd->status.pc_class == 4022) - pc_setoption (sd, sd->status.option | 0x0020); // [Valaris] - - if ((pc_isriding (sd) && pc_checkskill (sd, KN_RIDING) > 0) - && (sd->status.pc_class == 7 || sd->status.pc_class == 14 - || sd->status.pc_class == 4008 || sd->status.pc_class == 4015)) - pc_setriding (sd); // update peco riders for people upgrading athena [Valaris] - - if (map[sd->bl.m].flag.snow) - clif_specialeffect (&sd->bl, 162, 1); - if (map[sd->bl.m].flag.fog) - clif_specialeffect (&sd->bl, 233, 1); - if (map[sd->bl.m].flag.sakura) - clif_specialeffect (&sd->bl, 163, 1); - if (map[sd->bl.m].flag.leaves) - clif_specialeffect (&sd->bl, 333, 1); - if (map[sd->bl.m].flag.rain) - clif_specialeffect (&sd->bl, 161, 1); - -// clif_changelook_accessories(&sd->bl, NULL); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_spawnnpc (struct npc_data *nd) -{ - unsigned char buf[64]; - int len; - - nullpo_retr (0, nd); - - if (nd->npc_class < 0 || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) - return 0; - - 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->npc_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_spawn_fake_npc_for_player (struct map_session_data *sd, int fake_npc_id) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - if (!fd) - return 0; - - WFIFOW (fd, 0) = 0x7c; - WFIFOL (fd, 2) = fake_npc_id; - WFIFOW (fd, 6) = 0; - WFIFOW (fd, 8) = 0; - WFIFOW (fd, 10) = 0; - WFIFOW (fd, 12) = 0; - WFIFOW (fd, 20) = 127; - WFIFOPOS (fd, 36, sd->bl.x, sd->bl.y); - WFIFOSET (fd, packet_len_table[0x7c]); - - WFIFOW (fd, 0) = 0x78; - WFIFOL (fd, 2) = fake_npc_id; - WFIFOW (fd, 6) = 0; - WFIFOW (fd, 8) = 0; - WFIFOW (fd, 10) = 0; - WFIFOW (fd, 12) = 0; - WFIFOW (fd, 14) = 127; // identifies as NPC - WFIFOW (fd, 20) = 127; - WFIFOPOS (fd, 46, sd->bl.x, sd->bl.y); - WFIFOPOS (fd, 36, sd->bl.x, sd->bl.y); - WFIFOB (fd, 49) = 5; - WFIFOB (fd, 50) = 5; - WFIFOSET (fd, packet_len_table[0x78]); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_spawnmob (struct mob_data *md) -{ - unsigned char buf[64]; - int len; - - nullpo_retr (0, md); - - if (mob_get_viewclass (md->mob_class) > 23) - { - memset (buf, 0, packet_len_table[0x7c]); - - WBUFW (buf, 0) = 0x7c; - WBUFL (buf, 2) = md->bl.id; - WBUFW (buf, 6) = md->stats[MOB_SPEED]; - WBUFW (buf, 8) = md->opt1; - WBUFW (buf, 10) = md->opt2; - WBUFW (buf, 12) = md->option; - WBUFW (buf, 20) = mob_get_viewclass (md->mob_class); - 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 (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] - clif_mob_equip (md, mob_get_equip (md->mob_class)); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_servertick (struct map_session_data *sd) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - 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) = 0; - 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); - - if (sd->disguise > 23 && sd->disguise < 4001) - { - clif_send (buf, len, &sd->bl, AREA); - return 0; - } - else - clif_send (buf, len, &sd->bl, AREA_WOS); - - if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0) - clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, - sd->status.clothes_color); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_quitsave (int fd, struct map_session_data *sd) -{ - map_quit (sd); -} - -/*========================================== - * - *------------------------------------------ - */ -static void clif_waitclose (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - if (session[id]) - session[id]->eof = 1; -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_setwaitclose (int fd) -{ - add_timer (gettick () + 5000, clif_waitclose, fd, 0); -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_changemap (struct map_session_data *sd, char *mapname, int x, int y) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - - WFIFOW (fd, 0) = 0x91; - memcpy (WFIFOP (fd, 2), mapname, 16); - WFIFOW (fd, 18) = x; - WFIFOW (fd, 20) = y; - WFIFOSET (fd, packet_len_table[0x91]); - - if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] - clif_spawnpc (sd); - - 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; - WFIFOW (fd, 0) = 0x92; - memcpy (WFIFOP (fd, 2), mapname, 16); - 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_fixpos (struct block_list *bl) -{ - char buf[16]; - - nullpo_retr (0, bl); - - WBUFW (buf, 0) = 0x88; - 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; - 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; - 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) = 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; - WFIFOW (fd, 0) = 0xc7; - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i]) - { - 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; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0xb4; - WFIFOW (fd, 2) = strlen (mes) + 9; - WFIFOL (fd, 4) = npcid; - strcpy (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; - 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; - 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; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0xb7; - WFIFOW (fd, 2) = strlen (mes) + 8; - WFIFOL (fd, 4) = npcid; - strcpy (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; - 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; - 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; - 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; - WFIFOW (fd, 0) = 0x1b3; - memcpy (WFIFOP (fd, 2), image, 64); - WFIFOB (fd, 66) = type; - WFIFOSET (fd, packet_len_table[0x1b3]); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_additem (struct map_session_data *sd, int n, int amount, int fail) -{ - int fd, j; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - if (sd->status.inventory[n].broken == 1) - WBUFB (buf, 9) = 1; // is weapon broken [Valaris] - else - WBUFB (buf, 9) = sd->status.inventory[n].attribute; - WBUFB (buf, 10) = sd->status.inventory[n].refine; - if (sd->status.inventory[n].card[0] == 0x00ff - || sd->status.inventory[n].card[0] == 0x00fe - || sd->status.inventory[n].card[0] == (short) 0xff00) - { - WBUFW (buf, 11) = sd->status.inventory[n].card[0]; - WBUFW (buf, 13) = sd->status.inventory[n].card[1]; - WBUFW (buf, 15) = sd->status.inventory[n].card[2]; - WBUFW (buf, 17) = sd->status.inventory[n].card[3]; - } - else - { - if (sd->status.inventory[n].card[0] > 0 - && (j = itemdb_viewid (sd->status.inventory[n].card[0])) > 0) - WBUFW (buf, 11) = j; - else - WBUFW (buf, 11) = sd->status.inventory[n].card[0]; - if (sd->status.inventory[n].card[1] > 0 - && (j = itemdb_viewid (sd->status.inventory[n].card[1])) > 0) - WBUFW (buf, 13) = j; - else - WBUFW (buf, 13) = sd->status.inventory[n].card[1]; - if (sd->status.inventory[n].card[2] > 0 - && (j = itemdb_viewid (sd->status.inventory[n].card[2])) > 0) - WBUFW (buf, 15) = j; - else - WBUFW (buf, 15) = sd->status.inventory[n].card[2]; - if (sd->status.inventory[n].card[3] > 0 - && (j = itemdb_viewid (sd->status.inventory[n].card[3])) > 0) - WBUFW (buf, 17) = j; - else - WBUFW (buf, 17) = sd->status.inventory[n].card[3]; - } - WBUFW (buf, 19) = pc_equippoint (sd, n); - WBUFB (buf, 21) = - (sd->inventory_data[n]->type == - 7) ? 4 : 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; - 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; - buf = WFIFOP (fd, 0); - 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) = 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; - WBUFW (buf, n * 18 + 14) = sd->status.inventory[i].card[0]; - WBUFW (buf, n * 18 + 16) = sd->status.inventory[i].card[1]; - WBUFW (buf, n * 18 + 18) = sd->status.inventory[i].card[2]; - WBUFW (buf, n * 18 + 20) = sd->status.inventory[i].card[3]; - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 18; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - if (arrow >= 0) - clif_arrowequip (sd, arrow); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_equiplist (struct map_session_data *sd) -{ - int i, j, n, fd; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - buf = WFIFOP (fd, 0); - WBUFW (buf, 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; - WBUFW (buf, n * 20 + 4) = i + 2; - if (sd->inventory_data[i]->view_id > 0) - WBUFW (buf, n * 20 + 6) = sd->inventory_data[i]->view_id; - else - WBUFW (buf, n * 20 + 6) = sd->status.inventory[i].nameid; - WBUFB (buf, n * 20 + 8) = - (sd->inventory_data[i]->type == - 7) ? 4 : sd->inventory_data[i]->type; - WBUFB (buf, n * 20 + 9) = sd->status.inventory[i].identify; - WBUFW (buf, n * 20 + 10) = pc_equippoint (sd, i); - WBUFW (buf, n * 20 + 12) = sd->status.inventory[i].equip; - if (sd->status.inventory[i].broken == 1) - WBUFB (buf, n * 20 + 14) = 1; // is weapon broken [Valaris] - else - WBUFB (buf, n * 20 + 14) = sd->status.inventory[i].attribute; - WBUFB (buf, n * 20 + 15) = sd->status.inventory[i].refine; - if (sd->status.inventory[i].card[0] == 0x00ff - || sd->status.inventory[i].card[0] == 0x00fe - || sd->status.inventory[i].card[0] == (short) 0xff00) - { - WBUFW (buf, n * 20 + 16) = sd->status.inventory[i].card[0]; - WBUFW (buf, n * 20 + 18) = sd->status.inventory[i].card[1]; - WBUFW (buf, n * 20 + 20) = sd->status.inventory[i].card[2]; - WBUFW (buf, n * 20 + 22) = sd->status.inventory[i].card[3]; - } - else - { - if (sd->status.inventory[i].card[0] > 0 - && (j = itemdb_viewid (sd->status.inventory[i].card[0])) > 0) - WBUFW (buf, n * 20 + 16) = j; - else - WBUFW (buf, n * 20 + 16) = sd->status.inventory[i].card[0]; - if (sd->status.inventory[i].card[1] > 0 - && (j = itemdb_viewid (sd->status.inventory[i].card[1])) > 0) - WBUFW (buf, n * 20 + 18) = j; - else - WBUFW (buf, n * 20 + 18) = sd->status.inventory[i].card[1]; - if (sd->status.inventory[i].card[2] > 0 - && (j = itemdb_viewid (sd->status.inventory[i].card[2])) > 0) - WBUFW (buf, n * 20 + 20) = j; - else - WBUFW (buf, n * 20 + 20) = sd->status.inventory[i].card[2]; - if (sd->status.inventory[i].card[3] > 0 - && (j = itemdb_viewid (sd->status.inventory[i].card[3])) > 0) - WBUFW (buf, n * 20 + 22) = j; - else - WBUFW (buf, n * 20 + 22) = sd->status.inventory[i].card[3]; - } - n++; - } - if (n) - { - WBUFW (buf, 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; - buf = WFIFOP (fd, 0); - 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) = 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; - WBUFW (buf, n * 18 + 14) = stor->storage_[i].card[0]; - WBUFW (buf, n * 18 + 16) = stor->storage_[i].card[1]; - WBUFW (buf, n * 18 + 18) = stor->storage_[i].card[2]; - WBUFW (buf, n * 18 + 20) = stor->storage_[i].card[3]; - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 18; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - return 0; -} - -/*========================================== - * カプラさんに預けてある装備リスト - *------------------------------------------ - */ -int clif_storageequiplist (struct map_session_data *sd, struct storage *stor) -{ - struct item_data *id; - int i, j, n, fd; - unsigned char *buf; - - nullpo_retr (0, sd); - nullpo_retr (0, stor); - - fd = sd->fd; - 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) = 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; - if (stor->storage_[i].broken == 1) - WBUFB (buf, n * 20 + 14) = 1; //is weapon broken [Valaris] - else - WBUFB (buf, n * 20 + 14) = stor->storage_[i].attribute; - WBUFB (buf, n * 20 + 15) = stor->storage_[i].refine; - if (stor->storage_[i].card[0] == 0x00ff - || stor->storage_[i].card[0] == 0x00fe - || stor->storage_[i].card[0] == (short) 0xff00) - { - WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; - WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; - WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; - WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; - } - else - { - if (stor->storage_[i].card[0] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[0])) > 0) - WBUFW (buf, n * 20 + 16) = j; - else - WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; - if (stor->storage_[i].card[1] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[1])) > 0) - WBUFW (buf, n * 20 + 18) = j; - else - WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; - if (stor->storage_[i].card[2] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[2])) > 0) - WBUFW (buf, n * 20 + 20) = j; - else - WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; - if (stor->storage_[i].card[3] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[3])) > 0) - WBUFW (buf, n * 20 + 22) = j; - else - WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; - } - 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; - buf = WFIFOP (fd, 0); - - 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) = 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; - WBUFW (buf, n * 18 + 14) = stor->storage_[i].card[0]; - WBUFW (buf, n * 18 + 16) = stor->storage_[i].card[1]; - WBUFW (buf, n * 18 + 18) = stor->storage_[i].card[2]; - WBUFW (buf, n * 18 + 20) = stor->storage_[i].card[3]; - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 18; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_guildstorageequiplist (struct map_session_data *sd, - struct guild_storage *stor) -{ - struct item_data *id; - int i, j, n, fd; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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) = 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; - if (stor->storage_[i].broken == 1) - WBUFB (buf, n * 20 + 14) = 1; // is weapon broken [Valaris] - else - WBUFB (buf, n * 20 + 14) = stor->storage_[i].attribute; - WBUFB (buf, n * 20 + 15) = stor->storage_[i].refine; - if (stor->storage_[i].card[0] == 0x00ff - || stor->storage_[i].card[0] == 0x00fe - || stor->storage_[i].card[0] == (short) 0xff00) - { - WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; - WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; - WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; - WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; - } - else - { - if (stor->storage_[i].card[0] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[0])) > 0) - WBUFW (buf, n * 20 + 16) = j; - else - WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; - if (stor->storage_[i].card[1] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[1])) > 0) - WBUFW (buf, n * 20 + 18) = j; - else - WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; - if (stor->storage_[i].card[2] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[2])) > 0) - WBUFW (buf, n * 20 + 20) = j; - else - WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; - if (stor->storage_[i].card[3] > 0 - && (j = itemdb_viewid (stor->storage_[i].card[3])) > 0) - WBUFW (buf, n * 20 + 22) = j; - else - WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; - } - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 20; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - return 0; -} - -/*========================================== - * ステータスを送りつける - * 表示専用数字はこの中で計算して送る - *------------------------------------------ - */ -int clif_updatestatus (struct map_session_data *sd, int type) -{ - int fd, len = 8; - - nullpo_retr (0, sd); - - fd = sd->fd; - - 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; - 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) = 0; - 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; - 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->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->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: - trade_verifyzeny (sd); - 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->attack_spell_override) - ? sd->attack_spell_range : 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; - - case SP_GM: - WFIFOL (fd, 4) = pc_isGM (sd); - break; - - default: - if (battle_config.error_log) - printf ("clif_updatestatus : make %d routine\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) - printf ("clif_changestatus : make %d routine\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) -{ - return clif_changelook_towards (bl, type, val, NULL); -} - -int clif_changelook_towards (struct block_list *bl, int type, int val, - struct map_session_data *dstsd) -{ - unsigned char rbuf[32]; - unsigned char *buf = dstsd ? WFIFOP (dstsd->fd, 0) : rbuf; // pick target buffer or general-purpose one - struct map_session_data *sd = NULL; - - nullpo_retr (0, bl); - - if (bl->type == BL_PC) - sd = (struct map_session_data *) bl; - - if (sd && sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] - return 0; - - if (sd && sd->status.option & OPTION_INVISIBILITY) - return 0; - - if (sd - && (type == LOOK_WEAPON || type == LOOK_SHIELD || type >= LOOK_SHOES)) - { - WBUFW (buf, 0) = 0x1d7; - WBUFL (buf, 2) = bl->id; - if (type >= LOOK_SHOES) - { - int equip_point = equip_points[type]; - - WBUFB (buf, 6) = type; - if (sd->equip_index[equip_point] >= 0 - && sd->inventory_data[sd->equip_index[equip_point]]) - { - if (sd-> - inventory_data[sd->equip_index[equip_point]]->view_id > 0) - WBUFW (buf, 7) = - sd->inventory_data[sd-> - equip_index[equip_point]]->view_id; - else - WBUFW (buf, 7) = - sd->status.inventory[sd-> - equip_index[equip_point]].nameid; - } - else - WBUFW (buf, 7) = 0; - WBUFW (buf, 9) = 0; - } - else - { - WBUFB (buf, 6) = 2; - if (sd->attack_spell_override) - WBUFW (buf, 7) = sd->attack_spell_look_override; - else - { - if (sd->equip_index[9] >= 0 - && sd->inventory_data[sd->equip_index[9]] - && sd->view_class != 22) - { - 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 != 22) - { - 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; - } - if (dstsd) - WFIFOSET (dstsd->fd, packet_len_table[0x1d7]); - else - clif_send (buf, packet_len_table[0x1d7], bl, AREA); - } - else - { - WBUFW (buf, 0) = 0x1d7; - WBUFL (buf, 2) = bl->id; - WBUFB (buf, 6) = type; - WBUFW (buf, 7) = val; - WBUFW (buf, 9) = 0; - if (dstsd) - WFIFOSET (dstsd->fd, packet_len_table[0x1d7]); - else - clif_send (buf, packet_len_table[0x1d7], bl, AREA); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_initialstatus (struct map_session_data *sd) -{ - int fd; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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->watk; - WBUFW (buf, 18) = sd->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; - 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; - WFIFOW (fd, 0) = 0x013b; - WFIFOW (fd, 2) = type; - - WFIFOSET (fd, packet_len_table[0x013b]); - - 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; - 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; - 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; - 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) -{ - 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_changeoption (struct block_list *bl) -{ - char buf[32]; - short option; - struct status_change *sc_data; - static const int omask[] = { 0x10, 0x20 }; - static const int scnum[] = { SC_FALCON, SC_RIDING }; - int i; - - nullpo_retr (0, bl); - - option = *battle_get_option (bl); - sc_data = battle_get_sc_data (bl); - - WBUFW (buf, 0) = 0x119; - WBUFL (buf, 2) = bl->id; - WBUFW (buf, 6) = *battle_get_opt1 (bl); - WBUFW (buf, 8) = *battle_get_opt2 (bl); - WBUFW (buf, 10) = option; - WBUFB (buf, 12) = 0; // ?? - - if (bl->type == BL_PC) - { // disguises [Valaris] - struct map_session_data *sd = ((struct map_session_data *) bl); - if (sd && sd->disguise > 23 && sd->disguise < 4001) - { - clif_send (buf, packet_len_table[0x119], bl, AREA_WOS); - clif_spawnpc (sd); - } - else - clif_send (buf, packet_len_table[0x119], bl, AREA); - } - else - clif_send (buf, packet_len_table[0x119], bl, AREA); - - // アイコンの表示 - for (i = 0; i < sizeof (omask) / sizeof (omask[0]); i++) - { - if (option & omask[i]) - { - if (sc_data[scnum[i]].timer == -1) - skill_status_change_start (bl, scnum[i], 0, 0, 0, 0, 0, 0); - } - else - { - skill_status_change_end (bl, scnum[i], -1); - } - } - - 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; - WFIFOW (fd, 0) = 0xa8; - WFIFOW (fd, 2) = index + 2; - WFIFOW (fd, 4) = amount; - WFIFOB (fd, 6) = ok; - WFIFOSET (fd, packet_len_table[0xa8]); - } - else - { - 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, SELF); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_createchat (struct map_session_data *sd, int fail) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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) -{ - char buf[128]; // 最大title(60バイト)+17 - - if (cd == NULL || *cd->owner == NULL) - return 1; - - WBUFW (buf, 0) = 0xd7; - WBUFW (buf, 2) = strlen (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 (WBUFP (buf, 17), cd->title); - if (fd) - { - 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) -{ - char buf[128]; // 最大title(60バイト)+17 - - if (cd == NULL || cd->usersd[0] == NULL) - return 1; - - WBUFW (buf, 0) = 0xdf; - WBUFW (buf, 2) = strlen (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 (WBUFP (buf, 17), cd->title); - clif_send (buf, WBUFW (buf, 2), &cd->usersd[0]->bl, CHAT); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_clearchat (struct chat_data *cd, int fd) -{ - char buf[32]; - - nullpo_retr (0, cd); - - WBUFW (buf, 0) = 0xd8; - WBUFL (buf, 2) = cd->bl.id; - if (fd) - { - 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; - - 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; - 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, 24); - } - WFIFOSET (fd, WFIFOW (fd, 2)); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_addchat (struct chat_data *cd, struct map_session_data *sd) -{ - 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, 24); - 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) -{ - 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, 24); - WBUFW (buf, 30) = 0xe1; - WBUFL (buf, 32) = 0; - memcpy (WBUFP (buf, 36), sd->status.name, 24); - - 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) -{ - 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, 24); - 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; - WFIFOW (fd, 0) = 0xe5; - strcpy (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; - 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, j; - - nullpo_retr (0, sd); - nullpo_retr (0, tsd); - - fd = tsd->fd; - 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; - 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 - if (sd->status.inventory[index].broken == 1) - WFIFOB (fd, 9) = 1; // is broke weapon [Valaris] - else - WFIFOB (fd, 9) = sd->status.inventory[index].attribute; // attribute - WFIFOB (fd, 10) = sd->status.inventory[index].refine; //refine - if (sd->status.inventory[index].card[0] == 0x00ff - || sd->status.inventory[index].card[0] == 0x00fe - || sd->status.inventory[index].card[0] == (short) 0xff00) - { - WFIFOW (fd, 11) = sd->status.inventory[index].card[0]; //card (4w) - WFIFOW (fd, 13) = sd->status.inventory[index].card[1]; //card (4w) - WFIFOW (fd, 15) = sd->status.inventory[index].card[2]; //card (4w) - WFIFOW (fd, 17) = sd->status.inventory[index].card[3]; //card (4w) - } - else - { - if (sd->status.inventory[index].card[0] > 0 - && (j = - itemdb_viewid (sd->status.inventory[index].card[0])) > 0) - WFIFOW (fd, 11) = j; - else - WFIFOW (fd, 11) = sd->status.inventory[index].card[0]; - if (sd->status.inventory[index].card[1] > 0 - && (j = - itemdb_viewid (sd->status.inventory[index].card[1])) > 0) - WFIFOW (fd, 13) = j; - else - WFIFOW (fd, 13) = sd->status.inventory[index].card[1]; - if (sd->status.inventory[index].card[2] > 0 - && (j = - itemdb_viewid (sd->status.inventory[index].card[2])) > 0) - WFIFOW (fd, 15) = j; - else - WFIFOW (fd, 15) = sd->status.inventory[index].card[2]; - if (sd->status.inventory[index].card[3] > 0 - && (j = - itemdb_viewid (sd->status.inventory[index].card[3])) > 0) - WFIFOW (fd, 17) = j; - else - WFIFOW (fd, 17) = sd->status.inventory[index].card[3]; - } - } - WFIFOSET (fd, packet_len_table[0xe9]); - - return 0; -} - -/*========================================== - * アイテム追加成功/失敗 - *------------------------------------------ - */ -int clif_tradeitemok (struct map_session_data *sd, int index, int amount, - int fail) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0x1b1; - //WFIFOW(fd,0)=0xea; - WFIFOW (fd, 2) = index; - WFIFOW (fd, 4) = amount; - WFIFOB (fd, 6) = fail; - WFIFOSET (fd, packet_len_table[0x1b1]); - - return 0; -} - -/*========================================== - * 取り引きok押し - *------------------------------------------ - */ -int clif_tradedeal_lock (struct map_session_data *sd, int fail) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - 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; - 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; - 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 fd, j; - - nullpo_retr (0, sd); - nullpo_retr (0, stor); - - fd = sd->fd; - 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; - WFIFOB (fd, 10) = stor->storage_[index].identify; //identify flag - if (stor->storage_[index].broken == 1) - WFIFOB (fd, 11) = 1; // is weapon broken [Valaris] - else - WFIFOB (fd, 11) = stor->storage_[index].attribute; // attribute - WFIFOB (fd, 12) = stor->storage_[index].refine; //refine - if (stor->storage_[index].card[0] == 0x00ff - || stor->storage_[index].card[0] == 0x00fe - || stor->storage_[index].card[0] == (short) 0xff00) - { - WFIFOW (fd, 13) = stor->storage_[index].card[0]; //card (4w) - WFIFOW (fd, 15) = stor->storage_[index].card[1]; //card (4w) - WFIFOW (fd, 17) = stor->storage_[index].card[2]; //card (4w) - WFIFOW (fd, 19) = stor->storage_[index].card[3]; //card (4w) - } - else - { - if (stor->storage_[index].card[0] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[0])) > 0) - WFIFOW (fd, 13) = j; - else - WFIFOW (fd, 13) = stor->storage_[index].card[0]; - if (stor->storage_[index].card[1] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[1])) > 0) - WFIFOW (fd, 15) = j; - else - WFIFOW (fd, 15) = stor->storage_[index].card[1]; - if (stor->storage_[index].card[2] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[2])) > 0) - WFIFOW (fd, 17) = j; - else - WFIFOW (fd, 17) = stor->storage_[index].card[2]; - if (stor->storage_[index].card[3] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[3])) > 0) - WFIFOW (fd, 19) = j; - else - WFIFOW (fd, 19) = stor->storage_[index].card[3]; - } - 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; - 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, j; - - nullpo_retr (0, sd); - nullpo_retr (0, stor); - - fd = sd->fd; - 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 - if (stor->storage_[index].broken == 1) - WFIFOB (fd, 11) = 1; // is weapon broken [Valaris] - else - WFIFOB (fd, 11) = stor->storage_[index].attribute; // attribute - WFIFOB (fd, 12) = stor->storage_[index].refine; //refine - if (stor->storage_[index].card[0] == 0x00ff - || stor->storage_[index].card[0] == 0x00fe - || stor->storage_[index].card[0] == (short) 0xff00) - { - WFIFOW (fd, 13) = stor->storage_[index].card[0]; //card (4w) - WFIFOW (fd, 15) = stor->storage_[index].card[1]; //card (4w) - WFIFOW (fd, 17) = stor->storage_[index].card[2]; //card (4w) - WFIFOW (fd, 19) = stor->storage_[index].card[3]; //card (4w) - } - else - { - if (stor->storage_[index].card[0] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[0])) > 0) - WFIFOW (fd, 13) = j; - else - WFIFOW (fd, 13) = stor->storage_[index].card[0]; - if (stor->storage_[index].card[1] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[1])) > 0) - WFIFOW (fd, 15) = j; - else - WFIFOW (fd, 15) = stor->storage_[index].card[1]; - if (stor->storage_[index].card[2] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[2])) > 0) - WFIFOW (fd, 17) = j; - else - WFIFOW (fd, 17) = stor->storage_[index].card[2]; - if (stor->storage_[index].card[3] > 0 - && (j = itemdb_viewid (stor->storage_[index].card[3])) > 0) - WFIFOW (fd, 19) = j; - else - WFIFOW (fd, 19) = stor->storage_[index].card[3]; - } - 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; - 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; - WFIFOW (fd, 0) = 0xf8; // Storage Closed - WFIFOSET (fd, packet_len_table[0xf8]); - - return 0; -} - -void -clif_changelook_accessories (struct block_list *bl, - struct map_session_data *dest) -{ - int i; - - for (i = LOOK_SHOES; i <= LOOK_LAST; i++) - clif_changelook_towards (bl, i, 0, dest); -} - -// -// callback系 ? -// -/*========================================== - * PC表示 - *------------------------------------------ - */ -void clif_getareachar_pc (struct map_session_data *sd, - struct map_session_data *dstsd) -{ - int len; - - if (dstsd->status.option & OPTION_INVISIBILITY) - return; - - nullpo_retv (sd); - nullpo_retv (dstsd); - - if (dstsd->walktimer != -1) - { - len = clif_set007b (dstsd, WFIFOP (sd->fd, 0)); - WFIFOSET (sd->fd, len); - } - else - { - len = clif_set0078 (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->spiritball > 0) - { - clif_set01e1 (dstsd, WFIFOP (sd->fd, 0)); - WFIFOSET (sd->fd, packet_len_table[0x1e1]); - } - if (battle_config.save_clothcolor == 1 && dstsd->status.clothes_color > 0) - clif_changelook (&dstsd->bl, LOOK_CLOTHES_COLOR, - dstsd->status.clothes_color); - - if (sd->status.manner < 0) - clif_changestatus (&sd->bl, SP_MANNER, sd->status.manner); - - clif_changelook_accessories (&sd->bl, dstsd); - clif_changelook_accessories (&dstsd->bl, sd); -} - -/*========================================== - * NPC表示 - *------------------------------------------ - */ -void clif_getareachar_npc (struct map_session_data *sd, struct npc_data *nd) -{ - int len; - - nullpo_retv (sd); - nullpo_retv (nd); - - if (nd->npc_class < 0 || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) - return; - - 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 (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] - clif_mob_equip (md, mob_get_equip (md->mob_class)); - - 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); - } - clif_changelook_accessories (&sd->bl, NULL); - - return 0; -} - -/*========================================== - * 通常攻撃エフェクト&ダメージ - *------------------------------------------ - */ -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); - - sc_data = battle_get_sc_data (dst); - - if (type != 4 && dst->type == BL_PC - && ((struct map_session_data *) dst)->special_state.infinite_endure) - type = 9; - if (sc_data) - { - if (type != 4 && sc_data[SC_ENDURE].timer != -1) - type = 9; - if (sc_data[SC_HALLUCINATION].timer != -1) - { - if (damage > 0) - damage = - damage * (5 + sc_data[SC_HALLUCINATION].val1) + - MRAND (100); - if (damage2 > 0) - damage2 = - damage2 * (5 + sc_data[SC_HALLUCINATION].val1) + - MRAND (100); - } - } - - WBUFW (buf, 0) = 0x8a; - WBUFL (buf, 2) = src->id; - 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); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_getareachar_mob (struct map_session_data *sd, struct mob_data *md) -{ - int len; - nullpo_retv (sd); - nullpo_retv (md); - - if (md->state.state == MS_WALK) - { - len = clif_mob007b (md, WFIFOP (sd->fd, 0)); - WFIFOSET (sd->fd, len); - } - else - { - len = clif_mob0078 (md, WFIFOP (sd->fd, 0)); - WFIFOSET (sd->fd, len); - } - - if (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] - clif_mob_equip (md, mob_get_equip (md->mob_class)); -} - -/*========================================== - * - *------------------------------------------ - */ -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 - 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); - 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; - 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 - - /* Graffiti [Valaris] */ - if (unit->group->unit_id == 0xb0) - { - WFIFOL (fd, 15) = 1; - WFIFOL (fd, 16) = 1; - memcpy (WFIFOP (fd, 17), unit->group->valstr, 80); - } - - WFIFOSET (fd, packet_len_table[0x1c9]); - 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); - - WFIFOW (fd, 0) = 0x120; - WFIFOL (fd, 2) = unit->bl.id; - WFIFOSET (fd, packet_len_table[0x120]); - if (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) -{ - 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 *); - - 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_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) - printf ("get area char ??? %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->chatID) - { - struct chat_data *cd; - cd = (struct chat_data *) map_id2bl (dstsd->chatID); - if (cd->usersd[0] == dstsd) - clif_dispchat (cd, sd->fd); - } - } - break; - case BL_NPC: - if (((struct npc_data *) bl)->npc_class != INVISIBLE_CLASS) - clif_clearchar_id (bl->id, 0, sd->fd); - break; - case BL_MOB: - 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_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)) - { - 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)) - { - clif_getareachar_mob (sd, md); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_skillinfo (struct map_session_data *sd, int skillid, int type, - int range) -{ - int fd, id; - - nullpo_retr (0, sd); - - fd = sd->fd; - if ((id = sd->status.skill[skillid].id) <= 0) - return 0; - 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_range (id, sd->status.skill[skillid].lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - WFIFOW (fd, 12) = range; - } - else - WFIFOW (fd, 12) = range; - memset (WFIFOP (fd, 14), 0, 24); - WFIFOB (fd, 38) = - (sd->status.skill[skillid].lv < skill_get_max_raise (id)) ? 1 : 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, range; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0x10f; - for (i = c = 0; i < MAX_SKILL; i++) - { - if ((id = sd->status.skill[i].id) != 0 && (sd->tmw_version >= 1)) - { // [Fate] Version 1 and later don't crash because of bad skill IDs anymore - WFIFOW (fd, len) = id; - WFIFOW (fd, len + 2) = skill_get_inf (id); - WFIFOW (fd, len + 4) = - skill_db[i].poolflags | (sd->status. - skill[i].flags & - (SKILL_POOL_ACTIVATED)); - WFIFOW (fd, len + 6) = sd->status.skill[i].lv; - WFIFOW (fd, len + 8) = skill_get_sp (id, sd->status.skill[i].lv); - range = skill_get_range (id, sd->status.skill[i].lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - WFIFOW (fd, len + 10) = range; - memset (WFIFOP (fd, len + 12), 0, 24); - WFIFOB (fd, len + 36) = - (sd->status.skill[i].lv < skill_get_max_raise (id)) ? 1 : 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 range, fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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); - range = skill_get_range (skill_num, sd->status.skill[skill_num].lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - WFIFOW (fd, 8) = range; - WFIFOB (fd, 10) = - (sd->status.skill[skill_num].lv < - skill_get_max_raise (sd->status.skill[skill_num].id)) ? 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) -{ - 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) = skill_get_pl (skill_num); //属性 - WBUFL (buf, 20) = casttime; //skill詠唱時間 - 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; - - if (type == 0x4 && battle_config.display_delay_skill_fail == 0) - { - return 0; - } - - 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); - - sc_data = battle_get_sc_data (dst); - - if (type != 5 && dst->type == BL_PC - && ((struct map_session_data *) dst)->special_state.infinite_endure) - type = 9; - if (sc_data) - { - if (type != 5 && sc_data[SC_ENDURE].timer != -1) - type = 9; - if (sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) - damage = - damage * (5 + sc_data[SC_HALLUCINATION].val1) + MRAND (100); - } - - WBUFW (buf, 0) = 0x1de; - 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; - WBUFL (buf, 24) = damage; - WBUFW (buf, 28) = skill_lv; - WBUFW (buf, 30) = div; - WBUFB (buf, 32) = (type > 0) ? type : skill_get_hit (skill_id); - clif_send (buf, packet_len_table[0x1de], src, AREA); - - 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); - - sc_data = battle_get_sc_data (dst); - - if (type != 5 && dst->type == BL_PC - && ((struct map_session_data *) dst)->special_state.infinite_endure) - type = 9; - if (sc_data) - { - if (type != 5 && sc_data[SC_ENDURE].timer != -1) - type = 9; - if (sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) - damage = - damage * (5 + sc_data[SC_HALLUCINATION].val1) + MRAND (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 > 0) ? type : skill_get_hit (skill_id); - clif_send (buf, packet_len_table[0x115], src, AREA); - - 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); - - 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; - 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 - - /* Graffiti [Valaris] */ - if (unit->group->unit_id == 0xb0) - { - WBUFL (buf, 15) = 1; - WBUFL (buf, 16) = 1; - memcpy (WBUFP (buf, 17), unit->group->valstr, 80); - } - - clif_send (buf, packet_len_table[0x1c9], &unit->bl, AREA); - 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; - WFIFOW (fd, 0) = 0x11c; - WFIFOW (fd, 2) = skill_num; - memcpy (WFIFOP (fd, 4), map1, 16); - memcpy (WFIFOP (fd, 20), map2, 16); - memcpy (WFIFOP (fd, 36), map3, 16); - memcpy (WFIFOP (fd, 52), map4, 16); - 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; - - 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; - 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; - - 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->mob_class); - WBUFW (buf, 4) = mob_db[md->mob_class].lv; - WBUFW (buf, 6) = mob_db[md->mob_class].size; - WBUFL (buf, 8) = md->hp; - WBUFW (buf, 12) = battle_get_def2 (&md->bl); - WBUFW (buf, 14) = mob_db[md->mob_class].race; - WBUFW (buf, 16) = - battle_get_mdef2 (&md->bl) - (mob_db[md->mob_class].vit >> 1); - WBUFW (buf, 18) = battle_get_elem_type (&md->bl); - for (i = 0; i < 9; i++) - WBUFB (buf, 20 + i) = battle_attr_fix (100, i + 1, md->def_ele); - - if (sd->status.party_id > 0) - clif_send (buf, packet_len_table[0x18c], &sd->bl, PARTY_AREA); - else - { - memcpy (WFIFOP (sd->fd, 0), buf, packet_len_table[0x18c]); - WFIFOSET (sd->fd, packet_len_table[0x18c]); - } - return 0; -} - -/*========================================== - * 状態異常アイコン/メッセージ表示 - *------------------------------------------ - */ -int clif_status_change (struct block_list *bl, int type, int flag) -{ - unsigned char buf[16]; - - 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) -{ - 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. - 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 lbuf[255]; - unsigned char *buf = - ((len + 16) >= sizeof (lbuf)) ? (unsigned char*)malloc (len + 16) : lbuf; - int lp = (flag & 0x10) ? 8 : 4; - - 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 != lbuf) - free (buf); - return 0; -} - -/*========================================== - * HPSP回復エフェクトを送信する - *------------------------------------------ - */ -int clif_heal (int fd, int type, int val) -{ - 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); - - if (bl->type == BL_PC) - { // disguises [Valaris] - struct map_session_data *sd = ((struct map_session_data *) bl); - if (sd && sd->disguise > 23 && sd->disguise < 4001) - clif_spawnpc (sd); - } - - 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); - - return 0; -} - -/*========================================== - * PVP実装?(仮) - *------------------------------------------ - */ -int clif_set0199 (int fd, int type) -{ - 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) - { - 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 - { - char buf[32]; - - WBUFW (buf, 0) = 0x19a; - WBUFL (buf, 2) = sd->bl.id; - if (sd->status.option & 0x46) - WBUFL (buf, 6) = -1; - 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; - 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) -{ - 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 -{ - WFIFOW (fd, 0) = 0x97; - WFIFOW (fd, 2) = mes_len + 24 + 4; - memcpy (WFIFOP (fd, 4), nick, 24); - 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 -{ - 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) - { - WFIFOW (fd, 0) = 0x194; - WFIFOL (fd, 2) = char_id; - memcpy (WFIFOP (fd, 6), p, 24); - 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) -{ - nullpo_retr (0, sd); - - if (sd->inventory_data[idx]) - { - int i, c; - int ep = sd->inventory_data[idx]->equip; - int fd = sd->fd; - 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) // 製造武器 - continue; - if (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; - 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; - - 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; - 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) -{ - int i, c; - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - - WFIFOW (fd, 0) = 0x0; - for (i = c = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].nameid > 0 - && sd->status.inventory[i].broken != 0) - { - 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_skill (struct map_session_data *sd, int skillid, int skilllv, - const char *name) -{ - int range, fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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); - range = skill_get_range (skillid, skilllv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - WFIFOW (fd, 12) = range; - memcpy (WFIFOP (fd, 14), name, 24); - 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, j, fd; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - if (sd->status.cart[n].broken == 1) //is weapon broken [Valaris] - WBUFB (buf, 11) = 1; - else - WBUFB (buf, 11) = sd->status.cart[n].attribute; - WBUFB (buf, 12) = sd->status.cart[n].refine; - if (sd->status.cart[n].card[0] == 0x00ff - || sd->status.cart[n].card[0] == 0x00fe - || sd->status.cart[n].card[0] == (short) 0xff00) - { - WBUFW (buf, 13) = sd->status.cart[n].card[0]; - WBUFW (buf, 15) = sd->status.cart[n].card[1]; - WBUFW (buf, 17) = sd->status.cart[n].card[2]; - WBUFW (buf, 19) = sd->status.cart[n].card[3]; - } - else - { - if (sd->status.cart[n].card[0] > 0 - && (j = itemdb_viewid (sd->status.cart[n].card[0])) > 0) - WBUFW (buf, 13) = j; - else - WBUFW (buf, 13) = sd->status.cart[n].card[0]; - if (sd->status.cart[n].card[1] > 0 - && (j = itemdb_viewid (sd->status.cart[n].card[1])) > 0) - WBUFW (buf, 15) = j; - else - WBUFW (buf, 15) = sd->status.cart[n].card[1]; - if (sd->status.cart[n].card[2] > 0 - && (j = itemdb_viewid (sd->status.cart[n].card[2])) > 0) - WBUFW (buf, 17) = j; - else - WBUFW (buf, 17) = sd->status.cart[n].card[2]; - if (sd->status.cart[n].card[3] > 0 - && (j = itemdb_viewid (sd->status.cart[n].card[3])) > 0) - WBUFW (buf, 19) = j; - else - WBUFW (buf, 19) = sd->status.cart[n].card[3]; - } - 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; - - 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; - buf = WFIFOP (fd, 0); - WBUFW (buf, 0) = 0x1ef; - 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) = 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; - WBUFW (buf, n * 18 + 14) = sd->status.cart[i].card[0]; - WBUFW (buf, n * 18 + 16) = sd->status.cart[i].card[1]; - WBUFW (buf, n * 18 + 18) = sd->status.cart[i].card[2]; - WBUFW (buf, n * 18 + 20) = sd->status.cart[i].card[3]; - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 18; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - return 0; -} - -/*========================================== - * カートの装備品リスト - *------------------------------------------ - */ -int clif_cart_equiplist (struct map_session_data *sd) -{ - struct item_data *id; - int i, j, n, fd; - unsigned char *buf; - - nullpo_retr (0, sd); - - fd = sd->fd; - buf = WFIFOP (fd, 0); - - WBUFW (buf, 0) = 0x122; - 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) = 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; - if (sd->status.cart[i].broken == 1) - WBUFB (buf, n * 20 + 14) = 1; //is weapon broken [Valaris] - else - WBUFB (buf, n * 20 + 14) = sd->status.cart[i].attribute; - WBUFB (buf, n * 20 + 15) = sd->status.cart[i].refine; - if (sd->status.cart[i].card[0] == 0x00ff - || sd->status.cart[i].card[0] == 0x00fe - || sd->status.cart[i].card[0] == (short) 0xff00) - { - WBUFW (buf, n * 20 + 16) = sd->status.cart[i].card[0]; - WBUFW (buf, n * 20 + 18) = sd->status.cart[i].card[1]; - WBUFW (buf, n * 20 + 20) = sd->status.cart[i].card[2]; - WBUFW (buf, n * 20 + 22) = sd->status.cart[i].card[3]; - } - else - { - if (sd->status.cart[i].card[0] > 0 - && (j = itemdb_viewid (sd->status.cart[i].card[0])) > 0) - WBUFW (buf, n * 20 + 16) = j; - else - WBUFW (buf, n * 20 + 16) = sd->status.cart[i].card[0]; - if (sd->status.cart[i].card[1] > 0 - && (j = itemdb_viewid (sd->status.cart[i].card[1])) > 0) - WBUFW (buf, n * 20 + 18) = j; - else - WBUFW (buf, n * 20 + 18) = sd->status.cart[i].card[1]; - if (sd->status.cart[i].card[2] > 0 - && (j = itemdb_viewid (sd->status.cart[i].card[2])) > 0) - WBUFW (buf, n * 20 + 20) = j; - else - WBUFW (buf, n * 20 + 20) = sd->status.cart[i].card[2]; - if (sd->status.cart[i].card[3] > 0 - && (j = itemdb_viewid (sd->status.cart[i].card[3])) > 0) - WBUFW (buf, n * 20 + 22) = j; - else - WBUFW (buf, n * 20 + 22) = sd->status.cart[i].card[3]; - } - n++; - } - if (n) - { - WBUFW (buf, 2) = 4 + n * 20; - WFIFOSET (fd, WFIFOW (fd, 2)); - } - return 0; -} - -/*========================================== - * パーティ作成完了 - * Relay the result of party creation. - * - * (R 00fa <flag>.B) - * - * flag: - * 0 The party was created. - * 1 The party name is invalid/taken. - * 2 The character is already in a party. - *------------------------------------------ - */ -int clif_party_created (struct map_session_data *sd, int flag) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0xfa; - WFIFOB (fd, 2) = flag; - WFIFOSET (fd, packet_len_table[0xfa]); - return 0; -} - -/*========================================== - * パーティ情報送信 - *------------------------------------------ - */ -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, 24); - 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, 24); - memcpy (WBUFP (buf, 28 + c * 46 + 28), m->map, 16); - 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が設定されてるならそれに送る - 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; -} - -/*========================================== - * パーティ勧誘 - * Relay a party invitation. - * - * (R 00fe <sender_ID>.l <party_name>.24B) - *------------------------------------------ - */ -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))) - return 0; - - WFIFOW (fd, 0) = 0xfe; - WFIFOL (fd, 2) = sd->status.account_id; - memcpy (WFIFOP (fd, 6), p->name, 24); - WFIFOSET (fd, packet_len_table[0xfe]); - return 0; -} - -/*========================================== - * パーティ勧誘結果 - * Relay the response to a party invitation. - * - * (R 00fd <name>.24B <flag>.B) - * - * flag: - * 0 The character is already in a party. - * 1 The invitation was rejected. - * 2 The invitation was accepted. - * 3 The party is full. - * 4 The character is in the same party. - *------------------------------------------ - */ -int clif_party_inviteack (struct map_session_data *sd, char *nick, int flag) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - WFIFOW (fd, 0) = 0xfd; - memcpy (WFIFOP (fd, 2), nick, 24); - 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) = ((flag & 0x10) ? 2 : p->item); - if (flag == 0) - clif_send (buf, packet_len_table[0x101], &sd->bl, PARTY); - else - { - 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, 24); - 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) - { - 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 party *p, 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); -// if(battle_config.etc_log) -// printf("clif_party_xy %d\n",sd->status.account_id); - return 0; -} - -/*========================================== - * パーティHP通知 - *------------------------------------------ - */ -int clif_party_hp (struct party *p, 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); -// if(battle_config.etc_log) -// printf("clif_party_hp %d\n",sd->status.account_id); - 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, 24); - memcpy (WBUFP (buf, 39), sd->status.name, 24); - memcpy (WBUFP (buf, 63), map[sd->bl.m].name, 16); - 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; - 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); - - 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; -} - -/*========================================== - * オートスペル リスト送信 - *------------------------------------------ - */ -int clif_autospell (struct map_session_data *sd, int skilllv) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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, int target) -{ - unsigned char buf[56]; - int n; - - nullpo_retr (0, sd); - - WBUFW (buf, 0) = 0x1cf; - WBUFL (buf, 2) = sd->bl.id; -// WBUFL(buf,6)=target; - for (n = 0; n < 5; n++) - WBUFL (buf, 6 + 4 * n) = sd->dev.val2[n]; -// WBUFL(buf,10+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_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 boolean) -{ - 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) = boolean; - - 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; - 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, 16); - 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; - 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; - WFIFOW (fd, 0) = 0x10b; - WFIFOL (fd, 2) = exp; - WFIFOSET (fd, packet_len_table[0x10b]); - return 0; -} - -/*========================================== - * ギルド作成可否通知 - * Relay the result of guild creation. - * - * (R 0167 <flag>.B) - * - * flag: - * 0 The guild was created. - * 1 The character is already in a guild. - * 2 The guild name is invalid/taken. - * 3 The Emperium item is required. - *------------------------------------------ - */ -int clif_guild_created (struct map_session_data *sd, int flag) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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); - - 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, 24); - 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); - - WBUFW (buf, 0) = 0x16d; - WBUFL (buf, 2) = g->member[idx].account_id; - WBUFL (buf, 6) = 0; - 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; - struct guild *g; - - nullpo_retr (0, sd); - - fd = sd->fd; - g = guild_search (sd->status.guild_id); - if (g != NULL && strcmp (g->master, sd->status.name) == 0) - type = 0xd7; - 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; - - 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, 24); - memcpy (WFIFOP (fd, 70), g->master, 24); - - 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 == 1) - memcpy (WFIFOP (fd, 94), "One Castle", 20); - else if (t == 2) - memcpy (WFIFOP (fd, 94), "Two Castles", 20); - else if (t == 3) - memcpy (WFIFOP (fd, 94), "Three Castles", 20); - else if (t == 4) - memcpy (WFIFOP (fd, 94), "Four Castles", 20); - else if (t == 5) - memcpy (WFIFOP (fd, 94), "Five Castles", 20); - else if (t == 6) - memcpy (WFIFOP (fd, 94), "Six Castles", 20); - else if (t == 7) - memcpy (WFIFOP (fd, 94), "Seven Castles", 20); - else if (t == 8) - memcpy (WFIFOP (fd, 94), "Eight Castles", 20); - else if (t == 9) - memcpy (WFIFOP (fd, 94), "Nine Castles", 20); - else if (t == 10) - memcpy (WFIFOP (fd, 94), "Ten Castles", 20); - else if (t == 11) - memcpy (WFIFOP (fd, 94), "Eleven Castles", 20); - else if (t == 12) - memcpy (WFIFOP (fd, 94), "Twelve Castles", 20); - else if (t == 13) - memcpy (WFIFOP (fd, 94), "Thirteen Castles", 20); - else if (t == 14) - memcpy (WFIFOP (fd, 94), "Fourteen Castles", 20); - else if (t == 15) - memcpy (WFIFOP (fd, 94), "Fifteen Castles", 20); - else if (t == 16) - memcpy (WFIFOP (fd, 94), "Sixteen Castles", 20); - else if (t == 17) - memcpy (WFIFOP (fd, 94), "Seventeen Castles", 20); - else if (t == 18) - memcpy (WFIFOP (fd, 94), "Eighteen Castles", 20); - else if (t == 19) - memcpy (WFIFOP (fd, 94), "Nineteen Castles", 20); - else if (t == 20) - memcpy (WFIFOP (fd, 94), "Twenty Castles", 20); - else if (t == 21) - memcpy (WFIFOP (fd, 94), "Twenty One Castles", 20); - else if (t == 22) - memcpy (WFIFOP (fd, 94), "Twenty Two Castles", 20); - else if (t == 23) - memcpy (WFIFOP (fd, 94), "Twenty Three Castles", 20); - else if (t == 24) - memcpy (WFIFOP (fd, 94), "Twenty Four Castles", 20); - else if (t == MAX_GUILDCASTLE) - memcpy (WFIFOP (fd, 94), "Total Domination", 20); - else - memcpy (WFIFOP (fd, 94), "None Taken", 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; - 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, 24); - 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; - g = guild_search (sd->status.guild_id); - if (g == NULL) - return 0; - - 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) = 0; - 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->pc_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, 24); - 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; - 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, 24); - } - 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; - 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, 24); - 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) = 0; - 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; - 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; - struct guild *g; - - nullpo_retr (0, sd); - - fd = sd->fd; - g = guild_search (sd->status.guild_id); - if (g == NULL) - return 0; - WFIFOW (fd, 0) = 0x0162; - WFIFOW (fd, 4) = g->skill_point; - for (i = c = 0; i < MAX_GUILDSKILL; i++) - { - if (g->skill[i].id > 0) - { - 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) = - guild_skill_get_sp (id, g->skill[i].lv); - WFIFOW (fd, c * 37 + 16) = guild_skill_get_range (id); - memset (WFIFOP (fd, c * 37 + 18), 0, 24); - WFIFOB (fd, c * 37 + 42) = //up; - (g->skill[i].lv < guild_skill_get_max (id)) ? 1 : 0; - 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 (*g->mes1 == 0 && *g->mes2 == 0) - return 0; - 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; - WFIFOW (fd, 0) = 0x16a; - WFIFOL (fd, 2) = g->guild_id; - memcpy (WFIFOP (fd, 6), g->name, 24); - 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; - 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, 24); - 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, 24); - memcpy (WBUFP (buf, 26), mes, 40); - memcpy (WBUFP (buf, 66), "dummy", 24); - 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; - 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, 24); - 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 lbuf[255]; - unsigned char *buf = lbuf; - if (len + 32 >= sizeof (lbuf)) - buf = (unsigned char *)malloc (len + 32); - 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 != lbuf) - free (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; - WFIFOW (fd, 0) = 0x10e; - WFIFOW (fd, 2) = skill_num; - WFIFOW (fd, 4) = lv; - WFIFOW (fd, 6) = guild_skill_get_sp (skill_num, lv); - WFIFOW (fd, 8) = guild_skill_get_range (skill_num); - 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; - WFIFOW (fd, 0) = 0x171; - WFIFOL (fd, 2) = account_id; - memcpy (WFIFOP (fd, 6), name, 24); - 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; - 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; - 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; - 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,24); - 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; - 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); -} - -static void clif_emotion_towards (struct block_list *bl, - struct block_list *target, int type) -{ - unsigned char buf[8]; - int len = packet_len_table[0xc0]; - struct map_session_data *sd = (struct map_session_data *) target; - - nullpo_retv (bl); - nullpo_retv (target); - - if (target->type != BL_PC) - return; - - WBUFW (buf, 0) = 0xc0; - WBUFL (buf, 2) = bl->id; - WBUFB (buf, 6) = type; - - memcpy (WFIFOP (sd->fd, 0), buf, len); - WFIFOSET (sd->fd, len); -} - -/*========================================== - * トーキーボックス - *------------------------------------------ - */ -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, 80); - 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,24); - }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; -} -*/ -/*========================================== - * 座る - *------------------------------------------ - */ -void clif_sitting (int fd, 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); -} - -/*========================================== - * - *------------------------------------------ - */ -int clif_disp_onlyself (struct map_session_data *sd, char *mes, int len) -{ - unsigned char lbuf[255]; - unsigned char *buf = - (len + 32 >= sizeof (lbuf)) ? (unsigned char *)malloc (len + 32) : lbuf; - - nullpo_retr (0, sd); - - WBUFW (buf, 0) = 0x17f; - WBUFW (buf, 2) = len + 8; - memcpy (WBUFP (buf, 4), mes, len + 4); - - clif_send (buf, WBUFW (buf, 2), &sd->bl, SELF); - - if (buf != lbuf) - free (buf); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ - -int clif_GM_kickack (struct map_session_data *sd, int id) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - clif_parse_QuitGame (tsd->fd, tsd); - - return 0; -} - -/*========================================== - * Wis拒否許可応答 - *------------------------------------------ - */ -int clif_wisexin (struct map_session_data *sd, int type, int flag) -{ - int fd; - - nullpo_retr (0, sd); - - fd = sd->fd; - 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; - 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; - WFIFOW (fd, 0) = 0x1d3; - memcpy (WFIFOP (fd, 2), name, 24); - WFIFOB (fd, 26) = type; - WFIFOL (fd, 27) = 0; - WFIFOL (fd, 31) = bl->id; - WFIFOSET (fd, packet_len_table[0x1d3]); - - return; -} - -// 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[0x19b]); - - WBUFW (buf, 0) = 0x19b; - WBUFL (buf, 2) = bl->id; - WBUFL (buf, 6) = type; - - if (flag == 2) - { - struct map_session_data *sd = NULL; - int i; - 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) - clif_specialeffect (&sd->bl, type, 1); - } - } - - else if (flag == 1) - clif_send (buf, packet_len_table[0x19b], bl, SELF); - else if (!flag) - clif_send (buf, packet_len_table[0x19b], bl, AREA); - - return 0; - -} - -// ------------ -// clif_parse_* -// ------------ -// パケット読み取って色々操作 -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_WantToConnection (int fd, struct map_session_data *sd) -{ - struct map_session_data *old_sd; - int account_id; // account_id in the packet - - if (sd) - { - if (battle_config.error_log) - printf ("clif_parse_WantToConnection : invalid request?\n"); - return; - } - - if (RFIFOW (fd, 0) == 0x72) - { - account_id = RFIFOL (fd, 2); - } - else - return; // Not the auth packet - - WFIFOL (fd, 0) = account_id; - WFIFOSET (fd, 4); - - // if same account already connected, we disconnect the 2 sessions - if ((old_sd = map_id2sd (account_id)) != NULL) - { - clif_authfail_fd (fd, 2); // same id - clif_authfail_fd (old_sd->fd, 2); // same id - printf - ("clif_parse_WantToConnection: Double connection for account %d (sessions: #%d (new) and #%d (old)).\n", - account_id, fd, old_sd->fd); - } - else - { - CREATE (sd, struct map_session_data, 1); - session[fd]->session_data = sd; - sd->fd = fd; - - pc_setnewpc (sd, account_id, RFIFOL (fd, 6), RFIFOL (fd, 10), - RFIFOL (fd, 14), RFIFOB (fd, 18), fd); - - map_addiddb (&sd->bl); - - chrif_authreq (sd); - } - - return; -} - -/*========================================== - * 007d クライアント側マップ読み込み完了 - * map侵入時に必要なデータを全て送りつける - *------------------------------------------ - */ -void clif_parse_LoadEndAck (int fd, struct map_session_data *sd) -{ -// struct item_data* item; - int i; - nullpo_retv (sd); - - if (sd->bl.prev != NULL) - return; - - // 接続ok時 - //clif_authok(); - if (sd->npc_id) - npc_event_dequeue (sd); - clif_skillinfoblock (sd); - pc_checkitem (sd); - //guild_info(); - - // 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); - // 119 - // 78 - - if (battle_config.pc_invincible_time > 0) - { - if (map[sd->bl.m].flag.gvg) - 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); - - // pvp - if (sd->pvp_timer != -1 && !battle_config.pk_mode) - 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] - 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; - } - clif_set0199 (sd->fd, 1); - } - else - { - sd->pvp_timer = -1; - } - if (map[sd->bl.m].flag.gvg) - { - clif_set0199 (sd->fd, 3); - } - - if (sd->state.connect_new) - { - sd->state.connect_new = 0; - if (sd->status.pc_class != sd->view_class) - clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); - -/* 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 - clif_changelook (&sd->bl, LOOK_WEAPON, 0); - if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0) - clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, - sd->status.clothes_color); - - if (sd->status.hp < sd->status.max_hp >> 2 - && pc_checkskill (sd, SM_AUTOBERSERK) > 0 - && (sd->sc_data[SC_PROVOKE].timer == -1 - || sd->sc_data[SC_PROVOKE].val2 == 0)) - // オートバーサーク発動 - skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, 0); - -// if(time(&timer) < ((weddingtime=pc_readglobalreg(sd,"PC_WEDDING_TIME")) + 3600)) -// skill_status_change_start(&sd->bl,SC_WEDDING,0,weddingtime,0,0,36000,0); - - if (battle_config.muting_players && sd->status.manner < 0) - skill_status_change_start (&sd->bl, SC_NOCHAT, 0, 0, 0, 0, 0, 0); - - // option - clif_changeoption (&sd->bl); - if (sd->sc_data[SC_TRICKDEAD].timer != -1) - skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); - if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 - && !battle_check_undead (7, sd->def_ele)) - skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); - if (sd->special_state.infinite_endure - && sd->sc_data[SC_ENDURE].timer == -1) - skill_status_change_start (&sd->bl, SC_ENDURE, 10, 1, 0, 0, 0, 0); - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0002 - && sd->status.inventory[i].broken == 1) - skill_status_change_start (&sd->bl, SC_BROKNWEAPON, 0, 0, 0, 0, 0, - 0); - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0010 - && sd->status.inventory[i].broken == 1) - skill_status_change_start (&sd->bl, SC_BROKNARMOR, 0, 0, 0, 0, 0, - 0); - } - -// clif_changelook_accessories(sd, NULL); - - 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, 0, sd); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_TickSend (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - sd->client_tick = RFIFOL (fd, 2); - sd->server_tick = gettick (); - clif_servertick (sd); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_WalkToXY (int fd, struct map_session_data *sd) -{ - int x, y; - - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - - if (sd->npc_id != 0 || sd->state.storage_flag) - return; - - if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) <= 0) // フリーキャスト - return; - - if (sd->chatID) - return; - - if (sd->canmove_tick > gettick ()) - return; - - // ステータス異常やハイディング中(トンネルドライブ無)で動けない - if ((sd->opt1 > 0 && sd->opt1 != 6) || 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)) //合奏スキル演奏中は動けない - return; - if ((sd->status.option & 2) && pc_checkskill (sd, RG_TUNNELDRIVE) <= 0) - return; - - if (sd->invincible_timer != -1) - pc_delinvincibletimer (sd); - - pc_stopattack (sd); - - x = RFIFOB (fd, 2) * 4 + (RFIFOB (fd, 3) >> 6); - y = ((RFIFOB (fd, 3) & 0x3f) << 4) + (RFIFOB (fd, 4) >> 4); - 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; - - nullpo_retv (sd); - - WFIFOW (fd, 0) = 0x18b; - if ((!pc_isdead (sd) - && (sd->opt1 - || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) - || sd->skilltimer != -1 || (DIFF_TICK (tick, sd->canact_tick) < 0) - || (sd->sc_data && sd->sc_data[SC_DANCING].timer != -1 - && sd->sc_data[SC_DANCING].val4 - && (sg = (struct skill_unit_group *) sd->sc_data[SC_DANCING].val2) - && sg->src_id == sd->bl.id)) - { - WFIFOW (fd, 2) = 1; - WFIFOSET (fd, packet_len_table[0x18b]); - return; - } - - /* Rovert's prevent logout option fixed [Valaris] */ - if ((battle_config.prevent_logout - && (gettick () - sd->canlog_tick) >= 10000) - || (!battle_config.prevent_logout)) - { - clif_setwaitclose (fd); - WFIFOW (fd, 2) = 0; - } - else - { - WFIFOW (fd, 2) = 1; - } - WFIFOSET (fd, packet_len_table[0x18b]); - -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_GetCharNameRequest (int fd, struct map_session_data *sd) -{ - struct block_list *bl; - int account_id; - - account_id = RFIFOL (fd, 2); - bl = map_id2bl (account_id); - if (bl == NULL) - return; - - WFIFOW (fd, 0) = 0x95; - WFIFOL (fd, 2) = account_id; - - switch (bl->type) - { - case BL_PC: - { - struct map_session_data *ssd = (struct map_session_data *) bl; - - nullpo_retv (ssd); - - if (ssd->state.shroud_active) - memset (WFIFOP (fd, 6), 0, 24); - else - memcpy (WFIFOP (fd, 6), ssd->status.name, 24); - WFIFOSET (fd, packet_len_table[0x95]); - - struct guild *g = NULL; - struct party *p = NULL; - - char *guild_name = "", *guild_pos = "", *party_name = ""; - - int send = 0; - - if (ssd->status.guild_id > 0 && (g = guild_search (ssd->status.guild_id)) != NULL) - { - // there used to be a comment near here, but the code has changed slightly - // ギルド所属ならパケット0195を返す - // google says that means: 0195 return if the packet belongs Guild - int i, ps = -1; - for (i = 0; i < g->max_member; i++) - { - if (g->member[i].account_id == ssd->status.account_id) - ps = g->member[i].position; - } - if (ps >= 0 && ps < MAX_GUILDPOSITION) - { - guild_name = g->name; - guild_pos = g->position[ps].name; - send = 1; - } - } - if (ssd->status.party_id > 0 && (p = party_search (ssd->status.party_id)) != NULL) - { - party_name = p->name; - send = 1; - } - - if (send) - { - WFIFOW (fd, 0) = 0x195; - WFIFOL (fd, 2) = account_id; - memcpy (WFIFOP (fd, 6), party_name, 24); - memcpy (WFIFOP (fd, 30), guild_name, 24); - memcpy (WFIFOP (fd, 54), guild_pos, 24); - memcpy (WFIFOP (fd, 78), guild_pos, 24); // We send this value twice because the client expects it - WFIFOSET (fd, packet_len_table[0x195]); - - } - - if (pc_isGM(sd) >= battle_config.hack_info_GM_level) - { - in_addr_t ip = ssd->ip; - WFIFOW (fd, 0) = 0x20C; - - // Mask the IP using the char-server password - if (battle_config.mask_ip_gms) - ip = MD5_ip(chrif_getpasswd (), ssd->ip); - - WFIFOL (fd, 2) = account_id; - WFIFOL (fd, 6) = ip; - WFIFOSET (fd, packet_len_table[0x20C]); - } - - } - break; - case BL_NPC: - memcpy (WFIFOP (fd, 6), ((struct npc_data *) bl)->name, 24); - { - char *start = WFIFOP (fd, 6); - char *end = strchr (start, '#'); // [fate] elim hashed out/invisible names for the client - if (end) - while (*end) - *end++ = 0; - - // [fate] Elim preceding underscores for (hackish) name position fine-tuning - while (*start == '_') - *start++ = ' '; - } - WFIFOSET (fd, packet_len_table[0x95]); - break; - case BL_MOB: - { - struct mob_data *md = (struct mob_data *) bl; - - nullpo_retv (md); - - memcpy (WFIFOP (fd, 6), md->name, 24); - WFIFOSET (fd, packet_len_table[0x95]); - } - break; - default: - if (battle_config.error_log) - printf ("clif_parse_GetCharNameRequest : bad type %d(%d)\n", - bl->type, account_id); - break; - } -} - -/*========================================== - * Validate and process transmission of a - * global/public message. - * - * (S 008c <len>.w <message>.?B) - *------------------------------------------ - */ -void clif_parse_GlobalMessage (int fd, struct map_session_data *sd) -{ - int msg_len = RFIFOW (fd, 2) - 4; /* Header (2) + length (2). */ - size_t message_len = 0; - char *buf = NULL; - char *message = NULL; /* The message text only. */ - - nullpo_retv (sd); - - if (!(buf = clif_validate_chat (sd, 2, &message, &message_len))) - { - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - if (is_atcommand (fd, sd, message, 0) != AtCommand_None - || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 - || sd->sc_data[SC_NOCHAT].timer != -1)))//チャット禁止 - { - free (buf); - return; - } - - if (!magic_message (sd, buf, msg_len)) - { - /* Don't send chat that results in an automatic ban. */ - if (tmw_CheckChatSpam (sd, message)) - { - free (buf); - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - /* It's not a spell/magic message, so send the message to others. */ - WBUFW (buf, 0) = 0x8d; - WBUFW (buf, 2) = msg_len + 8; /* Header (2) + length (2) + ID (4). */ - WBUFL (buf, 4) = sd->bl.id; - - clif_send (buf, msg_len + 8, &sd->bl, - sd->chatID ? CHAT_WOS : AREA_CHAT_WOC); - } - - /* Send the message back to the speaker. */ - memcpy (WFIFOP (fd, 0), RFIFOP (fd, 0), RFIFOW (fd, 2)); - WFIFOW (fd, 0) = 0x8e; - WFIFOSET (fd, WFIFOW (fd, 2)); - - free (buf); - return; -} - -int clif_message (struct block_list *bl, char *msg) -{ - unsigned short msg_len = strlen (msg) + 1; - unsigned char buf[512]; - - if (msg_len + 16 > 512) - return 0; - - 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); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_MapMove (int fd, struct map_session_data *sd) -{ -// /m /mapmove (as @rura GM command) - char output[100]; - char map_name[17]; - - nullpo_retv (sd); - - memset (output, '\0', sizeof (output)); - memset (map_name, '\0', sizeof (map_name)); - - if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && - (pc_isGM (sd) >= get_atcommand_level (AtCommand_MapMove))) - { - memcpy (map_name, RFIFOP (fd, 2), 16); - sprintf (output, "%s %d %d", map_name, RFIFOW (fd, 18), - RFIFOW (fd, 20)); - log_atcommand (sd, "@warp %s", output); - atcommand_warp (fd, sd, "@warp", output); - } - - return; -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_ChangeDir (int fd, struct map_session_data *sd) -{ - unsigned char buf[64]; - short dir; - - nullpo_retv (sd); - -// RFIFOW(fd,2); // Apparently does nothing? - dir = RFIFOB (fd, 4); - - if (dir == sd->dir) - return; - - pc_setdir (sd, dir); - - WBUFW (buf, 0) = 0x9c; - WBUFL (buf, 2) = sd->bl.id; - WBUFW (buf, 6) = 0; - WBUFB (buf, 8) = dir; - if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] - clif_send (buf, packet_len_table[0x9c], &sd->bl, AREA); - else - clif_send (buf, packet_len_table[0x9c], &sd->bl, AREA_WOS); - -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_Emotion (int fd, struct map_session_data *sd) -{ - unsigned char buf[64]; - - nullpo_retv (sd); - - if (battle_config.basic_skill_check == 0 - || pc_checkskill (sd, NV_EMOTE) >= 1) - { - 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) -{ - 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; - - nullpo_retv (sd); - - 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_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, 2); - action_type = RFIFOB (fd, 6); - - switch (action_type) - { - case 0x00: // once attack - case 0x07: // continuous attack - if (sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == 22 - || sd->status.option & OPTION_HIDE) - 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); - if (sd->attacktarget > 0) // [Valaris] - sd->attacktarget = 0; - pc_attack (sd, target_id, action_type != 0); - break; - case 0x02: // sitdown - pc_stop_walking (sd, 1); - skill_gangsterparadise (sd, 1); // ギャングスターパラダイス設定 - pc_setsit (sd); - clif_sitting (fd, sd); - break; - case 0x03: // standup - skill_gangsterparadise (sd, 0); // ギャングスターパラダイス解除 - pc_setstand (sd); - WBUFW (buf, 0) = 0x8a; - WBUFL (buf, 2) = sd->bl.id; - WBUFB (buf, 26) = 3; - clif_send (buf, packet_len_table[0x8a], &sd->bl, AREA); - break; - } -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_Restart (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - 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); - } - break; - case 0x01: - /*if(!pc_isdead(sd) && (sd->opt1 || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) - * return; */ - - /* Rovert's Prevent logout option - Fixed [Valaris] */ - if ((battle_config.prevent_logout - && (gettick () - sd->canlog_tick) >= 10000) - || (!battle_config.prevent_logout)) - { - chrif_charselectreq (sd); - } - else - { - WFIFOW (fd, 0) = 0x18b; - WFIFOW (fd, 2) = 1; - - WFIFOSET (fd, packet_len_table[0x018b]); - } - break; - } -} - -/*========================================== - * Validate and process transmission of a - * whisper/private message. - * - * (S 0096 <len>.w <nick>.24B <message>.?B) - * - * rewritten by [Yor], then partially by - * [remoitnane] - *------------------------------------------ - */ -void clif_parse_Wis (int fd, struct map_session_data *sd) -{ - size_t message_len = 0; - char *buf = NULL; - char *message = NULL; /* The message text only. */ - struct map_session_data *dstsd = NULL; - - nullpo_retv (sd); - - if (!(buf = clif_validate_chat (sd, 1, &message, &message_len))) - { - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - if (is_atcommand (fd, sd, message, 0) != AtCommand_None - || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 - || sd->sc_data[SC_NOCHAT].timer != -1))) - { - free (buf); - return; - } - - /* Don't send chat that results in an automatic ban. */ - if (tmw_CheckChatSpam (sd, message)) - { - free (buf); - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - /* - * The player is not on this server. Only send the whisper if the name is - * exactly the same, because if there are multiple map-servers and a name - * conflict (for instance, "Test" versus "test"), the char-server must - * settle the discrepancy. - */ - if (!(dstsd = map_nick2sd (RFIFOP (fd, 4))) - || strcmp (dstsd->status.name, RFIFOP (fd, 4)) != 0) - intif_wis_message (sd, RFIFOP (fd, 4), message, RFIFOW (fd, 2) - 28); - else - { - /* Refuse messages addressed to self. */ - if (dstsd->fd == fd) - { - /* "You cannot page yourself." */ - char *mes = msg_txt (504); - clif_wis_message (fd, wisp_server_name, mes, strlen (mes) + 1); - } - else - { - /* The target is ignoring all whispers. */ - if (dstsd->ignoreAll == 1) - /* Ignored by target. */ - clif_wis_end (fd, 2); - else - { - int i; - size_t end = sizeof (dstsd->ignore) / sizeof (dstsd->ignore[0]); - - /* See if the source player is being ignored. */ - for (i = 0; i < end; ++i) - if (strcmp (dstsd->ignore[i].name, sd->status.name) == 0) - { - /* Ignored by target. */ - clif_wis_end (fd, 2); - break; - } - - /* The player is not being ignored. */ - if (i == end) - { - clif_wis_message (dstsd->fd, sd->status.name, message, - RFIFOW (fd, 2) - 28); - /* The whisper was sent successfully. */ - clif_wis_end (fd, 0); - } - } - } - } - - free (buf); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_GMmessage (int fd, struct map_session_data *sd) -{ - char m[512]; - char output[200]; - nullpo_retv (sd); - - if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && - (pc_isGM (sd) >= get_atcommand_level (AtCommand_Broadcast))) - { - strncpy (m, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); - m[RFIFOW (fd, 2) - 4] = 0; - log_atcommand (sd, "/announce %s", m); - - memset (output, '\0', sizeof (output)); - snprintf (output, 199, "%s : %s", sd->status.name, m); - - intif_GMmessage (output, strlen (output) + 1, 0); - } -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_TakeItem (int fd, struct map_session_data *sd) -{ - struct flooritem_data *fitem; - int map_object_id; - - nullpo_retv (sd); - - map_object_id = RFIFOL (fd, 2); - fitem = (struct flooritem_data *) map_id2bl (map_object_id); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - - if (sd->npc_id != 0 || sd->opt1 > 0 || (sd->sc_data && - (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))) //会話禁止 - return; - - if (fitem == NULL || fitem->bl.m != sd->bl.m) - return; - - if (abs (sd->bl.x - fitem->bl.x) >= 2 - || abs (sd->bl.y - fitem->bl.y) >= 2) - return; // too far away to pick up - - if (sd->state.shroud_active && sd->state.shroud_disappears_on_pickup) - magic_unshroud (sd); - - pc_takeitem (sd, fitem); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_DropItem (int fd, struct map_session_data *sd) -{ - int item_index, item_amount; - - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - if (map[sd->bl.m].flag.no_player_drops) - { - clif_displaymessage(sd->fd, "Can't drop items here."); - return; - } - if (sd->npc_id != 0 || sd->opt1 > 0 || - (sd->sc_data && (sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター - sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り - sd->sc_data[SC_BERSERK].timer != -1))) //バーサーク - { - clif_displaymessage(sd->fd, "Can't drop items right now."); - return; - } - - item_index = RFIFOW (fd, 2) - 2; - item_amount = RFIFOW (fd, 4); - - pc_dropitem (sd, item_index, item_amount); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_UseItem (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - if (sd->npc_id != 0 || sd->opt1 > 0 || (sd->sc_data && - (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))) //会話禁止 - return; - - if (sd->invincible_timer != -1) - pc_delinvincibletimer (sd); - - pc_useitem (sd, RFIFOW (fd, 2) - 2); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_EquipItem (int fd, struct map_session_data *sd) -{ - int index; - - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - index = RFIFOW (fd, 2) - 2; - if (sd->npc_id != 0) - return; - if (sd->sc_data - && (sd->sc_data[SC_BLADESTOP].timer != -1 - || sd->sc_data[SC_BERSERK].timer != -1)) - return; - - if (sd->status.inventory[index].identify != 1) - { // 未鑑定 - // Bjorn: Auto-identify items when equipping them as there - // is no nice way to do this in the client yet. - 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 == 10) - RFIFOW (fd, 4) = 0x8000; // 矢を無理やり装備できるように(−−; - pc_equipitem (sd, index, RFIFOW (fd, 4)); - } -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_UnequipItem (int fd, struct map_session_data *sd) -{ - int index; - - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - index = RFIFOW (fd, 2) - 2; - if (sd->status.inventory[index].broken == 1 && sd->sc_data - && sd->sc_data[SC_BROKNWEAPON].timer != -1) - skill_status_change_end (&sd->bl, SC_BROKNWEAPON, -1); - if (sd->status.inventory[index].broken == 1 && sd->sc_data - && sd->sc_data[SC_BROKNARMOR].timer != -1) - skill_status_change_end (&sd->bl, SC_BROKNARMOR, -1); - if (sd->sc_data - && (sd->sc_data[SC_BLADESTOP].timer != -1 - || sd->sc_data[SC_BERSERK].timer != -1)) - return; - - if (sd->npc_id != 0 || sd->opt1 > 0) - return; - pc_unequipitem (sd, index, 0); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcClicked (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - if (pc_isdead (sd)) - { - clif_clearchar_area (&sd->bl, 1); - return; - } - if (sd->npc_id != 0) - return; - npc_click (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcBuySellSelected (int fd, struct map_session_data *sd) -{ - 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; - - n = (RFIFOW (fd, 2) - 4) / 4; - item_list = (unsigned short *) RFIFOP (fd, 4); - - fail = npc_buylist (sd, n, item_list); - - 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; - - n = (RFIFOW (fd, 2) - 4) / 4; - item_list = (unsigned short *) RFIFOP (fd, 4); - - fail = npc_selllist (sd, n, item_list); - - 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) -{ - chat_createchat (sd, RFIFOW (fd, 4), RFIFOB (fd, 6), RFIFOP (fd, 7), - RFIFOP (fd, 15), RFIFOW (fd, 2) - 15); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_ChatAddMember (int fd, struct map_session_data *sd) -{ - chat_joinchat (sd, RFIFOL (fd, 2), RFIFOP (fd, 6)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_ChatRoomStatusChange (int fd, struct map_session_data *sd) -{ - chat_changechatstatus (sd, RFIFOW (fd, 4), RFIFOB (fd, 6), RFIFOP (fd, 7), - RFIFOP (fd, 15), RFIFOW (fd, 2) - 15); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_ChangeChatOwner (int fd, struct map_session_data *sd) -{ - chat_changechatowner (sd, RFIFOP (fd, 6)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_KickFromChat (int fd, struct map_session_data *sd) -{ - chat_kickchat (sd, 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) -{ - nullpo_retv (sd); - - if (battle_config.basic_skill_check == 0 - || pc_checkskill (sd, NV_TRADE) >= 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) -{ - nullpo_retv (sd); - - trade_tradeack (sd, RFIFOB (sd->fd, 2)); -} - -/*========================================== - * アイテム追加 - *------------------------------------------ - */ -void clif_parse_TradeAddItem (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - 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_TradeCansel (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) -{ - nullpo_retv (sd); - - if (sd->npc_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) -{ - nullpo_retv (sd); - - if (sd->npc_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) -{ - if (pc_isriding (sd)) - { // jobchange when removing peco [Valaris] - if (sd->status.pc_class == 13) - sd->status.pc_class = sd->view_class = 7; - - if (sd->status.pc_class == 21) - sd->status.pc_class = sd->view_class = 14; - - if (sd->status.pc_class == 4014) - sd->status.pc_class = sd->view_class = 4008; - - if (sd->status.pc_class == 4022) - sd->status.pc_class = sd->view_class = 4015; - } - - pc_setoption (sd, 0); -} - -/*========================================== - * チェンジカート - *------------------------------------------ - */ -void clif_parse_ChangeCart (int fd, struct map_session_data *sd) -{ - pc_setcart (sd, RFIFOW (fd, 2)); -} - -/*========================================== - * ステータスアップ - *------------------------------------------ - */ -void clif_parse_StatusUp (int fd, struct map_session_data *sd) -{ - pc_statusup (sd, RFIFOW (fd, 2)); -} - -/*========================================== - * スキルレベルアップ - *------------------------------------------ - */ -void clif_parse_SkillUp (int fd, struct map_session_data *sd) -{ - 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 (); - - nullpo_retv (sd); - - if (map[sd->bl.m].flag.noskill) - return; - if (sd->chatID || sd->npc_id != 0 || sd->state.storage_flag) - return; - - skilllv = RFIFOW (fd, 2); - skillnum = RFIFOW (fd, 4); - target_id = RFIFOL (fd, 6); - - if (sd->skilltimer != -1) - { - if (skillnum != SA_CASTCANCEL) - 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 == 22) - 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_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_CHAINCRUSH))) - { - if (!sd->state.skill_flag) - { - sd->state.skill_flag = 1; - clif_skillinfo (sd, MO_EXTREMITYFIST, 1, -1); - return; - } - else if (sd->bl.id == target_id) - { - clif_skillinfo (sd, MO_EXTREMITYFIST, 1, -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_UseSkillToPos (int fd, struct map_session_data *sd) -{ - int skillnum, skilllv, lv, x, y; - unsigned int tick = gettick (); - int skillmoreinfo; - - nullpo_retv (sd); - - if (map[sd->bl.m].flag.noskill) - return; - if (sd->npc_id != 0 || sd->state.storage_flag) - return; - if (sd->chatID) - return; - - skillmoreinfo = -1; - skilllv = RFIFOW (fd, 2); - skillnum = RFIFOW (fd, 4); - x = RFIFOW (fd, 6); - y = RFIFOW (fd, 8); - if (RFIFOW (fd, 0) == 0x190) - skillmoreinfo = 10; - - if (skillmoreinfo != -1) - { - if (pc_issit (sd)) - { - clif_skill_fail (sd, skillnum, 0, 0); - return; - } - memcpy (talkie_mes, RFIFOP (fd, skillmoreinfo), 80); - } - - 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 == 22) - 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); - } - } -} - -/*========================================== - * スキル使用(map指定) - *------------------------------------------ - */ -void clif_parse_UseSkillMap (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - if (map[sd->bl.m].flag.noskill) - return; - if (sd->chatID) - return; - - if (sd->npc_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 == 22))) - return; - - if (sd->invincible_timer != -1) - pc_delinvincibletimer (sd); - - skill_castend_map (sd, RFIFOW (fd, 2), RFIFOP (fd, 4)); -} - -/*========================================== - * メモ要求 - *------------------------------------------ - */ -void clif_parse_RequestMemo (int fd, struct map_session_data *sd) -{ - pc_memo (sd, -1); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcSelectMenu (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - sd->npc_menu = RFIFOB (fd, 6); - map_scriptcont (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcNextClicked (int fd, struct map_session_data *sd) -{ - map_scriptcont (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcAmountInput (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - -#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_ - - map_scriptcont (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * Process string-based input for an NPC. - * - * (S 01d5 <len>.w <npc_ID>.l <message>.?B) - *------------------------------------------ - */ -void clif_parse_NpcStringInput (int fd, struct map_session_data *sd) -{ - int len; - nullpo_retv (sd); - - len = RFIFOW (fd, 2) - 8; - - /* - * If we check for equal to 0, too, we'll freeze clients that send (or - * claim to have sent) an "empty" message. - */ - if (len < 0) - return; - - if (len >= sizeof (sd->npc_str) - 1) - { - printf ("clif_parse_NpcStringInput(): Input string too long!\n"); - len = sizeof (sd->npc_str) - 1; - } - - if (len > 0) - strncpy (sd->npc_str, RFIFOP (fd, 8), len); - sd->npc_str[len] = '\0'; - - map_scriptcont (sd, RFIFOL (fd, 4)); -} - -/*========================================== - * - *------------------------------------------ - */ -void clif_parse_NpcCloseClicked (int fd, struct map_session_data *sd) -{ - map_scriptcont (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * アイテム鑑定 - *------------------------------------------ - */ -void clif_parse_ItemIdentify (int fd, struct map_session_data *sd) -{ - pc_item_identify (sd, RFIFOW (fd, 2) - 2); -} - -/*========================================== - * オートスペル受信 - *------------------------------------------ - */ -void clif_parse_AutoSpell (int fd, struct map_session_data *sd) -{ - skill_autospell (sd, RFIFOW (fd, 2)); -} - -/*========================================== - * カード使用 - *------------------------------------------ - */ -void clif_parse_UseCard (int fd, struct map_session_data *sd) -{ - clif_use_card (sd, RFIFOW (fd, 2) - 2); -} - -/*========================================== - * カード挿入装備選択 - *------------------------------------------ - */ -void clif_parse_InsertCard (int fd, struct map_session_data *sd) -{ - 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; - - char_id = RFIFOL (fd, 2); - clif_solved_charname (sd, char_id); -} - -/*========================================== - * 0197 /resetskill /resetstate - *------------------------------------------ - */ -void clif_parse_ResetChar (int fd, struct map_session_data *sd) -{ - nullpo_retv (sd); - - if (battle_config.atc_gmonly == 0 || pc_isGM (sd)) - { - switch (RFIFOW (fd, 2)) - { - case 0: - log_atcommand (sd, "@charstreset %s", sd->status.name); - if (pc_isGM (sd) >= - get_atcommand_level (AtCommand_ResetState)) - pc_resetstate (sd); - break; - case 1: - log_atcommand (sd, "@charskreset %s", sd->status.name); - if (pc_isGM (sd) >= - get_atcommand_level (AtCommand_ResetState)) - pc_resetskill (sd); - break; - } - } -} - -/*========================================== - * 019c /lb等 - *------------------------------------------ - */ -void clif_parse_LGMmessage (int fd, struct map_session_data *sd) -{ - unsigned char buf[64]; - - nullpo_retv (sd); - - 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; - - nullpo_retv (sd); - - item_index = RFIFOW (fd, 2) - 2; - item_amount = RFIFOL (fd, 4); - - if ((sd->npc_id != 0 && !sd->npc_flags.storage) || sd->trade_partner != 0 - || !sd->state.storage_flag) - 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; - - nullpo_retv (sd); - - item_index = RFIFOW (fd, 2) - 1; - item_amount = RFIFOL (fd, 4); - - if ((sd->npc_id != 0 && !sd->npc_flags.storage) || sd->trade_partner != 0 - || !sd->state.storage_flag) - return; - - 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) -{ - nullpo_retv (sd); - - if ((sd->npc_id != 0 && !sd->npc_flags.storage) || 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) -{ - nullpo_retv (sd); - - if ((sd->npc_id != 0 && !sd->npc_flags.storage) || 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) -{ - nullpo_retv (sd); - - if (sd->state.storage_flag == 1) - storage_storageclose (sd); - else if (sd->state.storage_flag == 2) - storage_guild_storageclose (sd); -} - -/*========================================== - * パーティを作る - * Process request to create a party. - * - * (S 00f9 <party_name>.24B) - *------------------------------------------ - */ -void clif_parse_CreateParty (int fd, struct map_session_data *sd) -{ - if (battle_config.basic_skill_check == 0 - || pc_checkskill (sd, NV_PARTY) >= 2) - { - party_create (sd, RFIFOP (fd, 2)); - } - else - clif_skill_fail (sd, 1, 0, 4); -} - -/*========================================== - * パーティを作る - * Process request to create a party. - * - * (S 01e8 <party_name>.24B <exp>.B <itm>.B) - * - * Note: Upstream eAthena uses this to - * specify experience/item sharing, - * respectively, but it was left - * incomplete here. - *------------------------------------------ - */ -void clif_parse_CreateParty2 (int fd, struct map_session_data *sd) -{ - if (battle_config.basic_skill_check == 0 - || pc_checkskill (sd, NV_PARTY) >= 2) - { - party_create (sd, RFIFOP (fd, 2)); - } - else - clif_skill_fail (sd, 1, 0, 4); -} - -/*========================================== - * パーティに勧誘 - * Process invitation to join a party. - * - * (S 00fc <account_ID>.l) - *------------------------------------------ - */ -void clif_parse_PartyInvite (int fd, struct map_session_data *sd) -{ - party_invite (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * パーティ勧誘返答 - * Process reply to party invitation. - * - * (S 00ff <account_ID>.l <flag>.l) - *------------------------------------------ - */ -void clif_parse_ReplyPartyInvite (int fd, struct map_session_data *sd) -{ - if (battle_config.basic_skill_check == 0 - || pc_checkskill (sd, NV_PARTY) >= 1) - { - party_reply_invite (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); - } - else - { - party_reply_invite (sd, RFIFOL (fd, 2), 0); - 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) -{ - party_removemember (sd, RFIFOL (fd, 2), RFIFOP (fd, 6)); -} - -/*========================================== - * パーティ設定変更要求 - *------------------------------------------ - */ -void clif_parse_PartyChangeOption (int fd, struct map_session_data *sd) -{ - party_changeoption (sd, RFIFOW (fd, 2), RFIFOW (fd, 4)); -} - -/*========================================== - * パーティメッセージ送信要求 - * Validate and process transmission of a - * party message. - * - * (S 0108 <len>.w <message>.?B) - *------------------------------------------ - */ -void clif_parse_PartyMessage (int fd, struct map_session_data *sd) -{ - size_t message_len = 0; - char *buf = NULL; - char *message = NULL; /* The message text only. */ - - nullpo_retv (sd); - - if (!(buf = clif_validate_chat (sd, 0, &message, &message_len))) - { - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - if (is_atcommand (fd, sd, message, 0) != AtCommand_None - || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 - || sd->sc_data[SC_NOCHAT].timer != -1))) //チャット禁止 - { - free (buf); - return; - } - - /* Don't send chat that results in an automatic ban. */ - if (tmw_CheckChatSpam (sd, message)) - { - free (buf); - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - party_send_message (sd, message, RFIFOW (fd, 2) - 4); - free (buf); -} - -/*========================================== - * /monster /item rewriten by [Yor] - *------------------------------------------ - */ -void clif_parse_GM_Monster_Item (int fd, struct map_session_data *sd) -{ - char monster_item_name[25]; - - nullpo_retv (sd); - - memset (monster_item_name, '\0', sizeof (monster_item_name)); - - if (battle_config.atc_gmonly == 0 || pc_isGM (sd)) - { - memcpy (monster_item_name, RFIFOP (fd, 2), 24); - - if (mobdb_searchname (monster_item_name) != 0) - { - if (pc_isGM (sd) >= get_atcommand_level (AtCommand_Monster)) - { - log_atcommand (sd, "@spawn %s", monster_item_name); - atcommand_spawn (fd, sd, "@spawn", monster_item_name); // as @spawn - } - } - else if (itemdb_searchname (monster_item_name) != NULL) - { - if (pc_isGM (sd) >= get_atcommand_level (AtCommand_Item)) - { - log_atcommand (sd, "@item %s", monster_item_name); - atcommand_item (fd, sd, "@item", monster_item_name); // as @item - } - } - - } -} - -/*========================================== - * ギルドを作る - * Process request to create a guild. - * - * (S 0165 <account_ID>.l <guild_name>.24B) - * - * Note: The account ID seems to be ignored. - *------------------------------------------ - */ -void clif_parse_CreateGuild (int fd, struct map_session_data *sd) -{ - guild_create (sd, RFIFOP (fd, 6)); -} - -/*========================================== - * ギルドマスターかどうか確認 - *------------------------------------------ - */ -void clif_parse_GuildCheckMaster (int fd, struct map_session_data *sd) -{ - clif_guild_masterormember (sd); -} - -/*========================================== - * ギルド情報要求 - *------------------------------------------ - */ -void clif_parse_GuildReqeustInfo (int fd, struct map_session_data *sd) -{ - 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) - printf ("clif: guild request info: unknown type %d\n", - RFIFOL (fd, 2)); - break; - } -} - -/*========================================== - * ギルド役職変更 - *------------------------------------------ - */ -void clif_parse_GuildChangePositionInfo (int fd, struct map_session_data *sd) -{ - struct guild *g; - int i, ps; - - nullpo_retv (sd); - - g = guild_search (sd->status.guild_id); - - if (g == NULL) - return; - - if ((ps = guild_getposition (sd, g)) < 0 - || (!(g->position[ps].mode & 0x0010) && strcmp (g->master, sd->status.name))) - return; - - for (i = 4; i < RFIFOW (fd, 2); i += 40) - { - guild_change_position (sd, RFIFOL (fd, i), RFIFOL (fd, i + 4), - RFIFOL (fd, i + 12), RFIFOP (fd, i + 16)); - } -} - -/*========================================== - * ギルドメンバ役職変更 - *------------------------------------------ - */ -void clif_parse_GuildChangeMemberPosition (int fd, - struct map_session_data *sd) -{ - struct guild *g; - int i, ps; - - nullpo_retv (sd); - - g = guild_search (sd->status.guild_id); - - if (g == NULL) - return; - - if ((ps = guild_getposition (sd, g)) < 0 - || (!(g->position[ps].mode & 0x0010) && strcmp (g->master, sd->status.name))) - return; - - 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 = guild_search (RFIFOL (fd, 2)); - if (g != NULL) - clif_guild_emblem (sd, g); -} - -/*========================================== - * ギルドエンブレム変更 - *------------------------------------------ - */ -void clif_parse_GuildChangeEmblem (int fd, struct map_session_data *sd) -{ - guild_change_emblem (sd, RFIFOW (fd, 2) - 4, RFIFOP (fd, 4)); -} - -/*========================================== - * ギルド告知変更 - *------------------------------------------ - */ -void clif_parse_GuildChangeNotice (int fd, struct map_session_data *sd) -{ - guild_change_notice (sd, RFIFOL (fd, 2), RFIFOP (fd, 6), RFIFOP (fd, 66)); -} - -/*========================================== - * ギルド勧誘 - *------------------------------------------ - */ -void clif_parse_GuildInvite (int fd, struct map_session_data *sd) -{ - guild_invite (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * ギルド勧誘返信 - *------------------------------------------ - */ -void clif_parse_GuildReplyInvite (int fd, struct map_session_data *sd) -{ - guild_reply_invite (sd, RFIFOL (fd, 2), RFIFOB (fd, 6)); -} - -/*========================================== - * ギルド脱退 - *------------------------------------------ - */ -void clif_parse_GuildLeave (int fd, struct map_session_data *sd) -{ - guild_leave (sd, RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), - RFIFOP (fd, 14)); -} - -/*========================================== - * ギルド追放 - *------------------------------------------ - */ -void clif_parse_GuildExplusion (int fd, struct map_session_data *sd) -{ - guild_explusion (sd, RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), - RFIFOP (fd, 14)); -} - -/*========================================== - * ギルド会話 - * Validate and process transmission of a - * guild message. - * - * (S 017e <len>.w <message>.?B) - *------------------------------------------ - */ -void clif_parse_GuildMessage (int fd, struct map_session_data *sd) -{ - size_t message_len = 0; - char *buf = NULL; - char *message = NULL; /* The message text only. */ - - nullpo_retv (sd); - - if (!(buf = clif_validate_chat (sd, 2, &message, &message_len))) - { - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - if (is_atcommand (fd, sd, message, 0) != AtCommand_None - || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 - || sd->sc_data[SC_NOCHAT].timer != -1))) //チャット禁止 - { - free (buf); - return; - } - - /* Don't send chat that results in an automatic ban. */ - if (tmw_CheckChatSpam (sd, message)) - { - free (buf); - /* "Your message could not be sent." */ - clif_displaymessage (fd, msg_txt (505)); - return; - } - - guild_send_message (sd, buf + 8, RFIFOW (fd, 2) - 4); - free (buf); -} - -/*========================================== - * ギルド同盟要求 - *------------------------------------------ - */ -void clif_parse_GuildRequestAlliance (int fd, struct map_session_data *sd) -{ - guild_reqalliance (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * ギルド同盟要求返信 - *------------------------------------------ - */ -void clif_parse_GuildReplyAlliance (int fd, struct map_session_data *sd) -{ - guild_reply_reqalliance (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); -} - -/*========================================== - * ギルド関係解消 - *------------------------------------------ - */ -void clif_parse_GuildDelAlliance (int fd, struct map_session_data *sd) -{ - guild_delalliance (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); -} - -/*========================================== - * ギルド敵対 - *------------------------------------------ - */ -void clif_parse_GuildOpposition (int fd, struct map_session_data *sd) -{ - guild_opposition (sd, RFIFOL (fd, 2)); -} - -/*========================================== - * ギルド解散 - *------------------------------------------ - */ -void clif_parse_GuildBreak (int fd, struct map_session_data *sd) -{ - guild_break (sd, 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 = RFIFOL (fd, 2); - - nullpo_retv (sd); - - 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; - log_atcommand (sd, "@kick %s", tsd->status.name); - 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[25]; - - nullpo_retv (sd); - - memset (player_name, '\0', sizeof (player_name)); - - if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && - (pc_isGM (sd) >= get_atcommand_level (AtCommand_Goto))) - { - memcpy (player_name, RFIFOP (fd, 2), 24); - log_atcommand (sd, "@goto %s", player_name); - atcommand_goto (fd, sd, "@goto", player_name); // as @jumpto - } - - return; -} - -/*========================================== - * /recall - *------------------------------------------ - */ -void clif_parse_Recall (int fd, struct map_session_data *sd) -{ // Added by RoVeRT - char player_name[25]; - - nullpo_retv (sd); - - memset (player_name, '\0', sizeof (player_name)); - - if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && - (pc_isGM (sd) >= get_atcommand_level (AtCommand_Recall))) - { - memcpy (player_name, RFIFOP (fd, 2), 24); - log_atcommand (sd, "@recall %s", player_name); - atcommand_recall (fd, sd, "@recall", player_name); // as @recall - } - - return; -} - -void clif_parse_GMHide (int fd, struct map_session_data *sd) -{ // Modified by [Yor] - nullpo_retv (sd); - - //printf("%2x %2x %2x\n", RFIFOW(fd,0), RFIFOW(fd,2), RFIFOW(fd,4)); // R 019d <Option_value>.2B <flag>.2B - if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && - (pc_isGM (sd) >= get_atcommand_level (AtCommand_Hide))) - { - log_atcommand (sd, "@hide"); - if (sd->status.option & OPTION_HIDE) - { // OPTION_HIDE = 0x40 - sd->status.option &= ~OPTION_HIDE; // OPTION_HIDE = 0x40 - /* "Invisible: Off." */ - clif_displaymessage (fd, msg_txt (10)); - } - else - { - sd->status.option |= OPTION_HIDE; // OPTION_HIDE = 0x40 - /* "Invisible: On." */ - clif_displaymessage (fd, msg_txt (11)); - } - clif_changeoption (&sd->bl); - } -} - -/*========================================== - * GMによるチャット禁止時間付与 - *------------------------------------------ - */ -void clif_parse_GMReqNoChat (int fd, struct map_session_data *sd) -{ - int tid = RFIFOL (fd, 2); - int type = RFIFOB (fd, 6); - int limit = RFIFOW (fd, 7); - struct block_list *bl = map_id2bl (tid); - struct map_session_data *dstsd; - int dstfd; - - nullpo_retv (sd); - - if (!battle_config.muting_players) - { - /* "Muting is disabled." */ - clif_displaymessage (fd, msg_txt (245)); - return; - } - - if (type == 0) - limit = 0 - limit; - if (bl->type == BL_PC && (dstsd = (struct map_session_data *) bl)) - { - if ((tid == bl->id && type == 2 && !pc_isGM (sd)) - || (pc_isGM (sd) > pc_isGM (dstsd))) - { - dstfd = dstsd->fd; - WFIFOW (dstfd, 0) = 0x14b; - WFIFOB (dstfd, 2) = (type == 2) ? 1 : type; - memcpy (WFIFOP (dstfd, 3), sd->status.name, 24); - WFIFOSET (dstfd, packet_len_table[0x14b]); - dstsd->status.manner -= limit; - if (dstsd->status.manner < 0) - skill_status_change_start (bl, SC_NOCHAT, 0, 0, 0, 0, 0, 0); - else - { - dstsd->status.manner = 0; - skill_status_change_end (bl, SC_NOCHAT, -1); - } - printf ("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 = RFIFOL (fd, 2); - - WFIFOW (fd, 0) = 0x1e0; - WFIFOL (fd, 2) = tid; - sprintf (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[1024]; - char *nick; // S 00cf <nick>.24B <type>.B: 00 (/ex nick) deny speech from nick, 01 (/in nick) allow speech from nick - int i; - int pos; - - memset (output, '\0', sizeof (output)); - - nick = RFIFOP (fd, 2); // speed up - //printf("Ignore: char '%s' state: %d\n", nick, RFIFOB(fd,26)); - // we ask for deny (we add nick only if it's not already exist - if (RFIFOB (fd, 26) == 0) - { // type - if (strlen (nick) >= 4 && strlen (nick) < 24) - { // do something only if nick can be exist - pos = -1; - for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); - i++) - { - if (strcmp (sd->ignore[i].name, nick) == 0) - break; - else if (pos == -1 && sd->ignore[i].name[0] == '\0') - pos = i; - } - WFIFOW (fd, 0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail - WFIFOB (fd, 2) = 0; - // if a position is found and name not found, we add it in the list - if (pos != -1 - && i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) - { - memcpy (sd->ignore[pos].name, nick, 24); - WFIFOB (fd, 3) = 0; // success - WFIFOSET (fd, packet_len_table[0x0d1]); - if (strcmp (wisp_server_name, nick) == 0) - { // to found possible bot users that automaticaly ignores 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, strlen (output) + 1); - // 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 - if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) - { - 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 that automaticaly ignores 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, strlen (output) + 1); - } - } - else - { - 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 that automaticaly ignores 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, strlen (output) + 1); - } - } - } - } - else - clif_wis_message (fd, wisp_server_name, - "It's impossible to block this player.", - strlen ("It's impossible to block this player.") - + 1); - // we ask for allow (we remove all same nick if exist) - } - else - { - if (strlen (nick) >= 4 && strlen (nick) < 24) - { // do something only if nick can be exist - WFIFOW (fd, 0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail - WFIFOB (fd, 2) = 1; - for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); - i++) - if (strcmp (sd->ignore[i].name, nick) == 0) - { - memset (sd->ignore[i].name, 0, - sizeof (sd->ignore[i].name)); - WFIFOB (fd, 3) = 0; // success - WFIFOSET (fd, packet_len_table[0x0d1]); - break; - } - if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) - { - 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); - } - } - else - clif_wis_message (fd, wisp_server_name, - "It's impossible to unblock this player.", - strlen - ("It's impossible to unblock this player.") + - 1); - } - -// for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); 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)); - if (RFIFOB (fd, 2) == 0) - { // S 00d0 <type>len.B: 00 (/exall) deny all speech, 01 (/inall) allow all speech - 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 - { - 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; -} - -void clif_parse_skillMessage (int fd, struct map_session_data *sd) -{ // Added by RoVeRT - int skillid, skilllv, x, y; - char *mes; - - skilllv = RFIFOW (fd, 2); - skillid = RFIFOW (fd, 4); - - y = RFIFOB (fd, 6); - x = RFIFOB (fd, 8); - - mes = RFIFOP (fd, 10); - - // skill 220 = graffiti -// printf("skill: %d %d location: %3d %3d message: %s\n", skillid, skilllv, x, y, (char*)mes); -} - -int monk (struct map_session_data *sd, struct block_list *target, int type) -{ -//R 01d1 <Monk id>L <Target monster id>L <Bool>L - int fd = sd->fd; - WFIFOW (fd, 0) = 0x1d1; - WFIFOL (fd, 2) = sd->bl.id; - WFIFOL (fd, 6) = target->id; - WFIFOL (fd, 10) = type; - WFIFOSET (fd, packet_len_table[0x1d1]); - - return 0; -} - -/*========================================== - * スパノビの/doridoriによるSPR2倍 - *------------------------------------------ - */ -void clif_parse_sn_doridori (int fd, struct map_session_data *sd) -{ - if (sd) - sd->doridori_counter = 1; - - return; -} - -/*========================================== - * スパノビの爆裂波動 - *------------------------------------------ - */ -void clif_parse_sn_explosionspirits (int fd, struct map_session_data *sd) -{ - if (sd) - { - int nextbaseexp = pc_nextbaseexp (sd); - struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); - if (battle_config.etc_log) - { - if (nextbaseexp != 0) - printf ("SuperNovice explosionspirits!! %d %d %d %d\n", - sd->bl.id, s_class.job, sd->status.base_exp, - (int) ((double) 1000 * sd->status.base_exp / - nextbaseexp)); - else - printf ("SuperNovice explosionspirits!! %d %d %d 000\n", - sd->bl.id, s_class.job, sd->status.base_exp); - } - if (s_class.job == 23 && 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); - skill_status_change_start (&sd->bl, - SkillStatusChangeTable - [MO_EXPLOSIONSPIRITS], 5, 0, 0, 0, - skill_get_time (MO_EXPLOSIONSPIRITS, - 5), 0); - } - } - return; -} - -// functions list. Rate is how many milliseconds are required between -// calls. Packets exceeding this rate will be dropped. flood_rates in -// map.h must be the same length as this table. rate 0 is default -// rate -1 is unlimited -typedef struct func_table -{ - void (*func)(int fd, struct map_session_data *sd); - int rate; -} func_table; -// *INDENT-OFF* -func_table clif_parse_func_table[0x220] = -{ - { NULL, 0 }, // 0 - { NULL, 0 }, // 1 - { NULL, 0 }, // 2 - { NULL, 0 }, // 3 - { NULL, 0 }, // 4 - { NULL, 0 }, // 5 - { NULL, 0 }, // 6 - { NULL, 0 }, // 7 - { NULL, 0 }, // 8 - { NULL, 0 }, // 9 - { NULL, 0 }, // a - { NULL, 0 }, // b - { NULL, 0 }, // c - { NULL, 0 }, // d - { NULL, 0 }, // e - { NULL, 0 }, // f - { NULL, 0 }, // 10 - { NULL, 0 }, // 11 - { NULL, 0 }, // 12 - { NULL, 0 }, // 13 - { NULL, 0 }, // 14 - { NULL, 0 }, // 15 - { NULL, 0 }, // 16 - { NULL, 0 }, // 17 - { NULL, 0 }, // 18 - { NULL, 0 }, // 19 - { NULL, 0 }, // 1a - { NULL, 0 }, // 1b - { NULL, 0 }, // 1c - { NULL, 0 }, // 1d - { NULL, 0 }, // 1e - { NULL, 0 }, // 1f - { NULL, 0 }, // 20 - { NULL, 0 }, // 21 - { NULL, 0 }, // 22 - { NULL, 0 }, // 23 - { NULL, 0 }, // 24 - { NULL, 0 }, // 25 - { NULL, 0 }, // 26 - { NULL, 0 }, // 27 - { NULL, 0 }, // 28 - { NULL, 0 }, // 29 - { NULL, 0 }, // 2a - { NULL, 0 }, // 2b - { NULL, 0 }, // 2c - { NULL, 0 }, // 2d - { NULL, 0 }, // 2e - { NULL, 0 }, // 2f - { NULL, 0 }, // 30 - { NULL, 0 }, // 31 - { NULL, 0 }, // 32 - { NULL, 0 }, // 33 - { NULL, 0 }, // 34 - { NULL, 0 }, // 35 - { NULL, 0 }, // 36 - { NULL, 0 }, // 37 - { NULL, 0 }, // 38 - { NULL, 0 }, // 39 - { NULL, 0 }, // 3a - { NULL, 0 }, // 3b - { NULL, 0 }, // 3c - { NULL, 0 }, // 3d - { NULL, 0 }, // 3e - { NULL, 0 }, // 3f - { NULL, 0 }, // 40 - { NULL, 0 }, // 41 - { NULL, 0 }, // 42 - { NULL, 0 }, // 43 - { NULL, 0 }, // 44 - { NULL, 0 }, // 45 - { NULL, 0 }, // 46 - { NULL, 0 }, // 47 - { NULL, 0 }, // 48 - { NULL, 0 }, // 49 - { NULL, 0 }, // 4a - { NULL, 0 }, // 4b - { NULL, 0 }, // 4c - { NULL, 0 }, // 4d - { NULL, 0 }, // 4e - { NULL, 0 }, // 4f - { NULL, 0 }, // 50 - { NULL, 0 }, // 51 - { NULL, 0 }, // 52 - { NULL, 0 }, // 53 - { NULL, 0 }, // 54 - { NULL, 0 }, // 55 - { NULL, 0 }, // 56 - { NULL, 0 }, // 57 - { NULL, 0 }, // 58 - { NULL, 0 }, // 59 - { NULL, 0 }, // 5a - { NULL, 0 }, // 5b - { NULL, 0 }, // 5c - { NULL, 0 }, // 5d - { NULL, 0 }, // 5e - { NULL, 0 }, // 5f - { NULL, 0 }, // 60 - { NULL, 0 }, // 61 - { NULL, 0 }, // 62 - { NULL, 0 }, // 63 - { NULL, 0 }, // 64 - { NULL, 0 }, // 65 - { NULL, 0 }, // 66 - { NULL, 0 }, // 67 - { NULL, 0 }, // 68 - { NULL, 0 }, // 69 - { NULL, 0 }, // 6a - { NULL, 0 }, // 6b - { NULL, 0 }, // 6c - { NULL, 0 }, // 6d - { NULL, 0 }, // 6e - { NULL, 0 }, // 6f - { NULL, 0 }, // 70 - { NULL, 0 }, // 71 - { clif_parse_WantToConnection, 0 }, // 72 - { NULL, 0 }, // 73 - { NULL, 0 }, // 74 - { NULL, 0 }, // 75 - { NULL, 0 }, // 76 - { NULL, 0 }, // 77 - { NULL, 0 }, // 78 - { NULL, 0 }, // 79 - { NULL, 0 }, // 7a - { NULL, 0 }, // 7b - { NULL, 0 }, // 7c - { clif_parse_LoadEndAck, -1 }, // 7d - { clif_parse_TickSend, 0 }, // 7e - { NULL, 0 }, // 7f - { NULL, 0 }, // 80 - { NULL, 0 }, // 81 - { NULL, 0 }, // 82 - { NULL, 0 }, // 83 - { NULL, 0 }, // 84 - { clif_parse_WalkToXY, -1 }, // 85 Walk code limits this on it's own - { NULL, 0 }, // 86 - { NULL, 0 }, // 87 - { NULL, 0 }, // 88 - { clif_parse_ActionRequest, 1000 }, // 89 Special case - see below - { NULL, 0 }, // 8a - { NULL, 0 }, // 8b - { clif_parse_GlobalMessage, 300 }, // 8c - { NULL, 0 }, // 8d - { NULL, 0 }, // 8e - { NULL, 0 }, // 8f - { clif_parse_NpcClicked, 500 }, // 90 - { NULL, 0 }, // 91 - { NULL, 0 }, // 92 - { NULL, 0 }, // 93 - { clif_parse_GetCharNameRequest, -1 }, // 94 - { NULL, 0 }, // 95 - { clif_parse_Wis, 300 }, // 96 - { NULL, 0 }, // 97 - { NULL, 0 }, // 98 - { clif_parse_GMmessage, 300 }, // 99 - { NULL, 0 }, // 9a - { clif_parse_ChangeDir, -1 }, // 9b - { NULL, 0 }, // 9c - { NULL, 0 }, // 9d - { NULL, 0 }, // 9e - { clif_parse_TakeItem, 400 }, // 9f - { NULL, 0 }, // a0 - { NULL, 0 }, // a1 - { clif_parse_DropItem, 50 }, // a2 - { NULL, 0 }, // a3 - { NULL, 0 }, // a4 - { NULL, 0 }, // a5 - { NULL, 0 }, // a6 - { clif_parse_UseItem, 0 }, // a7 - { NULL, 0 }, // a8 - { clif_parse_EquipItem, -1 }, // a9 Special case - outfit window (not implemented yet - needs to allow bursts) - { NULL, 0 }, // aa - { clif_parse_UnequipItem, -1 }, // ab Special case - outfit window (not implemented yet - needs to allow bursts) - { NULL, 0 }, // ac - { NULL, 0 }, // ad - { NULL, 0 }, // ae - { NULL, 0 }, // af - { NULL, 0 }, // b0 - { NULL, 0 }, // b1 - { clif_parse_Restart, 0 }, // b2 - { NULL, 0 }, // b3 - { NULL, 0 }, // b4 - { NULL, 0 }, // b5 - { NULL, 0 }, // b6 - { NULL, 0 }, // b7 - { clif_parse_NpcSelectMenu, 0 }, // b8 - { clif_parse_NpcNextClicked, -1 }, // b9 - { NULL, 0 }, // ba - { clif_parse_StatusUp, -1 }, // bb People click this very quickly - { NULL, 0 }, // bc - { NULL, 0 }, // bd - { NULL, 0 }, // be - { clif_parse_Emotion, 1000 }, // bf - { NULL, 0 }, // c0 - { clif_parse_HowManyConnections, 0 }, // c1 - { NULL, 0 }, // c2 - { NULL, 0 }, // c3 - { NULL, 0 }, // c4 - { clif_parse_NpcBuySellSelected, 0 }, // c5 - { NULL, 0 }, // c6 - { NULL, 0 }, // c7 - { clif_parse_NpcBuyListSend, -1 }, // c8 - { clif_parse_NpcSellListSend, -1 }, // c9 Selling multiple 1-slot items - { NULL, 0 }, // ca - { NULL, 0 }, // cb - { clif_parse_GMKick, 0 }, // cc - { NULL, 0 }, // cd - { NULL, 0 }, // ce - { clif_parse_PMIgnore, 0 }, // cf - { clif_parse_PMIgnoreAll, 0 }, // d0 - { NULL, 0 }, // d1 - { NULL, 0 }, // d2 - { NULL, 0 }, // d3 - { NULL, 0 }, // d4 - { clif_parse_CreateChatRoom, 1000 }, // d5 - { NULL, 0 }, // d6 - { NULL, 0 }, // d7 - { NULL, 0 }, // d8 - { clif_parse_ChatAddMember, 0 }, // d9 - { NULL, 0 }, // da - { NULL, 0 }, // db - { NULL, 0 }, // dc - { NULL, 0 }, // dd - { clif_parse_ChatRoomStatusChange, 0 }, // de - { NULL, 0 }, // df - { clif_parse_ChangeChatOwner, 0 }, // e0 - { NULL, 0 }, // e1 - { clif_parse_KickFromChat, 0 }, // e2 - { clif_parse_ChatLeave, 0 }, // e3 - { clif_parse_TradeRequest, 2000 }, // e4 - { NULL, 0 }, // e5 - { clif_parse_TradeAck, 0 }, // e6 - { NULL, 0 }, // e7 - { clif_parse_TradeAddItem, 0 }, // e8 - { NULL, 0 }, // e9 - { NULL, 0 }, // ea - { clif_parse_TradeOk, 0 }, // eb - { NULL, 0 }, // ec - { clif_parse_TradeCansel, 0 }, // ed - { NULL, 0 }, // ee - { clif_parse_TradeCommit, 0 }, // ef - { NULL, 0 }, // f0 - { NULL, 0 }, // f1 - { NULL, 0 }, // f2 - { clif_parse_MoveToKafra, -1 }, // f3 - { NULL, 0 }, // f4 - { clif_parse_MoveFromKafra, -1 }, // f5 - { NULL, 0 }, // f6 - { clif_parse_CloseKafra, 0 }, // f7 - { NULL, 0 }, // f8 - { clif_parse_CreateParty, 2000 }, // f9 - { NULL, 0 }, // fa - { NULL, 0 }, // fb - { clif_parse_PartyInvite, 2000 }, // fc - { NULL, 0 }, // fd - { NULL, 0 }, // fe - { clif_parse_ReplyPartyInvite, 0 }, // ff - { clif_parse_LeaveParty, 0 }, // 100 - { NULL, 0 }, // 101 - { clif_parse_PartyChangeOption, 0 }, // 102 - { clif_parse_RemovePartyMember, 0 }, // 103 - { NULL, 0 }, // 104 - { NULL, 0 }, // 105 - { NULL, 0 }, // 106 - { NULL, 0 }, // 107 - { clif_parse_PartyMessage, 300 }, // 108 - { NULL, 0 }, // 109 - { NULL, 0 }, // 10a - { NULL, 0 }, // 10b - { NULL, 0 }, // 10c - { NULL, 0 }, // 10d - { NULL, 0 }, // 10e - { NULL, 0 }, // 10f - { NULL, 0 }, // 110 - { NULL, 0 }, // 111 - { clif_parse_SkillUp, -1 }, // 112 - { clif_parse_UseSkillToId, 0 }, // 113 - { NULL, 0 }, // 114 - { NULL, 0 }, // 115 - { clif_parse_UseSkillToPos, 0 }, // 116 - { NULL, 0 }, // 117 - { clif_parse_StopAttack, 0 }, // 118 - { NULL, 0 }, // 119 - { NULL, 0 }, // 11a - { clif_parse_UseSkillMap, 0 }, // 11b - { NULL, 0 }, // 11c - { clif_parse_RequestMemo, 0 }, // 11d - { NULL, 0 }, // 11e - { NULL, 0 }, // 11f - { NULL, 0 }, // 120 - { NULL, 0 }, // 121 - { NULL, 0 }, // 122 - { NULL, 0 }, // 123 - { NULL, 0 }, // 124 - { NULL, 0 }, // 125 - { clif_parse_PutItemToCart, 0 }, // 126 - { clif_parse_GetItemFromCart, 0 }, // 127 - { clif_parse_MoveFromKafraToCart, 0 }, // 128 - { clif_parse_MoveToKafraFromCart, 0 }, // 129 - { clif_parse_RemoveOption, 0 }, // 12a - { NULL, 0 }, // 12b - { NULL, 0 }, // 12c - { NULL, 0 }, // 12d - { NULL, 0 }, // 12e - { NULL, 0 }, // 12f - { NULL, 0 }, // 130 - { NULL, 0 }, // 131 - { NULL, 0 }, // 132 - { NULL, 0 }, // 133 - { NULL, 0 }, // 134 - { NULL, 0 }, // 135 - { NULL, 0 }, // 136 - { NULL, 0 }, // 137 - { NULL, 0 }, // 138 - { NULL, 0 }, // 139 - { NULL, 0 }, // 13a - { NULL, 0 }, // 13b - { NULL, 0 }, // 13c - { NULL, 0 }, // 13d - { NULL, 0 }, // 13e - { clif_parse_GM_Monster_Item, 0 }, // 13f - { clif_parse_MapMove, 0 }, // 140 - { NULL, 0 }, // 141 - { NULL, 0 }, // 142 - { clif_parse_NpcAmountInput, 300 }, // 143 - { NULL, 0 }, // 144 - { NULL, 0 }, // 145 - { clif_parse_NpcCloseClicked, 300 }, // 146 - { NULL, 0 }, // 147 - { NULL, 0 }, // 148 - { clif_parse_GMReqNoChat, 0 }, // 149 - { NULL, 0 }, // 14a - { NULL, 0 }, // 14b - { NULL, 0 }, // 14c - { clif_parse_GuildCheckMaster, 0 }, // 14d - { NULL, 0 }, // 14e - { clif_parse_GuildReqeustInfo, 0 }, // 14f - { NULL, 0 }, // 150 - { clif_parse_GuildRequestEmblem, 0 }, // 151 - { NULL, 0 }, // 152 - { clif_parse_GuildChangeEmblem, 0 }, // 153 - { NULL, 0 }, // 154 - { clif_parse_GuildChangeMemberPosition, 0 }, // 155 - { NULL, 0 }, // 156 - { NULL, 0 }, // 157 - { NULL, 0 }, // 158 - { clif_parse_GuildLeave, 0 }, // 159 - { NULL, 0 }, // 15a - { clif_parse_GuildExplusion, 0 }, // 15b - { NULL, 0 }, // 15c - { clif_parse_GuildBreak, 0 }, // 15d - { NULL, 0 }, // 15e - { NULL, 0 }, // 15f - { NULL, 0 }, // 160 - { clif_parse_GuildChangePositionInfo, 0 }, // 161 - { NULL, 0 }, // 162 - { NULL, 0 }, // 163 - { NULL, 0 }, // 164 - { clif_parse_CreateGuild, 0 }, // 165 - { NULL, 0 }, // 166 - { NULL, 0 }, // 167 - { clif_parse_GuildInvite, 2000 }, // 168 - { NULL, 0 }, // 169 - { NULL, 0 }, // 16a - { clif_parse_GuildReplyInvite, 0 }, // 16b - { NULL, 0 }, // 16c - { NULL, 0 }, // 16d - { clif_parse_GuildChangeNotice, 0 }, // 16e - { NULL, 0 }, // 16f - { clif_parse_GuildRequestAlliance, 0 }, // 170 - { NULL, 0 }, // 171 - { clif_parse_GuildReplyAlliance, 0 }, // 172 - { NULL, 0 }, // 173 - { NULL, 0 }, // 174 - { NULL, 0 }, // 175 - { NULL, 0 }, // 176 - { NULL, 0 }, // 177 - { clif_parse_ItemIdentify, 0 }, // 178 - { NULL, 0 }, // 179 - { clif_parse_UseCard, 0 }, // 17a - { NULL, 0 }, // 17b - { clif_parse_InsertCard, 0 }, // 17c - { NULL, 0 }, // 17d - { clif_parse_GuildMessage, 300 }, // 17e - { NULL, 0 }, // 17f - { clif_parse_GuildOpposition, 0 }, // 180 - { NULL, 0 }, // 181 - { NULL, 0 }, // 182 - { clif_parse_GuildDelAlliance, 0 }, // 183 - { NULL, 0 }, // 184 - { NULL, 0 }, // 185 - { NULL, 0 }, // 186 - { NULL, 0 }, // 187 - { NULL, 0 }, // 188 - { NULL, 0 }, // 189 - { clif_parse_QuitGame, 0 }, // 18a - { NULL, 0 }, // 18b - { NULL, 0 }, // 18c - { NULL, 0 }, // 18d - { NULL, 0 }, // 18e - { NULL, 0 }, // 18f - { clif_parse_UseSkillToPos, 0 }, // 190 - { NULL, 0 }, // 191 - { NULL, 0 }, // 192 - { clif_parse_SolveCharName, 0 }, // 193 - { NULL, 0 }, // 194 - { NULL, 0 }, // 195 - { NULL, 0 }, // 196 - { clif_parse_ResetChar, 0 }, // 197 - { NULL, 0 }, // 198 - { NULL, 0 }, // 199 - { NULL, 0 }, // 19a - { NULL, 0 }, // 19b - { clif_parse_LGMmessage, 0 }, // 19c - { clif_parse_GMHide, 300 }, // 19d - { NULL, 0 }, // 19e - { NULL, 0 }, // 19f - { NULL, 0 }, // 1a0 - { NULL, 0 }, // 1a1 - { NULL, 0 }, // 1a2 - { NULL, 0 }, // 1a3 - { NULL, 0 }, // 1a4 - { NULL, 0 }, // 1a5 - { NULL, 0 }, // 1a6 - { NULL, 0 }, // 1a7 - { NULL, 0 }, // 1a8 - { NULL, 0 }, // 1a9 - { NULL, 0 }, // 1aa - { NULL, 0 }, // 1ab - { NULL, 0 }, // 1ac - { NULL, 0 }, // 1ad - { NULL, 0 }, // 1ae - { clif_parse_ChangeCart, 0 }, // 1af - { NULL, 0 }, // 1b0 - { NULL, 0 }, // 1b1 - { NULL, 0 }, // 1b2 - { NULL, 0 }, // 1b3 - { NULL, 0 }, // 1b4 - { NULL, 0 }, // 1b5 - { NULL, 0 }, // 1b6 - { NULL, 0 }, // 1b7 - { NULL, 0 }, // 1b8 - { NULL, 0 }, // 1b9 - { clif_parse_Shift, 300 }, // 1ba - { clif_parse_Shift, 300 }, // 1bb - { clif_parse_Recall, 300 }, // 1bc - { clif_parse_Recall, 300 }, // 1bd - { NULL, 0 }, // 1be - { NULL, 0 }, // 1bf - { NULL, 0 }, // 1c0 - { NULL, 0 }, // 1c1 - { NULL, 0 }, // 1c2 - { NULL, 0 }, // 1c3 - { NULL, 0 }, // 1c4 - { NULL, 0 }, // 1c5 - { NULL, 0 }, // 1c6 - { NULL, 0 }, // 1c7 - { NULL, 0 }, // 1c8 - { NULL, 0 }, // 1c9 - { NULL, 0 }, // 1ca - { NULL, 0 }, // 1cb - { NULL, 0 }, // 1cc - { NULL, 0 }, // 1cd - { clif_parse_AutoSpell, 0 }, // 1ce - { NULL, 0 }, // 1cf - { NULL, 0 }, // 1d0 - { NULL, 0 }, // 1d1 - { NULL, 0 }, // 1d2 - { NULL, 0 }, // 1d3 - { NULL, 0 }, // 1d4 - { clif_parse_NpcStringInput, 300 }, // 1d5 - { NULL, 0 }, // 1d6 - { NULL, 0 }, // 1d7 - { NULL, 0 }, // 1d8 - { NULL, 0 }, // 1d9 - { NULL, 0 }, // 1da - { NULL, 0 }, // 1db - { NULL, 0 }, // 1dc - { NULL, 0 }, // 1dd - { NULL, 0 }, // 1de - { clif_parse_GMReqNoChatCount, 0 }, // 1df - { NULL, 0 }, // 1e0 - { NULL, 0 }, // 1e1 - { NULL, 0 }, // 1e2 - { NULL, 0 }, // 1e3 - { NULL, 0 }, // 1e4 - { NULL, 0 }, // 1e5 - { NULL, 0 }, // 1e6 - { clif_parse_sn_doridori, 0 }, // 1e7 - { clif_parse_CreateParty2, 1000 }, // 1e8 - { NULL, 0 }, // 1e9 - { NULL, 0 }, // 1ea - { NULL, 0 }, // 1eb - { NULL, 0 }, // 1ec - { clif_parse_sn_explosionspirits, 0 }, // 1ed - { NULL, 0 }, // 1ee - { NULL, 0 }, // 1ef - { NULL, 0 }, // 1f0 - { NULL, 0 }, // 1f1 - { NULL, 0 }, // 1f2 - { NULL, 0 }, // 1f3 - { NULL, 0 }, // 1f4 - { NULL, 0 }, // 1f5 - { NULL, 0 }, // 1f6 - { NULL, 0 }, // 1f7 - { NULL, 0 }, // 1f8 - { NULL, 0 }, // 1f9 - { NULL, 0 }, // 1fa - { NULL, 0 }, // 1fb - { NULL, 0 }, // 1fc - { NULL, 0 }, // 1fd - { NULL, 0 }, // 1fe - { NULL, 0 }, // 1ff - { NULL, 0 }, // 200 - { NULL, 0 }, // 201 - { NULL, 0 }, // 202 - { NULL, 0 }, // 203 - { NULL, 0 }, // 204 - { NULL, 0 }, // 205 - { NULL, 0 }, // 206 - { NULL, 0 }, // 207 - { NULL, 0 }, // 208 - { NULL, 0 }, // 209 - { NULL, 0 }, // 20a - { NULL, 0 }, // 20b - { NULL, 0 }, // 20c - { NULL, 0 }, // 20d - { NULL, 0 }, // 20e - { NULL, 0 }, // 20f - { NULL, 0 }, // 210 - { NULL, 0 }, // 211 - { NULL, 0 }, // 212 - { NULL, 0 }, // 213 - { NULL, 0 }, // 214 - { NULL, 0 }, // 215 - { NULL, 0 }, // 216 - { NULL, 0 }, // 217 - { NULL, 0 }, // 218 - { NULL, 0 }, // 219 - { NULL, 0 }, // 21a - { NULL, 0 }, // 21b - { NULL, 0 }, // 21c - { NULL, 0 }, // 21d - { NULL, 0 }, // 21e - { NULL, 0 }, // 21f -}; -// *INDENT-ON* - -// Checks for packet flooding -int clif_check_packet_flood(int fd, int cmd) -{ - struct map_session_data *sd = (struct map_session_data *)session[fd]->session_data; - unsigned int rate, tick = gettick(); - - // sd will not be set if the client hasn't requested - // WantToConnection yet. Do not apply flood logic to GMs - // as approved bots (GMlvl1) should not have to work around - // flood logic. - if (!sd || pc_isGM(sd) || clif_parse_func_table[cmd].rate == -1) - return 0; - - // Timer has wrapped - if (tick < sd->flood_rates[cmd]) - { - sd->flood_rates[cmd] = tick; - return 0; - } - - // Default rate is 100ms - if ((rate = clif_parse_func_table[cmd].rate) == 0) - rate = 100; - - // ActionRequest - attacks are allowed a faster rate than sit/stand - if (cmd == 0x89) - { - int action_type = RFIFOB (fd, 6); - if (action_type == 0x00 || action_type == 0x07) - rate = 20; - else - rate = 1000; - } - -// Restore this code when mana1.0 is released -#if 0 - // ChangeDir - only apply limit if not walking - if (cmd == 0x9b) - { - // .29 clients spam this packet when walking into a blocked tile - if (RFIFOB(fd, 4) == sd->dir || sd->walktimer != -1) - return 0; - - rate = 500; - } -#endif - - // They are flooding - if (tick < sd->flood_rates[cmd] + rate) - { - time_t now = time(NULL); - - // If it's a nasty flood we log and possibly kick - if (now > sd->packet_flood_reset_due) - { - sd->packet_flood_reset_due = now + battle_config.packet_spam_threshold; - sd->packet_flood_in = 0; - } - - sd->packet_flood_in++; - - if (sd->packet_flood_in >= battle_config.packet_spam_flood) - { - printf("packet flood detected from %s [0x%x]\n", sd->status.name, cmd); - if (battle_config.packet_spam_kick) - { - session[fd]->eof = 1; // Kick - return 1; - } - sd->packet_flood_in = 0; - } - - return 1; - } - - sd->flood_rates[cmd] = tick; - return 0; -} - -#define WARN_MALFORMED_MSG(sd, msg) \ - printf ("clif_validate_chat(): %s (ID %d) sent a malformed" \ - " message: %s.\n", sd->status.name, sd->status.account_id, msg) -/** - * Validate message integrity (inspired by upstream source (eAthena)). - * - * @param sd active session data - * @param type message type: - * 0 for when the sender's name is not included (party chat) - * 1 for when the target's name is included (whisper chat) - * 2 for when the sender's name is given ("sender : text", public/guild chat) - * @param[out] message the message text (pointing within return value, or NULL) - * @param[out] message_len the length of the actual text, excluding NUL - * @return a dynamically allocated copy of the message, or NULL upon failure - */ -static char *clif_validate_chat (struct map_session_data *sd, int type, - char **message, size_t *message_len) -{ - int fd; - unsigned int buf_len; /* Actual message length. */ - unsigned int msg_len; /* Reported message length. */ - unsigned int min_len; /* Minimum message length. */ - size_t name_len; /* Sender's name length. */ - char *buf = NULL; /* Copy of actual message data. */ - char *p = NULL; /* Temporary pointer to message. */ - - *message = NULL; - *message_len = 0; - - nullpo_retr (NULL, sd); - /* - * Don't send chat in the period between the ban and the connection's - * closure. - */ - if (type < 0 || type > 2 || sd->auto_ban_info.in_progress) - return NULL; - - fd = sd->fd; - msg_len = RFIFOW (fd, 2) - 4; - name_len = strlen (sd->status.name); - /* - * At least one character is required in all instances. - * Notes for length checks: - * - * For all types, header (2) + length (2) is considered empty. - * For type 1, the message must be longer than the maximum name length (24) - * to be valid. - * For type 2, the message must be longer than the sender's name length - * plus the length of the separator (" : "). - */ - min_len = (type == 1) ? 24 : (type == 2) ? name_len + 3 : 0; - - /* The player just sent the header (2) and length (2) words. */ - if (!msg_len) - { - WARN_MALFORMED_MSG (sd, "no message sent"); - return NULL; - } - - /* The client sent (or claims to have sent) an empty message. */ - if (msg_len == min_len) - { - WARN_MALFORMED_MSG (sd, "empty message"); - return NULL; - } - - /* The protocol specifies that the target must be 24 bytes long. */ - if (type == 1 && msg_len < min_len) - { - /* Disallow malformed messages. */ - clif_setwaitclose (fd); - WARN_MALFORMED_MSG (sd, "illegal target name"); - return NULL; - } - - p = (char *) (type != 1) ? RFIFOP (fd, 4) : RFIFOP (fd, 28); - buf_len = (type == 1) ? msg_len - min_len: msg_len; - - /* - * The client attempted to exceed the maximum message length. - * - * The conf suggests up to chat_maxline characters, after which the message - * is truncated. But the previous behavior was to drop the message, so - * we'll do that, too. - */ - if (buf_len >= battle_config.chat_maxline) - { - WARN_MALFORMED_MSG (sd, "exceeded maximum message length"); - return NULL; - } - - /* We're leaving an extra eight bytes for public/global chat, 1 for NUL. */ - buf_len += (type == 2) ? 8 + 1 : 1; - - buf = (char *) malloc (buf_len); - memcpy ((type != 2) ? buf : buf + 8, p, - (type != 2) ? buf_len - 1 : buf_len - 8 - 1); - buf[buf_len - 1] = '\0'; - p = (type != 2) ? buf : buf + 8; - - if (type != 2) - { - *message = buf; - /* Don't count the NUL. */ - *message_len = buf_len - 1; - } - else - { - char *pos = NULL; - if (!(pos = strstr(p, " : ")) - || strncmp (p, sd->status.name, name_len) - || pos - p != name_len) - { - free (buf); - /* Disallow malformed/spoofed messages. */ - clif_setwaitclose (fd); - WARN_MALFORMED_MSG (sd, "spoofed name/invalid format"); - return NULL; - } - /* Step beyond the separator. */ - *message = pos + 3; - /* Don't count the sender's name, the extra eight bytes, or the NUL. */ - *message_len = buf_len - min_len - 8 - 1; - } - - return buf; -} - -/*========================================== - * クライアントからのパケット解析 - * socket.cのdo_parsepacketから呼び出される - *------------------------------------------ - */ -static void clif_parse (int fd) -{ - int packet_len = 0, cmd = 0; - struct map_session_data *sd = (struct map_session_data *)session[fd]->session_data; - - if (!sd || (sd && !sd->state.auth)) - { - if (RFIFOREST (fd) < 2) - { // too small a packet disconnect - session[fd]->eof = 1; - } - if (RFIFOW (fd, 0) != 0x72) - { // first packet not auth, disconnect - session[fd]->eof = 1; - } - } - - // 接続が切れてるので後始末 - if (!chrif_isconnect () || session[fd]->eof) - { // char鯖に繋がってない間は接続禁止 (!chrif_isconnect()) - if (sd && sd->state.auth) - { - pc_logout (sd); - clif_quitsave (fd, sd); - if (sd->status.name != NULL) - printf ("Player [%s] has logged off your server.\n", sd->status.name); // Player logout display [Valaris] - else - printf ("Player with account [%d] has logged off your server.\n", sd->bl.id); // Player logout display [Yor] - } - else if (sd) - { // not authentified! (refused by char-server or disconnect before to be authentified) - printf ("Player with account [%d] has logged off your server (not auth account).\n", sd->bl.id); // Player logout display [Yor] - map_deliddb (&sd->bl); // account_id has been included in the DB before auth answer - } - if (fd) - close (fd); - if (fd) - delete_session (fd); - return; - } - - if (RFIFOREST (fd) < 2) - return; // Too small (no packet number) - - cmd = RFIFOW (fd, 0); - - // 管理用パケット処理 - if (cmd >= 30000) - { - switch (cmd) - { - case 0x7530: // Athena情報所得 - 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: // 接続の切断 - session[fd]->eof = 1; - break; - } - return; - } - else if (cmd >= 0x200) - return; - - // パケット長を計算 - packet_len = packet_len_table[cmd]; - if (packet_len == -1) - { - if (RFIFOREST (fd) < 4) - { - return; // Runt packet (variable length without a length sent) - } - packet_len = RFIFOW (fd, 2); - if (packet_len < 4 || packet_len > 32768) - { - session[fd]->eof = 1; - return; // Runt packet (variable out of bounds) - } - } - - if (RFIFOREST (fd) < packet_len) - { - return; // Runt packet (sent legnth is too small) - } - - if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) - { // 切断待ちの場合パケットを処理しない - - } - else if (clif_parse_func_table[cmd].func) - { - if (clif_check_packet_flood(fd, cmd)) - { - // Flood triggered. Skip packet. - RFIFOSKIP(sd->fd, packet_len); - return; - } - - clif_parse_func_table[cmd].func (fd, sd); - } - else - { - // 不明なパケット - if (battle_config.error_log) - { - if (fd) - printf ("\nclif_parse: session #%d, packet 0x%x, lenght %d\n", - fd, cmd, packet_len); -#ifdef DUMP_UNKNOWN_PACKET - { - int i; - FILE *fp; - char packet_txt[256] = "save/packet.txt"; - time_t now; - 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)); - } - 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); - - if ((fp = fopen_ (packet_txt, "a")) == NULL) - { - printf ("clif.c: cant write [%s] !!! data is lost !!!\n", - packet_txt); - return; - } - 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 (gmtime (&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 (gmtime (&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 (gmtime (&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 - } - } - RFIFOSKIP (fd, packet_len); -} - -/*========================================== - * - *------------------------------------------ - */ -int do_init_clif (void) -{ - int i; - - set_defaultparse (clif_parse); - for (i = 0; i < 10; i++) - { - if (make_listen_port (map_port)) - break; -#ifdef LCCWIN32 - Sleep (20000); -#else - sleep (20); -#endif - } - if (i == 10) - { - printf ("cant bind game port\n"); - exit (1); - } - - return 0; -} diff --git a/src/map/clif.cpp b/src/map/clif.cpp new file mode 100644 index 0000000..2d6ddf1 --- /dev/null +++ b/src/map/clif.cpp @@ -0,0 +1,10332 @@ +// $Id: clif.c 164 2004-10-01 16:46:58Z $ + +#define DUMP_UNKNOWN_PACKET 1 + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <sys/types.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <time.h> + +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/version.hpp" +#include "../common/nullpo.hpp" +#include "../common/md5calc.hpp" +#include "../common/mt_rand.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "chat.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "magic.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "storage.hpp" +#include "tmw.hpp" +#include "trade.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define STATE_BLIND 0x10 +#define EMOTE_IGNORED 0x0e + +static const int packet_len_table[0x220] = { + 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, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3, 108, 3, 2, + 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, +//#0x0080 + 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, // 0x8b unknown... size 2 or 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, 6, 2, 27, + 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, + 90, 86, 24, 6, 30, 102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6, + 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, 0, 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, 19, 10, -1, 24, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// local define +enum +{ + ALL_CLIENT, + ALL_SAMEMAP, + AREA, + AREA_WOS, + AREA_WOC, + AREA_WOSC, + AREA_CHAT_WOC, + CHAT, + CHAT_WOS, + 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 +}; + +#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] = (x0)>>2; __p[1] = ((x0)<<6) | (((y0)>>4)&0x3f); __p[2] = ((y0)<<4) | (((x1)>>6)&0x0f); __p[3]=((x1)<<2) | (((y1)>>8)&0x03); __p[4]=(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); } + +static char map_ip_str[16]; +static in_addr_t map_ip; +static int map_port = 5121; +int map_fd; +char talkie_mes[80]; + +/*========================================== + * map鯖のip設定 + *------------------------------------------ + */ +void clif_setip (char *ip) +{ + memcpy (map_ip_str, ip, 16); + map_ip = inet_addr (map_ip_str); +} + +/*========================================== + * 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; +} + +/*========================================== + * + *------------------------------------------ + */ +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 + && 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), ...) +{ + 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) && sd + && sd->state.auth) + func (sd, ap); + } + va_end (ap); + return 0; +} + +static int is_deaf (struct block_list *bl) +{ + struct map_session_data *sd = (struct map_session_data *) bl; + if (!bl || bl->type != BL_PC) + return 0; + return sd->special_state.deaf; +} + +static void clif_emotion_towards (struct block_list *bl, + struct block_list *target, int type); + +static char *clif_validate_chat (struct map_session_data *sd, int type, + char **message, size_t *message_len); + +/*========================================== + * clif_sendでAREA*指定時用 + *------------------------------------------ + */ +int clif_send_sub (struct block_list *bl, va_list ap) +{ + unsigned char *buf; + int len; + struct block_list *src_bl; + int type; + struct map_session_data *sd; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, sd = (struct map_session_data *) bl); + + 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 && bl == src_bl) + return 0; + break; + + case AREA_CHAT_WOC: + if (is_deaf (bl) + && !(bl->type == BL_PC + && pc_isGM ((struct map_session_data *) src_bl))) + { + clif_emotion_towards (src_bl, bl, EMOTE_IGNORED); + return 0; + } + /* fall through... */ + case AREA_WOC: + if ((sd && sd->chatID) || (bl && bl == src_bl)) + return 0; + + break; + case AREA_WOSC: + if ((sd) && sd->chatID + && sd->chatID == ((struct map_session_data *) src_bl)->chatID) + return 0; + break; + } + + if (session[sd->fd] != NULL) + { + 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_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + 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; + struct chat_data *cd; + struct party *p = NULL; + struct guild *g = NULL; + int x0 = 0, x1 = 0, y0 = 0, y1 = 0; + + if (type != ALL_CLIENT) + { + nullpo_retr (0, bl); + + if (bl->type == BL_PC) + { + struct map_session_data *sd = (struct map_session_data *) bl; + if (sd->status.option & OPTION_INVISIBILITY) + { + // Obscure hidden GMs + + switch (type) + { + case AREA: + case AREA_WOC: + type = SELF; + break; + + case AREA_WOS: + case AREA_WOSC: + return 1; + + default: + break; + } + } + } + } + + 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_len_table[RBUFW (buf, 0)]) + { // packet must exist + 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_len_table[RBUFW (buf, 0)]) + { // packet must exist + 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), + bl->y - (AREA_SIZE), + bl->x + (AREA_SIZE), + bl->y + (AREA_SIZE), BL_PC, buf, len, bl, + AREA_CHAT_WOC); + break; + case CHAT: + case CHAT_WOS: + cd = (struct chat_data *) bl; + if (bl->type == BL_PC) + { + sd = (struct map_session_data *) bl; + cd = (struct chat_data *) map_id2bl (sd->chatID); + } + else if (bl->type != BL_CHAT) + break; + if (cd == NULL) + break; + for (i = 0; i < cd->users; i++) + { + if (type == CHAT_WOS + && cd->usersd[i] == (struct map_session_data *) bl) + continue; + if (packet_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (cd->usersd[i]->fd, 0), buf, len); + WFIFOSET (cd->usersd[i]->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 (bl->type == BL_PC) + { + sd = (struct map_session_data *) bl; + if (sd->partyspy > 0) + { + p = party_search (sd->partyspy); + } + else + { + if (sd->status.party_id > 0) + 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->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_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + } + } + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL + && sd->state.auth) + { + if (sd->partyspy == p->party_id) + { + if (packet_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + } + } + } + } + break; + case SELF: + sd = (struct map_session_data *) bl; + if (packet_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + break; + +/* New definitions for guilds [Valaris] */ + + 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: + case GUILD_WOS: + if (bl && bl->type == BL_PC) + { // guildspy [Syrus22] + sd = (struct map_session_data *) bl; + if (sd->guildspy > 0) + { + g = guild_search (sd->guildspy); + } + else + { + if (sd->status.guild_id > 0) + 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 (type == GUILD_WOS && sd->bl.id == bl->id) + continue; + if (packet_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + } + } + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL + && sd->state.auth) + { + if (sd->guildspy == g->guild_id) + { + if (packet_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + } + } + } + } + break; + case GUILD_SAMEMAP: + case GUILD_SAMEMAP_WOS: + if (bl->type == BL_PC) + { + sd = (struct map_session_data *) bl; + if (sd->status.guild_id > 0) + 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->bl.id == bl->id && (type == GUILD_WOS || + type == GUILD_SAMEMAP_WOS + || type == + GUILD_AREA_WOS)) + continue; + if (type != GUILD && type != GUILD_WOS && bl->m != sd->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_len_table[RBUFW (buf, 0)]) + { // packet must exist + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); + } + } + } + } + break; + + default: + if (battle_config.error_log) + printf ("clif_send まだ作ってないよー\n"); + return -1; + } + + return 0; +} + +// +// パケット作って送信 +// +/*========================================== + * + *------------------------------------------ + */ +int clif_authok (struct map_session_data *sd) +{ + int fd; + + nullpo_retr (0, sd); + + if (!sd) + return 0; + + if (!sd->fd) + return 0; + + fd = sd->fd; + + 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]) + return 0; + + WFIFOW (fd, 0) = 0x81; + WFIFOL (fd, 2) = type; + WFIFOSET (fd, packet_len_table[0x81]); + + clif_setwaitclose (fd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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; + WFIFOW (fd, 0) = 0xb3; + WFIFOB (fd, 2) = 1; + WFIFOSET (fd, packet_len_table[0xb3]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set009e (struct flooritem_data *fitem, 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) +{ + 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 + { + 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; + if (type == 9) + { + WBUFB (buf, 6) = 0; + clif_send (buf, packet_len_table[0x80], bl, AREA); + } + else + { + WBUFB (buf, 6) = type; + clif_send (buf, packet_len_table[0x80], bl, + type == 1 ? AREA : AREA_WOS); + } + + return 0; +} + +static void clif_clearchar_delay_sub (timer_id tid, tick_t tick, custom_id_t id, + custom_data_t data) +{ + struct block_list *bl = (struct block_list *) id; + + clif_clearchar (bl, data); + map_freeblock (bl); +} + +int clif_clearchar_delay (unsigned int tick, struct block_list *bl, int type) +{ + struct block_list *tmpbl; + CREATE (tmpbl, struct block_list, 1); + + memcpy (tmpbl, bl, sizeof (struct block_list)); + add_timer (tick, clif_clearchar_delay_sub, (custom_id_t) tmpbl, 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; + memcpy (WFIFOP (fd, 0), buf, 7); + WFIFOSET (fd, packet_len_table[0x80]); + + return 0; +} + +/* +static int current_weapon(struct map_session_data *sd) +{ + if (sd->attack_spell_override) + return sd->attack_spell_look_override; + else { + return sd->status.weapon; + } +} +*/ + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set0078 (struct map_session_data *sd, unsigned char *buf) +{ + int level = 0; + + nullpo_retr (0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) + { // mob disguises [Valaris] + WBUFW (buf, 0) = 0x78; + WBUFL (buf, 2) = sd->bl.id; + WBUFW (buf, 6) = battle_get_speed (&sd->bl); + WBUFW (buf, 8) = sd->opt1; + WBUFW (buf, 10) = sd->opt2; + WBUFW (buf, 12) = sd->status.option; + WBUFW (buf, 14) = sd->disguise; + 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) = 0; + WBUFW (buf, 52) = + ((level = + battle_get_lv (&sd->bl)) > + battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x78]; + } + + 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) = sd->status.option; + WBUFW (buf, 14) = sd->view_class; + WBUFW (buf, 16) = sd->status.hair; + if (sd->attack_spell_override) + WBUFB (buf, 18) = sd->attack_spell_look_override; + else + { + if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] + && sd->view_class != 22) + { + if (sd->inventory_data[sd->equip_index[9]]->view_id > 0) + WBUFW (buf, 18) = + sd->inventory_data[sd->equip_index[9]]->view_id; + else + WBUFW (buf, 18) = + sd->status.inventory[sd->equip_index[9]].nameid; + } + else + WBUFW (buf, 18) = 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 != 22) + { + if (sd->inventory_data[sd->equip_index[8]]->view_id > 0) + WBUFW (buf, 20) = sd->inventory_data[sd->equip_index[8]]->view_id; + else + WBUFW (buf, 20) = sd->status.inventory[sd->equip_index[8]].nameid; + } + 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; + WBUFW (buf, 49) = (pc_isGM (sd) == 60 || pc_isGM (sd) == 99) ? 0x80 : 0; + WBUFB (buf, 51) = sd->state.dead_sit; + WBUFW (buf, 52) = 0; + + return packet_len_table[0x1d8]; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set007b (struct map_session_data *sd, unsigned char *buf) +{ + int level = 0; + nullpo_retr (0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) + { // mob disguises [Valaris] + WBUFW (buf, 0) = 0x7b; + WBUFL (buf, 2) = sd->bl.id; + WBUFW (buf, 6) = battle_get_speed (&sd->bl); + WBUFW (buf, 8) = sd->opt1; + WBUFW (buf, 10) = sd->opt2; + WBUFW (buf, 12) = sd->status.option; + WBUFW (buf, 14) = sd->disguise; + WBUFL (buf, 22) = gettick (); + WBUFW (buf, 46) = 0; + WBUFB (buf, 48) = 0; + WBUFPOS2 (buf, 50, sd->bl.x, sd->bl.y, sd->to_x, sd->to_y); + WBUFB (buf, 55) = 0; + WBUFB (buf, 56) = 5; + WBUFB (buf, 57) = 5; + WBUFW (buf, 58) = + ((level = + battle_get_lv (&sd->bl)) > + battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x7b]; + } + + 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) = sd->status.option; + WBUFW (buf, 14) = sd->view_class; + WBUFW (buf, 16) = sd->status.hair; + if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] + && sd->view_class != 22) + { + if (sd->inventory_data[sd->equip_index[9]]->view_id > 0) + WBUFW (buf, 18) = sd->inventory_data[sd->equip_index[9]]->view_id; + else + WBUFW (buf, 18) = sd->status.inventory[sd->equip_index[9]].nameid; + } + else + WBUFW (buf, 18) = 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 != 22) + { + if (sd->inventory_data[sd->equip_index[8]]->view_id > 0) + WBUFW (buf, 20) = sd->inventory_data[sd->equip_index[8]]->view_id; + else + WBUFW (buf, 20) = sd->status.inventory[sd->equip_index[8]].nameid; + } + 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); + WBUFW (buf, 55) = pc_isGM (sd) == 60 ? 0x80 : 0; + WBUFB (buf, 57) = 5; + WBUFW (buf, 58) = 0; + + return packet_len_table[0x1da]; +} + +/*========================================== + * クラスチェンジ typeはMobの場合は1で他は0? + *------------------------------------------ + */ +int clif_npc_class_change (struct block_list *bl, int npc_class, int type) +{ + char buf[16]; + + nullpo_retr (0, bl); + + if (npc_class >= MAX_PC_CLASS) + { + WBUFW (buf, 0) = 0x1b0; + WBUFL (buf, 2) = bl->id; + WBUFB (buf, 6) = type; + WBUFL (buf, 7) = npc_class; + + clif_send (buf, packet_len_table[0x1b0], bl, AREA); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_mob_class_change (struct mob_data *md, int class_) +{ + 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; + + memset (buf, 0, packet_len_table[0x78]); + + nullpo_retr (0, md); + + WBUFW (buf, 0) = 0x78; + WBUFL (buf, 2) = md->bl.id; + WBUFW (buf, 6) = battle_get_speed (&md->bl); + WBUFW (buf, 8) = md->opt1; + WBUFW (buf, 10) = md->opt2; + WBUFW (buf, 12) = md->option; + WBUFW (buf, 14) = mob_get_viewclass (md->mob_class); + if ((mob_get_viewclass (md->mob_class) <= 23) + || (mob_get_viewclass (md->mob_class) == 812) + || (mob_get_viewclass (md->mob_class) >= 4001)) + { + WBUFW (buf, 12) |= mob_db[md->mob_class].option; + WBUFW (buf, 16) = mob_get_hair (md->mob_class); + WBUFW (buf, 18) = mob_get_weapon (md->mob_class); + WBUFW (buf, 20) = mob_get_head_buttom (md->mob_class); + WBUFW (buf, 22) = mob_get_shield (md->mob_class); + WBUFW (buf, 24) = mob_get_head_top (md->mob_class); + WBUFW (buf, 26) = mob_get_head_mid (md->mob_class); + WBUFW (buf, 28) = mob_get_hair_color (md->mob_class); + WBUFW (buf, 30) = mob_get_clothes_color (md->mob_class); //Add for player monster dye - Valaris + WBUFB (buf, 45) = mob_get_sex (md->mob_class); + } + + if (md->mob_class >= 1285 && md->mob_class <= 1287) + { // Added guardian emblems [Valaris] + struct guild *g; + struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); + if (gc && gc->guild_id > 0) + { + g = guild_search (gc->guild_id); + if (g) + { + WBUFL (buf, 26) = gc->guild_id; + WBUFL (buf, 22) = g->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) = + ((level = + battle_get_lv (&md->bl)) > + battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x78]; +} + +/*========================================== + * MOB表示2 + *------------------------------------------ + */ +static int clif_mob007b (struct mob_data *md, unsigned char *buf) +{ + int level; + + memset (buf, 0, packet_len_table[0x7b]); + + nullpo_retr (0, md); + + WBUFW (buf, 0) = 0x7b; + WBUFL (buf, 2) = md->bl.id; + WBUFW (buf, 6) = battle_get_speed (&md->bl); + WBUFW (buf, 8) = md->opt1; + WBUFW (buf, 10) = md->opt2; + WBUFW (buf, 12) = md->option; + WBUFW (buf, 14) = mob_get_viewclass (md->mob_class); + if ((mob_get_viewclass (md->mob_class) < 24) + || (mob_get_viewclass (md->mob_class) > 4000)) + { + WBUFW (buf, 12) |= mob_db[md->mob_class].option; + WBUFW (buf, 16) = mob_get_hair (md->mob_class); + WBUFW (buf, 18) = mob_get_weapon (md->mob_class); + WBUFW (buf, 20) = mob_get_head_buttom (md->mob_class); + WBUFL (buf, 22) = gettick (); + WBUFW (buf, 26) = mob_get_shield (md->mob_class); + WBUFW (buf, 28) = mob_get_head_top (md->mob_class); + WBUFW (buf, 30) = mob_get_head_mid (md->mob_class); + WBUFW (buf, 32) = mob_get_hair_color (md->mob_class); + WBUFW (buf, 34) = mob_get_clothes_color (md->mob_class); //Add for player monster dye - Valaris + WBUFB (buf, 49) = mob_get_sex (md->mob_class); + } + else + WBUFL (buf, 22) = gettick (); + + if (md->mob_class >= 1285 && md->mob_class <= 1287) + { // Added guardian emblems [Valaris] + struct guild *g; + struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); + if (gc && gc->guild_id > 0) + { + g = guild_search (gc->guild_id); + if (g) + { + WBUFL (buf, 28) = gc->guild_id; + WBUFL (buf, 24) = g->emblem_id; + } + } + } // End addition + + WBUFPOS2 (buf, 50, md->bl.x, md->bl.y, md->to_x, md->to_y); + WBUFB (buf, 56) = 5; + WBUFB (buf, 57) = 5; + WBUFW (buf, 58) = + ((level = + battle_get_lv (&md->bl)) > + battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x7b]; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_npc0078 (struct npc_data *nd, unsigned char *buf) +{ + struct guild *g; + + nullpo_retr (0, nd); + + memset (buf, 0, packet_len_table[0x78]); + + WBUFW (buf, 0) = 0x78; + WBUFL (buf, 2) = nd->bl.id; + WBUFW (buf, 6) = nd->speed; + WBUFW (buf, 14) = nd->npc_class; + if ((nd->npc_class == 722) && (nd->u.scr.guild_id > 0) + && ((g = guild_search (nd->u.scr.guild_id)) != NULL)) + { + WBUFL (buf, 22) = g->emblem_id; + WBUFL (buf, 26) = 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]; +} + +/*========================================== + * + *------------------------------------------ + */ +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) +{ + WFIFOW (fd, 0) = 0x192; + WFIFOW (fd, 2) = x; + WFIFOW (fd, 4) = y; + WFIFOW (fd, 6) = type; + memcpy (WFIFOP (fd, 8), map[m].name, 16); + WFIFOSET (fd, packet_len_table[0x192]); + + return 0; +} + +/* These indices are derived from equip_pos in pc.c and some guesswork */ +static int equip_points[LOOK_LAST + 1] = { + -1, /* 0: base */ + -1, /* 1: hair */ + 9, /* 2: weapon */ + 4, /* 3: head botom -- leg armour */ + 6, /* 4: head top -- hat */ + 5, /* 5: head mid -- torso armour */ + -1, /* 6: hair colour */ + -1, /* 6: clothes colour */ + 8, /* 6: shield */ + 2, /* 9: shoes */ + 3, /* gloves */ + 1, /* cape */ + 7, /* misc1 */ + 0, /* misc2 */ +}; + +/*========================================== + * + *------------------------------------------ + */ +int clif_spawnpc (struct map_session_data *sd) +{ + unsigned char buf[128]; + + nullpo_retr (0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) + { // mob disguises [Valaris] + clif_clearchar (&sd->bl, 9); + + memset (buf, 0, packet_len_table[0x119]); + + WBUFW (buf, 0) = 0x119; + WBUFL (buf, 2) = sd->bl.id; + WBUFW (buf, 6) = 0; + WBUFW (buf, 8) = 0; + WBUFW (buf, 10) = 0x40; + WBUFB (buf, 12) = 0; + + clif_send (buf, packet_len_table[0x119], &sd->bl, SELF); + + memset (buf, 0, packet_len_table[0x7c]); + + WBUFW (buf, 0) = 0x7c; + WBUFL (buf, 2) = sd->bl.id; + WBUFW (buf, 6) = sd->speed; + WBUFW (buf, 8) = sd->opt1; + WBUFW (buf, 10) = sd->opt2; + 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); + } + + clif_set0078 (sd, buf); + + WBUFW (buf, 0) = 0x1d9; + WBUFW (buf, 51) = 0; + clif_send (buf, packet_len_table[0x1d9], &sd->bl, AREA_WOS); + + 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 (sd->status.pc_class == 13 || sd->status.pc_class == 21 + || sd->status.pc_class == 4014 || sd->status.pc_class == 4022) + pc_setoption (sd, sd->status.option | 0x0020); // [Valaris] + + if ((pc_isriding (sd) && pc_checkskill (sd, KN_RIDING) > 0) + && (sd->status.pc_class == 7 || sd->status.pc_class == 14 + || sd->status.pc_class == 4008 || sd->status.pc_class == 4015)) + pc_setriding (sd); // update peco riders for people upgrading athena [Valaris] + + if (map[sd->bl.m].flag.snow) + clif_specialeffect (&sd->bl, 162, 1); + if (map[sd->bl.m].flag.fog) + clif_specialeffect (&sd->bl, 233, 1); + if (map[sd->bl.m].flag.sakura) + clif_specialeffect (&sd->bl, 163, 1); + if (map[sd->bl.m].flag.leaves) + clif_specialeffect (&sd->bl, 333, 1); + if (map[sd->bl.m].flag.rain) + clif_specialeffect (&sd->bl, 161, 1); + +// clif_changelook_accessories(&sd->bl, NULL); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_spawnnpc (struct npc_data *nd) +{ + unsigned char buf[64]; + int len; + + nullpo_retr (0, nd); + + if (nd->npc_class < 0 || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) + return 0; + + 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->npc_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_spawn_fake_npc_for_player (struct map_session_data *sd, int fake_npc_id) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + if (!fd) + return 0; + + WFIFOW (fd, 0) = 0x7c; + WFIFOL (fd, 2) = fake_npc_id; + WFIFOW (fd, 6) = 0; + WFIFOW (fd, 8) = 0; + WFIFOW (fd, 10) = 0; + WFIFOW (fd, 12) = 0; + WFIFOW (fd, 20) = 127; + WFIFOPOS (fd, 36, sd->bl.x, sd->bl.y); + WFIFOSET (fd, packet_len_table[0x7c]); + + WFIFOW (fd, 0) = 0x78; + WFIFOL (fd, 2) = fake_npc_id; + WFIFOW (fd, 6) = 0; + WFIFOW (fd, 8) = 0; + WFIFOW (fd, 10) = 0; + WFIFOW (fd, 12) = 0; + WFIFOW (fd, 14) = 127; // identifies as NPC + WFIFOW (fd, 20) = 127; + WFIFOPOS (fd, 46, sd->bl.x, sd->bl.y); + WFIFOPOS (fd, 36, sd->bl.x, sd->bl.y); + WFIFOB (fd, 49) = 5; + WFIFOB (fd, 50) = 5; + WFIFOSET (fd, packet_len_table[0x78]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_spawnmob (struct mob_data *md) +{ + unsigned char buf[64]; + int len; + + nullpo_retr (0, md); + + if (mob_get_viewclass (md->mob_class) > 23) + { + memset (buf, 0, packet_len_table[0x7c]); + + WBUFW (buf, 0) = 0x7c; + WBUFL (buf, 2) = md->bl.id; + WBUFW (buf, 6) = md->stats[MOB_SPEED]; + WBUFW (buf, 8) = md->opt1; + WBUFW (buf, 10) = md->opt2; + WBUFW (buf, 12) = md->option; + WBUFW (buf, 20) = mob_get_viewclass (md->mob_class); + 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 (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] + clif_mob_equip (md, mob_get_equip (md->mob_class)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_servertick (struct map_session_data *sd) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + 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) = 0; + 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); + + if (sd->disguise > 23 && sd->disguise < 4001) + { + clif_send (buf, len, &sd->bl, AREA); + return 0; + } + else + clif_send (buf, len, &sd->bl, AREA_WOS); + + if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0) + clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, + sd->status.clothes_color); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_quitsave (int fd, struct map_session_data *sd) +{ + map_quit (sd); +} + +/*========================================== + * + *------------------------------------------ + */ +static void clif_waitclose (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + if (session[id]) + session[id]->eof = 1; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_setwaitclose (int fd) +{ + add_timer (gettick () + 5000, clif_waitclose, fd, 0); +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_changemap (struct map_session_data *sd, char *mapname, int x, int y) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + + WFIFOW (fd, 0) = 0x91; + memcpy (WFIFOP (fd, 2), mapname, 16); + WFIFOW (fd, 18) = x; + WFIFOW (fd, 20) = y; + WFIFOSET (fd, packet_len_table[0x91]); + + if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + clif_spawnpc (sd); + + 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; + WFIFOW (fd, 0) = 0x92; + memcpy (WFIFOP (fd, 2), mapname, 16); + 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_fixpos (struct block_list *bl) +{ + char buf[16]; + + nullpo_retr (0, bl); + + WBUFW (buf, 0) = 0x88; + 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; + 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; + 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) = 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; + WFIFOW (fd, 0) = 0xc7; + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i]) + { + 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; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0xb4; + WFIFOW (fd, 2) = strlen (mes) + 9; + WFIFOL (fd, 4) = npcid; + strcpy (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; + 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; + 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; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0xb7; + WFIFOW (fd, 2) = strlen (mes) + 8; + WFIFOL (fd, 4) = npcid; + strcpy (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; + 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; + 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; + 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; + WFIFOW (fd, 0) = 0x1b3; + memcpy (WFIFOP (fd, 2), image, 64); + WFIFOB (fd, 66) = type; + WFIFOSET (fd, packet_len_table[0x1b3]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_additem (struct map_session_data *sd, int n, int amount, int fail) +{ + int fd, j; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + if (sd->status.inventory[n].broken == 1) + WBUFB (buf, 9) = 1; // is weapon broken [Valaris] + else + WBUFB (buf, 9) = sd->status.inventory[n].attribute; + WBUFB (buf, 10) = sd->status.inventory[n].refine; + if (sd->status.inventory[n].card[0] == 0x00ff + || sd->status.inventory[n].card[0] == 0x00fe + || sd->status.inventory[n].card[0] == (short) 0xff00) + { + WBUFW (buf, 11) = sd->status.inventory[n].card[0]; + WBUFW (buf, 13) = sd->status.inventory[n].card[1]; + WBUFW (buf, 15) = sd->status.inventory[n].card[2]; + WBUFW (buf, 17) = sd->status.inventory[n].card[3]; + } + else + { + if (sd->status.inventory[n].card[0] > 0 + && (j = itemdb_viewid (sd->status.inventory[n].card[0])) > 0) + WBUFW (buf, 11) = j; + else + WBUFW (buf, 11) = sd->status.inventory[n].card[0]; + if (sd->status.inventory[n].card[1] > 0 + && (j = itemdb_viewid (sd->status.inventory[n].card[1])) > 0) + WBUFW (buf, 13) = j; + else + WBUFW (buf, 13) = sd->status.inventory[n].card[1]; + if (sd->status.inventory[n].card[2] > 0 + && (j = itemdb_viewid (sd->status.inventory[n].card[2])) > 0) + WBUFW (buf, 15) = j; + else + WBUFW (buf, 15) = sd->status.inventory[n].card[2]; + if (sd->status.inventory[n].card[3] > 0 + && (j = itemdb_viewid (sd->status.inventory[n].card[3])) > 0) + WBUFW (buf, 17) = j; + else + WBUFW (buf, 17) = sd->status.inventory[n].card[3]; + } + WBUFW (buf, 19) = pc_equippoint (sd, n); + WBUFB (buf, 21) = + (sd->inventory_data[n]->type == + 7) ? 4 : 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; + 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; + buf = WFIFOP (fd, 0); + 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) = 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; + WBUFW (buf, n * 18 + 14) = sd->status.inventory[i].card[0]; + WBUFW (buf, n * 18 + 16) = sd->status.inventory[i].card[1]; + WBUFW (buf, n * 18 + 18) = sd->status.inventory[i].card[2]; + WBUFW (buf, n * 18 + 20) = sd->status.inventory[i].card[3]; + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 18; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + if (arrow >= 0) + clif_arrowequip (sd, arrow); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_equiplist (struct map_session_data *sd) +{ + int i, j, n, fd; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + buf = WFIFOP (fd, 0); + WBUFW (buf, 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; + WBUFW (buf, n * 20 + 4) = i + 2; + if (sd->inventory_data[i]->view_id > 0) + WBUFW (buf, n * 20 + 6) = sd->inventory_data[i]->view_id; + else + WBUFW (buf, n * 20 + 6) = sd->status.inventory[i].nameid; + WBUFB (buf, n * 20 + 8) = + (sd->inventory_data[i]->type == + 7) ? 4 : sd->inventory_data[i]->type; + WBUFB (buf, n * 20 + 9) = sd->status.inventory[i].identify; + WBUFW (buf, n * 20 + 10) = pc_equippoint (sd, i); + WBUFW (buf, n * 20 + 12) = sd->status.inventory[i].equip; + if (sd->status.inventory[i].broken == 1) + WBUFB (buf, n * 20 + 14) = 1; // is weapon broken [Valaris] + else + WBUFB (buf, n * 20 + 14) = sd->status.inventory[i].attribute; + WBUFB (buf, n * 20 + 15) = sd->status.inventory[i].refine; + if (sd->status.inventory[i].card[0] == 0x00ff + || sd->status.inventory[i].card[0] == 0x00fe + || sd->status.inventory[i].card[0] == (short) 0xff00) + { + WBUFW (buf, n * 20 + 16) = sd->status.inventory[i].card[0]; + WBUFW (buf, n * 20 + 18) = sd->status.inventory[i].card[1]; + WBUFW (buf, n * 20 + 20) = sd->status.inventory[i].card[2]; + WBUFW (buf, n * 20 + 22) = sd->status.inventory[i].card[3]; + } + else + { + if (sd->status.inventory[i].card[0] > 0 + && (j = itemdb_viewid (sd->status.inventory[i].card[0])) > 0) + WBUFW (buf, n * 20 + 16) = j; + else + WBUFW (buf, n * 20 + 16) = sd->status.inventory[i].card[0]; + if (sd->status.inventory[i].card[1] > 0 + && (j = itemdb_viewid (sd->status.inventory[i].card[1])) > 0) + WBUFW (buf, n * 20 + 18) = j; + else + WBUFW (buf, n * 20 + 18) = sd->status.inventory[i].card[1]; + if (sd->status.inventory[i].card[2] > 0 + && (j = itemdb_viewid (sd->status.inventory[i].card[2])) > 0) + WBUFW (buf, n * 20 + 20) = j; + else + WBUFW (buf, n * 20 + 20) = sd->status.inventory[i].card[2]; + if (sd->status.inventory[i].card[3] > 0 + && (j = itemdb_viewid (sd->status.inventory[i].card[3])) > 0) + WBUFW (buf, n * 20 + 22) = j; + else + WBUFW (buf, n * 20 + 22) = sd->status.inventory[i].card[3]; + } + n++; + } + if (n) + { + WBUFW (buf, 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; + buf = WFIFOP (fd, 0); + 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) = 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; + WBUFW (buf, n * 18 + 14) = stor->storage_[i].card[0]; + WBUFW (buf, n * 18 + 16) = stor->storage_[i].card[1]; + WBUFW (buf, n * 18 + 18) = stor->storage_[i].card[2]; + WBUFW (buf, n * 18 + 20) = stor->storage_[i].card[3]; + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 18; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + return 0; +} + +/*========================================== + * カプラさんに預けてある装備リスト + *------------------------------------------ + */ +int clif_storageequiplist (struct map_session_data *sd, struct storage *stor) +{ + struct item_data *id; + int i, j, n, fd; + unsigned char *buf; + + nullpo_retr (0, sd); + nullpo_retr (0, stor); + + fd = sd->fd; + 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) = 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; + if (stor->storage_[i].broken == 1) + WBUFB (buf, n * 20 + 14) = 1; //is weapon broken [Valaris] + else + WBUFB (buf, n * 20 + 14) = stor->storage_[i].attribute; + WBUFB (buf, n * 20 + 15) = stor->storage_[i].refine; + if (stor->storage_[i].card[0] == 0x00ff + || stor->storage_[i].card[0] == 0x00fe + || stor->storage_[i].card[0] == (short) 0xff00) + { + WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; + WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; + WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; + WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; + } + else + { + if (stor->storage_[i].card[0] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[0])) > 0) + WBUFW (buf, n * 20 + 16) = j; + else + WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; + if (stor->storage_[i].card[1] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[1])) > 0) + WBUFW (buf, n * 20 + 18) = j; + else + WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; + if (stor->storage_[i].card[2] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[2])) > 0) + WBUFW (buf, n * 20 + 20) = j; + else + WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; + if (stor->storage_[i].card[3] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[3])) > 0) + WBUFW (buf, n * 20 + 22) = j; + else + WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; + } + 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; + buf = WFIFOP (fd, 0); + + 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) = 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; + WBUFW (buf, n * 18 + 14) = stor->storage_[i].card[0]; + WBUFW (buf, n * 18 + 16) = stor->storage_[i].card[1]; + WBUFW (buf, n * 18 + 18) = stor->storage_[i].card[2]; + WBUFW (buf, n * 18 + 20) = stor->storage_[i].card[3]; + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 18; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_guildstorageequiplist (struct map_session_data *sd, + struct guild_storage *stor) +{ + struct item_data *id; + int i, j, n, fd; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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) = 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; + if (stor->storage_[i].broken == 1) + WBUFB (buf, n * 20 + 14) = 1; // is weapon broken [Valaris] + else + WBUFB (buf, n * 20 + 14) = stor->storage_[i].attribute; + WBUFB (buf, n * 20 + 15) = stor->storage_[i].refine; + if (stor->storage_[i].card[0] == 0x00ff + || stor->storage_[i].card[0] == 0x00fe + || stor->storage_[i].card[0] == (short) 0xff00) + { + WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; + WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; + WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; + WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; + } + else + { + if (stor->storage_[i].card[0] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[0])) > 0) + WBUFW (buf, n * 20 + 16) = j; + else + WBUFW (buf, n * 20 + 16) = stor->storage_[i].card[0]; + if (stor->storage_[i].card[1] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[1])) > 0) + WBUFW (buf, n * 20 + 18) = j; + else + WBUFW (buf, n * 20 + 18) = stor->storage_[i].card[1]; + if (stor->storage_[i].card[2] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[2])) > 0) + WBUFW (buf, n * 20 + 20) = j; + else + WBUFW (buf, n * 20 + 20) = stor->storage_[i].card[2]; + if (stor->storage_[i].card[3] > 0 + && (j = itemdb_viewid (stor->storage_[i].card[3])) > 0) + WBUFW (buf, n * 20 + 22) = j; + else + WBUFW (buf, n * 20 + 22) = stor->storage_[i].card[3]; + } + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 20; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + return 0; +} + +/*========================================== + * ステータスを送りつける + * 表示専用数字はこの中で計算して送る + *------------------------------------------ + */ +int clif_updatestatus (struct map_session_data *sd, int type) +{ + int fd, len = 8; + + nullpo_retr (0, sd); + + fd = sd->fd; + + 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; + 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) = 0; + 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; + 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->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->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: + trade_verifyzeny (sd); + 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->attack_spell_override) + ? sd->attack_spell_range : 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; + + case SP_GM: + WFIFOL (fd, 4) = pc_isGM (sd); + break; + + default: + if (battle_config.error_log) + printf ("clif_updatestatus : make %d routine\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) + printf ("clif_changestatus : make %d routine\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) +{ + return clif_changelook_towards (bl, type, val, NULL); +} + +int clif_changelook_towards (struct block_list *bl, int type, int val, + struct map_session_data *dstsd) +{ + unsigned char rbuf[32]; + unsigned char *buf = dstsd ? WFIFOP (dstsd->fd, 0) : rbuf; // pick target buffer or general-purpose one + struct map_session_data *sd = NULL; + + nullpo_retr (0, bl); + + if (bl->type == BL_PC) + sd = (struct map_session_data *) bl; + + if (sd && sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + return 0; + + if (sd && sd->status.option & OPTION_INVISIBILITY) + return 0; + + if (sd + && (type == LOOK_WEAPON || type == LOOK_SHIELD || type >= LOOK_SHOES)) + { + WBUFW (buf, 0) = 0x1d7; + WBUFL (buf, 2) = bl->id; + if (type >= LOOK_SHOES) + { + int equip_point = equip_points[type]; + + WBUFB (buf, 6) = type; + if (sd->equip_index[equip_point] >= 0 + && sd->inventory_data[sd->equip_index[equip_point]]) + { + if (sd-> + inventory_data[sd->equip_index[equip_point]]->view_id > 0) + WBUFW (buf, 7) = + sd->inventory_data[sd-> + equip_index[equip_point]]->view_id; + else + WBUFW (buf, 7) = + sd->status.inventory[sd-> + equip_index[equip_point]].nameid; + } + else + WBUFW (buf, 7) = 0; + WBUFW (buf, 9) = 0; + } + else + { + WBUFB (buf, 6) = 2; + if (sd->attack_spell_override) + WBUFW (buf, 7) = sd->attack_spell_look_override; + else + { + if (sd->equip_index[9] >= 0 + && sd->inventory_data[sd->equip_index[9]] + && sd->view_class != 22) + { + 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 != 22) + { + 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; + } + if (dstsd) + WFIFOSET (dstsd->fd, packet_len_table[0x1d7]); + else + clif_send (buf, packet_len_table[0x1d7], bl, AREA); + } + else + { + WBUFW (buf, 0) = 0x1d7; + WBUFL (buf, 2) = bl->id; + WBUFB (buf, 6) = type; + WBUFW (buf, 7) = val; + WBUFW (buf, 9) = 0; + if (dstsd) + WFIFOSET (dstsd->fd, packet_len_table[0x1d7]); + else + clif_send (buf, packet_len_table[0x1d7], bl, AREA); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_initialstatus (struct map_session_data *sd) +{ + int fd; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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->watk; + WBUFW (buf, 18) = sd->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; + 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; + WFIFOW (fd, 0) = 0x013b; + WFIFOW (fd, 2) = type; + + WFIFOSET (fd, packet_len_table[0x013b]); + + 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; + 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; + 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; + 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) +{ + 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_changeoption (struct block_list *bl) +{ + char buf[32]; + short option; + struct status_change *sc_data; + static const int omask[] = { 0x10, 0x20 }; + static const int scnum[] = { SC_FALCON, SC_RIDING }; + int i; + + nullpo_retr (0, bl); + + option = *battle_get_option (bl); + sc_data = battle_get_sc_data (bl); + + WBUFW (buf, 0) = 0x119; + WBUFL (buf, 2) = bl->id; + WBUFW (buf, 6) = *battle_get_opt1 (bl); + WBUFW (buf, 8) = *battle_get_opt2 (bl); + WBUFW (buf, 10) = option; + WBUFB (buf, 12) = 0; // ?? + + if (bl->type == BL_PC) + { // disguises [Valaris] + struct map_session_data *sd = ((struct map_session_data *) bl); + if (sd && sd->disguise > 23 && sd->disguise < 4001) + { + clif_send (buf, packet_len_table[0x119], bl, AREA_WOS); + clif_spawnpc (sd); + } + else + clif_send (buf, packet_len_table[0x119], bl, AREA); + } + else + clif_send (buf, packet_len_table[0x119], bl, AREA); + + // アイコンの表示 + for (i = 0; i < sizeof (omask) / sizeof (omask[0]); i++) + { + if (option & omask[i]) + { + if (sc_data[scnum[i]].timer == -1) + skill_status_change_start (bl, scnum[i], 0, 0, 0, 0, 0, 0); + } + else + { + skill_status_change_end (bl, scnum[i], -1); + } + } + + 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; + WFIFOW (fd, 0) = 0xa8; + WFIFOW (fd, 2) = index + 2; + WFIFOW (fd, 4) = amount; + WFIFOB (fd, 6) = ok; + WFIFOSET (fd, packet_len_table[0xa8]); + } + else + { + 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, SELF); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_createchat (struct map_session_data *sd, int fail) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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) +{ + char buf[128]; // 最大title(60バイト)+17 + + if (cd == NULL || *cd->owner == NULL) + return 1; + + WBUFW (buf, 0) = 0xd7; + WBUFW (buf, 2) = strlen (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 (WBUFP (buf, 17), cd->title); + if (fd) + { + 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) +{ + char buf[128]; // 最大title(60バイト)+17 + + if (cd == NULL || cd->usersd[0] == NULL) + return 1; + + WBUFW (buf, 0) = 0xdf; + WBUFW (buf, 2) = strlen (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 (WBUFP (buf, 17), cd->title); + clif_send (buf, WBUFW (buf, 2), &cd->usersd[0]->bl, CHAT); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_clearchat (struct chat_data *cd, int fd) +{ + char buf[32]; + + nullpo_retr (0, cd); + + WBUFW (buf, 0) = 0xd8; + WBUFL (buf, 2) = cd->bl.id; + if (fd) + { + 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; + + 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; + 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, 24); + } + WFIFOSET (fd, WFIFOW (fd, 2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_addchat (struct chat_data *cd, struct map_session_data *sd) +{ + 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, 24); + 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) +{ + 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, 24); + WBUFW (buf, 30) = 0xe1; + WBUFL (buf, 32) = 0; + memcpy (WBUFP (buf, 36), sd->status.name, 24); + + 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) +{ + 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, 24); + 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; + WFIFOW (fd, 0) = 0xe5; + strcpy (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; + 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, j; + + nullpo_retr (0, sd); + nullpo_retr (0, tsd); + + fd = tsd->fd; + 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; + 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 + if (sd->status.inventory[index].broken == 1) + WFIFOB (fd, 9) = 1; // is broke weapon [Valaris] + else + WFIFOB (fd, 9) = sd->status.inventory[index].attribute; // attribute + WFIFOB (fd, 10) = sd->status.inventory[index].refine; //refine + if (sd->status.inventory[index].card[0] == 0x00ff + || sd->status.inventory[index].card[0] == 0x00fe + || sd->status.inventory[index].card[0] == (short) 0xff00) + { + WFIFOW (fd, 11) = sd->status.inventory[index].card[0]; //card (4w) + WFIFOW (fd, 13) = sd->status.inventory[index].card[1]; //card (4w) + WFIFOW (fd, 15) = sd->status.inventory[index].card[2]; //card (4w) + WFIFOW (fd, 17) = sd->status.inventory[index].card[3]; //card (4w) + } + else + { + if (sd->status.inventory[index].card[0] > 0 + && (j = + itemdb_viewid (sd->status.inventory[index].card[0])) > 0) + WFIFOW (fd, 11) = j; + else + WFIFOW (fd, 11) = sd->status.inventory[index].card[0]; + if (sd->status.inventory[index].card[1] > 0 + && (j = + itemdb_viewid (sd->status.inventory[index].card[1])) > 0) + WFIFOW (fd, 13) = j; + else + WFIFOW (fd, 13) = sd->status.inventory[index].card[1]; + if (sd->status.inventory[index].card[2] > 0 + && (j = + itemdb_viewid (sd->status.inventory[index].card[2])) > 0) + WFIFOW (fd, 15) = j; + else + WFIFOW (fd, 15) = sd->status.inventory[index].card[2]; + if (sd->status.inventory[index].card[3] > 0 + && (j = + itemdb_viewid (sd->status.inventory[index].card[3])) > 0) + WFIFOW (fd, 17) = j; + else + WFIFOW (fd, 17) = sd->status.inventory[index].card[3]; + } + } + WFIFOSET (fd, packet_len_table[0xe9]); + + return 0; +} + +/*========================================== + * アイテム追加成功/失敗 + *------------------------------------------ + */ +int clif_tradeitemok (struct map_session_data *sd, int index, int amount, + int fail) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0x1b1; + //WFIFOW(fd,0)=0xea; + WFIFOW (fd, 2) = index; + WFIFOW (fd, 4) = amount; + WFIFOB (fd, 6) = fail; + WFIFOSET (fd, packet_len_table[0x1b1]); + + return 0; +} + +/*========================================== + * 取り引きok押し + *------------------------------------------ + */ +int clif_tradedeal_lock (struct map_session_data *sd, int fail) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + 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; + 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; + 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 fd, j; + + nullpo_retr (0, sd); + nullpo_retr (0, stor); + + fd = sd->fd; + 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; + WFIFOB (fd, 10) = stor->storage_[index].identify; //identify flag + if (stor->storage_[index].broken == 1) + WFIFOB (fd, 11) = 1; // is weapon broken [Valaris] + else + WFIFOB (fd, 11) = stor->storage_[index].attribute; // attribute + WFIFOB (fd, 12) = stor->storage_[index].refine; //refine + if (stor->storage_[index].card[0] == 0x00ff + || stor->storage_[index].card[0] == 0x00fe + || stor->storage_[index].card[0] == (short) 0xff00) + { + WFIFOW (fd, 13) = stor->storage_[index].card[0]; //card (4w) + WFIFOW (fd, 15) = stor->storage_[index].card[1]; //card (4w) + WFIFOW (fd, 17) = stor->storage_[index].card[2]; //card (4w) + WFIFOW (fd, 19) = stor->storage_[index].card[3]; //card (4w) + } + else + { + if (stor->storage_[index].card[0] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[0])) > 0) + WFIFOW (fd, 13) = j; + else + WFIFOW (fd, 13) = stor->storage_[index].card[0]; + if (stor->storage_[index].card[1] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[1])) > 0) + WFIFOW (fd, 15) = j; + else + WFIFOW (fd, 15) = stor->storage_[index].card[1]; + if (stor->storage_[index].card[2] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[2])) > 0) + WFIFOW (fd, 17) = j; + else + WFIFOW (fd, 17) = stor->storage_[index].card[2]; + if (stor->storage_[index].card[3] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[3])) > 0) + WFIFOW (fd, 19) = j; + else + WFIFOW (fd, 19) = stor->storage_[index].card[3]; + } + 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; + 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, j; + + nullpo_retr (0, sd); + nullpo_retr (0, stor); + + fd = sd->fd; + 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 + if (stor->storage_[index].broken == 1) + WFIFOB (fd, 11) = 1; // is weapon broken [Valaris] + else + WFIFOB (fd, 11) = stor->storage_[index].attribute; // attribute + WFIFOB (fd, 12) = stor->storage_[index].refine; //refine + if (stor->storage_[index].card[0] == 0x00ff + || stor->storage_[index].card[0] == 0x00fe + || stor->storage_[index].card[0] == (short) 0xff00) + { + WFIFOW (fd, 13) = stor->storage_[index].card[0]; //card (4w) + WFIFOW (fd, 15) = stor->storage_[index].card[1]; //card (4w) + WFIFOW (fd, 17) = stor->storage_[index].card[2]; //card (4w) + WFIFOW (fd, 19) = stor->storage_[index].card[3]; //card (4w) + } + else + { + if (stor->storage_[index].card[0] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[0])) > 0) + WFIFOW (fd, 13) = j; + else + WFIFOW (fd, 13) = stor->storage_[index].card[0]; + if (stor->storage_[index].card[1] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[1])) > 0) + WFIFOW (fd, 15) = j; + else + WFIFOW (fd, 15) = stor->storage_[index].card[1]; + if (stor->storage_[index].card[2] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[2])) > 0) + WFIFOW (fd, 17) = j; + else + WFIFOW (fd, 17) = stor->storage_[index].card[2]; + if (stor->storage_[index].card[3] > 0 + && (j = itemdb_viewid (stor->storage_[index].card[3])) > 0) + WFIFOW (fd, 19) = j; + else + WFIFOW (fd, 19) = stor->storage_[index].card[3]; + } + 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; + 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; + WFIFOW (fd, 0) = 0xf8; // Storage Closed + WFIFOSET (fd, packet_len_table[0xf8]); + + return 0; +} + +void +clif_changelook_accessories (struct block_list *bl, + struct map_session_data *dest) +{ + int i; + + for (i = LOOK_SHOES; i <= LOOK_LAST; i++) + clif_changelook_towards (bl, i, 0, dest); +} + +// +// callback系 ? +// +/*========================================== + * PC表示 + *------------------------------------------ + */ +void clif_getareachar_pc (struct map_session_data *sd, + struct map_session_data *dstsd) +{ + int len; + + if (dstsd->status.option & OPTION_INVISIBILITY) + return; + + nullpo_retv (sd); + nullpo_retv (dstsd); + + if (dstsd->walktimer != -1) + { + len = clif_set007b (dstsd, WFIFOP (sd->fd, 0)); + WFIFOSET (sd->fd, len); + } + else + { + len = clif_set0078 (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->spiritball > 0) + { + clif_set01e1 (dstsd, WFIFOP (sd->fd, 0)); + WFIFOSET (sd->fd, packet_len_table[0x1e1]); + } + if (battle_config.save_clothcolor == 1 && dstsd->status.clothes_color > 0) + clif_changelook (&dstsd->bl, LOOK_CLOTHES_COLOR, + dstsd->status.clothes_color); + + if (sd->status.manner < 0) + clif_changestatus (&sd->bl, SP_MANNER, sd->status.manner); + + clif_changelook_accessories (&sd->bl, dstsd); + clif_changelook_accessories (&dstsd->bl, sd); +} + +/*========================================== + * NPC表示 + *------------------------------------------ + */ +void clif_getareachar_npc (struct map_session_data *sd, struct npc_data *nd) +{ + int len; + + nullpo_retv (sd); + nullpo_retv (nd); + + if (nd->npc_class < 0 || nd->flag & 1 || nd->npc_class == INVISIBLE_CLASS) + return; + + 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 (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] + clif_mob_equip (md, mob_get_equip (md->mob_class)); + + 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); + } + clif_changelook_accessories (&sd->bl, NULL); + + return 0; +} + +/*========================================== + * 通常攻撃エフェクト&ダメージ + *------------------------------------------ + */ +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); + + sc_data = battle_get_sc_data (dst); + + if (type != 4 && dst->type == BL_PC + && ((struct map_session_data *) dst)->special_state.infinite_endure) + type = 9; + if (sc_data) + { + if (type != 4 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if (sc_data[SC_HALLUCINATION].timer != -1) + { + if (damage > 0) + damage = + damage * (5 + sc_data[SC_HALLUCINATION].val1) + + MRAND (100); + if (damage2 > 0) + damage2 = + damage2 * (5 + sc_data[SC_HALLUCINATION].val1) + + MRAND (100); + } + } + + WBUFW (buf, 0) = 0x8a; + WBUFL (buf, 2) = src->id; + 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); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_getareachar_mob (struct map_session_data *sd, struct mob_data *md) +{ + int len; + nullpo_retv (sd); + nullpo_retv (md); + + if (md->state.state == MS_WALK) + { + len = clif_mob007b (md, WFIFOP (sd->fd, 0)); + WFIFOSET (sd->fd, len); + } + else + { + len = clif_mob0078 (md, WFIFOP (sd->fd, 0)); + WFIFOSET (sd->fd, len); + } + + if (mob_get_equip (md->mob_class) > 0) // mob equipment [Valaris] + clif_mob_equip (md, mob_get_equip (md->mob_class)); +} + +/*========================================== + * + *------------------------------------------ + */ +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 + 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); + 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; + 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 + + /* Graffiti [Valaris] */ + if (unit->group->unit_id == 0xb0) + { + WFIFOL (fd, 15) = 1; + WFIFOL (fd, 16) = 1; + memcpy (WFIFOP (fd, 17), unit->group->valstr, 80); + } + + WFIFOSET (fd, packet_len_table[0x1c9]); + 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); + + WFIFOW (fd, 0) = 0x120; + WFIFOL (fd, 2) = unit->bl.id; + WFIFOSET (fd, packet_len_table[0x120]); + if (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) +{ + 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 *); + + 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_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) + printf ("get area char ??? %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->chatID) + { + struct chat_data *cd; + cd = (struct chat_data *) map_id2bl (dstsd->chatID); + if (cd->usersd[0] == dstsd) + clif_dispchat (cd, sd->fd); + } + } + break; + case BL_NPC: + if (((struct npc_data *) bl)->npc_class != INVISIBLE_CLASS) + clif_clearchar_id (bl->id, 0, sd->fd); + break; + case BL_MOB: + 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_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)) + { + 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)) + { + clif_getareachar_mob (sd, md); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_skillinfo (struct map_session_data *sd, int skillid, int type, + int range) +{ + int fd, id; + + nullpo_retr (0, sd); + + fd = sd->fd; + if ((id = sd->status.skill[skillid].id) <= 0) + return 0; + 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_range (id, sd->status.skill[skillid].lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + WFIFOW (fd, 12) = range; + } + else + WFIFOW (fd, 12) = range; + memset (WFIFOP (fd, 14), 0, 24); + WFIFOB (fd, 38) = + (sd->status.skill[skillid].lv < skill_get_max_raise (id)) ? 1 : 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, range; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0x10f; + for (i = c = 0; i < MAX_SKILL; i++) + { + if ((id = sd->status.skill[i].id) != 0 && (sd->tmw_version >= 1)) + { // [Fate] Version 1 and later don't crash because of bad skill IDs anymore + WFIFOW (fd, len) = id; + WFIFOW (fd, len + 2) = skill_get_inf (id); + WFIFOW (fd, len + 4) = + skill_db[i].poolflags | (sd->status. + skill[i].flags & + (SKILL_POOL_ACTIVATED)); + WFIFOW (fd, len + 6) = sd->status.skill[i].lv; + WFIFOW (fd, len + 8) = skill_get_sp (id, sd->status.skill[i].lv); + range = skill_get_range (id, sd->status.skill[i].lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + WFIFOW (fd, len + 10) = range; + memset (WFIFOP (fd, len + 12), 0, 24); + WFIFOB (fd, len + 36) = + (sd->status.skill[i].lv < skill_get_max_raise (id)) ? 1 : 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 range, fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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); + range = skill_get_range (skill_num, sd->status.skill[skill_num].lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + WFIFOW (fd, 8) = range; + WFIFOB (fd, 10) = + (sd->status.skill[skill_num].lv < + skill_get_max_raise (sd->status.skill[skill_num].id)) ? 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) +{ + 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) = skill_get_pl (skill_num); //属性 + WBUFL (buf, 20) = casttime; //skill詠唱時間 + 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; + + if (type == 0x4 && battle_config.display_delay_skill_fail == 0) + { + return 0; + } + + 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); + + sc_data = battle_get_sc_data (dst); + + if (type != 5 && dst->type == BL_PC + && ((struct map_session_data *) dst)->special_state.infinite_endure) + type = 9; + if (sc_data) + { + if (type != 5 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if (sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) + damage = + damage * (5 + sc_data[SC_HALLUCINATION].val1) + MRAND (100); + } + + WBUFW (buf, 0) = 0x1de; + 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; + WBUFL (buf, 24) = damage; + WBUFW (buf, 28) = skill_lv; + WBUFW (buf, 30) = div; + WBUFB (buf, 32) = (type > 0) ? type : skill_get_hit (skill_id); + clif_send (buf, packet_len_table[0x1de], src, AREA); + + 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); + + sc_data = battle_get_sc_data (dst); + + if (type != 5 && dst->type == BL_PC + && ((struct map_session_data *) dst)->special_state.infinite_endure) + type = 9; + if (sc_data) + { + if (type != 5 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if (sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) + damage = + damage * (5 + sc_data[SC_HALLUCINATION].val1) + MRAND (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 > 0) ? type : skill_get_hit (skill_id); + clif_send (buf, packet_len_table[0x115], src, AREA); + + 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); + + 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; + 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 + + /* Graffiti [Valaris] */ + if (unit->group->unit_id == 0xb0) + { + WBUFL (buf, 15) = 1; + WBUFL (buf, 16) = 1; + memcpy (WBUFP (buf, 17), unit->group->valstr, 80); + } + + clif_send (buf, packet_len_table[0x1c9], &unit->bl, AREA); + 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; + WFIFOW (fd, 0) = 0x11c; + WFIFOW (fd, 2) = skill_num; + memcpy (WFIFOP (fd, 4), map1, 16); + memcpy (WFIFOP (fd, 20), map2, 16); + memcpy (WFIFOP (fd, 36), map3, 16); + memcpy (WFIFOP (fd, 52), map4, 16); + 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; + + 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; + 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; + + 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->mob_class); + WBUFW (buf, 4) = mob_db[md->mob_class].lv; + WBUFW (buf, 6) = mob_db[md->mob_class].size; + WBUFL (buf, 8) = md->hp; + WBUFW (buf, 12) = battle_get_def2 (&md->bl); + WBUFW (buf, 14) = mob_db[md->mob_class].race; + WBUFW (buf, 16) = + battle_get_mdef2 (&md->bl) - (mob_db[md->mob_class].vit >> 1); + WBUFW (buf, 18) = battle_get_elem_type (&md->bl); + for (i = 0; i < 9; i++) + WBUFB (buf, 20 + i) = battle_attr_fix (100, i + 1, md->def_ele); + + if (sd->status.party_id > 0) + clif_send (buf, packet_len_table[0x18c], &sd->bl, PARTY_AREA); + else + { + memcpy (WFIFOP (sd->fd, 0), buf, packet_len_table[0x18c]); + WFIFOSET (sd->fd, packet_len_table[0x18c]); + } + return 0; +} + +/*========================================== + * 状態異常アイコン/メッセージ表示 + *------------------------------------------ + */ +int clif_status_change (struct block_list *bl, int type, int flag) +{ + unsigned char buf[16]; + + 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) +{ + 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. + 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 lbuf[255]; + unsigned char *buf = + ((len + 16) >= sizeof (lbuf)) ? (unsigned char*)malloc (len + 16) : lbuf; + int lp = (flag & 0x10) ? 8 : 4; + + 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 != lbuf) + free (buf); + return 0; +} + +/*========================================== + * HPSP回復エフェクトを送信する + *------------------------------------------ + */ +int clif_heal (int fd, int type, int val) +{ + 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); + + if (bl->type == BL_PC) + { // disguises [Valaris] + struct map_session_data *sd = ((struct map_session_data *) bl); + if (sd && sd->disguise > 23 && sd->disguise < 4001) + clif_spawnpc (sd); + } + + 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); + + return 0; +} + +/*========================================== + * PVP実装?(仮) + *------------------------------------------ + */ +int clif_set0199 (int fd, int type) +{ + 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) + { + 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 + { + char buf[32]; + + WBUFW (buf, 0) = 0x19a; + WBUFL (buf, 2) = sd->bl.id; + if (sd->status.option & 0x46) + WBUFL (buf, 6) = -1; + 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; + 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) +{ + 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 +{ + WFIFOW (fd, 0) = 0x97; + WFIFOW (fd, 2) = mes_len + 24 + 4; + memcpy (WFIFOP (fd, 4), nick, 24); + 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 +{ + 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) + { + WFIFOW (fd, 0) = 0x194; + WFIFOL (fd, 2) = char_id; + memcpy (WFIFOP (fd, 6), p, 24); + 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) +{ + nullpo_retr (0, sd); + + if (sd->inventory_data[idx]) + { + int i, c; + int ep = sd->inventory_data[idx]->equip; + int fd = sd->fd; + 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) // 製造武器 + continue; + if (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; + 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; + + 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; + 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) +{ + int i, c; + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + + WFIFOW (fd, 0) = 0x0; + for (i = c = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].nameid > 0 + && sd->status.inventory[i].broken != 0) + { + 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_skill (struct map_session_data *sd, int skillid, int skilllv, + const char *name) +{ + int range, fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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); + range = skill_get_range (skillid, skilllv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + WFIFOW (fd, 12) = range; + memcpy (WFIFOP (fd, 14), name, 24); + 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, j, fd; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + if (sd->status.cart[n].broken == 1) //is weapon broken [Valaris] + WBUFB (buf, 11) = 1; + else + WBUFB (buf, 11) = sd->status.cart[n].attribute; + WBUFB (buf, 12) = sd->status.cart[n].refine; + if (sd->status.cart[n].card[0] == 0x00ff + || sd->status.cart[n].card[0] == 0x00fe + || sd->status.cart[n].card[0] == (short) 0xff00) + { + WBUFW (buf, 13) = sd->status.cart[n].card[0]; + WBUFW (buf, 15) = sd->status.cart[n].card[1]; + WBUFW (buf, 17) = sd->status.cart[n].card[2]; + WBUFW (buf, 19) = sd->status.cart[n].card[3]; + } + else + { + if (sd->status.cart[n].card[0] > 0 + && (j = itemdb_viewid (sd->status.cart[n].card[0])) > 0) + WBUFW (buf, 13) = j; + else + WBUFW (buf, 13) = sd->status.cart[n].card[0]; + if (sd->status.cart[n].card[1] > 0 + && (j = itemdb_viewid (sd->status.cart[n].card[1])) > 0) + WBUFW (buf, 15) = j; + else + WBUFW (buf, 15) = sd->status.cart[n].card[1]; + if (sd->status.cart[n].card[2] > 0 + && (j = itemdb_viewid (sd->status.cart[n].card[2])) > 0) + WBUFW (buf, 17) = j; + else + WBUFW (buf, 17) = sd->status.cart[n].card[2]; + if (sd->status.cart[n].card[3] > 0 + && (j = itemdb_viewid (sd->status.cart[n].card[3])) > 0) + WBUFW (buf, 19) = j; + else + WBUFW (buf, 19) = sd->status.cart[n].card[3]; + } + 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; + + 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; + buf = WFIFOP (fd, 0); + WBUFW (buf, 0) = 0x1ef; + 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) = 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; + WBUFW (buf, n * 18 + 14) = sd->status.cart[i].card[0]; + WBUFW (buf, n * 18 + 16) = sd->status.cart[i].card[1]; + WBUFW (buf, n * 18 + 18) = sd->status.cart[i].card[2]; + WBUFW (buf, n * 18 + 20) = sd->status.cart[i].card[3]; + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 18; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + return 0; +} + +/*========================================== + * カートの装備品リスト + *------------------------------------------ + */ +int clif_cart_equiplist (struct map_session_data *sd) +{ + struct item_data *id; + int i, j, n, fd; + unsigned char *buf; + + nullpo_retr (0, sd); + + fd = sd->fd; + buf = WFIFOP (fd, 0); + + WBUFW (buf, 0) = 0x122; + 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) = 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; + if (sd->status.cart[i].broken == 1) + WBUFB (buf, n * 20 + 14) = 1; //is weapon broken [Valaris] + else + WBUFB (buf, n * 20 + 14) = sd->status.cart[i].attribute; + WBUFB (buf, n * 20 + 15) = sd->status.cart[i].refine; + if (sd->status.cart[i].card[0] == 0x00ff + || sd->status.cart[i].card[0] == 0x00fe + || sd->status.cart[i].card[0] == (short) 0xff00) + { + WBUFW (buf, n * 20 + 16) = sd->status.cart[i].card[0]; + WBUFW (buf, n * 20 + 18) = sd->status.cart[i].card[1]; + WBUFW (buf, n * 20 + 20) = sd->status.cart[i].card[2]; + WBUFW (buf, n * 20 + 22) = sd->status.cart[i].card[3]; + } + else + { + if (sd->status.cart[i].card[0] > 0 + && (j = itemdb_viewid (sd->status.cart[i].card[0])) > 0) + WBUFW (buf, n * 20 + 16) = j; + else + WBUFW (buf, n * 20 + 16) = sd->status.cart[i].card[0]; + if (sd->status.cart[i].card[1] > 0 + && (j = itemdb_viewid (sd->status.cart[i].card[1])) > 0) + WBUFW (buf, n * 20 + 18) = j; + else + WBUFW (buf, n * 20 + 18) = sd->status.cart[i].card[1]; + if (sd->status.cart[i].card[2] > 0 + && (j = itemdb_viewid (sd->status.cart[i].card[2])) > 0) + WBUFW (buf, n * 20 + 20) = j; + else + WBUFW (buf, n * 20 + 20) = sd->status.cart[i].card[2]; + if (sd->status.cart[i].card[3] > 0 + && (j = itemdb_viewid (sd->status.cart[i].card[3])) > 0) + WBUFW (buf, n * 20 + 22) = j; + else + WBUFW (buf, n * 20 + 22) = sd->status.cart[i].card[3]; + } + n++; + } + if (n) + { + WBUFW (buf, 2) = 4 + n * 20; + WFIFOSET (fd, WFIFOW (fd, 2)); + } + return 0; +} + +/*========================================== + * パーティ作成完了 + * Relay the result of party creation. + * + * (R 00fa <flag>.B) + * + * flag: + * 0 The party was created. + * 1 The party name is invalid/taken. + * 2 The character is already in a party. + *------------------------------------------ + */ +int clif_party_created (struct map_session_data *sd, int flag) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0xfa; + WFIFOB (fd, 2) = flag; + WFIFOSET (fd, packet_len_table[0xfa]); + return 0; +} + +/*========================================== + * パーティ情報送信 + *------------------------------------------ + */ +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, 24); + 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, 24); + memcpy (WBUFP (buf, 28 + c * 46 + 28), m->map, 16); + 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が設定されてるならそれに送る + 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; +} + +/*========================================== + * パーティ勧誘 + * Relay a party invitation. + * + * (R 00fe <sender_ID>.l <party_name>.24B) + *------------------------------------------ + */ +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))) + return 0; + + WFIFOW (fd, 0) = 0xfe; + WFIFOL (fd, 2) = sd->status.account_id; + memcpy (WFIFOP (fd, 6), p->name, 24); + WFIFOSET (fd, packet_len_table[0xfe]); + return 0; +} + +/*========================================== + * パーティ勧誘結果 + * Relay the response to a party invitation. + * + * (R 00fd <name>.24B <flag>.B) + * + * flag: + * 0 The character is already in a party. + * 1 The invitation was rejected. + * 2 The invitation was accepted. + * 3 The party is full. + * 4 The character is in the same party. + *------------------------------------------ + */ +int clif_party_inviteack (struct map_session_data *sd, char *nick, int flag) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + WFIFOW (fd, 0) = 0xfd; + memcpy (WFIFOP (fd, 2), nick, 24); + 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) = ((flag & 0x10) ? 2 : p->item); + if (flag == 0) + clif_send (buf, packet_len_table[0x101], &sd->bl, PARTY); + else + { + 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, 24); + 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) + { + 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 party *p, 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); +// if(battle_config.etc_log) +// printf("clif_party_xy %d\n",sd->status.account_id); + return 0; +} + +/*========================================== + * パーティHP通知 + *------------------------------------------ + */ +int clif_party_hp (struct party *p, 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); +// if(battle_config.etc_log) +// printf("clif_party_hp %d\n",sd->status.account_id); + 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, 24); + memcpy (WBUFP (buf, 39), sd->status.name, 24); + memcpy (WBUFP (buf, 63), map[sd->bl.m].name, 16); + 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; + 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); + + 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; +} + +/*========================================== + * オートスペル リスト送信 + *------------------------------------------ + */ +int clif_autospell (struct map_session_data *sd, int skilllv) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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, int target) +{ + unsigned char buf[56]; + int n; + + nullpo_retr (0, sd); + + WBUFW (buf, 0) = 0x1cf; + WBUFL (buf, 2) = sd->bl.id; +// WBUFL(buf,6)=target; + for (n = 0; n < 5; n++) + WBUFL (buf, 6 + 4 * n) = sd->dev.val2[n]; +// WBUFL(buf,10+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_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 boolean) +{ + 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) = boolean; + + 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; + 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, 16); + 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; + 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; + WFIFOW (fd, 0) = 0x10b; + WFIFOL (fd, 2) = exp; + WFIFOSET (fd, packet_len_table[0x10b]); + return 0; +} + +/*========================================== + * ギルド作成可否通知 + * Relay the result of guild creation. + * + * (R 0167 <flag>.B) + * + * flag: + * 0 The guild was created. + * 1 The character is already in a guild. + * 2 The guild name is invalid/taken. + * 3 The Emperium item is required. + *------------------------------------------ + */ +int clif_guild_created (struct map_session_data *sd, int flag) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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); + + 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, 24); + 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); + + WBUFW (buf, 0) = 0x16d; + WBUFL (buf, 2) = g->member[idx].account_id; + WBUFL (buf, 6) = 0; + 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; + struct guild *g; + + nullpo_retr (0, sd); + + fd = sd->fd; + g = guild_search (sd->status.guild_id); + if (g != NULL && strcmp (g->master, sd->status.name) == 0) + type = 0xd7; + 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; + + 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, 24); + memcpy (WFIFOP (fd, 70), g->master, 24); + + 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 == 1) + memcpy (WFIFOP (fd, 94), "One Castle", 20); + else if (t == 2) + memcpy (WFIFOP (fd, 94), "Two Castles", 20); + else if (t == 3) + memcpy (WFIFOP (fd, 94), "Three Castles", 20); + else if (t == 4) + memcpy (WFIFOP (fd, 94), "Four Castles", 20); + else if (t == 5) + memcpy (WFIFOP (fd, 94), "Five Castles", 20); + else if (t == 6) + memcpy (WFIFOP (fd, 94), "Six Castles", 20); + else if (t == 7) + memcpy (WFIFOP (fd, 94), "Seven Castles", 20); + else if (t == 8) + memcpy (WFIFOP (fd, 94), "Eight Castles", 20); + else if (t == 9) + memcpy (WFIFOP (fd, 94), "Nine Castles", 20); + else if (t == 10) + memcpy (WFIFOP (fd, 94), "Ten Castles", 20); + else if (t == 11) + memcpy (WFIFOP (fd, 94), "Eleven Castles", 20); + else if (t == 12) + memcpy (WFIFOP (fd, 94), "Twelve Castles", 20); + else if (t == 13) + memcpy (WFIFOP (fd, 94), "Thirteen Castles", 20); + else if (t == 14) + memcpy (WFIFOP (fd, 94), "Fourteen Castles", 20); + else if (t == 15) + memcpy (WFIFOP (fd, 94), "Fifteen Castles", 20); + else if (t == 16) + memcpy (WFIFOP (fd, 94), "Sixteen Castles", 20); + else if (t == 17) + memcpy (WFIFOP (fd, 94), "Seventeen Castles", 20); + else if (t == 18) + memcpy (WFIFOP (fd, 94), "Eighteen Castles", 20); + else if (t == 19) + memcpy (WFIFOP (fd, 94), "Nineteen Castles", 20); + else if (t == 20) + memcpy (WFIFOP (fd, 94), "Twenty Castles", 20); + else if (t == 21) + memcpy (WFIFOP (fd, 94), "Twenty One Castles", 20); + else if (t == 22) + memcpy (WFIFOP (fd, 94), "Twenty Two Castles", 20); + else if (t == 23) + memcpy (WFIFOP (fd, 94), "Twenty Three Castles", 20); + else if (t == 24) + memcpy (WFIFOP (fd, 94), "Twenty Four Castles", 20); + else if (t == MAX_GUILDCASTLE) + memcpy (WFIFOP (fd, 94), "Total Domination", 20); + else + memcpy (WFIFOP (fd, 94), "None Taken", 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; + 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, 24); + 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; + g = guild_search (sd->status.guild_id); + if (g == NULL) + return 0; + + 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) = 0; + 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->pc_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, 24); + 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; + 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, 24); + } + 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; + 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, 24); + 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) = 0; + 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; + 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; + struct guild *g; + + nullpo_retr (0, sd); + + fd = sd->fd; + g = guild_search (sd->status.guild_id); + if (g == NULL) + return 0; + WFIFOW (fd, 0) = 0x0162; + WFIFOW (fd, 4) = g->skill_point; + for (i = c = 0; i < MAX_GUILDSKILL; i++) + { + if (g->skill[i].id > 0) + { + 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) = + guild_skill_get_sp (id, g->skill[i].lv); + WFIFOW (fd, c * 37 + 16) = guild_skill_get_range (id); + memset (WFIFOP (fd, c * 37 + 18), 0, 24); + WFIFOB (fd, c * 37 + 42) = //up; + (g->skill[i].lv < guild_skill_get_max (id)) ? 1 : 0; + 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 (*g->mes1 == 0 && *g->mes2 == 0) + return 0; + 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; + WFIFOW (fd, 0) = 0x16a; + WFIFOL (fd, 2) = g->guild_id; + memcpy (WFIFOP (fd, 6), g->name, 24); + 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; + 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, 24); + 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, 24); + memcpy (WBUFP (buf, 26), mes, 40); + memcpy (WBUFP (buf, 66), "dummy", 24); + 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; + 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, 24); + 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 lbuf[255]; + unsigned char *buf = lbuf; + if (len + 32 >= sizeof (lbuf)) + buf = (unsigned char *)malloc (len + 32); + 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 != lbuf) + free (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; + WFIFOW (fd, 0) = 0x10e; + WFIFOW (fd, 2) = skill_num; + WFIFOW (fd, 4) = lv; + WFIFOW (fd, 6) = guild_skill_get_sp (skill_num, lv); + WFIFOW (fd, 8) = guild_skill_get_range (skill_num); + 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; + WFIFOW (fd, 0) = 0x171; + WFIFOL (fd, 2) = account_id; + memcpy (WFIFOP (fd, 6), name, 24); + 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; + 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; + 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; + 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,24); + 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; + 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); +} + +static void clif_emotion_towards (struct block_list *bl, + struct block_list *target, int type) +{ + unsigned char buf[8]; + int len = packet_len_table[0xc0]; + struct map_session_data *sd = (struct map_session_data *) target; + + nullpo_retv (bl); + nullpo_retv (target); + + if (target->type != BL_PC) + return; + + WBUFW (buf, 0) = 0xc0; + WBUFL (buf, 2) = bl->id; + WBUFB (buf, 6) = type; + + memcpy (WFIFOP (sd->fd, 0), buf, len); + WFIFOSET (sd->fd, len); +} + +/*========================================== + * トーキーボックス + *------------------------------------------ + */ +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, 80); + 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,24); + }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; +} +*/ +/*========================================== + * 座る + *------------------------------------------ + */ +void clif_sitting (int fd, 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); +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_disp_onlyself (struct map_session_data *sd, char *mes, int len) +{ + unsigned char lbuf[255]; + unsigned char *buf = + (len + 32 >= sizeof (lbuf)) ? (unsigned char *)malloc (len + 32) : lbuf; + + nullpo_retr (0, sd); + + WBUFW (buf, 0) = 0x17f; + WBUFW (buf, 2) = len + 8; + memcpy (WBUFP (buf, 4), mes, len + 4); + + clif_send (buf, WBUFW (buf, 2), &sd->bl, SELF); + + if (buf != lbuf) + free (buf); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ + +int clif_GM_kickack (struct map_session_data *sd, int id) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + clif_parse_QuitGame (tsd->fd, tsd); + + return 0; +} + +/*========================================== + * Wis拒否許可応答 + *------------------------------------------ + */ +int clif_wisexin (struct map_session_data *sd, int type, int flag) +{ + int fd; + + nullpo_retr (0, sd); + + fd = sd->fd; + 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; + 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; + WFIFOW (fd, 0) = 0x1d3; + memcpy (WFIFOP (fd, 2), name, 24); + WFIFOB (fd, 26) = type; + WFIFOL (fd, 27) = 0; + WFIFOL (fd, 31) = bl->id; + WFIFOSET (fd, packet_len_table[0x1d3]); + + return; +} + +// 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[0x19b]); + + WBUFW (buf, 0) = 0x19b; + WBUFL (buf, 2) = bl->id; + WBUFL (buf, 6) = type; + + if (flag == 2) + { + struct map_session_data *sd = NULL; + int i; + 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) + clif_specialeffect (&sd->bl, type, 1); + } + } + + else if (flag == 1) + clif_send (buf, packet_len_table[0x19b], bl, SELF); + else if (!flag) + clif_send (buf, packet_len_table[0x19b], bl, AREA); + + return 0; + +} + +// ------------ +// clif_parse_* +// ------------ +// パケット読み取って色々操作 +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_WantToConnection (int fd, struct map_session_data *sd) +{ + struct map_session_data *old_sd; + int account_id; // account_id in the packet + + if (sd) + { + if (battle_config.error_log) + printf ("clif_parse_WantToConnection : invalid request?\n"); + return; + } + + if (RFIFOW (fd, 0) == 0x72) + { + account_id = RFIFOL (fd, 2); + } + else + return; // Not the auth packet + + WFIFOL (fd, 0) = account_id; + WFIFOSET (fd, 4); + + // if same account already connected, we disconnect the 2 sessions + if ((old_sd = map_id2sd (account_id)) != NULL) + { + clif_authfail_fd (fd, 2); // same id + clif_authfail_fd (old_sd->fd, 2); // same id + printf + ("clif_parse_WantToConnection: Double connection for account %d (sessions: #%d (new) and #%d (old)).\n", + account_id, fd, old_sd->fd); + } + else + { + CREATE (sd, struct map_session_data, 1); + session[fd]->session_data = sd; + sd->fd = fd; + + pc_setnewpc (sd, account_id, RFIFOL (fd, 6), RFIFOL (fd, 10), + RFIFOL (fd, 14), RFIFOB (fd, 18), fd); + + map_addiddb (&sd->bl); + + chrif_authreq (sd); + } + + return; +} + +/*========================================== + * 007d クライアント側マップ読み込み完了 + * map侵入時に必要なデータを全て送りつける + *------------------------------------------ + */ +void clif_parse_LoadEndAck (int fd, struct map_session_data *sd) +{ +// struct item_data* item; + int i; + nullpo_retv (sd); + + if (sd->bl.prev != NULL) + return; + + // 接続ok時 + //clif_authok(); + if (sd->npc_id) + npc_event_dequeue (sd); + clif_skillinfoblock (sd); + pc_checkitem (sd); + //guild_info(); + + // 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); + // 119 + // 78 + + if (battle_config.pc_invincible_time > 0) + { + if (map[sd->bl.m].flag.gvg) + 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); + + // pvp + if (sd->pvp_timer != -1 && !battle_config.pk_mode) + 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] + 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; + } + clif_set0199 (sd->fd, 1); + } + else + { + sd->pvp_timer = -1; + } + if (map[sd->bl.m].flag.gvg) + { + clif_set0199 (sd->fd, 3); + } + + if (sd->state.connect_new) + { + sd->state.connect_new = 0; + if (sd->status.pc_class != sd->view_class) + clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); + +/* 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 + clif_changelook (&sd->bl, LOOK_WEAPON, 0); + if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0) + clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, + sd->status.clothes_color); + + if (sd->status.hp < sd->status.max_hp >> 2 + && pc_checkskill (sd, SM_AUTOBERSERK) > 0 + && (sd->sc_data[SC_PROVOKE].timer == -1 + || sd->sc_data[SC_PROVOKE].val2 == 0)) + // オートバーサーク発動 + skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, 0); + +// if(time(&timer) < ((weddingtime=pc_readglobalreg(sd,"PC_WEDDING_TIME")) + 3600)) +// skill_status_change_start(&sd->bl,SC_WEDDING,0,weddingtime,0,0,36000,0); + + if (battle_config.muting_players && sd->status.manner < 0) + skill_status_change_start (&sd->bl, SC_NOCHAT, 0, 0, 0, 0, 0, 0); + + // option + clif_changeoption (&sd->bl); + if (sd->sc_data[SC_TRICKDEAD].timer != -1) + skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); + if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 + && !battle_check_undead (7, sd->def_ele)) + skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); + if (sd->special_state.infinite_endure + && sd->sc_data[SC_ENDURE].timer == -1) + skill_status_change_start (&sd->bl, SC_ENDURE, 10, 1, 0, 0, 0, 0); + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0002 + && sd->status.inventory[i].broken == 1) + skill_status_change_start (&sd->bl, SC_BROKNWEAPON, 0, 0, 0, 0, 0, + 0); + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0010 + && sd->status.inventory[i].broken == 1) + skill_status_change_start (&sd->bl, SC_BROKNARMOR, 0, 0, 0, 0, 0, + 0); + } + +// clif_changelook_accessories(sd, NULL); + + 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, 0, sd); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_TickSend (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + sd->client_tick = RFIFOL (fd, 2); + sd->server_tick = gettick (); + clif_servertick (sd); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_WalkToXY (int fd, struct map_session_data *sd) +{ + int x, y; + + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + + if (sd->npc_id != 0 || sd->state.storage_flag) + return; + + if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) <= 0) // フリーキャスト + return; + + if (sd->chatID) + return; + + if (sd->canmove_tick > gettick ()) + return; + + // ステータス異常やハイディング中(トンネルドライブ無)で動けない + if ((sd->opt1 > 0 && sd->opt1 != 6) || 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)) //合奏スキル演奏中は動けない + return; + if ((sd->status.option & 2) && pc_checkskill (sd, RG_TUNNELDRIVE) <= 0) + return; + + if (sd->invincible_timer != -1) + pc_delinvincibletimer (sd); + + pc_stopattack (sd); + + x = RFIFOB (fd, 2) * 4 + (RFIFOB (fd, 3) >> 6); + y = ((RFIFOB (fd, 3) & 0x3f) << 4) + (RFIFOB (fd, 4) >> 4); + 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; + + nullpo_retv (sd); + + WFIFOW (fd, 0) = 0x18b; + if ((!pc_isdead (sd) + && (sd->opt1 + || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) + || sd->skilltimer != -1 || (DIFF_TICK (tick, sd->canact_tick) < 0) + || (sd->sc_data && sd->sc_data[SC_DANCING].timer != -1 + && sd->sc_data[SC_DANCING].val4 + && (sg = (struct skill_unit_group *) sd->sc_data[SC_DANCING].val2) + && sg->src_id == sd->bl.id)) + { + WFIFOW (fd, 2) = 1; + WFIFOSET (fd, packet_len_table[0x18b]); + return; + } + + /* Rovert's prevent logout option fixed [Valaris] */ + if ((battle_config.prevent_logout + && (gettick () - sd->canlog_tick) >= 10000) + || (!battle_config.prevent_logout)) + { + clif_setwaitclose (fd); + WFIFOW (fd, 2) = 0; + } + else + { + WFIFOW (fd, 2) = 1; + } + WFIFOSET (fd, packet_len_table[0x18b]); + +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_GetCharNameRequest (int fd, struct map_session_data *sd) +{ + struct block_list *bl; + int account_id; + + account_id = RFIFOL (fd, 2); + bl = map_id2bl (account_id); + if (bl == NULL) + return; + + WFIFOW (fd, 0) = 0x95; + WFIFOL (fd, 2) = account_id; + + switch (bl->type) + { + case BL_PC: + { + struct map_session_data *ssd = (struct map_session_data *) bl; + + nullpo_retv (ssd); + + if (ssd->state.shroud_active) + memset (WFIFOP (fd, 6), 0, 24); + else + memcpy (WFIFOP (fd, 6), ssd->status.name, 24); + WFIFOSET (fd, packet_len_table[0x95]); + + struct guild *g = NULL; + struct party *p = NULL; + + char *guild_name = "", *guild_pos = "", *party_name = ""; + + int send = 0; + + if (ssd->status.guild_id > 0 && (g = guild_search (ssd->status.guild_id)) != NULL) + { + // there used to be a comment near here, but the code has changed slightly + // ギルド所属ならパケット0195を返す + // google says that means: 0195 return if the packet belongs Guild + int i, ps = -1; + for (i = 0; i < g->max_member; i++) + { + if (g->member[i].account_id == ssd->status.account_id) + ps = g->member[i].position; + } + if (ps >= 0 && ps < MAX_GUILDPOSITION) + { + guild_name = g->name; + guild_pos = g->position[ps].name; + send = 1; + } + } + if (ssd->status.party_id > 0 && (p = party_search (ssd->status.party_id)) != NULL) + { + party_name = p->name; + send = 1; + } + + if (send) + { + WFIFOW (fd, 0) = 0x195; + WFIFOL (fd, 2) = account_id; + memcpy (WFIFOP (fd, 6), party_name, 24); + memcpy (WFIFOP (fd, 30), guild_name, 24); + memcpy (WFIFOP (fd, 54), guild_pos, 24); + memcpy (WFIFOP (fd, 78), guild_pos, 24); // We send this value twice because the client expects it + WFIFOSET (fd, packet_len_table[0x195]); + + } + + if (pc_isGM(sd) >= battle_config.hack_info_GM_level) + { + in_addr_t ip = ssd->ip; + WFIFOW (fd, 0) = 0x20C; + + // Mask the IP using the char-server password + if (battle_config.mask_ip_gms) + ip = MD5_ip(chrif_getpasswd (), ssd->ip); + + WFIFOL (fd, 2) = account_id; + WFIFOL (fd, 6) = ip; + WFIFOSET (fd, packet_len_table[0x20C]); + } + + } + break; + case BL_NPC: + memcpy (WFIFOP (fd, 6), ((struct npc_data *) bl)->name, 24); + { + char *start = WFIFOP (fd, 6); + char *end = strchr (start, '#'); // [fate] elim hashed out/invisible names for the client + if (end) + while (*end) + *end++ = 0; + + // [fate] Elim preceding underscores for (hackish) name position fine-tuning + while (*start == '_') + *start++ = ' '; + } + WFIFOSET (fd, packet_len_table[0x95]); + break; + case BL_MOB: + { + struct mob_data *md = (struct mob_data *) bl; + + nullpo_retv (md); + + memcpy (WFIFOP (fd, 6), md->name, 24); + WFIFOSET (fd, packet_len_table[0x95]); + } + break; + default: + if (battle_config.error_log) + printf ("clif_parse_GetCharNameRequest : bad type %d(%d)\n", + bl->type, account_id); + break; + } +} + +/*========================================== + * Validate and process transmission of a + * global/public message. + * + * (S 008c <len>.w <message>.?B) + *------------------------------------------ + */ +void clif_parse_GlobalMessage (int fd, struct map_session_data *sd) +{ + int msg_len = RFIFOW (fd, 2) - 4; /* Header (2) + length (2). */ + size_t message_len = 0; + char *buf = NULL; + char *message = NULL; /* The message text only. */ + + nullpo_retv (sd); + + if (!(buf = clif_validate_chat (sd, 2, &message, &message_len))) + { + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + if (is_atcommand (fd, sd, message, 0) != AtCommand_None + || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 + || sd->sc_data[SC_NOCHAT].timer != -1)))//チャット禁止 + { + free (buf); + return; + } + + if (!magic_message (sd, buf, msg_len)) + { + /* Don't send chat that results in an automatic ban. */ + if (tmw_CheckChatSpam (sd, message)) + { + free (buf); + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + /* It's not a spell/magic message, so send the message to others. */ + WBUFW (buf, 0) = 0x8d; + WBUFW (buf, 2) = msg_len + 8; /* Header (2) + length (2) + ID (4). */ + WBUFL (buf, 4) = sd->bl.id; + + clif_send (buf, msg_len + 8, &sd->bl, + sd->chatID ? CHAT_WOS : AREA_CHAT_WOC); + } + + /* Send the message back to the speaker. */ + memcpy (WFIFOP (fd, 0), RFIFOP (fd, 0), RFIFOW (fd, 2)); + WFIFOW (fd, 0) = 0x8e; + WFIFOSET (fd, WFIFOW (fd, 2)); + + free (buf); + return; +} + +int clif_message (struct block_list *bl, char *msg) +{ + unsigned short msg_len = strlen (msg) + 1; + unsigned char buf[512]; + + if (msg_len + 16 > 512) + return 0; + + 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); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_MapMove (int fd, struct map_session_data *sd) +{ +// /m /mapmove (as @rura GM command) + char output[100]; + char map_name[17]; + + nullpo_retv (sd); + + memset (output, '\0', sizeof (output)); + memset (map_name, '\0', sizeof (map_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && + (pc_isGM (sd) >= get_atcommand_level (AtCommand_MapMove))) + { + memcpy (map_name, RFIFOP (fd, 2), 16); + sprintf (output, "%s %d %d", map_name, RFIFOW (fd, 18), + RFIFOW (fd, 20)); + log_atcommand (sd, "@warp %s", output); + atcommand_warp (fd, sd, "@warp", output); + } + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChangeDir (int fd, struct map_session_data *sd) +{ + unsigned char buf[64]; + short dir; + + nullpo_retv (sd); + +// RFIFOW(fd,2); // Apparently does nothing? + dir = RFIFOB (fd, 4); + + if (dir == sd->dir) + return; + + pc_setdir (sd, dir); + + WBUFW (buf, 0) = 0x9c; + WBUFL (buf, 2) = sd->bl.id; + WBUFW (buf, 6) = 0; + WBUFB (buf, 8) = dir; + if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + clif_send (buf, packet_len_table[0x9c], &sd->bl, AREA); + else + clif_send (buf, packet_len_table[0x9c], &sd->bl, AREA_WOS); + +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_Emotion (int fd, struct map_session_data *sd) +{ + unsigned char buf[64]; + + nullpo_retv (sd); + + if (battle_config.basic_skill_check == 0 + || pc_checkskill (sd, NV_EMOTE) >= 1) + { + 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) +{ + 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; + + nullpo_retv (sd); + + 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_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, 2); + action_type = RFIFOB (fd, 6); + + switch (action_type) + { + case 0x00: // once attack + case 0x07: // continuous attack + if (sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == 22 + || sd->status.option & OPTION_HIDE) + 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); + if (sd->attacktarget > 0) // [Valaris] + sd->attacktarget = 0; + pc_attack (sd, target_id, action_type != 0); + break; + case 0x02: // sitdown + pc_stop_walking (sd, 1); + skill_gangsterparadise (sd, 1); // ギャングスターパラダイス設定 + pc_setsit (sd); + clif_sitting (fd, sd); + break; + case 0x03: // standup + skill_gangsterparadise (sd, 0); // ギャングスターパラダイス解除 + pc_setstand (sd); + WBUFW (buf, 0) = 0x8a; + WBUFL (buf, 2) = sd->bl.id; + WBUFB (buf, 26) = 3; + clif_send (buf, packet_len_table[0x8a], &sd->bl, AREA); + break; + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_Restart (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + 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); + } + break; + case 0x01: + /*if(!pc_isdead(sd) && (sd->opt1 || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) + * return; */ + + /* Rovert's Prevent logout option - Fixed [Valaris] */ + if ((battle_config.prevent_logout + && (gettick () - sd->canlog_tick) >= 10000) + || (!battle_config.prevent_logout)) + { + chrif_charselectreq (sd); + } + else + { + WFIFOW (fd, 0) = 0x18b; + WFIFOW (fd, 2) = 1; + + WFIFOSET (fd, packet_len_table[0x018b]); + } + break; + } +} + +/*========================================== + * Validate and process transmission of a + * whisper/private message. + * + * (S 0096 <len>.w <nick>.24B <message>.?B) + * + * rewritten by [Yor], then partially by + * [remoitnane] + *------------------------------------------ + */ +void clif_parse_Wis (int fd, struct map_session_data *sd) +{ + size_t message_len = 0; + char *buf = NULL; + char *message = NULL; /* The message text only. */ + struct map_session_data *dstsd = NULL; + + nullpo_retv (sd); + + if (!(buf = clif_validate_chat (sd, 1, &message, &message_len))) + { + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + if (is_atcommand (fd, sd, message, 0) != AtCommand_None + || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 + || sd->sc_data[SC_NOCHAT].timer != -1))) + { + free (buf); + return; + } + + /* Don't send chat that results in an automatic ban. */ + if (tmw_CheckChatSpam (sd, message)) + { + free (buf); + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + /* + * The player is not on this server. Only send the whisper if the name is + * exactly the same, because if there are multiple map-servers and a name + * conflict (for instance, "Test" versus "test"), the char-server must + * settle the discrepancy. + */ + if (!(dstsd = map_nick2sd (RFIFOP (fd, 4))) + || strcmp (dstsd->status.name, RFIFOP (fd, 4)) != 0) + intif_wis_message (sd, RFIFOP (fd, 4), message, RFIFOW (fd, 2) - 28); + else + { + /* Refuse messages addressed to self. */ + if (dstsd->fd == fd) + { + /* "You cannot page yourself." */ + char *mes = msg_txt (504); + clif_wis_message (fd, wisp_server_name, mes, strlen (mes) + 1); + } + else + { + /* The target is ignoring all whispers. */ + if (dstsd->ignoreAll == 1) + /* Ignored by target. */ + clif_wis_end (fd, 2); + else + { + int i; + size_t end = sizeof (dstsd->ignore) / sizeof (dstsd->ignore[0]); + + /* See if the source player is being ignored. */ + for (i = 0; i < end; ++i) + if (strcmp (dstsd->ignore[i].name, sd->status.name) == 0) + { + /* Ignored by target. */ + clif_wis_end (fd, 2); + break; + } + + /* The player is not being ignored. */ + if (i == end) + { + clif_wis_message (dstsd->fd, sd->status.name, message, + RFIFOW (fd, 2) - 28); + /* The whisper was sent successfully. */ + clif_wis_end (fd, 0); + } + } + } + } + + free (buf); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_GMmessage (int fd, struct map_session_data *sd) +{ + char m[512]; + char output[200]; + nullpo_retv (sd); + + if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && + (pc_isGM (sd) >= get_atcommand_level (AtCommand_Broadcast))) + { + strncpy (m, RFIFOP (fd, 4), RFIFOW (fd, 2) - 4); + m[RFIFOW (fd, 2) - 4] = 0; + log_atcommand (sd, "/announce %s", m); + + memset (output, '\0', sizeof (output)); + snprintf (output, 199, "%s : %s", sd->status.name, m); + + intif_GMmessage (output, strlen (output) + 1, 0); + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_TakeItem (int fd, struct map_session_data *sd) +{ + struct flooritem_data *fitem; + int map_object_id; + + nullpo_retv (sd); + + map_object_id = RFIFOL (fd, 2); + fitem = (struct flooritem_data *) map_id2bl (map_object_id); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + + if (sd->npc_id != 0 || sd->opt1 > 0 || (sd->sc_data && + (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))) //会話禁止 + return; + + if (fitem == NULL || fitem->bl.m != sd->bl.m) + return; + + if (abs (sd->bl.x - fitem->bl.x) >= 2 + || abs (sd->bl.y - fitem->bl.y) >= 2) + return; // too far away to pick up + + if (sd->state.shroud_active && sd->state.shroud_disappears_on_pickup) + magic_unshroud (sd); + + pc_takeitem (sd, fitem); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_DropItem (int fd, struct map_session_data *sd) +{ + int item_index, item_amount; + + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + if (map[sd->bl.m].flag.no_player_drops) + { + clif_displaymessage(sd->fd, "Can't drop items here."); + return; + } + if (sd->npc_id != 0 || sd->opt1 > 0 || + (sd->sc_data && (sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター + sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + sd->sc_data[SC_BERSERK].timer != -1))) //バーサーク + { + clif_displaymessage(sd->fd, "Can't drop items right now."); + return; + } + + item_index = RFIFOW (fd, 2) - 2; + item_amount = RFIFOW (fd, 4); + + pc_dropitem (sd, item_index, item_amount); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_UseItem (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + if (sd->npc_id != 0 || sd->opt1 > 0 || (sd->sc_data && + (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))) //会話禁止 + return; + + if (sd->invincible_timer != -1) + pc_delinvincibletimer (sd); + + pc_useitem (sd, RFIFOW (fd, 2) - 2); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_EquipItem (int fd, struct map_session_data *sd) +{ + int index; + + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + index = RFIFOW (fd, 2) - 2; + if (sd->npc_id != 0) + return; + if (sd->sc_data + && (sd->sc_data[SC_BLADESTOP].timer != -1 + || sd->sc_data[SC_BERSERK].timer != -1)) + return; + + if (sd->status.inventory[index].identify != 1) + { // 未鑑定 + // Bjorn: Auto-identify items when equipping them as there + // is no nice way to do this in the client yet. + 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 == 10) + RFIFOW (fd, 4) = 0x8000; // 矢を無理やり装備できるように(−−; + pc_equipitem (sd, index, RFIFOW (fd, 4)); + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_UnequipItem (int fd, struct map_session_data *sd) +{ + int index; + + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + index = RFIFOW (fd, 2) - 2; + if (sd->status.inventory[index].broken == 1 && sd->sc_data + && sd->sc_data[SC_BROKNWEAPON].timer != -1) + skill_status_change_end (&sd->bl, SC_BROKNWEAPON, -1); + if (sd->status.inventory[index].broken == 1 && sd->sc_data + && sd->sc_data[SC_BROKNARMOR].timer != -1) + skill_status_change_end (&sd->bl, SC_BROKNARMOR, -1); + if (sd->sc_data + && (sd->sc_data[SC_BLADESTOP].timer != -1 + || sd->sc_data[SC_BERSERK].timer != -1)) + return; + + if (sd->npc_id != 0 || sd->opt1 > 0) + return; + pc_unequipitem (sd, index, 0); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcClicked (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + if (pc_isdead (sd)) + { + clif_clearchar_area (&sd->bl, 1); + return; + } + if (sd->npc_id != 0) + return; + npc_click (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcBuySellSelected (int fd, struct map_session_data *sd) +{ + 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; + + n = (RFIFOW (fd, 2) - 4) / 4; + item_list = (unsigned short *) RFIFOP (fd, 4); + + fail = npc_buylist (sd, n, item_list); + + 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; + + n = (RFIFOW (fd, 2) - 4) / 4; + item_list = (unsigned short *) RFIFOP (fd, 4); + + fail = npc_selllist (sd, n, item_list); + + 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) +{ + chat_createchat (sd, RFIFOW (fd, 4), RFIFOB (fd, 6), RFIFOP (fd, 7), + RFIFOP (fd, 15), RFIFOW (fd, 2) - 15); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChatAddMember (int fd, struct map_session_data *sd) +{ + chat_joinchat (sd, RFIFOL (fd, 2), RFIFOP (fd, 6)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChatRoomStatusChange (int fd, struct map_session_data *sd) +{ + chat_changechatstatus (sd, RFIFOW (fd, 4), RFIFOB (fd, 6), RFIFOP (fd, 7), + RFIFOP (fd, 15), RFIFOW (fd, 2) - 15); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChangeChatOwner (int fd, struct map_session_data *sd) +{ + chat_changechatowner (sd, RFIFOP (fd, 6)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_KickFromChat (int fd, struct map_session_data *sd) +{ + chat_kickchat (sd, 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) +{ + nullpo_retv (sd); + + if (battle_config.basic_skill_check == 0 + || pc_checkskill (sd, NV_TRADE) >= 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) +{ + nullpo_retv (sd); + + trade_tradeack (sd, RFIFOB (sd->fd, 2)); +} + +/*========================================== + * アイテム追加 + *------------------------------------------ + */ +void clif_parse_TradeAddItem (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + 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_TradeCansel (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) +{ + nullpo_retv (sd); + + if (sd->npc_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) +{ + nullpo_retv (sd); + + if (sd->npc_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) +{ + if (pc_isriding (sd)) + { // jobchange when removing peco [Valaris] + if (sd->status.pc_class == 13) + sd->status.pc_class = sd->view_class = 7; + + if (sd->status.pc_class == 21) + sd->status.pc_class = sd->view_class = 14; + + if (sd->status.pc_class == 4014) + sd->status.pc_class = sd->view_class = 4008; + + if (sd->status.pc_class == 4022) + sd->status.pc_class = sd->view_class = 4015; + } + + pc_setoption (sd, 0); +} + +/*========================================== + * チェンジカート + *------------------------------------------ + */ +void clif_parse_ChangeCart (int fd, struct map_session_data *sd) +{ + pc_setcart (sd, RFIFOW (fd, 2)); +} + +/*========================================== + * ステータスアップ + *------------------------------------------ + */ +void clif_parse_StatusUp (int fd, struct map_session_data *sd) +{ + pc_statusup (sd, RFIFOW (fd, 2)); +} + +/*========================================== + * スキルレベルアップ + *------------------------------------------ + */ +void clif_parse_SkillUp (int fd, struct map_session_data *sd) +{ + 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 (); + + nullpo_retv (sd); + + if (map[sd->bl.m].flag.noskill) + return; + if (sd->chatID || sd->npc_id != 0 || sd->state.storage_flag) + return; + + skilllv = RFIFOW (fd, 2); + skillnum = RFIFOW (fd, 4); + target_id = RFIFOL (fd, 6); + + if (sd->skilltimer != -1) + { + if (skillnum != SA_CASTCANCEL) + 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 == 22) + 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_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_CHAINCRUSH))) + { + if (!sd->state.skill_flag) + { + sd->state.skill_flag = 1; + clif_skillinfo (sd, MO_EXTREMITYFIST, 1, -1); + return; + } + else if (sd->bl.id == target_id) + { + clif_skillinfo (sd, MO_EXTREMITYFIST, 1, -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_UseSkillToPos (int fd, struct map_session_data *sd) +{ + int skillnum, skilllv, lv, x, y; + unsigned int tick = gettick (); + int skillmoreinfo; + + nullpo_retv (sd); + + if (map[sd->bl.m].flag.noskill) + return; + if (sd->npc_id != 0 || sd->state.storage_flag) + return; + if (sd->chatID) + return; + + skillmoreinfo = -1; + skilllv = RFIFOW (fd, 2); + skillnum = RFIFOW (fd, 4); + x = RFIFOW (fd, 6); + y = RFIFOW (fd, 8); + if (RFIFOW (fd, 0) == 0x190) + skillmoreinfo = 10; + + if (skillmoreinfo != -1) + { + if (pc_issit (sd)) + { + clif_skill_fail (sd, skillnum, 0, 0); + return; + } + memcpy (talkie_mes, RFIFOP (fd, skillmoreinfo), 80); + } + + 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 == 22) + 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); + } + } +} + +/*========================================== + * スキル使用(map指定) + *------------------------------------------ + */ +void clif_parse_UseSkillMap (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + if (map[sd->bl.m].flag.noskill) + return; + if (sd->chatID) + return; + + if (sd->npc_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 == 22))) + return; + + if (sd->invincible_timer != -1) + pc_delinvincibletimer (sd); + + skill_castend_map (sd, RFIFOW (fd, 2), RFIFOP (fd, 4)); +} + +/*========================================== + * メモ要求 + *------------------------------------------ + */ +void clif_parse_RequestMemo (int fd, struct map_session_data *sd) +{ + pc_memo (sd, -1); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcSelectMenu (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + sd->npc_menu = RFIFOB (fd, 6); + map_scriptcont (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcNextClicked (int fd, struct map_session_data *sd) +{ + map_scriptcont (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcAmountInput (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + +#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_ + + map_scriptcont (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * Process string-based input for an NPC. + * + * (S 01d5 <len>.w <npc_ID>.l <message>.?B) + *------------------------------------------ + */ +void clif_parse_NpcStringInput (int fd, struct map_session_data *sd) +{ + int len; + nullpo_retv (sd); + + len = RFIFOW (fd, 2) - 8; + + /* + * If we check for equal to 0, too, we'll freeze clients that send (or + * claim to have sent) an "empty" message. + */ + if (len < 0) + return; + + if (len >= sizeof (sd->npc_str) - 1) + { + printf ("clif_parse_NpcStringInput(): Input string too long!\n"); + len = sizeof (sd->npc_str) - 1; + } + + if (len > 0) + strncpy (sd->npc_str, RFIFOP (fd, 8), len); + sd->npc_str[len] = '\0'; + + map_scriptcont (sd, RFIFOL (fd, 4)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcCloseClicked (int fd, struct map_session_data *sd) +{ + map_scriptcont (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * アイテム鑑定 + *------------------------------------------ + */ +void clif_parse_ItemIdentify (int fd, struct map_session_data *sd) +{ + pc_item_identify (sd, RFIFOW (fd, 2) - 2); +} + +/*========================================== + * オートスペル受信 + *------------------------------------------ + */ +void clif_parse_AutoSpell (int fd, struct map_session_data *sd) +{ + skill_autospell (sd, RFIFOW (fd, 2)); +} + +/*========================================== + * カード使用 + *------------------------------------------ + */ +void clif_parse_UseCard (int fd, struct map_session_data *sd) +{ + clif_use_card (sd, RFIFOW (fd, 2) - 2); +} + +/*========================================== + * カード挿入装備選択 + *------------------------------------------ + */ +void clif_parse_InsertCard (int fd, struct map_session_data *sd) +{ + 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; + + char_id = RFIFOL (fd, 2); + clif_solved_charname (sd, char_id); +} + +/*========================================== + * 0197 /resetskill /resetstate + *------------------------------------------ + */ +void clif_parse_ResetChar (int fd, struct map_session_data *sd) +{ + nullpo_retv (sd); + + if (battle_config.atc_gmonly == 0 || pc_isGM (sd)) + { + switch (RFIFOW (fd, 2)) + { + case 0: + log_atcommand (sd, "@charstreset %s", sd->status.name); + if (pc_isGM (sd) >= + get_atcommand_level (AtCommand_ResetState)) + pc_resetstate (sd); + break; + case 1: + log_atcommand (sd, "@charskreset %s", sd->status.name); + if (pc_isGM (sd) >= + get_atcommand_level (AtCommand_ResetState)) + pc_resetskill (sd); + break; + } + } +} + +/*========================================== + * 019c /lb等 + *------------------------------------------ + */ +void clif_parse_LGMmessage (int fd, struct map_session_data *sd) +{ + unsigned char buf[64]; + + nullpo_retv (sd); + + 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; + + nullpo_retv (sd); + + item_index = RFIFOW (fd, 2) - 2; + item_amount = RFIFOL (fd, 4); + + if ((sd->npc_id != 0 && !sd->npc_flags.storage) || sd->trade_partner != 0 + || !sd->state.storage_flag) + 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; + + nullpo_retv (sd); + + item_index = RFIFOW (fd, 2) - 1; + item_amount = RFIFOL (fd, 4); + + if ((sd->npc_id != 0 && !sd->npc_flags.storage) || sd->trade_partner != 0 + || !sd->state.storage_flag) + return; + + 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) +{ + nullpo_retv (sd); + + if ((sd->npc_id != 0 && !sd->npc_flags.storage) || 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) +{ + nullpo_retv (sd); + + if ((sd->npc_id != 0 && !sd->npc_flags.storage) || 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) +{ + nullpo_retv (sd); + + if (sd->state.storage_flag == 1) + storage_storageclose (sd); + else if (sd->state.storage_flag == 2) + storage_guild_storageclose (sd); +} + +/*========================================== + * パーティを作る + * Process request to create a party. + * + * (S 00f9 <party_name>.24B) + *------------------------------------------ + */ +void clif_parse_CreateParty (int fd, struct map_session_data *sd) +{ + if (battle_config.basic_skill_check == 0 + || pc_checkskill (sd, NV_PARTY) >= 2) + { + party_create (sd, RFIFOP (fd, 2)); + } + else + clif_skill_fail (sd, 1, 0, 4); +} + +/*========================================== + * パーティを作る + * Process request to create a party. + * + * (S 01e8 <party_name>.24B <exp>.B <itm>.B) + * + * Note: Upstream eAthena uses this to + * specify experience/item sharing, + * respectively, but it was left + * incomplete here. + *------------------------------------------ + */ +void clif_parse_CreateParty2 (int fd, struct map_session_data *sd) +{ + if (battle_config.basic_skill_check == 0 + || pc_checkskill (sd, NV_PARTY) >= 2) + { + party_create (sd, RFIFOP (fd, 2)); + } + else + clif_skill_fail (sd, 1, 0, 4); +} + +/*========================================== + * パーティに勧誘 + * Process invitation to join a party. + * + * (S 00fc <account_ID>.l) + *------------------------------------------ + */ +void clif_parse_PartyInvite (int fd, struct map_session_data *sd) +{ + party_invite (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * パーティ勧誘返答 + * Process reply to party invitation. + * + * (S 00ff <account_ID>.l <flag>.l) + *------------------------------------------ + */ +void clif_parse_ReplyPartyInvite (int fd, struct map_session_data *sd) +{ + if (battle_config.basic_skill_check == 0 + || pc_checkskill (sd, NV_PARTY) >= 1) + { + party_reply_invite (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); + } + else + { + party_reply_invite (sd, RFIFOL (fd, 2), 0); + 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) +{ + party_removemember (sd, RFIFOL (fd, 2), RFIFOP (fd, 6)); +} + +/*========================================== + * パーティ設定変更要求 + *------------------------------------------ + */ +void clif_parse_PartyChangeOption (int fd, struct map_session_data *sd) +{ + party_changeoption (sd, RFIFOW (fd, 2), RFIFOW (fd, 4)); +} + +/*========================================== + * パーティメッセージ送信要求 + * Validate and process transmission of a + * party message. + * + * (S 0108 <len>.w <message>.?B) + *------------------------------------------ + */ +void clif_parse_PartyMessage (int fd, struct map_session_data *sd) +{ + size_t message_len = 0; + char *buf = NULL; + char *message = NULL; /* The message text only. */ + + nullpo_retv (sd); + + if (!(buf = clif_validate_chat (sd, 0, &message, &message_len))) + { + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + if (is_atcommand (fd, sd, message, 0) != AtCommand_None + || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 + || sd->sc_data[SC_NOCHAT].timer != -1))) //チャット禁止 + { + free (buf); + return; + } + + /* Don't send chat that results in an automatic ban. */ + if (tmw_CheckChatSpam (sd, message)) + { + free (buf); + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + party_send_message (sd, message, RFIFOW (fd, 2) - 4); + free (buf); +} + +/*========================================== + * /monster /item rewriten by [Yor] + *------------------------------------------ + */ +void clif_parse_GM_Monster_Item (int fd, struct map_session_data *sd) +{ + char monster_item_name[25]; + + nullpo_retv (sd); + + memset (monster_item_name, '\0', sizeof (monster_item_name)); + + if (battle_config.atc_gmonly == 0 || pc_isGM (sd)) + { + memcpy (monster_item_name, RFIFOP (fd, 2), 24); + + if (mobdb_searchname (monster_item_name) != 0) + { + if (pc_isGM (sd) >= get_atcommand_level (AtCommand_Monster)) + { + log_atcommand (sd, "@spawn %s", monster_item_name); + atcommand_spawn (fd, sd, "@spawn", monster_item_name); // as @spawn + } + } + else if (itemdb_searchname (monster_item_name) != NULL) + { + if (pc_isGM (sd) >= get_atcommand_level (AtCommand_Item)) + { + log_atcommand (sd, "@item %s", monster_item_name); + atcommand_item (fd, sd, "@item", monster_item_name); // as @item + } + } + + } +} + +/*========================================== + * ギルドを作る + * Process request to create a guild. + * + * (S 0165 <account_ID>.l <guild_name>.24B) + * + * Note: The account ID seems to be ignored. + *------------------------------------------ + */ +void clif_parse_CreateGuild (int fd, struct map_session_data *sd) +{ + guild_create (sd, RFIFOP (fd, 6)); +} + +/*========================================== + * ギルドマスターかどうか確認 + *------------------------------------------ + */ +void clif_parse_GuildCheckMaster (int fd, struct map_session_data *sd) +{ + clif_guild_masterormember (sd); +} + +/*========================================== + * ギルド情報要求 + *------------------------------------------ + */ +void clif_parse_GuildReqeustInfo (int fd, struct map_session_data *sd) +{ + 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) + printf ("clif: guild request info: unknown type %d\n", + RFIFOL (fd, 2)); + break; + } +} + +/*========================================== + * ギルド役職変更 + *------------------------------------------ + */ +void clif_parse_GuildChangePositionInfo (int fd, struct map_session_data *sd) +{ + struct guild *g; + int i, ps; + + nullpo_retv (sd); + + g = guild_search (sd->status.guild_id); + + if (g == NULL) + return; + + if ((ps = guild_getposition (sd, g)) < 0 + || (!(g->position[ps].mode & 0x0010) && strcmp (g->master, sd->status.name))) + return; + + for (i = 4; i < RFIFOW (fd, 2); i += 40) + { + guild_change_position (sd, RFIFOL (fd, i), RFIFOL (fd, i + 4), + RFIFOL (fd, i + 12), RFIFOP (fd, i + 16)); + } +} + +/*========================================== + * ギルドメンバ役職変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeMemberPosition (int fd, + struct map_session_data *sd) +{ + struct guild *g; + int i, ps; + + nullpo_retv (sd); + + g = guild_search (sd->status.guild_id); + + if (g == NULL) + return; + + if ((ps = guild_getposition (sd, g)) < 0 + || (!(g->position[ps].mode & 0x0010) && strcmp (g->master, sd->status.name))) + return; + + 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 = guild_search (RFIFOL (fd, 2)); + if (g != NULL) + clif_guild_emblem (sd, g); +} + +/*========================================== + * ギルドエンブレム変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeEmblem (int fd, struct map_session_data *sd) +{ + guild_change_emblem (sd, RFIFOW (fd, 2) - 4, RFIFOP (fd, 4)); +} + +/*========================================== + * ギルド告知変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeNotice (int fd, struct map_session_data *sd) +{ + guild_change_notice (sd, RFIFOL (fd, 2), RFIFOP (fd, 6), RFIFOP (fd, 66)); +} + +/*========================================== + * ギルド勧誘 + *------------------------------------------ + */ +void clif_parse_GuildInvite (int fd, struct map_session_data *sd) +{ + guild_invite (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * ギルド勧誘返信 + *------------------------------------------ + */ +void clif_parse_GuildReplyInvite (int fd, struct map_session_data *sd) +{ + guild_reply_invite (sd, RFIFOL (fd, 2), RFIFOB (fd, 6)); +} + +/*========================================== + * ギルド脱退 + *------------------------------------------ + */ +void clif_parse_GuildLeave (int fd, struct map_session_data *sd) +{ + guild_leave (sd, RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), + RFIFOP (fd, 14)); +} + +/*========================================== + * ギルド追放 + *------------------------------------------ + */ +void clif_parse_GuildExplusion (int fd, struct map_session_data *sd) +{ + guild_explusion (sd, RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), + RFIFOP (fd, 14)); +} + +/*========================================== + * ギルド会話 + * Validate and process transmission of a + * guild message. + * + * (S 017e <len>.w <message>.?B) + *------------------------------------------ + */ +void clif_parse_GuildMessage (int fd, struct map_session_data *sd) +{ + size_t message_len = 0; + char *buf = NULL; + char *message = NULL; /* The message text only. */ + + nullpo_retv (sd); + + if (!(buf = clif_validate_chat (sd, 2, &message, &message_len))) + { + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + if (is_atcommand (fd, sd, message, 0) != AtCommand_None + || (sd->sc_data && (sd->sc_data[SC_BERSERK].timer != -1 //バーサーク時は会話も不可 + || sd->sc_data[SC_NOCHAT].timer != -1))) //チャット禁止 + { + free (buf); + return; + } + + /* Don't send chat that results in an automatic ban. */ + if (tmw_CheckChatSpam (sd, message)) + { + free (buf); + /* "Your message could not be sent." */ + clif_displaymessage (fd, msg_txt (505)); + return; + } + + guild_send_message (sd, buf + 8, RFIFOW (fd, 2) - 4); + free (buf); +} + +/*========================================== + * ギルド同盟要求 + *------------------------------------------ + */ +void clif_parse_GuildRequestAlliance (int fd, struct map_session_data *sd) +{ + guild_reqalliance (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * ギルド同盟要求返信 + *------------------------------------------ + */ +void clif_parse_GuildReplyAlliance (int fd, struct map_session_data *sd) +{ + guild_reply_reqalliance (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); +} + +/*========================================== + * ギルド関係解消 + *------------------------------------------ + */ +void clif_parse_GuildDelAlliance (int fd, struct map_session_data *sd) +{ + guild_delalliance (sd, RFIFOL (fd, 2), RFIFOL (fd, 6)); +} + +/*========================================== + * ギルド敵対 + *------------------------------------------ + */ +void clif_parse_GuildOpposition (int fd, struct map_session_data *sd) +{ + guild_opposition (sd, RFIFOL (fd, 2)); +} + +/*========================================== + * ギルド解散 + *------------------------------------------ + */ +void clif_parse_GuildBreak (int fd, struct map_session_data *sd) +{ + guild_break (sd, 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 = RFIFOL (fd, 2); + + nullpo_retv (sd); + + 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; + log_atcommand (sd, "@kick %s", tsd->status.name); + 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[25]; + + nullpo_retv (sd); + + memset (player_name, '\0', sizeof (player_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && + (pc_isGM (sd) >= get_atcommand_level (AtCommand_Goto))) + { + memcpy (player_name, RFIFOP (fd, 2), 24); + log_atcommand (sd, "@goto %s", player_name); + atcommand_goto (fd, sd, "@goto", player_name); // as @jumpto + } + + return; +} + +/*========================================== + * /recall + *------------------------------------------ + */ +void clif_parse_Recall (int fd, struct map_session_data *sd) +{ // Added by RoVeRT + char player_name[25]; + + nullpo_retv (sd); + + memset (player_name, '\0', sizeof (player_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && + (pc_isGM (sd) >= get_atcommand_level (AtCommand_Recall))) + { + memcpy (player_name, RFIFOP (fd, 2), 24); + log_atcommand (sd, "@recall %s", player_name); + atcommand_recall (fd, sd, "@recall", player_name); // as @recall + } + + return; +} + +void clif_parse_GMHide (int fd, struct map_session_data *sd) +{ // Modified by [Yor] + nullpo_retv (sd); + + //printf("%2x %2x %2x\n", RFIFOW(fd,0), RFIFOW(fd,2), RFIFOW(fd,4)); // R 019d <Option_value>.2B <flag>.2B + if ((battle_config.atc_gmonly == 0 || pc_isGM (sd)) && + (pc_isGM (sd) >= get_atcommand_level (AtCommand_Hide))) + { + log_atcommand (sd, "@hide"); + if (sd->status.option & OPTION_HIDE) + { // OPTION_HIDE = 0x40 + sd->status.option &= ~OPTION_HIDE; // OPTION_HIDE = 0x40 + /* "Invisible: Off." */ + clif_displaymessage (fd, msg_txt (10)); + } + else + { + sd->status.option |= OPTION_HIDE; // OPTION_HIDE = 0x40 + /* "Invisible: On." */ + clif_displaymessage (fd, msg_txt (11)); + } + clif_changeoption (&sd->bl); + } +} + +/*========================================== + * GMによるチャット禁止時間付与 + *------------------------------------------ + */ +void clif_parse_GMReqNoChat (int fd, struct map_session_data *sd) +{ + int tid = RFIFOL (fd, 2); + int type = RFIFOB (fd, 6); + int limit = RFIFOW (fd, 7); + struct block_list *bl = map_id2bl (tid); + struct map_session_data *dstsd; + int dstfd; + + nullpo_retv (sd); + + if (!battle_config.muting_players) + { + /* "Muting is disabled." */ + clif_displaymessage (fd, msg_txt (245)); + return; + } + + if (type == 0) + limit = 0 - limit; + if (bl->type == BL_PC && (dstsd = (struct map_session_data *) bl)) + { + if ((tid == bl->id && type == 2 && !pc_isGM (sd)) + || (pc_isGM (sd) > pc_isGM (dstsd))) + { + dstfd = dstsd->fd; + WFIFOW (dstfd, 0) = 0x14b; + WFIFOB (dstfd, 2) = (type == 2) ? 1 : type; + memcpy (WFIFOP (dstfd, 3), sd->status.name, 24); + WFIFOSET (dstfd, packet_len_table[0x14b]); + dstsd->status.manner -= limit; + if (dstsd->status.manner < 0) + skill_status_change_start (bl, SC_NOCHAT, 0, 0, 0, 0, 0, 0); + else + { + dstsd->status.manner = 0; + skill_status_change_end (bl, SC_NOCHAT, -1); + } + printf ("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 = RFIFOL (fd, 2); + + WFIFOW (fd, 0) = 0x1e0; + WFIFOL (fd, 2) = tid; + sprintf (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[1024]; + char *nick; // S 00cf <nick>.24B <type>.B: 00 (/ex nick) deny speech from nick, 01 (/in nick) allow speech from nick + int i; + int pos; + + memset (output, '\0', sizeof (output)); + + nick = RFIFOP (fd, 2); // speed up + //printf("Ignore: char '%s' state: %d\n", nick, RFIFOB(fd,26)); + // we ask for deny (we add nick only if it's not already exist + if (RFIFOB (fd, 26) == 0) + { // type + if (strlen (nick) >= 4 && strlen (nick) < 24) + { // do something only if nick can be exist + pos = -1; + for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); + i++) + { + if (strcmp (sd->ignore[i].name, nick) == 0) + break; + else if (pos == -1 && sd->ignore[i].name[0] == '\0') + pos = i; + } + WFIFOW (fd, 0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB (fd, 2) = 0; + // if a position is found and name not found, we add it in the list + if (pos != -1 + && i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) + { + memcpy (sd->ignore[pos].name, nick, 24); + WFIFOB (fd, 3) = 0; // success + WFIFOSET (fd, packet_len_table[0x0d1]); + if (strcmp (wisp_server_name, nick) == 0) + { // to found possible bot users that automaticaly ignores 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, strlen (output) + 1); + // 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 + if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) + { + 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 that automaticaly ignores 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, strlen (output) + 1); + } + } + else + { + 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 that automaticaly ignores 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, strlen (output) + 1); + } + } + } + } + else + clif_wis_message (fd, wisp_server_name, + "It's impossible to block this player.", + strlen ("It's impossible to block this player.") + + 1); + // we ask for allow (we remove all same nick if exist) + } + else + { + if (strlen (nick) >= 4 && strlen (nick) < 24) + { // do something only if nick can be exist + WFIFOW (fd, 0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB (fd, 2) = 1; + for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); + i++) + if (strcmp (sd->ignore[i].name, nick) == 0) + { + memset (sd->ignore[i].name, 0, + sizeof (sd->ignore[i].name)); + WFIFOB (fd, 3) = 0; // success + WFIFOSET (fd, packet_len_table[0x0d1]); + break; + } + if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) + { + 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); + } + } + else + clif_wis_message (fd, wisp_server_name, + "It's impossible to unblock this player.", + strlen + ("It's impossible to unblock this player.") + + 1); + } + +// for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); 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)); + if (RFIFOB (fd, 2) == 0) + { // S 00d0 <type>len.B: 00 (/exall) deny all speech, 01 (/inall) allow all speech + 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 + { + 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; +} + +void clif_parse_skillMessage (int fd, struct map_session_data *sd) +{ // Added by RoVeRT + int skillid, skilllv, x, y; + char *mes; + + skilllv = RFIFOW (fd, 2); + skillid = RFIFOW (fd, 4); + + y = RFIFOB (fd, 6); + x = RFIFOB (fd, 8); + + mes = RFIFOP (fd, 10); + + // skill 220 = graffiti +// printf("skill: %d %d location: %3d %3d message: %s\n", skillid, skilllv, x, y, (char*)mes); +} + +int monk (struct map_session_data *sd, struct block_list *target, int type) +{ +//R 01d1 <Monk id>L <Target monster id>L <Bool>L + int fd = sd->fd; + WFIFOW (fd, 0) = 0x1d1; + WFIFOL (fd, 2) = sd->bl.id; + WFIFOL (fd, 6) = target->id; + WFIFOL (fd, 10) = type; + WFIFOSET (fd, packet_len_table[0x1d1]); + + return 0; +} + +/*========================================== + * スパノビの/doridoriによるSPR2倍 + *------------------------------------------ + */ +void clif_parse_sn_doridori (int fd, struct map_session_data *sd) +{ + if (sd) + sd->doridori_counter = 1; + + return; +} + +/*========================================== + * スパノビの爆裂波動 + *------------------------------------------ + */ +void clif_parse_sn_explosionspirits (int fd, struct map_session_data *sd) +{ + if (sd) + { + int nextbaseexp = pc_nextbaseexp (sd); + struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); + if (battle_config.etc_log) + { + if (nextbaseexp != 0) + printf ("SuperNovice explosionspirits!! %d %d %d %d\n", + sd->bl.id, s_class.job, sd->status.base_exp, + (int) ((double) 1000 * sd->status.base_exp / + nextbaseexp)); + else + printf ("SuperNovice explosionspirits!! %d %d %d 000\n", + sd->bl.id, s_class.job, sd->status.base_exp); + } + if (s_class.job == 23 && 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); + skill_status_change_start (&sd->bl, + SkillStatusChangeTable + [MO_EXPLOSIONSPIRITS], 5, 0, 0, 0, + skill_get_time (MO_EXPLOSIONSPIRITS, + 5), 0); + } + } + return; +} + +// functions list. Rate is how many milliseconds are required between +// calls. Packets exceeding this rate will be dropped. flood_rates in +// map.h must be the same length as this table. rate 0 is default +// rate -1 is unlimited +typedef struct func_table +{ + void (*func)(int fd, struct map_session_data *sd); + int rate; +} func_table; +// *INDENT-OFF* +func_table clif_parse_func_table[0x220] = +{ + { NULL, 0 }, // 0 + { NULL, 0 }, // 1 + { NULL, 0 }, // 2 + { NULL, 0 }, // 3 + { NULL, 0 }, // 4 + { NULL, 0 }, // 5 + { NULL, 0 }, // 6 + { NULL, 0 }, // 7 + { NULL, 0 }, // 8 + { NULL, 0 }, // 9 + { NULL, 0 }, // a + { NULL, 0 }, // b + { NULL, 0 }, // c + { NULL, 0 }, // d + { NULL, 0 }, // e + { NULL, 0 }, // f + { NULL, 0 }, // 10 + { NULL, 0 }, // 11 + { NULL, 0 }, // 12 + { NULL, 0 }, // 13 + { NULL, 0 }, // 14 + { NULL, 0 }, // 15 + { NULL, 0 }, // 16 + { NULL, 0 }, // 17 + { NULL, 0 }, // 18 + { NULL, 0 }, // 19 + { NULL, 0 }, // 1a + { NULL, 0 }, // 1b + { NULL, 0 }, // 1c + { NULL, 0 }, // 1d + { NULL, 0 }, // 1e + { NULL, 0 }, // 1f + { NULL, 0 }, // 20 + { NULL, 0 }, // 21 + { NULL, 0 }, // 22 + { NULL, 0 }, // 23 + { NULL, 0 }, // 24 + { NULL, 0 }, // 25 + { NULL, 0 }, // 26 + { NULL, 0 }, // 27 + { NULL, 0 }, // 28 + { NULL, 0 }, // 29 + { NULL, 0 }, // 2a + { NULL, 0 }, // 2b + { NULL, 0 }, // 2c + { NULL, 0 }, // 2d + { NULL, 0 }, // 2e + { NULL, 0 }, // 2f + { NULL, 0 }, // 30 + { NULL, 0 }, // 31 + { NULL, 0 }, // 32 + { NULL, 0 }, // 33 + { NULL, 0 }, // 34 + { NULL, 0 }, // 35 + { NULL, 0 }, // 36 + { NULL, 0 }, // 37 + { NULL, 0 }, // 38 + { NULL, 0 }, // 39 + { NULL, 0 }, // 3a + { NULL, 0 }, // 3b + { NULL, 0 }, // 3c + { NULL, 0 }, // 3d + { NULL, 0 }, // 3e + { NULL, 0 }, // 3f + { NULL, 0 }, // 40 + { NULL, 0 }, // 41 + { NULL, 0 }, // 42 + { NULL, 0 }, // 43 + { NULL, 0 }, // 44 + { NULL, 0 }, // 45 + { NULL, 0 }, // 46 + { NULL, 0 }, // 47 + { NULL, 0 }, // 48 + { NULL, 0 }, // 49 + { NULL, 0 }, // 4a + { NULL, 0 }, // 4b + { NULL, 0 }, // 4c + { NULL, 0 }, // 4d + { NULL, 0 }, // 4e + { NULL, 0 }, // 4f + { NULL, 0 }, // 50 + { NULL, 0 }, // 51 + { NULL, 0 }, // 52 + { NULL, 0 }, // 53 + { NULL, 0 }, // 54 + { NULL, 0 }, // 55 + { NULL, 0 }, // 56 + { NULL, 0 }, // 57 + { NULL, 0 }, // 58 + { NULL, 0 }, // 59 + { NULL, 0 }, // 5a + { NULL, 0 }, // 5b + { NULL, 0 }, // 5c + { NULL, 0 }, // 5d + { NULL, 0 }, // 5e + { NULL, 0 }, // 5f + { NULL, 0 }, // 60 + { NULL, 0 }, // 61 + { NULL, 0 }, // 62 + { NULL, 0 }, // 63 + { NULL, 0 }, // 64 + { NULL, 0 }, // 65 + { NULL, 0 }, // 66 + { NULL, 0 }, // 67 + { NULL, 0 }, // 68 + { NULL, 0 }, // 69 + { NULL, 0 }, // 6a + { NULL, 0 }, // 6b + { NULL, 0 }, // 6c + { NULL, 0 }, // 6d + { NULL, 0 }, // 6e + { NULL, 0 }, // 6f + { NULL, 0 }, // 70 + { NULL, 0 }, // 71 + { clif_parse_WantToConnection, 0 }, // 72 + { NULL, 0 }, // 73 + { NULL, 0 }, // 74 + { NULL, 0 }, // 75 + { NULL, 0 }, // 76 + { NULL, 0 }, // 77 + { NULL, 0 }, // 78 + { NULL, 0 }, // 79 + { NULL, 0 }, // 7a + { NULL, 0 }, // 7b + { NULL, 0 }, // 7c + { clif_parse_LoadEndAck, -1 }, // 7d + { clif_parse_TickSend, 0 }, // 7e + { NULL, 0 }, // 7f + { NULL, 0 }, // 80 + { NULL, 0 }, // 81 + { NULL, 0 }, // 82 + { NULL, 0 }, // 83 + { NULL, 0 }, // 84 + { clif_parse_WalkToXY, -1 }, // 85 Walk code limits this on it's own + { NULL, 0 }, // 86 + { NULL, 0 }, // 87 + { NULL, 0 }, // 88 + { clif_parse_ActionRequest, 1000 }, // 89 Special case - see below + { NULL, 0 }, // 8a + { NULL, 0 }, // 8b + { clif_parse_GlobalMessage, 300 }, // 8c + { NULL, 0 }, // 8d + { NULL, 0 }, // 8e + { NULL, 0 }, // 8f + { clif_parse_NpcClicked, 500 }, // 90 + { NULL, 0 }, // 91 + { NULL, 0 }, // 92 + { NULL, 0 }, // 93 + { clif_parse_GetCharNameRequest, -1 }, // 94 + { NULL, 0 }, // 95 + { clif_parse_Wis, 300 }, // 96 + { NULL, 0 }, // 97 + { NULL, 0 }, // 98 + { clif_parse_GMmessage, 300 }, // 99 + { NULL, 0 }, // 9a + { clif_parse_ChangeDir, -1 }, // 9b + { NULL, 0 }, // 9c + { NULL, 0 }, // 9d + { NULL, 0 }, // 9e + { clif_parse_TakeItem, 400 }, // 9f + { NULL, 0 }, // a0 + { NULL, 0 }, // a1 + { clif_parse_DropItem, 50 }, // a2 + { NULL, 0 }, // a3 + { NULL, 0 }, // a4 + { NULL, 0 }, // a5 + { NULL, 0 }, // a6 + { clif_parse_UseItem, 0 }, // a7 + { NULL, 0 }, // a8 + { clif_parse_EquipItem, -1 }, // a9 Special case - outfit window (not implemented yet - needs to allow bursts) + { NULL, 0 }, // aa + { clif_parse_UnequipItem, -1 }, // ab Special case - outfit window (not implemented yet - needs to allow bursts) + { NULL, 0 }, // ac + { NULL, 0 }, // ad + { NULL, 0 }, // ae + { NULL, 0 }, // af + { NULL, 0 }, // b0 + { NULL, 0 }, // b1 + { clif_parse_Restart, 0 }, // b2 + { NULL, 0 }, // b3 + { NULL, 0 }, // b4 + { NULL, 0 }, // b5 + { NULL, 0 }, // b6 + { NULL, 0 }, // b7 + { clif_parse_NpcSelectMenu, 0 }, // b8 + { clif_parse_NpcNextClicked, -1 }, // b9 + { NULL, 0 }, // ba + { clif_parse_StatusUp, -1 }, // bb People click this very quickly + { NULL, 0 }, // bc + { NULL, 0 }, // bd + { NULL, 0 }, // be + { clif_parse_Emotion, 1000 }, // bf + { NULL, 0 }, // c0 + { clif_parse_HowManyConnections, 0 }, // c1 + { NULL, 0 }, // c2 + { NULL, 0 }, // c3 + { NULL, 0 }, // c4 + { clif_parse_NpcBuySellSelected, 0 }, // c5 + { NULL, 0 }, // c6 + { NULL, 0 }, // c7 + { clif_parse_NpcBuyListSend, -1 }, // c8 + { clif_parse_NpcSellListSend, -1 }, // c9 Selling multiple 1-slot items + { NULL, 0 }, // ca + { NULL, 0 }, // cb + { clif_parse_GMKick, 0 }, // cc + { NULL, 0 }, // cd + { NULL, 0 }, // ce + { clif_parse_PMIgnore, 0 }, // cf + { clif_parse_PMIgnoreAll, 0 }, // d0 + { NULL, 0 }, // d1 + { NULL, 0 }, // d2 + { NULL, 0 }, // d3 + { NULL, 0 }, // d4 + { clif_parse_CreateChatRoom, 1000 }, // d5 + { NULL, 0 }, // d6 + { NULL, 0 }, // d7 + { NULL, 0 }, // d8 + { clif_parse_ChatAddMember, 0 }, // d9 + { NULL, 0 }, // da + { NULL, 0 }, // db + { NULL, 0 }, // dc + { NULL, 0 }, // dd + { clif_parse_ChatRoomStatusChange, 0 }, // de + { NULL, 0 }, // df + { clif_parse_ChangeChatOwner, 0 }, // e0 + { NULL, 0 }, // e1 + { clif_parse_KickFromChat, 0 }, // e2 + { clif_parse_ChatLeave, 0 }, // e3 + { clif_parse_TradeRequest, 2000 }, // e4 + { NULL, 0 }, // e5 + { clif_parse_TradeAck, 0 }, // e6 + { NULL, 0 }, // e7 + { clif_parse_TradeAddItem, 0 }, // e8 + { NULL, 0 }, // e9 + { NULL, 0 }, // ea + { clif_parse_TradeOk, 0 }, // eb + { NULL, 0 }, // ec + { clif_parse_TradeCansel, 0 }, // ed + { NULL, 0 }, // ee + { clif_parse_TradeCommit, 0 }, // ef + { NULL, 0 }, // f0 + { NULL, 0 }, // f1 + { NULL, 0 }, // f2 + { clif_parse_MoveToKafra, -1 }, // f3 + { NULL, 0 }, // f4 + { clif_parse_MoveFromKafra, -1 }, // f5 + { NULL, 0 }, // f6 + { clif_parse_CloseKafra, 0 }, // f7 + { NULL, 0 }, // f8 + { clif_parse_CreateParty, 2000 }, // f9 + { NULL, 0 }, // fa + { NULL, 0 }, // fb + { clif_parse_PartyInvite, 2000 }, // fc + { NULL, 0 }, // fd + { NULL, 0 }, // fe + { clif_parse_ReplyPartyInvite, 0 }, // ff + { clif_parse_LeaveParty, 0 }, // 100 + { NULL, 0 }, // 101 + { clif_parse_PartyChangeOption, 0 }, // 102 + { clif_parse_RemovePartyMember, 0 }, // 103 + { NULL, 0 }, // 104 + { NULL, 0 }, // 105 + { NULL, 0 }, // 106 + { NULL, 0 }, // 107 + { clif_parse_PartyMessage, 300 }, // 108 + { NULL, 0 }, // 109 + { NULL, 0 }, // 10a + { NULL, 0 }, // 10b + { NULL, 0 }, // 10c + { NULL, 0 }, // 10d + { NULL, 0 }, // 10e + { NULL, 0 }, // 10f + { NULL, 0 }, // 110 + { NULL, 0 }, // 111 + { clif_parse_SkillUp, -1 }, // 112 + { clif_parse_UseSkillToId, 0 }, // 113 + { NULL, 0 }, // 114 + { NULL, 0 }, // 115 + { clif_parse_UseSkillToPos, 0 }, // 116 + { NULL, 0 }, // 117 + { clif_parse_StopAttack, 0 }, // 118 + { NULL, 0 }, // 119 + { NULL, 0 }, // 11a + { clif_parse_UseSkillMap, 0 }, // 11b + { NULL, 0 }, // 11c + { clif_parse_RequestMemo, 0 }, // 11d + { NULL, 0 }, // 11e + { NULL, 0 }, // 11f + { NULL, 0 }, // 120 + { NULL, 0 }, // 121 + { NULL, 0 }, // 122 + { NULL, 0 }, // 123 + { NULL, 0 }, // 124 + { NULL, 0 }, // 125 + { clif_parse_PutItemToCart, 0 }, // 126 + { clif_parse_GetItemFromCart, 0 }, // 127 + { clif_parse_MoveFromKafraToCart, 0 }, // 128 + { clif_parse_MoveToKafraFromCart, 0 }, // 129 + { clif_parse_RemoveOption, 0 }, // 12a + { NULL, 0 }, // 12b + { NULL, 0 }, // 12c + { NULL, 0 }, // 12d + { NULL, 0 }, // 12e + { NULL, 0 }, // 12f + { NULL, 0 }, // 130 + { NULL, 0 }, // 131 + { NULL, 0 }, // 132 + { NULL, 0 }, // 133 + { NULL, 0 }, // 134 + { NULL, 0 }, // 135 + { NULL, 0 }, // 136 + { NULL, 0 }, // 137 + { NULL, 0 }, // 138 + { NULL, 0 }, // 139 + { NULL, 0 }, // 13a + { NULL, 0 }, // 13b + { NULL, 0 }, // 13c + { NULL, 0 }, // 13d + { NULL, 0 }, // 13e + { clif_parse_GM_Monster_Item, 0 }, // 13f + { clif_parse_MapMove, 0 }, // 140 + { NULL, 0 }, // 141 + { NULL, 0 }, // 142 + { clif_parse_NpcAmountInput, 300 }, // 143 + { NULL, 0 }, // 144 + { NULL, 0 }, // 145 + { clif_parse_NpcCloseClicked, 300 }, // 146 + { NULL, 0 }, // 147 + { NULL, 0 }, // 148 + { clif_parse_GMReqNoChat, 0 }, // 149 + { NULL, 0 }, // 14a + { NULL, 0 }, // 14b + { NULL, 0 }, // 14c + { clif_parse_GuildCheckMaster, 0 }, // 14d + { NULL, 0 }, // 14e + { clif_parse_GuildReqeustInfo, 0 }, // 14f + { NULL, 0 }, // 150 + { clif_parse_GuildRequestEmblem, 0 }, // 151 + { NULL, 0 }, // 152 + { clif_parse_GuildChangeEmblem, 0 }, // 153 + { NULL, 0 }, // 154 + { clif_parse_GuildChangeMemberPosition, 0 }, // 155 + { NULL, 0 }, // 156 + { NULL, 0 }, // 157 + { NULL, 0 }, // 158 + { clif_parse_GuildLeave, 0 }, // 159 + { NULL, 0 }, // 15a + { clif_parse_GuildExplusion, 0 }, // 15b + { NULL, 0 }, // 15c + { clif_parse_GuildBreak, 0 }, // 15d + { NULL, 0 }, // 15e + { NULL, 0 }, // 15f + { NULL, 0 }, // 160 + { clif_parse_GuildChangePositionInfo, 0 }, // 161 + { NULL, 0 }, // 162 + { NULL, 0 }, // 163 + { NULL, 0 }, // 164 + { clif_parse_CreateGuild, 0 }, // 165 + { NULL, 0 }, // 166 + { NULL, 0 }, // 167 + { clif_parse_GuildInvite, 2000 }, // 168 + { NULL, 0 }, // 169 + { NULL, 0 }, // 16a + { clif_parse_GuildReplyInvite, 0 }, // 16b + { NULL, 0 }, // 16c + { NULL, 0 }, // 16d + { clif_parse_GuildChangeNotice, 0 }, // 16e + { NULL, 0 }, // 16f + { clif_parse_GuildRequestAlliance, 0 }, // 170 + { NULL, 0 }, // 171 + { clif_parse_GuildReplyAlliance, 0 }, // 172 + { NULL, 0 }, // 173 + { NULL, 0 }, // 174 + { NULL, 0 }, // 175 + { NULL, 0 }, // 176 + { NULL, 0 }, // 177 + { clif_parse_ItemIdentify, 0 }, // 178 + { NULL, 0 }, // 179 + { clif_parse_UseCard, 0 }, // 17a + { NULL, 0 }, // 17b + { clif_parse_InsertCard, 0 }, // 17c + { NULL, 0 }, // 17d + { clif_parse_GuildMessage, 300 }, // 17e + { NULL, 0 }, // 17f + { clif_parse_GuildOpposition, 0 }, // 180 + { NULL, 0 }, // 181 + { NULL, 0 }, // 182 + { clif_parse_GuildDelAlliance, 0 }, // 183 + { NULL, 0 }, // 184 + { NULL, 0 }, // 185 + { NULL, 0 }, // 186 + { NULL, 0 }, // 187 + { NULL, 0 }, // 188 + { NULL, 0 }, // 189 + { clif_parse_QuitGame, 0 }, // 18a + { NULL, 0 }, // 18b + { NULL, 0 }, // 18c + { NULL, 0 }, // 18d + { NULL, 0 }, // 18e + { NULL, 0 }, // 18f + { clif_parse_UseSkillToPos, 0 }, // 190 + { NULL, 0 }, // 191 + { NULL, 0 }, // 192 + { clif_parse_SolveCharName, 0 }, // 193 + { NULL, 0 }, // 194 + { NULL, 0 }, // 195 + { NULL, 0 }, // 196 + { clif_parse_ResetChar, 0 }, // 197 + { NULL, 0 }, // 198 + { NULL, 0 }, // 199 + { NULL, 0 }, // 19a + { NULL, 0 }, // 19b + { clif_parse_LGMmessage, 0 }, // 19c + { clif_parse_GMHide, 300 }, // 19d + { NULL, 0 }, // 19e + { NULL, 0 }, // 19f + { NULL, 0 }, // 1a0 + { NULL, 0 }, // 1a1 + { NULL, 0 }, // 1a2 + { NULL, 0 }, // 1a3 + { NULL, 0 }, // 1a4 + { NULL, 0 }, // 1a5 + { NULL, 0 }, // 1a6 + { NULL, 0 }, // 1a7 + { NULL, 0 }, // 1a8 + { NULL, 0 }, // 1a9 + { NULL, 0 }, // 1aa + { NULL, 0 }, // 1ab + { NULL, 0 }, // 1ac + { NULL, 0 }, // 1ad + { NULL, 0 }, // 1ae + { clif_parse_ChangeCart, 0 }, // 1af + { NULL, 0 }, // 1b0 + { NULL, 0 }, // 1b1 + { NULL, 0 }, // 1b2 + { NULL, 0 }, // 1b3 + { NULL, 0 }, // 1b4 + { NULL, 0 }, // 1b5 + { NULL, 0 }, // 1b6 + { NULL, 0 }, // 1b7 + { NULL, 0 }, // 1b8 + { NULL, 0 }, // 1b9 + { clif_parse_Shift, 300 }, // 1ba + { clif_parse_Shift, 300 }, // 1bb + { clif_parse_Recall, 300 }, // 1bc + { clif_parse_Recall, 300 }, // 1bd + { NULL, 0 }, // 1be + { NULL, 0 }, // 1bf + { NULL, 0 }, // 1c0 + { NULL, 0 }, // 1c1 + { NULL, 0 }, // 1c2 + { NULL, 0 }, // 1c3 + { NULL, 0 }, // 1c4 + { NULL, 0 }, // 1c5 + { NULL, 0 }, // 1c6 + { NULL, 0 }, // 1c7 + { NULL, 0 }, // 1c8 + { NULL, 0 }, // 1c9 + { NULL, 0 }, // 1ca + { NULL, 0 }, // 1cb + { NULL, 0 }, // 1cc + { NULL, 0 }, // 1cd + { clif_parse_AutoSpell, 0 }, // 1ce + { NULL, 0 }, // 1cf + { NULL, 0 }, // 1d0 + { NULL, 0 }, // 1d1 + { NULL, 0 }, // 1d2 + { NULL, 0 }, // 1d3 + { NULL, 0 }, // 1d4 + { clif_parse_NpcStringInput, 300 }, // 1d5 + { NULL, 0 }, // 1d6 + { NULL, 0 }, // 1d7 + { NULL, 0 }, // 1d8 + { NULL, 0 }, // 1d9 + { NULL, 0 }, // 1da + { NULL, 0 }, // 1db + { NULL, 0 }, // 1dc + { NULL, 0 }, // 1dd + { NULL, 0 }, // 1de + { clif_parse_GMReqNoChatCount, 0 }, // 1df + { NULL, 0 }, // 1e0 + { NULL, 0 }, // 1e1 + { NULL, 0 }, // 1e2 + { NULL, 0 }, // 1e3 + { NULL, 0 }, // 1e4 + { NULL, 0 }, // 1e5 + { NULL, 0 }, // 1e6 + { clif_parse_sn_doridori, 0 }, // 1e7 + { clif_parse_CreateParty2, 1000 }, // 1e8 + { NULL, 0 }, // 1e9 + { NULL, 0 }, // 1ea + { NULL, 0 }, // 1eb + { NULL, 0 }, // 1ec + { clif_parse_sn_explosionspirits, 0 }, // 1ed + { NULL, 0 }, // 1ee + { NULL, 0 }, // 1ef + { NULL, 0 }, // 1f0 + { NULL, 0 }, // 1f1 + { NULL, 0 }, // 1f2 + { NULL, 0 }, // 1f3 + { NULL, 0 }, // 1f4 + { NULL, 0 }, // 1f5 + { NULL, 0 }, // 1f6 + { NULL, 0 }, // 1f7 + { NULL, 0 }, // 1f8 + { NULL, 0 }, // 1f9 + { NULL, 0 }, // 1fa + { NULL, 0 }, // 1fb + { NULL, 0 }, // 1fc + { NULL, 0 }, // 1fd + { NULL, 0 }, // 1fe + { NULL, 0 }, // 1ff + { NULL, 0 }, // 200 + { NULL, 0 }, // 201 + { NULL, 0 }, // 202 + { NULL, 0 }, // 203 + { NULL, 0 }, // 204 + { NULL, 0 }, // 205 + { NULL, 0 }, // 206 + { NULL, 0 }, // 207 + { NULL, 0 }, // 208 + { NULL, 0 }, // 209 + { NULL, 0 }, // 20a + { NULL, 0 }, // 20b + { NULL, 0 }, // 20c + { NULL, 0 }, // 20d + { NULL, 0 }, // 20e + { NULL, 0 }, // 20f + { NULL, 0 }, // 210 + { NULL, 0 }, // 211 + { NULL, 0 }, // 212 + { NULL, 0 }, // 213 + { NULL, 0 }, // 214 + { NULL, 0 }, // 215 + { NULL, 0 }, // 216 + { NULL, 0 }, // 217 + { NULL, 0 }, // 218 + { NULL, 0 }, // 219 + { NULL, 0 }, // 21a + { NULL, 0 }, // 21b + { NULL, 0 }, // 21c + { NULL, 0 }, // 21d + { NULL, 0 }, // 21e + { NULL, 0 }, // 21f +}; +// *INDENT-ON* + +// Checks for packet flooding +int clif_check_packet_flood(int fd, int cmd) +{ + struct map_session_data *sd = (struct map_session_data *)session[fd]->session_data; + unsigned int rate, tick = gettick(); + + // sd will not be set if the client hasn't requested + // WantToConnection yet. Do not apply flood logic to GMs + // as approved bots (GMlvl1) should not have to work around + // flood logic. + if (!sd || pc_isGM(sd) || clif_parse_func_table[cmd].rate == -1) + return 0; + + // Timer has wrapped + if (tick < sd->flood_rates[cmd]) + { + sd->flood_rates[cmd] = tick; + return 0; + } + + // Default rate is 100ms + if ((rate = clif_parse_func_table[cmd].rate) == 0) + rate = 100; + + // ActionRequest - attacks are allowed a faster rate than sit/stand + if (cmd == 0x89) + { + int action_type = RFIFOB (fd, 6); + if (action_type == 0x00 || action_type == 0x07) + rate = 20; + else + rate = 1000; + } + +// Restore this code when mana1.0 is released +#if 0 + // ChangeDir - only apply limit if not walking + if (cmd == 0x9b) + { + // .29 clients spam this packet when walking into a blocked tile + if (RFIFOB(fd, 4) == sd->dir || sd->walktimer != -1) + return 0; + + rate = 500; + } +#endif + + // They are flooding + if (tick < sd->flood_rates[cmd] + rate) + { + time_t now = time(NULL); + + // If it's a nasty flood we log and possibly kick + if (now > sd->packet_flood_reset_due) + { + sd->packet_flood_reset_due = now + battle_config.packet_spam_threshold; + sd->packet_flood_in = 0; + } + + sd->packet_flood_in++; + + if (sd->packet_flood_in >= battle_config.packet_spam_flood) + { + printf("packet flood detected from %s [0x%x]\n", sd->status.name, cmd); + if (battle_config.packet_spam_kick) + { + session[fd]->eof = 1; // Kick + return 1; + } + sd->packet_flood_in = 0; + } + + return 1; + } + + sd->flood_rates[cmd] = tick; + return 0; +} + +#define WARN_MALFORMED_MSG(sd, msg) \ + printf ("clif_validate_chat(): %s (ID %d) sent a malformed" \ + " message: %s.\n", sd->status.name, sd->status.account_id, msg) +/** + * Validate message integrity (inspired by upstream source (eAthena)). + * + * @param sd active session data + * @param type message type: + * 0 for when the sender's name is not included (party chat) + * 1 for when the target's name is included (whisper chat) + * 2 for when the sender's name is given ("sender : text", public/guild chat) + * @param[out] message the message text (pointing within return value, or NULL) + * @param[out] message_len the length of the actual text, excluding NUL + * @return a dynamically allocated copy of the message, or NULL upon failure + */ +static char *clif_validate_chat (struct map_session_data *sd, int type, + char **message, size_t *message_len) +{ + int fd; + unsigned int buf_len; /* Actual message length. */ + unsigned int msg_len; /* Reported message length. */ + unsigned int min_len; /* Minimum message length. */ + size_t name_len; /* Sender's name length. */ + char *buf = NULL; /* Copy of actual message data. */ + char *p = NULL; /* Temporary pointer to message. */ + + *message = NULL; + *message_len = 0; + + nullpo_retr (NULL, sd); + /* + * Don't send chat in the period between the ban and the connection's + * closure. + */ + if (type < 0 || type > 2 || sd->auto_ban_info.in_progress) + return NULL; + + fd = sd->fd; + msg_len = RFIFOW (fd, 2) - 4; + name_len = strlen (sd->status.name); + /* + * At least one character is required in all instances. + * Notes for length checks: + * + * For all types, header (2) + length (2) is considered empty. + * For type 1, the message must be longer than the maximum name length (24) + * to be valid. + * For type 2, the message must be longer than the sender's name length + * plus the length of the separator (" : "). + */ + min_len = (type == 1) ? 24 : (type == 2) ? name_len + 3 : 0; + + /* The player just sent the header (2) and length (2) words. */ + if (!msg_len) + { + WARN_MALFORMED_MSG (sd, "no message sent"); + return NULL; + } + + /* The client sent (or claims to have sent) an empty message. */ + if (msg_len == min_len) + { + WARN_MALFORMED_MSG (sd, "empty message"); + return NULL; + } + + /* The protocol specifies that the target must be 24 bytes long. */ + if (type == 1 && msg_len < min_len) + { + /* Disallow malformed messages. */ + clif_setwaitclose (fd); + WARN_MALFORMED_MSG (sd, "illegal target name"); + return NULL; + } + + p = (char *) (type != 1) ? RFIFOP (fd, 4) : RFIFOP (fd, 28); + buf_len = (type == 1) ? msg_len - min_len: msg_len; + + /* + * The client attempted to exceed the maximum message length. + * + * The conf suggests up to chat_maxline characters, after which the message + * is truncated. But the previous behavior was to drop the message, so + * we'll do that, too. + */ + if (buf_len >= battle_config.chat_maxline) + { + WARN_MALFORMED_MSG (sd, "exceeded maximum message length"); + return NULL; + } + + /* We're leaving an extra eight bytes for public/global chat, 1 for NUL. */ + buf_len += (type == 2) ? 8 + 1 : 1; + + buf = (char *) malloc (buf_len); + memcpy ((type != 2) ? buf : buf + 8, p, + (type != 2) ? buf_len - 1 : buf_len - 8 - 1); + buf[buf_len - 1] = '\0'; + p = (type != 2) ? buf : buf + 8; + + if (type != 2) + { + *message = buf; + /* Don't count the NUL. */ + *message_len = buf_len - 1; + } + else + { + char *pos = NULL; + if (!(pos = strstr(p, " : ")) + || strncmp (p, sd->status.name, name_len) + || pos - p != name_len) + { + free (buf); + /* Disallow malformed/spoofed messages. */ + clif_setwaitclose (fd); + WARN_MALFORMED_MSG (sd, "spoofed name/invalid format"); + return NULL; + } + /* Step beyond the separator. */ + *message = pos + 3; + /* Don't count the sender's name, the extra eight bytes, or the NUL. */ + *message_len = buf_len - min_len - 8 - 1; + } + + return buf; +} + +/*========================================== + * クライアントからのパケット解析 + * socket.cのdo_parsepacketから呼び出される + *------------------------------------------ + */ +static void clif_parse (int fd) +{ + int packet_len = 0, cmd = 0; + struct map_session_data *sd = (struct map_session_data *)session[fd]->session_data; + + if (!sd || (sd && !sd->state.auth)) + { + if (RFIFOREST (fd) < 2) + { // too small a packet disconnect + session[fd]->eof = 1; + } + if (RFIFOW (fd, 0) != 0x72) + { // first packet not auth, disconnect + session[fd]->eof = 1; + } + } + + // 接続が切れてるので後始末 + if (!chrif_isconnect () || session[fd]->eof) + { // char鯖に繋がってない間は接続禁止 (!chrif_isconnect()) + if (sd && sd->state.auth) + { + pc_logout (sd); + clif_quitsave (fd, sd); + if (sd->status.name != NULL) + printf ("Player [%s] has logged off your server.\n", sd->status.name); // Player logout display [Valaris] + else + printf ("Player with account [%d] has logged off your server.\n", sd->bl.id); // Player logout display [Yor] + } + else if (sd) + { // not authentified! (refused by char-server or disconnect before to be authentified) + printf ("Player with account [%d] has logged off your server (not auth account).\n", sd->bl.id); // Player logout display [Yor] + map_deliddb (&sd->bl); // account_id has been included in the DB before auth answer + } + if (fd) + close (fd); + if (fd) + delete_session (fd); + return; + } + + if (RFIFOREST (fd) < 2) + return; // Too small (no packet number) + + cmd = RFIFOW (fd, 0); + + // 管理用パケット処理 + if (cmd >= 30000) + { + switch (cmd) + { + case 0x7530: // Athena情報所得 + 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: // 接続の切断 + session[fd]->eof = 1; + break; + } + return; + } + else if (cmd >= 0x200) + return; + + // パケット長を計算 + packet_len = packet_len_table[cmd]; + if (packet_len == -1) + { + if (RFIFOREST (fd) < 4) + { + return; // Runt packet (variable length without a length sent) + } + packet_len = RFIFOW (fd, 2); + if (packet_len < 4 || packet_len > 32768) + { + session[fd]->eof = 1; + return; // Runt packet (variable out of bounds) + } + } + + if (RFIFOREST (fd) < packet_len) + { + return; // Runt packet (sent legnth is too small) + } + + if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) + { // 切断待ちの場合パケットを処理しない + + } + else if (clif_parse_func_table[cmd].func) + { + if (clif_check_packet_flood(fd, cmd)) + { + // Flood triggered. Skip packet. + RFIFOSKIP(sd->fd, packet_len); + return; + } + + clif_parse_func_table[cmd].func (fd, sd); + } + else + { + // 不明なパケット + if (battle_config.error_log) + { + if (fd) + printf ("\nclif_parse: session #%d, packet 0x%x, lenght %d\n", + fd, cmd, packet_len); +#ifdef DUMP_UNKNOWN_PACKET + { + int i; + FILE *fp; + char packet_txt[256] = "save/packet.txt"; + time_t now; + 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)); + } + 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); + + if ((fp = fopen_ (packet_txt, "a")) == NULL) + { + printf ("clif.c: cant write [%s] !!! data is lost !!!\n", + packet_txt); + return; + } + 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 (gmtime (&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 (gmtime (&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 (gmtime (&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 + } + } + RFIFOSKIP (fd, packet_len); +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_clif (void) +{ + int i; + + set_defaultparse (clif_parse); + for (i = 0; i < 10; i++) + { + if (make_listen_port (map_port)) + break; +#ifdef LCCWIN32 + Sleep (20000); +#else + sleep (20); +#endif + } + if (i == 10) + { + printf ("cant bind game port\n"); + exit (1); + } + + return 0; +} diff --git a/src/map/clif.h b/src/map/clif.h deleted file mode 100644 index a1914e3..0000000 --- a/src/map/clif.h +++ /dev/null @@ -1,285 +0,0 @@ -// $Id: clif.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ -#ifndef _CLIF_H_ -#define _CLIF_H_ - -#include <sys/types.h> - -#ifdef LCCWIN32 -#include <winsock.h> -typedef unsigned int in_addr_t; -#else -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#endif - -#include "map.h" - -void clif_setip (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); -int clif_charselectok (int); -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_spawn_fake_npc_for_player (struct map_session_data *sd, - int fake_npc_id); -int clif_spawnmob (struct mob_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_changemap (struct map_session_data *, char *, int, int); //self -int clif_changemapserver (struct map_session_data *, char *, int, int, int, int); //self -int clif_fixpos (struct block_list *); // area -int clif_fixmobpos (struct mob_data *md); -int clif_fixpcpos (struct map_session_data *sd); -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_changelook_towards (struct block_list *, int, int, struct map_session_data *dst); // area or target -void clif_changelook_accessories (struct block_list *bl, struct map_session_data *dst); // area or target; list gloves, boots etc. -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_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_changeoption (struct block_list *); // area -int clif_useitemack (struct map_session_data *, int, int, int); // self - -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 - -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_sitting (int fd, struct map_session_data *sd); -//void clif_callpartner(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); - -// 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 amount, - 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_npc_class_change (struct block_list *bl, int npc_class, int type); -int clif_mob_class_change (struct mob_data *md, int mob_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_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 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 boolean); -int clif_changemapcell (int m, int x, int y, int cell_type, int type); - -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_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); - -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); - -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_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 party *p, struct map_session_data *sd); -int clif_party_hp (struct party *p, 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); - -// 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); -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); - -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_foreachclient (int (*)(struct map_session_data *, va_list), ...); - -int do_final_clif (void); -int do_init_clif (void); - -#endif diff --git a/src/map/clif.hpp b/src/map/clif.hpp new file mode 100644 index 0000000..f484a18 --- /dev/null +++ b/src/map/clif.hpp @@ -0,0 +1,285 @@ +// $Id: clif.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef CLIF_HPP +#define CLIF_HPP + +#include <sys/types.h> + +#ifdef LCCWIN32 +#include <winsock.h> +typedef unsigned int in_addr_t; +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "map.hpp" + +void clif_setip (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); +int clif_charselectok (int); +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_spawn_fake_npc_for_player (struct map_session_data *sd, + int fake_npc_id); +int clif_spawnmob (struct mob_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_changemap (struct map_session_data *, char *, int, int); //self +int clif_changemapserver (struct map_session_data *, char *, int, int, int, int); //self +int clif_fixpos (struct block_list *); // area +int clif_fixmobpos (struct mob_data *md); +int clif_fixpcpos (struct map_session_data *sd); +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_changelook_towards (struct block_list *, int, int, struct map_session_data *dst); // area or target +void clif_changelook_accessories (struct block_list *bl, struct map_session_data *dst); // area or target; list gloves, boots etc. +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_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_changeoption (struct block_list *); // area +int clif_useitemack (struct map_session_data *, int, int, int); // self + +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 + +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_sitting (int fd, struct map_session_data *sd); +//void clif_callpartner(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); + +// 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 amount, + 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.hpp" +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_npc_class_change (struct block_list *bl, int npc_class, int type); +int clif_mob_class_change (struct mob_data *md, int mob_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_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 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 boolean); +int clif_changemapcell (int m, int x, int y, int cell_type, int type); + +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_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); + +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); + +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_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 party *p, struct map_session_data *sd); +int clif_party_hp (struct party *p, 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); + +// 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); +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); + +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_foreachclient (int (*)(struct map_session_data *, va_list), ...); + +int do_final_clif (void); +int do_init_clif (void); + +#endif diff --git a/src/map/guild.c b/src/map/guild.c deleted file mode 100644 index e4e0ca8..0000000 --- a/src/map/guild.c +++ /dev/null @@ -1,1915 +0,0 @@ -// $Id: guild.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "guild.h" -#include "storage.h" -#include "../common/db.h" -#include "../common/timer.h" -#include "../common/socket.h" -#include "../common/nullpo.h" -#include "battle.h" -#include "npc.h" -#include "pc.h" -#include "map.h" -#include "mob.h" -#include "intif.h" -#include "clif.h" -#include "tmw.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -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_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒) -#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数 - -// ギルドのEXPキャッシュ -struct guild_expcache -{ - int guild_id, account_id, char_id, exp; -}; - -// ギルドスキルdbのアクセサ(今は直打ちで代用) -int guild_skill_get_inf (int id) -{ - return 0; -} - -int guild_skill_get_sp (int id, int lv) -{ - return 0; -} - -int guild_skill_get_range (int id) -{ - return 0; -} - -int guild_skill_get_max (int id) -{ - return (id == 10004) ? 10 : 1; -} - -// ギルドスキルがあるか確認 -int guild_checkskill (struct guild *g, int id) -{ - return g->skill[id - 10000].lv; -} - -void guild_payexp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); -void guild_gvg_eliminate_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); - -static int guild_read_castledb (void) -{ - FILE *fp; - char line[1024]; - int j, ln = 0; - char *str[32], *p; - struct guild_castle *gc; - - if ((fp = fopen_ ("db/castle_db.txt", "r")) == NULL) - { - printf ("can't read db/castle_db.txt\n"); - return -1; - } - - while (fgets (line, 1020, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - memset (str, 0, sizeof (str)); - CREATE (gc, struct guild_castle, 1); - for (j = 0, p = line; j < 6 && p; j++) - { - str[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - - gc->guild_id = 0; // <Agit> Clear Data for Initialize - gc->economy = 0; - gc->defense = 0; - gc->triggerE = 0; - gc->triggerD = 0; - gc->nextTime = 0; - gc->payTime = 0; - gc->createTime = 0; - gc->visibleC = 0; - gc->visibleG0 = 0; - gc->visibleG1 = 0; - gc->visibleG2 = 0; - gc->visibleG3 = 0; - gc->visibleG4 = 0; - gc->visibleG5 = 0; - gc->visibleG6 = 0; - gc->visibleG7 = 0; - gc->Ghp0 = 0; - gc->Ghp1 = 0; - gc->Ghp2 = 0; - gc->Ghp3 = 0; - gc->Ghp4 = 0; - gc->Ghp5 = 0; - gc->Ghp6 = 0; - gc->Ghp7 = 0; // guardian HP [Valaris] - - gc->castle_id = atoi (str[0]); - memcpy (gc->map_name, str[1], 24); - memcpy (gc->castle_name, str[2], 24); - memcpy (gc->castle_event, str[3], 24); - - numdb_insert (castle_db, gc->castle_id, gc); - - //intif_guild_castle_info(gc->castle_id); - - ln++; - } - fclose_ (fp); - printf ("read db/castle_db.txt done (count=%d)\n", ln); - return 0; -} - -// 初期化 -void do_init_guild (void) -{ - guild_db = numdb_init (); - castle_db = numdb_init (); - guild_expcache_db = numdb_init (); - guild_infoevent_db = numdb_init (); - guild_castleinfoevent_db = numdb_init (); - - guild_read_castledb (); - - add_timer_interval (gettick () + GUILD_PAYEXP_INVERVAL, - guild_payexp_timer, 0, 0, GUILD_PAYEXP_INVERVAL); -} - -// 検索 -struct guild *guild_search (int guild_id) -{ - return (struct guild *)numdb_search (guild_db, guild_id); -} - -void guild_searchname_sub (db_key_t key, db_val_t 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 (strcasecmp (g->name, str) == 0) - *dst = g; -} - -// ギルド名検索 -struct guild *guild_searchname (char *str) -{ - struct guild *g = NULL; - numdb_foreach (guild_db, guild_searchname_sub, str, &g); - return g; -} - -struct guild_castle *guild_castle_search (int gcid) -{ - return (struct guild_castle *)numdb_search (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; -} - -// ログイン中のギルドメンバーの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) - 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) - 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 = 0; - m->hair = sd->status.hair; - m->hair_color = sd->status.hair_color; - m->gender = sd->sex; - m->pc_class = sd->status.pc_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, 24); - 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, 0 /*char_id*/); - return 0; -} - -// ギルドのEXPキャッシュをinter鯖にフラッシュする -void guild_payexp_timer_sub (db_key_t key, db_val_t data, va_list ap) -{ - int i, *dellist, *delp, dataid = key.i; - struct guild_expcache *c; - struct guild *g; - - nullpo_retv (ap); - nullpo_retv (c = (struct guild_expcache *) data); - nullpo_retv (dellist = va_arg (ap, int *)); - nullpo_retv (delp = va_arg (ap, int *)); - - if (*delp >= GUILD_PAYEXP_LIST - || (g = guild_search (c->guild_id)) == NULL) - return; - if ((i = guild_getindex (g, c->account_id, 0 /*c->char_id*/)) < 0) - return; - - g->member[i].exp += c->exp; - intif_guild_change_memberinfo (g->guild_id, c->account_id, 0 /*char_id*/, - GMI_EXP, &g->member[i].exp, - sizeof (g->member[i].exp)); - c->exp = 0; - - dellist[(*delp)++] = dataid; - free (c); -} - -void guild_payexp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int dellist[GUILD_PAYEXP_LIST], delp = 0, i; - numdb_foreach (guild_expcache_db, guild_payexp_timer_sub, dellist, &delp); - for (i = 0; i < delp; i++) - numdb_erase (guild_expcache_db, dellist[i]); -// if(battle_config.etc_log) -// printf("guild exp %d charactor's exp flushed !\n",delp); -} - -//------------------------------------------------------------------------ - -/* Process a guild creation request. */ -int guild_create (struct map_session_data *sd, char *name) -{ - char pname[24]; - - nullpo_retr (0, sd); - - strncpy (pname, name, 24); - pname[23] = '\0'; - tmw_TrimStr (pname); - - /* The guild name is empty/invalid. */ - if (!*pname) - clif_guild_created (sd, 2); - - /* Make sure the character isn't already in a guild. */ - if (sd->status.guild_id == 0) - { - /* - * A special item is required to create a guild. This is specified - * in battle_athena.conf as guild_emperium_check. This item will - * be removed from the player's inventory when used to create a - * guild. - */ - if (!battle_config.guild_emperium_check - || pc_search_inventory (sd, - battle_config.guild_emperium_check) >= 0) - { - struct guild_member m; - guild_makemember (&m, sd); - m.position = 0; - intif_guild_create (pname, &m); - } - else - clif_guild_created (sd, 3); - } - else - clif_guild_created (sd, 1); - - return 0; -} - -/* Relay the result of a guild creation request. */ -int guild_created (int account_id, int guild_id) -{ - struct map_session_data *sd = map_id2sd (account_id); - - if (!sd) - return 0; - - /* The guild name is valid and not already taken. */ - if (guild_id > 0) - { - struct guild *g; - sd->status.guild_id = guild_id; - sd->guild_sended = 0; - - if ((g = (struct guild *)numdb_search (guild_db, guild_id)) != NULL) - { - printf ("guild_created(): ID already exists!\n"); - exit (1); - } - - /* The guild was created successfully. */ - clif_guild_created (sd, 0); - - if (battle_config.guild_emperium_check) - pc_delitem (sd, - pc_search_inventory (sd, - battle_config. - guild_emperium_check), 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); - - CREATE (ev, struct eventlist, 1); - memcpy (ev->name, event, sizeof (ev->name)); - ev->next = - (struct eventlist *) numdb_search (guild_infoevent_db, guild_id); - numdb_insert (guild_infoevent_db, guild_id, ev); - return guild_request_info (guild_id); -} - -// 所属キャラの確認 -int guild_check_member (const struct guild *g) -{ - int i; - struct map_session_data *sd; - - nullpo_retr (0, g); - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) - { - 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) - f = 0; - } - if (f) - { - sd->status.guild_id = 0; - sd->guild_sended = 0; - sd->guild_emblem_id = 0; - if (battle_config.error_log) - printf ("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; - 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) - { - 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 = (struct guild *)numdb_search (guild_db, sg->guild_id)) == NULL) - { - CREATE (g, struct guild, 1); - numdb_insert (guild_db, sg->guild_id, g); - before = *sg; - - // 最初のロードなのでユーザーのチェックを行う - guild_check_member (sg); - } - 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); - g->member[i].sd = (sd != NULL && - sd->status.guild_id == - g->guild_id) ? 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->guild_sended == 0) - { // 未送信なら所属情報も送る - clif_guild_belonginfo (sd, g); - clif_guild_notice (sd, g); - sd->guild_emblem_id = g->emblem_id; - sd->guild_sended = 1; - } - } - - // イベントの発生 - if ((ev = (struct eventlist *)numdb_search (guild_infoevent_db, sg->guild_id)) != NULL) - { - numdb_erase (guild_infoevent_db, sg->guild_id); - for (; ev; ev2 = ev->next, free (ev), ev = ev2) - { - npc_event_do (ev->name); - } - } - - 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)); - - 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; - if (tsd == NULL) - return 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) && flag == 0) - { - // キャラ側に登録できなかったため脱退要求を出す - if (battle_config.error_log) - printf ("guild: member added error %d is not online\n", - account_id); - intif_guild_leave (guild_id, account_id, 0 /*char_id*/, 0, "**登録失敗**"); - return 0; - } - sd->guild_invite = 0; - sd->guild_invite_account = 0; - - sd2 = map_id2sd (sd->guild_invite_account); - - if (flag == 1) - { // 失敗 - if (sd2 != NULL) - clif_guild_inviteack (sd2, 3); - return 0; - } - - // 成功 - sd->guild_sended = 0; - sd->status.guild_id = guild_id; - - if (sd2 != NULL) - clif_guild_inviteack (sd2, 2); - - // いちおう競合確認 - guild_check_conflict (sd); - - 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.guild_id != guild_id) - return 0; - - for (i = 0; i < g->max_member; i++) - { // 所属しているか - if (g->member[i].account_id == sd->status.account_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) - { - intif_guild_leave (g->guild_id, account_id, 0 /*char_id*/, 1, mes); - 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_id2sd (account_id); - struct guild *g = guild_search (guild_id); - int i; - - if (g != NULL) - { - int i; - for (i = 0; i < g->max_member; i++) - if (g->member[i].account_id == account_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); - } - g->member[i].account_id = 0; - g->member[i].sd = NULL; - } - } - if (sd != NULL && sd->status.guild_id == guild_id) - { - if (sd->state.storage_flag == 2) //Close the guild storage. - storage_guild_storageclose (sd); - sd->status.guild_id = 0; - sd->guild_emblem_id = 0; - sd->guild_sended = 0; - } - - // メンバーリストを全員に再通知 - for (i = 0; i < g->max_member; i++) - { - if (g->member[i].sd != NULL) - clif_guild_memberlist (g->member[i].sd); - } - - 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, 0 /*char_id*/, - online, sd->status.base_level, - sd->status.pc_class); - - if (!online) - { // ログアウトするならsdをクリアして終了 - int i = - guild_getindex (g, sd->status.account_id, 0 /*char_id*/); - if (i >= 0) - g->member[i].sd = NULL; - return 0; - } - - if (sd->guild_sended != 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->guild_sended = 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 pc_class) -{ - int i, alv, c, idx = 0, 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) - { - oldonline = m->online; - m->online = online; - m->lv = lv; - m->pc_class = pc_class; - idx = i; - } - if (m->account_id > 0) - { - alv += m->lv; - c++; - } - if (m->online) - om++; - } - if (idx == g->max_member) - { - if (battle_config.error_log) - printf ("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); - g->member[i].sd = (sd != NULL && - sd->status.guild_id == guild_id) ? 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); - 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, 0 /*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); - 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, 24); - 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); - if (g == NULL) - return 0; - memcpy (&g->position[idx], p, sizeof (struct guild_position)); - clif_guild_positionchanged (g, idx); - return 0; -} - -// ギルド告知変更 -int guild_change_notice (struct map_session_data *sd, int guild_id, - const char *mes1, const char *mes2) -{ - struct guild *g; - int ps; - - nullpo_retr (0, sd); - - g = guild_search (sd->status.guild_id); - - if (g == NULL) - return 0; - - if ((ps = guild_getposition (sd, g)) < 0 - || !(g->position[ps].mode & 0x0010)) - return 0; - - 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; - int ps; - - nullpo_retr (0, sd); - - g = guild_search (sd->status.guild_id); - - if (g == NULL) - return 0; - - if ((ps = guild_getposition (sd, g)) < 0 - || !(g->position[ps].mode & 0x0010)) - 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; -} - -// ギルドのEXP上納 -int guild_payexp (struct map_session_data *sd, int exp) -{ - struct guild *g; - struct guild_expcache *c; - int per, exp2; - - nullpo_retr (0, sd); - - if (sd->status.guild_id == 0 - || (g = guild_search (sd->status.guild_id)) == NULL) - return 0; - if ((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; - - if ((c = (struct guild_expcache *)numdb_search (guild_expcache_db, sd->status.account_id /*char_id*/)) == NULL) - { - CREATE (c, struct guild_expcache, 1); - c->guild_id = sd->status.guild_id; - c->account_id = sd->status.account_id; - c->char_id = 0; - c->exp = exp2; - numdb_insert (guild_expcache_db, c->account_id /*char_id*/, c); - } - else - { - c->exp += exp2; - } - return exp2; -} - -// スキルポイント割り振り -int guild_skillup (struct map_session_data *sd, int skill_num) -{ - struct guild *g; - int idx; - - nullpo_retr (0, sd); - - 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 && - g->skill[(idx = skill_num - 10000)].id != 0 && - g->skill[idx].lv < guild_skill_get_max (skill_num)) - { - intif_guild_skillup (g->guild_id, skill_num, sd->status.account_id); - } - 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 - 10000].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; -} - -// ギルド同盟要求 -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, ps; - - 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 ((ps = guild_getposition (sd, g[0])) < 0 - || !(g[0]->position[ps].mode & 0x0010)) - 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; // 同盟数再確認 - if ((g = guild_search (sd->status.guild_id)) == NULL || - guild_get_alliance_count (g, 0) > 3) - { - clif_guild_allianceack (sd, 4); - clif_guild_allianceack (tsd, 3); - return 0; - } - if ((g = guild_search (tsd->status.guild_id)) == NULL || - guild_get_alliance_count (g, 0) > 3) - { - clif_guild_allianceack (sd, 3); - clif_guild_allianceack (tsd, 4); - return 0; - } - - // 敵対関係なら敵対を止める - if ((g = guild_search (sd->status.guild_id)) == NULL) - 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); - } - if ((g = guild_search (tsd->status.guild_id)) == NULL) - return 0; - for (i = 0; i < MAX_GUILDALLIANCE; i++) - { - if (g->alliance[i].guild_id == sd->status.guild_id && - g->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] - - struct guild *g; - int ps; - - nullpo_retr (0, sd); - - g = guild_search (sd->status.guild_id); - - if (g == NULL) - return 0; - - if ((ps = guild_getposition (sd, g)) < 0 - || !(g->position[ps].mode & 0x0010)) - return 0; - - 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 account_id /*char_id*/) -{ - struct map_session_data *tsd = map_id2sd (account_id /*char_id*/); - struct guild *g; - int i, ps; - - nullpo_retr (0, sd); - - g = guild_search (sd->status.guild_id); - if (g == NULL || tsd == NULL) - return 0; - - if ((ps = guild_getposition (sd, g)) < 0 - || !(g->position[ps].mode & 0x0010)) - 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] = { guild_id1, guild_id2 }; - const char *guild_name[2] = { name1, name2 }; - struct map_session_data *sd[2] = - { map_id2sd (account_id1), map_id2sd (account_id2) }; - int j, i; - - 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], - 24); - 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; -} - -// ギルド解散通知用 -void guild_broken_sub (db_key_t key, db_val_t 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_retv (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); - g->alliance[i].guild_id = 0; - } - } -} - -// ギルド解散通知 -int guild_broken (int guild_id, int flag) -{ - struct guild *g = guild_search (guild_id); - struct map_session_data *sd; - int i; - if (flag != 0 || g == NULL) - return 0; - - 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->guild_sended = 0; - clif_guild_broken (g->member[i].sd, 0); - } - } - - numdb_foreach (guild_db, guild_broken_sub, guild_id); - numdb_erase (guild_db, guild_id); - guild_storage_delete (guild_id); - free (g); - return 0; -} - -// ギルド解散 -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)) - 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; - - CREATE (ev, struct eventlist, 1); - memcpy (ev->name, name, sizeof (ev->name)); - ev->next = (struct eventlist *)numdb_search (guild_castleinfoevent_db, code); - numdb_insert (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; - 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: - gc->visibleG0 = value; - break; - case 11: - gc->visibleG1 = value; - break; - case 12: - gc->visibleG2 = value; - break; - case 13: - gc->visibleG3 = value; - break; - case 14: - gc->visibleG4 = value; - break; - case 15: - gc->visibleG5 = value; - break; - case 16: - gc->visibleG6 = value; - break; - case 17: - gc->visibleG7 = value; - break; - case 18: - gc->Ghp0 = value; - break; // guardian HP [Valaris] - case 19: - gc->Ghp1 = value; - break; - case 20: - gc->Ghp2 = value; - break; - case 21: - gc->Ghp3 = value; - break; - case 22: - gc->Ghp4 = value; - break; - case 23: - gc->Ghp5 = value; - break; - case 24: - gc->Ghp6 = value; - break; - case 25: - gc->Ghp7 = value; - break; // end additions [Valaris] - default: - printf ("guild_castledataloadack ERROR!! (Not found index=%d)\n", - index); - return 0; - } - if ((ev = (struct eventlist *)numdb_search (guild_castleinfoevent_db, code)) != NULL) - { - numdb_erase (guild_castleinfoevent_db, code); - for (; ev; ev2 = ev->next, free (ev), ev = ev2) - { - npc_event_do (ev->name); - } - } - return 1; -} - -// ギルド城データ変更要求 -int guild_castledatasave (int castle_id, int index, int value) -{ - 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: - gc->visibleG0 = value; - break; - case 11: - gc->visibleG1 = value; - break; - case 12: - gc->visibleG2 = value; - break; - case 13: - gc->visibleG3 = value; - break; - case 14: - gc->visibleG4 = value; - break; - case 15: - gc->visibleG5 = value; - break; - case 16: - gc->visibleG6 = value; - break; - case 17: - gc->visibleG7 = value; - break; - case 18: - gc->Ghp0 = value; - break; // guardian HP [Valaris] - case 19: - gc->Ghp1 = value; - break; - case 20: - gc->Ghp2 = value; - break; - case 21: - gc->Ghp3 = value; - break; - case 22: - gc->Ghp4 = value; - break; - case 23: - gc->Ghp5 = value; - break; - case 24: - gc->Ghp6 = value; - break; - case 25: - gc->Ghp7 = value; - break; // end additions [Valaris] - default: - printf ("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); - - // イベント付きで要求するデータ位置を探す(最後の占拠データ) - 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) - { - printf ("guild_castlealldataload ??\n"); - 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) - npc_event_doall ("OnAgitInit"); - return 0; -} - -int guild_agit_start (void) -{ // Run All NPC_Event[OnAgitStart] - int c = npc_event_doall ("OnAgitStart"); - printf ("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n", c); - return 0; -} - -int guild_agit_end (void) -{ // Run All NPC_Event[OnAgitEnd] - int c = npc_event_doall ("OnAgitEnd"); - printf ("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n", c); - return 0; -} - -void guild_gvg_eliminate_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ // Run One NPC_Event[OnAgitEliminate] - size_t len = strlen ((const char *) data); - char *evname; - CREATE (evname, char, len + 4); - int c = 0; - - if (!agit_flag) - return; // Agit already End - memcpy (evname, (const char *) data, len - 5); - strcpy (evname + len - 5, "Eliminate"); - c = npc_event_do (evname); - printf ("NPC_Event:[%s] Run (%d) Events.\n", evname, c); -} - -int guild_agit_break (struct mob_data *md) -{ // Run One NPC_Event[OnAgitBreak] - char *evname; - - nullpo_retr (0, md); - - CREATE (evname, char, strlen (md->npc_event) + 1); - - 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; -} - -static void guild_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -static void castle_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -static void guild_expcache_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -static void guild_infoevent_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -void do_final_guild (void) -{ - if (guild_db) - numdb_final (guild_db, guild_db_final); - if (castle_db) - numdb_final (castle_db, castle_db_final); - if (guild_expcache_db) - numdb_final (guild_expcache_db, guild_expcache_db_final); - if (guild_infoevent_db) - numdb_final (guild_infoevent_db, guild_infoevent_db_final); - if (guild_castleinfoevent_db) - numdb_final (guild_castleinfoevent_db, guild_infoevent_db_final); -} diff --git a/src/map/guild.cpp b/src/map/guild.cpp new file mode 100644 index 0000000..c001f78 --- /dev/null +++ b/src/map/guild.cpp @@ -0,0 +1,1915 @@ +// $Id: guild.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "guild.hpp" +#include "storage.hpp" +#include "../common/db.hpp" +#include "../common/timer.hpp" +#include "../common/socket.hpp" +#include "../common/nullpo.hpp" +#include "battle.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "intif.hpp" +#include "clif.hpp" +#include "tmw.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +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_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒) +#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数 + +// ギルドのEXPキャッシュ +struct guild_expcache +{ + int guild_id, account_id, char_id, exp; +}; + +// ギルドスキルdbのアクセサ(今は直打ちで代用) +int guild_skill_get_inf (int id) +{ + return 0; +} + +int guild_skill_get_sp (int id, int lv) +{ + return 0; +} + +int guild_skill_get_range (int id) +{ + return 0; +} + +int guild_skill_get_max (int id) +{ + return (id == 10004) ? 10 : 1; +} + +// ギルドスキルがあるか確認 +int guild_checkskill (struct guild *g, int id) +{ + return g->skill[id - 10000].lv; +} + +void guild_payexp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); +void guild_gvg_eliminate_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); + +static int guild_read_castledb (void) +{ + FILE *fp; + char line[1024]; + int j, ln = 0; + char *str[32], *p; + struct guild_castle *gc; + + if ((fp = fopen_ ("db/castle_db.txt", "r")) == NULL) + { + printf ("can't read db/castle_db.txt\n"); + return -1; + } + + while (fgets (line, 1020, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + memset (str, 0, sizeof (str)); + CREATE (gc, struct guild_castle, 1); + for (j = 0, p = line; j < 6 && p; j++) + { + str[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + + gc->guild_id = 0; // <Agit> Clear Data for Initialize + gc->economy = 0; + gc->defense = 0; + gc->triggerE = 0; + gc->triggerD = 0; + gc->nextTime = 0; + gc->payTime = 0; + gc->createTime = 0; + gc->visibleC = 0; + gc->visibleG0 = 0; + gc->visibleG1 = 0; + gc->visibleG2 = 0; + gc->visibleG3 = 0; + gc->visibleG4 = 0; + gc->visibleG5 = 0; + gc->visibleG6 = 0; + gc->visibleG7 = 0; + gc->Ghp0 = 0; + gc->Ghp1 = 0; + gc->Ghp2 = 0; + gc->Ghp3 = 0; + gc->Ghp4 = 0; + gc->Ghp5 = 0; + gc->Ghp6 = 0; + gc->Ghp7 = 0; // guardian HP [Valaris] + + gc->castle_id = atoi (str[0]); + memcpy (gc->map_name, str[1], 24); + memcpy (gc->castle_name, str[2], 24); + memcpy (gc->castle_event, str[3], 24); + + numdb_insert (castle_db, gc->castle_id, gc); + + //intif_guild_castle_info(gc->castle_id); + + ln++; + } + fclose_ (fp); + printf ("read db/castle_db.txt done (count=%d)\n", ln); + return 0; +} + +// 初期化 +void do_init_guild (void) +{ + guild_db = numdb_init (); + castle_db = numdb_init (); + guild_expcache_db = numdb_init (); + guild_infoevent_db = numdb_init (); + guild_castleinfoevent_db = numdb_init (); + + guild_read_castledb (); + + add_timer_interval (gettick () + GUILD_PAYEXP_INVERVAL, + guild_payexp_timer, 0, 0, GUILD_PAYEXP_INVERVAL); +} + +// 検索 +struct guild *guild_search (int guild_id) +{ + return (struct guild *)numdb_search (guild_db, guild_id); +} + +void guild_searchname_sub (db_key_t key, db_val_t 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 (strcasecmp (g->name, str) == 0) + *dst = g; +} + +// ギルド名検索 +struct guild *guild_searchname (char *str) +{ + struct guild *g = NULL; + numdb_foreach (guild_db, guild_searchname_sub, str, &g); + return g; +} + +struct guild_castle *guild_castle_search (int gcid) +{ + return (struct guild_castle *)numdb_search (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; +} + +// ログイン中のギルドメンバーの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) + 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) + 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 = 0; + m->hair = sd->status.hair; + m->hair_color = sd->status.hair_color; + m->gender = sd->sex; + m->pc_class = sd->status.pc_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, 24); + 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, 0 /*char_id*/); + return 0; +} + +// ギルドのEXPキャッシュをinter鯖にフラッシュする +void guild_payexp_timer_sub (db_key_t key, db_val_t data, va_list ap) +{ + int i, *dellist, *delp, dataid = key.i; + struct guild_expcache *c; + struct guild *g; + + nullpo_retv (ap); + nullpo_retv (c = (struct guild_expcache *) data); + nullpo_retv (dellist = va_arg (ap, int *)); + nullpo_retv (delp = va_arg (ap, int *)); + + if (*delp >= GUILD_PAYEXP_LIST + || (g = guild_search (c->guild_id)) == NULL) + return; + if ((i = guild_getindex (g, c->account_id, 0 /*c->char_id*/)) < 0) + return; + + g->member[i].exp += c->exp; + intif_guild_change_memberinfo (g->guild_id, c->account_id, 0 /*char_id*/, + GMI_EXP, &g->member[i].exp, + sizeof (g->member[i].exp)); + c->exp = 0; + + dellist[(*delp)++] = dataid; + free (c); +} + +void guild_payexp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int dellist[GUILD_PAYEXP_LIST], delp = 0, i; + numdb_foreach (guild_expcache_db, guild_payexp_timer_sub, dellist, &delp); + for (i = 0; i < delp; i++) + numdb_erase (guild_expcache_db, dellist[i]); +// if(battle_config.etc_log) +// printf("guild exp %d charactor's exp flushed !\n",delp); +} + +//------------------------------------------------------------------------ + +/* Process a guild creation request. */ +int guild_create (struct map_session_data *sd, char *name) +{ + char pname[24]; + + nullpo_retr (0, sd); + + strncpy (pname, name, 24); + pname[23] = '\0'; + tmw_TrimStr (pname); + + /* The guild name is empty/invalid. */ + if (!*pname) + clif_guild_created (sd, 2); + + /* Make sure the character isn't already in a guild. */ + if (sd->status.guild_id == 0) + { + /* + * A special item is required to create a guild. This is specified + * in battle_athena.conf as guild_emperium_check. This item will + * be removed from the player's inventory when used to create a + * guild. + */ + if (!battle_config.guild_emperium_check + || pc_search_inventory (sd, + battle_config.guild_emperium_check) >= 0) + { + struct guild_member m; + guild_makemember (&m, sd); + m.position = 0; + intif_guild_create (pname, &m); + } + else + clif_guild_created (sd, 3); + } + else + clif_guild_created (sd, 1); + + return 0; +} + +/* Relay the result of a guild creation request. */ +int guild_created (int account_id, int guild_id) +{ + struct map_session_data *sd = map_id2sd (account_id); + + if (!sd) + return 0; + + /* The guild name is valid and not already taken. */ + if (guild_id > 0) + { + struct guild *g; + sd->status.guild_id = guild_id; + sd->guild_sended = 0; + + if ((g = (struct guild *)numdb_search (guild_db, guild_id)) != NULL) + { + printf ("guild_created(): ID already exists!\n"); + exit (1); + } + + /* The guild was created successfully. */ + clif_guild_created (sd, 0); + + if (battle_config.guild_emperium_check) + pc_delitem (sd, + pc_search_inventory (sd, + battle_config. + guild_emperium_check), 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); + + CREATE (ev, struct eventlist, 1); + memcpy (ev->name, event, sizeof (ev->name)); + ev->next = + (struct eventlist *) numdb_search (guild_infoevent_db, guild_id); + numdb_insert (guild_infoevent_db, guild_id, ev); + return guild_request_info (guild_id); +} + +// 所属キャラの確認 +int guild_check_member (const struct guild *g) +{ + int i; + struct map_session_data *sd; + + nullpo_retr (0, g); + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) + { + 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) + f = 0; + } + if (f) + { + sd->status.guild_id = 0; + sd->guild_sended = 0; + sd->guild_emblem_id = 0; + if (battle_config.error_log) + printf ("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; + 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) + { + 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 = (struct guild *)numdb_search (guild_db, sg->guild_id)) == NULL) + { + CREATE (g, struct guild, 1); + numdb_insert (guild_db, sg->guild_id, g); + before = *sg; + + // 最初のロードなのでユーザーのチェックを行う + guild_check_member (sg); + } + 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); + g->member[i].sd = (sd != NULL && + sd->status.guild_id == + g->guild_id) ? 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->guild_sended == 0) + { // 未送信なら所属情報も送る + clif_guild_belonginfo (sd, g); + clif_guild_notice (sd, g); + sd->guild_emblem_id = g->emblem_id; + sd->guild_sended = 1; + } + } + + // イベントの発生 + if ((ev = (struct eventlist *)numdb_search (guild_infoevent_db, sg->guild_id)) != NULL) + { + numdb_erase (guild_infoevent_db, sg->guild_id); + for (; ev; ev2 = ev->next, free (ev), ev = ev2) + { + npc_event_do (ev->name); + } + } + + 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)); + + 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; + if (tsd == NULL) + return 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) && flag == 0) + { + // キャラ側に登録できなかったため脱退要求を出す + if (battle_config.error_log) + printf ("guild: member added error %d is not online\n", + account_id); + intif_guild_leave (guild_id, account_id, 0 /*char_id*/, 0, "**登録失敗**"); + return 0; + } + sd->guild_invite = 0; + sd->guild_invite_account = 0; + + sd2 = map_id2sd (sd->guild_invite_account); + + if (flag == 1) + { // 失敗 + if (sd2 != NULL) + clif_guild_inviteack (sd2, 3); + return 0; + } + + // 成功 + sd->guild_sended = 0; + sd->status.guild_id = guild_id; + + if (sd2 != NULL) + clif_guild_inviteack (sd2, 2); + + // いちおう競合確認 + guild_check_conflict (sd); + + 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.guild_id != guild_id) + return 0; + + for (i = 0; i < g->max_member; i++) + { // 所属しているか + if (g->member[i].account_id == sd->status.account_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) + { + intif_guild_leave (g->guild_id, account_id, 0 /*char_id*/, 1, mes); + 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_id2sd (account_id); + struct guild *g = guild_search (guild_id); + int i; + + if (g != NULL) + { + int i; + for (i = 0; i < g->max_member; i++) + if (g->member[i].account_id == account_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); + } + g->member[i].account_id = 0; + g->member[i].sd = NULL; + } + } + if (sd != NULL && sd->status.guild_id == guild_id) + { + if (sd->state.storage_flag == 2) //Close the guild storage. + storage_guild_storageclose (sd); + sd->status.guild_id = 0; + sd->guild_emblem_id = 0; + sd->guild_sended = 0; + } + + // メンバーリストを全員に再通知 + for (i = 0; i < g->max_member; i++) + { + if (g->member[i].sd != NULL) + clif_guild_memberlist (g->member[i].sd); + } + + 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, 0 /*char_id*/, + online, sd->status.base_level, + sd->status.pc_class); + + if (!online) + { // ログアウトするならsdをクリアして終了 + int i = + guild_getindex (g, sd->status.account_id, 0 /*char_id*/); + if (i >= 0) + g->member[i].sd = NULL; + return 0; + } + + if (sd->guild_sended != 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->guild_sended = 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 pc_class) +{ + int i, alv, c, idx = 0, 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) + { + oldonline = m->online; + m->online = online; + m->lv = lv; + m->pc_class = pc_class; + idx = i; + } + if (m->account_id > 0) + { + alv += m->lv; + c++; + } + if (m->online) + om++; + } + if (idx == g->max_member) + { + if (battle_config.error_log) + printf ("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); + g->member[i].sd = (sd != NULL && + sd->status.guild_id == guild_id) ? 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); + 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, 0 /*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); + 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, 24); + 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); + if (g == NULL) + return 0; + memcpy (&g->position[idx], p, sizeof (struct guild_position)); + clif_guild_positionchanged (g, idx); + return 0; +} + +// ギルド告知変更 +int guild_change_notice (struct map_session_data *sd, int guild_id, + const char *mes1, const char *mes2) +{ + struct guild *g; + int ps; + + nullpo_retr (0, sd); + + g = guild_search (sd->status.guild_id); + + if (g == NULL) + return 0; + + if ((ps = guild_getposition (sd, g)) < 0 + || !(g->position[ps].mode & 0x0010)) + return 0; + + 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; + int ps; + + nullpo_retr (0, sd); + + g = guild_search (sd->status.guild_id); + + if (g == NULL) + return 0; + + if ((ps = guild_getposition (sd, g)) < 0 + || !(g->position[ps].mode & 0x0010)) + 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; +} + +// ギルドのEXP上納 +int guild_payexp (struct map_session_data *sd, int exp) +{ + struct guild *g; + struct guild_expcache *c; + int per, exp2; + + nullpo_retr (0, sd); + + if (sd->status.guild_id == 0 + || (g = guild_search (sd->status.guild_id)) == NULL) + return 0; + if ((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; + + if ((c = (struct guild_expcache *)numdb_search (guild_expcache_db, sd->status.account_id /*char_id*/)) == NULL) + { + CREATE (c, struct guild_expcache, 1); + c->guild_id = sd->status.guild_id; + c->account_id = sd->status.account_id; + c->char_id = 0; + c->exp = exp2; + numdb_insert (guild_expcache_db, c->account_id /*char_id*/, c); + } + else + { + c->exp += exp2; + } + return exp2; +} + +// スキルポイント割り振り +int guild_skillup (struct map_session_data *sd, int skill_num) +{ + struct guild *g; + int idx; + + nullpo_retr (0, sd); + + 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 && + g->skill[(idx = skill_num - 10000)].id != 0 && + g->skill[idx].lv < guild_skill_get_max (skill_num)) + { + intif_guild_skillup (g->guild_id, skill_num, sd->status.account_id); + } + 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 - 10000].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; +} + +// ギルド同盟要求 +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, ps; + + 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 ((ps = guild_getposition (sd, g[0])) < 0 + || !(g[0]->position[ps].mode & 0x0010)) + 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; // 同盟数再確認 + if ((g = guild_search (sd->status.guild_id)) == NULL || + guild_get_alliance_count (g, 0) > 3) + { + clif_guild_allianceack (sd, 4); + clif_guild_allianceack (tsd, 3); + return 0; + } + if ((g = guild_search (tsd->status.guild_id)) == NULL || + guild_get_alliance_count (g, 0) > 3) + { + clif_guild_allianceack (sd, 3); + clif_guild_allianceack (tsd, 4); + return 0; + } + + // 敵対関係なら敵対を止める + if ((g = guild_search (sd->status.guild_id)) == NULL) + 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); + } + if ((g = guild_search (tsd->status.guild_id)) == NULL) + return 0; + for (i = 0; i < MAX_GUILDALLIANCE; i++) + { + if (g->alliance[i].guild_id == sd->status.guild_id && + g->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] + + struct guild *g; + int ps; + + nullpo_retr (0, sd); + + g = guild_search (sd->status.guild_id); + + if (g == NULL) + return 0; + + if ((ps = guild_getposition (sd, g)) < 0 + || !(g->position[ps].mode & 0x0010)) + return 0; + + 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 account_id /*char_id*/) +{ + struct map_session_data *tsd = map_id2sd (account_id /*char_id*/); + struct guild *g; + int i, ps; + + nullpo_retr (0, sd); + + g = guild_search (sd->status.guild_id); + if (g == NULL || tsd == NULL) + return 0; + + if ((ps = guild_getposition (sd, g)) < 0 + || !(g->position[ps].mode & 0x0010)) + 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] = { guild_id1, guild_id2 }; + const char *guild_name[2] = { name1, name2 }; + struct map_session_data *sd[2] = + { map_id2sd (account_id1), map_id2sd (account_id2) }; + int j, i; + + 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], + 24); + 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; +} + +// ギルド解散通知用 +void guild_broken_sub (db_key_t key, db_val_t 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_retv (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); + g->alliance[i].guild_id = 0; + } + } +} + +// ギルド解散通知 +int guild_broken (int guild_id, int flag) +{ + struct guild *g = guild_search (guild_id); + struct map_session_data *sd; + int i; + if (flag != 0 || g == NULL) + return 0; + + 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->guild_sended = 0; + clif_guild_broken (g->member[i].sd, 0); + } + } + + numdb_foreach (guild_db, guild_broken_sub, guild_id); + numdb_erase (guild_db, guild_id); + guild_storage_delete (guild_id); + free (g); + return 0; +} + +// ギルド解散 +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)) + 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; + + CREATE (ev, struct eventlist, 1); + memcpy (ev->name, name, sizeof (ev->name)); + ev->next = (struct eventlist *)numdb_search (guild_castleinfoevent_db, code); + numdb_insert (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; + 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: + gc->visibleG0 = value; + break; + case 11: + gc->visibleG1 = value; + break; + case 12: + gc->visibleG2 = value; + break; + case 13: + gc->visibleG3 = value; + break; + case 14: + gc->visibleG4 = value; + break; + case 15: + gc->visibleG5 = value; + break; + case 16: + gc->visibleG6 = value; + break; + case 17: + gc->visibleG7 = value; + break; + case 18: + gc->Ghp0 = value; + break; // guardian HP [Valaris] + case 19: + gc->Ghp1 = value; + break; + case 20: + gc->Ghp2 = value; + break; + case 21: + gc->Ghp3 = value; + break; + case 22: + gc->Ghp4 = value; + break; + case 23: + gc->Ghp5 = value; + break; + case 24: + gc->Ghp6 = value; + break; + case 25: + gc->Ghp7 = value; + break; // end additions [Valaris] + default: + printf ("guild_castledataloadack ERROR!! (Not found index=%d)\n", + index); + return 0; + } + if ((ev = (struct eventlist *)numdb_search (guild_castleinfoevent_db, code)) != NULL) + { + numdb_erase (guild_castleinfoevent_db, code); + for (; ev; ev2 = ev->next, free (ev), ev = ev2) + { + npc_event_do (ev->name); + } + } + return 1; +} + +// ギルド城データ変更要求 +int guild_castledatasave (int castle_id, int index, int value) +{ + 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: + gc->visibleG0 = value; + break; + case 11: + gc->visibleG1 = value; + break; + case 12: + gc->visibleG2 = value; + break; + case 13: + gc->visibleG3 = value; + break; + case 14: + gc->visibleG4 = value; + break; + case 15: + gc->visibleG5 = value; + break; + case 16: + gc->visibleG6 = value; + break; + case 17: + gc->visibleG7 = value; + break; + case 18: + gc->Ghp0 = value; + break; // guardian HP [Valaris] + case 19: + gc->Ghp1 = value; + break; + case 20: + gc->Ghp2 = value; + break; + case 21: + gc->Ghp3 = value; + break; + case 22: + gc->Ghp4 = value; + break; + case 23: + gc->Ghp5 = value; + break; + case 24: + gc->Ghp6 = value; + break; + case 25: + gc->Ghp7 = value; + break; // end additions [Valaris] + default: + printf ("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); + + // イベント付きで要求するデータ位置を探す(最後の占拠データ) + 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) + { + printf ("guild_castlealldataload ??\n"); + 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) + npc_event_doall ("OnAgitInit"); + return 0; +} + +int guild_agit_start (void) +{ // Run All NPC_Event[OnAgitStart] + int c = npc_event_doall ("OnAgitStart"); + printf ("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n", c); + return 0; +} + +int guild_agit_end (void) +{ // Run All NPC_Event[OnAgitEnd] + int c = npc_event_doall ("OnAgitEnd"); + printf ("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n", c); + return 0; +} + +void guild_gvg_eliminate_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ // Run One NPC_Event[OnAgitEliminate] + size_t len = strlen ((const char *) data); + char *evname; + CREATE (evname, char, len + 4); + int c = 0; + + if (!agit_flag) + return; // Agit already End + memcpy (evname, (const char *) data, len - 5); + strcpy (evname + len - 5, "Eliminate"); + c = npc_event_do (evname); + printf ("NPC_Event:[%s] Run (%d) Events.\n", evname, c); +} + +int guild_agit_break (struct mob_data *md) +{ // Run One NPC_Event[OnAgitBreak] + char *evname; + + nullpo_retr (0, md); + + CREATE (evname, char, strlen (md->npc_event) + 1); + + 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; +} + +static void guild_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +static void castle_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +static void guild_expcache_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +static void guild_infoevent_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +void do_final_guild (void) +{ + if (guild_db) + numdb_final (guild_db, guild_db_final); + if (castle_db) + numdb_final (castle_db, castle_db_final); + if (guild_expcache_db) + numdb_final (guild_expcache_db, guild_expcache_db_final); + if (guild_infoevent_db) + numdb_final (guild_infoevent_db, guild_infoevent_db_final); + if (guild_castleinfoevent_db) + numdb_final (guild_castleinfoevent_db, guild_infoevent_db_final); +} diff --git a/src/map/guild.h b/src/map/guild.h deleted file mode 100644 index 8e7c32e..0000000 --- a/src/map/guild.h +++ /dev/null @@ -1,95 +0,0 @@ -// $Id: guild.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ -#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_sp (int id, int lv); -int guild_skill_get_range (int id); -int guild_skill_get_max (int id); - -int guild_checkskill (struct guild *g, int id); -int guild_checkcastles (struct guild *g); // [MouseJstr] -int guild_isallied (struct guild *g, struct guild_castle *gc); - -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 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_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 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_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_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_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/guild.hpp b/src/map/guild.hpp new file mode 100644 index 0000000..f6d1356 --- /dev/null +++ b/src/map/guild.hpp @@ -0,0 +1,95 @@ +// $Id: guild.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef GUILD_HPP +#define GUILD_HPP + +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_sp (int id, int lv); +int guild_skill_get_range (int id); +int guild_skill_get_max (int id); + +int guild_checkskill (struct guild *g, int id); +int guild_checkcastles (struct guild *g); // [MouseJstr] +int guild_isallied (struct guild *g, struct guild_castle *gc); + +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 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_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 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_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_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_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 deleted file mode 100644 index 7069e3a..0000000 --- a/src/map/intif.c +++ /dev/null @@ -1,1201 +0,0 @@ -// $Id: intif.c,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ -#include <sys/types.h> -#ifdef LCCWIN32 -#include <winsock.h> -#else -#include <sys/socket.h> -#include <netinet/in.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#ifndef LCCWIN32 -#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/nullpo.h" -#include "../common/socket.h" -#include "../common/timer.h" - -#include "battle.h" -#include "chrif.h" -#include "clif.h" -#include "guild.h" -#include "intif.h" -#include "map.h" -#include "party.h" -#include "pc.h" -#include "storage.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -static const int packet_len_table[] = { - -1, -1, 27, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -1, 7, 0, 0, 0, 0, 0, 0, -1, 11, 0, 0, 0, 0, 0, 0, - 35, -1, 11, 15, 34, 29, 7, -1, 0, 0, 0, 0, 0, 0, 0, 0, - 10, -1, 15, 0, 79, 19, 7, -1, 0, -1, -1, -1, 14, 67, 186, -1, - 9, 9, -1, 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, - 11, -1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -extern int char_fd; // inter serverのfdはchar_fdを使う -#define inter_fd (char_fd) // エイリアス - -//----------------------------------------------------------------- -// inter serverへの送信 - -// Message for all GMs on all map servers -int intif_GMmessage (char *mes, int len, int flag) -{ - int lp = (flag & 0x10) ? 8 : 4; - WFIFOW (inter_fd, 0) = 0x3000; - WFIFOW (inter_fd, 2) = lp + len; - WFIFOL (inter_fd, 4) = 0x65756c62; - memcpy (WFIFOP (inter_fd, lp), 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); - - WFIFOW (inter_fd, 0) = 0x3001; - WFIFOW (inter_fd, 2) = mes_len + 52; - memcpy (WFIFOP (inter_fd, 4), sd->status.name, 24); - memcpy (WFIFOP (inter_fd, 28), nick, 24); - memcpy (WFIFOP (inter_fd, 52), mes, mes_len); - WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); - - if (battle_config.etc_log) - printf ("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) -{ - 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) - printf ("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) -{ - WFIFOW (inter_fd, 0) = 0x3003; - WFIFOW (inter_fd, 2) = mes_len + 30; - memcpy (WFIFOP (inter_fd, 4), Wisp_name, 24); - WFIFOW (inter_fd, 28) = (short) min_gm_level; - memcpy (WFIFOP (inter_fd, 30), mes, mes_len); - WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); - - if (battle_config.etc_log) - printf - ("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", - Wisp_name, min_gm_level, mes); - - return 0; -} - -// アカウント変数送信 -int intif_saveaccountreg (struct map_session_data *sd) -{ - int j, p; - - nullpo_retr (0, sd); - - WFIFOW (inter_fd, 0) = 0x3004; - WFIFOL (inter_fd, 4) = sd->bl.id; - for (j = 0, p = 8; j < sd->status.account_reg_num; j++, p += 36) - { - memcpy (WFIFOP (inter_fd, p), sd->status.account_reg[j].str, 32); - WFIFOL (inter_fd, p + 32) = sd->status.account_reg[j].value; - } - WFIFOW (inter_fd, 2) = p; - WFIFOSET (inter_fd, p); - return 0; -} - -// アカウント変数要求 -int intif_request_accountreg (struct map_session_data *sd) -{ - nullpo_retr (0, sd); - - WFIFOW (inter_fd, 0) = 0x3005; - WFIFOL (inter_fd, 2) = sd->bl.id; - WFIFOSET (inter_fd, 6); - return 0; -} - -// 倉庫データ要求 -int intif_request_storage (int account_id) -{ - WFIFOW (inter_fd, 0) = 0x3010; - WFIFOL (inter_fd, 2) = account_id; - WFIFOSET (inter_fd, 6); - return 0; -} - -// 倉庫データ送信 -int intif_send_storage (struct storage *stor) -{ - nullpo_retr (0, stor); - 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) -{ - 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) -{ - 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) -{ - nullpo_retr (0, sd); - - WFIFOW (inter_fd, 0) = 0x3020; - WFIFOL (inter_fd, 2) = sd->status.account_id; - memcpy (WFIFOP (inter_fd, 6), name, 24); - memcpy (WFIFOP (inter_fd, 30), sd->status.name, 24); - memcpy (WFIFOP (inter_fd, 54), map[sd->bl.m].name, 16); - WFIFOW (inter_fd, 70) = sd->status.base_level; - WFIFOSET (inter_fd, 72); -// if(battle_config.etc_log) -// printf("intif: create party\n"); - return 0; -} - -// パーティ情報要求 -int intif_request_partyinfo (int party_id) -{ - 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, int account_id) -{ - struct map_session_data *sd; - sd = map_id2sd (account_id); -// if(battle_config.etc_log) -// printf("intif: party add member %d %d\n",party_id,account_id); - if (sd != NULL) - { - WFIFOW (inter_fd, 0) = 0x3022; - WFIFOL (inter_fd, 2) = party_id; - WFIFOL (inter_fd, 6) = account_id; - memcpy (WFIFOP (inter_fd, 10), sd->status.name, 24); - memcpy (WFIFOP (inter_fd, 34), map[sd->bl.m].name, 16); - WFIFOW (inter_fd, 50) = sd->status.base_level; - WFIFOSET (inter_fd, 52); - } - return 0; -} - -// パーティ設定変更 -int intif_party_changeoption (int party_id, int account_id, int exp, int item) -{ - 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) = item; - WFIFOSET (inter_fd, 14); - return 0; -} - -// パーティ脱退要求 -int intif_party_leave (int party_id, int account_id) -{ -// if(battle_config.etc_log) -// printf("intif: party leave %d %d\n",party_id,account_id); - WFIFOW (inter_fd, 0) = 0x3024; - WFIFOL (inter_fd, 2) = party_id; - WFIFOL (inter_fd, 6) = account_id; - WFIFOSET (inter_fd, 10); - return 0; -} - -// パーティ移動要求 -int intif_party_changemap (struct map_session_data *sd, int online) -{ - if (sd != NULL) - { - WFIFOW (inter_fd, 0) = 0x3025; - WFIFOL (inter_fd, 2) = sd->status.party_id; - WFIFOL (inter_fd, 6) = sd->status.account_id; - memcpy (WFIFOP (inter_fd, 10), map[sd->bl.m].name, 16); - WFIFOB (inter_fd, 26) = online; - WFIFOW (inter_fd, 27) = sd->status.base_level; - WFIFOSET (inter_fd, 29); - } -// if(battle_config.etc_log) -// printf("party: change map\n"); - return 0; -} - -// パーティー解散要求 -int intif_break_party (int party_id) -{ - 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(battle_config.etc_log) -// printf("intif_party_message: %s\n",mes); - 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, char *nick) -{ - WFIFOW (inter_fd, 0) = 0x3028; - WFIFOL (inter_fd, 2) = party_id; - WFIFOL (inter_fd, 6) = account_id; - memcpy (WFIFOP (inter_fd, 10), nick, 24); - WFIFOSET (inter_fd, 34); - return 0; -} - -// ギルド作成要求 -int intif_guild_create (const char *name, const struct guild_member *master) -{ - nullpo_retr (0, master); - - WFIFOW (inter_fd, 0) = 0x3030; - WFIFOW (inter_fd, 2) = sizeof (struct guild_member) + 32; - WFIFOL (inter_fd, 4) = master->account_id; - memcpy (WFIFOP (inter_fd, 8), name, 24); - memcpy (WFIFOP (inter_fd, 32), master, sizeof (struct guild_member)); - WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); - return 0; -} - -// ギルド情報要求 -int intif_guild_request_info (int guild_id) -{ - 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) -{ - 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_leave (int guild_id, int account_id, int char_id, int flag, - const char *mes) -{ - 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_) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - WFIFOW (inter_fd, 0) = 0x303c; - WFIFOL (inter_fd, 2) = guild_id; - WFIFOL (inter_fd, 6) = skill_num; - WFIFOL (inter_fd, 10) = account_id; - WFIFOSET (inter_fd, 14); - return 0; -} - -// ギルド同盟/敵対要求 -int intif_guild_alliance (int guild_id1, int guild_id2, int account_id1, - int account_id2, int flag) -{ - 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) -{ - 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 (guild_id <= 0 || len < 0 || len > 2000) - return 0; - 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) -{ - 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) -{ - 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; - int i; - char *wisp_source; - - if (battle_config.etc_log) - printf - ("intif_parse_wismessage: id: %d, from: %s, to: %s, message: '%s'\n", - RFIFOL (fd, 4), RFIFOP (fd, 8), RFIFOP (fd, 32), RFIFOP (fd, - 56)); - sd = map_nick2sd (RFIFOP (fd, 32)); // Searching destination player - if (sd != NULL && strcmp (sd->status.name, RFIFOP (fd, 32)) == 0) - { // exactly same name (inter-server have checked the name before) - // if player ignore all - if (sd->ignoreAll == 1) - intif_wis_replay (RFIFOL (fd, 4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - else - { - wisp_source = RFIFOP (fd, 8); // speed up - // if player ignore the source character - for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); - i++) - if (strcmp (sd->ignore[i].name, wisp_source) == 0) - { - intif_wis_replay (RFIFOL (fd, 4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - break; - } - // if source player not found in ignore list - if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) - { - clif_wis_message (sd->fd, RFIFOP (fd, 8), RFIFOP (fd, 56), - RFIFOW (fd, 2) - 56); - intif_wis_replay (RFIFOL (fd, 4), 0); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - } - } - } - else - intif_wis_replay (RFIFOL (fd, 4), 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - return 0; -} - -// Wisp/page transmission result reception -int intif_parse_WisEnd (int fd) -{ - struct map_session_data *sd; - - if (battle_config.etc_log) - printf ("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 = map_nick2sd (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, len; - struct map_session_data *pl_sd; - char Wisp_name[24]; - char mbuf[255]; - - if (RFIFOW (fd, 2) - 30 <= 0) - return 0; - - len = RFIFOW (fd, 2) - 30; - char *message = ((len) >= 255) ? (char *) malloc (len) : mbuf; - - min_gm_level = (int) RFIFOW (fd, 28); - memcpy (Wisp_name, RFIFOP (fd, 4), 24); - Wisp_name[23] = '\0'; - memcpy (message, RFIFOP (fd, 30), len); - message[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) - free (message); - - return 0; -} - -// アカウント変数通知 -int intif_parse_AccountReg (int fd) -{ - int j, p; - struct map_session_data *sd; - - if ((sd = map_id2sd (RFIFOL (fd, 4))) == NULL) - return 1; - for (p = 8, j = 0; p < RFIFOW (fd, 2) && j < ACCOUNT_REG_NUM; - p += 36, j++) - { - memcpy (sd->status.account_reg[j].str, RFIFOP (fd, p), 32); - sd->status.account_reg[j].value = RFIFOL (fd, p + 32); - } - sd->status.account_reg_num = j; -// printf("intif: accountreg\n"); - - return 0; -} - -// 倉庫データ受信 -int intif_parse_LoadStorage (int fd) -{ - struct storage *stor; - struct map_session_data *sd; - - sd = map_id2sd (RFIFOL (fd, 4)); - if (sd == NULL) - { - if (battle_config.error_log) - printf ("intif_parse_LoadStorage: user not found %d\n", - RFIFOL (fd, 4)); - return 1; - } - stor = account2storage (RFIFOL (fd, 4)); - if (stor->storage_status == 1) - { // Already open.. lets ignore this update - if (battle_config.error_log) - printf - ("intif_parse_LoadStorage: storage received for a client already open (User %d:%d)\n", - sd->status.account_id, sd->status.char_id); - return 1; - } - if (stor->dirty) - { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] - if (battle_config.error_log) - printf - ("intif_parse_LoadStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", - sd->status.account_id, sd->status.char_id); - return 1; - } - - if (RFIFOW (fd, 2) - 8 != sizeof (struct storage)) - { - if (battle_config.error_log) - printf ("intif_parse_LoadStorage: data size error %d %d\n", - RFIFOW (fd, 2) - 8, sizeof (struct storage)); - return 1; - } - if (battle_config.save_log) - printf ("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) -{ - if (battle_config.save_log) - printf ("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; - - guild_id = RFIFOL (fd, 8); - if (guild_id > 0) - { - gstor = guild2storage (guild_id); - if (!gstor) - { - if (battle_config.error_log) - printf - ("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) - printf - ("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) - printf ("intif_parse_LoadGuildStorage: user not found %d\n", - RFIFOL (fd, 4)); - return 1; - } - if (gstor->storage_status == 1) - { // Already open.. lets ignore this update - if (battle_config.error_log) - printf - ("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", - sd->status.account_id, sd->status.char_id); - return 1; - } - if (gstor->dirty) - { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] - if (battle_config.error_log) - printf - ("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", - sd->status.account_id, sd->status.char_id); - return 1; - } - if (battle_config.save_log) - printf ("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) -{ - if (battle_config.save_log) - { - printf ("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) -{ - if (battle_config.etc_log) - printf ("intif: party created\n"); - party_created (RFIFOL (fd, 2), RFIFOB (fd, 6), RFIFOL (fd, 7), - RFIFOP (fd, 11)); - return 0; -} - -// パーティ情報 -int intif_parse_PartyInfo (int fd) -{ - if (RFIFOW (fd, 2) == 8) - { - if (battle_config.error_log) - printf ("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) - printf ("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) -{ - if (battle_config.etc_log) - printf ("intif: party member added %d %d %d\n", RFIFOL (fd, 2), - RFIFOL (fd, 6), RFIFOB (fd, 10)); - party_member_added (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOB (fd, 10)); - return 0; -} - -// パーティ設定変更通知 -int intif_parse_PartyOptionChanged (int 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) -{ - if (battle_config.etc_log) - printf ("intif: party member leaved %d %d %s\n", RFIFOL (fd, 2), - RFIFOL (fd, 6), RFIFOP (fd, 10)); - party_member_leaved (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOP (fd, 10)); - return 0; -} - -// パーティ解散通知 -int intif_parse_PartyBroken (int fd) -{ - party_broken (RFIFOL (fd, 2)); - return 0; -} - -// パーティ移動通知 -int intif_parse_PartyMove (int fd) -{ -// if(battle_config.etc_log) -// printf("intif: party move %d %d %s %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOB(fd,26),RFIFOW(fd,27)); - party_recv_movemap (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOP (fd, 10), - RFIFOB (fd, 26), RFIFOW (fd, 27)); - return 0; -} - -// パーティメッセージ -int intif_parse_PartyMessage (int fd) -{ -// if(battle_config.etc_log) -// printf("intif_parse_PartyMessage: %s\n",RFIFOP(fd,12)); - party_recv_message (RFIFOL (fd, 4), RFIFOL (fd, 8), RFIFOP (fd, 12), - RFIFOW (fd, 2) - 12); - return 0; -} - -// ギルド作成可否 -int intif_parse_GuildCreated (int fd) -{ - guild_created (RFIFOL (fd, 2), RFIFOL (fd, 6)); - return 0; -} - -// ギルド情報 -int intif_parse_GuildInfo (int fd) -{ - if (RFIFOW (fd, 2) == 8) - { - if (battle_config.error_log) - printf ("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) - printf ("intif: guild info : data size error\n %d %d %d", - 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) -{ - if (battle_config.etc_log) - printf ("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) -{ - guild_member_leaved (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), - RFIFOB (fd, 14), RFIFOP (fd, 55), RFIFOP (fd, 15)); - return 0; -} - -// ギルドメンバオンライン状態/Lv変更通知 -int intif_parse_GuildMemberInfoShort (int 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) -{ - guild_broken (RFIFOL (fd, 2), RFIFOB (fd, 6)); - return 0; -} - -// ギルド基本情報変更通知 -int intif_parse_GuildBasicInfoChanged (int fd) -{ - int type = RFIFOW (fd, 8), guild_id = RFIFOL (fd, 4); - void *data = RFIFOP (fd, 10); - struct guild *g = guild_search (guild_id); - short dw = *((short *) data); - int 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 = RFIFOW (fd, 16), guild_id = RFIFOL (fd, 4); - int account_id = RFIFOL (fd, 8), char_id = RFIFOL (fd, 12); - void *data = RFIFOP (fd, 18); - struct guild *g = guild_search (guild_id); - int idx, 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; - } - return 0; -} - -// ギルド役職変更通知 -int intif_parse_GuildPosition (int fd) -{ - if (RFIFOW (fd, 2) != sizeof (struct guild_position) + 12) - { - if (battle_config.error_log) - printf ("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) -{ - guild_skillupack (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10)); - return 0; -} - -// ギルド同盟/敵対通知 -int intif_parse_GuildAlliance (int fd) -{ - guild_allianceack (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), - RFIFOL (fd, 14), RFIFOB (fd, 18), RFIFOP (fd, 19), - RFIFOP (fd, 43)); - return 0; -} - -// ギルド告知変更通知 -int intif_parse_GuildNotice (int fd) -{ - guild_notice_changed (RFIFOL (fd, 2), RFIFOP (fd, 6), RFIFOP (fd, 66)); - return 0; -} - -// ギルドエンブレム変更通知 -int intif_parse_GuildEmblem (int fd) -{ - guild_emblem_changed (RFIFOW (fd, 2) - 12, RFIFOL (fd, 4), RFIFOL (fd, 8), - RFIFOP (fd, 12)); - return 0; -} - -// ギルド会話受信 -int intif_parse_GuildMessage (int fd) -{ - guild_recv_message (RFIFOL (fd, 4), RFIFOL (fd, 8), RFIFOP (fd, 12), - RFIFOW (fd, 2) - 12); - return 0; -} - -// ギルド城データ要求返信 -int intif_parse_GuildCastleDataLoad (int fd) -{ - return guild_castledataloadack (RFIFOW (fd, 2), RFIFOB (fd, 4), - RFIFOL (fd, 5)); -} - -// ギルド城データ変更通知 -int intif_parse_GuildCastleDataSave (int fd) -{ - return guild_castledatasaveack (RFIFOW (fd, 2), RFIFOB (fd, 4), - RFIFOL (fd, 5)); -} - -// ギルド城データ一括受信(初期化時) -int intif_parse_GuildCastleAllDataLoad (int fd) -{ - return guild_castlealldataload (RFIFOW (fd, 2), - (struct guild_castle *) RFIFOP (fd, 4)); -} - -//----------------------------------------------------------------- -// inter serverからの通信 -// エラーがあれば0(false)を返すこと -// パケットが処理できれば1,パケット長が足りなければ2を返すこと -int intif_parse (int fd) -{ - int packet_len; - int 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 (RFIFOREST (fd) < packet_len) - { - return 2; - } - // 処理分岐 - switch (cmd) - { - case 0x3800: - clif_GMmessage (NULL, RFIFOP (fd, 4), packet_len - 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_AccountReg (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 0x3880: intif_parse_CreateP.et(fd); break; - //case 0x3881: intif_parse_RecvP.etData(fd); break; - //case 0x3882: intif_parse_SaveP.etOk(fd); break; - //case 0x3883: intif_parse_DeleteP.etOk(fd); break; - default: - if (battle_config.error_log) - printf ("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.cpp b/src/map/intif.cpp new file mode 100644 index 0000000..3ccb13a --- /dev/null +++ b/src/map/intif.cpp @@ -0,0 +1,1201 @@ +// $Id: intif.c,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ +#include <sys/types.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#ifndef LCCWIN32 +#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/nullpo.hpp" +#include "../common/socket.hpp" +#include "../common/timer.hpp" + +#include "battle.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "map.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "storage.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +static const int packet_len_table[] = { + -1, -1, 27, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 7, 0, 0, 0, 0, 0, 0, -1, 11, 0, 0, 0, 0, 0, 0, + 35, -1, 11, 15, 34, 29, 7, -1, 0, 0, 0, 0, 0, 0, 0, 0, + 10, -1, 15, 0, 79, 19, 7, -1, 0, -1, -1, -1, 14, 67, 186, -1, + 9, 9, -1, 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, + 11, -1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +extern int char_fd; // inter serverのfdはchar_fdを使う +#define inter_fd (char_fd) // エイリアス + +//----------------------------------------------------------------- +// inter serverへの送信 + +// Message for all GMs on all map servers +int intif_GMmessage (char *mes, int len, int flag) +{ + int lp = (flag & 0x10) ? 8 : 4; + WFIFOW (inter_fd, 0) = 0x3000; + WFIFOW (inter_fd, 2) = lp + len; + WFIFOL (inter_fd, 4) = 0x65756c62; + memcpy (WFIFOP (inter_fd, lp), 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); + + WFIFOW (inter_fd, 0) = 0x3001; + WFIFOW (inter_fd, 2) = mes_len + 52; + memcpy (WFIFOP (inter_fd, 4), sd->status.name, 24); + memcpy (WFIFOP (inter_fd, 28), nick, 24); + memcpy (WFIFOP (inter_fd, 52), mes, mes_len); + WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); + + if (battle_config.etc_log) + printf ("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) +{ + 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) + printf ("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) +{ + WFIFOW (inter_fd, 0) = 0x3003; + WFIFOW (inter_fd, 2) = mes_len + 30; + memcpy (WFIFOP (inter_fd, 4), Wisp_name, 24); + WFIFOW (inter_fd, 28) = (short) min_gm_level; + memcpy (WFIFOP (inter_fd, 30), mes, mes_len); + WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); + + if (battle_config.etc_log) + printf + ("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", + Wisp_name, min_gm_level, mes); + + return 0; +} + +// アカウント変数送信 +int intif_saveaccountreg (struct map_session_data *sd) +{ + int j, p; + + nullpo_retr (0, sd); + + WFIFOW (inter_fd, 0) = 0x3004; + WFIFOL (inter_fd, 4) = sd->bl.id; + for (j = 0, p = 8; j < sd->status.account_reg_num; j++, p += 36) + { + memcpy (WFIFOP (inter_fd, p), sd->status.account_reg[j].str, 32); + WFIFOL (inter_fd, p + 32) = sd->status.account_reg[j].value; + } + WFIFOW (inter_fd, 2) = p; + WFIFOSET (inter_fd, p); + return 0; +} + +// アカウント変数要求 +int intif_request_accountreg (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + WFIFOW (inter_fd, 0) = 0x3005; + WFIFOL (inter_fd, 2) = sd->bl.id; + WFIFOSET (inter_fd, 6); + return 0; +} + +// 倉庫データ要求 +int intif_request_storage (int account_id) +{ + WFIFOW (inter_fd, 0) = 0x3010; + WFIFOL (inter_fd, 2) = account_id; + WFIFOSET (inter_fd, 6); + return 0; +} + +// 倉庫データ送信 +int intif_send_storage (struct storage *stor) +{ + nullpo_retr (0, stor); + 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) +{ + 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) +{ + 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) +{ + nullpo_retr (0, sd); + + WFIFOW (inter_fd, 0) = 0x3020; + WFIFOL (inter_fd, 2) = sd->status.account_id; + memcpy (WFIFOP (inter_fd, 6), name, 24); + memcpy (WFIFOP (inter_fd, 30), sd->status.name, 24); + memcpy (WFIFOP (inter_fd, 54), map[sd->bl.m].name, 16); + WFIFOW (inter_fd, 70) = sd->status.base_level; + WFIFOSET (inter_fd, 72); +// if(battle_config.etc_log) +// printf("intif: create party\n"); + return 0; +} + +// パーティ情報要求 +int intif_request_partyinfo (int party_id) +{ + 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, int account_id) +{ + struct map_session_data *sd; + sd = map_id2sd (account_id); +// if(battle_config.etc_log) +// printf("intif: party add member %d %d\n",party_id,account_id); + if (sd != NULL) + { + WFIFOW (inter_fd, 0) = 0x3022; + WFIFOL (inter_fd, 2) = party_id; + WFIFOL (inter_fd, 6) = account_id; + memcpy (WFIFOP (inter_fd, 10), sd->status.name, 24); + memcpy (WFIFOP (inter_fd, 34), map[sd->bl.m].name, 16); + WFIFOW (inter_fd, 50) = sd->status.base_level; + WFIFOSET (inter_fd, 52); + } + return 0; +} + +// パーティ設定変更 +int intif_party_changeoption (int party_id, int account_id, int exp, int item) +{ + 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) = item; + WFIFOSET (inter_fd, 14); + return 0; +} + +// パーティ脱退要求 +int intif_party_leave (int party_id, int account_id) +{ +// if(battle_config.etc_log) +// printf("intif: party leave %d %d\n",party_id,account_id); + WFIFOW (inter_fd, 0) = 0x3024; + WFIFOL (inter_fd, 2) = party_id; + WFIFOL (inter_fd, 6) = account_id; + WFIFOSET (inter_fd, 10); + return 0; +} + +// パーティ移動要求 +int intif_party_changemap (struct map_session_data *sd, int online) +{ + if (sd != NULL) + { + WFIFOW (inter_fd, 0) = 0x3025; + WFIFOL (inter_fd, 2) = sd->status.party_id; + WFIFOL (inter_fd, 6) = sd->status.account_id; + memcpy (WFIFOP (inter_fd, 10), map[sd->bl.m].name, 16); + WFIFOB (inter_fd, 26) = online; + WFIFOW (inter_fd, 27) = sd->status.base_level; + WFIFOSET (inter_fd, 29); + } +// if(battle_config.etc_log) +// printf("party: change map\n"); + return 0; +} + +// パーティー解散要求 +int intif_break_party (int party_id) +{ + 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(battle_config.etc_log) +// printf("intif_party_message: %s\n",mes); + 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, char *nick) +{ + WFIFOW (inter_fd, 0) = 0x3028; + WFIFOL (inter_fd, 2) = party_id; + WFIFOL (inter_fd, 6) = account_id; + memcpy (WFIFOP (inter_fd, 10), nick, 24); + WFIFOSET (inter_fd, 34); + return 0; +} + +// ギルド作成要求 +int intif_guild_create (const char *name, const struct guild_member *master) +{ + nullpo_retr (0, master); + + WFIFOW (inter_fd, 0) = 0x3030; + WFIFOW (inter_fd, 2) = sizeof (struct guild_member) + 32; + WFIFOL (inter_fd, 4) = master->account_id; + memcpy (WFIFOP (inter_fd, 8), name, 24); + memcpy (WFIFOP (inter_fd, 32), master, sizeof (struct guild_member)); + WFIFOSET (inter_fd, WFIFOW (inter_fd, 2)); + return 0; +} + +// ギルド情報要求 +int intif_guild_request_info (int guild_id) +{ + 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) +{ + 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_leave (int guild_id, int account_id, int char_id, int flag, + const char *mes) +{ + 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_) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + WFIFOW (inter_fd, 0) = 0x303c; + WFIFOL (inter_fd, 2) = guild_id; + WFIFOL (inter_fd, 6) = skill_num; + WFIFOL (inter_fd, 10) = account_id; + WFIFOSET (inter_fd, 14); + return 0; +} + +// ギルド同盟/敵対要求 +int intif_guild_alliance (int guild_id1, int guild_id2, int account_id1, + int account_id2, int flag) +{ + 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) +{ + 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 (guild_id <= 0 || len < 0 || len > 2000) + return 0; + 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) +{ + 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) +{ + 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; + int i; + char *wisp_source; + + if (battle_config.etc_log) + printf + ("intif_parse_wismessage: id: %d, from: %s, to: %s, message: '%s'\n", + RFIFOL (fd, 4), RFIFOP (fd, 8), RFIFOP (fd, 32), RFIFOP (fd, + 56)); + sd = map_nick2sd (RFIFOP (fd, 32)); // Searching destination player + if (sd != NULL && strcmp (sd->status.name, RFIFOP (fd, 32)) == 0) + { // exactly same name (inter-server have checked the name before) + // if player ignore all + if (sd->ignoreAll == 1) + intif_wis_replay (RFIFOL (fd, 4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + else + { + wisp_source = RFIFOP (fd, 8); // speed up + // if player ignore the source character + for (i = 0; i < (sizeof (sd->ignore) / sizeof (sd->ignore[0])); + i++) + if (strcmp (sd->ignore[i].name, wisp_source) == 0) + { + intif_wis_replay (RFIFOL (fd, 4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + break; + } + // if source player not found in ignore list + if (i == (sizeof (sd->ignore) / sizeof (sd->ignore[0]))) + { + clif_wis_message (sd->fd, RFIFOP (fd, 8), RFIFOP (fd, 56), + RFIFOW (fd, 2) - 56); + intif_wis_replay (RFIFOL (fd, 4), 0); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + } + } + } + else + intif_wis_replay (RFIFOL (fd, 4), 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + return 0; +} + +// Wisp/page transmission result reception +int intif_parse_WisEnd (int fd) +{ + struct map_session_data *sd; + + if (battle_config.etc_log) + printf ("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 = map_nick2sd (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, len; + struct map_session_data *pl_sd; + char Wisp_name[24]; + char mbuf[255]; + + if (RFIFOW (fd, 2) - 30 <= 0) + return 0; + + len = RFIFOW (fd, 2) - 30; + char *message = ((len) >= 255) ? (char *) malloc (len) : mbuf; + + min_gm_level = (int) RFIFOW (fd, 28); + memcpy (Wisp_name, RFIFOP (fd, 4), 24); + Wisp_name[23] = '\0'; + memcpy (message, RFIFOP (fd, 30), len); + message[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) + free (message); + + return 0; +} + +// アカウント変数通知 +int intif_parse_AccountReg (int fd) +{ + int j, p; + struct map_session_data *sd; + + if ((sd = map_id2sd (RFIFOL (fd, 4))) == NULL) + return 1; + for (p = 8, j = 0; p < RFIFOW (fd, 2) && j < ACCOUNT_REG_NUM; + p += 36, j++) + { + memcpy (sd->status.account_reg[j].str, RFIFOP (fd, p), 32); + sd->status.account_reg[j].value = RFIFOL (fd, p + 32); + } + sd->status.account_reg_num = j; +// printf("intif: accountreg\n"); + + return 0; +} + +// 倉庫データ受信 +int intif_parse_LoadStorage (int fd) +{ + struct storage *stor; + struct map_session_data *sd; + + sd = map_id2sd (RFIFOL (fd, 4)); + if (sd == NULL) + { + if (battle_config.error_log) + printf ("intif_parse_LoadStorage: user not found %d\n", + RFIFOL (fd, 4)); + return 1; + } + stor = account2storage (RFIFOL (fd, 4)); + if (stor->storage_status == 1) + { // Already open.. lets ignore this update + if (battle_config.error_log) + printf + ("intif_parse_LoadStorage: storage received for a client already open (User %d:%d)\n", + sd->status.account_id, sd->status.char_id); + return 1; + } + if (stor->dirty) + { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] + if (battle_config.error_log) + printf + ("intif_parse_LoadStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", + sd->status.account_id, sd->status.char_id); + return 1; + } + + if (RFIFOW (fd, 2) - 8 != sizeof (struct storage)) + { + if (battle_config.error_log) + printf ("intif_parse_LoadStorage: data size error %d %d\n", + RFIFOW (fd, 2) - 8, sizeof (struct storage)); + return 1; + } + if (battle_config.save_log) + printf ("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) +{ + if (battle_config.save_log) + printf ("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; + + guild_id = RFIFOL (fd, 8); + if (guild_id > 0) + { + gstor = guild2storage (guild_id); + if (!gstor) + { + if (battle_config.error_log) + printf + ("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) + printf + ("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) + printf ("intif_parse_LoadGuildStorage: user not found %d\n", + RFIFOL (fd, 4)); + return 1; + } + if (gstor->storage_status == 1) + { // Already open.. lets ignore this update + if (battle_config.error_log) + printf + ("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", + sd->status.account_id, sd->status.char_id); + return 1; + } + if (gstor->dirty) + { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] + if (battle_config.error_log) + printf + ("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", + sd->status.account_id, sd->status.char_id); + return 1; + } + if (battle_config.save_log) + printf ("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) +{ + if (battle_config.save_log) + { + printf ("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) +{ + if (battle_config.etc_log) + printf ("intif: party created\n"); + party_created (RFIFOL (fd, 2), RFIFOB (fd, 6), RFIFOL (fd, 7), + RFIFOP (fd, 11)); + return 0; +} + +// パーティ情報 +int intif_parse_PartyInfo (int fd) +{ + if (RFIFOW (fd, 2) == 8) + { + if (battle_config.error_log) + printf ("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) + printf ("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) +{ + if (battle_config.etc_log) + printf ("intif: party member added %d %d %d\n", RFIFOL (fd, 2), + RFIFOL (fd, 6), RFIFOB (fd, 10)); + party_member_added (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOB (fd, 10)); + return 0; +} + +// パーティ設定変更通知 +int intif_parse_PartyOptionChanged (int 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) +{ + if (battle_config.etc_log) + printf ("intif: party member leaved %d %d %s\n", RFIFOL (fd, 2), + RFIFOL (fd, 6), RFIFOP (fd, 10)); + party_member_leaved (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOP (fd, 10)); + return 0; +} + +// パーティ解散通知 +int intif_parse_PartyBroken (int fd) +{ + party_broken (RFIFOL (fd, 2)); + return 0; +} + +// パーティ移動通知 +int intif_parse_PartyMove (int fd) +{ +// if(battle_config.etc_log) +// printf("intif: party move %d %d %s %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOB(fd,26),RFIFOW(fd,27)); + party_recv_movemap (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOP (fd, 10), + RFIFOB (fd, 26), RFIFOW (fd, 27)); + return 0; +} + +// パーティメッセージ +int intif_parse_PartyMessage (int fd) +{ +// if(battle_config.etc_log) +// printf("intif_parse_PartyMessage: %s\n",RFIFOP(fd,12)); + party_recv_message (RFIFOL (fd, 4), RFIFOL (fd, 8), RFIFOP (fd, 12), + RFIFOW (fd, 2) - 12); + return 0; +} + +// ギルド作成可否 +int intif_parse_GuildCreated (int fd) +{ + guild_created (RFIFOL (fd, 2), RFIFOL (fd, 6)); + return 0; +} + +// ギルド情報 +int intif_parse_GuildInfo (int fd) +{ + if (RFIFOW (fd, 2) == 8) + { + if (battle_config.error_log) + printf ("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) + printf ("intif: guild info : data size error\n %d %d %d", + 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) +{ + if (battle_config.etc_log) + printf ("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) +{ + guild_member_leaved (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), + RFIFOB (fd, 14), RFIFOP (fd, 55), RFIFOP (fd, 15)); + return 0; +} + +// ギルドメンバオンライン状態/Lv変更通知 +int intif_parse_GuildMemberInfoShort (int 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) +{ + guild_broken (RFIFOL (fd, 2), RFIFOB (fd, 6)); + return 0; +} + +// ギルド基本情報変更通知 +int intif_parse_GuildBasicInfoChanged (int fd) +{ + int type = RFIFOW (fd, 8), guild_id = RFIFOL (fd, 4); + void *data = RFIFOP (fd, 10); + struct guild *g = guild_search (guild_id); + short dw = *((short *) data); + int 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 = RFIFOW (fd, 16), guild_id = RFIFOL (fd, 4); + int account_id = RFIFOL (fd, 8), char_id = RFIFOL (fd, 12); + void *data = RFIFOP (fd, 18); + struct guild *g = guild_search (guild_id); + int idx, 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; + } + return 0; +} + +// ギルド役職変更通知 +int intif_parse_GuildPosition (int fd) +{ + if (RFIFOW (fd, 2) != sizeof (struct guild_position) + 12) + { + if (battle_config.error_log) + printf ("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) +{ + guild_skillupack (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10)); + return 0; +} + +// ギルド同盟/敵対通知 +int intif_parse_GuildAlliance (int fd) +{ + guild_allianceack (RFIFOL (fd, 2), RFIFOL (fd, 6), RFIFOL (fd, 10), + RFIFOL (fd, 14), RFIFOB (fd, 18), RFIFOP (fd, 19), + RFIFOP (fd, 43)); + return 0; +} + +// ギルド告知変更通知 +int intif_parse_GuildNotice (int fd) +{ + guild_notice_changed (RFIFOL (fd, 2), RFIFOP (fd, 6), RFIFOP (fd, 66)); + return 0; +} + +// ギルドエンブレム変更通知 +int intif_parse_GuildEmblem (int fd) +{ + guild_emblem_changed (RFIFOW (fd, 2) - 12, RFIFOL (fd, 4), RFIFOL (fd, 8), + RFIFOP (fd, 12)); + return 0; +} + +// ギルド会話受信 +int intif_parse_GuildMessage (int fd) +{ + guild_recv_message (RFIFOL (fd, 4), RFIFOL (fd, 8), RFIFOP (fd, 12), + RFIFOW (fd, 2) - 12); + return 0; +} + +// ギルド城データ要求返信 +int intif_parse_GuildCastleDataLoad (int fd) +{ + return guild_castledataloadack (RFIFOW (fd, 2), RFIFOB (fd, 4), + RFIFOL (fd, 5)); +} + +// ギルド城データ変更通知 +int intif_parse_GuildCastleDataSave (int fd) +{ + return guild_castledatasaveack (RFIFOW (fd, 2), RFIFOB (fd, 4), + RFIFOL (fd, 5)); +} + +// ギルド城データ一括受信(初期化時) +int intif_parse_GuildCastleAllDataLoad (int fd) +{ + return guild_castlealldataload (RFIFOW (fd, 2), + (struct guild_castle *) RFIFOP (fd, 4)); +} + +//----------------------------------------------------------------- +// inter serverからの通信 +// エラーがあれば0(false)を返すこと +// パケットが処理できれば1,パケット長が足りなければ2を返すこと +int intif_parse (int fd) +{ + int packet_len; + int 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 (RFIFOREST (fd) < packet_len) + { + return 2; + } + // 処理分岐 + switch (cmd) + { + case 0x3800: + clif_GMmessage (NULL, RFIFOP (fd, 4), packet_len - 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_AccountReg (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 0x3880: intif_parse_CreateP.et(fd); break; + //case 0x3881: intif_parse_RecvP.etData(fd); break; + //case 0x3882: intif_parse_SaveP.etOk(fd); break; + //case 0x3883: intif_parse_DeleteP.etOk(fd); break; + default: + if (battle_config.error_log) + printf ("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 deleted file mode 100644 index 374c95a..0000000 --- a/src/map/intif.h +++ /dev/null @@ -1,56 +0,0 @@ -// $Id: intif.h,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ -#ifndef _INTIF_H_ -#define _INFIF_H_ - -int intif_parse (int fd); - -int intif_GMmessage (char *mes, int len, 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 mes_len); - -int intif_saveaccountreg (struct map_session_data *sd); -int intif_request_accountreg (struct map_session_data *sd); - -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 intif_request_partyinfo (int party_id); -int intif_party_addmember (int party_id, int account_id); -int intif_party_changeoption (int party_id, int account_id, int exp, - int item); -int intif_party_leave (int party_id, int accound_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, char *nick); - -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_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 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); - -#endif diff --git a/src/map/intif.hpp b/src/map/intif.hpp new file mode 100644 index 0000000..dc002e5 --- /dev/null +++ b/src/map/intif.hpp @@ -0,0 +1,56 @@ +// $Id: intif.h,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef INTIF_HPP +#define INFIF_HPP + +int intif_parse (int fd); + +int intif_GMmessage (char *mes, int len, 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 mes_len); + +int intif_saveaccountreg (struct map_session_data *sd); +int intif_request_accountreg (struct map_session_data *sd); + +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 intif_request_partyinfo (int party_id); +int intif_party_addmember (int party_id, int account_id); +int intif_party_changeoption (int party_id, int account_id, int exp, + int item); +int intif_party_leave (int party_id, int accound_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, char *nick); + +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_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 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); + +#endif diff --git a/src/map/itemdb.c b/src/map/itemdb.c deleted file mode 100644 index f89446b..0000000 --- a/src/map/itemdb.c +++ /dev/null @@ -1,753 +0,0 @@ -// $Id: itemdb.c,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "../common/db.h" -#include "../common/grfio.h" -#include "../common/nullpo.h" -#include "map.h" -#include "battle.h" -#include "itemdb.h" -#include "script.h" -#include "pc.h" -#include "../common/socket.h" -#include "../common/mt_rand.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define MAX_RANDITEM 2000 - -// ** 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]; -static int blue_box_count = 0, violet_box_count = 0, card_album_count = - 0, gift_box_count = 0, scroll_count = 0; -static int blue_box_default = 0, violet_box_default = 0, card_album_default = - 0, gift_box_default = 0, scroll_default = 0; - -// Function declarations - -static void itemdb_read (void); -static int itemdb_readdb (void); -static int itemdb_read_randomitem (void); -static int itemdb_read_itemavail (void); -static int itemdb_read_itemnametable (void); -static int itemdb_read_noequip (void); -void itemdb_reload (void); - -/*========================================== - * 名前で検索用 - *------------------------------------------ - */ -// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) -void itemdb_searchname_sub (db_key_t key, db_val_t 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( strcasecmp(item->name,str)==0 || strcmp(item->jname,str)==0 || -// memcmp(item->name,str,24)==0 || memcmp(item->jname,str,24)==0 ) - if (strcasecmp (item->name, str) == 0) //by lupus - *dst = item; -} - -/*========================================== - * 名前で検索用 - *------------------------------------------ - */ -int itemdb_searchjname_sub (void *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 (strcasecmp (item->jname, str) == 0) - *dst = item; - return 0; -} - -/*========================================== - * 名前で検索 - *------------------------------------------ - */ -struct item_data *itemdb_searchname (const char *str) -{ - struct item_data *item = NULL; - numdb_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[] = - { - { - 0, 0, NULL}, - { - blue_box_default, blue_box_count, blue_box}, - { - violet_box_default, violet_box_count, violet_box}, - { - card_album_default, card_album_count, card_album}, - { - gift_box_default, gift_box_count, gift_box}, - { - scroll_default, scroll_count, scroll},}; - - if (flags >= 1 && flags <= 5) - { - nameid = data[flags].nameid; - count = data[flags].count; - list = data[flags].list; - - if (count > 0) - { - for (i = 0; i < 1000; i++) - { - index = MRAND (count); - if (MRAND (1000000) < list[index].per) - { - nameid = list[index].nameid; - break; - } - } - } - } - return nameid; -} - -/*========================================== - * DBの存在確認 - *------------------------------------------ - */ -struct item_data *itemdb_exists (int nameid) -{ - return (struct item_data *)numdb_search (item_db, nameid); -} - -/*========================================== - * DBの検索 - *------------------------------------------ - */ -struct item_data *itemdb_search (int nameid) -{ - struct item_data *id = (struct item_data *)numdb_search (item_db, nameid); - if (id) - return id; - - id = (struct item_data *) calloc (1, sizeof (struct item_data)); - numdb_insert (item_db, nameid, id); - - id->nameid = nameid; - id->value_buy = 10; - id->value_sell = id->value_buy / 2; - id->weight = 10; - id->sex = 2; - id->elv = 0; - id->flag.available = 0; - id->flag.value_notdc = 0; //一応・・・ - id->flag.value_notoc = 0; - id->flag.no_equip = 0; - id->view_id = 0; - - 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 - - return id; -} - -/*========================================== - * - *------------------------------------------ - */ -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; -} - -/*========================================== - * - *------------------------------------------ - */ -int itemdb_isequip3 (int nameid) -{ - int type = itemdb_type (nameid); - if (type == 4 || type == 5 || type == 8) - return 1; - return 0; -} - -/*========================================== - * 捨てられるアイテムは1、そうでないアイテムは0 - *------------------------------------------ - */ -int itemdb_isdropable (int nameid) -{ - //結婚指輪は捨てられない - switch (nameid) - { - case 2634: //結婚指輪 - case 2635: //結婚指輪 - return 0; - } - - return 1; -} - -// -// 初期化 -// -/*========================================== - * - *------------------------------------------ - */ -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; - sscanf (p, "%d#%d#", &nameid, &equip); - itemdb_search (nameid)->equip = equip; - p = strchr (p, 10); - if (!p) - break; - p++; - p = strchr (p, 10); - if (!p) - break; - p++; - } - free (buf); - - return 0; -} - -/*========================================== - * アイテムデータベースの読み込み - *------------------------------------------ - */ -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[] = { "db/item_db.txt", "db/item_db2.txt" }; - - for (i = 0; i < 2; i++) - { - - fp = fopen_ (filename[i], "r"); - if (fp == NULL) - { - if (i > 0) - continue; - printf ("can't read %s\n", filename[i]); - 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 < 17 && p; j++) - { - while (*p == '\t' || *p == ' ') - p++; - 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; - ln++; - - //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Gender,Loc,wLV,eLV,View - id = itemdb_search (nameid); - memcpy (id->name, str[1], 24); - memcpy (id->jname, str[2], 24); - id->type = atoi (str[3]); - id->value_buy = atoi (str[4]); - id->value_sell = atoi (str[5]); - if (id->value_buy == 0 && id->value_sell == 0) - { - } - else if (id->value_buy == 0) - { - id->value_buy = id->value_sell * 2; - } - else if (id->value_sell == 0) - { - id->value_sell = id->value_buy / 2; - } - id->weight = atoi (str[6]); - id->atk = atoi (str[7]); - id->def = atoi (str[8]); - id->range = atoi (str[9]); - id->magic_bonus = atoi (str[10]); - id->slot = atoi (str[11]); - id->sex = atoi (str[12]); - id->equip = atoi (str[13]); - id->wlv = atoi (str[14]); - id->elv = atoi (str[15]); - id->look = atoi (str[16]); - id->flag.available = 1; - id->flag.value_notdc = 0; - id->flag.value_notoc = 0; - id->view_id = 0; - - id->use_script = NULL; - id->equip_script = NULL; - - if ((p = strchr (np, '{')) == NULL) - continue; - id->use_script = parse_script (p, lines); - - if ((p = strchr (p + 1, '{')) == NULL) - continue; - id->equip_script = parse_script (p, lines); - } - fclose_ (fp); - printf ("read %s done (count=%d)\n", filename[i], ln); - } - return 0; -} - -// Removed item_value_db, don't re-add! - -/*========================================== - * ランダムアイテム出現データの読み込み - *------------------------------------------ - */ -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[] = - { - { - "db/item_bluebox.txt", blue_box, &blue_box_count, - &blue_box_default}, - { - "db/item_violetbox.txt", violet_box, &violet_box_count, - &violet_box_default}, - { - "db/item_cardalbum.txt", card_album, &card_album_count, - &card_album_default}, - { - "db/item_giftbox.txt", gift_box, &gift_box_count, - &gift_box_default}, - { - "db/item_scroll.txt", scroll, &scroll_count, &scroll_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; - const char *fn = data[i].filename; - - *pdefault = 0; - if ((fp = fopen_ (fn, "r")) == NULL) - { - printf ("can't read %s\n", fn); - 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); - printf ("read %s done (count=%d)\n", fn, *pc); - } - - return 0; -} - -/*========================================== - * アイテム使用可能フラグのオーバーライド - *------------------------------------------ - */ -static int itemdb_read_itemavail (void) -{ - FILE *fp; - char line[1024]; - int ln = 0; - int nameid, j, k; - char *str[10], *p; - - if ((fp = fopen_ ("db/item_avail.txt", "r")) == NULL) - { - printf ("can't read db/item_avail.txt\n"); - return -1; - } - - while (fgets (line, 1020, fp)) - { - struct item_data *id; - 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; - k = atoi (str[1]); - if (k > 0) - { - id->flag.available = 1; - id->view_id = k; - } - else - id->flag.available = 0; - ln++; - } - fclose_ (fp); - printf ("read db/item_avail.txt done (count=%d)\n", ln); - 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]; - - if (sscanf (p, "%d#%[^#]#", &nameid, buf2) == 2) - { - -#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE - if (itemdb_exists (nameid) && - strncmp (itemdb_search (nameid)->jname, buf2, 24) != 0) - { - printf ("[override] %d %s => %s\n", nameid, - itemdb_search (nameid)->jname, buf2); - } -#endif - - memcpy (itemdb_search (nameid)->jname, buf2, 24); - } - - p = strchr (p, 10); - if (!p) - break; - p++; - } - free (buf); - printf ("read data\\idnum2itemdisplaynametable.txt done.\n"); - - 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); -// printf("%d %s\n",nameid,itemdb_search(nameid)->cardillustname); - } - - p = strchr (p, 10); - if (!p) - break; - p++; - } - free (buf); - printf ("read data\\num2cardillustnametable.txt done.\n"); - - 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; - - if ((fp = fopen_ ("db/item_noequip.txt", "r")) == NULL) - { - printf ("can't read db/item_noequip.txt\n"); - 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); - printf ("read db/item_noequip.txt done (count=%d)\n", ln); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static void itemdb_final (db_key_t key, db_val_t data, va_list ap) -{ - struct item_data *id; - - nullpo_retv (id = (struct item_data *)data); - - if (id->use_script) - free (id->use_script); - if (id->equip_script) - free (id->equip_script); - free (id); -} - -void itemdb_reload (void) -{ - /* - * - * <empty item databases> - * itemdb_read(); - * - */ - - do_init_itemdb (); -} - -/*========================================== - * - *------------------------------------------ - */ -void do_final_itemdb (void) -{ - if (item_db) - { - numdb_final (item_db, itemdb_final); - item_db = NULL; - } -} - -/* -static FILE *dfp; -static int itemdebug(void *key,void *data,va_list ap){ -// struct item_data *id=(struct item_data *)data; - fprintf(dfp,"%6d",(int)key); - return 0; -} -void itemdebugtxt() -{ - dfp=fopen_("itemdebug.txt","wt"); - numdb_foreach(item_db,itemdebug); - fclose_(dfp); -} -*/ - -/*==================================== - * Removed item_value_db, don't re-add - *------------------------------------ - */ -static void itemdb_read (void) -{ - itemdb_read_itemslottable (); - itemdb_readdb (); - itemdb_read_randomitem (); - itemdb_read_itemavail (); - itemdb_read_noequip (); - itemdb_read_cardillustnametable (); - if (!battle_config.item_name_override_grffile) - itemdb_read_itemnametable (); -} - -/*========================================== - * - *------------------------------------------ - */ -int do_init_itemdb (void) -{ - item_db = numdb_init (); - - itemdb_read (); - - return 0; -} diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp new file mode 100644 index 0000000..c1255bd --- /dev/null +++ b/src/map/itemdb.cpp @@ -0,0 +1,753 @@ +// $Id: itemdb.c,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../common/db.hpp" +#include "../common/grfio.hpp" +#include "../common/nullpo.hpp" +#include "map.hpp" +#include "battle.hpp" +#include "itemdb.hpp" +#include "script.hpp" +#include "pc.hpp" +#include "../common/socket.hpp" +#include "../common/mt_rand.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define MAX_RANDITEM 2000 + +// ** 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]; +static int blue_box_count = 0, violet_box_count = 0, card_album_count = + 0, gift_box_count = 0, scroll_count = 0; +static int blue_box_default = 0, violet_box_default = 0, card_album_default = + 0, gift_box_default = 0, scroll_default = 0; + +// Function declarations + +static void itemdb_read (void); +static int itemdb_readdb (void); +static int itemdb_read_randomitem (void); +static int itemdb_read_itemavail (void); +static int itemdb_read_itemnametable (void); +static int itemdb_read_noequip (void); +void itemdb_reload (void); + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) +void itemdb_searchname_sub (db_key_t key, db_val_t 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( strcasecmp(item->name,str)==0 || strcmp(item->jname,str)==0 || +// memcmp(item->name,str,24)==0 || memcmp(item->jname,str,24)==0 ) + if (strcasecmp (item->name, str) == 0) //by lupus + *dst = item; +} + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +int itemdb_searchjname_sub (void *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 (strcasecmp (item->jname, str) == 0) + *dst = item; + return 0; +} + +/*========================================== + * 名前で検索 + *------------------------------------------ + */ +struct item_data *itemdb_searchname (const char *str) +{ + struct item_data *item = NULL; + numdb_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[] = + { + { + 0, 0, NULL}, + { + blue_box_default, blue_box_count, blue_box}, + { + violet_box_default, violet_box_count, violet_box}, + { + card_album_default, card_album_count, card_album}, + { + gift_box_default, gift_box_count, gift_box}, + { + scroll_default, scroll_count, scroll},}; + + if (flags >= 1 && flags <= 5) + { + nameid = data[flags].nameid; + count = data[flags].count; + list = data[flags].list; + + if (count > 0) + { + for (i = 0; i < 1000; i++) + { + index = MRAND (count); + if (MRAND (1000000) < list[index].per) + { + nameid = list[index].nameid; + break; + } + } + } + } + return nameid; +} + +/*========================================== + * DBの存在確認 + *------------------------------------------ + */ +struct item_data *itemdb_exists (int nameid) +{ + return (struct item_data *)numdb_search (item_db, nameid); +} + +/*========================================== + * DBの検索 + *------------------------------------------ + */ +struct item_data *itemdb_search (int nameid) +{ + struct item_data *id = (struct item_data *)numdb_search (item_db, nameid); + if (id) + return id; + + id = (struct item_data *) calloc (1, sizeof (struct item_data)); + numdb_insert (item_db, nameid, id); + + id->nameid = nameid; + id->value_buy = 10; + id->value_sell = id->value_buy / 2; + id->weight = 10; + id->sex = 2; + id->elv = 0; + id->flag.available = 0; + id->flag.value_notdc = 0; //一応・・・ + id->flag.value_notoc = 0; + id->flag.no_equip = 0; + id->view_id = 0; + + 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 + + return id; +} + +/*========================================== + * + *------------------------------------------ + */ +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; +} + +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip3 (int nameid) +{ + int type = itemdb_type (nameid); + if (type == 4 || type == 5 || type == 8) + return 1; + return 0; +} + +/*========================================== + * 捨てられるアイテムは1、そうでないアイテムは0 + *------------------------------------------ + */ +int itemdb_isdropable (int nameid) +{ + //結婚指輪は捨てられない + switch (nameid) + { + case 2634: //結婚指輪 + case 2635: //結婚指輪 + return 0; + } + + return 1; +} + +// +// 初期化 +// +/*========================================== + * + *------------------------------------------ + */ +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; + sscanf (p, "%d#%d#", &nameid, &equip); + itemdb_search (nameid)->equip = equip; + p = strchr (p, 10); + if (!p) + break; + p++; + p = strchr (p, 10); + if (!p) + break; + p++; + } + free (buf); + + return 0; +} + +/*========================================== + * アイテムデータベースの読み込み + *------------------------------------------ + */ +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[] = { "db/item_db.txt", "db/item_db2.txt" }; + + for (i = 0; i < 2; i++) + { + + fp = fopen_ (filename[i], "r"); + if (fp == NULL) + { + if (i > 0) + continue; + printf ("can't read %s\n", filename[i]); + 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 < 17 && p; j++) + { + while (*p == '\t' || *p == ' ') + p++; + 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; + ln++; + + //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Gender,Loc,wLV,eLV,View + id = itemdb_search (nameid); + memcpy (id->name, str[1], 24); + memcpy (id->jname, str[2], 24); + id->type = atoi (str[3]); + id->value_buy = atoi (str[4]); + id->value_sell = atoi (str[5]); + if (id->value_buy == 0 && id->value_sell == 0) + { + } + else if (id->value_buy == 0) + { + id->value_buy = id->value_sell * 2; + } + else if (id->value_sell == 0) + { + id->value_sell = id->value_buy / 2; + } + id->weight = atoi (str[6]); + id->atk = atoi (str[7]); + id->def = atoi (str[8]); + id->range = atoi (str[9]); + id->magic_bonus = atoi (str[10]); + id->slot = atoi (str[11]); + id->sex = atoi (str[12]); + id->equip = atoi (str[13]); + id->wlv = atoi (str[14]); + id->elv = atoi (str[15]); + id->look = atoi (str[16]); + id->flag.available = 1; + id->flag.value_notdc = 0; + id->flag.value_notoc = 0; + id->view_id = 0; + + id->use_script = NULL; + id->equip_script = NULL; + + if ((p = strchr (np, '{')) == NULL) + continue; + id->use_script = parse_script (p, lines); + + if ((p = strchr (p + 1, '{')) == NULL) + continue; + id->equip_script = parse_script (p, lines); + } + fclose_ (fp); + printf ("read %s done (count=%d)\n", filename[i], ln); + } + return 0; +} + +// Removed item_value_db, don't re-add! + +/*========================================== + * ランダムアイテム出現データの読み込み + *------------------------------------------ + */ +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[] = + { + { + "db/item_bluebox.txt", blue_box, &blue_box_count, + &blue_box_default}, + { + "db/item_violetbox.txt", violet_box, &violet_box_count, + &violet_box_default}, + { + "db/item_cardalbum.txt", card_album, &card_album_count, + &card_album_default}, + { + "db/item_giftbox.txt", gift_box, &gift_box_count, + &gift_box_default}, + { + "db/item_scroll.txt", scroll, &scroll_count, &scroll_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; + const char *fn = data[i].filename; + + *pdefault = 0; + if ((fp = fopen_ (fn, "r")) == NULL) + { + printf ("can't read %s\n", fn); + 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); + printf ("read %s done (count=%d)\n", fn, *pc); + } + + return 0; +} + +/*========================================== + * アイテム使用可能フラグのオーバーライド + *------------------------------------------ + */ +static int itemdb_read_itemavail (void) +{ + FILE *fp; + char line[1024]; + int ln = 0; + int nameid, j, k; + char *str[10], *p; + + if ((fp = fopen_ ("db/item_avail.txt", "r")) == NULL) + { + printf ("can't read db/item_avail.txt\n"); + return -1; + } + + while (fgets (line, 1020, fp)) + { + struct item_data *id; + 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; + k = atoi (str[1]); + if (k > 0) + { + id->flag.available = 1; + id->view_id = k; + } + else + id->flag.available = 0; + ln++; + } + fclose_ (fp); + printf ("read db/item_avail.txt done (count=%d)\n", ln); + 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]; + + if (sscanf (p, "%d#%[^#]#", &nameid, buf2) == 2) + { + +#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE + if (itemdb_exists (nameid) && + strncmp (itemdb_search (nameid)->jname, buf2, 24) != 0) + { + printf ("[override] %d %s => %s\n", nameid, + itemdb_search (nameid)->jname, buf2); + } +#endif + + memcpy (itemdb_search (nameid)->jname, buf2, 24); + } + + p = strchr (p, 10); + if (!p) + break; + p++; + } + free (buf); + printf ("read data\\idnum2itemdisplaynametable.txt done.\n"); + + 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); +// printf("%d %s\n",nameid,itemdb_search(nameid)->cardillustname); + } + + p = strchr (p, 10); + if (!p) + break; + p++; + } + free (buf); + printf ("read data\\num2cardillustnametable.txt done.\n"); + + 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; + + if ((fp = fopen_ ("db/item_noequip.txt", "r")) == NULL) + { + printf ("can't read db/item_noequip.txt\n"); + 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); + printf ("read db/item_noequip.txt done (count=%d)\n", ln); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static void itemdb_final (db_key_t key, db_val_t data, va_list ap) +{ + struct item_data *id; + + nullpo_retv (id = (struct item_data *)data); + + if (id->use_script) + free (id->use_script); + if (id->equip_script) + free (id->equip_script); + free (id); +} + +void itemdb_reload (void) +{ + /* + * + * <empty item databases> + * itemdb_read(); + * + */ + + do_init_itemdb (); +} + +/*========================================== + * + *------------------------------------------ + */ +void do_final_itemdb (void) +{ + if (item_db) + { + numdb_final (item_db, itemdb_final); + item_db = NULL; + } +} + +/* +static FILE *dfp; +static int itemdebug(void *key,void *data,va_list ap){ +// struct item_data *id=(struct item_data *)data; + fprintf(dfp,"%6d",(int)key); + return 0; +} +void itemdebugtxt() +{ + dfp=fopen_("itemdebug.txt","wt"); + numdb_foreach(item_db,itemdebug); + fclose_(dfp); +} +*/ + +/*==================================== + * Removed item_value_db, don't re-add + *------------------------------------ + */ +static void itemdb_read (void) +{ + itemdb_read_itemslottable (); + itemdb_readdb (); + itemdb_read_randomitem (); + itemdb_read_itemavail (); + itemdb_read_noequip (); + itemdb_read_cardillustnametable (); + if (!battle_config.item_name_override_grffile) + itemdb_read_itemnametable (); +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_itemdb (void) +{ + item_db = numdb_init (); + + itemdb_read (); + + return 0; +} diff --git a/src/map/itemdb.h b/src/map/itemdb.h deleted file mode 100644 index de07822..0000000 --- a/src/map/itemdb.h +++ /dev/null @@ -1,82 +0,0 @@ -// $Id: itemdb.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ -#ifndef _ITEMDB_H_ -#define _ITEMDB_H_ - -#include "map.h" - -struct item_data -{ - int nameid; - char name[24], jname[24]; - char prefix[24], suffix[24]; - char cardillustname[64]; - int value_buy; - int value_sell; - int type; - int sex; - int equip; - int weight; - int atk; - int def; - int range; - int magic_bonus; - int slot; - int look; - int elv; - int wlv; - int refine; - char *use_script; - char *equip_script; - struct - { - unsigned available:1; - unsigned value_notdc:1; - unsigned value_notoc:1; - unsigned no_equip:3; - unsigned no_drop:1; - unsigned no_use:1; - } flag; - int view_id; -}; - -struct random_item_data -{ - int nameid; - int per; -}; - -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)->use_script -#define itemdb_equipscript(n) itemdb_search(n)->equip_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_searchrandomid (int flags); - -#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 - -int itemdb_isequip (int); -int itemdb_isequip2 (struct item_data *); -int itemdb_isequip3 (int); -int itemdb_isdropable (int nameid); - -void itemdb_reload (void); - -void do_final_itemdb (void); -int do_init_itemdb (void); - -#endif diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp new file mode 100644 index 0000000..ef5eab2 --- /dev/null +++ b/src/map/itemdb.hpp @@ -0,0 +1,82 @@ +// $Id: itemdb.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef ITEMDB_HPP +#define ITEMDB_HPP + +#include "map.hpp" + +struct item_data +{ + int nameid; + char name[24], jname[24]; + char prefix[24], suffix[24]; + char cardillustname[64]; + int value_buy; + int value_sell; + int type; + int sex; + int equip; + int weight; + int atk; + int def; + int range; + int magic_bonus; + int slot; + int look; + int elv; + int wlv; + int refine; + char *use_script; + char *equip_script; + struct + { + unsigned available:1; + unsigned value_notdc:1; + unsigned value_notoc:1; + unsigned no_equip:3; + unsigned no_drop:1; + unsigned no_use:1; + } flag; + int view_id; +}; + +struct random_item_data +{ + int nameid; + int per; +}; + +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)->use_script +#define itemdb_equipscript(n) itemdb_search(n)->equip_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_searchrandomid (int flags); + +#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 + +int itemdb_isequip (int); +int itemdb_isequip2 (struct item_data *); +int itemdb_isequip3 (int); +int itemdb_isdropable (int nameid); + +void itemdb_reload (void); + +void do_final_itemdb (void); +int do_init_itemdb (void); + +#endif diff --git a/src/map/magic-expr-eval.h b/src/map/magic-expr-eval.h deleted file mode 100644 index 71c1d86..0000000 --- a/src/map/magic-expr-eval.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef MAGIC_EXPR_EVAL -#define MAGIC_EXPR_EVAL - -/* Helper definitions for dealing with functions and operations */ - -static int heading_x[8] = { 0, -1, -1, -1, 0, 1, 1, 1 }; -static int heading_y[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; - -int - magic_signature_check (char *opname, char *funname, char *signature, - int args_nr, val_t * args, int line, int column); - -void -magic_area_rect (int *m, int *x, int *y, int *width, int *height, - area_t * area); - -#define ARGINT(x) args[x].v.v_int -#define ARGDIR(x) args[x].v.v_int -#define ARGSTR(x) args[x].v.v_string -#define ARGENTITY(x) args[x].v.v_entity -#define ARGLOCATION(x) args[x].v.v_location -#define ARGAREA(x) args[x].v.v_area -#define ARGSPELL(x) args[x].v.v_spell -#define ARGINVOCATION(x) args[x].v.v_invocation - -#define RESULTINT result->v.v_int -#define RESULTDIR result->v.v_int -#define RESULTSTR result->v.v_string -#define RESULTENTITY result->v.v_entity -#define RESULTLOCATION result->v.v_location -#define RESULTAREA result->v.v_area -#define RESULTSPELL result->v.v_spell -#define RESULTINVOCATION result->v.v_invocation - -#define TY(x) args[x].ty -#define ETY(x) ARGENTITY(x)->type - -#define ARGPC(x) ((struct map_session_data *)ARGENTITY(x)) -#define ARGNPC(x) ((struct map_session_data *)ARGENTITY(x)) -#define ARGMOB(x) ((struct map_session_data *)ARGENTITY(x)) - -#define ARG_MAY_BE_AREA(x) (TY(x) == TY_AREA || TY(x) == TY_LOCATION) - -#endif /* !defined(MAGIC_EXPR_EVAL) */ diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp new file mode 100644 index 0000000..71c1d86 --- /dev/null +++ b/src/map/magic-expr-eval.hpp @@ -0,0 +1,44 @@ +#ifndef MAGIC_EXPR_EVAL +#define MAGIC_EXPR_EVAL + +/* Helper definitions for dealing with functions and operations */ + +static int heading_x[8] = { 0, -1, -1, -1, 0, 1, 1, 1 }; +static int heading_y[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; + +int + magic_signature_check (char *opname, char *funname, char *signature, + int args_nr, val_t * args, int line, int column); + +void +magic_area_rect (int *m, int *x, int *y, int *width, int *height, + area_t * area); + +#define ARGINT(x) args[x].v.v_int +#define ARGDIR(x) args[x].v.v_int +#define ARGSTR(x) args[x].v.v_string +#define ARGENTITY(x) args[x].v.v_entity +#define ARGLOCATION(x) args[x].v.v_location +#define ARGAREA(x) args[x].v.v_area +#define ARGSPELL(x) args[x].v.v_spell +#define ARGINVOCATION(x) args[x].v.v_invocation + +#define RESULTINT result->v.v_int +#define RESULTDIR result->v.v_int +#define RESULTSTR result->v.v_string +#define RESULTENTITY result->v.v_entity +#define RESULTLOCATION result->v.v_location +#define RESULTAREA result->v.v_area +#define RESULTSPELL result->v.v_spell +#define RESULTINVOCATION result->v.v_invocation + +#define TY(x) args[x].ty +#define ETY(x) ARGENTITY(x)->type + +#define ARGPC(x) ((struct map_session_data *)ARGENTITY(x)) +#define ARGNPC(x) ((struct map_session_data *)ARGENTITY(x)) +#define ARGMOB(x) ((struct map_session_data *)ARGENTITY(x)) + +#define ARG_MAY_BE_AREA(x) (TY(x) == TY_AREA || TY(x) == TY_LOCATION) + +#endif /* !defined(MAGIC_EXPR_EVAL) */ diff --git a/src/map/magic-expr.c b/src/map/magic-expr.c deleted file mode 100644 index a0a4935..0000000 --- a/src/map/magic-expr.c +++ /dev/null @@ -1,1655 +0,0 @@ -#include "magic-expr.h" -#include "magic-expr-eval.h" -#include "itemdb.h" -#include <math.h> - -#include "../common/mt_rand.h" - -#define IS_SOLID(c) ((c) == 1 || (c) == 5) - -int map_is_solid (int m, int x, int y) -{ - return (IS_SOLID (map_getcell (m, x, y))); -} - -#undef IS_SOLID - -static void free_area (area_t * area) -{ - if (!area) - return; - - switch (area->ty) - { - case AREA_UNION: - free_area (area->a.a_union[0]); - free_area (area->a.a_union[1]); - break; - default: - break; - } - - free (area); -} - -static area_t *dup_area (area_t * area) -{ - area_t *retval = (area_t *)malloc (sizeof (area_t)); - *retval = *area; - - switch (area->ty) - { - case AREA_UNION: - retval->a.a_union[0] = dup_area (retval->a.a_union[0]); - retval->a.a_union[1] = dup_area (retval->a.a_union[1]); - break; - default: - break; - } - - return retval; -} - -void magic_copy_var (val_t * dest, val_t * src) -{ - *dest = *src; - - switch (dest->ty) - { - case TY_STRING: - dest->v.v_string = strdup (dest->v.v_string); - break; - case TY_AREA: - dest->v.v_area = dup_area (dest->v.v_area); - break; - default: - break; - } - -} - -void magic_clear_var (val_t * v) -{ - switch (v->ty) - { - case TY_STRING: - free (v->v.v_string); - break; - case TY_AREA: - free_area (v->v.v_area); - break; - default: - break; - } -} - -static char *show_entity (entity_t * entity) -{ - switch (entity->type) - { - case BL_PC: - return ((struct map_session_data *) entity)->status.name; - case BL_NPC: - return ((struct npc_data *) entity)->name; - case BL_MOB: - return ((struct mob_data *) entity)->name; - case BL_ITEM: - /* Sorry about this one... */ - return ((struct item_data - *) (&((struct flooritem_data *) entity)-> - item_data))->name; - case BL_SKILL: - return "%skill"; - case BL_SPELL: - return "%invocation(ERROR:this-should-not-be-an-entity)"; - default: - return "%unknown-entity"; - } -} - -static void stringify (val_t * v, int within_op) -{ - static char *dirs[8] = - { "south", "south-west", "west", "north-west", "north", "north-east", - "east", "south-east" - }; - char *buf; - - switch (v->ty) - { - case TY_UNDEF: - buf = strdup ("UNDEF"); - break; - - case TY_INT: - buf = (char *)malloc (32); - sprintf (buf, "%i", v->v.v_int); - break; - - case TY_STRING: - return; - - case TY_DIR: - buf = strdup (dirs[v->v.v_int]); - break; - - case TY_ENTITY: - buf = strdup (show_entity (v->v.v_entity)); - break; - - case TY_LOCATION: - buf = (char *) malloc (128); - sprintf (buf, "<\"%s\", %d, %d>", map[v->v.v_location.m].name, - v->v.v_location.x, v->v.v_location.y); - break; - - case TY_AREA: - buf = strdup ("%area"); - free_area (v->v.v_area); - break; - - case TY_SPELL: - buf = strdup (v->v.v_spell->name); - break; - - case TY_INVOCATION: - { - invocation_t *invocation = within_op - ? v->v.v_invocation : (invocation_t *) map_id2bl (v->v.v_int); - buf = strdup (invocation->spell->name); - } - break; - - default: - fprintf (stderr, "[magic] INTERNAL ERROR: Cannot stringify %d\n", - v->ty); - return; - } - - v->v.v_string = buf; - v->ty = TY_STRING; -} - -static void intify (val_t * v) -{ - if (v->ty == TY_INT) - return; - - magic_clear_var (v); - v->ty = TY_INT; - v->v.v_int = 1; -} - -area_t *area_new (int ty) -{ - area_t *retval; - CREATE (retval, area_t, 1); - retval->ty = ty; - return retval; -} - -area_t *area_union (area_t * area, area_t * other_area) -{ - area_t *retval = area_new (AREA_UNION); - retval->a.a_union[0] = area; - retval->a.a_union[1] = other_area; - retval->size = area->size + other_area->size; /* Assume no overlap */ - return retval; -} - -/** - * Turns location into area, leaves other types untouched - */ -static void make_area (val_t * v) -{ - if (v->ty == TY_LOCATION) - { - area_t *a = (char *)malloc (sizeof (area_t)); - v->ty = TY_AREA; - a->ty = AREA_LOCATION; - a->a.a_loc = v->v.v_location; - v->v.v_area = a; - } -} - -static void make_location (val_t * v) -{ - if (v->ty == TY_AREA && v->v.v_area->ty == AREA_LOCATION) - { - location_t location = v->v.v_area->a.a_loc; - free_area (v->v.v_area); - v->ty = TY_LOCATION; - v->v.v_location = location; - } -} - -static void make_spell (val_t * v) -{ - if (v->ty == TY_INVOCATION) - { - invocation_t *invoc = v->v.v_invocation; //(invocation_t *) map_id2bl(v->v.v_int); - if (!invoc) - v->ty = TY_FAIL; - else - { - v->ty = TY_SPELL; - v->v.v_spell = invoc->spell; - } - } -} - -static int fun_add (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (TY (0) == TY_INT && TY (1) == TY_INT) - { - /* Integer addition */ - RESULTINT = ARGINT (0) + ARGINT (1); - result->ty = TY_INT; - } - else if (ARG_MAY_BE_AREA (0) && ARG_MAY_BE_AREA (1)) - { - /* Area union */ - make_area (&args[0]); - make_area (&args[1]); - RESULTAREA = area_union (ARGAREA (0), ARGAREA (1)); - ARGAREA (0) = NULL; - ARGAREA (1) = NULL; - result->ty = TY_AREA; - } - else - { - /* Anything else -> string concatenation */ - stringify (&args[0], 1); - stringify (&args[1], 1); - /* Yes, we could speed this up. */ - RESULTSTR = - (char *) malloc (1 + strlen (ARGSTR (0)) + strlen (ARGSTR (1))); - strcpy (RESULTSTR, ARGSTR (0)); - strcat (RESULTSTR, ARGSTR (1)); - result->ty = TY_STRING; - } - return 0; -} - -static int fun_sub (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) - ARGINT (1); - return 0; -} - -static int fun_mul (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) * ARGINT (1); - return 0; -} - -static int fun_div (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (!ARGINT (1)) - return 1; /* division by zero */ - RESULTINT = ARGINT (0) / ARGINT (1); - return 0; -} - -static int fun_mod (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (!ARGINT (1)) - return 1; /* division by zero */ - RESULTINT = ARGINT (0) % ARGINT (1); - return 0; -} - -static int fun_or (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) || ARGINT (1); - return 0; -} - -static int fun_and (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) && ARGINT (1); - return 0; -} - -static int fun_not (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = !ARGINT (0); - return 0; -} - -static int fun_neg (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ~ARGINT (0); - return 0; -} - -static int fun_gte (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (TY (0) == TY_STRING || TY (1) == TY_STRING) - { - stringify (&args[0], 1); - stringify (&args[1], 1); - RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) >= 0; - } - else - { - intify (&args[0]); - intify (&args[1]); - RESULTINT = ARGINT (0) >= ARGINT (1); - } - return 0; -} - -static int fun_gt (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (TY (0) == TY_STRING || TY (1) == TY_STRING) - { - stringify (&args[0], 1); - stringify (&args[1], 1); - RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) > 0; - } - else - { - intify (&args[0]); - intify (&args[1]); - RESULTINT = ARGINT (0) > ARGINT (1); - } - return 0; -} - -static int fun_eq (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (TY (0) == TY_STRING || TY (1) == TY_STRING) - { - stringify (&args[0], 1); - stringify (&args[1], 1); - RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) == 0; - } - else if (TY (0) == TY_DIR && TY (1) == TY_DIR) - RESULTINT = ARGDIR (0) == ARGDIR (1); - else if (TY (0) == TY_ENTITY && TY (1) == TY_ENTITY) - RESULTINT = ARGENTITY (0) == ARGENTITY (1); - else if (TY (0) == TY_LOCATION && TY (1) == TY_LOCATION) - RESULTINT = (ARGLOCATION (0).x == ARGLOCATION (1).x - && ARGLOCATION (0).y == ARGLOCATION (1).y - && ARGLOCATION (0).m == ARGLOCATION (1).m); - else if (TY (0) == TY_AREA && TY (1) == TY_AREA) - RESULTINT = ARGAREA (0) == ARGAREA (1); /* Probably not that great an idea... */ - else if (TY (0) == TY_SPELL && TY (1) == TY_SPELL) - RESULTINT = ARGSPELL (0) == ARGSPELL (1); - else if (TY (0) == TY_INVOCATION && TY (1) == TY_INVOCATION) - RESULTINT = ARGINVOCATION (0) == ARGINVOCATION (1); - else - { - intify (&args[0]); - intify (&args[1]); - RESULTINT = ARGINT (0) == ARGINT (1); - } - return 0; -} - -static int fun_bitand (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) & ARGINT (1); - return 0; -} - -static int fun_bitor (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) | ARGINT (1); - return 0; -} - -static int fun_bitxor (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) ^ ARGINT (1); - return 0; -} - -static int fun_bitshl (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) << ARGINT (1); - return 0; -} - -static int fun_bitshr (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGINT (0) >> ARGINT (1); - return 0; -} - -static int fun_max (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = MAX (ARGINT (0), ARGINT (1)); - return 0; -} - -static int fun_min (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = MIN (ARGINT (0), ARGINT (1)); - return 0; -} - -static int -fun_if_then_else (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ARGINT (0)) - magic_copy_var (result, &args[1]); - else - magic_copy_var (result, &args[2]); - return 0; -} - -void -magic_area_rect (int *m, int *x, int *y, int *width, int *height, - area_t * area) -{ - switch (area->ty) - { - case AREA_UNION: - break; - - case AREA_LOCATION: - *m = area->a.a_loc.m; - *x = area->a.a_loc.x; - *y = area->a.a_loc.y; - *width = 1; - *height = 1; - break; - - case AREA_RECT: - *m = area->a.a_rect.loc.m; - *x = area->a.a_rect.loc.x; - *y = area->a.a_rect.loc.y; - *width = area->a.a_rect.width; - *height = area->a.a_rect.height; - break; - - case AREA_BAR: - { - int tx = area->a.a_bar.loc.x; - int ty = area->a.a_bar.loc.y; - int twidth = area->a.a_bar.width; - int tdepth = area->a.a_bar.width; - *m = area->a.a_bar.loc.m; - - switch (area->a.a_bar.dir) - { - case DIR_S: - *x = tx - twidth; - *y = ty; - *width = twidth * 2 + 1; - *height = tdepth; - break; - - case DIR_W: - *x = tx - tdepth; - *y = ty - twidth; - *width = tdepth; - *height = twidth * 2 + 1; - break; - - case DIR_N: - *x = tx - twidth; - *y = ty - tdepth; - *width = twidth * 2 + 1; - *height = tdepth; - break; - - case DIR_E: - *x = tx; - *y = ty - twidth; - *width = tdepth; - *height = twidth * 2 + 1; - break; - - default: - fprintf (stderr, - "Error: Trying to compute area of NE/SE/NW/SW-facing bar"); - *x = tx; - *y = ty; - *width = *height = 1; - } - break; - } - } -} - -int magic_location_in_area (int m, int x, int y, area_t * area) -{ - switch (area->ty) - { - case AREA_UNION: - return magic_location_in_area (m, x, y, area->a.a_union[0]) - || magic_location_in_area (m, x, y, area->a.a_union[1]); - case AREA_LOCATION: - case AREA_RECT: - case AREA_BAR: - { - int am; - int ax, ay, awidth, aheight; - magic_area_rect (&am, &ax, &ay, &awidth, &aheight, area); - return (am == m - && (x >= ax) && (y >= ay) - && (x < ax + awidth) && (y < ay + aheight)); - } - default: - fprintf (stderr, "INTERNAL ERROR: Invalid area\n"); - return 0; - } -} - -static int fun_is_in (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = magic_location_in_area (ARGLOCATION (0).m, - ARGLOCATION (0).x, - ARGLOCATION (0).y, ARGAREA (1)); - return 0; -} - -static int fun_skill (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ETY (0) != BL_PC - || ARGINT (1) < 0 - || ARGINT (1) >= MAX_SKILL - || ARGPC (0)->status.skill[ARGINT (1)].id != ARGINT (1)) - RESULTINT = 0; - else - RESULTINT = ARGPC (0)->status.skill[ARGINT (1)].lv; - return 0; -} - -static int -fun_has_shroud (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->state.shroud_active); - return 0; -} - -#define BATTLE_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { RESULTINT = battle_get_##name(ARGENTITY(0)); return 0; } - -BATTLE_GETTER (str); -BATTLE_GETTER (agi); -BATTLE_GETTER (vit); -BATTLE_GETTER (dex); -BATTLE_GETTER (luk); -BATTLE_GETTER (int); -BATTLE_GETTER (lv); -BATTLE_GETTER (hp); -BATTLE_GETTER (mdef); -BATTLE_GETTER (def); -BATTLE_GETTER (max_hp); -BATTLE_GETTER (dir); - -#define MMO_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { \ - if (ETY(0) == BL_PC) \ - RESULTINT = ARGPC(0)->status.name; \ - else \ - RESULTINT = 0; \ - return 0; } - -MMO_GETTER (sp); -MMO_GETTER (max_sp); - -static int -fun_name_of (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (TY (0) == TY_ENTITY) - { - RESULTSTR = strdup (show_entity (ARGENTITY (0))); - return 0; - } - else if (TY (0) == TY_SPELL) - { - RESULTSTR = strdup (ARGSPELL (0)->name); - return 0; - } - else if (TY (0) == TY_INVOCATION) - { - RESULTSTR = strdup (ARGINVOCATION (0)->spell->name); - return 0; - } - return 1; -} - -/* [Freeyorp] I'm putting this one in as name_of seems to have issues with summoned or spawned mobs. */ -static int -fun_mob_id (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ETY (0) != BL_MOB) return 1; - RESULTINT = ((struct mob_data *) (ARGENTITY(0)))->mob_class; - return 0; -} - -#define COPY_LOCATION(dest, src) (dest).x = (src).x; (dest).y = (src).y; (dest).m = (src).m; - -static int -fun_location (env_t * env, int args_nr, val_t * result, val_t * args) -{ - COPY_LOCATION (RESULTLOCATION, *(ARGENTITY (0))); - return 0; -} - -static int fun_random (env_t * env, int args_nr, val_t * result, val_t * args) -{ - int delta = ARGINT (0); - if (delta < 0) - delta = -delta; - if (delta == 0) - { - RESULTINT = 0; - return 0; - } - RESULTINT = MRAND (delta); - - if (ARGINT (0) < 0) - RESULTINT = -RESULTINT; - return 0; -} - -static int -fun_random_dir (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ARGINT (0)) - RESULTDIR = mt_random () & 0x7; - else - RESULTDIR = (mt_random () & 0x3) * 2; - return 0; -} - -static int -fun_hash_entity (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGENTITY (0)->id; - return 0; -} - -int // ret -1: not a string, ret 1: no such item, ret 0: OK -magic_find_item (val_t * args, int index, struct item *item, int *stackable) -{ - struct item_data *item_data; - int must_add_sequentially; - - if (TY (index) == TY_INT) - item_data = itemdb_exists (ARGINT (index)); - else if (TY (index) == TY_STRING) - item_data = itemdb_searchname (ARGSTR (index)); - else - return -1; - - if (!item_data) - return 1; - - must_add_sequentially = (item_data->type == 4 || item_data->type == 5 || item_data->type == 7 || item_data->type == 8); /* Very elegant. */ - - if (stackable) - *stackable = !must_add_sequentially; - - memset (item, 0, sizeof (struct item)); - item->nameid = item_data->nameid; - item->identify = 1; - - return 0; -} - -static int -fun_count_item (env_t * env, int args_nr, val_t * result, val_t * args) -{ - character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - int stackable; - struct item item; - - GET_ARG_ITEM (1, item, stackable); - - if (!chr) - return 1; - - RESULTINT = pc_count_all_items (chr, item.nameid); - return 0; -} - -static int -fun_is_equipped (env_t * env, int args_nr, val_t * result, val_t * args) -{ - character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - int stackable; - struct item item; - int i; - int retval = 0; - - GET_ARG_ITEM (1, item, stackable); - - if (!chr) - return 1; - - for (i = 0; i < 11; i++) - if (chr->equip_index[i] >= 0 - && chr->status.inventory[chr->equip_index[i]].nameid == - item.nameid) - { - retval = i + 1; - break; - } - - RESULTINT = retval; - return 0; -} - -static int -fun_is_married (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->status.partner_id); - return 0; -} - -static int -fun_is_dead (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = (ETY (0) == BL_PC && pc_isdead (ARGPC (0))); - return 0; -} - -static int fun_is_pc (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = (ETY (0) == BL_PC); - return 0; -} - -static int -fun_partner (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ETY (0) == BL_PC && ARGPC (0)->status.partner_id) - { - RESULTENTITY = - (entity_t *) - map_nick2sd (map_charid2nick (ARGPC (0)->status.partner_id)); - return 0; - } - else - return 1; -} - -static int -fun_awayfrom (env_t * env, int args_nr, val_t * result, val_t * args) -{ - location_t *loc = &ARGLOCATION (0); - int dx = heading_x[ARGDIR (1)]; - int dy = heading_y[ARGDIR (1)]; - int distance = ARGINT (2); - while (distance-- && !map_is_solid (loc->m, loc->x + dx, loc->y + dy)) - { - loc->x += dx; - loc->y += dy; - } - - RESULTLOCATION = *loc; - return 0; -} - -static int fun_failed (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = TY (0) == TY_FAIL; - return 0; -} - -static int fun_npc (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTENTITY = (entity_t *) npc_name2id (ARGSTR (0)); - return RESULTENTITY == NULL; -} - -static int fun_pc (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTENTITY = (entity_t *) map_nick2sd (ARGSTR (0)); - return RESULTENTITY == NULL; -} - -static int -fun_distance (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ARGLOCATION (0).m != ARGLOCATION (1).m) - RESULTINT = INT_MAX; - else - RESULTINT = MAX (abs (ARGLOCATION (0).x - ARGLOCATION (1).x), - abs (ARGLOCATION (0).y - ARGLOCATION (1).y)); - return 0; -} - -static int -fun_rdistance (env_t * env, int args_nr, val_t * result, val_t * args) -{ - if (ARGLOCATION (0).m != ARGLOCATION (1).m) - RESULTINT = INT_MAX; - else - { - int dx = ARGLOCATION (0).x - ARGLOCATION (1).x; - int dy = ARGLOCATION (0).y - ARGLOCATION (1).y; - RESULTINT = (int) (sqrt ((dx * dx) + (dy * dy))); - } - return 0; -} - -static int fun_anchor (env_t * env, int args_nr, val_t * result, val_t * args) -{ - teleport_anchor_t *anchor = magic_find_anchor (ARGSTR (0)); - - if (!anchor) - return 1; - - magic_eval (env, result, anchor->location); - - make_area (result); - if (result->ty != TY_AREA) - { - magic_clear_var (result); - return 1; - } - - return 0; -} - -static int -fun_line_of_sight (env_t * env, int args_nr, val_t * result, val_t * args) -{ - entity_t e1, e2; - - COPY_LOCATION (e1, ARGLOCATION (0)); - COPY_LOCATION (e2, ARGLOCATION (1)); - - RESULTINT = battle_check_range (&e1, &e2, 0); - - return 0; -} - -void magic_random_location (location_t * dest, area_t * area) -{ - switch (area->ty) - { - case AREA_UNION: - { - int rv = MRAND (area->size); - if (rv < area->a.a_union[0]->size) - magic_random_location (dest, area->a.a_union[0]); - else - magic_random_location (dest, area->a.a_union[1]); - break; - } - - case AREA_LOCATION: - case AREA_RECT: - case AREA_BAR: - { - int m, x, y, w, h; - magic_area_rect (&m, &x, &y, &w, &h, area); - - if (w <= 1) - w = 1; - - if (h <= 1) - h = 1; - - x += MRAND (w); - y += MRAND (h); - - if (!map_is_solid (m, x, y)) - { - int start_x = x; - int start_y = y; - int i; - int initial_dir = mt_random () & 0x7; - int dir = initial_dir; - - /* try all directions, up to a distance to 10, for a free slot */ - do - { - x = start_x; - y = start_y; - - for (i = 0; i < 10 && map_is_solid (m, x, y); i++) - { - x += heading_x[dir]; - y += heading_y[dir]; - } - - dir = (dir + 1) & 0x7; - } - while (map_is_solid (m, x, y) && dir != initial_dir); - - } - /* We've tried our best. If the map is still solid, the engine will automatically randomise the target location if we try to warp. */ - - dest->m = m; - dest->x = x; - dest->y = y; - break; - } - - default: - fprintf (stderr, "Unknown area type %d\n", area->ty); - } -} - -static int -fun_pick_location (env_t * env, int args_nr, val_t * result, val_t * args) -{ - magic_random_location (&result->v.v_location, ARGAREA (0)); - return 0; -} - -static int -fun_read_script_int (env_t * env, int args_nr, val_t * result, val_t * args) -{ - entity_t *subject_p = ARGENTITY (0); - char *var_name = ARGSTR (1); - - if (subject_p->type != BL_PC) - return 1; - - RESULTINT = pc_readglobalreg ((character_t *) subject_p, var_name); - return 0; -} - -static int fun_rbox (env_t * env, int args_nr, val_t * result, val_t * args) -{ - location_t loc = ARGLOCATION (0); - int radius = ARGINT (1); - - RESULTAREA = area_new (AREA_RECT); - RESULTAREA->a.a_rect.loc.m = loc.m; - RESULTAREA->a.a_rect.loc.x = loc.x - radius; - RESULTAREA->a.a_rect.loc.y = loc.y - radius; - RESULTAREA->a.a_rect.width = radius * 2 + 1; - RESULTAREA->a.a_rect.height = radius * 2 + 1; - - return 0; -} - -static int -fun_running_status_update (env_t * env, int args_nr, val_t * result, - val_t * args) -{ - if (ETY (0) != BL_PC && ETY (0) != BL_MOB) - return 1; - - RESULTINT = battle_get_sc_data (ARGENTITY (0))[ARGINT (1)].timer != -1; - return 0; -} - -static int -fun_status_option (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = - ((((struct map_session_data *) ARGENTITY (0))-> - status.option & ARGINT (0)) != 0); - return 0; -} - -static int -fun_element (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = battle_get_element (ARGENTITY (0)) % 10; - return 0; -} - -static int -fun_element_level (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = battle_get_element (ARGENTITY (0)) / 10; - return 0; -} - -static int fun_index (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = ARGSPELL (0)->index; - return 0; -} - -static int -fun_is_exterior (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = map[ARGLOCATION (0).m].name[4] == '1'; - return 0; -} - -static int -fun_contains_string (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = NULL != strstr (ARGSTR (0), ARGSTR (1)); - return 0; -} - -static int fun_strstr (env_t * env, int args_nr, val_t * result, val_t * args) -{ - char *offset = strstr (ARGSTR (0), ARGSTR (1)); - RESULTINT = offset - ARGSTR (0); - return offset == NULL; -} - -static int fun_strlen (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = strlen (ARGSTR (0)); - return 0; -} - -static int fun_substr (env_t * env, int args_nr, val_t * result, val_t * args) -{ - const char *src = ARGSTR (0); - const int slen = strlen (src); - int offset = ARGINT (1); - int len = ARGINT (2); - - if (len < 0) - len = 0; - if (offset < 0) - offset = 0; - - if (offset > slen) - offset = slen; - - if (offset + len > slen) - len = slen - offset; - - RESULTSTR = (char *) calloc (1, 1 + len); - memcpy (RESULTSTR, src + offset, len); - - return 0; -} - -static int fun_sqrt (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = (int) sqrt (ARGINT (0)); - return 0; -} - -static int -fun_map_level (env_t * env, int args_nr, val_t * result, val_t * args) -{ - RESULTINT = map[ARGLOCATION (0).m].name[4] - '0'; - return 0; -} - -static int fun_map_nr (env_t * env, int args_nr, val_t * result, val_t * args) -{ - const char *mapname = map[ARGLOCATION (0).m].name; - - RESULTINT = ((mapname[0] - '0') * 100) - + ((mapname[1] - '0') * 10) + ((mapname[2] - '0')); - return 0; -} - -static int -fun_dir_towards (env_t * env, int args_nr, val_t * result, val_t * args) -{ - int dx; - int dy; - - if (ARGLOCATION (0).m != ARGLOCATION (1).m) - return 1; - - dx = ARGLOCATION (1).x - ARGLOCATION (0).x; - dy = ARGLOCATION (1).y - ARGLOCATION (0).y; - - if (ARGINT (1)) - { - /* 8-direction mode */ - if (abs (dx) > abs (dy) * 2) - { /* east or west */ - if (dx < 0) - RESULTINT = 2 /* west */ ; - else - RESULTINT = 6 /* east */ ; - } - else if (abs (dy) > abs (dx) * 2) - { /* north or south */ - if (dy > 0) - RESULTINT = 0 /* south */ ; - else - RESULTINT = 4 /* north */ ; - } - else if (dx < 0) - { /* north-west or south-west */ - if (dy < 0) - RESULTINT = 3 /* north-west */ ; - else - RESULTINT = 1 /* south-west */ ; - } - else - { /* north-east or south-east */ - if (dy < 0) - RESULTINT = 5 /* north-east */ ; - else - RESULTINT = 7 /* south-east */ ; - } - } - else - { - /* 4-direction mode */ - if (abs (dx) > abs (dy)) - { /* east or west */ - if (dx < 0) - RESULTINT = 2 /* west */ ; - else - RESULTINT = 6 /* east */ ; - } - else - { /* north or south */ - if (dy > 0) - RESULTINT = 0 /* south */ ; - else - RESULTINT = 4 /* north */ ; - } - } - - return 0; -} - -static int -fun_extract_healer_xp (env_t * env, int args_nr, val_t * result, val_t * args) -{ - character_t *sd = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - - if (!sd) - RESULTINT = 0; - else - RESULTINT = pc_extract_healer_exp (sd, ARGINT (1)); - return 0; -} - -#define BATTLE_RECORD2(sname, name) { sname, "e", 'i', fun_get_##name } -#define BATTLE_RECORD(name) BATTLE_RECORD2(#name, name) -static fun_t functions[] = { - {"+", "..", '.', fun_add}, - {"-", "ii", 'i', fun_sub}, - {"*", "ii", 'i', fun_mul}, - {"/", "ii", 'i', fun_div}, - {"%", "ii", 'i', fun_mod}, - {"||", "ii", 'i', fun_or}, - {"&&", "ii", 'i', fun_and}, - {">", "..", 'i', fun_gt}, - {">=", "..", 'i', fun_gte}, - {"=", "..", 'i', fun_eq}, - {"|", "..", 'i', fun_bitor}, - {"&", "ii", 'i', fun_bitand}, - {"^", "ii", 'i', fun_bitxor}, - {"<<", "ii", 'i', fun_bitshl}, - {">>", "ii", 'i', fun_bitshr}, - {"not", "i", 'i', fun_not}, - {"neg", "i", 'i', fun_neg}, - {"max", "ii", 'i', fun_max}, - {"min", "ii", 'i', fun_min}, - {"is_in", "la", 'i', fun_is_in}, - {"if_then_else", "i__", '_', fun_if_then_else}, - {"skill", "ei", 'i', fun_skill}, - BATTLE_RECORD (str), - BATTLE_RECORD (agi), - BATTLE_RECORD (vit), - BATTLE_RECORD (dex), - BATTLE_RECORD (luk), - BATTLE_RECORD (int), - BATTLE_RECORD2 ("level", lv), - BATTLE_RECORD (mdef), - BATTLE_RECORD (def), - BATTLE_RECORD (hp), - BATTLE_RECORD (max_hp), - BATTLE_RECORD (sp), - BATTLE_RECORD (max_sp), - {"dir", "e", 'd', fun_get_dir}, - {"name_of", ".", 's', fun_name_of}, - {"mob_id", "e", 'i', fun_mob_id}, - {"location", "e", 'l', fun_location}, - {"random", "i", 'i', fun_random}, - {"random_dir", "i", 'd', fun_random_dir}, - {"hash_entity", "e", 'i', fun_hash_entity}, - {"is_married", "e", 'i', fun_is_married}, - {"partner", "e", 'e', fun_partner}, - {"awayfrom", "ldi", 'l', fun_awayfrom}, - {"failed", "_", 'i', fun_failed}, - {"pc", "s", 'e', fun_pc}, - {"npc", "s", 'e', fun_npc}, - {"distance", "ll", 'i', fun_distance}, - {"rdistance", "ll", 'i', fun_rdistance}, - {"anchor", "s", 'a', fun_anchor}, - {"random_location", "a", 'l', fun_pick_location}, - {"script_int", "es", 'i', fun_read_script_int}, - {"rbox", "li", 'a', fun_rbox}, - {"count_item", "e.", 'i', fun_count_item}, - {"line_of_sight", "ll", 'i', fun_line_of_sight}, - {"running_status_update", "ei", 'i', fun_running_status_update}, - {"status_option", "ei", 'i', fun_status_option}, - {"element", "e", 'i', fun_element}, - {"element_level", "e", 'i', fun_element_level}, - {"has_shroud", "e", 'i', fun_has_shroud}, - {"is_equipped", "e.", 'i', fun_is_equipped}, - {"spell_index", "S", 'i', fun_index}, - {"is_exterior", "l", 'i', fun_is_exterior}, - {"contains_string", "ss", 'i', fun_contains_string}, - {"strstr", "ss", 'i', fun_strstr}, - {"strlen", "s", 'i', fun_strlen}, - {"substr", "sii", 's', fun_substr}, - {"sqrt", "i", 'i', fun_sqrt}, - {"map_level", "l", 'i', fun_map_level}, - {"map_nr", "l", 'i', fun_map_nr}, - {"dir_towards", "lli", 'd', fun_dir_towards}, - {"is_dead", "e", 'i', fun_is_dead}, - {"is_pc", "e", 'i', fun_is_pc}, - {"extract_healer_experience", "ei", 'i', fun_extract_healer_xp}, - {NULL, NULL, '.', NULL} -}; - -static int functions_are_sorted = 0; - -int compare_fun (const void *lhs, const void *rhs) -{ - return strcmp (((fun_t *) lhs)->name, ((fun_t *) rhs)->name); -} - -fun_t *magic_get_fun (const char *name, int *index) -{ - static int functions_nr; - fun_t *result; - fun_t key; - - if (!functions_are_sorted) - { - fun_t *it = functions; - - while (it->name) - ++it; - functions_nr = it - functions; - - qsort (functions, functions_nr, sizeof (fun_t), compare_fun); - functions_are_sorted = 1; - } - - key.name = name; - result = (fun_t *) bsearch (&key, functions, functions_nr, sizeof (fun_t), - compare_fun); - - if (result && index) - *index = result - functions; - - return result; -} - -static int // 1 on failure -eval_location (env_t * env, location_t * dest, e_location_t * expr) -{ - val_t m, x, y; - magic_eval (env, &m, expr->m); - magic_eval (env, &x, expr->x); - magic_eval (env, &y, expr->y); - - if (CHECK_TYPE (&m, TY_STRING) - && CHECK_TYPE (&x, TY_INT) && CHECK_TYPE (&y, TY_INT)) - { - int map_id = map_mapname2mapid (m.v.v_string); - magic_clear_var (&m); - if (map_id < 0) - return 1; - dest->m = map_id; - dest->x = x.v.v_int; - dest->y = y.v.v_int; - return 0; - } - else - { - magic_clear_var (&m); - magic_clear_var (&x); - magic_clear_var (&y); - return 1; - } -} - -static area_t *eval_area (env_t * env, e_area_t * expr) -{ - area_t *area = (area_t *)malloc (sizeof (area_t)); - area->ty = expr->ty; - - switch (expr->ty) - { - case AREA_LOCATION: - area->size = 1; - if (eval_location (env, &area->a.a_loc, &expr->a.a_loc)) - { - free (area); - return NULL; - } - else - return area; - - case AREA_UNION: - { - int i, fail = 0; - for (i = 0; i < 2; i++) - { - area->a.a_union[i] = eval_area (env, expr->a.a_union[i]); - if (!area->a.a_union[i]) - fail = 1; - } - - if (fail) - { - for (i = 0; i < 2; i++) - { - if (area->a.a_union[i]) - free_area (area->a.a_union[i]); - } - free (area); - return NULL; - } - area->size = area->a.a_union[0]->size + area->a.a_union[1]->size; - return area; - } - - case AREA_RECT: - { - val_t width, height; - magic_eval (env, &width, expr->a.a_rect.width); - magic_eval (env, &height, expr->a.a_rect.height); - - area->a.a_rect.width = width.v.v_int; - area->a.a_rect.height = height.v.v_int; - - if (CHECK_TYPE (&width, TY_INT) - && CHECK_TYPE (&height, TY_INT) - && !eval_location (env, &(area->a.a_rect.loc), - &expr->a.a_rect.loc)) - { - area->size = area->a.a_rect.width * area->a.a_rect.height; - magic_clear_var (&width); - magic_clear_var (&height); - return area; - } - else - { - free (area); - magic_clear_var (&width); - magic_clear_var (&height); - return NULL; - } - } - - case AREA_BAR: - { - val_t width, depth, dir; - magic_eval (env, &width, expr->a.a_bar.width); - magic_eval (env, &depth, expr->a.a_bar.depth); - magic_eval (env, &dir, expr->a.a_bar.dir); - - area->a.a_bar.width = width.v.v_int; - area->a.a_bar.depth = depth.v.v_int; - area->a.a_bar.dir = dir.v.v_int; - - if (CHECK_TYPE (&width, TY_INT) - && CHECK_TYPE (&depth, TY_INT) - && CHECK_TYPE (&dir, TY_DIR) - && !eval_location (env, &area->a.a_bar.loc, - &expr->a.a_bar.loc)) - { - area->size = - (area->a.a_bar.width * 2 + 1) * area->a.a_bar.depth; - magic_clear_var (&width); - magic_clear_var (&depth); - magic_clear_var (&dir); - return area; - } - else - { - free (area); - magic_clear_var (&width); - magic_clear_var (&depth); - magic_clear_var (&dir); - return NULL; - } - } - - default: - fprintf (stderr, "INTERNAL ERROR: Unknown area type %d\n", - area->ty); - free (area); - return NULL; - } -} - -static int type_key (char ty_key) -{ - switch (ty_key) - { - case 'i': - return TY_INT; - case 'd': - return TY_DIR; - case 's': - return TY_STRING; - case 'e': - return TY_ENTITY; - case 'l': - return TY_LOCATION; - case 'a': - return TY_AREA; - case 'S': - return TY_SPELL; - case 'I': - return TY_INVOCATION; - default: - return -1; - } -} - -int -magic_signature_check (char *opname, char *funname, char *signature, - int args_nr, val_t * args, int line, int column) -{ - int i; - for (i = 0; i < args_nr; i++) - { - val_t *arg = &args[i]; - char ty_key = signature[i]; - int ty = arg->ty; - int desired_ty = type_key (ty_key); - - if (ty == TY_ENTITY) - { - /* Dereference entities in preparation for calling function */ - arg->v.v_entity = map_id2bl (arg->v.v_int); - if (!arg->v.v_entity) - ty = arg->ty = TY_FAIL; - } - else if (ty == TY_INVOCATION) - { - arg->v.v_invocation = (invocation_t *) map_id2bl (arg->v.v_int); - if (!arg->v.v_entity) - ty = arg->ty = TY_FAIL; - } - - if (!ty_key) - { - fprintf (stderr, - "[magic-eval]: L%d:%d: Too many arguments (%d) to %s `%s'\n", - line, column, args_nr, opname, funname); - return 1; - } - - if (ty == TY_FAIL && ty_key != '_') - return 1; /* Fail `in a sane way': This is a perfectly permissible error */ - - if (ty == desired_ty || desired_ty < 0 /* `dontcare' */ ) - continue; - - if (ty == TY_UNDEF) - { - fprintf (stderr, - "[magic-eval]: L%d:%d: Argument #%d to %s `%s' undefined\n", - line, column, i + 1, opname, funname); - return 1; - } - - /* If we are here, we have a type mismatch but no failure _yet_. Try to coerce. */ - switch (desired_ty) - { - case TY_INT: - intify (arg); - break; /* 100% success rate */ - case TY_STRING: - stringify (arg, 1); - break; /* 100% success rate */ - case TY_AREA: - make_area (arg); - break; /* Only works for locations */ - case TY_LOCATION: - make_location (arg); - break; /* Only works for some areas */ - case TY_SPELL: - make_spell (arg); - break; /* Only works for still-active invocatoins */ - default: - break; /* We'll fail right below */ - } - - ty = arg->ty; - if (ty != desired_ty) - { /* Coercion failed? */ - if (ty != TY_FAIL) - fprintf (stderr, - "[magic-eval]: L%d:%d: Argument #%d to %s `%s' of incorrect type (%d)\n", - line, column, i + 1, opname, funname, ty); - return 1; - } - } - - return 0; -} - -void magic_eval (env_t * env, val_t * dest, expr_t * expr) -{ - switch (expr->ty) - { - case EXPR_VAL: - magic_copy_var (dest, &expr->e.e_val); - break; - - case EXPR_LOCATION: - if (eval_location (env, &dest->v.v_location, &expr->e.e_location)) - dest->ty = TY_FAIL; - else - dest->ty = TY_LOCATION; - break; - - case EXPR_AREA: - if ((dest->v.v_area = eval_area (env, &expr->e.e_area))) - dest->ty = TY_AREA; - else - dest->ty = TY_FAIL; - break; - - case EXPR_FUNAPP: - { - val_t arguments[MAX_ARGS]; - int args_nr = expr->e.e_funapp.args_nr; - int i; - fun_t *f = functions + expr->e.e_funapp.id; - - for (i = 0; i < args_nr; ++i) - magic_eval (env, &arguments[i], expr->e.e_funapp.args[i]); - if (magic_signature_check - ("function", f->name, f->signature, args_nr, arguments, - expr->e.e_funapp.line_nr, expr->e.e_funapp.column) - || f->fun (env, args_nr, dest, arguments)) - dest->ty = TY_FAIL; - else - { - int dest_ty = type_key (f->ret_ty); - if (dest_ty != -1) - dest->ty = dest_ty; - - /* translate entity back into persistent int */ - if (dest->ty == TY_ENTITY) - { - if (dest->v.v_entity) - dest->v.v_int = dest->v.v_entity->id; - else - dest->ty = TY_FAIL; - } - } - - for (i = 0; i < args_nr; ++i) - magic_clear_var (&arguments[i]); - break; - } - - case EXPR_ID: - { - val_t v = VAR (expr->e.e_id); - magic_copy_var (dest, &v); - break; - } - - case EXPR_SPELLFIELD: - { - val_t v; - int id = expr->e.e_field.id; - magic_eval (env, &v, expr->e.e_field.expr); - - if (v.ty == TY_INVOCATION) - { - invocation_t *t = (invocation_t *) map_id2bl (v.v.v_int); - - if (!t) - dest->ty = TY_UNDEF; - else - { - env_t *env = t->env; - val_t v = VAR (id); - magic_copy_var (dest, &v); - } - } - else - { - fprintf (stderr, - "[magic] Attempt to access field %s on non-spell\n", - env->base_env->var_name[id]); - dest->ty = TY_FAIL; - } - break; - } - - default: - fprintf (stderr, - "[magic] INTERNAL ERROR: Unknown expression type %d\n", - expr->ty); - break; - } -} - -int magic_eval_int (env_t * env, expr_t * expr) -{ - val_t result; - magic_eval (env, &result, expr); - - if (result.ty == TY_FAIL || result.ty == TY_UNDEF) - return 0; - - intify (&result); - - return result.v.v_int; -} - -char *magic_eval_str (env_t * env, expr_t * expr) -{ - val_t result; - magic_eval (env, &result, expr); - - if (result.ty == TY_FAIL || result.ty == TY_UNDEF) - return strdup ("?"); - - stringify (&result, 0); - - return result.v.v_string; -} - -expr_t *magic_new_expr (int ty) -{ - expr_t *expr = (expr_t *) malloc (sizeof (expr_t)); - expr->ty = ty; - return expr; -} diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp new file mode 100644 index 0000000..d75042d --- /dev/null +++ b/src/map/magic-expr.cpp @@ -0,0 +1,1655 @@ +#include "magic-expr.hpp" +#include "magic-expr-eval.hpp" +#include "itemdb.hpp" +#include <math.h> + +#include "../common/mt_rand.hpp" + +#define IS_SOLID(c) ((c) == 1 || (c) == 5) + +int map_is_solid (int m, int x, int y) +{ + return (IS_SOLID (map_getcell (m, x, y))); +} + +#undef IS_SOLID + +static void free_area (area_t * area) +{ + if (!area) + return; + + switch (area->ty) + { + case AREA_UNION: + free_area (area->a.a_union[0]); + free_area (area->a.a_union[1]); + break; + default: + break; + } + + free (area); +} + +static area_t *dup_area (area_t * area) +{ + area_t *retval = (area_t *)malloc (sizeof (area_t)); + *retval = *area; + + switch (area->ty) + { + case AREA_UNION: + retval->a.a_union[0] = dup_area (retval->a.a_union[0]); + retval->a.a_union[1] = dup_area (retval->a.a_union[1]); + break; + default: + break; + } + + return retval; +} + +void magic_copy_var (val_t * dest, val_t * src) +{ + *dest = *src; + + switch (dest->ty) + { + case TY_STRING: + dest->v.v_string = strdup (dest->v.v_string); + break; + case TY_AREA: + dest->v.v_area = dup_area (dest->v.v_area); + break; + default: + break; + } + +} + +void magic_clear_var (val_t * v) +{ + switch (v->ty) + { + case TY_STRING: + free (v->v.v_string); + break; + case TY_AREA: + free_area (v->v.v_area); + break; + default: + break; + } +} + +static char *show_entity (entity_t * entity) +{ + switch (entity->type) + { + case BL_PC: + return ((struct map_session_data *) entity)->status.name; + case BL_NPC: + return ((struct npc_data *) entity)->name; + case BL_MOB: + return ((struct mob_data *) entity)->name; + case BL_ITEM: + /* Sorry about this one... */ + return ((struct item_data + *) (&((struct flooritem_data *) entity)-> + item_data))->name; + case BL_SKILL: + return "%skill"; + case BL_SPELL: + return "%invocation(ERROR:this-should-not-be-an-entity)"; + default: + return "%unknown-entity"; + } +} + +static void stringify (val_t * v, int within_op) +{ + static char *dirs[8] = + { "south", "south-west", "west", "north-west", "north", "north-east", + "east", "south-east" + }; + char *buf; + + switch (v->ty) + { + case TY_UNDEF: + buf = strdup ("UNDEF"); + break; + + case TY_INT: + buf = (char *)malloc (32); + sprintf (buf, "%i", v->v.v_int); + break; + + case TY_STRING: + return; + + case TY_DIR: + buf = strdup (dirs[v->v.v_int]); + break; + + case TY_ENTITY: + buf = strdup (show_entity (v->v.v_entity)); + break; + + case TY_LOCATION: + buf = (char *) malloc (128); + sprintf (buf, "<\"%s\", %d, %d>", map[v->v.v_location.m].name, + v->v.v_location.x, v->v.v_location.y); + break; + + case TY_AREA: + buf = strdup ("%area"); + free_area (v->v.v_area); + break; + + case TY_SPELL: + buf = strdup (v->v.v_spell->name); + break; + + case TY_INVOCATION: + { + invocation_t *invocation = within_op + ? v->v.v_invocation : (invocation_t *) map_id2bl (v->v.v_int); + buf = strdup (invocation->spell->name); + } + break; + + default: + fprintf (stderr, "[magic] INTERNAL ERROR: Cannot stringify %d\n", + v->ty); + return; + } + + v->v.v_string = buf; + v->ty = TY_STRING; +} + +static void intify (val_t * v) +{ + if (v->ty == TY_INT) + return; + + magic_clear_var (v); + v->ty = TY_INT; + v->v.v_int = 1; +} + +area_t *area_new (int ty) +{ + area_t *retval; + CREATE (retval, area_t, 1); + retval->ty = ty; + return retval; +} + +area_t *area_union (area_t * area, area_t * other_area) +{ + area_t *retval = area_new (AREA_UNION); + retval->a.a_union[0] = area; + retval->a.a_union[1] = other_area; + retval->size = area->size + other_area->size; /* Assume no overlap */ + return retval; +} + +/** + * Turns location into area, leaves other types untouched + */ +static void make_area (val_t * v) +{ + if (v->ty == TY_LOCATION) + { + area_t *a = (char *)malloc (sizeof (area_t)); + v->ty = TY_AREA; + a->ty = AREA_LOCATION; + a->a.a_loc = v->v.v_location; + v->v.v_area = a; + } +} + +static void make_location (val_t * v) +{ + if (v->ty == TY_AREA && v->v.v_area->ty == AREA_LOCATION) + { + location_t location = v->v.v_area->a.a_loc; + free_area (v->v.v_area); + v->ty = TY_LOCATION; + v->v.v_location = location; + } +} + +static void make_spell (val_t * v) +{ + if (v->ty == TY_INVOCATION) + { + invocation_t *invoc = v->v.v_invocation; //(invocation_t *) map_id2bl(v->v.v_int); + if (!invoc) + v->ty = TY_FAIL; + else + { + v->ty = TY_SPELL; + v->v.v_spell = invoc->spell; + } + } +} + +static int fun_add (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_INT && TY (1) == TY_INT) + { + /* Integer addition */ + RESULTINT = ARGINT (0) + ARGINT (1); + result->ty = TY_INT; + } + else if (ARG_MAY_BE_AREA (0) && ARG_MAY_BE_AREA (1)) + { + /* Area union */ + make_area (&args[0]); + make_area (&args[1]); + RESULTAREA = area_union (ARGAREA (0), ARGAREA (1)); + ARGAREA (0) = NULL; + ARGAREA (1) = NULL; + result->ty = TY_AREA; + } + else + { + /* Anything else -> string concatenation */ + stringify (&args[0], 1); + stringify (&args[1], 1); + /* Yes, we could speed this up. */ + RESULTSTR = + (char *) malloc (1 + strlen (ARGSTR (0)) + strlen (ARGSTR (1))); + strcpy (RESULTSTR, ARGSTR (0)); + strcat (RESULTSTR, ARGSTR (1)); + result->ty = TY_STRING; + } + return 0; +} + +static int fun_sub (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) - ARGINT (1); + return 0; +} + +static int fun_mul (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) * ARGINT (1); + return 0; +} + +static int fun_div (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (!ARGINT (1)) + return 1; /* division by zero */ + RESULTINT = ARGINT (0) / ARGINT (1); + return 0; +} + +static int fun_mod (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (!ARGINT (1)) + return 1; /* division by zero */ + RESULTINT = ARGINT (0) % ARGINT (1); + return 0; +} + +static int fun_or (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) || ARGINT (1); + return 0; +} + +static int fun_and (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) && ARGINT (1); + return 0; +} + +static int fun_not (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = !ARGINT (0); + return 0; +} + +static int fun_neg (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ~ARGINT (0); + return 0; +} + +static int fun_gte (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) >= 0; + } + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) >= ARGINT (1); + } + return 0; +} + +static int fun_gt (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) > 0; + } + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) > ARGINT (1); + } + return 0; +} + +static int fun_eq (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_STRING || TY (1) == TY_STRING) + { + stringify (&args[0], 1); + stringify (&args[1], 1); + RESULTINT = strcmp (ARGSTR (0), ARGSTR (1)) == 0; + } + else if (TY (0) == TY_DIR && TY (1) == TY_DIR) + RESULTINT = ARGDIR (0) == ARGDIR (1); + else if (TY (0) == TY_ENTITY && TY (1) == TY_ENTITY) + RESULTINT = ARGENTITY (0) == ARGENTITY (1); + else if (TY (0) == TY_LOCATION && TY (1) == TY_LOCATION) + RESULTINT = (ARGLOCATION (0).x == ARGLOCATION (1).x + && ARGLOCATION (0).y == ARGLOCATION (1).y + && ARGLOCATION (0).m == ARGLOCATION (1).m); + else if (TY (0) == TY_AREA && TY (1) == TY_AREA) + RESULTINT = ARGAREA (0) == ARGAREA (1); /* Probably not that great an idea... */ + else if (TY (0) == TY_SPELL && TY (1) == TY_SPELL) + RESULTINT = ARGSPELL (0) == ARGSPELL (1); + else if (TY (0) == TY_INVOCATION && TY (1) == TY_INVOCATION) + RESULTINT = ARGINVOCATION (0) == ARGINVOCATION (1); + else + { + intify (&args[0]); + intify (&args[1]); + RESULTINT = ARGINT (0) == ARGINT (1); + } + return 0; +} + +static int fun_bitand (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) & ARGINT (1); + return 0; +} + +static int fun_bitor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) | ARGINT (1); + return 0; +} + +static int fun_bitxor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) ^ ARGINT (1); + return 0; +} + +static int fun_bitshl (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) << ARGINT (1); + return 0; +} + +static int fun_bitshr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGINT (0) >> ARGINT (1); + return 0; +} + +static int fun_max (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = MAX (ARGINT (0), ARGINT (1)); + return 0; +} + +static int fun_min (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = MIN (ARGINT (0), ARGINT (1)); + return 0; +} + +static int +fun_if_then_else (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGINT (0)) + magic_copy_var (result, &args[1]); + else + magic_copy_var (result, &args[2]); + return 0; +} + +void +magic_area_rect (int *m, int *x, int *y, int *width, int *height, + area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + break; + + case AREA_LOCATION: + *m = area->a.a_loc.m; + *x = area->a.a_loc.x; + *y = area->a.a_loc.y; + *width = 1; + *height = 1; + break; + + case AREA_RECT: + *m = area->a.a_rect.loc.m; + *x = area->a.a_rect.loc.x; + *y = area->a.a_rect.loc.y; + *width = area->a.a_rect.width; + *height = area->a.a_rect.height; + break; + + case AREA_BAR: + { + int tx = area->a.a_bar.loc.x; + int ty = area->a.a_bar.loc.y; + int twidth = area->a.a_bar.width; + int tdepth = area->a.a_bar.width; + *m = area->a.a_bar.loc.m; + + switch (area->a.a_bar.dir) + { + case DIR_S: + *x = tx - twidth; + *y = ty; + *width = twidth * 2 + 1; + *height = tdepth; + break; + + case DIR_W: + *x = tx - tdepth; + *y = ty - twidth; + *width = tdepth; + *height = twidth * 2 + 1; + break; + + case DIR_N: + *x = tx - twidth; + *y = ty - tdepth; + *width = twidth * 2 + 1; + *height = tdepth; + break; + + case DIR_E: + *x = tx; + *y = ty - twidth; + *width = tdepth; + *height = twidth * 2 + 1; + break; + + default: + fprintf (stderr, + "Error: Trying to compute area of NE/SE/NW/SW-facing bar"); + *x = tx; + *y = ty; + *width = *height = 1; + } + break; + } + } +} + +int magic_location_in_area (int m, int x, int y, area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + return magic_location_in_area (m, x, y, area->a.a_union[0]) + || magic_location_in_area (m, x, y, area->a.a_union[1]); + case AREA_LOCATION: + case AREA_RECT: + case AREA_BAR: + { + int am; + int ax, ay, awidth, aheight; + magic_area_rect (&am, &ax, &ay, &awidth, &aheight, area); + return (am == m + && (x >= ax) && (y >= ay) + && (x < ax + awidth) && (y < ay + aheight)); + } + default: + fprintf (stderr, "INTERNAL ERROR: Invalid area\n"); + return 0; + } +} + +static int fun_is_in (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = magic_location_in_area (ARGLOCATION (0).m, + ARGLOCATION (0).x, + ARGLOCATION (0).y, ARGAREA (1)); + return 0; +} + +static int fun_skill (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) != BL_PC + || ARGINT (1) < 0 + || ARGINT (1) >= MAX_SKILL + || ARGPC (0)->status.skill[ARGINT (1)].id != ARGINT (1)) + RESULTINT = 0; + else + RESULTINT = ARGPC (0)->status.skill[ARGINT (1)].lv; + return 0; +} + +static int +fun_has_shroud (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->state.shroud_active); + return 0; +} + +#define BATTLE_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { RESULTINT = battle_get_##name(ARGENTITY(0)); return 0; } + +BATTLE_GETTER (str); +BATTLE_GETTER (agi); +BATTLE_GETTER (vit); +BATTLE_GETTER (dex); +BATTLE_GETTER (luk); +BATTLE_GETTER (int); +BATTLE_GETTER (lv); +BATTLE_GETTER (hp); +BATTLE_GETTER (mdef); +BATTLE_GETTER (def); +BATTLE_GETTER (max_hp); +BATTLE_GETTER (dir); + +#define MMO_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { \ + if (ETY(0) == BL_PC) \ + RESULTINT = ARGPC(0)->status.name; \ + else \ + RESULTINT = 0; \ + return 0; } + +MMO_GETTER (sp); +MMO_GETTER (max_sp); + +static int +fun_name_of (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (TY (0) == TY_ENTITY) + { + RESULTSTR = strdup (show_entity (ARGENTITY (0))); + return 0; + } + else if (TY (0) == TY_SPELL) + { + RESULTSTR = strdup (ARGSPELL (0)->name); + return 0; + } + else if (TY (0) == TY_INVOCATION) + { + RESULTSTR = strdup (ARGINVOCATION (0)->spell->name); + return 0; + } + return 1; +} + +/* [Freeyorp] I'm putting this one in as name_of seems to have issues with summoned or spawned mobs. */ +static int +fun_mob_id (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) != BL_MOB) return 1; + RESULTINT = ((struct mob_data *) (ARGENTITY(0)))->mob_class; + return 0; +} + +#define COPY_LOCATION(dest, src) (dest).x = (src).x; (dest).y = (src).y; (dest).m = (src).m; + +static int +fun_location (env_t * env, int args_nr, val_t * result, val_t * args) +{ + COPY_LOCATION (RESULTLOCATION, *(ARGENTITY (0))); + return 0; +} + +static int fun_random (env_t * env, int args_nr, val_t * result, val_t * args) +{ + int delta = ARGINT (0); + if (delta < 0) + delta = -delta; + if (delta == 0) + { + RESULTINT = 0; + return 0; + } + RESULTINT = MRAND (delta); + + if (ARGINT (0) < 0) + RESULTINT = -RESULTINT; + return 0; +} + +static int +fun_random_dir (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGINT (0)) + RESULTDIR = mt_random () & 0x7; + else + RESULTDIR = (mt_random () & 0x3) * 2; + return 0; +} + +static int +fun_hash_entity (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGENTITY (0)->id; + return 0; +} + +int // ret -1: not a string, ret 1: no such item, ret 0: OK +magic_find_item (val_t * args, int index, struct item *item, int *stackable) +{ + struct item_data *item_data; + int must_add_sequentially; + + if (TY (index) == TY_INT) + item_data = itemdb_exists (ARGINT (index)); + else if (TY (index) == TY_STRING) + item_data = itemdb_searchname (ARGSTR (index)); + else + return -1; + + if (!item_data) + return 1; + + must_add_sequentially = (item_data->type == 4 || item_data->type == 5 || item_data->type == 7 || item_data->type == 8); /* Very elegant. */ + + if (stackable) + *stackable = !must_add_sequentially; + + memset (item, 0, sizeof (struct item)); + item->nameid = item_data->nameid; + item->identify = 1; + + return 0; +} + +static int +fun_count_item (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + int stackable; + struct item item; + + GET_ARG_ITEM (1, item, stackable); + + if (!chr) + return 1; + + RESULTINT = pc_count_all_items (chr, item.nameid); + return 0; +} + +static int +fun_is_equipped (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *chr = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + int stackable; + struct item item; + int i; + int retval = 0; + + GET_ARG_ITEM (1, item, stackable); + + if (!chr) + return 1; + + for (i = 0; i < 11; i++) + if (chr->equip_index[i] >= 0 + && chr->status.inventory[chr->equip_index[i]].nameid == + item.nameid) + { + retval = i + 1; + break; + } + + RESULTINT = retval; + return 0; +} + +static int +fun_is_married (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && ARGPC (0)->status.partner_id); + return 0; +} + +static int +fun_is_dead (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC && pc_isdead (ARGPC (0))); + return 0; +} + +static int fun_is_pc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (ETY (0) == BL_PC); + return 0; +} + +static int +fun_partner (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ETY (0) == BL_PC && ARGPC (0)->status.partner_id) + { + RESULTENTITY = + (entity_t *) + map_nick2sd (map_charid2nick (ARGPC (0)->status.partner_id)); + return 0; + } + else + return 1; +} + +static int +fun_awayfrom (env_t * env, int args_nr, val_t * result, val_t * args) +{ + location_t *loc = &ARGLOCATION (0); + int dx = heading_x[ARGDIR (1)]; + int dy = heading_y[ARGDIR (1)]; + int distance = ARGINT (2); + while (distance-- && !map_is_solid (loc->m, loc->x + dx, loc->y + dy)) + { + loc->x += dx; + loc->y += dy; + } + + RESULTLOCATION = *loc; + return 0; +} + +static int fun_failed (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = TY (0) == TY_FAIL; + return 0; +} + +static int fun_npc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTENTITY = (entity_t *) npc_name2id (ARGSTR (0)); + return RESULTENTITY == NULL; +} + +static int fun_pc (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTENTITY = (entity_t *) map_nick2sd (ARGSTR (0)); + return RESULTENTITY == NULL; +} + +static int +fun_distance (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + RESULTINT = INT_MAX; + else + RESULTINT = MAX (abs (ARGLOCATION (0).x - ARGLOCATION (1).x), + abs (ARGLOCATION (0).y - ARGLOCATION (1).y)); + return 0; +} + +static int +fun_rdistance (env_t * env, int args_nr, val_t * result, val_t * args) +{ + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + RESULTINT = INT_MAX; + else + { + int dx = ARGLOCATION (0).x - ARGLOCATION (1).x; + int dy = ARGLOCATION (0).y - ARGLOCATION (1).y; + RESULTINT = (int) (sqrt ((dx * dx) + (dy * dy))); + } + return 0; +} + +static int fun_anchor (env_t * env, int args_nr, val_t * result, val_t * args) +{ + teleport_anchor_t *anchor = magic_find_anchor (ARGSTR (0)); + + if (!anchor) + return 1; + + magic_eval (env, result, anchor->location); + + make_area (result); + if (result->ty != TY_AREA) + { + magic_clear_var (result); + return 1; + } + + return 0; +} + +static int +fun_line_of_sight (env_t * env, int args_nr, val_t * result, val_t * args) +{ + entity_t e1, e2; + + COPY_LOCATION (e1, ARGLOCATION (0)); + COPY_LOCATION (e2, ARGLOCATION (1)); + + RESULTINT = battle_check_range (&e1, &e2, 0); + + return 0; +} + +void magic_random_location (location_t * dest, area_t * area) +{ + switch (area->ty) + { + case AREA_UNION: + { + int rv = MRAND (area->size); + if (rv < area->a.a_union[0]->size) + magic_random_location (dest, area->a.a_union[0]); + else + magic_random_location (dest, area->a.a_union[1]); + break; + } + + case AREA_LOCATION: + case AREA_RECT: + case AREA_BAR: + { + int m, x, y, w, h; + magic_area_rect (&m, &x, &y, &w, &h, area); + + if (w <= 1) + w = 1; + + if (h <= 1) + h = 1; + + x += MRAND (w); + y += MRAND (h); + + if (!map_is_solid (m, x, y)) + { + int start_x = x; + int start_y = y; + int i; + int initial_dir = mt_random () & 0x7; + int dir = initial_dir; + + /* try all directions, up to a distance to 10, for a free slot */ + do + { + x = start_x; + y = start_y; + + for (i = 0; i < 10 && map_is_solid (m, x, y); i++) + { + x += heading_x[dir]; + y += heading_y[dir]; + } + + dir = (dir + 1) & 0x7; + } + while (map_is_solid (m, x, y) && dir != initial_dir); + + } + /* We've tried our best. If the map is still solid, the engine will automatically randomise the target location if we try to warp. */ + + dest->m = m; + dest->x = x; + dest->y = y; + break; + } + + default: + fprintf (stderr, "Unknown area type %d\n", area->ty); + } +} + +static int +fun_pick_location (env_t * env, int args_nr, val_t * result, val_t * args) +{ + magic_random_location (&result->v.v_location, ARGAREA (0)); + return 0; +} + +static int +fun_read_script_int (env_t * env, int args_nr, val_t * result, val_t * args) +{ + entity_t *subject_p = ARGENTITY (0); + char *var_name = ARGSTR (1); + + if (subject_p->type != BL_PC) + return 1; + + RESULTINT = pc_readglobalreg ((character_t *) subject_p, var_name); + return 0; +} + +static int fun_rbox (env_t * env, int args_nr, val_t * result, val_t * args) +{ + location_t loc = ARGLOCATION (0); + int radius = ARGINT (1); + + RESULTAREA = area_new (AREA_RECT); + RESULTAREA->a.a_rect.loc.m = loc.m; + RESULTAREA->a.a_rect.loc.x = loc.x - radius; + RESULTAREA->a.a_rect.loc.y = loc.y - radius; + RESULTAREA->a.a_rect.width = radius * 2 + 1; + RESULTAREA->a.a_rect.height = radius * 2 + 1; + + return 0; +} + +static int +fun_running_status_update (env_t * env, int args_nr, val_t * result, + val_t * args) +{ + if (ETY (0) != BL_PC && ETY (0) != BL_MOB) + return 1; + + RESULTINT = battle_get_sc_data (ARGENTITY (0))[ARGINT (1)].timer != -1; + return 0; +} + +static int +fun_status_option (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = + ((((struct map_session_data *) ARGENTITY (0))-> + status.option & ARGINT (0)) != 0); + return 0; +} + +static int +fun_element (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = battle_get_element (ARGENTITY (0)) % 10; + return 0; +} + +static int +fun_element_level (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = battle_get_element (ARGENTITY (0)) / 10; + return 0; +} + +static int fun_index (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = ARGSPELL (0)->index; + return 0; +} + +static int +fun_is_exterior (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = map[ARGLOCATION (0).m].name[4] == '1'; + return 0; +} + +static int +fun_contains_string (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = NULL != strstr (ARGSTR (0), ARGSTR (1)); + return 0; +} + +static int fun_strstr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + char *offset = strstr (ARGSTR (0), ARGSTR (1)); + RESULTINT = offset - ARGSTR (0); + return offset == NULL; +} + +static int fun_strlen (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = strlen (ARGSTR (0)); + return 0; +} + +static int fun_substr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + const char *src = ARGSTR (0); + const int slen = strlen (src); + int offset = ARGINT (1); + int len = ARGINT (2); + + if (len < 0) + len = 0; + if (offset < 0) + offset = 0; + + if (offset > slen) + offset = slen; + + if (offset + len > slen) + len = slen - offset; + + RESULTSTR = (char *) calloc (1, 1 + len); + memcpy (RESULTSTR, src + offset, len); + + return 0; +} + +static int fun_sqrt (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = (int) sqrt (ARGINT (0)); + return 0; +} + +static int +fun_map_level (env_t * env, int args_nr, val_t * result, val_t * args) +{ + RESULTINT = map[ARGLOCATION (0).m].name[4] - '0'; + return 0; +} + +static int fun_map_nr (env_t * env, int args_nr, val_t * result, val_t * args) +{ + const char *mapname = map[ARGLOCATION (0).m].name; + + RESULTINT = ((mapname[0] - '0') * 100) + + ((mapname[1] - '0') * 10) + ((mapname[2] - '0')); + return 0; +} + +static int +fun_dir_towards (env_t * env, int args_nr, val_t * result, val_t * args) +{ + int dx; + int dy; + + if (ARGLOCATION (0).m != ARGLOCATION (1).m) + return 1; + + dx = ARGLOCATION (1).x - ARGLOCATION (0).x; + dy = ARGLOCATION (1).y - ARGLOCATION (0).y; + + if (ARGINT (1)) + { + /* 8-direction mode */ + if (abs (dx) > abs (dy) * 2) + { /* east or west */ + if (dx < 0) + RESULTINT = 2 /* west */ ; + else + RESULTINT = 6 /* east */ ; + } + else if (abs (dy) > abs (dx) * 2) + { /* north or south */ + if (dy > 0) + RESULTINT = 0 /* south */ ; + else + RESULTINT = 4 /* north */ ; + } + else if (dx < 0) + { /* north-west or south-west */ + if (dy < 0) + RESULTINT = 3 /* north-west */ ; + else + RESULTINT = 1 /* south-west */ ; + } + else + { /* north-east or south-east */ + if (dy < 0) + RESULTINT = 5 /* north-east */ ; + else + RESULTINT = 7 /* south-east */ ; + } + } + else + { + /* 4-direction mode */ + if (abs (dx) > abs (dy)) + { /* east or west */ + if (dx < 0) + RESULTINT = 2 /* west */ ; + else + RESULTINT = 6 /* east */ ; + } + else + { /* north or south */ + if (dy > 0) + RESULTINT = 0 /* south */ ; + else + RESULTINT = 4 /* north */ ; + } + } + + return 0; +} + +static int +fun_extract_healer_xp (env_t * env, int args_nr, val_t * result, val_t * args) +{ + character_t *sd = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!sd) + RESULTINT = 0; + else + RESULTINT = pc_extract_healer_exp (sd, ARGINT (1)); + return 0; +} + +#define BATTLE_RECORD2(sname, name) { sname, "e", 'i', fun_get_##name } +#define BATTLE_RECORD(name) BATTLE_RECORD2(#name, name) +static fun_t functions[] = { + {"+", "..", '.', fun_add}, + {"-", "ii", 'i', fun_sub}, + {"*", "ii", 'i', fun_mul}, + {"/", "ii", 'i', fun_div}, + {"%", "ii", 'i', fun_mod}, + {"||", "ii", 'i', fun_or}, + {"&&", "ii", 'i', fun_and}, + {">", "..", 'i', fun_gt}, + {">=", "..", 'i', fun_gte}, + {"=", "..", 'i', fun_eq}, + {"|", "..", 'i', fun_bitor}, + {"&", "ii", 'i', fun_bitand}, + {"^", "ii", 'i', fun_bitxor}, + {"<<", "ii", 'i', fun_bitshl}, + {">>", "ii", 'i', fun_bitshr}, + {"not", "i", 'i', fun_not}, + {"neg", "i", 'i', fun_neg}, + {"max", "ii", 'i', fun_max}, + {"min", "ii", 'i', fun_min}, + {"is_in", "la", 'i', fun_is_in}, + {"if_then_else", "i__", '_', fun_if_then_else}, + {"skill", "ei", 'i', fun_skill}, + BATTLE_RECORD (str), + BATTLE_RECORD (agi), + BATTLE_RECORD (vit), + BATTLE_RECORD (dex), + BATTLE_RECORD (luk), + BATTLE_RECORD (int), + BATTLE_RECORD2 ("level", lv), + BATTLE_RECORD (mdef), + BATTLE_RECORD (def), + BATTLE_RECORD (hp), + BATTLE_RECORD (max_hp), + BATTLE_RECORD (sp), + BATTLE_RECORD (max_sp), + {"dir", "e", 'd', fun_get_dir}, + {"name_of", ".", 's', fun_name_of}, + {"mob_id", "e", 'i', fun_mob_id}, + {"location", "e", 'l', fun_location}, + {"random", "i", 'i', fun_random}, + {"random_dir", "i", 'd', fun_random_dir}, + {"hash_entity", "e", 'i', fun_hash_entity}, + {"is_married", "e", 'i', fun_is_married}, + {"partner", "e", 'e', fun_partner}, + {"awayfrom", "ldi", 'l', fun_awayfrom}, + {"failed", "_", 'i', fun_failed}, + {"pc", "s", 'e', fun_pc}, + {"npc", "s", 'e', fun_npc}, + {"distance", "ll", 'i', fun_distance}, + {"rdistance", "ll", 'i', fun_rdistance}, + {"anchor", "s", 'a', fun_anchor}, + {"random_location", "a", 'l', fun_pick_location}, + {"script_int", "es", 'i', fun_read_script_int}, + {"rbox", "li", 'a', fun_rbox}, + {"count_item", "e.", 'i', fun_count_item}, + {"line_of_sight", "ll", 'i', fun_line_of_sight}, + {"running_status_update", "ei", 'i', fun_running_status_update}, + {"status_option", "ei", 'i', fun_status_option}, + {"element", "e", 'i', fun_element}, + {"element_level", "e", 'i', fun_element_level}, + {"has_shroud", "e", 'i', fun_has_shroud}, + {"is_equipped", "e.", 'i', fun_is_equipped}, + {"spell_index", "S", 'i', fun_index}, + {"is_exterior", "l", 'i', fun_is_exterior}, + {"contains_string", "ss", 'i', fun_contains_string}, + {"strstr", "ss", 'i', fun_strstr}, + {"strlen", "s", 'i', fun_strlen}, + {"substr", "sii", 's', fun_substr}, + {"sqrt", "i", 'i', fun_sqrt}, + {"map_level", "l", 'i', fun_map_level}, + {"map_nr", "l", 'i', fun_map_nr}, + {"dir_towards", "lli", 'd', fun_dir_towards}, + {"is_dead", "e", 'i', fun_is_dead}, + {"is_pc", "e", 'i', fun_is_pc}, + {"extract_healer_experience", "ei", 'i', fun_extract_healer_xp}, + {NULL, NULL, '.', NULL} +}; + +static int functions_are_sorted = 0; + +int compare_fun (const void *lhs, const void *rhs) +{ + return strcmp (((fun_t *) lhs)->name, ((fun_t *) rhs)->name); +} + +fun_t *magic_get_fun (const char *name, int *index) +{ + static int functions_nr; + fun_t *result; + fun_t key; + + if (!functions_are_sorted) + { + fun_t *it = functions; + + while (it->name) + ++it; + functions_nr = it - functions; + + qsort (functions, functions_nr, sizeof (fun_t), compare_fun); + functions_are_sorted = 1; + } + + key.name = name; + result = (fun_t *) bsearch (&key, functions, functions_nr, sizeof (fun_t), + compare_fun); + + if (result && index) + *index = result - functions; + + return result; +} + +static int // 1 on failure +eval_location (env_t * env, location_t * dest, e_location_t * expr) +{ + val_t m, x, y; + magic_eval (env, &m, expr->m); + magic_eval (env, &x, expr->x); + magic_eval (env, &y, expr->y); + + if (CHECK_TYPE (&m, TY_STRING) + && CHECK_TYPE (&x, TY_INT) && CHECK_TYPE (&y, TY_INT)) + { + int map_id = map_mapname2mapid (m.v.v_string); + magic_clear_var (&m); + if (map_id < 0) + return 1; + dest->m = map_id; + dest->x = x.v.v_int; + dest->y = y.v.v_int; + return 0; + } + else + { + magic_clear_var (&m); + magic_clear_var (&x); + magic_clear_var (&y); + return 1; + } +} + +static area_t *eval_area (env_t * env, e_area_t * expr) +{ + area_t *area = (area_t *)malloc (sizeof (area_t)); + area->ty = expr->ty; + + switch (expr->ty) + { + case AREA_LOCATION: + area->size = 1; + if (eval_location (env, &area->a.a_loc, &expr->a.a_loc)) + { + free (area); + return NULL; + } + else + return area; + + case AREA_UNION: + { + int i, fail = 0; + for (i = 0; i < 2; i++) + { + area->a.a_union[i] = eval_area (env, expr->a.a_union[i]); + if (!area->a.a_union[i]) + fail = 1; + } + + if (fail) + { + for (i = 0; i < 2; i++) + { + if (area->a.a_union[i]) + free_area (area->a.a_union[i]); + } + free (area); + return NULL; + } + area->size = area->a.a_union[0]->size + area->a.a_union[1]->size; + return area; + } + + case AREA_RECT: + { + val_t width, height; + magic_eval (env, &width, expr->a.a_rect.width); + magic_eval (env, &height, expr->a.a_rect.height); + + area->a.a_rect.width = width.v.v_int; + area->a.a_rect.height = height.v.v_int; + + if (CHECK_TYPE (&width, TY_INT) + && CHECK_TYPE (&height, TY_INT) + && !eval_location (env, &(area->a.a_rect.loc), + &expr->a.a_rect.loc)) + { + area->size = area->a.a_rect.width * area->a.a_rect.height; + magic_clear_var (&width); + magic_clear_var (&height); + return area; + } + else + { + free (area); + magic_clear_var (&width); + magic_clear_var (&height); + return NULL; + } + } + + case AREA_BAR: + { + val_t width, depth, dir; + magic_eval (env, &width, expr->a.a_bar.width); + magic_eval (env, &depth, expr->a.a_bar.depth); + magic_eval (env, &dir, expr->a.a_bar.dir); + + area->a.a_bar.width = width.v.v_int; + area->a.a_bar.depth = depth.v.v_int; + area->a.a_bar.dir = dir.v.v_int; + + if (CHECK_TYPE (&width, TY_INT) + && CHECK_TYPE (&depth, TY_INT) + && CHECK_TYPE (&dir, TY_DIR) + && !eval_location (env, &area->a.a_bar.loc, + &expr->a.a_bar.loc)) + { + area->size = + (area->a.a_bar.width * 2 + 1) * area->a.a_bar.depth; + magic_clear_var (&width); + magic_clear_var (&depth); + magic_clear_var (&dir); + return area; + } + else + { + free (area); + magic_clear_var (&width); + magic_clear_var (&depth); + magic_clear_var (&dir); + return NULL; + } + } + + default: + fprintf (stderr, "INTERNAL ERROR: Unknown area type %d\n", + area->ty); + free (area); + return NULL; + } +} + +static int type_key (char ty_key) +{ + switch (ty_key) + { + case 'i': + return TY_INT; + case 'd': + return TY_DIR; + case 's': + return TY_STRING; + case 'e': + return TY_ENTITY; + case 'l': + return TY_LOCATION; + case 'a': + return TY_AREA; + case 'S': + return TY_SPELL; + case 'I': + return TY_INVOCATION; + default: + return -1; + } +} + +int +magic_signature_check (char *opname, char *funname, char *signature, + int args_nr, val_t * args, int line, int column) +{ + int i; + for (i = 0; i < args_nr; i++) + { + val_t *arg = &args[i]; + char ty_key = signature[i]; + int ty = arg->ty; + int desired_ty = type_key (ty_key); + + if (ty == TY_ENTITY) + { + /* Dereference entities in preparation for calling function */ + arg->v.v_entity = map_id2bl (arg->v.v_int); + if (!arg->v.v_entity) + ty = arg->ty = TY_FAIL; + } + else if (ty == TY_INVOCATION) + { + arg->v.v_invocation = (invocation_t *) map_id2bl (arg->v.v_int); + if (!arg->v.v_entity) + ty = arg->ty = TY_FAIL; + } + + if (!ty_key) + { + fprintf (stderr, + "[magic-eval]: L%d:%d: Too many arguments (%d) to %s `%s'\n", + line, column, args_nr, opname, funname); + return 1; + } + + if (ty == TY_FAIL && ty_key != '_') + return 1; /* Fail `in a sane way': This is a perfectly permissible error */ + + if (ty == desired_ty || desired_ty < 0 /* `dontcare' */ ) + continue; + + if (ty == TY_UNDEF) + { + fprintf (stderr, + "[magic-eval]: L%d:%d: Argument #%d to %s `%s' undefined\n", + line, column, i + 1, opname, funname); + return 1; + } + + /* If we are here, we have a type mismatch but no failure _yet_. Try to coerce. */ + switch (desired_ty) + { + case TY_INT: + intify (arg); + break; /* 100% success rate */ + case TY_STRING: + stringify (arg, 1); + break; /* 100% success rate */ + case TY_AREA: + make_area (arg); + break; /* Only works for locations */ + case TY_LOCATION: + make_location (arg); + break; /* Only works for some areas */ + case TY_SPELL: + make_spell (arg); + break; /* Only works for still-active invocatoins */ + default: + break; /* We'll fail right below */ + } + + ty = arg->ty; + if (ty != desired_ty) + { /* Coercion failed? */ + if (ty != TY_FAIL) + fprintf (stderr, + "[magic-eval]: L%d:%d: Argument #%d to %s `%s' of incorrect type (%d)\n", + line, column, i + 1, opname, funname, ty); + return 1; + } + } + + return 0; +} + +void magic_eval (env_t * env, val_t * dest, expr_t * expr) +{ + switch (expr->ty) + { + case EXPR_VAL: + magic_copy_var (dest, &expr->e.e_val); + break; + + case EXPR_LOCATION: + if (eval_location (env, &dest->v.v_location, &expr->e.e_location)) + dest->ty = TY_FAIL; + else + dest->ty = TY_LOCATION; + break; + + case EXPR_AREA: + if ((dest->v.v_area = eval_area (env, &expr->e.e_area))) + dest->ty = TY_AREA; + else + dest->ty = TY_FAIL; + break; + + case EXPR_FUNAPP: + { + val_t arguments[MAX_ARGS]; + int args_nr = expr->e.e_funapp.args_nr; + int i; + fun_t *f = functions + expr->e.e_funapp.id; + + for (i = 0; i < args_nr; ++i) + magic_eval (env, &arguments[i], expr->e.e_funapp.args[i]); + if (magic_signature_check + ("function", f->name, f->signature, args_nr, arguments, + expr->e.e_funapp.line_nr, expr->e.e_funapp.column) + || f->fun (env, args_nr, dest, arguments)) + dest->ty = TY_FAIL; + else + { + int dest_ty = type_key (f->ret_ty); + if (dest_ty != -1) + dest->ty = dest_ty; + + /* translate entity back into persistent int */ + if (dest->ty == TY_ENTITY) + { + if (dest->v.v_entity) + dest->v.v_int = dest->v.v_entity->id; + else + dest->ty = TY_FAIL; + } + } + + for (i = 0; i < args_nr; ++i) + magic_clear_var (&arguments[i]); + break; + } + + case EXPR_ID: + { + val_t v = VAR (expr->e.e_id); + magic_copy_var (dest, &v); + break; + } + + case EXPR_SPELLFIELD: + { + val_t v; + int id = expr->e.e_field.id; + magic_eval (env, &v, expr->e.e_field.expr); + + if (v.ty == TY_INVOCATION) + { + invocation_t *t = (invocation_t *) map_id2bl (v.v.v_int); + + if (!t) + dest->ty = TY_UNDEF; + else + { + env_t *env = t->env; + val_t v = VAR (id); + magic_copy_var (dest, &v); + } + } + else + { + fprintf (stderr, + "[magic] Attempt to access field %s on non-spell\n", + env->base_env->var_name[id]); + dest->ty = TY_FAIL; + } + break; + } + + default: + fprintf (stderr, + "[magic] INTERNAL ERROR: Unknown expression type %d\n", + expr->ty); + break; + } +} + +int magic_eval_int (env_t * env, expr_t * expr) +{ + val_t result; + magic_eval (env, &result, expr); + + if (result.ty == TY_FAIL || result.ty == TY_UNDEF) + return 0; + + intify (&result); + + return result.v.v_int; +} + +char *magic_eval_str (env_t * env, expr_t * expr) +{ + val_t result; + magic_eval (env, &result, expr); + + if (result.ty == TY_FAIL || result.ty == TY_UNDEF) + return strdup ("?"); + + stringify (&result, 0); + + return result.v.v_string; +} + +expr_t *magic_new_expr (int ty) +{ + expr_t *expr = (expr_t *) malloc (sizeof (expr_t)); + expr->ty = ty; + return expr; +} diff --git a/src/map/magic-expr.h b/src/map/magic-expr.h deleted file mode 100644 index e912d14..0000000 --- a/src/map/magic-expr.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef MAGIC_EXPR_H_ -#define MAGIC_EXPR_H_ -#include "magic-interpreter.h" -#include "magic-interpreter-aux.h" - -#ifndef MAX -# define MAX(x,y) (((x)>(y)) ? (x) : (y)) -#endif -#ifndef MIN -# define MIN(x,y) (((x)<(y)) ? (x) : (y)) -#endif - -#ifndef INT_MAX -# define INT_MAX (1<<30) // It's more than that, but this is quite sufficient for our purposes. -#endif - -/* - * Argument types: - * i : int - * d : dir - * s : string - * e : entity - * l : location - * a : area - * S : spell - * I : invocation - * . : any, except for fail/undef - * _ : any, including fail, but not undef - */ -typedef struct fun -{ - char *name; - char *signature; - char ret_ty; - int (*fun) (env_t * env, int args_nr, val_t * result, val_t * args); -} fun_t; - -typedef struct op -{ - char *name; - char *signature; - int (*op) (env_t * env, int args_nr, val_t * args); -} op_t; - -/** - * Retrieves a function by name - * @param name The name to look up - * @return A function of that name, or NULL, and a function index - */ -fun_t *magic_get_fun (const char *name, int *index); - -/** - * Retrieves an operation by name - * @param name The name to look up - * @return An operation of that name, or NULL, and a function index - */ -op_t *magic_get_op (char *name, int *index); - -/** - * Evaluates an expression and stores the result in `dest' - */ -void magic_eval (env_t * env, val_t * dest, expr_t * expr); - -/** - * Evaluates an expression and coerces the result into an integer - */ -int magic_eval_int (env_t * env, expr_t * expr); - -/** - * Evaluates an expression and coerces the result into a string - */ -char *magic_eval_str (env_t * env, expr_t * expr); - -int map_is_solid (int m, int x, int y); - -expr_t *magic_new_expr (int ty); - -void magic_clear_var (val_t * v); - -void magic_copy_var (val_t * dest, val_t * src); - -void magic_random_location (location_t * dest, area_t * area); - -int // ret -1: not a string, ret 1: no such item, ret 0: OK - - - - - magic_find_item (val_t * args, int index, struct item *item, int *stackable); - -#define GET_ARG_ITEM(index, dest, stackable) switch(magic_find_item(args, index, &dest, &stackable)) { case -1 : return 1; case 1 : return 0; } - -int magic_location_in_area (int m, int x, int y, area_t * area); - -#endif /* !defined(MAGIC_EXPR_H_) */ diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp new file mode 100644 index 0000000..4551585 --- /dev/null +++ b/src/map/magic-expr.hpp @@ -0,0 +1,95 @@ +#ifndef MAGIC_EXPR_HPP +#define MAGIC_EXPR_HPP +#include "magic-interpreter.hpp" +#include "magic-interpreter-aux.hpp" + +#ifndef MAX +# define MAX(x,y) (((x)>(y)) ? (x) : (y)) +#endif +#ifndef MIN +# define MIN(x,y) (((x)<(y)) ? (x) : (y)) +#endif + +#ifndef INT_MAX +# define INT_MAX (1<<30) // It's more than that, but this is quite sufficient for our purposes. +#endif + +/* + * Argument types: + * i : int + * d : dir + * s : string + * e : entity + * l : location + * a : area + * S : spell + * I : invocation + * . : any, except for fail/undef + * _ : any, including fail, but not undef + */ +typedef struct fun +{ + char *name; + char *signature; + char ret_ty; + int (*fun) (env_t * env, int args_nr, val_t * result, val_t * args); +} fun_t; + +typedef struct op +{ + char *name; + char *signature; + int (*op) (env_t * env, int args_nr, val_t * args); +} op_t; + +/** + * Retrieves a function by name + * @param name The name to look up + * @return A function of that name, or NULL, and a function index + */ +fun_t *magic_get_fun (const char *name, int *index); + +/** + * Retrieves an operation by name + * @param name The name to look up + * @return An operation of that name, or NULL, and a function index + */ +op_t *magic_get_op (char *name, int *index); + +/** + * Evaluates an expression and stores the result in `dest' + */ +void magic_eval (env_t * env, val_t * dest, expr_t * expr); + +/** + * Evaluates an expression and coerces the result into an integer + */ +int magic_eval_int (env_t * env, expr_t * expr); + +/** + * Evaluates an expression and coerces the result into a string + */ +char *magic_eval_str (env_t * env, expr_t * expr); + +int map_is_solid (int m, int x, int y); + +expr_t *magic_new_expr (int ty); + +void magic_clear_var (val_t * v); + +void magic_copy_var (val_t * dest, val_t * src); + +void magic_random_location (location_t * dest, area_t * area); + +int // ret -1: not a string, ret 1: no such item, ret 0: OK + + + + + magic_find_item (val_t * args, int index, struct item *item, int *stackable); + +#define GET_ARG_ITEM(index, dest, stackable) switch(magic_find_item(args, index, &dest, &stackable)) { case -1 : return 1; case 1 : return 0; } + +int magic_location_in_area (int m, int x, int y, area_t * area); + +#endif /* !defined(MAGIC_EXPR_H_) */ diff --git a/src/map/magic-interpreter-aux.h b/src/map/magic-interpreter-aux.h deleted file mode 100644 index 975e502..0000000 --- a/src/map/magic-interpreter-aux.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MAGIC_INTERPRETER_AUX_H_ -#define MAGIC_INTERPRETER_AUX_H_ - -#define CHECK_TYPE(v, t) ((v)->ty == t) - -#define VAR(i) ((!env->vars || env->vars[i].ty == TY_UNDEF)? env->base_env->vars[i] : env->vars[i]) - -#endif /* !defined(MAGIC_INTERPRETER_AUX_H_) */ diff --git a/src/map/magic-interpreter-aux.hpp b/src/map/magic-interpreter-aux.hpp new file mode 100644 index 0000000..ea05b88 --- /dev/null +++ b/src/map/magic-interpreter-aux.hpp @@ -0,0 +1,8 @@ +#ifndef MAGIC_INTERPRETER_AUX_HPP +#define MAGIC_INTERPRETER_AUX_HPP + +#define CHECK_TYPE(v, t) ((v)->ty == t) + +#define VAR(i) ((!env->vars || env->vars[i].ty == TY_UNDEF)? env->base_env->vars[i] : env->vars[i]) + +#endif /* !defined(MAGIC_INTERPRETER_AUX_H_) */ diff --git a/src/map/magic-interpreter-base.c b/src/map/magic-interpreter-base.c deleted file mode 100644 index 671f962..0000000 --- a/src/map/magic-interpreter-base.c +++ /dev/null @@ -1,562 +0,0 @@ -#include "magic.h" -#include "magic-interpreter.h" -#include "magic-expr.h" -#include "magic-interpreter-aux.h" - -static void set_int_p (val_t * v, int i, int t) -{ - v->ty = t; - v->v.v_int = i; -} - -#define set_int(v, i) set_int_p(v, i, TY_INT) -#define set_dir(v, i) set_int_p(v, i, TY_DIR) - -#define SETTER(tty, dyn_ty, field) (val_t *v, tty x) { v->ty = dyn_ty; v->v.field = x; } - -static void set_string SETTER (char *, TY_STRING, v_string); - -static void set_entity (val_t * v, entity_t * e) -{ - v->ty = TY_ENTITY; - v->v.v_int = e->id; -} - -static void set_invocation (val_t * v, invocation_t * i) -{ - v->ty = TY_INVOCATION; - v->v.v_int = i->bl.id; -} - -static void set_spell SETTER (spell_t *, TY_SPELL, v_spell); - -#define setenv(f, v, x) f(&(env->vars[v]), x) - -#define set_env_int(v, x) setenv(set_int, v, x) -#define set_env_dir(v, x) setenv(set_dir, v, x) -#define set_env_string(v, x) setenv(set_string, v, x) -#define set_env_entity(v, x) setenv(set_entity, v, x) -#define set_env_location(v, x) setenv(set_location, v, x) -#define set_env_area(v, x) setenv(set_area, v, x) -#define set_env_invocation(v, x) setenv(set_invocation, v, x) -#define set_env_spell(v, x) setenv(set_spell, v, x) - -magic_conf_t magic_conf; /* Global magic conf */ -env_t magic_default_env = { &magic_conf, NULL }; - -static int spells_sorted = 0; - -char *magic_find_invocation (char *spellname) -{ - int i; - - for (i = 0; i < abs (magic_conf.spells_nr); i++) - if (!strcmp (magic_conf.spells[i]->name, spellname)) - return magic_conf.spells[i]->invocation; - - return NULL; -} - -static int spell_compare (const void *lhs, const void *rhs) -{ - return strcmp ((*((spell_t **) lhs))->invocation, - (*((spell_t **) rhs))->invocation); -} - -spell_t *magic_find_spell (char *invocation) -{ - spell_t key; - spell_t *keyp = &key; - spell_t **retval; - - if (!spells_sorted) - { - qsort (magic_conf.spells, magic_conf.spells_nr, sizeof (spell_t *), - spell_compare); - spells_sorted = 1; - } - - key.invocation = invocation; - - retval = - ((spell_t **) - bsearch (&keyp, magic_conf.spells, magic_conf.spells_nr, - sizeof (spell_t *), spell_compare)); - - if (!retval) - return NULL; - else - return *retval; -} - -/* -------------------------------------------------------------------------------- */ -/* Spell anchors */ -/* -------------------------------------------------------------------------------- */ - -static int compare_teleport_anchor (const void *lhs, const void *rhs) -{ - return strcmp ((*((teleport_anchor_t **) lhs))->invocation, - (*((teleport_anchor_t **) rhs))->invocation); -} - -char *magic_find_anchor_invocation (char *anchor_name) -{ - int i; - - for (i = 0; i < abs (magic_conf.anchors_nr); i++) - if (!strcmp (magic_conf.anchors[i]->name, anchor_name)) - return magic_conf.anchors[i]->invocation; - - return NULL; -} - -teleport_anchor_t *magic_find_anchor (char *name) -{ - teleport_anchor_t key; - teleport_anchor_t *keyp = &key; - teleport_anchor_t **retval; - - if (magic_conf.anchors_nr > 0) - { /* unsorted */ - qsort (magic_conf.anchors, magic_conf.anchors_nr, - sizeof (teleport_anchor_t *), compare_teleport_anchor); - magic_conf.anchors_nr = -magic_conf.anchors_nr; - } - - key.invocation = name; - - retval = (teleport_anchor_t **) bsearch (&keyp, - magic_conf.anchors, - -magic_conf.anchors_nr, - sizeof (teleport_anchor_t *), - compare_teleport_anchor); - - if (!retval) - return NULL; - else - return *retval; -} - -/* -------------------------------------------------------------------------------- */ -/* Spell guard checks */ -/* -------------------------------------------------------------------------------- */ - -static env_t *alloc_env (magic_conf_t * conf) -{ - env_t *env; - CREATE (env, env_t, 1); - CREATE (env->vars, val_t, conf->vars_nr); - env->base_env = conf; - return env; -} - -static env_t *clone_env (env_t * src) -{ - env_t *retval = alloc_env (src->base_env); - int i; - - for (i = 0; i < src->base_env->vars_nr; i++) - magic_copy_var (&retval->vars[i], &src->vars[i]); - - return retval; -} - -void magic_free_env (env_t * env) -{ - int i; - for (i = 0; i < env->base_env->vars_nr; i++) - magic_clear_var (&env->vars[i]); - free (env); -} - -env_t *spell_create_env (magic_conf_t * conf, spell_t * spell, - character_t * caster, int spellpower, char *param) -{ - env_t *env = alloc_env (conf); - - switch (spell->spellarg_ty) - { - - case SPELLARG_STRING: - set_env_string (spell->arg, param); - break; - - case SPELLARG_PC: - { - character_t *subject = map_nick2sd (param); - if (!subject) - subject = caster; - set_env_entity (spell->arg, &subject->bl); - free (param); - break; - } - - case SPELLARG_NONE: - free (param); - break; - - default: - free (param); - fprintf (stderr, "Unexpected spellarg type %d\n", - spell->spellarg_ty); - } - - set_env_entity (VAR_CASTER, &caster->bl); - set_env_int (VAR_SPELLPOWER, spellpower); - set_env_spell (VAR_SPELL, spell); - - return env; -} - -static void free_components (component_t ** component_holder) -{ - if (*component_holder == NULL) - return; - free_components (&(*component_holder)->next); - free (*component_holder); - *component_holder = NULL; -} - -void magic_add_component (component_t ** component_holder, int id, int count) -{ - if (count <= 0) - return; - - if (*component_holder == NULL) - { - component_t *component = - (component_t *) malloc (sizeof (component_t)); - component->next = NULL; - component->item_id = id; - component->count = count; - *component_holder = component; - } - else - { - component_t *component = *component_holder; - if (component->item_id == id) - { - component->count += count; - return; - } - else - magic_add_component (&component->next, id, count); - /* Tail-recurse; gcc can optimise this. Not that it matters. */ - } -} - -static void -copy_components (component_t ** component_holder, component_t * component) -{ - if (component == NULL) - return; - - magic_add_component (component_holder, component->item_id, - component->count); - copy_components (component_holder, component->next); -} - -typedef struct spellguard_check -{ - component_t *catalysts, *components; - int mana, casttime; -} spellguard_check_t; - -static int check_prerequisites (character_t * caster, component_t * component) -{ - while (component) - { - if (pc_count_all_items (caster, component->item_id) - < component->count) - return 0; /* insufficient */ - - component = component->next; - } - - return 1; -} - -static void consume_components (character_t * caster, component_t * component) -{ - while (component) - { - pc_remove_items (caster, component->item_id, component->count); - component = component->next; - } -} - -static int -spellguard_can_satisfy (spellguard_check_t * check, character_t * caster, - env_t * env, int *near_miss) -{ - unsigned int tick = gettick (); - - int retval = check_prerequisites (caster, check->catalysts); - -/* - fprintf(stderr, "MC(%d/%s)? %d%d%d%d (%u <= %u)\n", - caster->bl.id, caster->status.name, - retval, - caster->cast_tick <= tick, - check->mana <= caster->status.sp, - check_prerequisites(caster, check->components), - caster->cast_tick, tick); -*/ - - if (retval && near_miss) - *near_miss = 1; // close enough! - - retval = retval && caster->cast_tick <= tick /* Hasn't cast a spell too recently */ - && check->mana <= caster->status.sp - && check_prerequisites (caster, check->components); - - if (retval) - { - unsigned int casttime = (unsigned int) check->casttime; - - if (VAR (VAR_MIN_CASTTIME).ty == TY_INT) - casttime = MAX (casttime, VAR (VAR_MIN_CASTTIME).v.v_int); - - caster->cast_tick = tick + casttime; /* Make sure not to cast too frequently */ - - consume_components (caster, check->components); - pc_heal (caster, 0, -check->mana); - } - - return retval; -} - -static effect_set_t *spellguard_check_sub (spellguard_check_t * check, - spellguard_t * guard, - character_t * caster, env_t * env, - int *near_miss) -{ - if (guard == NULL) - return NULL; - - switch (guard->ty) - { - case SPELLGUARD_CONDITION: - if (!magic_eval_int (env, guard->s.s_condition)) - return NULL; - break; - - case SPELLGUARD_COMPONENTS: - copy_components (&check->components, guard->s.s_components); - break; - - case SPELLGUARD_CATALYSTS: - copy_components (&check->catalysts, guard->s.s_catalysts); - break; - - case SPELLGUARD_CHOICE: - { - spellguard_check_t altcheck = *check; - effect_set_t *retval; - - altcheck.components = NULL; - altcheck.catalysts = NULL; - - copy_components (&altcheck.catalysts, check->catalysts); - copy_components (&altcheck.components, check->components); - - retval = - spellguard_check_sub (&altcheck, guard->next, caster, env, - near_miss); - free_components (&altcheck.catalysts); - free_components (&altcheck.components); - if (retval) - return retval; - else - return spellguard_check_sub (check, guard->s.s_alt, caster, - env, near_miss); - } - - case SPELLGUARD_MANA: - check->mana += magic_eval_int (env, guard->s.s_mana); - break; - - case SPELLGUARD_CASTTIME: - check->casttime += magic_eval_int (env, guard->s.s_mana); - break; - - case SPELLGUARD_EFFECT: - if (spellguard_can_satisfy (check, caster, env, near_miss)) - return &guard->s.s_effect; - else - return NULL; - - default: - fprintf (stderr, "Unexpected spellguard type %d\n", guard->ty); - return NULL; - } - - return spellguard_check_sub (check, guard->next, caster, env, near_miss); -} - -static effect_set_t *check_spellguard (spellguard_t * guard, - character_t * caster, env_t * env, - int *near_miss) -{ - spellguard_check_t check; - effect_set_t *retval; - check.catalysts = NULL; - check.components = NULL; - check.mana = check.casttime = 0; - - retval = spellguard_check_sub (&check, guard, caster, env, near_miss); - - free_components (&check.catalysts); - free_components (&check.components); - - return retval; -} - -/* -------------------------------------------------------------------------------- */ -/* Public API */ -/* -------------------------------------------------------------------------------- */ - -effect_set_t *spell_trigger (spell_t * spell, character_t * caster, - env_t * env, int *near_miss) -{ - int i; - spellguard_t *guard = spell->spellguard; - - if (near_miss) - *near_miss = 0; - - for (i = 0; i < spell->letdefs_nr; i++) - magic_eval (env, - &env->vars[spell->letdefs[i].id], spell->letdefs[i].expr); - - return check_spellguard (guard, caster, env, near_miss); -} - -static void spell_set_location (invocation_t * invocation, entity_t * entity) -{ - magic_clear_var (&invocation->env->vars[VAR_LOCATION]); - invocation->env->vars[VAR_LOCATION].ty = TY_LOCATION; - invocation->env->vars[VAR_LOCATION].v.v_location.m = entity->m; - invocation->env->vars[VAR_LOCATION].v.v_location.x = entity->x; - invocation->env->vars[VAR_LOCATION].v.v_location.y = entity->y; -} - -void spell_update_location (invocation_t * invocation) -{ - if (invocation->spell->flags & SPELL_FLAG_LOCAL) - return; - else - { - character_t *owner = (character_t *) map_id2bl (invocation->subject); - if (!owner) - return; - - spell_set_location (invocation, (entity_t *) owner); - } -} - -invocation_t *spell_instantiate (effect_set_t * effect_set, env_t * env) -{ - invocation_t *retval; - CREATE (retval, invocation_t, 1); - entity_t *caster; - - retval->env = env; - - retval->caster = VAR (VAR_CASTER).v.v_int; - retval->spell = VAR (VAR_SPELL).v.v_spell; - retval->stack_size = 0; - retval->current_effect = effect_set->effect; - retval->trigger_effect = effect_set->at_trigger; - retval->end_effect = effect_set->at_end; - - caster = map_id2bl (retval->caster); // must still exist - retval->bl.id = map_addobject (&retval->bl); - retval->bl.type = BL_SPELL; - retval->bl.m = caster->m; - retval->bl.x = caster->x; - retval->bl.y = caster->y; - - map_addblock (&retval->bl); - set_env_invocation (VAR_INVOCATION, retval); - - return retval; -} - -invocation_t *spell_clone_effect (invocation_t * base) -{ - invocation_t *retval = (invocation_t *) malloc (sizeof (invocation_t)); - env_t *env; - - memcpy (retval, base, sizeof (invocation_t)); - - retval->env = clone_env (retval->env); - env = retval->env; - retval->current_effect = retval->trigger_effect; - retval->next_invocation = NULL; - retval->end_effect = NULL; - retval->script_pos = 0; - retval->stack_size = 0; - retval->timer = 0; - retval->subject = 0; - retval->status_change_refs_nr = 0; - retval->status_change_refs = NULL; - retval->flags = 0; - - retval->bl.id = 0; - retval->bl.prev = NULL; - retval->bl.next = NULL; - - retval->bl.id = map_addobject (&retval->bl); - set_env_invocation (VAR_INVOCATION, retval); - - return retval; -} - -void spell_bind (character_t * subject, invocation_t * invocation) -{ - /* Only bind nonlocal spells */ - - if (!(invocation->spell->flags & SPELL_FLAG_LOCAL)) - { - if (invocation->flags & INVOCATION_FLAG_BOUND - || invocation->subject || invocation->next_invocation) - { - int *i = NULL; - fprintf (stderr, - "[magic] INTERNAL ERROR: Attempt to re-bind spell invocation `%s'\n", - invocation->spell->name); - *i = 1; - return; - } - - invocation->next_invocation = subject->active_spells; - subject->active_spells = invocation; - invocation->flags |= INVOCATION_FLAG_BOUND; - invocation->subject = subject->bl.id; - } - - spell_set_location (invocation, (entity_t *) subject); -} - -int spell_unbind (character_t * subject, invocation_t * invocation) -{ - invocation_t **seeker = &subject->active_spells; - - while (*seeker) - { - if (*seeker == invocation) - { - *seeker = invocation->next_invocation; - - invocation->flags &= ~INVOCATION_FLAG_BOUND; - invocation->next_invocation = NULL; - invocation->subject = 0; - - return 0; - } - seeker = &((*seeker)->next_invocation); - } - - return 1; -} diff --git a/src/map/magic-interpreter-base.cpp b/src/map/magic-interpreter-base.cpp new file mode 100644 index 0000000..1dc90a5 --- /dev/null +++ b/src/map/magic-interpreter-base.cpp @@ -0,0 +1,562 @@ +#include "magic.hpp" +#include "magic-interpreter.hpp" +#include "magic-expr.hpp" +#include "magic-interpreter-aux.hpp" + +static void set_int_p (val_t * v, int i, int t) +{ + v->ty = t; + v->v.v_int = i; +} + +#define set_int(v, i) set_int_p(v, i, TY_INT) +#define set_dir(v, i) set_int_p(v, i, TY_DIR) + +#define SETTER(tty, dyn_ty, field) (val_t *v, tty x) { v->ty = dyn_ty; v->v.field = x; } + +static void set_string SETTER (char *, TY_STRING, v_string); + +static void set_entity (val_t * v, entity_t * e) +{ + v->ty = TY_ENTITY; + v->v.v_int = e->id; +} + +static void set_invocation (val_t * v, invocation_t * i) +{ + v->ty = TY_INVOCATION; + v->v.v_int = i->bl.id; +} + +static void set_spell SETTER (spell_t *, TY_SPELL, v_spell); + +#define setenv(f, v, x) f(&(env->vars[v]), x) + +#define set_env_int(v, x) setenv(set_int, v, x) +#define set_env_dir(v, x) setenv(set_dir, v, x) +#define set_env_string(v, x) setenv(set_string, v, x) +#define set_env_entity(v, x) setenv(set_entity, v, x) +#define set_env_location(v, x) setenv(set_location, v, x) +#define set_env_area(v, x) setenv(set_area, v, x) +#define set_env_invocation(v, x) setenv(set_invocation, v, x) +#define set_env_spell(v, x) setenv(set_spell, v, x) + +magic_conf_t magic_conf; /* Global magic conf */ +env_t magic_default_env = { &magic_conf, NULL }; + +static int spells_sorted = 0; + +char *magic_find_invocation (char *spellname) +{ + int i; + + for (i = 0; i < abs (magic_conf.spells_nr); i++) + if (!strcmp (magic_conf.spells[i]->name, spellname)) + return magic_conf.spells[i]->invocation; + + return NULL; +} + +static int spell_compare (const void *lhs, const void *rhs) +{ + return strcmp ((*((spell_t **) lhs))->invocation, + (*((spell_t **) rhs))->invocation); +} + +spell_t *magic_find_spell (char *invocation) +{ + spell_t key; + spell_t *keyp = &key; + spell_t **retval; + + if (!spells_sorted) + { + qsort (magic_conf.spells, magic_conf.spells_nr, sizeof (spell_t *), + spell_compare); + spells_sorted = 1; + } + + key.invocation = invocation; + + retval = + ((spell_t **) + bsearch (&keyp, magic_conf.spells, magic_conf.spells_nr, + sizeof (spell_t *), spell_compare)); + + if (!retval) + return NULL; + else + return *retval; +} + +/* -------------------------------------------------------------------------------- */ +/* Spell anchors */ +/* -------------------------------------------------------------------------------- */ + +static int compare_teleport_anchor (const void *lhs, const void *rhs) +{ + return strcmp ((*((teleport_anchor_t **) lhs))->invocation, + (*((teleport_anchor_t **) rhs))->invocation); +} + +char *magic_find_anchor_invocation (char *anchor_name) +{ + int i; + + for (i = 0; i < abs (magic_conf.anchors_nr); i++) + if (!strcmp (magic_conf.anchors[i]->name, anchor_name)) + return magic_conf.anchors[i]->invocation; + + return NULL; +} + +teleport_anchor_t *magic_find_anchor (char *name) +{ + teleport_anchor_t key; + teleport_anchor_t *keyp = &key; + teleport_anchor_t **retval; + + if (magic_conf.anchors_nr > 0) + { /* unsorted */ + qsort (magic_conf.anchors, magic_conf.anchors_nr, + sizeof (teleport_anchor_t *), compare_teleport_anchor); + magic_conf.anchors_nr = -magic_conf.anchors_nr; + } + + key.invocation = name; + + retval = (teleport_anchor_t **) bsearch (&keyp, + magic_conf.anchors, + -magic_conf.anchors_nr, + sizeof (teleport_anchor_t *), + compare_teleport_anchor); + + if (!retval) + return NULL; + else + return *retval; +} + +/* -------------------------------------------------------------------------------- */ +/* Spell guard checks */ +/* -------------------------------------------------------------------------------- */ + +static env_t *alloc_env (magic_conf_t * conf) +{ + env_t *env; + CREATE (env, env_t, 1); + CREATE (env->vars, val_t, conf->vars_nr); + env->base_env = conf; + return env; +} + +static env_t *clone_env (env_t * src) +{ + env_t *retval = alloc_env (src->base_env); + int i; + + for (i = 0; i < src->base_env->vars_nr; i++) + magic_copy_var (&retval->vars[i], &src->vars[i]); + + return retval; +} + +void magic_free_env (env_t * env) +{ + int i; + for (i = 0; i < env->base_env->vars_nr; i++) + magic_clear_var (&env->vars[i]); + free (env); +} + +env_t *spell_create_env (magic_conf_t * conf, spell_t * spell, + character_t * caster, int spellpower, char *param) +{ + env_t *env = alloc_env (conf); + + switch (spell->spellarg_ty) + { + + case SPELLARG_STRING: + set_env_string (spell->arg, param); + break; + + case SPELLARG_PC: + { + character_t *subject = map_nick2sd (param); + if (!subject) + subject = caster; + set_env_entity (spell->arg, &subject->bl); + free (param); + break; + } + + case SPELLARG_NONE: + free (param); + break; + + default: + free (param); + fprintf (stderr, "Unexpected spellarg type %d\n", + spell->spellarg_ty); + } + + set_env_entity (VAR_CASTER, &caster->bl); + set_env_int (VAR_SPELLPOWER, spellpower); + set_env_spell (VAR_SPELL, spell); + + return env; +} + +static void free_components (component_t ** component_holder) +{ + if (*component_holder == NULL) + return; + free_components (&(*component_holder)->next); + free (*component_holder); + *component_holder = NULL; +} + +void magic_add_component (component_t ** component_holder, int id, int count) +{ + if (count <= 0) + return; + + if (*component_holder == NULL) + { + component_t *component = + (component_t *) malloc (sizeof (component_t)); + component->next = NULL; + component->item_id = id; + component->count = count; + *component_holder = component; + } + else + { + component_t *component = *component_holder; + if (component->item_id == id) + { + component->count += count; + return; + } + else + magic_add_component (&component->next, id, count); + /* Tail-recurse; gcc can optimise this. Not that it matters. */ + } +} + +static void +copy_components (component_t ** component_holder, component_t * component) +{ + if (component == NULL) + return; + + magic_add_component (component_holder, component->item_id, + component->count); + copy_components (component_holder, component->next); +} + +typedef struct spellguard_check +{ + component_t *catalysts, *components; + int mana, casttime; +} spellguard_check_t; + +static int check_prerequisites (character_t * caster, component_t * component) +{ + while (component) + { + if (pc_count_all_items (caster, component->item_id) + < component->count) + return 0; /* insufficient */ + + component = component->next; + } + + return 1; +} + +static void consume_components (character_t * caster, component_t * component) +{ + while (component) + { + pc_remove_items (caster, component->item_id, component->count); + component = component->next; + } +} + +static int +spellguard_can_satisfy (spellguard_check_t * check, character_t * caster, + env_t * env, int *near_miss) +{ + unsigned int tick = gettick (); + + int retval = check_prerequisites (caster, check->catalysts); + +/* + fprintf(stderr, "MC(%d/%s)? %d%d%d%d (%u <= %u)\n", + caster->bl.id, caster->status.name, + retval, + caster->cast_tick <= tick, + check->mana <= caster->status.sp, + check_prerequisites(caster, check->components), + caster->cast_tick, tick); +*/ + + if (retval && near_miss) + *near_miss = 1; // close enough! + + retval = retval && caster->cast_tick <= tick /* Hasn't cast a spell too recently */ + && check->mana <= caster->status.sp + && check_prerequisites (caster, check->components); + + if (retval) + { + unsigned int casttime = (unsigned int) check->casttime; + + if (VAR (VAR_MIN_CASTTIME).ty == TY_INT) + casttime = MAX (casttime, VAR (VAR_MIN_CASTTIME).v.v_int); + + caster->cast_tick = tick + casttime; /* Make sure not to cast too frequently */ + + consume_components (caster, check->components); + pc_heal (caster, 0, -check->mana); + } + + return retval; +} + +static effect_set_t *spellguard_check_sub (spellguard_check_t * check, + spellguard_t * guard, + character_t * caster, env_t * env, + int *near_miss) +{ + if (guard == NULL) + return NULL; + + switch (guard->ty) + { + case SPELLGUARD_CONDITION: + if (!magic_eval_int (env, guard->s.s_condition)) + return NULL; + break; + + case SPELLGUARD_COMPONENTS: + copy_components (&check->components, guard->s.s_components); + break; + + case SPELLGUARD_CATALYSTS: + copy_components (&check->catalysts, guard->s.s_catalysts); + break; + + case SPELLGUARD_CHOICE: + { + spellguard_check_t altcheck = *check; + effect_set_t *retval; + + altcheck.components = NULL; + altcheck.catalysts = NULL; + + copy_components (&altcheck.catalysts, check->catalysts); + copy_components (&altcheck.components, check->components); + + retval = + spellguard_check_sub (&altcheck, guard->next, caster, env, + near_miss); + free_components (&altcheck.catalysts); + free_components (&altcheck.components); + if (retval) + return retval; + else + return spellguard_check_sub (check, guard->s.s_alt, caster, + env, near_miss); + } + + case SPELLGUARD_MANA: + check->mana += magic_eval_int (env, guard->s.s_mana); + break; + + case SPELLGUARD_CASTTIME: + check->casttime += magic_eval_int (env, guard->s.s_mana); + break; + + case SPELLGUARD_EFFECT: + if (spellguard_can_satisfy (check, caster, env, near_miss)) + return &guard->s.s_effect; + else + return NULL; + + default: + fprintf (stderr, "Unexpected spellguard type %d\n", guard->ty); + return NULL; + } + + return spellguard_check_sub (check, guard->next, caster, env, near_miss); +} + +static effect_set_t *check_spellguard (spellguard_t * guard, + character_t * caster, env_t * env, + int *near_miss) +{ + spellguard_check_t check; + effect_set_t *retval; + check.catalysts = NULL; + check.components = NULL; + check.mana = check.casttime = 0; + + retval = spellguard_check_sub (&check, guard, caster, env, near_miss); + + free_components (&check.catalysts); + free_components (&check.components); + + return retval; +} + +/* -------------------------------------------------------------------------------- */ +/* Public API */ +/* -------------------------------------------------------------------------------- */ + +effect_set_t *spell_trigger (spell_t * spell, character_t * caster, + env_t * env, int *near_miss) +{ + int i; + spellguard_t *guard = spell->spellguard; + + if (near_miss) + *near_miss = 0; + + for (i = 0; i < spell->letdefs_nr; i++) + magic_eval (env, + &env->vars[spell->letdefs[i].id], spell->letdefs[i].expr); + + return check_spellguard (guard, caster, env, near_miss); +} + +static void spell_set_location (invocation_t * invocation, entity_t * entity) +{ + magic_clear_var (&invocation->env->vars[VAR_LOCATION]); + invocation->env->vars[VAR_LOCATION].ty = TY_LOCATION; + invocation->env->vars[VAR_LOCATION].v.v_location.m = entity->m; + invocation->env->vars[VAR_LOCATION].v.v_location.x = entity->x; + invocation->env->vars[VAR_LOCATION].v.v_location.y = entity->y; +} + +void spell_update_location (invocation_t * invocation) +{ + if (invocation->spell->flags & SPELL_FLAG_LOCAL) + return; + else + { + character_t *owner = (character_t *) map_id2bl (invocation->subject); + if (!owner) + return; + + spell_set_location (invocation, (entity_t *) owner); + } +} + +invocation_t *spell_instantiate (effect_set_t * effect_set, env_t * env) +{ + invocation_t *retval; + CREATE (retval, invocation_t, 1); + entity_t *caster; + + retval->env = env; + + retval->caster = VAR (VAR_CASTER).v.v_int; + retval->spell = VAR (VAR_SPELL).v.v_spell; + retval->stack_size = 0; + retval->current_effect = effect_set->effect; + retval->trigger_effect = effect_set->at_trigger; + retval->end_effect = effect_set->at_end; + + caster = map_id2bl (retval->caster); // must still exist + retval->bl.id = map_addobject (&retval->bl); + retval->bl.type = BL_SPELL; + retval->bl.m = caster->m; + retval->bl.x = caster->x; + retval->bl.y = caster->y; + + map_addblock (&retval->bl); + set_env_invocation (VAR_INVOCATION, retval); + + return retval; +} + +invocation_t *spell_clone_effect (invocation_t * base) +{ + invocation_t *retval = (invocation_t *) malloc (sizeof (invocation_t)); + env_t *env; + + memcpy (retval, base, sizeof (invocation_t)); + + retval->env = clone_env (retval->env); + env = retval->env; + retval->current_effect = retval->trigger_effect; + retval->next_invocation = NULL; + retval->end_effect = NULL; + retval->script_pos = 0; + retval->stack_size = 0; + retval->timer = 0; + retval->subject = 0; + retval->status_change_refs_nr = 0; + retval->status_change_refs = NULL; + retval->flags = 0; + + retval->bl.id = 0; + retval->bl.prev = NULL; + retval->bl.next = NULL; + + retval->bl.id = map_addobject (&retval->bl); + set_env_invocation (VAR_INVOCATION, retval); + + return retval; +} + +void spell_bind (character_t * subject, invocation_t * invocation) +{ + /* Only bind nonlocal spells */ + + if (!(invocation->spell->flags & SPELL_FLAG_LOCAL)) + { + if (invocation->flags & INVOCATION_FLAG_BOUND + || invocation->subject || invocation->next_invocation) + { + int *i = NULL; + fprintf (stderr, + "[magic] INTERNAL ERROR: Attempt to re-bind spell invocation `%s'\n", + invocation->spell->name); + *i = 1; + return; + } + + invocation->next_invocation = subject->active_spells; + subject->active_spells = invocation; + invocation->flags |= INVOCATION_FLAG_BOUND; + invocation->subject = subject->bl.id; + } + + spell_set_location (invocation, (entity_t *) subject); +} + +int spell_unbind (character_t * subject, invocation_t * invocation) +{ + invocation_t **seeker = &subject->active_spells; + + while (*seeker) + { + if (*seeker == invocation) + { + *seeker = invocation->next_invocation; + + invocation->flags &= ~INVOCATION_FLAG_BOUND; + invocation->next_invocation = NULL; + invocation->subject = 0; + + return 0; + } + seeker = &((*seeker)->next_invocation); + } + + return 1; +} diff --git a/src/map/magic-interpreter-lexer.l b/src/map/magic-interpreter-lexer.l deleted file mode 100644 index 2c9792f..0000000 --- a/src/map/magic-interpreter-lexer.l +++ /dev/null @@ -1,140 +0,0 @@ -%{ -#include "magic-interpreter.h" -#include "magic-interpreter-parser.h" - -#ifdef HEADING -# undef HEADING -#endif - -#define FIXLOC magic_frontend_lloc.first_line = magic_frontend_lineno - -#define HEADING(dir) { magic_frontend_lval.i = dir; FIXLOC; return DIR; } - -%} - -%option yylineno -%option noyywrap -%option prefix="magic_frontend_" -%option nounput -%option noinput -%option bison-bridge bison-locations - -%% - -"S" HEADING(0); -"SW" HEADING(1); -"W" HEADING(2); -"NW" HEADING(3); -"N" HEADING(4); -"NE" HEADING(5); -"E" HEADING(6); -"SE" HEADING(7); -"=" {FIXLOC; return '=';} -"==" {FIXLOC; return EQ;} -"<>" {FIXLOC; return NEQ;} -"!=" {FIXLOC; return NEQ;} -">" {FIXLOC; return '>';} -"<" {FIXLOC; return '<';} -">=" {FIXLOC; return GTE;} -"<=" {FIXLOC; return LTE;} -"(" {FIXLOC; return '(';} -")" {FIXLOC; return ')';} -"+" {FIXLOC; return '+';} -"-" {FIXLOC; return '-';} -"*" {FIXLOC; return '*';} -"/" {FIXLOC; return '/';} -"%" {FIXLOC; return '%';} -"&&" {FIXLOC; return ANDAND;} -"||" {FIXLOC; return OROR;} -";" {FIXLOC; return ';';} -":" {FIXLOC; return ':';} -"," {FIXLOC; return ',';} -"@" {FIXLOC; return '@';} -"|" {FIXLOC; return '|';} -"[" {FIXLOC; return '[';} -"]" {FIXLOC; return ']';} -"&" {FIXLOC; return '&';} -"^" {FIXLOC; return '^';} -"." {FIXLOC; return '.';} -"<<" {FIXLOC; return SHL;} -">>" {FIXLOC; return SHR;} -"PROCEDURE" {FIXLOC; return PROCEDURE;} -"CALL" {FIXLOC; return CALL;} -"OR" {FIXLOC; return OR;} -"TO" {FIXLOC; return TO;} -"TOWARDS" {FIXLOC; return TOWARDS;} -"TELEPORT-ANCHOR" {FIXLOC; return TELEPORT_ANCHOR;} -"SILENT" {FIXLOC; return SILENT;} -"LOCAL" {FIXLOC; return LOCAL;} -"NONMAGIC" {FIXLOC; return NONMAGIC;} -"SPELL" {FIXLOC; return SPELL;} -"LET" {FIXLOC; return LET;} -"IN" {FIXLOC; return IN;} -"END" {FIXLOC; return END;} -"=>" {FIXLOC; return DARROW;} -"STRING" {FIXLOC; return STRING_TY;} -"REQUIRE" {FIXLOC; return REQUIRE;} -"CATALYSTS" {FIXLOC; return CATALYSTS;} -"COMPONENTS" {FIXLOC; return COMPONENTS;} -"MANA" {FIXLOC; return MANA;} -"CASTTIME" {FIXLOC; return CASTTIME;} -"SKIP" {FIXLOC; return SKIP;} -"ABORT" {FIXLOC; return ABORT;} -"BREAK" {FIXLOC; return BREAK;} -"EFFECT" {FIXLOC; return EFFECT;} -"ATEND" {FIXLOC; return ATEND;} -"ATTRIGGER" {FIXLOC; return ATTRIGGER;} -"CONST" {FIXLOC; return CONST;} -"PC" {FIXLOC; return PC_F;} -"NPC" {FIXLOC; return NPC_F;} -"MOB" {FIXLOC; return MOB_F;} -"ENTITY" {FIXLOC; return ENTITY_F;} -"TARGET" {FIXLOC; return TARGET_F;} -"IF" {FIXLOC; return IF;} -"THEN" {FIXLOC; return THEN;} -"ELSE" {FIXLOC; return ELSE;} -"FOREACH" {FIXLOC; return FOREACH;} -"FOR" {FIXLOC; return FOR;} -"DO" {FIXLOC; return DO;} -"WAIT" {FIXLOC; return SLEEP;} - -\{([^\}]|\\.)*\} { char *string = strdup(yytext); - magic_frontend_lval.s = string; - FIXLOC; - return SCRIPT_DATA; - } - -\"([^\"]|\\.)*\" { char *string = strdup(yytext + 1); - char *src = string; - char *dst = string; - while (*src && *src != '"') - if (*src == '\\') { - *dst++ = src[1]; - src += 2; - } else - *dst++ = *src++; - *dst = '\0'; /* terminate */ - magic_frontend_lval.s = string; - FIXLOC; - return STRING; - } - -"-"?[0-9]+ { magic_frontend_lval.i = atoi(yytext); - FIXLOC; - return INT; } - -"0x"[0-9a-fA-F]+ { magic_frontend_lval.i = strtol(yytext + 2, NULL, 16); - FIXLOC; - return INT; } - -[a-zA-Z][-_a-zA-Z0-9?!]* { magic_frontend_lval.s = strdup(yytext); - FIXLOC; - return ID; } - -"#".*$ /* Ignore comments */ -"//".*$ /* Ignore comments */ -[ \n\t\r] /* ignore whitespace */ -. fprintf(stderr, "%s: Unexpected character in line %d\n", MAGIC_CONFIG_FILE, magic_frontend_lineno); - - -%% diff --git a/src/map/magic-interpreter-lexer.lpp b/src/map/magic-interpreter-lexer.lpp new file mode 100644 index 0000000..c7f9c09 --- /dev/null +++ b/src/map/magic-interpreter-lexer.lpp @@ -0,0 +1,140 @@ +%{ +#include "magic-interpreter.hpp" +#include "magic-interpreter-parser.hpp" + +#ifdef HEADING +# undef HEADING +#endif + +#define FIXLOC magic_frontend_lloc.first_line = magic_frontend_lineno + +#define HEADING(dir) { magic_frontend_lval.i = dir; FIXLOC; return DIR; } + +%} + +%option yylineno +%option noyywrap +%option prefix="magic_frontend_" +%option nounput +%option noinput +%option bison-bridge bison-locations + +%% + +"S" HEADING(0); +"SW" HEADING(1); +"W" HEADING(2); +"NW" HEADING(3); +"N" HEADING(4); +"NE" HEADING(5); +"E" HEADING(6); +"SE" HEADING(7); +"=" {FIXLOC; return '=';} +"==" {FIXLOC; return EQ;} +"<>" {FIXLOC; return NEQ;} +"!=" {FIXLOC; return NEQ;} +">" {FIXLOC; return '>';} +"<" {FIXLOC; return '<';} +">=" {FIXLOC; return GTE;} +"<=" {FIXLOC; return LTE;} +"(" {FIXLOC; return '(';} +")" {FIXLOC; return ')';} +"+" {FIXLOC; return '+';} +"-" {FIXLOC; return '-';} +"*" {FIXLOC; return '*';} +"/" {FIXLOC; return '/';} +"%" {FIXLOC; return '%';} +"&&" {FIXLOC; return ANDAND;} +"||" {FIXLOC; return OROR;} +";" {FIXLOC; return ';';} +":" {FIXLOC; return ':';} +"," {FIXLOC; return ',';} +"@" {FIXLOC; return '@';} +"|" {FIXLOC; return '|';} +"[" {FIXLOC; return '[';} +"]" {FIXLOC; return ']';} +"&" {FIXLOC; return '&';} +"^" {FIXLOC; return '^';} +"." {FIXLOC; return '.';} +"<<" {FIXLOC; return SHL;} +">>" {FIXLOC; return SHR;} +"PROCEDURE" {FIXLOC; return PROCEDURE;} +"CALL" {FIXLOC; return CALL;} +"OR" {FIXLOC; return OR;} +"TO" {FIXLOC; return TO;} +"TOWARDS" {FIXLOC; return TOWARDS;} +"TELEPORT-ANCHOR" {FIXLOC; return TELEPORT_ANCHOR;} +"SILENT" {FIXLOC; return SILENT;} +"LOCAL" {FIXLOC; return LOCAL;} +"NONMAGIC" {FIXLOC; return NONMAGIC;} +"SPELL" {FIXLOC; return SPELL;} +"LET" {FIXLOC; return LET;} +"IN" {FIXLOC; return IN;} +"END" {FIXLOC; return END;} +"=>" {FIXLOC; return DARROW;} +"STRING" {FIXLOC; return STRING_TY;} +"REQUIRE" {FIXLOC; return REQUIRE;} +"CATALYSTS" {FIXLOC; return CATALYSTS;} +"COMPONENTS" {FIXLOC; return COMPONENTS;} +"MANA" {FIXLOC; return MANA;} +"CASTTIME" {FIXLOC; return CASTTIME;} +"SKIP" {FIXLOC; return SKIP;} +"ABORT" {FIXLOC; return ABORT;} +"BREAK" {FIXLOC; return BREAK;} +"EFFECT" {FIXLOC; return EFFECT;} +"ATEND" {FIXLOC; return ATEND;} +"ATTRIGGER" {FIXLOC; return ATTRIGGER;} +"CONST" {FIXLOC; return CONST;} +"PC" {FIXLOC; return PC_F;} +"NPC" {FIXLOC; return NPC_F;} +"MOB" {FIXLOC; return MOB_F;} +"ENTITY" {FIXLOC; return ENTITY_F;} +"TARGET" {FIXLOC; return TARGET_F;} +"IF" {FIXLOC; return IF;} +"THEN" {FIXLOC; return THEN;} +"ELSE" {FIXLOC; return ELSE;} +"FOREACH" {FIXLOC; return FOREACH;} +"FOR" {FIXLOC; return FOR;} +"DO" {FIXLOC; return DO;} +"WAIT" {FIXLOC; return SLEEP;} + +\{([^\}]|\\.)*\} { char *string = strdup(yytext); + magic_frontend_lval.s = string; + FIXLOC; + return SCRIPT_DATA; + } + +\"([^\"]|\\.)*\" { char *string = strdup(yytext + 1); + char *src = string; + char *dst = string; + while (*src && *src != '"') + if (*src == '\\') { + *dst++ = src[1]; + src += 2; + } else + *dst++ = *src++; + *dst = '\0'; /* terminate */ + magic_frontend_lval.s = string; + FIXLOC; + return STRING; + } + +"-"?[0-9]+ { magic_frontend_lval.i = atoi(yytext); + FIXLOC; + return INT; } + +"0x"[0-9a-fA-F]+ { magic_frontend_lval.i = strtol(yytext + 2, NULL, 16); + FIXLOC; + return INT; } + +[a-zA-Z][-_a-zA-Z0-9?!]* { magic_frontend_lval.s = strdup(yytext); + FIXLOC; + return ID; } + +"#".*$ /* Ignore comments */ +"//".*$ /* Ignore comments */ +[ \n\t\r] /* ignore whitespace */ +. fprintf(stderr, "%s: Unexpected character in line %d\n", MAGIC_CONFIG_FILE, magic_frontend_lineno); + + +%% diff --git a/src/map/magic-interpreter-parser.y b/src/map/magic-interpreter-parser.y deleted file mode 100644 index 9df0f3e..0000000 --- a/src/map/magic-interpreter-parser.y +++ /dev/null @@ -1,1089 +0,0 @@ -%{ -#include "magic-interpreter.h" -#include "magic-expr.h" -#include <stdarg.h> - -magic_conf_t magic_conf; - -static int -intern_id(const char *id_name); - - -static expr_t * -fun_expr(const char *name, int args_nr, expr_t **args, int line, int column); - -static expr_t * -dot_expr(expr_t *lhs, int id); - -#define BIN_EXPR(x, name, arg1, arg2, line, column) { expr_t *e[2]; e[0] = arg1; e[1] = arg2; x = fun_expr(name, 2, e, line, column); } - -static int failed_flag = 0; - -static void -magic_frontend_error(const char *msg); - -static void -fail(int line, int column, const char *fmt, ...); - -static spell_t * -new_spell(spellguard_t *guard); - -static spellguard_t * -spellguard_implication(spellguard_t *a, spellguard_t *b); - -static spellguard_t * -new_spellguard(int ty); - -static effect_t * -new_effect(int ty); - -static effect_t * -set_effect_continuation(effect_t *src, effect_t *continuation); - -static void -add_spell(spell_t *spell, int line_nr); - -static void -add_teleport_anchor(teleport_anchor_t *anchor, int line_nr); - -static effect_t * -op_effect(char *name, int args_nr, expr_t **args, int line, int column); - -int -magic_frontend_lex(void); - -static void -install_proc(proc_t *proc); - -static effect_t * -call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column); - -static void -bind_constant(char *name, val_t *val, int line_nr); - -static val_t * -find_constant(char *name); - - -%} - -%name-prefix="magic_frontend_" - -%locations - -%union { - int i; - char *s; - int op; - magic_conf_t *magic_conf; - val_t value; - expr_t *expr; - e_location_t location; - e_area_t area; - args_rec_t arg_list; - struct { int letdefs_nr; letdef_t *letdefs; } letdefs; - spell_t *spell; - struct { int id, ty; } spellarg_def; - letdef_t vardef; - spellguard_t *spellguard; - component_t *components; - struct {int id, count; } component; - effect_t *effect; - proc_t *proc; -}; - -%expect 7 - -%token <i> INT -%token <s> STRING -%token <s> ID -%token <i> DIR - -%token '=' -%token '<' -%token '>' -%token '+' -%token '-' -%token '*' -%token '/' -%token '%' -%token '@' -%token ',' -%token '.' -%token ':' -%token ';' -%token '|' -%token '[' -%token ']' -%token '&' -%token '^' - -%token CONST -%token PROCEDURE -%token CALL -%token SILENT -%token LOCAL -%token NONMAGIC -%token SHL -%token SHR -%token EQ -%token NEQ -%token GTE -%token LTE -%token ANDAND -%token OROR -%token <s> SCRIPT_DATA -%token TO -%token TOWARDS -%token TELEPORT_ANCHOR -%token SPELL -%token LET -%token IN -%token END -%token DARROW -%token STRING_TY -%token REQUIRE -%token CATALYSTS -%token COMPONENTS -%token MANA -%token CASTTIME -%token SKIP -%token ABORT -%token BREAK -%token EFFECT -%token ATEND -%token ATTRIGGER -%token PC_F -%token NPC_F -%token MOB_F -%token ENTITY_F -%token TARGET_F -%token IF -%token THEN -%token ELSE -%token FOREACH -%token FOR -%token DO -%token SLEEP - -%type <value> value -%type <location> location -%type <area> area -%type <arg_list> arg_list -%type <arg_list> arg_list_ne -%type <letdefs> defs -%type <spell> spelldef -%type <spellarg_def> argopt -%type <vardef> def -%type <spellguard> spellbody_list -%type <spellguard> spellbody -%type <spellguard> spellguard -%type <spellguard> spellguard_list -%type <spellguard> prereq -%type <component> item -%type <components> items -%type <components> item_list -%type <i> item_name -%type <i> selection; -%type <effect> effect -%type <effect> effect_list -%type <effect> maybe_trigger -%type <effect> maybe_end -%type <i> spell_flags; - -%type <expr> expr -%type <i> arg_ty -%type <proc> proc_formals_list -%type <proc> proc_formals_list_ne - -%left OROR -%left ANDAND -%left '<' '>' GTE LTE NEQ EQ -%left '+' '-' -%left '*' '/' '%' -%left SHL SHR '&' '^' '|' -%right '=' -%left OR -%left DARROW -%left '.' - -%% - -spellconf : /* empty */ - {} - | spellconf_option semicolons spellconf - {} - ; - - -semicolons : /* empty */ - {} - | semicolons ';' - {} - ; - - -proc_formals_list : /* empty */ - { CREATE ($$, proc_t, 1); } - | proc_formals_list_ne - { $$ = $1; } - ; - -proc_formals_list_ne : ID - { CREATE ($$, proc_t, 1); - $$->args_nr = 1; - $$->args = (int*)malloc(sizeof(int)); - $$->args[0] = intern_id($1); - } - | proc_formals_list_ne ',' ID - { $$ = $1; - $$->args = (int*)realloc($$->args, sizeof(int) * (1 + $$->args_nr)); - $$->args[$$->args_nr++] = intern_id($3); - } - ; - -spellconf_option : ID '=' expr - { - int var_id; - if (find_constant($1)) { - fail(@1.first_line, 0, "Attempt to redefine constant `%s' as global\n", $1); - free($1); - } else { - var_id = intern_id($1); - magic_eval(&magic_default_env, &magic_conf.vars[var_id], $3); - } - } - | CONST ID '=' expr - { - val_t var; - magic_eval(&magic_default_env, &var, $4); - bind_constant($2, &var, @1.first_line); - } - | TELEPORT_ANCHOR ID ':' expr '=' expr - { - teleport_anchor_t *anchor; - CREATE (anchor, teleport_anchor_t, 1); - anchor->name = $2; - anchor->invocation = magic_eval_str(&magic_default_env, $4); - anchor->location = $6; - - if (!failed_flag) - add_teleport_anchor(anchor, @1.first_line); - else - free(anchor); - failed_flag = 0; - } - | PROCEDURE ID '(' proc_formals_list ')' '=' effect_list - { - proc_t *proc = $4; - proc->name = $2; - proc->body = $7; - if (!failed_flag) - install_proc(proc); - failed_flag = 0; - } - | spell_flags SPELL ID argopt ':' expr '=' spelldef - { spell_t *spell = $8; - spell->name = $3; - spell->invocation = magic_eval_str(&magic_default_env, $6); - spell->arg = $4.id; - spell->spellarg_ty = $4.ty; - spell->flags = $1; - if (!failed_flag) - add_spell(spell, @1.first_line); - failed_flag = 0; - } - -spell_flags : /* empty */ - { $$ = 0; } - | LOCAL spell_flags - { if ($2 & SPELL_FLAG_LOCAL) - fail(@1.first_line, @1.first_column, "`LOCAL' specified more than once"); - $$ = $2 | SPELL_FLAG_LOCAL; - } - | NONMAGIC spell_flags - { if ($2 & SPELL_FLAG_NONMAGIC) - fail(@1.first_line, @1.first_column, "`NONMAGIC' specified more than once"); - $$ = $2 | SPELL_FLAG_NONMAGIC; - } - | SILENT spell_flags - { if ($2 & SPELL_FLAG_SILENT) - fail(@1.first_line, @1.first_column, "`SILENT' specified more than once"); - $$ = $2 | SPELL_FLAG_SILENT; - } - - -argopt : /* empty */ - { $$.ty = SPELLARG_NONE; } - | '(' ID ':' arg_ty ')' - { $$.id = intern_id($2); - $$.ty = $4; } - ; - - -arg_ty : PC_F - { $$ = SPELLARG_PC; } - | STRING_TY - { $$ = SPELLARG_STRING; } - ; - - -value : DIR - { $$.ty = TY_DIR; - $$.v.v_int = $1; } - | INT - { $$.ty = TY_INT; - $$.v.v_int = $1; } - | STRING - { $$.ty = TY_STRING; - $$.v.v_string = $1; } - ; - - -expr : value - { $$ = magic_new_expr(EXPR_VAL); - $$->e.e_val = $1; } - | ID - { - val_t *val; - if ((val = find_constant($1))) { - $$ = magic_new_expr(EXPR_VAL); - $$->e.e_val = *val; - } else { - $$ = magic_new_expr(EXPR_ID); - $$->e.e_id = intern_id($1); - } - } - | area - { $$ = magic_new_expr(EXPR_AREA); - $$->e.e_area = $1; } - | expr '+' expr - { BIN_EXPR($$, "+", $1, $3, @1.first_line, @1.first_column); } - | expr '-' expr - { BIN_EXPR($$, "-", $1, $3, @1.first_line, @1.first_column); } - | expr '*' expr - { BIN_EXPR($$, "*", $1, $3, @1.first_line, @1.first_column); } - | expr '%' expr - { BIN_EXPR($$, "%", $1, $3, @1.first_line, @1.first_column); } - | expr '/' expr - { BIN_EXPR($$, "/", $1, $3, @1.first_line, @1.first_column); } - | expr '<' expr - { BIN_EXPR($$, ">", $3, $1, @1.first_line, @1.first_column); } - | expr '>' expr - { BIN_EXPR($$, ">", $1, $3, @1.first_line, @1.first_column); } - | expr '&' expr - { BIN_EXPR($$, "&", $1, $3, @1.first_line, @1.first_column); } - | expr '^' expr - { BIN_EXPR($$, "^", $1, $3, @1.first_line, @1.first_column); } - | expr '|' expr - { BIN_EXPR($$, "|", $1, $3, @1.first_line, @1.first_column); } - | expr SHL expr - { BIN_EXPR($$, "<<", $1, $3, @1.first_line, @1.first_column); } - | expr SHR expr - { BIN_EXPR($$, ">>", $1, $3, @1.first_line, @1.first_column); } - | expr LTE expr - { BIN_EXPR($$, ">=", $3, $1, @1.first_line, @1.first_column); } - | expr GTE expr - { BIN_EXPR($$, ">=", $1, $3, @1.first_line, @1.first_column); } - | expr ANDAND expr - { BIN_EXPR($$, "&&", $1, $3, @1.first_line, @1.first_column); } - | expr OROR expr - { BIN_EXPR($$, "||", $1, $3, @1.first_line, @1.first_column); } - | expr EQ expr - { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); } - | expr '=' expr - { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); } - | expr NEQ expr - { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); - $$ = fun_expr("not", 1, &$$, @1.first_line, @1.first_column); } - | ID '(' arg_list ')' - { $$ = fun_expr($1, $3.args_nr, $3.args, @1.first_line, @1.first_column); - if ($3.args) - free($3.args); - free($1); } - | '(' expr ')' - { $$ = $2; } - | expr '.' ID - { $$ = dot_expr($1, intern_id($3)); } - ; - -arg_list : /* empty */ - { $$.args_nr = 0; } - | arg_list_ne - { $$ = $1; } - ; - - -arg_list_ne : expr - { CREATE($$.args, expr_t *, 1); - $$.args_nr = 1; - $$.args[0] = $1; - } - | arg_list_ne ',' expr - { RECREATE($$.args, expr_t *, 1 + $$.args_nr); - $$.args[$$.args_nr++] = $3; - } - ; - - -location : '@' '(' expr ',' expr ',' expr ')' - { $$.m = $3; $$.x = $5; $$.y = $7; } - ; - -area : location - { $$.ty = AREA_LOCATION; - $$.a.a_loc = $1; - } - | location '@' '+' '(' expr ',' expr ')' - { $$.ty = AREA_RECT; - $$.a.a_rect.loc = $1; - $$.a.a_rect.width = $5; - $$.a.a_rect.height = $7; - } - | location TOWARDS expr ':' '(' expr ',' expr ')' - { $$.ty = AREA_BAR; - $$.a.a_bar.loc = $1; - $$.a.a_bar.width = $6; - $$.a.a_bar.depth = $8; - $$.a.a_bar.dir = $3; - } - ; - - -spelldef : spellbody_list - { $$ = new_spell($1); } - | LET defs IN spellbody_list - { $$ = new_spell($4); - $$->letdefs_nr = $2.letdefs_nr; - $$->letdefs = $2.letdefs; - $$->spellguard = $4; - } - ; - - -defs : semicolons - { $$.letdefs_nr = 0; - CREATE($$.letdefs, letdef_t, 1); - } - | defs def semicolons - { $$ = $1; - $$.letdefs_nr++; - RECREATE ($$.letdefs, letdef_t, $$.letdefs_nr); - $$.letdefs[$1.letdefs_nr] = $2; - } - ; - - -def : ID '=' expr - { - if (find_constant($1)) { - fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' as LET-bound variable.\n", $1); - free($1); - } else { - $$.id = intern_id($1); - $$.expr = $3; - } - } - ; - - -spellbody_list : spellbody - { $$ = $1; } - | spellbody '|' spellbody_list - { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE); - sg->next = $1; - sg->s.s_alt = $3; - $$ = sg; - } - ; - - -spellbody : spellguard DARROW spellbody - { $$ = spellguard_implication($1, $3); } - | '(' spellbody_list ')' - { $$ = $2; } - | EFFECT effect_list maybe_trigger maybe_end - { spellguard_t *sg = new_spellguard(SPELLGUARD_EFFECT); - sg->s.s_effect.effect = $2; - sg->s.s_effect.at_trigger = $3; - sg->s.s_effect.at_end = $4; - $$ = sg; - } - ; - - -maybe_trigger : /* empty */ - { $$ = NULL; } - | ATTRIGGER effect_list - { $$ = $2; } - ; - - -maybe_end : /* empty */ - { $$ = NULL; } - | ATEND effect_list - { $$ = $2; } - ; - - -spellguard : prereq - { $$ = $1; } - | spellguard OR spellguard - { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE); - sg->next = $1; - sg->s.s_alt = $3; - $$ = sg; - } - | '(' spellguard_list ')' - { $$ = $2; } - ; - - -spellguard_list : spellguard - { $$ = $1; } - | spellguard ',' spellguard_list - { $$ = spellguard_implication ($1, $3); } - ; - - -prereq : REQUIRE expr - { $$ = new_spellguard(SPELLGUARD_CONDITION); - $$->s.s_condition = $2; - } - | CATALYSTS items - { $$ = new_spellguard(SPELLGUARD_CATALYSTS); - $$->s.s_catalysts = $2; - } - | COMPONENTS items - { $$ = new_spellguard(SPELLGUARD_COMPONENTS); - $$->s.s_components = $2; - } - | MANA expr - { $$ = new_spellguard(SPELLGUARD_MANA); - $$->s.s_mana = $2; - } - | CASTTIME expr - { $$ = new_spellguard(SPELLGUARD_CASTTIME); - $$->s.s_casttime = $2; - } - ; - - -items : '[' item_list ']' - { $$ = $2; } - ; - - -item_list : item - { $$ = NULL; - magic_add_component(&$$, $1.id, $1.count); - } - | item_list ',' item - { $$ = $1; - magic_add_component(&$$, $3.id, $3.count); - } - ; - - -item : INT '*' item_name - { $$.id = $3; $$.count = $1; } - | item_name - { $$.id = $1; $$.count = 1; } - ; - - -item_name : STRING - { struct item_data *item = itemdb_searchname($1); - if (!item) { - fail (@1.first_line, @1.first_column, "Unknown item `%s'\n", $1); - $$ = 0; - } else - $$ = item->nameid; - free ($1); - } - | INT - { $$ = $1; } - ; - - -selection : PC_F - { $$ = FOREACH_FILTER_PC; } - | MOB_F - { $$ = FOREACH_FILTER_MOB; } - | ENTITY_F - { $$ = FOREACH_FILTER_ENTITY; } - | SPELL - { $$ = FOREACH_FILTER_SPELL; } - | TARGET_F - { $$ = FOREACH_FILTER_TARGET; } - | NPC_F - { $$ = FOREACH_FILTER_NPC; } - ; - - -effect : '(' effect_list ')' - { $$ = $2; } - | SKIP ';' - { $$ = new_effect(EFFECT_SKIP); } - | ABORT ';' - { $$ = new_effect(EFFECT_ABORT); } - | END ';' - { $$ = new_effect(EFFECT_END); } - | BREAK ';' - { $$ = new_effect(EFFECT_BREAK); } - | ID '=' expr ';' - { - if (find_constant($1)) { - fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' in assignment.", $1); - free($1); - } else { - $$ = new_effect(EFFECT_ASSIGN); - $$->e.e_assign.id = intern_id($1); - $$->e.e_assign.expr = $3; - } - } - | FOREACH selection ID IN expr DO effect - { $$ = new_effect(EFFECT_FOREACH); - $$->e.e_foreach.id = intern_id($3); - $$->e.e_foreach.area = $5; - $$->e.e_foreach.body = $7; - $$->e.e_foreach.filter = $2; - } - | FOR ID '=' expr TO expr DO effect - { $$ = new_effect(EFFECT_FOR); - $$->e.e_for.id = intern_id($2); - $$->e.e_for.start = $4; - $$->e.e_for.stop = $6; - $$->e.e_for.body = $8; - } - | IF expr THEN effect ELSE effect - { $$ = new_effect(EFFECT_IF); - $$->e.e_if.cond = $2; - $$->e.e_if.true_branch = $4; - $$->e.e_if.false_branch = $6; - } - | IF expr THEN effect - { $$ = new_effect(EFFECT_IF); - $$->e.e_if.cond = $2; - $$->e.e_if.true_branch = $4; - $$->e.e_if.false_branch = new_effect(EFFECT_SKIP); - } - | SLEEP expr ';' - { $$ = new_effect(EFFECT_SLEEP); - $$->e.e_sleep = $2; - } - | ID '(' arg_list ')' ';' - { $$ = op_effect($1, $3.args_nr, $3.args, @1.first_line, @1.first_column); - free($1); - } - | SCRIPT_DATA - { $$ = new_effect(EFFECT_SCRIPT); - $$->e.e_script = parse_script((unsigned char *) $1, @1.first_line); - free($1); - if ($$->e.e_script == NULL) - fail(@1.first_line, @1.first_column, "Failed to compile script\n"); - } - | CALL ID '(' arg_list ')' ';' - { $$ = call_proc($2, $4.args_nr, $4.args, @1.first_line, @1.first_column); - free($2); - } - ; - -effect_list : /* empty */ - { $$ = new_effect(EFFECT_SKIP); } - | effect semicolons effect_list - { $$ = set_effect_continuation($1, $3); } - ; - - -%% - -/* We do incremental realloc here to store our results. Since this happens only once - * during startup for a relatively manageable set of configs, it should be fine. */ - -static int -intern_id(const char *id_name) -{ - int i; - - for (i = 0; i < magic_conf.vars_nr; i++) - if (!strcmp(id_name, magic_conf.var_name[i])) { - free((char*)id_name); - return i; - } - - /* Must add new */ - i = magic_conf.vars_nr++; - RECREATE(magic_conf.var_name, const char *, magic_conf.vars_nr); - magic_conf.var_name[i] = id_name; - RECREATE(magic_conf.vars, val_t, magic_conf.vars_nr); - magic_conf.vars[i].ty = TY_UNDEF; - - return i; -} - -static void -add_spell(spell_t *spell, int line_nr) -{ - int index = magic_conf.spells_nr; - int i; - - for (i = 0; i < index; i++) { - if (!strcmp(magic_conf.spells[i]->name, spell->name)) { - fail(line_nr, 0, "Attempt to redefine spell `%s'\n", spell->name); - return; - } - if (!strcmp(magic_conf.spells[i]->invocation, spell->invocation)) { - fail(line_nr, 0, "Attempt to redefine spell invocation `%s' between spells `%s' and `%s'\n", - spell->invocation, magic_conf.spells[i]->name, spell->name); - return; - } - } - magic_conf.spells_nr++; - - RECREATE(magic_conf.spells, spell_t *, magic_conf.spells_nr); - magic_conf.spells[index] = spell; - - -} - -static void -add_teleport_anchor(teleport_anchor_t *anchor, int line_nr) -{ - int index = magic_conf.anchors_nr; - int i; - - for (i = 0; i < index; i++) { - if (!strcmp(magic_conf.anchors[i]->name, anchor->name)) { - fail(line_nr, 0, "Attempt to redefine teleport anchor `%s'\n", anchor->name); - return; - } - if (!strcmp(magic_conf.anchors[i]->invocation, anchor->invocation)) { - fail(line_nr, 0, "Attempt to redefine anchor invocation `%s' between anchors `%s' and `%s'\n", - anchor->invocation, magic_conf.anchors[i]->name, anchor->name); - return; - } - } - magic_conf.anchors_nr++; - - RECREATE(magic_conf.anchors, teleport_anchor_t *, magic_conf.anchors_nr); - magic_conf.anchors[index] = anchor; -} - - -static void -fail(int line, int column, const char *fmt, ...) -{ - va_list ap; - fprintf(stderr, "[magic-init] L%d:%d: ", line, column); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - failed_flag = 1; -} - -static expr_t * -dot_expr(expr_t *expr, int id) -{ - expr_t *retval = magic_new_expr(EXPR_SPELLFIELD); - retval->e.e_field.id = id; - retval->e.e_field.expr = expr; - - return retval; -} - -static expr_t * -fun_expr(const char *name, int args_nr, expr_t **args, int line, int column) -{ - int id; - expr_t *expr; - fun_t *fun = magic_get_fun(name, &id); - - if (!fun) { - fail(line, column, "Unknown function `%s'\n", name); - } else if (strlen(fun->signature) != args_nr) { - fail(line, column, "Incorrect number of arguments to function `%s': Expected %d, found %d\n", name, strlen(fun->signature), args_nr); - fun = NULL; - } - - if (fun) { - int i; - - expr = magic_new_expr(EXPR_FUNAPP); - expr->e.e_funapp.line_nr = line; - expr->e.e_funapp.column = column; - expr->e.e_funapp.id = id; - expr->e.e_funapp.args_nr = args_nr; - - for (i = 0; i < args_nr; i++) - expr->e.e_funapp.args[i] = args[i]; - } else { /* failure */ - expr = magic_new_expr(EXPR_VAL); - expr->e.e_val.ty = TY_FAIL; - } - - return expr; -} - -static spell_t * -new_spell(spellguard_t *guard) -{ - static int spell_counter = 0; - - spell_t *retval = (spell_t*)calloc(1, sizeof(spell_t)); - retval->index = ++spell_counter; - retval->spellguard = guard; - return retval; -} - -static spellguard_t * -new_spellguard(int ty) -{ - spellguard_t *retval = (spellguard_t *)calloc(1, sizeof(spellguard_t)); - retval->ty = ty; - return retval; -} - -static spellguard_t * -spellguard_implication(spellguard_t *a, spellguard_t *b) -{ - spellguard_t *retval = a; - - if (a == b) /* This can happen due to reference sharing: - * e.g., - * (R0 -> (R1 | R2)) => (R3) - * yields - * (R0 -> (R1 -> R3 | R2 -> R3)) - * - * So if we now add => R4 to that, we want - * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4)) - * - * but we only need to add it once, because the R3 reference is shared. - */ - return retval; - - /* If the premise is a disjunction, b is the continuation of _all_ branches */ - if (a->ty == SPELLGUARD_CHOICE) - spellguard_implication(a->s.s_alt, b); - if (a->next) - spellguard_implication(a->next, b); - else - a->next = b; - - - return retval; -} - -static effect_t * -new_effect(int ty) -{ - effect_t *effect = (effect_t *) calloc(1, sizeof(effect_t)); - effect->ty = ty; - return effect; -} - -static effect_t * -set_effect_continuation(effect_t *src, effect_t *continuation) -{ - effect_t *retval = src; - /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */ - - if (src == continuation) - return retval; - - /* For FOR and FOREACH, we use special stack handlers and thus don't have to set - * the continuation. It's only IF that we need to handle in this fashion. */ - if (src->ty == EFFECT_IF) { - set_effect_continuation(src->e.e_if.true_branch, continuation); - set_effect_continuation(src->e.e_if.false_branch, continuation); - } - if (src->next) - set_effect_continuation(src->next, continuation); - else - src->next = continuation; - - return retval; -} - -static effect_t * -op_effect(char *name, int args_nr, expr_t **args, int line, int column) -{ - int id; - effect_t *effect; - op_t *op = magic_get_op(name, &id); - - if (!op) - fail(line, column, "Unknown operation `%s'\n", name); - else if (strlen(op->signature) != args_nr) { - fail(line, column, "Incorrect number of arguments to operation `%s': Expected %d, found %d\n", name, strlen(op->signature), args_nr); - op = NULL; - } - - if (op) { - int i; - - effect = new_effect(EFFECT_OP); - effect->e.e_op.line_nr = line; - effect->e.e_op.column = column; - effect->e.e_op.id = id; - effect->e.e_op.args_nr = args_nr; - - for (i = 0; i < args_nr; i++) - effect->e.e_op.args[i] = args[i]; - } else /* failure */ - effect = new_effect(EFFECT_SKIP); - - return effect; -} - - -proc_t *procs = NULL; -int procs_nr = 0; - -// I think this is a memory leak, or undefined behavior -static void -install_proc(proc_t *proc) -{ - if (!procs) { - procs = proc; - procs_nr = 1; - } else { - RECREATE (procs, proc_t, 1 + procs_nr); - procs[procs_nr++] = *proc; - } -} - -static effect_t * -call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column) -{ - proc_t *p = NULL; - int i; - effect_t *retval; - - for (i = 0; i < procs_nr; i++) - if (!strcmp(procs[i].name, name)) { - p = &procs[i]; - break; - } - - if (!p) { - fail(line_nr, column, "Unknown procedure `%s'\n", name); - return new_effect(EFFECT_SKIP); - } - - if (p->args_nr != args_nr) { - fail(line_nr, column, "Procedure %s/%d invoked with %d parameters\n", name, p->args_nr, args_nr); - return new_effect(EFFECT_SKIP); - } - - retval = new_effect(EFFECT_CALL); - retval->e.e_call.body = p->body; - retval->e.e_call.args_nr = args_nr; - retval->e.e_call.formals = p->args; - retval->e.e_call.actuals = args; - return retval; -} - -struct const_def_rec { - char *name; - val_t val; -} *const_defs = NULL; - -int const_defs_nr = 0; - -static void -bind_constant(char *name, val_t *val, int line_nr) -{ - if (find_constant(name)) { - fail(line_nr, 0, "Redefinition of constant `%s'\n", name); - return; - } - - if (!const_defs) - const_defs = (struct const_def_rec *)malloc(sizeof(struct const_def_rec)); - else - const_defs = (struct const_def_rec *)realloc(const_defs, - (const_defs_nr + 1) * sizeof(struct const_def_rec)); - - const_defs[const_defs_nr].name = name; - const_defs[const_defs_nr].val = *val; - ++const_defs_nr; -} - -static val_t * -find_constant(char *name) -{ - int i; - for (i = 0; i < const_defs_nr; i++) { - if (!strcmp(const_defs[i].name, name)) { - free(name); - return &const_defs[i].val; - } - } - - return NULL; -} - - - - -#define INTERN_ASSERT(name, id) { int zid = intern_id(name); if (zid != id) fprintf(stderr, "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n", name, zid, id); error_flag = 1; } - -extern FILE *magic_frontend_in; - -int -magic_init(char *conffile) // must be called after itemdb initialisation -{ - int error_flag = 0; - - magic_conf.vars_nr = 0; - magic_conf.var_name = (char **)malloc(1); - magic_conf.vars = (val_t *)malloc(1); - - magic_conf.obscure_chance = 95; - magic_conf.min_casttime = 100; - - magic_conf.spells_nr = 0; - CREATE(magic_conf.spells, spell_t *, 1); - - magic_conf.anchors_nr = 0; - CREATE(magic_conf.anchors, teleport_anchor_t *, 1); - - INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME); - INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE); - INTERN_ASSERT("caster", VAR_CASTER); - INTERN_ASSERT("spellpower", VAR_SPELLPOWER); - INTERN_ASSERT("self_spell", VAR_SPELL); - INTERN_ASSERT("self_invocation", VAR_INVOCATION); - INTERN_ASSERT("target", VAR_TARGET); - INTERN_ASSERT("script_target", VAR_SCRIPTTARGET); - INTERN_ASSERT("location", VAR_LOCATION); - - magic_frontend_in = fopen(conffile, "r"); - if (!magic_frontend_in) { - fprintf(stderr, "[magic-conf] Magic configuration file `%s' not found -> no magic.\n", conffile); - return 0; - } - magic_frontend_parse(); - - if (magic_conf.vars[VAR_MIN_CASTTIME].ty == TY_INT) - magic_conf.min_casttime = magic_conf.vars[VAR_MIN_CASTTIME].v.v_int; - - if (magic_conf.vars[VAR_OBSCURE_CHANCE].ty == TY_INT) - magic_conf.obscure_chance = magic_conf.vars[VAR_OBSCURE_CHANCE].v.v_int; - - printf("[magic-conf] Magic initialised; obscure at %d%%. %d spells, %d teleport anchors.\n", - magic_conf.obscure_chance, magic_conf.spells_nr, magic_conf.anchors_nr); - - if (procs) - free(procs); - return error_flag; -} - -extern int magic_frontend_lineno; - -static void -magic_frontend_error(const char *msg) -{ - fprintf(stderr, "[magic-conf] Parse error: %s at line %d\n", msg, magic_frontend_lineno); - failed_flag = 1; -} diff --git a/src/map/magic-interpreter-parser.ypp b/src/map/magic-interpreter-parser.ypp new file mode 100644 index 0000000..2648be9 --- /dev/null +++ b/src/map/magic-interpreter-parser.ypp @@ -0,0 +1,1089 @@ +%{ +#include "magic-interpreter.hpp" +#include "magic-expr.hpp" +#include <stdarg.h> + +magic_conf_t magic_conf; + +static int +intern_id(const char *id_name); + + +static expr_t * +fun_expr(const char *name, int args_nr, expr_t **args, int line, int column); + +static expr_t * +dot_expr(expr_t *lhs, int id); + +#define BIN_EXPR(x, name, arg1, arg2, line, column) { expr_t *e[2]; e[0] = arg1; e[1] = arg2; x = fun_expr(name, 2, e, line, column); } + +static int failed_flag = 0; + +static void +magic_frontend_error(const char *msg); + +static void +fail(int line, int column, const char *fmt, ...); + +static spell_t * +new_spell(spellguard_t *guard); + +static spellguard_t * +spellguard_implication(spellguard_t *a, spellguard_t *b); + +static spellguard_t * +new_spellguard(int ty); + +static effect_t * +new_effect(int ty); + +static effect_t * +set_effect_continuation(effect_t *src, effect_t *continuation); + +static void +add_spell(spell_t *spell, int line_nr); + +static void +add_teleport_anchor(teleport_anchor_t *anchor, int line_nr); + +static effect_t * +op_effect(char *name, int args_nr, expr_t **args, int line, int column); + +int +magic_frontend_lex(void); + +static void +install_proc(proc_t *proc); + +static effect_t * +call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column); + +static void +bind_constant(char *name, val_t *val, int line_nr); + +static val_t * +find_constant(char *name); + + +%} + +%name-prefix="magic_frontend_" + +%locations + +%union { + int i; + char *s; + int op; + magic_conf_t *magic_conf; + val_t value; + expr_t *expr; + e_location_t location; + e_area_t area; + args_rec_t arg_list; + struct { int letdefs_nr; letdef_t *letdefs; } letdefs; + spell_t *spell; + struct { int id, ty; } spellarg_def; + letdef_t vardef; + spellguard_t *spellguard; + component_t *components; + struct {int id, count; } component; + effect_t *effect; + proc_t *proc; +}; + +%expect 7 + +%token <i> INT +%token <s> STRING +%token <s> ID +%token <i> DIR + +%token '=' +%token '<' +%token '>' +%token '+' +%token '-' +%token '*' +%token '/' +%token '%' +%token '@' +%token ',' +%token '.' +%token ':' +%token ';' +%token '|' +%token '[' +%token ']' +%token '&' +%token '^' + +%token CONST +%token PROCEDURE +%token CALL +%token SILENT +%token LOCAL +%token NONMAGIC +%token SHL +%token SHR +%token EQ +%token NEQ +%token GTE +%token LTE +%token ANDAND +%token OROR +%token <s> SCRIPT_DATA +%token TO +%token TOWARDS +%token TELEPORT_ANCHOR +%token SPELL +%token LET +%token IN +%token END +%token DARROW +%token STRING_TY +%token REQUIRE +%token CATALYSTS +%token COMPONENTS +%token MANA +%token CASTTIME +%token SKIP +%token ABORT +%token BREAK +%token EFFECT +%token ATEND +%token ATTRIGGER +%token PC_F +%token NPC_F +%token MOB_F +%token ENTITY_F +%token TARGET_F +%token IF +%token THEN +%token ELSE +%token FOREACH +%token FOR +%token DO +%token SLEEP + +%type <value> value +%type <location> location +%type <area> area +%type <arg_list> arg_list +%type <arg_list> arg_list_ne +%type <letdefs> defs +%type <spell> spelldef +%type <spellarg_def> argopt +%type <vardef> def +%type <spellguard> spellbody_list +%type <spellguard> spellbody +%type <spellguard> spellguard +%type <spellguard> spellguard_list +%type <spellguard> prereq +%type <component> item +%type <components> items +%type <components> item_list +%type <i> item_name +%type <i> selection; +%type <effect> effect +%type <effect> effect_list +%type <effect> maybe_trigger +%type <effect> maybe_end +%type <i> spell_flags; + +%type <expr> expr +%type <i> arg_ty +%type <proc> proc_formals_list +%type <proc> proc_formals_list_ne + +%left OROR +%left ANDAND +%left '<' '>' GTE LTE NEQ EQ +%left '+' '-' +%left '*' '/' '%' +%left SHL SHR '&' '^' '|' +%right '=' +%left OR +%left DARROW +%left '.' + +%% + +spellconf : /* empty */ + {} + | spellconf_option semicolons spellconf + {} + ; + + +semicolons : /* empty */ + {} + | semicolons ';' + {} + ; + + +proc_formals_list : /* empty */ + { CREATE ($$, proc_t, 1); } + | proc_formals_list_ne + { $$ = $1; } + ; + +proc_formals_list_ne : ID + { CREATE ($$, proc_t, 1); + $$->args_nr = 1; + $$->args = (int*)malloc(sizeof(int)); + $$->args[0] = intern_id($1); + } + | proc_formals_list_ne ',' ID + { $$ = $1; + $$->args = (int*)realloc($$->args, sizeof(int) * (1 + $$->args_nr)); + $$->args[$$->args_nr++] = intern_id($3); + } + ; + +spellconf_option : ID '=' expr + { + int var_id; + if (find_constant($1)) { + fail(@1.first_line, 0, "Attempt to redefine constant `%s' as global\n", $1); + free($1); + } else { + var_id = intern_id($1); + magic_eval(&magic_default_env, &magic_conf.vars[var_id], $3); + } + } + | CONST ID '=' expr + { + val_t var; + magic_eval(&magic_default_env, &var, $4); + bind_constant($2, &var, @1.first_line); + } + | TELEPORT_ANCHOR ID ':' expr '=' expr + { + teleport_anchor_t *anchor; + CREATE (anchor, teleport_anchor_t, 1); + anchor->name = $2; + anchor->invocation = magic_eval_str(&magic_default_env, $4); + anchor->location = $6; + + if (!failed_flag) + add_teleport_anchor(anchor, @1.first_line); + else + free(anchor); + failed_flag = 0; + } + | PROCEDURE ID '(' proc_formals_list ')' '=' effect_list + { + proc_t *proc = $4; + proc->name = $2; + proc->body = $7; + if (!failed_flag) + install_proc(proc); + failed_flag = 0; + } + | spell_flags SPELL ID argopt ':' expr '=' spelldef + { spell_t *spell = $8; + spell->name = $3; + spell->invocation = magic_eval_str(&magic_default_env, $6); + spell->arg = $4.id; + spell->spellarg_ty = $4.ty; + spell->flags = $1; + if (!failed_flag) + add_spell(spell, @1.first_line); + failed_flag = 0; + } + +spell_flags : /* empty */ + { $$ = 0; } + | LOCAL spell_flags + { if ($2 & SPELL_FLAG_LOCAL) + fail(@1.first_line, @1.first_column, "`LOCAL' specified more than once"); + $$ = $2 | SPELL_FLAG_LOCAL; + } + | NONMAGIC spell_flags + { if ($2 & SPELL_FLAG_NONMAGIC) + fail(@1.first_line, @1.first_column, "`NONMAGIC' specified more than once"); + $$ = $2 | SPELL_FLAG_NONMAGIC; + } + | SILENT spell_flags + { if ($2 & SPELL_FLAG_SILENT) + fail(@1.first_line, @1.first_column, "`SILENT' specified more than once"); + $$ = $2 | SPELL_FLAG_SILENT; + } + + +argopt : /* empty */ + { $$.ty = SPELLARG_NONE; } + | '(' ID ':' arg_ty ')' + { $$.id = intern_id($2); + $$.ty = $4; } + ; + + +arg_ty : PC_F + { $$ = SPELLARG_PC; } + | STRING_TY + { $$ = SPELLARG_STRING; } + ; + + +value : DIR + { $$.ty = TY_DIR; + $$.v.v_int = $1; } + | INT + { $$.ty = TY_INT; + $$.v.v_int = $1; } + | STRING + { $$.ty = TY_STRING; + $$.v.v_string = $1; } + ; + + +expr : value + { $$ = magic_new_expr(EXPR_VAL); + $$->e.e_val = $1; } + | ID + { + val_t *val; + if ((val = find_constant($1))) { + $$ = magic_new_expr(EXPR_VAL); + $$->e.e_val = *val; + } else { + $$ = magic_new_expr(EXPR_ID); + $$->e.e_id = intern_id($1); + } + } + | area + { $$ = magic_new_expr(EXPR_AREA); + $$->e.e_area = $1; } + | expr '+' expr + { BIN_EXPR($$, "+", $1, $3, @1.first_line, @1.first_column); } + | expr '-' expr + { BIN_EXPR($$, "-", $1, $3, @1.first_line, @1.first_column); } + | expr '*' expr + { BIN_EXPR($$, "*", $1, $3, @1.first_line, @1.first_column); } + | expr '%' expr + { BIN_EXPR($$, "%", $1, $3, @1.first_line, @1.first_column); } + | expr '/' expr + { BIN_EXPR($$, "/", $1, $3, @1.first_line, @1.first_column); } + | expr '<' expr + { BIN_EXPR($$, ">", $3, $1, @1.first_line, @1.first_column); } + | expr '>' expr + { BIN_EXPR($$, ">", $1, $3, @1.first_line, @1.first_column); } + | expr '&' expr + { BIN_EXPR($$, "&", $1, $3, @1.first_line, @1.first_column); } + | expr '^' expr + { BIN_EXPR($$, "^", $1, $3, @1.first_line, @1.first_column); } + | expr '|' expr + { BIN_EXPR($$, "|", $1, $3, @1.first_line, @1.first_column); } + | expr SHL expr + { BIN_EXPR($$, "<<", $1, $3, @1.first_line, @1.first_column); } + | expr SHR expr + { BIN_EXPR($$, ">>", $1, $3, @1.first_line, @1.first_column); } + | expr LTE expr + { BIN_EXPR($$, ">=", $3, $1, @1.first_line, @1.first_column); } + | expr GTE expr + { BIN_EXPR($$, ">=", $1, $3, @1.first_line, @1.first_column); } + | expr ANDAND expr + { BIN_EXPR($$, "&&", $1, $3, @1.first_line, @1.first_column); } + | expr OROR expr + { BIN_EXPR($$, "||", $1, $3, @1.first_line, @1.first_column); } + | expr EQ expr + { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); } + | expr '=' expr + { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); } + | expr NEQ expr + { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); + $$ = fun_expr("not", 1, &$$, @1.first_line, @1.first_column); } + | ID '(' arg_list ')' + { $$ = fun_expr($1, $3.args_nr, $3.args, @1.first_line, @1.first_column); + if ($3.args) + free($3.args); + free($1); } + | '(' expr ')' + { $$ = $2; } + | expr '.' ID + { $$ = dot_expr($1, intern_id($3)); } + ; + +arg_list : /* empty */ + { $$.args_nr = 0; } + | arg_list_ne + { $$ = $1; } + ; + + +arg_list_ne : expr + { CREATE($$.args, expr_t *, 1); + $$.args_nr = 1; + $$.args[0] = $1; + } + | arg_list_ne ',' expr + { RECREATE($$.args, expr_t *, 1 + $$.args_nr); + $$.args[$$.args_nr++] = $3; + } + ; + + +location : '@' '(' expr ',' expr ',' expr ')' + { $$.m = $3; $$.x = $5; $$.y = $7; } + ; + +area : location + { $$.ty = AREA_LOCATION; + $$.a.a_loc = $1; + } + | location '@' '+' '(' expr ',' expr ')' + { $$.ty = AREA_RECT; + $$.a.a_rect.loc = $1; + $$.a.a_rect.width = $5; + $$.a.a_rect.height = $7; + } + | location TOWARDS expr ':' '(' expr ',' expr ')' + { $$.ty = AREA_BAR; + $$.a.a_bar.loc = $1; + $$.a.a_bar.width = $6; + $$.a.a_bar.depth = $8; + $$.a.a_bar.dir = $3; + } + ; + + +spelldef : spellbody_list + { $$ = new_spell($1); } + | LET defs IN spellbody_list + { $$ = new_spell($4); + $$->letdefs_nr = $2.letdefs_nr; + $$->letdefs = $2.letdefs; + $$->spellguard = $4; + } + ; + + +defs : semicolons + { $$.letdefs_nr = 0; + CREATE($$.letdefs, letdef_t, 1); + } + | defs def semicolons + { $$ = $1; + $$.letdefs_nr++; + RECREATE ($$.letdefs, letdef_t, $$.letdefs_nr); + $$.letdefs[$1.letdefs_nr] = $2; + } + ; + + +def : ID '=' expr + { + if (find_constant($1)) { + fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' as LET-bound variable.\n", $1); + free($1); + } else { + $$.id = intern_id($1); + $$.expr = $3; + } + } + ; + + +spellbody_list : spellbody + { $$ = $1; } + | spellbody '|' spellbody_list + { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE); + sg->next = $1; + sg->s.s_alt = $3; + $$ = sg; + } + ; + + +spellbody : spellguard DARROW spellbody + { $$ = spellguard_implication($1, $3); } + | '(' spellbody_list ')' + { $$ = $2; } + | EFFECT effect_list maybe_trigger maybe_end + { spellguard_t *sg = new_spellguard(SPELLGUARD_EFFECT); + sg->s.s_effect.effect = $2; + sg->s.s_effect.at_trigger = $3; + sg->s.s_effect.at_end = $4; + $$ = sg; + } + ; + + +maybe_trigger : /* empty */ + { $$ = NULL; } + | ATTRIGGER effect_list + { $$ = $2; } + ; + + +maybe_end : /* empty */ + { $$ = NULL; } + | ATEND effect_list + { $$ = $2; } + ; + + +spellguard : prereq + { $$ = $1; } + | spellguard OR spellguard + { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE); + sg->next = $1; + sg->s.s_alt = $3; + $$ = sg; + } + | '(' spellguard_list ')' + { $$ = $2; } + ; + + +spellguard_list : spellguard + { $$ = $1; } + | spellguard ',' spellguard_list + { $$ = spellguard_implication ($1, $3); } + ; + + +prereq : REQUIRE expr + { $$ = new_spellguard(SPELLGUARD_CONDITION); + $$->s.s_condition = $2; + } + | CATALYSTS items + { $$ = new_spellguard(SPELLGUARD_CATALYSTS); + $$->s.s_catalysts = $2; + } + | COMPONENTS items + { $$ = new_spellguard(SPELLGUARD_COMPONENTS); + $$->s.s_components = $2; + } + | MANA expr + { $$ = new_spellguard(SPELLGUARD_MANA); + $$->s.s_mana = $2; + } + | CASTTIME expr + { $$ = new_spellguard(SPELLGUARD_CASTTIME); + $$->s.s_casttime = $2; + } + ; + + +items : '[' item_list ']' + { $$ = $2; } + ; + + +item_list : item + { $$ = NULL; + magic_add_component(&$$, $1.id, $1.count); + } + | item_list ',' item + { $$ = $1; + magic_add_component(&$$, $3.id, $3.count); + } + ; + + +item : INT '*' item_name + { $$.id = $3; $$.count = $1; } + | item_name + { $$.id = $1; $$.count = 1; } + ; + + +item_name : STRING + { struct item_data *item = itemdb_searchname($1); + if (!item) { + fail (@1.first_line, @1.first_column, "Unknown item `%s'\n", $1); + $$ = 0; + } else + $$ = item->nameid; + free ($1); + } + | INT + { $$ = $1; } + ; + + +selection : PC_F + { $$ = FOREACH_FILTER_PC; } + | MOB_F + { $$ = FOREACH_FILTER_MOB; } + | ENTITY_F + { $$ = FOREACH_FILTER_ENTITY; } + | SPELL + { $$ = FOREACH_FILTER_SPELL; } + | TARGET_F + { $$ = FOREACH_FILTER_TARGET; } + | NPC_F + { $$ = FOREACH_FILTER_NPC; } + ; + + +effect : '(' effect_list ')' + { $$ = $2; } + | SKIP ';' + { $$ = new_effect(EFFECT_SKIP); } + | ABORT ';' + { $$ = new_effect(EFFECT_ABORT); } + | END ';' + { $$ = new_effect(EFFECT_END); } + | BREAK ';' + { $$ = new_effect(EFFECT_BREAK); } + | ID '=' expr ';' + { + if (find_constant($1)) { + fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' in assignment.", $1); + free($1); + } else { + $$ = new_effect(EFFECT_ASSIGN); + $$->e.e_assign.id = intern_id($1); + $$->e.e_assign.expr = $3; + } + } + | FOREACH selection ID IN expr DO effect + { $$ = new_effect(EFFECT_FOREACH); + $$->e.e_foreach.id = intern_id($3); + $$->e.e_foreach.area = $5; + $$->e.e_foreach.body = $7; + $$->e.e_foreach.filter = $2; + } + | FOR ID '=' expr TO expr DO effect + { $$ = new_effect(EFFECT_FOR); + $$->e.e_for.id = intern_id($2); + $$->e.e_for.start = $4; + $$->e.e_for.stop = $6; + $$->e.e_for.body = $8; + } + | IF expr THEN effect ELSE effect + { $$ = new_effect(EFFECT_IF); + $$->e.e_if.cond = $2; + $$->e.e_if.true_branch = $4; + $$->e.e_if.false_branch = $6; + } + | IF expr THEN effect + { $$ = new_effect(EFFECT_IF); + $$->e.e_if.cond = $2; + $$->e.e_if.true_branch = $4; + $$->e.e_if.false_branch = new_effect(EFFECT_SKIP); + } + | SLEEP expr ';' + { $$ = new_effect(EFFECT_SLEEP); + $$->e.e_sleep = $2; + } + | ID '(' arg_list ')' ';' + { $$ = op_effect($1, $3.args_nr, $3.args, @1.first_line, @1.first_column); + free($1); + } + | SCRIPT_DATA + { $$ = new_effect(EFFECT_SCRIPT); + $$->e.e_script = parse_script((unsigned char *) $1, @1.first_line); + free($1); + if ($$->e.e_script == NULL) + fail(@1.first_line, @1.first_column, "Failed to compile script\n"); + } + | CALL ID '(' arg_list ')' ';' + { $$ = call_proc($2, $4.args_nr, $4.args, @1.first_line, @1.first_column); + free($2); + } + ; + +effect_list : /* empty */ + { $$ = new_effect(EFFECT_SKIP); } + | effect semicolons effect_list + { $$ = set_effect_continuation($1, $3); } + ; + + +%% + +/* We do incremental realloc here to store our results. Since this happens only once + * during startup for a relatively manageable set of configs, it should be fine. */ + +static int +intern_id(const char *id_name) +{ + int i; + + for (i = 0; i < magic_conf.vars_nr; i++) + if (!strcmp(id_name, magic_conf.var_name[i])) { + free((char*)id_name); + return i; + } + + /* Must add new */ + i = magic_conf.vars_nr++; + RECREATE(magic_conf.var_name, const char *, magic_conf.vars_nr); + magic_conf.var_name[i] = id_name; + RECREATE(magic_conf.vars, val_t, magic_conf.vars_nr); + magic_conf.vars[i].ty = TY_UNDEF; + + return i; +} + +static void +add_spell(spell_t *spell, int line_nr) +{ + int index = magic_conf.spells_nr; + int i; + + for (i = 0; i < index; i++) { + if (!strcmp(magic_conf.spells[i]->name, spell->name)) { + fail(line_nr, 0, "Attempt to redefine spell `%s'\n", spell->name); + return; + } + if (!strcmp(magic_conf.spells[i]->invocation, spell->invocation)) { + fail(line_nr, 0, "Attempt to redefine spell invocation `%s' between spells `%s' and `%s'\n", + spell->invocation, magic_conf.spells[i]->name, spell->name); + return; + } + } + magic_conf.spells_nr++; + + RECREATE(magic_conf.spells, spell_t *, magic_conf.spells_nr); + magic_conf.spells[index] = spell; + + +} + +static void +add_teleport_anchor(teleport_anchor_t *anchor, int line_nr) +{ + int index = magic_conf.anchors_nr; + int i; + + for (i = 0; i < index; i++) { + if (!strcmp(magic_conf.anchors[i]->name, anchor->name)) { + fail(line_nr, 0, "Attempt to redefine teleport anchor `%s'\n", anchor->name); + return; + } + if (!strcmp(magic_conf.anchors[i]->invocation, anchor->invocation)) { + fail(line_nr, 0, "Attempt to redefine anchor invocation `%s' between anchors `%s' and `%s'\n", + anchor->invocation, magic_conf.anchors[i]->name, anchor->name); + return; + } + } + magic_conf.anchors_nr++; + + RECREATE(magic_conf.anchors, teleport_anchor_t *, magic_conf.anchors_nr); + magic_conf.anchors[index] = anchor; +} + + +static void +fail(int line, int column, const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "[magic-init] L%d:%d: ", line, column); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + failed_flag = 1; +} + +static expr_t * +dot_expr(expr_t *expr, int id) +{ + expr_t *retval = magic_new_expr(EXPR_SPELLFIELD); + retval->e.e_field.id = id; + retval->e.e_field.expr = expr; + + return retval; +} + +static expr_t * +fun_expr(const char *name, int args_nr, expr_t **args, int line, int column) +{ + int id; + expr_t *expr; + fun_t *fun = magic_get_fun(name, &id); + + if (!fun) { + fail(line, column, "Unknown function `%s'\n", name); + } else if (strlen(fun->signature) != args_nr) { + fail(line, column, "Incorrect number of arguments to function `%s': Expected %d, found %d\n", name, strlen(fun->signature), args_nr); + fun = NULL; + } + + if (fun) { + int i; + + expr = magic_new_expr(EXPR_FUNAPP); + expr->e.e_funapp.line_nr = line; + expr->e.e_funapp.column = column; + expr->e.e_funapp.id = id; + expr->e.e_funapp.args_nr = args_nr; + + for (i = 0; i < args_nr; i++) + expr->e.e_funapp.args[i] = args[i]; + } else { /* failure */ + expr = magic_new_expr(EXPR_VAL); + expr->e.e_val.ty = TY_FAIL; + } + + return expr; +} + +static spell_t * +new_spell(spellguard_t *guard) +{ + static int spell_counter = 0; + + spell_t *retval = (spell_t*)calloc(1, sizeof(spell_t)); + retval->index = ++spell_counter; + retval->spellguard = guard; + return retval; +} + +static spellguard_t * +new_spellguard(int ty) +{ + spellguard_t *retval = (spellguard_t *)calloc(1, sizeof(spellguard_t)); + retval->ty = ty; + return retval; +} + +static spellguard_t * +spellguard_implication(spellguard_t *a, spellguard_t *b) +{ + spellguard_t *retval = a; + + if (a == b) /* This can happen due to reference sharing: + * e.g., + * (R0 -> (R1 | R2)) => (R3) + * yields + * (R0 -> (R1 -> R3 | R2 -> R3)) + * + * So if we now add => R4 to that, we want + * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4)) + * + * but we only need to add it once, because the R3 reference is shared. + */ + return retval; + + /* If the premise is a disjunction, b is the continuation of _all_ branches */ + if (a->ty == SPELLGUARD_CHOICE) + spellguard_implication(a->s.s_alt, b); + if (a->next) + spellguard_implication(a->next, b); + else + a->next = b; + + + return retval; +} + +static effect_t * +new_effect(int ty) +{ + effect_t *effect = (effect_t *) calloc(1, sizeof(effect_t)); + effect->ty = ty; + return effect; +} + +static effect_t * +set_effect_continuation(effect_t *src, effect_t *continuation) +{ + effect_t *retval = src; + /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */ + + if (src == continuation) + return retval; + + /* For FOR and FOREACH, we use special stack handlers and thus don't have to set + * the continuation. It's only IF that we need to handle in this fashion. */ + if (src->ty == EFFECT_IF) { + set_effect_continuation(src->e.e_if.true_branch, continuation); + set_effect_continuation(src->e.e_if.false_branch, continuation); + } + if (src->next) + set_effect_continuation(src->next, continuation); + else + src->next = continuation; + + return retval; +} + +static effect_t * +op_effect(char *name, int args_nr, expr_t **args, int line, int column) +{ + int id; + effect_t *effect; + op_t *op = magic_get_op(name, &id); + + if (!op) + fail(line, column, "Unknown operation `%s'\n", name); + else if (strlen(op->signature) != args_nr) { + fail(line, column, "Incorrect number of arguments to operation `%s': Expected %d, found %d\n", name, strlen(op->signature), args_nr); + op = NULL; + } + + if (op) { + int i; + + effect = new_effect(EFFECT_OP); + effect->e.e_op.line_nr = line; + effect->e.e_op.column = column; + effect->e.e_op.id = id; + effect->e.e_op.args_nr = args_nr; + + for (i = 0; i < args_nr; i++) + effect->e.e_op.args[i] = args[i]; + } else /* failure */ + effect = new_effect(EFFECT_SKIP); + + return effect; +} + + +proc_t *procs = NULL; +int procs_nr = 0; + +// I think this is a memory leak, or undefined behavior +static void +install_proc(proc_t *proc) +{ + if (!procs) { + procs = proc; + procs_nr = 1; + } else { + RECREATE (procs, proc_t, 1 + procs_nr); + procs[procs_nr++] = *proc; + } +} + +static effect_t * +call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column) +{ + proc_t *p = NULL; + int i; + effect_t *retval; + + for (i = 0; i < procs_nr; i++) + if (!strcmp(procs[i].name, name)) { + p = &procs[i]; + break; + } + + if (!p) { + fail(line_nr, column, "Unknown procedure `%s'\n", name); + return new_effect(EFFECT_SKIP); + } + + if (p->args_nr != args_nr) { + fail(line_nr, column, "Procedure %s/%d invoked with %d parameters\n", name, p->args_nr, args_nr); + return new_effect(EFFECT_SKIP); + } + + retval = new_effect(EFFECT_CALL); + retval->e.e_call.body = p->body; + retval->e.e_call.args_nr = args_nr; + retval->e.e_call.formals = p->args; + retval->e.e_call.actuals = args; + return retval; +} + +struct const_def_rec { + char *name; + val_t val; +} *const_defs = NULL; + +int const_defs_nr = 0; + +static void +bind_constant(char *name, val_t *val, int line_nr) +{ + if (find_constant(name)) { + fail(line_nr, 0, "Redefinition of constant `%s'\n", name); + return; + } + + if (!const_defs) + const_defs = (struct const_def_rec *)malloc(sizeof(struct const_def_rec)); + else + const_defs = (struct const_def_rec *)realloc(const_defs, + (const_defs_nr + 1) * sizeof(struct const_def_rec)); + + const_defs[const_defs_nr].name = name; + const_defs[const_defs_nr].val = *val; + ++const_defs_nr; +} + +static val_t * +find_constant(char *name) +{ + int i; + for (i = 0; i < const_defs_nr; i++) { + if (!strcmp(const_defs[i].name, name)) { + free(name); + return &const_defs[i].val; + } + } + + return NULL; +} + + + + +#define INTERN_ASSERT(name, id) { int zid = intern_id(name); if (zid != id) fprintf(stderr, "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n", name, zid, id); error_flag = 1; } + +extern FILE *magic_frontend_in; + +int +magic_init(char *conffile) // must be called after itemdb initialisation +{ + int error_flag = 0; + + magic_conf.vars_nr = 0; + magic_conf.var_name = (char **)malloc(1); + magic_conf.vars = (val_t *)malloc(1); + + magic_conf.obscure_chance = 95; + magic_conf.min_casttime = 100; + + magic_conf.spells_nr = 0; + CREATE(magic_conf.spells, spell_t *, 1); + + magic_conf.anchors_nr = 0; + CREATE(magic_conf.anchors, teleport_anchor_t *, 1); + + INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME); + INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE); + INTERN_ASSERT("caster", VAR_CASTER); + INTERN_ASSERT("spellpower", VAR_SPELLPOWER); + INTERN_ASSERT("self_spell", VAR_SPELL); + INTERN_ASSERT("self_invocation", VAR_INVOCATION); + INTERN_ASSERT("target", VAR_TARGET); + INTERN_ASSERT("script_target", VAR_SCRIPTTARGET); + INTERN_ASSERT("location", VAR_LOCATION); + + magic_frontend_in = fopen(conffile, "r"); + if (!magic_frontend_in) { + fprintf(stderr, "[magic-conf] Magic configuration file `%s' not found -> no magic.\n", conffile); + return 0; + } + magic_frontend_parse(); + + if (magic_conf.vars[VAR_MIN_CASTTIME].ty == TY_INT) + magic_conf.min_casttime = magic_conf.vars[VAR_MIN_CASTTIME].v.v_int; + + if (magic_conf.vars[VAR_OBSCURE_CHANCE].ty == TY_INT) + magic_conf.obscure_chance = magic_conf.vars[VAR_OBSCURE_CHANCE].v.v_int; + + printf("[magic-conf] Magic initialised; obscure at %d%%. %d spells, %d teleport anchors.\n", + magic_conf.obscure_chance, magic_conf.spells_nr, magic_conf.anchors_nr); + + if (procs) + free(procs); + return error_flag; +} + +extern int magic_frontend_lineno; + +static void +magic_frontend_error(const char *msg) +{ + fprintf(stderr, "[magic-conf] Parse error: %s at line %d\n", msg, magic_frontend_lineno); + failed_flag = 1; +} diff --git a/src/map/magic-interpreter.h b/src/map/magic-interpreter.h deleted file mode 100644 index 0b4b73c..0000000 --- a/src/map/magic-interpreter.h +++ /dev/null @@ -1,500 +0,0 @@ -/* Magic interpreter */ - -#ifndef MAGIC_INTERPRETER_H -#define MAGIC_INTERPRETER_H - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "../common/nullpo.h" - -#include "battle.h" -#include "chat.h" -#include "chrif.h" -#include "clif.h" -#include "intif.h" -#include "itemdb.h" -#include "magic.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "pc.h" -#include "party.h" -#include "script.h" -#include "skill.h" -#include "storage.h" -#include "trade.h" - -#include "../common/timer.h" -#include "../common/socket.h" - -#define SPELLARG_NONE 0 /* No spell parameter */ -#define SPELLARG_PC 1 /* Spell parameter describes pc (defaults to self) */ -#define SPELLARG_STRING 2 /* Spell parameter describes pc (defaults to self) */ - -/* ------ */ -/* Values */ -/* ------ */ - -#define TY_UNDEF 0 -#define TY_INT 1 -#define TY_DIR 2 -#define TY_STRING 3 -#define TY_ENTITY 5 -#define TY_LOCATION 6 -#define TY_AREA 7 -#define TY_SPELL 8 -#define TY_INVOCATION 9 -#define TY_FAIL 127 - -#define DIR_S 0 -#define DIR_SW 1 -#define DIR_W 2 -#define DIR_NW 3 -#define DIR_N 4 -#define DIR_NE 5 -#define DIR_E 6 -#define DIR_SE 7 - -struct expr; -struct val; -struct location; -struct area; -struct spell; -struct invocation; - -typedef struct location -{ - int m; - int x, y; -} location_t; - -#define AREA_LOCATION 0 -#define AREA_UNION 1 -#define AREA_RECT 2 -#define AREA_BAR 3 - -typedef struct area -{ - union a - { - location_t a_loc; - struct - { - location_t loc; - int width, depth, dir; - } a_bar; - struct - { - location_t loc; - int width, height; - } a_rect; - struct area *a_union[2]; - } a; - int size; - unsigned char ty; -} area_t; - -typedef struct val -{ - union v - { - int v_int; - char *v_string; - entity_t *v_entity; /* Used ONLY during operation/function invocation; otherwise we use v_int */ - area_t *v_area; - location_t v_location; - struct invocation *v_invocation; /* Used ONLY during operation/function invocation; otherwise we use v_int */ - struct spell *v_spell; - } v; - unsigned char ty; -} val_t; - -/* ----------- */ -/* Expressions */ -/* ----------- */ - -#define MAX_ARGS 7 /* Max. # of args used in builtin primitive functions */ - -#define EXPR_VAL 0 -#define EXPR_LOCATION 1 -#define EXPR_AREA 2 -#define EXPR_FUNAPP 3 -#define EXPR_ID 4 -#define EXPR_SPELLFIELD 5 - -typedef struct e_location -{ - struct expr *m, *x, *y; -} e_location_t; - -typedef struct e_area -{ - union a0 - { - e_location_t a_loc; - struct - { - e_location_t loc; - struct expr *width, *depth, *dir; - } a_bar; - struct - { - e_location_t loc; - struct expr *width, *height; - } a_rect; - struct e_area *a_union[2]; - } a; - unsigned char ty; -} e_area_t; - -typedef struct expr -{ - union e - { - val_t e_val; - e_location_t e_location; - e_area_t e_area; - struct - { - int id, line_nr, column; - int args_nr; - struct expr *args[MAX_ARGS]; - } e_funapp; - int e_id; - struct - { - struct expr *expr; - int id; - } e_field; - } e; - unsigned char ty; -} expr_t; - -/* ------- */ -/* Effects */ -/* ------- */ - -#define EFFECT_SKIP 0 -#define EFFECT_ABORT 1 -#define EFFECT_ASSIGN 2 -#define EFFECT_FOREACH 3 -#define EFFECT_FOR 4 -#define EFFECT_IF 5 -#define EFFECT_SLEEP 6 -#define EFFECT_SCRIPT 7 -#define EFFECT_BREAK 8 -#define EFFECT_OP 9 -#define EFFECT_END 10 -#define EFFECT_CALL 11 - -#define FOREACH_FILTER_MOB 1 -#define FOREACH_FILTER_PC 2 -#define FOREACH_FILTER_ENTITY 3 -#define FOREACH_FILTER_TARGET 4 -#define FOREACH_FILTER_SPELL 5 -#define FOREACH_FILTER_NPC 6 - -typedef struct effect -{ - struct effect *next; - union e0 - { - struct - { - int id; - expr_t *expr; - } e_assign; - struct - { - int id; - expr_t *area; - struct effect *body; - unsigned char filter; - } e_foreach; - struct - { - int id; - expr_t *start, *stop; - struct effect *body; - } e_for; - struct - { - expr_t *cond; - struct effect *true_branch, *false_branch; - } e_if; - expr_t *e_sleep; /* sleep time */ - unsigned char *e_script; - struct - { - int id; - int args_nr; - int line_nr, column; - expr_t *args[MAX_ARGS]; - } e_op; - struct - { - int args_nr, *formals; - expr_t **actuals; - struct effect *body; - } e_call; - } e; - unsigned char ty; -} effect_t; - -/* ---------- */ -/* Components */ -/* ---------- */ - -typedef struct component -{ - struct component *next; - int item_id; - int count; -} component_t; - -/* ----------- */ -/* Spellguards */ -/* ----------- */ - -#define SPELLGUARD_CONDITION 0 -#define SPELLGUARD_COMPONENTS 1 -#define SPELLGUARD_CATALYSTS 2 -#define SPELLGUARD_CHOICE 3 -#define SPELLGUARD_MANA 4 -#define SPELLGUARD_CASTTIME 5 -#define SPELLGUARD_EFFECT 6 - -typedef struct effect_set -{ - effect_t *effect, *at_trigger, *at_end; -} effect_set_t; - -typedef struct spellguard -{ - struct spellguard *next; - union s - { - expr_t *s_condition; - expr_t *s_mana; - expr_t *s_casttime; - component_t *s_components; - component_t *s_catalysts; - struct spellguard *s_alt; /* either `next' or `s.s_alt' */ - effect_set_t s_effect; - } s; - unsigned char ty; -} spellguard_t; - -/* ------ */ -/* Spells */ -/* ------ */ - -typedef struct letdef -{ - int id; - expr_t *expr; -} letdef_t; - -#define SPELL_FLAG_LOCAL (1 << 0) // spell associated not with caster but with place -#define SPELL_FLAG_SILENT (1 << 1) // spell invocation never uttered -#define SPELL_FLAG_NONMAGIC (1 << 2) // `magic word' only: don't require spellcasting ability - -typedef struct spell -{ - char *name; - char *invocation; - int index; // Relative location in the definitions file - int flags; - int arg; - int spellarg_ty; - - int letdefs_nr; - letdef_t *letdefs; - - spellguard_t *spellguard; -} spell_t; - -/* ------- */ -/* Anchors */ -/* ------- */ - -typedef struct teleport_anchor -{ - char *name; - char *invocation; - expr_t *location; -} teleport_anchor_t; - -/* ------------------- */ -/* The big config blob */ -/* ------------------- */ - -typedef struct -{ - int vars_nr; - const char **var_name; - val_t *vars; /* Initial assignments, if any, or NULL */ - - int obscure_chance; - int min_casttime; - - int spells_nr; - spell_t **spells; - - int anchors_nr; /* NEGATIVE iff we have sorted the anchors */ - teleport_anchor_t **anchors; -} magic_conf_t; - -/* Execution environment */ - -#define VAR_MIN_CASTTIME 0 -#define VAR_OBSCURE_CHANCE 1 -#define VAR_CASTER 2 -#define VAR_SPELLPOWER 3 -#define VAR_SPELL 4 -#define VAR_INVOCATION 5 -#define VAR_TARGET 6 -#define VAR_SCRIPTTARGET 7 -#define VAR_LOCATION 8 - -struct magic_config; - -typedef struct env -{ - magic_conf_t *base_env; - val_t *vars; -} env_t; - -#define MAX_STACK_SIZE 32 - -#define CONT_STACK_FOREACH 0 -#define CONT_STACK_FOR 1 -#define CONT_STACK_PROC 2 - -typedef struct cont_activation_record -{ - effect_t *return_location; - union c - { - struct - { - int id, ty; - effect_t *body; - int entities_nr; - int *entities; - int index; - } c_foreach; - struct - { - int id; - effect_t *body; - int current; - int stop; - } c_for; - struct - { - int args_nr, *formals; - val_t *old_actuals; - } c_proc; - } c; - unsigned char ty; -} cont_activation_record_t; - -typedef struct status_change_ref -{ - int sc_type; - int bl_id; -} status_change_ref_t; - -#define INVOCATION_FLAG_BOUND (1 << 0) /* Bound directly to the caster (i.e., ignore its location) */ -#define INVOCATION_FLAG_ABORTED (1 << 1) /* Used `abort' to terminate */ -#define INVOCATION_FLAG_STOPATTACK (1 << 2) /* On magical attacks: if we run out of steam, stop attacking altogether */ - -typedef struct invocation -{ - struct block_list bl; - - struct invocation *next_invocation; /* used for spells directly associated with a caster: they form a singly-linked list */ - int flags; - - env_t *env; - spell_t *spell; - int caster; /* this is the person who originally invoked the spell */ - int subject; /* when this person dies, the spell dies with it */ - - int timer; /* spell timer, if any */ - - int stack_size; - cont_activation_record_t stack[MAX_STACK_SIZE]; - - int script_pos; /* Script position; if nonzero, resume the script we were running. */ - effect_t *current_effect; - effect_t *trigger_effect; /* If non-NULL, this is used to spawn a cloned effect based on the same environment */ - effect_t *end_effect; /* If non-NULL, this is executed when the spell terminates naturally, e.g. when all status changes have run out or all delays are over. */ - - /* Status change references: for status change updates, keep track of whom we updated where */ - int status_change_refs_nr; - status_change_ref_t *status_change_refs; - -} invocation_t; - -extern magic_conf_t magic_conf; /* Global magic conf */ -extern env_t magic_default_env; /* Fake default environment */ - -/** - * Adds a component selection to a component holder (which may initially be NULL) - */ -void magic_add_component (component_t ** component_holder, int id, int count); - -teleport_anchor_t *magic_find_anchor (char *name); - -/** - * The parameter `param' must have been dynamically allocated; ownership is transferred to the resultant env_t. - */ -env_t *spell_create_env (magic_conf_t * conf, spell_t * spell, - character_t * caster, int spellpower, char *param); - -void magic_free_env (env_t * env); - -/** - * near_miss is set to nonzero iff the spell only failed due to ephemereal issues (spell delay in effect, out of mana, out of components) - */ -effect_set_t *spell_trigger (spell_t * spell, character_t * caster, - env_t * env, int *near_miss); - -invocation_t *spell_instantiate (effect_set_t * effect, env_t * env); - -/** - * Bind a spell to a subject (this is a no-op for `local' spells). - */ -void spell_bind (character_t * subject, invocation_t * invocation); - -int // 1 on failure - spell_unbind (character_t * subject, invocation_t * invocation); - -/** - * Clones a spell to run the at_effect field - */ -invocation_t *spell_clone_effect (invocation_t * source); - -spell_t *magic_find_spell (char *invocation); - -/* The following is used only by the parser: */ -typedef struct args_rec -{ - int args_nr; - expr_t **args; -} args_rec_t; - -typedef struct -{ - char *name; - int args_nr; - int *args; - effect_t *body; -} proc_t; - -#endif /* !defined (MAGIC_INTERPRETER_H) */ diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp new file mode 100644 index 0000000..e040975 --- /dev/null +++ b/src/map/magic-interpreter.hpp @@ -0,0 +1,500 @@ +/* Magic interpreter */ + +#ifndef MAGIC_INTERPRETER_HPP +#define MAGIC_INTERPRETER_HPP + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "../common/nullpo.hpp" + +#include "battle.hpp" +#include "chat.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "magic.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "party.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "storage.hpp" +#include "trade.hpp" + +#include "../common/timer.hpp" +#include "../common/socket.hpp" + +#define SPELLARG_NONE 0 /* No spell parameter */ +#define SPELLARG_PC 1 /* Spell parameter describes pc (defaults to self) */ +#define SPELLARG_STRING 2 /* Spell parameter describes pc (defaults to self) */ + +/* ------ */ +/* Values */ +/* ------ */ + +#define TY_UNDEF 0 +#define TY_INT 1 +#define TY_DIR 2 +#define TY_STRING 3 +#define TY_ENTITY 5 +#define TY_LOCATION 6 +#define TY_AREA 7 +#define TY_SPELL 8 +#define TY_INVOCATION 9 +#define TY_FAIL 127 + +#define DIR_S 0 +#define DIR_SW 1 +#define DIR_W 2 +#define DIR_NW 3 +#define DIR_N 4 +#define DIR_NE 5 +#define DIR_E 6 +#define DIR_SE 7 + +struct expr; +struct val; +struct location; +struct area; +struct spell; +struct invocation; + +typedef struct location +{ + int m; + int x, y; +} location_t; + +#define AREA_LOCATION 0 +#define AREA_UNION 1 +#define AREA_RECT 2 +#define AREA_BAR 3 + +typedef struct area +{ + union a + { + location_t a_loc; + struct + { + location_t loc; + int width, depth, dir; + } a_bar; + struct + { + location_t loc; + int width, height; + } a_rect; + struct area *a_union[2]; + } a; + int size; + unsigned char ty; +} area_t; + +typedef struct val +{ + union v + { + int v_int; + char *v_string; + entity_t *v_entity; /* Used ONLY during operation/function invocation; otherwise we use v_int */ + area_t *v_area; + location_t v_location; + struct invocation *v_invocation; /* Used ONLY during operation/function invocation; otherwise we use v_int */ + struct spell *v_spell; + } v; + unsigned char ty; +} val_t; + +/* ----------- */ +/* Expressions */ +/* ----------- */ + +#define MAX_ARGS 7 /* Max. # of args used in builtin primitive functions */ + +#define EXPR_VAL 0 +#define EXPR_LOCATION 1 +#define EXPR_AREA 2 +#define EXPR_FUNAPP 3 +#define EXPR_ID 4 +#define EXPR_SPELLFIELD 5 + +typedef struct e_location +{ + struct expr *m, *x, *y; +} e_location_t; + +typedef struct e_area +{ + union a0 + { + e_location_t a_loc; + struct + { + e_location_t loc; + struct expr *width, *depth, *dir; + } a_bar; + struct + { + e_location_t loc; + struct expr *width, *height; + } a_rect; + struct e_area *a_union[2]; + } a; + unsigned char ty; +} e_area_t; + +typedef struct expr +{ + union e + { + val_t e_val; + e_location_t e_location; + e_area_t e_area; + struct + { + int id, line_nr, column; + int args_nr; + struct expr *args[MAX_ARGS]; + } e_funapp; + int e_id; + struct + { + struct expr *expr; + int id; + } e_field; + } e; + unsigned char ty; +} expr_t; + +/* ------- */ +/* Effects */ +/* ------- */ + +#define EFFECT_SKIP 0 +#define EFFECT_ABORT 1 +#define EFFECT_ASSIGN 2 +#define EFFECT_FOREACH 3 +#define EFFECT_FOR 4 +#define EFFECT_IF 5 +#define EFFECT_SLEEP 6 +#define EFFECT_SCRIPT 7 +#define EFFECT_BREAK 8 +#define EFFECT_OP 9 +#define EFFECT_END 10 +#define EFFECT_CALL 11 + +#define FOREACH_FILTER_MOB 1 +#define FOREACH_FILTER_PC 2 +#define FOREACH_FILTER_ENTITY 3 +#define FOREACH_FILTER_TARGET 4 +#define FOREACH_FILTER_SPELL 5 +#define FOREACH_FILTER_NPC 6 + +typedef struct effect +{ + struct effect *next; + union e0 + { + struct + { + int id; + expr_t *expr; + } e_assign; + struct + { + int id; + expr_t *area; + struct effect *body; + unsigned char filter; + } e_foreach; + struct + { + int id; + expr_t *start, *stop; + struct effect *body; + } e_for; + struct + { + expr_t *cond; + struct effect *true_branch, *false_branch; + } e_if; + expr_t *e_sleep; /* sleep time */ + unsigned char *e_script; + struct + { + int id; + int args_nr; + int line_nr, column; + expr_t *args[MAX_ARGS]; + } e_op; + struct + { + int args_nr, *formals; + expr_t **actuals; + struct effect *body; + } e_call; + } e; + unsigned char ty; +} effect_t; + +/* ---------- */ +/* Components */ +/* ---------- */ + +typedef struct component +{ + struct component *next; + int item_id; + int count; +} component_t; + +/* ----------- */ +/* Spellguards */ +/* ----------- */ + +#define SPELLGUARD_CONDITION 0 +#define SPELLGUARD_COMPONENTS 1 +#define SPELLGUARD_CATALYSTS 2 +#define SPELLGUARD_CHOICE 3 +#define SPELLGUARD_MANA 4 +#define SPELLGUARD_CASTTIME 5 +#define SPELLGUARD_EFFECT 6 + +typedef struct effect_set +{ + effect_t *effect, *at_trigger, *at_end; +} effect_set_t; + +typedef struct spellguard +{ + struct spellguard *next; + union s + { + expr_t *s_condition; + expr_t *s_mana; + expr_t *s_casttime; + component_t *s_components; + component_t *s_catalysts; + struct spellguard *s_alt; /* either `next' or `s.s_alt' */ + effect_set_t s_effect; + } s; + unsigned char ty; +} spellguard_t; + +/* ------ */ +/* Spells */ +/* ------ */ + +typedef struct letdef +{ + int id; + expr_t *expr; +} letdef_t; + +#define SPELL_FLAG_LOCAL (1 << 0) // spell associated not with caster but with place +#define SPELL_FLAG_SILENT (1 << 1) // spell invocation never uttered +#define SPELL_FLAG_NONMAGIC (1 << 2) // `magic word' only: don't require spellcasting ability + +typedef struct spell +{ + char *name; + char *invocation; + int index; // Relative location in the definitions file + int flags; + int arg; + int spellarg_ty; + + int letdefs_nr; + letdef_t *letdefs; + + spellguard_t *spellguard; +} spell_t; + +/* ------- */ +/* Anchors */ +/* ------- */ + +typedef struct teleport_anchor +{ + char *name; + char *invocation; + expr_t *location; +} teleport_anchor_t; + +/* ------------------- */ +/* The big config blob */ +/* ------------------- */ + +typedef struct +{ + int vars_nr; + const char **var_name; + val_t *vars; /* Initial assignments, if any, or NULL */ + + int obscure_chance; + int min_casttime; + + int spells_nr; + spell_t **spells; + + int anchors_nr; /* NEGATIVE iff we have sorted the anchors */ + teleport_anchor_t **anchors; +} magic_conf_t; + +/* Execution environment */ + +#define VAR_MIN_CASTTIME 0 +#define VAR_OBSCURE_CHANCE 1 +#define VAR_CASTER 2 +#define VAR_SPELLPOWER 3 +#define VAR_SPELL 4 +#define VAR_INVOCATION 5 +#define VAR_TARGET 6 +#define VAR_SCRIPTTARGET 7 +#define VAR_LOCATION 8 + +struct magic_config; + +typedef struct env +{ + magic_conf_t *base_env; + val_t *vars; +} env_t; + +#define MAX_STACK_SIZE 32 + +#define CONT_STACK_FOREACH 0 +#define CONT_STACK_FOR 1 +#define CONT_STACK_PROC 2 + +typedef struct cont_activation_record +{ + effect_t *return_location; + union c + { + struct + { + int id, ty; + effect_t *body; + int entities_nr; + int *entities; + int index; + } c_foreach; + struct + { + int id; + effect_t *body; + int current; + int stop; + } c_for; + struct + { + int args_nr, *formals; + val_t *old_actuals; + } c_proc; + } c; + unsigned char ty; +} cont_activation_record_t; + +typedef struct status_change_ref +{ + int sc_type; + int bl_id; +} status_change_ref_t; + +#define INVOCATION_FLAG_BOUND (1 << 0) /* Bound directly to the caster (i.e., ignore its location) */ +#define INVOCATION_FLAG_ABORTED (1 << 1) /* Used `abort' to terminate */ +#define INVOCATION_FLAG_STOPATTACK (1 << 2) /* On magical attacks: if we run out of steam, stop attacking altogether */ + +typedef struct invocation +{ + struct block_list bl; + + struct invocation *next_invocation; /* used for spells directly associated with a caster: they form a singly-linked list */ + int flags; + + env_t *env; + spell_t *spell; + int caster; /* this is the person who originally invoked the spell */ + int subject; /* when this person dies, the spell dies with it */ + + int timer; /* spell timer, if any */ + + int stack_size; + cont_activation_record_t stack[MAX_STACK_SIZE]; + + int script_pos; /* Script position; if nonzero, resume the script we were running. */ + effect_t *current_effect; + effect_t *trigger_effect; /* If non-NULL, this is used to spawn a cloned effect based on the same environment */ + effect_t *end_effect; /* If non-NULL, this is executed when the spell terminates naturally, e.g. when all status changes have run out or all delays are over. */ + + /* Status change references: for status change updates, keep track of whom we updated where */ + int status_change_refs_nr; + status_change_ref_t *status_change_refs; + +} invocation_t; + +extern magic_conf_t magic_conf; /* Global magic conf */ +extern env_t magic_default_env; /* Fake default environment */ + +/** + * Adds a component selection to a component holder (which may initially be NULL) + */ +void magic_add_component (component_t ** component_holder, int id, int count); + +teleport_anchor_t *magic_find_anchor (char *name); + +/** + * The parameter `param' must have been dynamically allocated; ownership is transferred to the resultant env_t. + */ +env_t *spell_create_env (magic_conf_t * conf, spell_t * spell, + character_t * caster, int spellpower, char *param); + +void magic_free_env (env_t * env); + +/** + * near_miss is set to nonzero iff the spell only failed due to ephemereal issues (spell delay in effect, out of mana, out of components) + */ +effect_set_t *spell_trigger (spell_t * spell, character_t * caster, + env_t * env, int *near_miss); + +invocation_t *spell_instantiate (effect_set_t * effect, env_t * env); + +/** + * Bind a spell to a subject (this is a no-op for `local' spells). + */ +void spell_bind (character_t * subject, invocation_t * invocation); + +int // 1 on failure + spell_unbind (character_t * subject, invocation_t * invocation); + +/** + * Clones a spell to run the at_effect field + */ +invocation_t *spell_clone_effect (invocation_t * source); + +spell_t *magic_find_spell (char *invocation); + +/* The following is used only by the parser: */ +typedef struct args_rec +{ + int args_nr; + expr_t **args; +} args_rec_t; + +typedef struct +{ + char *name; + int args_nr; + int *args; + effect_t *body; +} proc_t; + +#endif /* !defined (MAGIC_INTERPRETER_H) */ diff --git a/src/map/magic-stmt.c b/src/map/magic-stmt.c deleted file mode 100644 index 5569217..0000000 --- a/src/map/magic-stmt.c +++ /dev/null @@ -1,1597 +0,0 @@ -#include "magic-interpreter.h" -#include "magic-expr.h" -#include "magic-expr-eval.h" -#include "magic-interpreter-aux.h" - -int - clif_spawn_fake_npc_for_player (struct map_session_data *sd, - int fake_npc_id); - -#define INVISIBLE_NPC 127 /* used for local spell effects */ - -//#define DEBUG - -#ifdef DEBUG -static void print_val (val_t * v) -{ - switch (v->ty) - { - case TY_UNDEF: - fprintf (stderr, "UNDEF"); - break; - case TY_INT: - fprintf (stderr, "%d", v->v.v_int); - break; - case TY_DIR: - fprintf (stderr, "dir%d", v->v.v_int); - break; - case TY_STRING: - fprintf (stderr, "`%s'", v->v.v_string); - break; - default: - fprintf (stderr, "ty%d", v->ty); - break; - } -} - -static void dump_env (env_t * env) -{ - int i; - for (i = 0; i < env->base_env->vars_nr; i++) - { - val_t *v = &env->vars[i]; - val_t *bv = &env->base_env->vars[i]; - - fprintf (stderr, "%02x %30s ", i, env->base_env->var_name[i]); - print_val (v); - fprintf (stderr, "\t("); - print_val (bv); - fprintf (stderr, ")\n"); - } -} -#endif - -static void clear_activation_record (cont_activation_record_t * ar) -{ - switch (ar->ty) - { - case CONT_STACK_FOREACH: - free (ar->c.c_foreach.entities); - break; - case CONT_STACK_PROC: - free (ar->c.c_proc.old_actuals); - break; - } -} - -static void -invocation_timer_callback (timer_id UNUSED, tick_t UNUSED, custom_id_t id, custom_data_t data) -{ - invocation_t *invocation = (invocation_t *) map_id2bl (id); - - if (invocation) - { - invocation->timer = 0; - spell_execute (invocation); - } -} - -static void clear_stack (invocation_t * invocation) -{ - int i; - - for (i = 0; i < invocation->stack_size; i++) - clear_activation_record (&invocation->stack[i]); - - invocation->stack_size = 0; -} - -void spell_free_invocation (invocation_t * invocation) -{ - if (invocation->status_change_refs) - { - free (invocation->status_change_refs); - /* The following cleanup shouldn't be necessary, but I've added it to help tracking a certain bug */ - invocation->status_change_refs = NULL; - invocation->status_change_refs_nr = 0; - } - - if (invocation->flags & INVOCATION_FLAG_BOUND) - { - entity_t *e = map_id2bl (invocation->subject); - if (e && e->type == BL_PC) - spell_unbind ((character_t *) e, invocation); - } - - clear_stack (invocation); - - if (invocation->timer) - delete_timer (invocation->timer, invocation_timer_callback); - - magic_free_env (invocation->env); - - map_delblock (&invocation->bl); - map_delobject (invocation->bl.id, BL_SPELL); // also frees the object -// free(invocation); -} - -static void -char_set_weapon_icon (character_t * subject, int count, int icon, int look) -{ - const int old_icon = subject->attack_spell_icon_override; - - subject->attack_spell_icon_override = icon; - subject->attack_spell_look_override = look; - - if (old_icon && old_icon != icon) - clif_status_change (&subject->bl, old_icon, 0); - - clif_fixpcpos (subject); - if (count) - { - clif_changelook (&subject->bl, LOOK_WEAPON, look); - if (icon) - clif_status_change (&subject->bl, icon, 1); - } - else - { - /* Set it to `normal' */ - clif_changelook (&subject->bl, LOOK_WEAPON, subject->status.weapon); - } -} - -static void char_set_attack_info (character_t * subject, int speed, int range) -{ - subject->attack_spell_delay = speed; - subject->attack_spell_range = range; - - if (speed == 0) - { - pc_calcstatus (subject, 1); - clif_updatestatus (subject, SP_ASPD); - clif_updatestatus (subject, SP_ATTACKRANGE); - } - else - { - subject->aspd = speed; - clif_updatestatus (subject, SP_ASPD); - clif_updatestatus (subject, SP_ATTACKRANGE); - } -} - -void magic_stop_completely (character_t * c) -{ - int i; - // Zap all status change references to spells - for (i = 0; i < MAX_STATUSCHANGE; i++) - c->sc_data[i].spell_invocation = 0; - - while (c->active_spells) - spell_free_invocation (c->active_spells); - - if (c->attack_spell_override) - { - invocation_t *attack_spell = - (invocation_t *) map_id2bl (c->attack_spell_override); - if (attack_spell) - spell_free_invocation (attack_spell); - c->attack_spell_override = 0; - char_set_weapon_icon (c, 0, 0, 0); - char_set_attack_info (c, 0, 0); - } -} - -/* Spell execution has finished normally or we have been notified by a finished skill timer */ -static void try_to_finish_invocation (invocation_t * invocation) -{ - if (invocation->status_change_refs_nr == 0 && !invocation->current_effect) - { - if (invocation->end_effect) - { - clear_stack (invocation); - invocation->current_effect = invocation->end_effect; - invocation->end_effect = NULL; - spell_execute (invocation); - } - else - spell_free_invocation (invocation); - } -} - -static int trigger_spell (int subject, int spell) -{ - invocation_t *invocation = (invocation_t *) map_id2bl (spell); - - if (!invocation) - return 0; - - invocation = spell_clone_effect (invocation); - - spell_bind ((character_t *) map_id2bl (subject), invocation); - magic_clear_var (&invocation->env->vars[VAR_CASTER]); - invocation->env->vars[VAR_CASTER].ty = TY_ENTITY; - invocation->env->vars[VAR_CASTER].v.v_int = subject; - - return invocation->bl.id; -} - -static void entity_warp (entity_t * target, int destm, int destx, int desty); - -static void char_update (character_t * character) -{ - entity_warp ((entity_t *) character, character->bl.m, character->bl.x, - character->bl.y); -} - -static void timer_callback_effect (timer_id UNUSED, tick_t UNUSED, custom_id_t id, custom_data_t data) -{ - entity_t *target = map_id2bl (id); - if (target) - clif_misceffect (target, data); -} - -static void entity_effect (entity_t * entity, int effect_nr, int delay) -{ - add_timer (gettick () + delay, - &timer_callback_effect, entity->id, effect_nr); -} - -void magic_unshroud (character_t * other_char) -{ - other_char->state.shroud_active = 0; - // Now warp the caster out of and back into here to refresh everyone's display - char_update (other_char); - clif_displaymessage (other_char->fd, "Your shroud has been dispelled!"); -// entity_effect(&other_char->bl, MAGIC_EFFECT_REVEAL); -} - -static void -timer_callback_effect_npc_delete (timer_id UNUSED, tick_t odelay, - custom_id_t npc_id, custom_data_t UNUSED) -{ - struct npc_data *effect_npc = (struct npc_data *) map_id2bl (npc_id); - npc_free (effect_npc); -} - -static struct npc_data *local_spell_effect (int m, int x, int y, int effect, - int tdelay) -{ - int delay = 30000; /* 1 minute should be enough for all interesting spell effects, I hope */ - struct npc_data *effect_npc = npc_spawn_text (m, x, y, - INVISIBLE_NPC, "", "?"); - int effect_npc_id = effect_npc->bl.id; - - entity_effect (&effect_npc->bl, effect, tdelay); - add_timer (gettick () + delay, - timer_callback_effect_npc_delete, effect_npc_id, 0); - - return effect_npc; -} - -static int op_sfx (env_t * env, int args_nr, val_t * args) -{ - int delay = ARGINT (2); - - if (TY (0) == TY_ENTITY) - { - entity_effect (ARGENTITY (0), ARGINT (1), delay); - } - else if (TY (0) == TY_LOCATION) - { - local_spell_effect (ARGLOCATION (0).m, - ARGLOCATION (0).x, - ARGLOCATION (0).y, ARGINT (1), delay); - } - else - return 1; - - return 0; -} - -static int op_instaheal (env_t * env, int args_nr, val_t * args) -{ - entity_t *caster = (VAR (VAR_CASTER).ty == TY_ENTITY) - ? map_id2bl (VAR (VAR_CASTER).v.v_int) : NULL; - entity_t *subject = ARGENTITY (0); - if (!caster) - caster = subject; - - if (caster->type == BL_PC && subject->type == BL_PC) - { - character_t *caster_pc = (character_t *) caster; - character_t *subject_pc = (character_t *) subject; - MAP_LOG_PC (caster_pc, "SPELLHEAL-INSTA PC%d FOR %d", - subject_pc->status.char_id, ARGINT (1)); - } - - battle_heal (caster, subject, ARGINT (1), ARGINT (2), 0); - return 0; -} - -static int op_itemheal (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - if (subject->type == BL_PC) - { - pc_itemheal ((struct map_session_data *) subject, - ARGINT (1), ARGINT (2)); - } - else - return op_instaheal (env, args_nr, args); - - return 0; -} - -#define SHROUD_HIDE_NAME_TALKING_FLAG (1 << 0) -#define SHROUD_DISAPPEAR_ON_PICKUP_FLAG (1 << 1) -#define SHROUD_DISAPPEAR_ON_TALK_FLAG (1 << 2) - -#define ARGCHAR(n) (ARGENTITY(n)->type == BL_PC) ? (character_t *)(ARGENTITY(n)) : NULL - -static int op_shroud (env_t * env, int args_nr, val_t * args) -{ - character_t *subject = ARGCHAR (0); - int arg = ARGINT (1); - - if (!subject) - return 0; - - subject->state.shroud_active = 1; - subject->state.shroud_hides_name_talking = - (arg & SHROUD_HIDE_NAME_TALKING_FLAG) != 0; - subject->state.shroud_disappears_on_pickup = - (arg & SHROUD_DISAPPEAR_ON_PICKUP_FLAG) != 0; - subject->state.shroud_disappears_on_talk = - (arg & SHROUD_DISAPPEAR_ON_TALK_FLAG) != 0; - return 0; -} - -static int op_reveal (env_t * env, int args_nr, val_t * args) -{ - character_t *subject = ARGCHAR (0); - - if (subject && subject->state.shroud_active) - magic_unshroud (subject); - - return 0; -} - -static int op_message (env_t * env, int args_nr, val_t * args) -{ - character_t *subject = ARGCHAR (0); - - if (subject) - clif_displaymessage (subject->fd, ARGSTR (1)); - - return 0; -} - -static void -timer_callback_kill_npc (timer_id UNUSED, tick_t odelay, custom_id_t npc_id, - custom_data_t data) -{ - struct npc_data *npc = (struct npc_data *) map_id2bl (npc_id); - if (npc) - npc_free (npc); -} - -static int op_messenger_npc (env_t * env, int args_nr, val_t * args) -{ - struct npc_data *npc; - location_t *loc = &ARGLOCATION (0); - - npc = npc_spawn_text (loc->m, loc->x, loc->y, - ARGINT (1), ARGSTR (2), ARGSTR (3)); - - add_timer (gettick () + ARGINT (4), - &timer_callback_kill_npc, npc->bl.id, 0); - - return 0; -} - -static void entity_warp (entity_t * target, int destm, int destx, int desty) -{ - if (target->type == BL_PC || target->type == BL_MOB) - { - - switch (target->type) - { - case BL_PC: - { - character_t *character = (character_t *) target; - char *map_name; - clif_clearchar_area (&character->bl, 3); - map_delblock (&character->bl); - character->bl.x = destx; - character->bl.y = desty; - character->bl.m = destm; - - pc_touch_all_relevant_npcs (character); - - // Note that touching NPCs may have triggered warping and thereby updated x and y: - map_name = map[character->bl.m].name; - - // Warp part #1: update relevant data, interrupt trading etc.: - pc_setpos (character, map_name, character->bl.x, character->bl.y, 0); - // Warp part #2: now notify the client - clif_changemap (character, map_name, - character->bl.x, character->bl.y); - break; - } - case BL_MOB: - target->x = destx; - target->y = desty; - target->m = destm; - clif_fixmobpos ((struct mob_data *) target); - break; - } - } -} - -static int op_move (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - int dir = ARGDIR (1); - - int newx = subject->x + heading_x[dir]; - int newy = subject->y + heading_y[dir]; - - if (!map_is_solid (subject->m, newx, newy)) - entity_warp (subject, subject->m, newx, newy); - - return 0; -} - -static int op_warp (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - location_t *loc = &ARGLOCATION (1); - - entity_warp (subject, loc->m, loc->x, loc->y); - - return 0; -} - -static int op_banish (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - - if (subject->type == BL_MOB) - { - struct mob_data *mob = (struct mob_data *) subject; - - if (mob->mode & MOB_MODE_SUMMONED) - mob_catch_delete (mob, 3); - } - - return 0; -} - -static void -record_status_change (invocation_t * invocation, int bl_id, int sc_id) -{ - int index = invocation->status_change_refs_nr++; - status_change_ref_t *cr; - - RECREATE (invocation->status_change_refs, status_change_ref_t, invocation->status_change_refs_nr); - - cr = &invocation->status_change_refs[index]; - - cr->sc_type = sc_id; - cr->bl_id = bl_id; -} - -static int op_status_change (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - int invocation_id = VAR (VAR_INVOCATION).ty == TY_INVOCATION - ? VAR (VAR_INVOCATION).v.v_int : 0; - invocation_t *invocation = (invocation_t *) map_id2bl (invocation_id); - - skill_status_effect (subject, ARGINT (1), ARGINT (2), ARGINT (3), - ARGINT (4), ARGINT (5), ARGINT (6), 0, - invocation_id); - - if (invocation && subject->type == BL_PC) - record_status_change (invocation, subject->id, ARGINT (1)); - - return 0; -} - -static int op_stop_status_change (env_t * env, int args_nr, val_t * args) -{ - entity_t *subject = ARGENTITY (0); - - skill_status_change_end (subject, ARGINT (1), -1); - - return 0; -} - -static int op_override_attack (env_t * env, int args_nr, val_t * args) -{ - entity_t *psubject = ARGENTITY (0); - int charges = ARGINT (1); - int attack_delay = ARGINT (2); - int attack_range = ARGINT (3); - int icon = ARGINT (4); - int look = ARGINT (5); - int stopattack = ARGINT (6); - character_t *subject; - - if (psubject->type != BL_PC) - return 0; - - subject = (character_t *) psubject; - - if (subject->attack_spell_override) - { - invocation_t *old_invocation = - (invocation_t *) map_id2bl (subject->attack_spell_override); - if (old_invocation) - spell_free_invocation (old_invocation); - } - - subject->attack_spell_override = - trigger_spell (subject->bl.id, VAR (VAR_INVOCATION).v.v_int); - subject->attack_spell_charges = charges; - - if (subject->attack_spell_override) - { - invocation_t *attack_spell = - (invocation_t *) map_id2bl (subject->attack_spell_override); - if (attack_spell && stopattack) - attack_spell->flags |= INVOCATION_FLAG_STOPATTACK; - - char_set_weapon_icon (subject, charges, icon, look); - char_set_attack_info (subject, attack_delay, attack_range); - } - - return 0; -} - -static int op_create_item (env_t * env, int args_nr, val_t * args) -{ - struct item item; - entity_t *entity = ARGENTITY (0); - character_t *subject; - int stackable; - int count = ARGINT (2); - if (count <= 0) - return 0; - - if (entity->type == BL_PC) - subject = (character_t *) entity; - else - return 0; - - GET_ARG_ITEM (1, item, stackable); - - if (!stackable) - while (count--) - pc_additem (subject, &item, 1); - else - pc_additem (subject, &item, count); - - return 0; -} - -#define AGGRAVATION_MODE_ATTACKS_CASTER(n) ((n) == 0 || (n) == 2) -#define AGGRAVATION_MODE_MAKES_AGGRESSIVE(n) ((n) > 0) - -static int op_aggravate (env_t * env, int args_nr, val_t * args) -{ - entity_t *victim = ARGENTITY (2); - int mode = ARGINT (1); - entity_t *target = ARGENTITY (0); - struct mob_data *other; - - if (target->type == BL_MOB) - other = (struct mob_data *) target; - else - return 0; - - mob_target (other, victim, battle_get_range (victim)); - - if (AGGRAVATION_MODE_MAKES_AGGRESSIVE (mode)) - other->mode = 0x85 | (other->mode & MOB_SENSIBLE_MASK); /* war */ - - if (AGGRAVATION_MODE_ATTACKS_CASTER (mode)) - { - other->target_id = victim->id; - other->attacked_id = victim->id; - } - - return 0; -} - -#define MONSTER_ATTITUDE_HOSTILE 0 -#define MONSTER_ATTITUDE_FRIENDLY 1 -#define MONSTER_ATTITUDE_SERVANT 2 -#define MONSTER_ATTITUDE_FROZEN 3 - -static int op_spawn (env_t * env, int args_nr, val_t * args) -{ - area_t *area = ARGAREA (0); - entity_t *owner_e = ARGENTITY (1); - int monster_id = ARGINT (2); - int monster_attitude = ARGINT (3); - int monster_count = ARGINT (4); - int monster_lifetime = ARGINT (5); - int i; - - character_t *owner = (monster_attitude == MONSTER_ATTITUDE_SERVANT - && owner_e->type == - BL_PC) ? (character_t *) owner_e : NULL; - - for (i = 0; i < monster_count; i++) - { - location_t loc; - magic_random_location (&loc, area); - - int mob_id; - struct mob_data *mob; - - mob_id = mob_once_spawn (owner, map[loc.m].name, loc.x, loc.y, "--ja--", // Is that needed? - monster_id, 1, ""); - - mob = (struct mob_data *) map_id2bl (mob_id); - - if (mob) - { - mob->mode = mob_db[monster_id].mode; - - switch (monster_attitude) - { - - case MONSTER_ATTITUDE_SERVANT: - mob->state.special_mob_ai = 1; - mob->mode |= 0x04; - break; - - case MONSTER_ATTITUDE_FRIENDLY: - mob->mode = 0x80 | (mob->mode & 1); - break; - - case MONSTER_ATTITUDE_HOSTILE: - mob->mode = 0x84 | (mob->mode & 1); - if (owner) - { - mob->target_id = owner->bl.id; - mob->attacked_id = owner->bl.id; - } - break; - - case MONSTER_ATTITUDE_FROZEN: - mob->mode = 0; - break; - } - - mob->mode |= - MOB_MODE_SUMMONED | MOB_MODE_TURNS_AGAINST_BAD_MASTER; - - mob->deletetimer = add_timer (gettick () + monster_lifetime, - mob_timer_delete, mob_id, 0); - - if (owner) - { - mob->master_id = owner->bl.id; - mob->master_dist = 6; - } - } - } - - return 0; -} - -static char *get_invocation_name (env_t * env) -{ - invocation_t *invocation; - - if (VAR (VAR_INVOCATION).ty != TY_INVOCATION) - return "?"; - invocation = (invocation_t *) map_id2bl (VAR (VAR_INVOCATION).v.v_int); - - if (invocation) - return invocation->spell->name; - else - return "??"; -} - -static int op_injure (env_t * env, int args_nr, val_t * args) -{ - entity_t *caster = ARGENTITY (0); - entity_t *target = ARGENTITY (1); - int damage_caused = ARGINT (2); - int mp_damage = ARGINT (3); - int target_hp = battle_get_hp (target); - int mdef = battle_get_mdef (target); - - if (target->type == BL_PC && !map[target->m].flag.pvp && !((character_t *) target)->special_state.killable && (caster->type != BL_PC || !((character_t *) caster)->special_state.killer)) - return 0; /* Cannot damage other players outside of pvp */ - - if (target != caster) - { - /* Not protected against own spells */ - damage_caused = (damage_caused * (100 - mdef)) / 100; - mp_damage = (mp_damage * (100 - mdef)) / 100; - } - - damage_caused = (damage_caused > target_hp) ? target_hp : damage_caused; - - if (damage_caused < 0) - damage_caused = 0; - - // display damage first, because dealing damage may deallocate the target. - clif_damage (caster, target, gettick (), 0, 0, damage_caused, 0, 0, 0); - - if (caster->type == BL_PC) - { - character_t *caster_pc = (character_t *) caster; - if (target->type == BL_MOB) - { - struct mob_data *mob = (struct mob_data *) target; - - MAP_LOG_PC (caster_pc, "SPELLDMG MOB%d %d FOR %d BY %s", - mob->bl.id, mob->mob_class, damage_caused, - get_invocation_name (env)); - } - } - battle_damage (caster, target, damage_caused, mp_damage); - - return 0; -} - -static int op_emote (env_t * env, int args_nr, val_t * args) -{ - entity_t *victim = ARGENTITY (0); - int emotion = ARGINT (1); - clif_emotion (victim, emotion); - - return 0; -} - -static int op_set_script_variable (env_t * env, int args_nr, val_t * args) -{ - character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - - if (!c) - return 1; - - pc_setglobalreg (c, ARGSTR (1), ARGINT (2)); - - return 0; -} - -static int op_set_hair_colour (env_t * env, int args_nr, val_t * args) -{ - character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - - if (!c) - return 1; - - pc_changelook (c, LOOK_HAIR_COLOR, ARGINT (1)); - - return 0; -} - -static int op_set_hair_style (env_t * env, int args_nr, val_t * args) -{ - character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - - if (!c) - return 1; - - pc_changelook (c, LOOK_HAIR, ARGINT (1)); - - return 0; -} - -static int op_drop_item_for (env_t * env, int args_nr, val_t * args) -{ - struct item item; - int stackable; - location_t *loc = &ARGLOCATION (0); - int count = ARGINT (2); - int time = ARGINT (3); - character_t *c = ((args_nr > 4) && (ETY (4) == BL_PC)) ? ARGPC (4) : NULL; - int delay = (args_nr > 5) ? ARGINT (5) : 0; - int delaytime[3] = { delay, delay, delay }; - character_t *owners[3] = { c, NULL, NULL }; - - GET_ARG_ITEM (1, item, stackable); - - if (stackable) - map_addflooritem_any (&item, count, loc->m, loc->x, loc->y, - owners, delaytime, time, 0); - else - while (count-- > 0) - map_addflooritem_any (&item, 1, loc->m, loc->x, loc->y, - owners, delaytime, time, 0); - - return 0; -} - -static int op_gain_exp (env_t * env, int args_nr, val_t * args) -{ - character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; - - if (!c) - return 1; - - pc_gainexp_reason (c, ARGINT (1), ARGINT (2), ARGINT (3)); - return 0; -} - -static op_t operations[] = { - {"sfx", ".ii", op_sfx}, - {"instaheal", "eii", op_instaheal}, - {"itemheal", "eii", op_itemheal}, - {"shroud", "ei", op_shroud}, - {"unshroud", "e", op_reveal}, - {"message", "es", op_message}, - {"messenger_npc", "lissi", op_messenger_npc}, - {"move", "ed", op_move}, - {"warp", "el", op_warp}, - {"banish", "e", op_banish}, - {"status_change", "eiiiiii", op_status_change}, - {"stop_status_change", "ei", op_stop_status_change}, - {"override_attack", "eiiiiii", op_override_attack}, - {"create_item", "e.i", op_create_item}, - {"aggravate", "eie", op_aggravate}, - {"spawn", "aeiiii", op_spawn}, - {"injure", "eeii", op_injure}, - {"emote", "ei", op_emote}, - {"set_script_variable", "esi", op_set_script_variable}, - {"set_hair_colour", "ei", op_set_hair_colour}, - {"set_hair_style", "ei", op_set_hair_style}, - {"drop_item", "l.ii", op_drop_item_for}, - {"drop_item_for", "l.iiei", op_drop_item_for}, - {"gain_experience", "eiii", op_gain_exp}, - {NULL, NULL, NULL} -}; - -static int operations_sorted = 0; -static int operation_count; - -int compare_operations (const void *lhs, const void *rhs) -{ - return strcmp (((op_t *) lhs)->name, ((op_t *) rhs)->name); -} - -op_t *magic_get_op (char *name, int *index) -{ - op_t key; - - if (!operations_sorted) - { - op_t *opc = operations; - - while (opc->name) - ++opc; - - operation_count = opc - operations; - - qsort (operations, operation_count, sizeof (op_t), - compare_operations); - operations_sorted = 1; - } - - key.name = name; - op_t *op = (op_t *)bsearch (&key, operations, operation_count, sizeof (op_t), - compare_operations); - - if (op && index) - *index = op - operations; - - return op; -} - -void -spell_effect_report_termination (int invocation_id, int bl_id, int sc_id, - int supplanted) -{ - int i; - int index = -1; - invocation_t *invocation = (invocation_t *) map_id2bl (invocation_id); - - if (!invocation || invocation->bl.type != BL_SPELL) - return; - - for (i = 0; i < invocation->status_change_refs_nr; i++) - { - status_change_ref_t *cr = &invocation->status_change_refs[i]; - if (cr->sc_type == sc_id && cr->bl_id == bl_id) - { - index = i; - break; - } - } - - if (index == -1) - { - entity_t *entity = map_id2bl (bl_id); - if (entity->type == BL_PC) - fprintf (stderr, - "[magic] INTERNAL ERROR: spell-effect-report-termination: tried to terminate on unexpected bl %d, sc %d\n", - bl_id, sc_id); - return; - } - - if (index == invocation->status_change_refs_nr - 1) - invocation->status_change_refs_nr--; - else /* Copy last change ref to the one we are deleting */ - invocation->status_change_refs[index] = - invocation-> - status_change_refs[--invocation->status_change_refs_nr]; - - try_to_finish_invocation (invocation); -} - -static effect_t *return_to_stack (invocation_t * invocation) -{ - if (!invocation->stack_size) - return NULL; - else - { - cont_activation_record_t *ar = - invocation->stack + (invocation->stack_size - 1); - switch (ar->ty) - { - - case CONT_STACK_PROC: - { - effect_t *ret = ar->return_location; - int i; - - for (i = 0; i < ar->c.c_proc.args_nr; i++) - { - val_t *var = - &invocation->env->vars[ar->c.c_proc.formals[i]]; - magic_clear_var (var); - *var = ar->c.c_proc.old_actuals[i]; - } - - clear_activation_record (ar); - --invocation->stack_size; - - return ret; - } - - case CONT_STACK_FOREACH: - { - int entity_id; - val_t *var = &invocation->env->vars[ar->c.c_foreach.id]; - - do - { - if (ar->c.c_foreach.index >= ar->c.c_foreach.entities_nr) - { - effect_t *ret = ar->return_location; - clear_activation_record (ar); - --invocation->stack_size; - return ret; - } - - entity_id = - ar->c.c_foreach.entities[ar->c.c_foreach.index++]; - } - while (!entity_id || !map_id2bl (entity_id)); - - magic_clear_var (var); - var->ty = ar->c.c_foreach.ty; - var->v.v_int = entity_id; - - return ar->c.c_foreach.body; - } - - case CONT_STACK_FOR: - if (ar->c.c_for.current > ar->c.c_for.stop) - { - effect_t *ret = ar->return_location; - clear_activation_record (ar); - --invocation->stack_size; - return ret; - } - - magic_clear_var (&invocation->env->vars[ar->c.c_for.id]); - invocation->env->vars[ar->c.c_for.id].ty = TY_INT; - invocation->env->vars[ar->c.c_for.id].v.v_int = - ar->c.c_for.current++; - - return ar->c.c_for.body; - - default: - fprintf (stderr, - "[magic] INTERNAL ERROR: While executing spell `%s': stack corruption\n", - invocation->spell->name); - return NULL; - } - } -} - -static cont_activation_record_t *add_stack_entry (invocation_t * invocation, - int ty, - effect_t * return_location) -{ - cont_activation_record_t *ar = - invocation->stack + invocation->stack_size++; - if (invocation->stack_size >= MAX_STACK_SIZE) - { - fprintf (stderr, - "[magic] Execution stack size exceeded in spell `%s'; truncating effect\n", - invocation->spell->name); - invocation->stack_size--; - return NULL; - } - - ar->ty = ty; - ar->return_location = return_location; - return ar; -} - -static int find_entities_in_area_c (entity_t * target, va_list va) -{ - int *entities_allocd_p = va_arg (va, int *); - int *entities_nr_p = va_arg (va, int *); - int **entities_p = va_arg (va, int **); - int filter = va_arg (va, int); - -/* The following macro adds an entity to the result list: */ -#define ADD_ENTITY(e) \ - if (*entities_nr_p == *entities_allocd_p) { \ - /* Need more space */ \ - (*entities_allocd_p) += 32; \ - RECREATE (*entities_p, int, *entities_allocd_p); \ - } \ - (*entities_p)[(*entities_nr_p)++] = e; - - switch (target->type) - { - - case BL_PC: - if (filter == FOREACH_FILTER_PC - || filter == FOREACH_FILTER_ENTITY - || (filter == FOREACH_FILTER_TARGET - && map[target->m].flag.pvp)) - break; - else if (filter == FOREACH_FILTER_SPELL) - { /* Check all spells bound to the caster */ - invocation_t *invoc = ((character_t *) target)->active_spells; - /* Add all spells locked onto thie PC */ - - while (invoc) - { - ADD_ENTITY (invoc->bl.id); - invoc = invoc->next_invocation; - } - } - return 0; - - case BL_MOB: - if (filter == FOREACH_FILTER_MOB - || filter == FOREACH_FILTER_ENTITY - || filter == FOREACH_FILTER_TARGET) - break; - else - return 0; - - case BL_SPELL: - if (filter == FOREACH_FILTER_SPELL) - { - invocation_t *invocation = (invocation_t *) target; - - /* Check whether the spell is `bound'-- if so, we'll consider it iff we see the caster (case BL_PC). */ - if (invocation->flags & INVOCATION_FLAG_BOUND) - return 0; - else - break; /* Add the spell */ - } - else - return 0; - - case BL_NPC: - if (filter == FOREACH_FILTER_NPC) - break; - else - return 0; - - default: - return 0; - } - - ADD_ENTITY (target->id); -#undef ADD_ENTITY - return 0; -} - -static void -find_entities_in_area (area_t * area, int *entities_allocd_p, - int *entities_nr_p, int **entities_p, int filter) -{ - switch (area->ty) - { - case AREA_UNION: - find_entities_in_area (area->a.a_union[0], entities_allocd_p, - entities_nr_p, entities_p, filter); - find_entities_in_area (area->a.a_union[1], entities_allocd_p, - entities_nr_p, entities_p, filter); - break; - - default: - { - int m, x, y, width, height; - magic_area_rect (&m, &x, &y, &width, &height, area); - map_foreachinarea (find_entities_in_area_c, - m, x, y, x + width, y + height, - 0 /* filter elsewhere */ , - entities_allocd_p, entities_nr_p, entities_p, - filter); - } - } -} - -static effect_t *run_foreach (invocation_t * invocation, effect_t * foreach, - effect_t * return_location) -{ - val_t area; - int filter = foreach->e.e_foreach.filter; - int id = foreach->e.e_foreach.id; - effect_t *body = foreach->e.e_foreach.body; - - magic_eval (invocation->env, &area, foreach->e.e_foreach.area); - - if (area.ty != TY_AREA) - { - magic_clear_var (&area); - fprintf (stderr, - "[magic] Error in spell `%s': FOREACH loop over non-area\n", - invocation->spell->name); - return return_location; - } - else - { - cont_activation_record_t *ar = - add_stack_entry (invocation, CONT_STACK_FOREACH, return_location); - int entities_allocd = 64; - int *entities_collect; - int *entities; - int *shuffle_board; - int entities_nr = 0; - int i; - - if (!ar) - return return_location; - - CREATE (entities_collect, int, entities_allocd); - - find_entities_in_area (area.v.v_area, &entities_allocd, &entities_nr, - &entities_collect, filter); - - /* Now shuffle */ - CREATE (shuffle_board, int, entities_nr); - CREATE (entities, int, entities_nr); - for (i = 0; i < entities_nr; i++) - shuffle_board[i] = i; - - for (i = entities_nr - 1; i >= 0; i--) - { - int random_index = rand () % (i + 1); - entities[i] = entities_collect[shuffle_board[random_index]]; - shuffle_board[random_index] = shuffle_board[i]; // thus, we are guaranteed only to use unused indices - } - - free (entities_collect); - free (shuffle_board); - /* Done shuffling */ - - ar->c.c_foreach.id = id; - ar->c.c_foreach.body = body; - ar->c.c_foreach.index = 0; - ar->c.c_foreach.entities_nr = entities_nr; - ar->c.c_foreach.entities = entities; - ar->c.c_foreach.ty = - (filter == FOREACH_FILTER_SPELL) ? TY_INVOCATION : TY_ENTITY; - - magic_clear_var (&area); - - return return_to_stack (invocation); - } -} - -static effect_t *run_for (invocation_t * invocation, effect_t * for_, - effect_t * return_location) -{ - cont_activation_record_t *ar; - int id = for_->e.e_for.id; - val_t start; - val_t stop; - - magic_eval (invocation->env, &start, for_->e.e_for.start); - magic_eval (invocation->env, &stop, for_->e.e_for.stop); - - if (start.ty != TY_INT || stop.ty != TY_INT) - { - magic_clear_var (&start); - magic_clear_var (&stop); - fprintf (stderr, - "[magic] Error in spell `%s': FOR loop start or stop point is not an integer\n", - invocation->spell->name); - return return_location; - } - - ar = add_stack_entry (invocation, CONT_STACK_FOR, return_location); - - if (!ar) - return return_location; - - ar->c.c_for.id = id; - ar->c.c_for.current = start.v.v_int; - ar->c.c_for.stop = stop.v.v_int; - ar->c.c_for.body = for_->e.e_for.body; - - return return_to_stack (invocation); -} - -static effect_t *run_call (invocation_t * invocation, - effect_t * return_location) -{ - effect_t *current = invocation->current_effect; - cont_activation_record_t *ar; - int args_nr = current->e.e_call.args_nr; - int *formals = current->e.e_call.formals; - val_t *old_actuals; - CREATE (old_actuals, val_t, args_nr); - int i; - - ar = add_stack_entry (invocation, CONT_STACK_PROC, return_location); - ar->c.c_proc.args_nr = args_nr; - ar->c.c_proc.formals = formals; - ar->c.c_proc.old_actuals = old_actuals; - for (i = 0; i < args_nr; i++) - { - val_t *env_val = &invocation->env->vars[formals[i]]; - val_t result; - magic_copy_var (&old_actuals[i], env_val); - magic_eval (invocation->env, &result, current->e.e_call.actuals[i]); - *env_val = result; - } - - return current->e.e_call.body; -} - -#ifdef DEBUG -static void print_cfg (int i, effect_t * e) -{ - int j; - for (j = 0; j < i; j++) - printf (" "); - - printf ("%p: ", e); - - if (!e) - { - puts (" -- end --"); - return; - } - - switch (e->ty) - { - case EFFECT_SKIP: - puts ("SKIP"); - break; - case EFFECT_END: - puts ("END"); - break; - case EFFECT_ABORT: - puts ("ABORT"); - break; - case EFFECT_ASSIGN: - puts ("ASSIGN"); - break; - case EFFECT_FOREACH: - puts ("FOREACH"); - print_cfg (i + 1, e->e.e_foreach.body); - break; - case EFFECT_FOR: - puts ("FOR"); - print_cfg (i + 1, e->e.e_for.body); - break; - case EFFECT_IF: - puts ("IF"); - for (j = 0; j < i; j++) - printf (" "); - puts ("THEN"); - print_cfg (i + 1, e->e.e_if.true_branch); - for (j = 0; j < i; j++) - printf (" "); - puts ("ELSE"); - print_cfg (i + 1, e->e.e_if.false_branch); - break; - case EFFECT_SLEEP: - puts ("SLEEP"); - break; - case EFFECT_SCRIPT: - puts ("SCRIPT"); - break; - case EFFECT_BREAK: - puts ("BREAK"); - break; - case EFFECT_OP: - puts ("OP"); - break; - } - print_cfg (i, e->next); -} -#endif - -/** - * Execute a spell invocation until we abort, finish, or hit the next `sleep'. - * - * Use spell_execute() to automate handling of timers - * - * Returns: 0 if finished (all memory is freed implicitly) - * >1 if we hit `sleep'; the result is the number of ticks we should sleep for. - * -1 if we paused to wait for a user action (via script interaction) - */ -static int spell_run (invocation_t * invocation, int allow_delete) -{ - const int invocation_id = invocation->bl.id; -#define REFRESH_INVOCATION invocation = (invocation_t *) map_id2bl(invocation_id); if (!invocation) return 0; - -#ifdef DEBUG - fprintf (stderr, "Resuming execution: invocation of `%s'\n", - invocation->spell->name); - print_cfg (1, invocation->current_effect); -#endif - while (invocation->current_effect) - { - effect_t *e = invocation->current_effect; - effect_t *next = e->next; - int i; - -#ifdef DEBUG - fprintf (stderr, "Next step of type %d\n", e->ty); - dump_env (invocation->env); -#endif - - switch (e->ty) - { - case EFFECT_SKIP: - break; - - case EFFECT_ABORT: - invocation->flags |= INVOCATION_FLAG_ABORTED; - invocation->end_effect = NULL; - case EFFECT_END: - clear_stack (invocation); - next = NULL; - break; - - case EFFECT_ASSIGN: - magic_eval (invocation->env, - &invocation->env->vars[e->e.e_assign.id], - e->e.e_assign.expr); - break; - - case EFFECT_FOREACH: - next = run_foreach (invocation, e, next); - break; - - case EFFECT_FOR: - next = run_for (invocation, e, next); - break; - - case EFFECT_IF: - if (magic_eval_int (invocation->env, e->e.e_if.cond)) - next = e->e.e_if.true_branch; - else - next = e->e.e_if.false_branch; - break; - - case EFFECT_SLEEP: - { - int sleeptime = - magic_eval_int (invocation->env, e->e.e_sleep); - invocation->current_effect = next; - if (sleeptime > 0) - return sleeptime; - break; - } - - case EFFECT_SCRIPT: - { - character_t *caster = - (character_t *) map_id2bl (invocation->caster); - if (caster) - { - env_t *env = invocation->env; - character_t *caster = - (character_t *) map_id2bl (invocation->caster); - argrec_t arg[] = { {"@target",.v.i = - VAR (VAR_TARGET).ty == - TY_ENTITY ? 0 : VAR (VAR_TARGET). - v.v_int} - , - {"@caster",.v.i = invocation->caster} - , - {"@caster_name$",.v.s = caster ? caster->status.name : ""} - }; - int message_recipient = - VAR (VAR_SCRIPTTARGET).ty == - TY_ENTITY ? VAR (VAR_SCRIPTTARGET). - v.v_int : invocation->caster; - character_t *recipient = - (character_t *) map_id2bl (message_recipient); - - if (recipient->npc_id - && recipient->npc_id != invocation->bl.id) - break; /* Don't send multiple message boxes at once */ - - if (!invocation->script_pos) // first time running this script? - clif_spawn_fake_npc_for_player (recipient, - invocation->bl.id); - // We have to do this or otherwise the client won't think that it's - // dealing with an NPC - - int newpos = run_script_l (e->e.e_script, - invocation->script_pos, - message_recipient, - invocation->bl.id, - 3, arg); - /* Returns the new script position, or -1 once the script is finished */ - if (newpos != -1) - { - /* Must set up for continuation */ - recipient->npc_id = invocation->bl.id; - recipient->npc_pos = invocation->script_pos = newpos; - return -1; /* Signal `wait for script' */ - } - else - invocation->script_pos = 0; - clif_clearchar_id (invocation->bl.id, 1, caster->fd); - } - REFRESH_INVOCATION; // Script may have killed the caster - break; - } - - case EFFECT_BREAK: - next = return_to_stack (invocation); - break; - - case EFFECT_OP: - { - op_t *op = &operations[e->e.e_op.id]; - val_t args[MAX_ARGS]; - - for (i = 0; i < e->e.e_op.args_nr; i++) - magic_eval (invocation->env, &args[i], e->e.e_op.args[i]); - - if (!magic_signature_check ("effect", op->name, op->signature, - e->e.e_op.args_nr, args, - e->e.e_op.line_nr, - e->e.e_op.column)) - op->op (invocation->env, e->e.e_op.args_nr, args); - - for (i = 0; i < e->e.e_op.args_nr; i++) - magic_clear_var (&args[i]); - - REFRESH_INVOCATION; // Effect may have killed the caster - break; - } - - case EFFECT_CALL: - next = run_call (invocation, next); - break; - - default: - fprintf (stderr, - "[magic] INTERNAL ERROR: Unknown effect %d\n", - e->ty); - } - - if (!next) - next = return_to_stack (invocation); - - invocation->current_effect = next; - } - - if (allow_delete) - try_to_finish_invocation (invocation); - return 0; -#undef REFRESH_INVOCATION -} - -extern void spell_update_location (invocation_t * invocation); - -void spell_execute_d (invocation_t * invocation, int allow_deletion) -{ - int delta; - - spell_update_location (invocation); - delta = spell_run (invocation, allow_deletion); - - if (delta > 0) - { - if (invocation->timer) - { - fprintf (stderr, - "[magic] FATAL ERROR: Trying to add multiple timers to the same spell! Already had timer: %d\n", - invocation->timer); - /* *((int *)0x0) = 0; */ - } - invocation->timer = add_timer (gettick () + delta, - &invocation_timer_callback, - invocation->bl.id, 0); - } - - /* If 0, the script cleaned itself. If -1 (wait-for-script), we must wait for the user. */ -} - -void spell_execute (invocation_t * invocation) -{ - spell_execute_d (invocation, 1); -} - -void spell_execute_script (invocation_t * invocation) -{ - if (invocation->script_pos) - spell_execute_d (invocation, 1); - /* Otherwise the script-within-the-spell has been terminated by some other means. - * In practice this happens when the script doesn't wait for user input: the client - * may still notify the server that it's done. Without the above check, we'd be - * running the same spell twice! */ -} - -int spell_attack (int caster_id, int target_id) -{ - character_t *caster = (character_t *) map_id2bl (caster_id); - invocation_t *invocation; - int stop_attack = 0; - - if (!caster) - return 0; - - invocation = (invocation_t *) map_id2bl (caster->attack_spell_override); - - if (invocation && invocation->flags & INVOCATION_FLAG_STOPATTACK) - stop_attack = 1; - - if (invocation && caster->attack_spell_charges > 0) - { - magic_clear_var (&invocation->env->vars[VAR_TARGET]); - invocation->env->vars[VAR_TARGET].ty = TY_ENTITY; - invocation->env->vars[VAR_TARGET].v.v_int = target_id; - - invocation->current_effect = invocation->trigger_effect; - invocation->flags &= ~INVOCATION_FLAG_ABORTED; - spell_execute_d (invocation, - 0 /* don't delete the invocation if done */ ); - - // If the caster died, we need to refresh here: - invocation = - (invocation_t *) map_id2bl (caster->attack_spell_override); - - if (invocation && !(invocation->flags & INVOCATION_FLAG_ABORTED)) // If we didn't abort: - caster->attack_spell_charges--; - } - - if (invocation && caster->attack_spell_override != invocation->bl.id) - { - /* Attack spell changed / was refreshed */ - // spell_free_invocation(invocation); // [Fate] This would be a double free. - } - else if (!invocation || caster->attack_spell_charges <= 0) - { - caster->attack_spell_override = 0; - char_set_weapon_icon (caster, 0, 0, 0); - char_set_attack_info (caster, 0, 0); - - if (stop_attack) - pc_stopattack (caster); - - if (invocation) - spell_free_invocation (invocation); - } - - return 1; -} diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp new file mode 100644 index 0000000..20130dd --- /dev/null +++ b/src/map/magic-stmt.cpp @@ -0,0 +1,1597 @@ +#include "magic-interpreter.hpp" +#include "magic-expr.hpp" +#include "magic-expr-eval.hpp" +#include "magic-interpreter-aux.hpp" + +int + clif_spawn_fake_npc_for_player (struct map_session_data *sd, + int fake_npc_id); + +#define INVISIBLE_NPC 127 /* used for local spell effects */ + +//#define DEBUG + +#ifdef DEBUG +static void print_val (val_t * v) +{ + switch (v->ty) + { + case TY_UNDEF: + fprintf (stderr, "UNDEF"); + break; + case TY_INT: + fprintf (stderr, "%d", v->v.v_int); + break; + case TY_DIR: + fprintf (stderr, "dir%d", v->v.v_int); + break; + case TY_STRING: + fprintf (stderr, "`%s'", v->v.v_string); + break; + default: + fprintf (stderr, "ty%d", v->ty); + break; + } +} + +static void dump_env (env_t * env) +{ + int i; + for (i = 0; i < env->base_env->vars_nr; i++) + { + val_t *v = &env->vars[i]; + val_t *bv = &env->base_env->vars[i]; + + fprintf (stderr, "%02x %30s ", i, env->base_env->var_name[i]); + print_val (v); + fprintf (stderr, "\t("); + print_val (bv); + fprintf (stderr, ")\n"); + } +} +#endif + +static void clear_activation_record (cont_activation_record_t * ar) +{ + switch (ar->ty) + { + case CONT_STACK_FOREACH: + free (ar->c.c_foreach.entities); + break; + case CONT_STACK_PROC: + free (ar->c.c_proc.old_actuals); + break; + } +} + +static void +invocation_timer_callback (timer_id UNUSED, tick_t UNUSED, custom_id_t id, custom_data_t data) +{ + invocation_t *invocation = (invocation_t *) map_id2bl (id); + + if (invocation) + { + invocation->timer = 0; + spell_execute (invocation); + } +} + +static void clear_stack (invocation_t * invocation) +{ + int i; + + for (i = 0; i < invocation->stack_size; i++) + clear_activation_record (&invocation->stack[i]); + + invocation->stack_size = 0; +} + +void spell_free_invocation (invocation_t * invocation) +{ + if (invocation->status_change_refs) + { + free (invocation->status_change_refs); + /* The following cleanup shouldn't be necessary, but I've added it to help tracking a certain bug */ + invocation->status_change_refs = NULL; + invocation->status_change_refs_nr = 0; + } + + if (invocation->flags & INVOCATION_FLAG_BOUND) + { + entity_t *e = map_id2bl (invocation->subject); + if (e && e->type == BL_PC) + spell_unbind ((character_t *) e, invocation); + } + + clear_stack (invocation); + + if (invocation->timer) + delete_timer (invocation->timer, invocation_timer_callback); + + magic_free_env (invocation->env); + + map_delblock (&invocation->bl); + map_delobject (invocation->bl.id, BL_SPELL); // also frees the object +// free(invocation); +} + +static void +char_set_weapon_icon (character_t * subject, int count, int icon, int look) +{ + const int old_icon = subject->attack_spell_icon_override; + + subject->attack_spell_icon_override = icon; + subject->attack_spell_look_override = look; + + if (old_icon && old_icon != icon) + clif_status_change (&subject->bl, old_icon, 0); + + clif_fixpcpos (subject); + if (count) + { + clif_changelook (&subject->bl, LOOK_WEAPON, look); + if (icon) + clif_status_change (&subject->bl, icon, 1); + } + else + { + /* Set it to `normal' */ + clif_changelook (&subject->bl, LOOK_WEAPON, subject->status.weapon); + } +} + +static void char_set_attack_info (character_t * subject, int speed, int range) +{ + subject->attack_spell_delay = speed; + subject->attack_spell_range = range; + + if (speed == 0) + { + pc_calcstatus (subject, 1); + clif_updatestatus (subject, SP_ASPD); + clif_updatestatus (subject, SP_ATTACKRANGE); + } + else + { + subject->aspd = speed; + clif_updatestatus (subject, SP_ASPD); + clif_updatestatus (subject, SP_ATTACKRANGE); + } +} + +void magic_stop_completely (character_t * c) +{ + int i; + // Zap all status change references to spells + for (i = 0; i < MAX_STATUSCHANGE; i++) + c->sc_data[i].spell_invocation = 0; + + while (c->active_spells) + spell_free_invocation (c->active_spells); + + if (c->attack_spell_override) + { + invocation_t *attack_spell = + (invocation_t *) map_id2bl (c->attack_spell_override); + if (attack_spell) + spell_free_invocation (attack_spell); + c->attack_spell_override = 0; + char_set_weapon_icon (c, 0, 0, 0); + char_set_attack_info (c, 0, 0); + } +} + +/* Spell execution has finished normally or we have been notified by a finished skill timer */ +static void try_to_finish_invocation (invocation_t * invocation) +{ + if (invocation->status_change_refs_nr == 0 && !invocation->current_effect) + { + if (invocation->end_effect) + { + clear_stack (invocation); + invocation->current_effect = invocation->end_effect; + invocation->end_effect = NULL; + spell_execute (invocation); + } + else + spell_free_invocation (invocation); + } +} + +static int trigger_spell (int subject, int spell) +{ + invocation_t *invocation = (invocation_t *) map_id2bl (spell); + + if (!invocation) + return 0; + + invocation = spell_clone_effect (invocation); + + spell_bind ((character_t *) map_id2bl (subject), invocation); + magic_clear_var (&invocation->env->vars[VAR_CASTER]); + invocation->env->vars[VAR_CASTER].ty = TY_ENTITY; + invocation->env->vars[VAR_CASTER].v.v_int = subject; + + return invocation->bl.id; +} + +static void entity_warp (entity_t * target, int destm, int destx, int desty); + +static void char_update (character_t * character) +{ + entity_warp ((entity_t *) character, character->bl.m, character->bl.x, + character->bl.y); +} + +static void timer_callback_effect (timer_id UNUSED, tick_t UNUSED, custom_id_t id, custom_data_t data) +{ + entity_t *target = map_id2bl (id); + if (target) + clif_misceffect (target, data); +} + +static void entity_effect (entity_t * entity, int effect_nr, int delay) +{ + add_timer (gettick () + delay, + &timer_callback_effect, entity->id, effect_nr); +} + +void magic_unshroud (character_t * other_char) +{ + other_char->state.shroud_active = 0; + // Now warp the caster out of and back into here to refresh everyone's display + char_update (other_char); + clif_displaymessage (other_char->fd, "Your shroud has been dispelled!"); +// entity_effect(&other_char->bl, MAGIC_EFFECT_REVEAL); +} + +static void +timer_callback_effect_npc_delete (timer_id UNUSED, tick_t odelay, + custom_id_t npc_id, custom_data_t UNUSED) +{ + struct npc_data *effect_npc = (struct npc_data *) map_id2bl (npc_id); + npc_free (effect_npc); +} + +static struct npc_data *local_spell_effect (int m, int x, int y, int effect, + int tdelay) +{ + int delay = 30000; /* 1 minute should be enough for all interesting spell effects, I hope */ + struct npc_data *effect_npc = npc_spawn_text (m, x, y, + INVISIBLE_NPC, "", "?"); + int effect_npc_id = effect_npc->bl.id; + + entity_effect (&effect_npc->bl, effect, tdelay); + add_timer (gettick () + delay, + timer_callback_effect_npc_delete, effect_npc_id, 0); + + return effect_npc; +} + +static int op_sfx (env_t * env, int args_nr, val_t * args) +{ + int delay = ARGINT (2); + + if (TY (0) == TY_ENTITY) + { + entity_effect (ARGENTITY (0), ARGINT (1), delay); + } + else if (TY (0) == TY_LOCATION) + { + local_spell_effect (ARGLOCATION (0).m, + ARGLOCATION (0).x, + ARGLOCATION (0).y, ARGINT (1), delay); + } + else + return 1; + + return 0; +} + +static int op_instaheal (env_t * env, int args_nr, val_t * args) +{ + entity_t *caster = (VAR (VAR_CASTER).ty == TY_ENTITY) + ? map_id2bl (VAR (VAR_CASTER).v.v_int) : NULL; + entity_t *subject = ARGENTITY (0); + if (!caster) + caster = subject; + + if (caster->type == BL_PC && subject->type == BL_PC) + { + character_t *caster_pc = (character_t *) caster; + character_t *subject_pc = (character_t *) subject; + MAP_LOG_PC (caster_pc, "SPELLHEAL-INSTA PC%d FOR %d", + subject_pc->status.char_id, ARGINT (1)); + } + + battle_heal (caster, subject, ARGINT (1), ARGINT (2), 0); + return 0; +} + +static int op_itemheal (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + if (subject->type == BL_PC) + { + pc_itemheal ((struct map_session_data *) subject, + ARGINT (1), ARGINT (2)); + } + else + return op_instaheal (env, args_nr, args); + + return 0; +} + +#define SHROUD_HIDE_NAME_TALKING_FLAG (1 << 0) +#define SHROUD_DISAPPEAR_ON_PICKUP_FLAG (1 << 1) +#define SHROUD_DISAPPEAR_ON_TALK_FLAG (1 << 2) + +#define ARGCHAR(n) (ARGENTITY(n)->type == BL_PC) ? (character_t *)(ARGENTITY(n)) : NULL + +static int op_shroud (env_t * env, int args_nr, val_t * args) +{ + character_t *subject = ARGCHAR (0); + int arg = ARGINT (1); + + if (!subject) + return 0; + + subject->state.shroud_active = 1; + subject->state.shroud_hides_name_talking = + (arg & SHROUD_HIDE_NAME_TALKING_FLAG) != 0; + subject->state.shroud_disappears_on_pickup = + (arg & SHROUD_DISAPPEAR_ON_PICKUP_FLAG) != 0; + subject->state.shroud_disappears_on_talk = + (arg & SHROUD_DISAPPEAR_ON_TALK_FLAG) != 0; + return 0; +} + +static int op_reveal (env_t * env, int args_nr, val_t * args) +{ + character_t *subject = ARGCHAR (0); + + if (subject && subject->state.shroud_active) + magic_unshroud (subject); + + return 0; +} + +static int op_message (env_t * env, int args_nr, val_t * args) +{ + character_t *subject = ARGCHAR (0); + + if (subject) + clif_displaymessage (subject->fd, ARGSTR (1)); + + return 0; +} + +static void +timer_callback_kill_npc (timer_id UNUSED, tick_t odelay, custom_id_t npc_id, + custom_data_t data) +{ + struct npc_data *npc = (struct npc_data *) map_id2bl (npc_id); + if (npc) + npc_free (npc); +} + +static int op_messenger_npc (env_t * env, int args_nr, val_t * args) +{ + struct npc_data *npc; + location_t *loc = &ARGLOCATION (0); + + npc = npc_spawn_text (loc->m, loc->x, loc->y, + ARGINT (1), ARGSTR (2), ARGSTR (3)); + + add_timer (gettick () + ARGINT (4), + &timer_callback_kill_npc, npc->bl.id, 0); + + return 0; +} + +static void entity_warp (entity_t * target, int destm, int destx, int desty) +{ + if (target->type == BL_PC || target->type == BL_MOB) + { + + switch (target->type) + { + case BL_PC: + { + character_t *character = (character_t *) target; + char *map_name; + clif_clearchar_area (&character->bl, 3); + map_delblock (&character->bl); + character->bl.x = destx; + character->bl.y = desty; + character->bl.m = destm; + + pc_touch_all_relevant_npcs (character); + + // Note that touching NPCs may have triggered warping and thereby updated x and y: + map_name = map[character->bl.m].name; + + // Warp part #1: update relevant data, interrupt trading etc.: + pc_setpos (character, map_name, character->bl.x, character->bl.y, 0); + // Warp part #2: now notify the client + clif_changemap (character, map_name, + character->bl.x, character->bl.y); + break; + } + case BL_MOB: + target->x = destx; + target->y = desty; + target->m = destm; + clif_fixmobpos ((struct mob_data *) target); + break; + } + } +} + +static int op_move (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + int dir = ARGDIR (1); + + int newx = subject->x + heading_x[dir]; + int newy = subject->y + heading_y[dir]; + + if (!map_is_solid (subject->m, newx, newy)) + entity_warp (subject, subject->m, newx, newy); + + return 0; +} + +static int op_warp (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + location_t *loc = &ARGLOCATION (1); + + entity_warp (subject, loc->m, loc->x, loc->y); + + return 0; +} + +static int op_banish (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + + if (subject->type == BL_MOB) + { + struct mob_data *mob = (struct mob_data *) subject; + + if (mob->mode & MOB_MODE_SUMMONED) + mob_catch_delete (mob, 3); + } + + return 0; +} + +static void +record_status_change (invocation_t * invocation, int bl_id, int sc_id) +{ + int index = invocation->status_change_refs_nr++; + status_change_ref_t *cr; + + RECREATE (invocation->status_change_refs, status_change_ref_t, invocation->status_change_refs_nr); + + cr = &invocation->status_change_refs[index]; + + cr->sc_type = sc_id; + cr->bl_id = bl_id; +} + +static int op_status_change (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + int invocation_id = VAR (VAR_INVOCATION).ty == TY_INVOCATION + ? VAR (VAR_INVOCATION).v.v_int : 0; + invocation_t *invocation = (invocation_t *) map_id2bl (invocation_id); + + skill_status_effect (subject, ARGINT (1), ARGINT (2), ARGINT (3), + ARGINT (4), ARGINT (5), ARGINT (6), 0, + invocation_id); + + if (invocation && subject->type == BL_PC) + record_status_change (invocation, subject->id, ARGINT (1)); + + return 0; +} + +static int op_stop_status_change (env_t * env, int args_nr, val_t * args) +{ + entity_t *subject = ARGENTITY (0); + + skill_status_change_end (subject, ARGINT (1), -1); + + return 0; +} + +static int op_override_attack (env_t * env, int args_nr, val_t * args) +{ + entity_t *psubject = ARGENTITY (0); + int charges = ARGINT (1); + int attack_delay = ARGINT (2); + int attack_range = ARGINT (3); + int icon = ARGINT (4); + int look = ARGINT (5); + int stopattack = ARGINT (6); + character_t *subject; + + if (psubject->type != BL_PC) + return 0; + + subject = (character_t *) psubject; + + if (subject->attack_spell_override) + { + invocation_t *old_invocation = + (invocation_t *) map_id2bl (subject->attack_spell_override); + if (old_invocation) + spell_free_invocation (old_invocation); + } + + subject->attack_spell_override = + trigger_spell (subject->bl.id, VAR (VAR_INVOCATION).v.v_int); + subject->attack_spell_charges = charges; + + if (subject->attack_spell_override) + { + invocation_t *attack_spell = + (invocation_t *) map_id2bl (subject->attack_spell_override); + if (attack_spell && stopattack) + attack_spell->flags |= INVOCATION_FLAG_STOPATTACK; + + char_set_weapon_icon (subject, charges, icon, look); + char_set_attack_info (subject, attack_delay, attack_range); + } + + return 0; +} + +static int op_create_item (env_t * env, int args_nr, val_t * args) +{ + struct item item; + entity_t *entity = ARGENTITY (0); + character_t *subject; + int stackable; + int count = ARGINT (2); + if (count <= 0) + return 0; + + if (entity->type == BL_PC) + subject = (character_t *) entity; + else + return 0; + + GET_ARG_ITEM (1, item, stackable); + + if (!stackable) + while (count--) + pc_additem (subject, &item, 1); + else + pc_additem (subject, &item, count); + + return 0; +} + +#define AGGRAVATION_MODE_ATTACKS_CASTER(n) ((n) == 0 || (n) == 2) +#define AGGRAVATION_MODE_MAKES_AGGRESSIVE(n) ((n) > 0) + +static int op_aggravate (env_t * env, int args_nr, val_t * args) +{ + entity_t *victim = ARGENTITY (2); + int mode = ARGINT (1); + entity_t *target = ARGENTITY (0); + struct mob_data *other; + + if (target->type == BL_MOB) + other = (struct mob_data *) target; + else + return 0; + + mob_target (other, victim, battle_get_range (victim)); + + if (AGGRAVATION_MODE_MAKES_AGGRESSIVE (mode)) + other->mode = 0x85 | (other->mode & MOB_SENSIBLE_MASK); /* war */ + + if (AGGRAVATION_MODE_ATTACKS_CASTER (mode)) + { + other->target_id = victim->id; + other->attacked_id = victim->id; + } + + return 0; +} + +#define MONSTER_ATTITUDE_HOSTILE 0 +#define MONSTER_ATTITUDE_FRIENDLY 1 +#define MONSTER_ATTITUDE_SERVANT 2 +#define MONSTER_ATTITUDE_FROZEN 3 + +static int op_spawn (env_t * env, int args_nr, val_t * args) +{ + area_t *area = ARGAREA (0); + entity_t *owner_e = ARGENTITY (1); + int monster_id = ARGINT (2); + int monster_attitude = ARGINT (3); + int monster_count = ARGINT (4); + int monster_lifetime = ARGINT (5); + int i; + + character_t *owner = (monster_attitude == MONSTER_ATTITUDE_SERVANT + && owner_e->type == + BL_PC) ? (character_t *) owner_e : NULL; + + for (i = 0; i < monster_count; i++) + { + location_t loc; + magic_random_location (&loc, area); + + int mob_id; + struct mob_data *mob; + + mob_id = mob_once_spawn (owner, map[loc.m].name, loc.x, loc.y, "--ja--", // Is that needed? + monster_id, 1, ""); + + mob = (struct mob_data *) map_id2bl (mob_id); + + if (mob) + { + mob->mode = mob_db[monster_id].mode; + + switch (monster_attitude) + { + + case MONSTER_ATTITUDE_SERVANT: + mob->state.special_mob_ai = 1; + mob->mode |= 0x04; + break; + + case MONSTER_ATTITUDE_FRIENDLY: + mob->mode = 0x80 | (mob->mode & 1); + break; + + case MONSTER_ATTITUDE_HOSTILE: + mob->mode = 0x84 | (mob->mode & 1); + if (owner) + { + mob->target_id = owner->bl.id; + mob->attacked_id = owner->bl.id; + } + break; + + case MONSTER_ATTITUDE_FROZEN: + mob->mode = 0; + break; + } + + mob->mode |= + MOB_MODE_SUMMONED | MOB_MODE_TURNS_AGAINST_BAD_MASTER; + + mob->deletetimer = add_timer (gettick () + monster_lifetime, + mob_timer_delete, mob_id, 0); + + if (owner) + { + mob->master_id = owner->bl.id; + mob->master_dist = 6; + } + } + } + + return 0; +} + +static char *get_invocation_name (env_t * env) +{ + invocation_t *invocation; + + if (VAR (VAR_INVOCATION).ty != TY_INVOCATION) + return "?"; + invocation = (invocation_t *) map_id2bl (VAR (VAR_INVOCATION).v.v_int); + + if (invocation) + return invocation->spell->name; + else + return "??"; +} + +static int op_injure (env_t * env, int args_nr, val_t * args) +{ + entity_t *caster = ARGENTITY (0); + entity_t *target = ARGENTITY (1); + int damage_caused = ARGINT (2); + int mp_damage = ARGINT (3); + int target_hp = battle_get_hp (target); + int mdef = battle_get_mdef (target); + + if (target->type == BL_PC && !map[target->m].flag.pvp && !((character_t *) target)->special_state.killable && (caster->type != BL_PC || !((character_t *) caster)->special_state.killer)) + return 0; /* Cannot damage other players outside of pvp */ + + if (target != caster) + { + /* Not protected against own spells */ + damage_caused = (damage_caused * (100 - mdef)) / 100; + mp_damage = (mp_damage * (100 - mdef)) / 100; + } + + damage_caused = (damage_caused > target_hp) ? target_hp : damage_caused; + + if (damage_caused < 0) + damage_caused = 0; + + // display damage first, because dealing damage may deallocate the target. + clif_damage (caster, target, gettick (), 0, 0, damage_caused, 0, 0, 0); + + if (caster->type == BL_PC) + { + character_t *caster_pc = (character_t *) caster; + if (target->type == BL_MOB) + { + struct mob_data *mob = (struct mob_data *) target; + + MAP_LOG_PC (caster_pc, "SPELLDMG MOB%d %d FOR %d BY %s", + mob->bl.id, mob->mob_class, damage_caused, + get_invocation_name (env)); + } + } + battle_damage (caster, target, damage_caused, mp_damage); + + return 0; +} + +static int op_emote (env_t * env, int args_nr, val_t * args) +{ + entity_t *victim = ARGENTITY (0); + int emotion = ARGINT (1); + clif_emotion (victim, emotion); + + return 0; +} + +static int op_set_script_variable (env_t * env, int args_nr, val_t * args) +{ + character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!c) + return 1; + + pc_setglobalreg (c, ARGSTR (1), ARGINT (2)); + + return 0; +} + +static int op_set_hair_colour (env_t * env, int args_nr, val_t * args) +{ + character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!c) + return 1; + + pc_changelook (c, LOOK_HAIR_COLOR, ARGINT (1)); + + return 0; +} + +static int op_set_hair_style (env_t * env, int args_nr, val_t * args) +{ + character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!c) + return 1; + + pc_changelook (c, LOOK_HAIR, ARGINT (1)); + + return 0; +} + +static int op_drop_item_for (env_t * env, int args_nr, val_t * args) +{ + struct item item; + int stackable; + location_t *loc = &ARGLOCATION (0); + int count = ARGINT (2); + int time = ARGINT (3); + character_t *c = ((args_nr > 4) && (ETY (4) == BL_PC)) ? ARGPC (4) : NULL; + int delay = (args_nr > 5) ? ARGINT (5) : 0; + int delaytime[3] = { delay, delay, delay }; + character_t *owners[3] = { c, NULL, NULL }; + + GET_ARG_ITEM (1, item, stackable); + + if (stackable) + map_addflooritem_any (&item, count, loc->m, loc->x, loc->y, + owners, delaytime, time, 0); + else + while (count-- > 0) + map_addflooritem_any (&item, 1, loc->m, loc->x, loc->y, + owners, delaytime, time, 0); + + return 0; +} + +static int op_gain_exp (env_t * env, int args_nr, val_t * args) +{ + character_t *c = (ETY (0) == BL_PC) ? ARGPC (0) : NULL; + + if (!c) + return 1; + + pc_gainexp_reason (c, ARGINT (1), ARGINT (2), ARGINT (3)); + return 0; +} + +static op_t operations[] = { + {"sfx", ".ii", op_sfx}, + {"instaheal", "eii", op_instaheal}, + {"itemheal", "eii", op_itemheal}, + {"shroud", "ei", op_shroud}, + {"unshroud", "e", op_reveal}, + {"message", "es", op_message}, + {"messenger_npc", "lissi", op_messenger_npc}, + {"move", "ed", op_move}, + {"warp", "el", op_warp}, + {"banish", "e", op_banish}, + {"status_change", "eiiiiii", op_status_change}, + {"stop_status_change", "ei", op_stop_status_change}, + {"override_attack", "eiiiiii", op_override_attack}, + {"create_item", "e.i", op_create_item}, + {"aggravate", "eie", op_aggravate}, + {"spawn", "aeiiii", op_spawn}, + {"injure", "eeii", op_injure}, + {"emote", "ei", op_emote}, + {"set_script_variable", "esi", op_set_script_variable}, + {"set_hair_colour", "ei", op_set_hair_colour}, + {"set_hair_style", "ei", op_set_hair_style}, + {"drop_item", "l.ii", op_drop_item_for}, + {"drop_item_for", "l.iiei", op_drop_item_for}, + {"gain_experience", "eiii", op_gain_exp}, + {NULL, NULL, NULL} +}; + +static int operations_sorted = 0; +static int operation_count; + +int compare_operations (const void *lhs, const void *rhs) +{ + return strcmp (((op_t *) lhs)->name, ((op_t *) rhs)->name); +} + +op_t *magic_get_op (char *name, int *index) +{ + op_t key; + + if (!operations_sorted) + { + op_t *opc = operations; + + while (opc->name) + ++opc; + + operation_count = opc - operations; + + qsort (operations, operation_count, sizeof (op_t), + compare_operations); + operations_sorted = 1; + } + + key.name = name; + op_t *op = (op_t *)bsearch (&key, operations, operation_count, sizeof (op_t), + compare_operations); + + if (op && index) + *index = op - operations; + + return op; +} + +void +spell_effect_report_termination (int invocation_id, int bl_id, int sc_id, + int supplanted) +{ + int i; + int index = -1; + invocation_t *invocation = (invocation_t *) map_id2bl (invocation_id); + + if (!invocation || invocation->bl.type != BL_SPELL) + return; + + for (i = 0; i < invocation->status_change_refs_nr; i++) + { + status_change_ref_t *cr = &invocation->status_change_refs[i]; + if (cr->sc_type == sc_id && cr->bl_id == bl_id) + { + index = i; + break; + } + } + + if (index == -1) + { + entity_t *entity = map_id2bl (bl_id); + if (entity->type == BL_PC) + fprintf (stderr, + "[magic] INTERNAL ERROR: spell-effect-report-termination: tried to terminate on unexpected bl %d, sc %d\n", + bl_id, sc_id); + return; + } + + if (index == invocation->status_change_refs_nr - 1) + invocation->status_change_refs_nr--; + else /* Copy last change ref to the one we are deleting */ + invocation->status_change_refs[index] = + invocation-> + status_change_refs[--invocation->status_change_refs_nr]; + + try_to_finish_invocation (invocation); +} + +static effect_t *return_to_stack (invocation_t * invocation) +{ + if (!invocation->stack_size) + return NULL; + else + { + cont_activation_record_t *ar = + invocation->stack + (invocation->stack_size - 1); + switch (ar->ty) + { + + case CONT_STACK_PROC: + { + effect_t *ret = ar->return_location; + int i; + + for (i = 0; i < ar->c.c_proc.args_nr; i++) + { + val_t *var = + &invocation->env->vars[ar->c.c_proc.formals[i]]; + magic_clear_var (var); + *var = ar->c.c_proc.old_actuals[i]; + } + + clear_activation_record (ar); + --invocation->stack_size; + + return ret; + } + + case CONT_STACK_FOREACH: + { + int entity_id; + val_t *var = &invocation->env->vars[ar->c.c_foreach.id]; + + do + { + if (ar->c.c_foreach.index >= ar->c.c_foreach.entities_nr) + { + effect_t *ret = ar->return_location; + clear_activation_record (ar); + --invocation->stack_size; + return ret; + } + + entity_id = + ar->c.c_foreach.entities[ar->c.c_foreach.index++]; + } + while (!entity_id || !map_id2bl (entity_id)); + + magic_clear_var (var); + var->ty = ar->c.c_foreach.ty; + var->v.v_int = entity_id; + + return ar->c.c_foreach.body; + } + + case CONT_STACK_FOR: + if (ar->c.c_for.current > ar->c.c_for.stop) + { + effect_t *ret = ar->return_location; + clear_activation_record (ar); + --invocation->stack_size; + return ret; + } + + magic_clear_var (&invocation->env->vars[ar->c.c_for.id]); + invocation->env->vars[ar->c.c_for.id].ty = TY_INT; + invocation->env->vars[ar->c.c_for.id].v.v_int = + ar->c.c_for.current++; + + return ar->c.c_for.body; + + default: + fprintf (stderr, + "[magic] INTERNAL ERROR: While executing spell `%s': stack corruption\n", + invocation->spell->name); + return NULL; + } + } +} + +static cont_activation_record_t *add_stack_entry (invocation_t * invocation, + int ty, + effect_t * return_location) +{ + cont_activation_record_t *ar = + invocation->stack + invocation->stack_size++; + if (invocation->stack_size >= MAX_STACK_SIZE) + { + fprintf (stderr, + "[magic] Execution stack size exceeded in spell `%s'; truncating effect\n", + invocation->spell->name); + invocation->stack_size--; + return NULL; + } + + ar->ty = ty; + ar->return_location = return_location; + return ar; +} + +static int find_entities_in_area_c (entity_t * target, va_list va) +{ + int *entities_allocd_p = va_arg (va, int *); + int *entities_nr_p = va_arg (va, int *); + int **entities_p = va_arg (va, int **); + int filter = va_arg (va, int); + +/* The following macro adds an entity to the result list: */ +#define ADD_ENTITY(e) \ + if (*entities_nr_p == *entities_allocd_p) { \ + /* Need more space */ \ + (*entities_allocd_p) += 32; \ + RECREATE (*entities_p, int, *entities_allocd_p); \ + } \ + (*entities_p)[(*entities_nr_p)++] = e; + + switch (target->type) + { + + case BL_PC: + if (filter == FOREACH_FILTER_PC + || filter == FOREACH_FILTER_ENTITY + || (filter == FOREACH_FILTER_TARGET + && map[target->m].flag.pvp)) + break; + else if (filter == FOREACH_FILTER_SPELL) + { /* Check all spells bound to the caster */ + invocation_t *invoc = ((character_t *) target)->active_spells; + /* Add all spells locked onto thie PC */ + + while (invoc) + { + ADD_ENTITY (invoc->bl.id); + invoc = invoc->next_invocation; + } + } + return 0; + + case BL_MOB: + if (filter == FOREACH_FILTER_MOB + || filter == FOREACH_FILTER_ENTITY + || filter == FOREACH_FILTER_TARGET) + break; + else + return 0; + + case BL_SPELL: + if (filter == FOREACH_FILTER_SPELL) + { + invocation_t *invocation = (invocation_t *) target; + + /* Check whether the spell is `bound'-- if so, we'll consider it iff we see the caster (case BL_PC). */ + if (invocation->flags & INVOCATION_FLAG_BOUND) + return 0; + else + break; /* Add the spell */ + } + else + return 0; + + case BL_NPC: + if (filter == FOREACH_FILTER_NPC) + break; + else + return 0; + + default: + return 0; + } + + ADD_ENTITY (target->id); +#undef ADD_ENTITY + return 0; +} + +static void +find_entities_in_area (area_t * area, int *entities_allocd_p, + int *entities_nr_p, int **entities_p, int filter) +{ + switch (area->ty) + { + case AREA_UNION: + find_entities_in_area (area->a.a_union[0], entities_allocd_p, + entities_nr_p, entities_p, filter); + find_entities_in_area (area->a.a_union[1], entities_allocd_p, + entities_nr_p, entities_p, filter); + break; + + default: + { + int m, x, y, width, height; + magic_area_rect (&m, &x, &y, &width, &height, area); + map_foreachinarea (find_entities_in_area_c, + m, x, y, x + width, y + height, + 0 /* filter elsewhere */ , + entities_allocd_p, entities_nr_p, entities_p, + filter); + } + } +} + +static effect_t *run_foreach (invocation_t * invocation, effect_t * foreach, + effect_t * return_location) +{ + val_t area; + int filter = foreach->e.e_foreach.filter; + int id = foreach->e.e_foreach.id; + effect_t *body = foreach->e.e_foreach.body; + + magic_eval (invocation->env, &area, foreach->e.e_foreach.area); + + if (area.ty != TY_AREA) + { + magic_clear_var (&area); + fprintf (stderr, + "[magic] Error in spell `%s': FOREACH loop over non-area\n", + invocation->spell->name); + return return_location; + } + else + { + cont_activation_record_t *ar = + add_stack_entry (invocation, CONT_STACK_FOREACH, return_location); + int entities_allocd = 64; + int *entities_collect; + int *entities; + int *shuffle_board; + int entities_nr = 0; + int i; + + if (!ar) + return return_location; + + CREATE (entities_collect, int, entities_allocd); + + find_entities_in_area (area.v.v_area, &entities_allocd, &entities_nr, + &entities_collect, filter); + + /* Now shuffle */ + CREATE (shuffle_board, int, entities_nr); + CREATE (entities, int, entities_nr); + for (i = 0; i < entities_nr; i++) + shuffle_board[i] = i; + + for (i = entities_nr - 1; i >= 0; i--) + { + int random_index = rand () % (i + 1); + entities[i] = entities_collect[shuffle_board[random_index]]; + shuffle_board[random_index] = shuffle_board[i]; // thus, we are guaranteed only to use unused indices + } + + free (entities_collect); + free (shuffle_board); + /* Done shuffling */ + + ar->c.c_foreach.id = id; + ar->c.c_foreach.body = body; + ar->c.c_foreach.index = 0; + ar->c.c_foreach.entities_nr = entities_nr; + ar->c.c_foreach.entities = entities; + ar->c.c_foreach.ty = + (filter == FOREACH_FILTER_SPELL) ? TY_INVOCATION : TY_ENTITY; + + magic_clear_var (&area); + + return return_to_stack (invocation); + } +} + +static effect_t *run_for (invocation_t * invocation, effect_t * for_, + effect_t * return_location) +{ + cont_activation_record_t *ar; + int id = for_->e.e_for.id; + val_t start; + val_t stop; + + magic_eval (invocation->env, &start, for_->e.e_for.start); + magic_eval (invocation->env, &stop, for_->e.e_for.stop); + + if (start.ty != TY_INT || stop.ty != TY_INT) + { + magic_clear_var (&start); + magic_clear_var (&stop); + fprintf (stderr, + "[magic] Error in spell `%s': FOR loop start or stop point is not an integer\n", + invocation->spell->name); + return return_location; + } + + ar = add_stack_entry (invocation, CONT_STACK_FOR, return_location); + + if (!ar) + return return_location; + + ar->c.c_for.id = id; + ar->c.c_for.current = start.v.v_int; + ar->c.c_for.stop = stop.v.v_int; + ar->c.c_for.body = for_->e.e_for.body; + + return return_to_stack (invocation); +} + +static effect_t *run_call (invocation_t * invocation, + effect_t * return_location) +{ + effect_t *current = invocation->current_effect; + cont_activation_record_t *ar; + int args_nr = current->e.e_call.args_nr; + int *formals = current->e.e_call.formals; + val_t *old_actuals; + CREATE (old_actuals, val_t, args_nr); + int i; + + ar = add_stack_entry (invocation, CONT_STACK_PROC, return_location); + ar->c.c_proc.args_nr = args_nr; + ar->c.c_proc.formals = formals; + ar->c.c_proc.old_actuals = old_actuals; + for (i = 0; i < args_nr; i++) + { + val_t *env_val = &invocation->env->vars[formals[i]]; + val_t result; + magic_copy_var (&old_actuals[i], env_val); + magic_eval (invocation->env, &result, current->e.e_call.actuals[i]); + *env_val = result; + } + + return current->e.e_call.body; +} + +#ifdef DEBUG +static void print_cfg (int i, effect_t * e) +{ + int j; + for (j = 0; j < i; j++) + printf (" "); + + printf ("%p: ", e); + + if (!e) + { + puts (" -- end --"); + return; + } + + switch (e->ty) + { + case EFFECT_SKIP: + puts ("SKIP"); + break; + case EFFECT_END: + puts ("END"); + break; + case EFFECT_ABORT: + puts ("ABORT"); + break; + case EFFECT_ASSIGN: + puts ("ASSIGN"); + break; + case EFFECT_FOREACH: + puts ("FOREACH"); + print_cfg (i + 1, e->e.e_foreach.body); + break; + case EFFECT_FOR: + puts ("FOR"); + print_cfg (i + 1, e->e.e_for.body); + break; + case EFFECT_IF: + puts ("IF"); + for (j = 0; j < i; j++) + printf (" "); + puts ("THEN"); + print_cfg (i + 1, e->e.e_if.true_branch); + for (j = 0; j < i; j++) + printf (" "); + puts ("ELSE"); + print_cfg (i + 1, e->e.e_if.false_branch); + break; + case EFFECT_SLEEP: + puts ("SLEEP"); + break; + case EFFECT_SCRIPT: + puts ("SCRIPT"); + break; + case EFFECT_BREAK: + puts ("BREAK"); + break; + case EFFECT_OP: + puts ("OP"); + break; + } + print_cfg (i, e->next); +} +#endif + +/** + * Execute a spell invocation until we abort, finish, or hit the next `sleep'. + * + * Use spell_execute() to automate handling of timers + * + * Returns: 0 if finished (all memory is freed implicitly) + * >1 if we hit `sleep'; the result is the number of ticks we should sleep for. + * -1 if we paused to wait for a user action (via script interaction) + */ +static int spell_run (invocation_t * invocation, int allow_delete) +{ + const int invocation_id = invocation->bl.id; +#define REFRESH_INVOCATION invocation = (invocation_t *) map_id2bl(invocation_id); if (!invocation) return 0; + +#ifdef DEBUG + fprintf (stderr, "Resuming execution: invocation of `%s'\n", + invocation->spell->name); + print_cfg (1, invocation->current_effect); +#endif + while (invocation->current_effect) + { + effect_t *e = invocation->current_effect; + effect_t *next = e->next; + int i; + +#ifdef DEBUG + fprintf (stderr, "Next step of type %d\n", e->ty); + dump_env (invocation->env); +#endif + + switch (e->ty) + { + case EFFECT_SKIP: + break; + + case EFFECT_ABORT: + invocation->flags |= INVOCATION_FLAG_ABORTED; + invocation->end_effect = NULL; + case EFFECT_END: + clear_stack (invocation); + next = NULL; + break; + + case EFFECT_ASSIGN: + magic_eval (invocation->env, + &invocation->env->vars[e->e.e_assign.id], + e->e.e_assign.expr); + break; + + case EFFECT_FOREACH: + next = run_foreach (invocation, e, next); + break; + + case EFFECT_FOR: + next = run_for (invocation, e, next); + break; + + case EFFECT_IF: + if (magic_eval_int (invocation->env, e->e.e_if.cond)) + next = e->e.e_if.true_branch; + else + next = e->e.e_if.false_branch; + break; + + case EFFECT_SLEEP: + { + int sleeptime = + magic_eval_int (invocation->env, e->e.e_sleep); + invocation->current_effect = next; + if (sleeptime > 0) + return sleeptime; + break; + } + + case EFFECT_SCRIPT: + { + character_t *caster = + (character_t *) map_id2bl (invocation->caster); + if (caster) + { + env_t *env = invocation->env; + character_t *caster = + (character_t *) map_id2bl (invocation->caster); + argrec_t arg[] = { {"@target",.v.i = + VAR (VAR_TARGET).ty == + TY_ENTITY ? 0 : VAR (VAR_TARGET). + v.v_int} + , + {"@caster",.v.i = invocation->caster} + , + {"@caster_name$",.v.s = caster ? caster->status.name : ""} + }; + int message_recipient = + VAR (VAR_SCRIPTTARGET).ty == + TY_ENTITY ? VAR (VAR_SCRIPTTARGET). + v.v_int : invocation->caster; + character_t *recipient = + (character_t *) map_id2bl (message_recipient); + + if (recipient->npc_id + && recipient->npc_id != invocation->bl.id) + break; /* Don't send multiple message boxes at once */ + + if (!invocation->script_pos) // first time running this script? + clif_spawn_fake_npc_for_player (recipient, + invocation->bl.id); + // We have to do this or otherwise the client won't think that it's + // dealing with an NPC + + int newpos = run_script_l (e->e.e_script, + invocation->script_pos, + message_recipient, + invocation->bl.id, + 3, arg); + /* Returns the new script position, or -1 once the script is finished */ + if (newpos != -1) + { + /* Must set up for continuation */ + recipient->npc_id = invocation->bl.id; + recipient->npc_pos = invocation->script_pos = newpos; + return -1; /* Signal `wait for script' */ + } + else + invocation->script_pos = 0; + clif_clearchar_id (invocation->bl.id, 1, caster->fd); + } + REFRESH_INVOCATION; // Script may have killed the caster + break; + } + + case EFFECT_BREAK: + next = return_to_stack (invocation); + break; + + case EFFECT_OP: + { + op_t *op = &operations[e->e.e_op.id]; + val_t args[MAX_ARGS]; + + for (i = 0; i < e->e.e_op.args_nr; i++) + magic_eval (invocation->env, &args[i], e->e.e_op.args[i]); + + if (!magic_signature_check ("effect", op->name, op->signature, + e->e.e_op.args_nr, args, + e->e.e_op.line_nr, + e->e.e_op.column)) + op->op (invocation->env, e->e.e_op.args_nr, args); + + for (i = 0; i < e->e.e_op.args_nr; i++) + magic_clear_var (&args[i]); + + REFRESH_INVOCATION; // Effect may have killed the caster + break; + } + + case EFFECT_CALL: + next = run_call (invocation, next); + break; + + default: + fprintf (stderr, + "[magic] INTERNAL ERROR: Unknown effect %d\n", + e->ty); + } + + if (!next) + next = return_to_stack (invocation); + + invocation->current_effect = next; + } + + if (allow_delete) + try_to_finish_invocation (invocation); + return 0; +#undef REFRESH_INVOCATION +} + +extern void spell_update_location (invocation_t * invocation); + +void spell_execute_d (invocation_t * invocation, int allow_deletion) +{ + int delta; + + spell_update_location (invocation); + delta = spell_run (invocation, allow_deletion); + + if (delta > 0) + { + if (invocation->timer) + { + fprintf (stderr, + "[magic] FATAL ERROR: Trying to add multiple timers to the same spell! Already had timer: %d\n", + invocation->timer); + /* *((int *)0x0) = 0; */ + } + invocation->timer = add_timer (gettick () + delta, + &invocation_timer_callback, + invocation->bl.id, 0); + } + + /* If 0, the script cleaned itself. If -1 (wait-for-script), we must wait for the user. */ +} + +void spell_execute (invocation_t * invocation) +{ + spell_execute_d (invocation, 1); +} + +void spell_execute_script (invocation_t * invocation) +{ + if (invocation->script_pos) + spell_execute_d (invocation, 1); + /* Otherwise the script-within-the-spell has been terminated by some other means. + * In practice this happens when the script doesn't wait for user input: the client + * may still notify the server that it's done. Without the above check, we'd be + * running the same spell twice! */ +} + +int spell_attack (int caster_id, int target_id) +{ + character_t *caster = (character_t *) map_id2bl (caster_id); + invocation_t *invocation; + int stop_attack = 0; + + if (!caster) + return 0; + + invocation = (invocation_t *) map_id2bl (caster->attack_spell_override); + + if (invocation && invocation->flags & INVOCATION_FLAG_STOPATTACK) + stop_attack = 1; + + if (invocation && caster->attack_spell_charges > 0) + { + magic_clear_var (&invocation->env->vars[VAR_TARGET]); + invocation->env->vars[VAR_TARGET].ty = TY_ENTITY; + invocation->env->vars[VAR_TARGET].v.v_int = target_id; + + invocation->current_effect = invocation->trigger_effect; + invocation->flags &= ~INVOCATION_FLAG_ABORTED; + spell_execute_d (invocation, + 0 /* don't delete the invocation if done */ ); + + // If the caster died, we need to refresh here: + invocation = + (invocation_t *) map_id2bl (caster->attack_spell_override); + + if (invocation && !(invocation->flags & INVOCATION_FLAG_ABORTED)) // If we didn't abort: + caster->attack_spell_charges--; + } + + if (invocation && caster->attack_spell_override != invocation->bl.id) + { + /* Attack spell changed / was refreshed */ + // spell_free_invocation(invocation); // [Fate] This would be a double free. + } + else if (!invocation || caster->attack_spell_charges <= 0) + { + caster->attack_spell_override = 0; + char_set_weapon_icon (caster, 0, 0, 0); + char_set_attack_info (caster, 0, 0); + + if (stop_attack) + pc_stopattack (caster); + + if (invocation) + spell_free_invocation (invocation); + } + + return 1; +} diff --git a/src/map/magic.c b/src/map/magic.c deleted file mode 100644 index 797dc16..0000000 --- a/src/map/magic.c +++ /dev/null @@ -1,130 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "magic-interpreter.h" - -#undef DEBUG - -static char *magic_preprocess_message (character_t * character, char *start, - char *end) -{ - if (character->state.shroud_active - && character->state.shroud_disappears_on_talk) - magic_unshroud (character); - - if (character->state.shroud_active - && character->state.shroud_hides_name_talking) - { - int len = strlen (end); - strcpy (start, "? "); - memmove (start + 2, end, len + 1); - return start + 4; - } - else - return end + 2; /* step past blank */ -} - -#define ISBLANK(c) ((c) == ' ') - -/* Returns a dynamically allocated copy of `src'. - * `*parameter' may point within that copy or be NULL. */ -static char *magic_tokenise (char *src, char **parameter) -{ - char *retval = strdup (src); - char *seeker = retval; - - while (*seeker && !ISBLANK (*seeker)) - ++seeker; - - if (!*seeker) - *parameter = NULL; - else - { - *seeker = 0; /* Terminate invocation */ - ++seeker; - - while (ISBLANK (*seeker)) - ++seeker; - - *parameter = seeker; - } - - return retval; -} - -int magic_message (character_t * caster, char *spell_, size_t spell_len) -{ - if (pc_isdead (caster)) - return 0; - - int power = caster->matk1; - char *invocation_base = spell_ + 8; - char *source_invocation = - 1 + invocation_base + strlen (caster->status.name); - spell_t *spell; - char *parameter; - char *spell_invocation; - - if (!source_invocation) - return 0; - - /* Pre-message filter in case some spell alters output */ - source_invocation = - magic_preprocess_message (caster, invocation_base, source_invocation); - - spell_invocation = magic_tokenise (source_invocation, ¶meter); - parameter = parameter ? strdup (parameter) : strdup (""); - - spell = magic_find_spell (spell_invocation); - free (spell_invocation); - - if (spell) - { - int near_miss; - env_t *env = - spell_create_env (&magic_conf, spell, caster, power, parameter); - effect_set_t *effects; - - if ((spell->flags & SPELL_FLAG_NONMAGIC) || (power >= 1)) - effects = spell_trigger (spell, caster, env, &near_miss); - else - effects = NULL; - -#ifdef DEBUG - fprintf (stderr, "Found spell `%s', triggered = %d\n", spell_, - effects != NULL); -#endif - if (caster->status.option & OPTION_HIDE) - return 0; // No spellcasting while hidden - - MAP_LOG_PC (caster, "CAST %s %s", - spell->name, effects ? "SUCCESS" : "FAILURE"); - - if (effects) - { - invocation_t *invocation = spell_instantiate (effects, env); - - spell_bind (caster, invocation); - spell_execute (invocation); - - return (spell->flags & SPELL_FLAG_SILENT) ? -1 : 1; - } - else - magic_free_env (env); - - return 1; - } - else - free (parameter); - - return 0; /* Not a spell */ -} - -int magic_init (char *conffile); // must be called after itemdb initialisation - -void do_init_magic (void) -{ - magic_init (MAGIC_CONFIG_FILE); -} diff --git a/src/map/magic.cpp b/src/map/magic.cpp new file mode 100644 index 0000000..03b18a4 --- /dev/null +++ b/src/map/magic.cpp @@ -0,0 +1,130 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "magic-interpreter.hpp" + +#undef DEBUG + +static char *magic_preprocess_message (character_t * character, char *start, + char *end) +{ + if (character->state.shroud_active + && character->state.shroud_disappears_on_talk) + magic_unshroud (character); + + if (character->state.shroud_active + && character->state.shroud_hides_name_talking) + { + int len = strlen (end); + strcpy (start, "? "); + memmove (start + 2, end, len + 1); + return start + 4; + } + else + return end + 2; /* step past blank */ +} + +#define ISBLANK(c) ((c) == ' ') + +/* Returns a dynamically allocated copy of `src'. + * `*parameter' may point within that copy or be NULL. */ +static char *magic_tokenise (char *src, char **parameter) +{ + char *retval = strdup (src); + char *seeker = retval; + + while (*seeker && !ISBLANK (*seeker)) + ++seeker; + + if (!*seeker) + *parameter = NULL; + else + { + *seeker = 0; /* Terminate invocation */ + ++seeker; + + while (ISBLANK (*seeker)) + ++seeker; + + *parameter = seeker; + } + + return retval; +} + +int magic_message (character_t * caster, char *spell_, size_t spell_len) +{ + if (pc_isdead (caster)) + return 0; + + int power = caster->matk1; + char *invocation_base = spell_ + 8; + char *source_invocation = + 1 + invocation_base + strlen (caster->status.name); + spell_t *spell; + char *parameter; + char *spell_invocation; + + if (!source_invocation) + return 0; + + /* Pre-message filter in case some spell alters output */ + source_invocation = + magic_preprocess_message (caster, invocation_base, source_invocation); + + spell_invocation = magic_tokenise (source_invocation, ¶meter); + parameter = parameter ? strdup (parameter) : strdup (""); + + spell = magic_find_spell (spell_invocation); + free (spell_invocation); + + if (spell) + { + int near_miss; + env_t *env = + spell_create_env (&magic_conf, spell, caster, power, parameter); + effect_set_t *effects; + + if ((spell->flags & SPELL_FLAG_NONMAGIC) || (power >= 1)) + effects = spell_trigger (spell, caster, env, &near_miss); + else + effects = NULL; + +#ifdef DEBUG + fprintf (stderr, "Found spell `%s', triggered = %d\n", spell_, + effects != NULL); +#endif + if (caster->status.option & OPTION_HIDE) + return 0; // No spellcasting while hidden + + MAP_LOG_PC (caster, "CAST %s %s", + spell->name, effects ? "SUCCESS" : "FAILURE"); + + if (effects) + { + invocation_t *invocation = spell_instantiate (effects, env); + + spell_bind (caster, invocation); + spell_execute (invocation); + + return (spell->flags & SPELL_FLAG_SILENT) ? -1 : 1; + } + else + magic_free_env (env); + + return 1; + } + else + free (parameter); + + return 0; /* Not a spell */ +} + +int magic_init (char *conffile); // must be called after itemdb initialisation + +void do_init_magic (void) +{ + magic_init (MAGIC_CONFIG_FILE); +} diff --git a/src/map/magic.h b/src/map/magic.h deleted file mode 100644 index 1979914..0000000 --- a/src/map/magic.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef MAGIC_H_ -#define MAGIC_H_ - -#include "clif.h" -#include "intif.h" - -#define MAGIC_CONFIG_FILE "conf/magic.conf" - -typedef struct map_session_data character_t; -typedef struct block_list entity_t; - -struct invocation; /* Spell invocation */ - -/** - * Try to cast magic. - * - * As an intended side effect, the magic message may be distorted (text only). - * - * \param caster Player attempting to cast magic - * \param spell The prospective incantation - * \param spell_len Number of characters in the incantation - * \return 1 or -1 if the input message was magic and was handled by this function, 0 otherwise. -1 is returned when the - * message should not be repeated. - */ -int magic_message (character_t * caster, char *spell, size_t spell_len); - -/** - * Removes the shroud from a character - * - * \param character The character to remove the shroud from - */ -void magic_unshroud (character_t * character); - -/** - * Notifies a running spell that a status_change timer triggered by the spell has expired - * - * \param invocation The invocation to notify - * \param bl_id ID of the PC for whom this happened - * \param type sc_id ID of the status change entry that finished - * \param supplanted Whether the status_change finished normally (0) or was supplanted by a new status_change (1) - */ -void -spell_effect_report_termination (int invocation, int bl_id, int sc_id, - int supplanted); - -/** - * Initialise all spells, read config data - */ -void do_init_magic (void); - -/** - * Identifies the invocation used to trigger a spell - * - * Returns NULL if not found - */ -char *magic_find_invocation (char *spellame); - -/** - * Identifies the invocation used to denote a teleport location - * - * Returns NULL if not found - */ -char *magic_find_anchor_invocation (char *teleport_location); - -/** - * Execute a spell invocation and sets up timers to finish - */ -void spell_execute (struct invocation *invocation); - -/** - * Continue an NPC script embedded in a spell - */ -void spell_execute_script (struct invocation *invocation); - -/** - * Stops all magic bound to the specified character - * - */ -void magic_stop_completely (character_t * c); - -/** - * Attacks with a magical spell charged to the character - * - * Returns 0 if there is no charged spell or the spell is depleted. - */ -int spell_attack (int caster, int target); - -void spell_free_invocation (struct invocation *invocation); - -#endif /* !defined(MAGIC_H_) */ diff --git a/src/map/magic.hpp b/src/map/magic.hpp new file mode 100644 index 0000000..0c776ea --- /dev/null +++ b/src/map/magic.hpp @@ -0,0 +1,90 @@ +#ifndef MAGIC_HPP +#define MAGIC_HPP + +#include "clif.hpp" +#include "intif.hpp" + +#define MAGIC_CONFIG_FILE "conf/magic.conf" + +typedef struct map_session_data character_t; +typedef struct block_list entity_t; + +struct invocation; /* Spell invocation */ + +/** + * Try to cast magic. + * + * As an intended side effect, the magic message may be distorted (text only). + * + * \param caster Player attempting to cast magic + * \param spell The prospective incantation + * \param spell_len Number of characters in the incantation + * \return 1 or -1 if the input message was magic and was handled by this function, 0 otherwise. -1 is returned when the + * message should not be repeated. + */ +int magic_message (character_t * caster, char *spell, size_t spell_len); + +/** + * Removes the shroud from a character + * + * \param character The character to remove the shroud from + */ +void magic_unshroud (character_t * character); + +/** + * Notifies a running spell that a status_change timer triggered by the spell has expired + * + * \param invocation The invocation to notify + * \param bl_id ID of the PC for whom this happened + * \param type sc_id ID of the status change entry that finished + * \param supplanted Whether the status_change finished normally (0) or was supplanted by a new status_change (1) + */ +void +spell_effect_report_termination (int invocation, int bl_id, int sc_id, + int supplanted); + +/** + * Initialise all spells, read config data + */ +void do_init_magic (void); + +/** + * Identifies the invocation used to trigger a spell + * + * Returns NULL if not found + */ +char *magic_find_invocation (char *spellame); + +/** + * Identifies the invocation used to denote a teleport location + * + * Returns NULL if not found + */ +char *magic_find_anchor_invocation (char *teleport_location); + +/** + * Execute a spell invocation and sets up timers to finish + */ +void spell_execute (struct invocation *invocation); + +/** + * Continue an NPC script embedded in a spell + */ +void spell_execute_script (struct invocation *invocation); + +/** + * Stops all magic bound to the specified character + * + */ +void magic_stop_completely (character_t * c); + +/** + * Attacks with a magical spell charged to the character + * + * Returns 0 if there is no charged spell or the spell is depleted. + */ +int spell_attack (int caster, int target); + +void spell_free_invocation (struct invocation *invocation); + +#endif /* !defined(MAGIC_H_) */ diff --git a/src/map/map.c b/src/map/map.c deleted file mode 100644 index d840486..0000000 --- a/src/map/map.c +++ /dev/null @@ -1,2215 +0,0 @@ -// $Id: map.c,v 1.6 2004/09/25 17:37:01 MouseJstr Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#ifdef LCCWIN32 -#include <winsock.h> -#else -#include <netdb.h> -#endif - -#include "../common/core.h" -#include "../common/timer.h" -#include "../common/db.h" -#include "../common/grfio.h" -#include "../common/mt_rand.h" -#include "map.h" -#include "chrif.h" -#include "clif.h" -#include "intif.h" -#include "npc.h" -#include "pc.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 "atcommand.h" -#include "../common/nullpo.h" -#include "../common/socket.h" -#include "magic.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -// 極力 staticでローカルに収める -static struct dbt *id_db = NULL; -static struct dbt *map_db = NULL; -static struct dbt *nick_db = NULL; -static struct dbt *charid_db = NULL; - -static int users = 0; -static struct block_list *object[MAX_FLOORITEM]; -static int first_free_object_id = 0, last_object_id = 0; - -#define block_free_max 1048576 -static void *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; - -struct map_data map[MAX_MAP_PER_SERVER]; -int map_num = 0; - -int map_port = 0; - -int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int save_settings = 0xFFFF; -int agit_flag = 0; -int night_flag = 0; // 0=day, 1=night [Yor] - -struct charid2nick -{ - char nick[24]; - int req_id; -}; - -char motd_txt[256] = "conf/motd.txt"; -char help_txt[256] = "conf/help.txt"; - -char wisp_server_name[24] = "Server"; // can be modified in char-server configuration file - -/*========================================== - * 全map鯖総計での接続数設定 - * (char鯖から送られてくる) - *------------------------------------------ - */ -void map_setusers (int n) -{ - users = n; -} - -/*========================================== - * 全map鯖総計での接続数取得 (/wへの応答用) - *------------------------------------------ - */ -int map_getusers (void) -{ - return users; -} - -// -// block削除の安全性確保処理 -// - -/*========================================== - * blockをfreeするときfreeの変わりに呼ぶ - * ロックされているときはバッファにためる - *------------------------------------------ - */ -int map_freeblock (void *bl) -{ - if (block_free_lock == 0) - { - free (bl); - bl = NULL; - } - else - { - if (block_free_count >= block_free_max) - { - if (battle_config.error_log) - printf - ("map_freeblock: *WARNING* 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を一時的に禁止する - *------------------------------------------ - */ -int map_freeblock_lock (void) -{ - return ++block_free_lock; -} - -/*========================================== - * blockのfreeのロックを解除する - * このとき、ロックが完全になくなると - * バッファにたまっていたblockを全部削除 - *------------------------------------------ - */ -int map_freeblock_unlock (void) -{ - if ((--block_free_lock) == 0) - { - int i; -// if(block_free_count>0) { -// if(battle_config.error_log) -// printf("map_freeblock_unlock: free %d object\n",block_free_count); -// } - for (i = 0; i < block_free_count; i++) - { - free (block_free[i]); - block_free[i] = NULL; - } - block_free_count = 0; - } - else if (block_free_lock < 0) - { - if (battle_config.error_log) - printf ("map_freeblock_unlock: lock count < 0 !\n"); - } - return block_free_lock; -} - -// -// block化処理 -// -/*========================================== - * map[]のblock_listから繋がっている場合に - * bl->prevにbl_headのアドレスを入れておく - *------------------------------------------ - */ -static struct block_list bl_head; - -/*========================================== - * map[]のblock_listに追加 - * mobは数が多いので別リスト - * - * 既にlink済みかの確認が無い。危険かも - *------------------------------------------ - */ -int map_addblock (struct block_list *bl) -{ - int m, x, y; - - nullpo_retr (0, bl); - - if (bl->prev != NULL) - { - if (battle_config.error_log) - printf ("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; - - if (bl->type == BL_MOB) - { - bl->next = - map[m].block_mob[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]; - bl->prev = &bl_head; - if (bl->next) - bl->next->prev = bl; - map[m].block_mob[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs] = bl; - map[m].block_mob_count[x / BLOCK_SIZE + - (y / BLOCK_SIZE) * map[m].bxs]++; - } - else - { - bl->next = - map[m].block[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]; - bl->prev = &bl_head; - if (bl->next) - bl->next->prev = bl; - map[m].block[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs] = bl; - map[m].block_count[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]++; - if (bl->type == BL_PC) - map[m].users++; - } - - return 0; -} - -/*========================================== - * map[]のblock_listから外す - * prevがNULLの場合listに繋がってない - *------------------------------------------ - */ -int map_delblock (struct block_list *bl) -{ - int b; - nullpo_retr (0, bl); - - // 既にblocklistから抜けている - if (bl->prev == NULL) - { - if (bl->next != NULL) - { - // prevがNULLでnextがNULLでないのは有ってはならない - if (battle_config.error_log) - printf ("map_delblock error : bl->next!=NULL\n"); - } - return 0; - } - - b = bl->x / BLOCK_SIZE + (bl->y / BLOCK_SIZE) * map[bl->m].bxs; - - if (bl->type == BL_PC) - map[bl->m].users--; - - 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; -} - -/*========================================== - * 周囲のPC人数を数える (現在未使用) - *------------------------------------------ - */ -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]; - for (; bl; bl = bl->next) - { - if (bl->type == BL_PC) - c++; - } - } - } - return c; -} - -/*========================================== - * セル上のPCとMOBの数を数える (グランドクロス用) - *------------------------------------------ - */ -int map_count_oncell (int m, int x, int y) -{ - 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 1; - 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_PC) - count++; - } - 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++; - } - if (!count) - count = 1; - return count; -} - -/*========================================== - * map m (x0,y0)-(x1,y1)内の全objに対して - * funcを呼ぶ - * type!=0 ならその種類のみ - *------------------------------------------ - */ -void map_foreachinarea (int (*func) (struct block_list *, va_list), int m, - int x0, int y0, int x1, int y1, int type, ...) -{ - int bx, by; - struct block_list *bl = NULL; - va_list ap = NULL; - int blockcount = bl_list_count, i, c; - - if (m < 0) - return; - va_start (ap, type); - 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 == 0 || 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 && type && bl->type != type) - continue; - 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 (type == 0 || 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) - printf ("map_foreachinarea: *WARNING* block count too many!\n"); - } - - map_freeblock_lock (); // メモリからの解放を禁止する - - for (i = blockcount; i < bl_list_count; i++) - if (bl_list[i]->prev) // 有効かどうかチェック - func (bl_list[i], ap); - - map_freeblock_unlock (); // 解放を許可する - - va_end (ap); - bl_list_count = blockcount; -} - -/*========================================== - * 矩形(x0,y0)-(x1,y1)が(dx,dy)移動した時の - * 領域外になる領域(矩形かL字形)内のobjに - * 対してfuncを呼ぶ - * - * dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?) - *------------------------------------------ - */ -void 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; - struct block_list *bl = NULL; - va_list ap = NULL; - int blockcount = bl_list_count, i, c; - - va_start (ap, type); - 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++) - { - 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 && type && bl->type != type) - continue; - 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; - } - 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 && type && bl->type != type) - continue; - 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++) - { - 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 && type && bl->type != type) - continue; - 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; - } - 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 && type && bl->type != type) - continue; - 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) - printf ("map_foreachinarea: *WARNING* block count too many!\n"); - } - - map_freeblock_lock (); // メモリからの解放を禁止する - - for (i = blockcount; i < bl_list_count; i++) - if (bl_list[i]->prev) // 有効かどうかチェック - func (bl_list[i], ap); - - map_freeblock_unlock (); // 解放を許可する - - va_end (ap); - bl_list_count = blockcount; -} - -// -- 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) -// -void map_foreachincell (int (*func) (struct block_list *, va_list), int m, - int x, int y, int type, ...) -{ - int bx, by; - struct block_list *bl = NULL; - va_list ap = NULL; - int blockcount = bl_list_count, i, c; - - va_start (ap, type); - - by = y / BLOCK_SIZE; - bx = x / 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 (type && bl && bl->type != type) - continue; - if (bl && bl->x == x && bl->y == y && bl_list_count < BL_LIST_MAX) - bl_list[bl_list_count++] = bl; - } - } - - 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 && 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) - printf ("map_foreachincell: *WARNING* block count too many!\n"); - } - - map_freeblock_lock (); // メモリからの解放を禁止する - - for (i = blockcount; i < bl_list_count; i++) - if (bl_list[i]->prev) // 有効かどうかチェック - func (bl_list[i], ap); - - map_freeblock_unlock (); // 解放を許可する - - va_end (ap); - bl_list_count = blockcount; -} - -/*========================================== - * 床アイテムやエフェクト用の一時obj割り当て - * object[]への保存とid_db登録まで - * - * bl->idもこの中で設定して問題無い? - *------------------------------------------ - */ -int map_addobject (struct block_list *bl) -{ - int i; - if (bl == NULL) - { - printf ("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; i++) - if (object[i] == NULL) - break; - if (i >= MAX_FLOORITEM) - { - if (battle_config.error_log) - printf ("no free object id\n"); - return 0; - } - first_free_object_id = i; - if (last_object_id < i) - last_object_id = i; - object[i] = bl; - numdb_insert (id_db, i, bl); - return i; -} - -/*========================================== - * 一時objectの解放 - * map_delobjectのfreeしないバージョン - *------------------------------------------ - */ -int map_delobjectnofree (int id, int type) -{ - if (object[id] == NULL) - return 0; - - if (object[id]->type != type) - { - fprintf (stderr, "Incorrect type: expected %d, got %d\n", type, - object[id]->type); - *((char *) 0) = 0; // break for backtrace - } - - map_delblock (object[id]); - numdb_erase (id_db, id); -// map_freeblock(object[id]); - object[id] = NULL; - - if (first_free_object_id > id) - first_free_object_id = id; - - while (last_object_id > 2 && object[last_object_id] == NULL) - last_object_id--; - - return 0; -} - -/*========================================== - * 一時objectの解放 - * block_listからの削除、id_dbからの削除 - * object dataのfree、object[]へのNULL代入 - * - * addとの対称性が無いのが気になる - *------------------------------------------ - */ -int map_delobject (int id, int type) -{ - struct block_list *obj = object[id]; - - if (obj == NULL) - return 0; - - map_delobjectnofree (id, type); - if (obj->type == BL_PC) // [Fate] Not sure where else to put this... I'm not sure where delobject for PCs is called from - pc_cleanup ((struct map_session_data *) obj); - - map_freeblock (obj); - - return 0; -} - -/*========================================== - * 全一時obj相手にfuncを呼ぶ - * - *------------------------------------------ - */ -void map_foreachobject (int (*func) (struct block_list *, va_list), int type, - ...) -{ - int i; - int blockcount = bl_list_count; - va_list ap = NULL; - - va_start (ap, type); - - for (i = 2; i <= last_object_id; i++) - { - if (object[i]) - { - if (type && object[i]->type != type) - continue; - if (bl_list_count >= BL_LIST_MAX) - { - if (battle_config.error_log) - printf ("map_foreachobject: too many block !\n"); - } - else - bl_list[bl_list_count++] = object[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してある - *------------------------------------------ - */ -void map_clearflooritem_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct flooritem_data *fitem = NULL; - - fitem = (struct flooritem_data *) object[id]; - if (fitem == NULL || fitem->bl.type != BL_ITEM - || (!data && fitem->cleartimer != tid)) - { - if (battle_config.error_log) - printf ("map_clearflooritem_timer : error\n"); - return; - } - if (data) - delete_timer (fitem->cleartimer, map_clearflooritem_timer); - clif_clearflooritem (fitem, 0); - map_delobject (fitem->bl.id, BL_ITEM); -} - -/*========================================== - * (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, c; - - 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 ((c = read_gat (m, j + x, i + y)) == 1 || c == 5) - continue; - free_cell++; - } - } - if (free_cell == 0) - return -1; - free_cell = MRAND (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 ((c = read_gat (m, j + x, i + y)) == 1 || c == 5) - 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_any (struct item *item_data, int amount, int m, int x, - int y, struct map_session_data **owners, - int *owner_protection, int lifetime, int dispersal) -{ - int xy, r; - unsigned int tick; - struct flooritem_data *fitem = NULL; - - nullpo_retr (0, item_data); - - if ((xy = map_searchrandfreecell (m, x, y, dispersal)) < 0) - return 0; - r = mt_random (); - - CREATE (fitem, struct flooritem_data, 1); - 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->first_get_id = 0; - fitem->first_get_tick = 0; - fitem->second_get_id = 0; - fitem->second_get_tick = 0; - fitem->third_get_id = 0; - fitem->third_get_tick = 0; - - fitem->bl.id = map_addobject (&fitem->bl); - if (fitem->bl.id == 0) - { - free (fitem); - return 0; - } - - tick = gettick (); - - if (owners[0]) - fitem->first_get_id = owners[0]->bl.id; - fitem->first_get_tick = tick + owner_protection[0]; - - if (owners[1]) - fitem->second_get_id = owners[1]->bl.id; - fitem->second_get_tick = tick + owner_protection[1]; - - if (owners[2]) - fitem->third_get_id = owners[2]->bl.id; - fitem->third_get_tick = tick + owner_protection[2]; - - 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 () + lifetime, map_clearflooritem_timer, - fitem->bl.id, 0); - - map_addblock (&fitem->bl); - clif_dropflooritem (fitem); - - return fitem->bl.id; -} - -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) -{ - struct map_session_data *owners[3] = { first_sd, second_sd, third_sd }; - int owner_protection[3]; - - if (type) - { - owner_protection[0] = battle_config.mvp_item_first_get_time; - owner_protection[1] = - owner_protection[0] + battle_config.mvp_item_second_get_time; - owner_protection[2] = - owner_protection[1] + battle_config.mvp_item_third_get_time; - } - else - { - owner_protection[0] = battle_config.item_first_get_time; - owner_protection[1] = - owner_protection[0] + battle_config.item_second_get_time; - owner_protection[2] = - owner_protection[1] + battle_config.item_third_get_time; - } - - return map_addflooritem_any (item_data, amount, m, x, y, - owners, owner_protection, - battle_config.flooritem_lifetime, 1); -} - -/* 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->first_get_id = 0; */ -/* fitem->first_get_tick = 0; */ -/* fitem->second_get_id = 0; */ -/* fitem->second_get_tick = 0; */ -/* fitem->third_get_id = 0; */ -/* fitem->third_get_tick = 0; */ - -/* fitem->bl.id = map_addobject(&fitem->bl); */ -/* if(fitem->bl.id==0){ */ -/* free(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; */ -/* } */ - -/*========================================== - * charid_dbへ追加(返信待ちがあれば返信) - *------------------------------------------ - */ -void map_addchariddb (int charid, char *name) -{ - struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, charid); - if (p == NULL) - { // データベースにない - CREATE (p, struct charid2nick, 1); - p->req_id = 0; - } - else - numdb_erase (charid_db, charid); - - int req = p->req_id; - memcpy (p->nick, name, 24); - p->req_id = 0; - numdb_insert (charid_db, charid, p); - if (req) - { // 返信待ちがあれば返信 - struct map_session_data *sd = map_id2sd (req); - if (sd != NULL) - clif_solved_charname (sd, charid); - } -} - -/*========================================== - * charid_dbへ追加(返信要求のみ) - *------------------------------------------ - */ -int map_reqchariddb (struct map_session_data *sd, int charid) -{ - nullpo_retr (0, sd); - - struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, charid); - if (p != NULL) // データベースにすでにある - return 0; - CREATE (p, struct charid2nick, 1); - p->req_id = sd->bl.id; - numdb_insert (charid_db, charid, p); - return 0; -} - -/*========================================== - * id_dbへblを追加 - *------------------------------------------ - */ -void map_addiddb (struct block_list *bl) -{ - nullpo_retv (bl); - - numdb_insert (id_db, bl->id, bl); -} - -/*========================================== - * id_dbからblを削除 - *------------------------------------------ - */ -void map_deliddb (struct block_list *bl) -{ - nullpo_retv (bl); - - numdb_erase (id_db, bl->id); -} - -/*========================================== - * nick_dbへsdを追加 - *------------------------------------------ - */ -void map_addnickdb (struct map_session_data *sd) -{ - nullpo_retv (sd); - - strdb_insert (nick_db, sd->status.name, sd); -} - -/*========================================== - * PCのquit処理 map.c内分 - * - * quit処理の主体が違うような気もしてきた - *------------------------------------------ - */ -int map_quit (struct map_session_data *sd) -{ - nullpo_retr (0, sd); - - 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); - - party_send_logout (sd); // パーティのログアウトメッセージ送信 - - guild_send_memberinfoshort (sd, 0); // ギルドのログアウトメッセージ送信 - - pc_cleareventtimer (sd); // イベントタイマを破棄する - - skill_castcancel (&sd->bl, 0); // 詠唱を中断する - skill_stop_dancing (&sd->bl, 1); // ダンス/演奏中断 - - if (sd->sc_data && sd->sc_data[SC_BERSERK].timer != -1) //バーサーク中の終了はHPを100に - sd->status.hp = 100; - - skill_status_change_clear (&sd->bl, 1); // ステータス異常を解除する - skill_clear_unitgroup (&sd->bl); // スキルユニットグループの削除 - skill_cleartimerskill (&sd->bl); - pc_stop_walking (sd, 0); - pc_stopattack (sd); - pc_delinvincibletimer (sd); - pc_delspiritball (sd, sd->spiritball, 1); - skill_gangsterparadise (sd, 0); - - pc_calcstatus (sd, 4); - - clif_clearchar_area (&sd->bl, 2); - - if (pc_isdead (sd)) - pc_setrestartvalue (sd, 2); - pc_makesavestatus (sd); - //クローンスキルで覚えたスキルは消す - - //The storage closing routines will save the char if needed. [Skotlex] - if (!sd->state.storage_flag) - chrif_save (sd); - else if (sd->state.storage_flag == 1) - storage_storage_quit (sd); - else if (sd->state.storage_flag == 2) - storage_guild_storage_quit (sd, 1); - - if (sd->npc_stackbuf && sd->npc_stackbuf != NULL) - free (sd->npc_stackbuf); - - map_delblock (&sd->bl); - - numdb_erase (id_db, sd->bl.id); - strdb_erase (nick_db, sd->status.name); - numdb_erase (charid_db, sd->status.char_id); - - return 0; -} - -/*========================================== - * id番号のPCを探す。居なければNULL - *------------------------------------------ - */ -struct map_session_data *map_id2sd (int id) -{ -// remove search from db, because: -// 1 - all players, npc, items and mob are in this db (to search, it's not speed, and search in session is more sure) -// 2 - DB seems not always correct. Sometimes, when a player disconnects, its id (account value) is not removed and structure -// point to a memory area that is not more a session_data and value are incorrect (or out of available memory) -> crash -// replaced by searching in all session. -// by searching in session, we are sure that fd, session, and account exist. -/* - struct block_list *bl; - - bl=numdb_search(id_db,id); - if(bl && bl->type==BL_PC) - return (struct map_session_data*)bl; - return NULL; -*/ - int i; - struct map_session_data *sd = NULL; - - for (i = 0; i < fd_max; i++) - if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->bl.id == id) - return sd; - - return NULL; -} - -/*========================================== - * char_id番号の名前を探す - *------------------------------------------ - */ -char *map_charid2nick (int id) -{ - struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, id); - - if (p == NULL) - return NULL; - if (p->req_id != 0) - return NULL; - return p->nick; -} - -/*========================================*/ -/* [Fate] Operations to iterate over active map sessions */ - -static struct map_session_data *map_get_session (int i) -{ - struct map_session_data *d; - - if (i >= 0 && i < fd_max - && session[i] && (d = (struct map_session_data *)session[i]->session_data) && d->state.auth) - return d; - - return NULL; -} - -static struct map_session_data *map_get_session_forward (int start) -{ - int i; - for (i = start; i < fd_max; i++) - { - struct map_session_data *d = map_get_session (i); - if (d) - return d; - } - - return NULL; -} - -static struct map_session_data *map_get_session_backward (int start) -{ - int i; - for (i = start; i >= 0; i--) - { - struct map_session_data *d = map_get_session (i); - if (d) - return d; - } - - return NULL; -} - -struct map_session_data *map_get_first_session (void) -{ - return map_get_session_forward (0); -} - -struct map_session_data *map_get_next_session (struct map_session_data *d) -{ - return map_get_session_forward (d->fd + 1); -} - -struct map_session_data *map_get_last_session (void) -{ - return map_get_session_backward (fd_max); -} - -struct map_session_data *map_get_prev_session (struct map_session_data *d) -{ - return map_get_session_backward (d->fd - 1); -} - -/*========================================== - * 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; - struct map_session_data *sd = NULL; - struct map_session_data *pl_sd = NULL; - - if (nick == NULL) - return NULL; - - nicklen = strlen (nick); - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) - && pl_sd->state.auth) - { - // Without case sensitive check (increase the number of similar character names found) - if (strncasecmp (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番号の物を探す - * 一時objectの場合は配列を引くのみ - *------------------------------------------ - */ -struct block_list *map_id2bl (int id) -{ - struct block_list *bl = NULL; - if (id < sizeof (object) / sizeof (object[0])) - bl = object[id]; - else - bl = (struct block_list *)numdb_search (id_db, id); - - return bl; -} - -/*========================================== - * id_db内の全てにfuncを実行 - *------------------------------------------ - */ -int map_foreachiddb (db_func_t func, ...) -{ - va_list ap = NULL; - - va_start (ap, func); - numdb_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) - printf ("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; - numdb_insert (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); - numdb_erase (id_db, map[m].npc[i]->bl.id); - if (map[m].npc[i]->bl.subtype == SCRIPT) - { -// free(map[m].npc[i]->u.scr.script); -// free(map[m].npc[i]->u.scr.label_list); - } - free (map[m].npc[i]); - map[m].npc[i] = NULL; - n++; - } - } - } - printf ("%d NPCs removed.\n", n); -} - -/*========================================== - * map名からmap番号へ変換 - *------------------------------------------ - */ -int map_mapname2mapid (char *name) -{ - struct map_data *md = (struct map_data *)strdb_search (map_db, name); - if (md == NULL || md->gat == NULL) - return -1; - return md->m; -} - -/*========================================== - * 他鯖map名からip,port変換 - *------------------------------------------ - */ -int map_mapname2ipport (char *name, int *ip, int *port) -{ - struct map_data_other_server *mdos = (struct map_data_other_server *)strdb_search (map_db, name); - if (mdos == NULL || mdos->gat) - return -1; - *ip = mdos->ip; - *port = mdos->port; - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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; -} - -/*========================================== - * 彼我の方向を計算 - *------------------------------------------ - */ -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 * 3 - 1 < dy) - dir = 0; // 上 - if (dx > dy * 3) - dir = 6; // 右 - } - else if (dx >= 0 && dy <= 0) - { // 方向的に右下 - dir = 5; // 右下 - if (dx * 3 - 1 < -dy) - dir = 4; // 下 - if (dx > -dy * 3) - dir = 6; // 右 - } - else if (dx <= 0 && dy <= 0) - { // 方向的に左下 - dir = 3; // 左下 - if (dx * 3 + 1 > dy) - dir = 4; // 下 - if (dx < dy * 3) - dir = 2; // 左 - } - else - { // 方向的に左上 - dir = 1; // 左上 - if (-dx * 3 - 1 < dy) - dir = 0; // 上 - if (-dx > dy * 3) - dir = 2; // 左 - } - return dir; -} - -// gat系 -/*========================================== - * (m,x,y)の状態を調べる - *------------------------------------------ - */ -int map_getcell (int m, int x, int y) -{ - if (x < 0 || x >= map[m].xs - 1 || y < 0 || y >= map[m].ys - 1) - return 1; - return map[m].gat[x + y * map[m].xs]; -} - -/*========================================== - * (m,x,y)の状態をtにする - *------------------------------------------ - */ -int map_setcell (int m, int x, int y, int t) -{ - if (x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys) - return t; - return map[m].gat[x + y * map[m].xs] = t; -} - -/*========================================== - * 他鯖管理のマップをdbに追加 - *------------------------------------------ - */ -int map_setipport (char *name, unsigned long ip, int port) -{ - struct map_data_other_server *mdos = NULL; - - struct map_data *md = (struct map_data *)strdb_search (map_db, name); - if (md == NULL) - { // not exist -> add new data - CREATE (mdos, struct map_data_other_server, 1); - memcpy (mdos->name, name, 24); - mdos->gat = NULL; - mdos->ip = ip; - mdos->port = port; - strdb_insert (map_db, mdos->name, mdos); - } - else - { - if (md->gat) - { // local -> check data - if (ip != clif_getip () || port != clif_getport ()) - { - printf ("from char server : %s -> %08lx:%d\n", name, ip, - port); - return 1; - } - } - else - { // update - mdos = (struct map_data_other_server *) md; - mdos->ip = ip; - mdos->port = port; - } - } - return 0; -} - -// 初期化周り -/*========================================== - * 水場高さ設定 - *------------------------------------------ - */ -static struct Waterlist -{ - char mapname[24]; - int waterheight; -} *waterlist = NULL; - -#define NO_WATER 1000000 - -static 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) - { - printf ("file not found: %s\n", watertxt); - return; - } - if (waterlist == NULL) - { - CREATE (waterlist, struct Waterlist, MAX_MAP_PER_SERVER); - } - 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; - } - strcpy (waterlist[n].mapname, w1); - if (count >= 2) - waterlist[n].waterheight = wh; - else - waterlist[n].waterheight = 3; - n++; - } - fclose_ (fp); -} - -/*========================================== - * マップ1枚読み込み - *------------------------------------------ - */ -static int map_readmap (int m, char *fn, char *alias) -{ - int s; - int x, y, xs, ys; - struct gat_1cell - { - char type; - } *p; - int wh; - size_t size; - - // read & convert fn - uint8_t *gat = (uint8_t *)grfio_read (fn); - if (gat == NULL) - return -1; - - printf ("\rLoading Maps [%d/%d]: %-50s ", m, map_num, fn); - fflush (stdout); - - map[m].m = m; - xs = map[m].xs = *(short *) (gat); - ys = map[m].ys = *(short *) (gat + 2); - printf ("\n%i %i\n", xs, ys); - map[m].gat = (uint8_t *)calloc (s = map[m].xs * map[m].ys, 1); - if (map[m].gat == NULL) - { - printf ("out of memory : map_readmap gat\n"); - exit (1); - } - - map[m].npc_num = 0; - map[m].users = 0; - memset (&map[m].flag, 0, sizeof (map[m].flag)); - if (battle_config.pk_mode) - map[m].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] - wh = map_waterheight (map[m].name); - for (y = 0; y < ys; y++) - { - p = (struct gat_1cell *) (gat + y * xs + 4); - for (x = 0; x < xs; x++) - { - /*if(wh!=NO_WATER && p->type==0){ - * // 水場判定 - * map[m].gat[x+y*xs]=(p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0; - * } else { */ - map[m].gat[x + y * xs] = p->type; - //} - p++; - } - } - free (gat); - - map[m].bxs = (xs + BLOCK_SIZE - 1) / BLOCK_SIZE; - map[m].bys = (ys + BLOCK_SIZE - 1) / BLOCK_SIZE; - size = map[m].bxs * map[m].bys; - - CREATE (map[m].block, struct block_list *, size); - - CREATE (map[m].block_mob, struct block_list *, size); - - CREATE (map[m].block_count, int, size); - - CREATE (map[m].block_mob_count, int, size); - - strdb_insert (map_db, map[m].name, &map[m]); - -// printf("%s read done\n",fn); - - return 0; -} - -/*========================================== - * 全てのmapデータを読み込む - *------------------------------------------ - */ -int map_readallmap (void) -{ - int i, maps_removed = 0; - char fn[256] = ""; - - // 先に全部のャbプの存在を確認 - for (i = 0; i < map_num; i++) - { - if (strstr (map[i].name, ".gat") == NULL) - continue; - sprintf (fn, "data\\%s", map[i].name); - // TODO - remove this, it is the last call to grfio_size, which is deprecated - if (!grfio_size (fn)) - { - map_delmap (map[i].name); - maps_removed++; - } - } - for (i = 0; i < map_num; i++) - { - if (strstr (map[i].name, ".gat") != NULL) - { - char *p = strstr (map[i].name, ">"); // [MouseJstr] - if (p != NULL) - { - char alias[64]; - *p = '\0'; - strcpy (alias, map[i].name); - strcpy (map[i].name, p + 1); - sprintf (fn, "data\\%s", map[i].name); - if (map_readmap (i, fn, alias) == -1) - { - map_delmap (map[i].name); - maps_removed++; - } - } - else - { - sprintf (fn, "data\\%s", map[i].name); - if (map_readmap (i, fn, NULL) == -1) - { - map_delmap (map[i].name); - maps_removed++; - } - } - } - } - - free (waterlist); - printf ("\rMaps Loaded: %d %60s\n", map_num, ""); - printf ("\rMaps Removed: %d \n", maps_removed); - return 0; -} - -/*========================================== - * 読み込むmapを追加する - *------------------------------------------ - */ -int map_addmap (char *mapname) -{ - if (strcasecmp (mapname, "clear") == 0) - { - map_num = 0; - return 0; - } - - if (map_num >= MAX_MAP_PER_SERVER - 1) - { - printf ("too many map\n"); - return 1; - } - memcpy (map[map_num].name, mapname, 24); - map_num++; - return 0; -} - -/*========================================== - * 読み込むmapを削除する - *------------------------------------------ - */ -int map_delmap (char *mapname) -{ - int i; - - if (strcasecmp (mapname, "all") == 0) - { - map_num = 0; - return 0; - } - - for (i = 0; i < map_num; i++) - { - if (strcmp (map[i].name, mapname) == 0) - { - printf ("Removing map [ %s ] from maplist\n", map[i].name); - memmove (map + i, map + i + 1, - sizeof (map[0]) * (map_num - i - 1)); - map_num--; - } - } - return 0; -} - -extern char *gm_logfile_name; - -#define LOGFILE_SECONDS_PER_CHUNK_SHIFT 10 - -FILE *map_logfile = NULL; -char *map_logfile_name = NULL; -static long map_logfile_index; - -static void map_close_logfile (void) -{ - if (map_logfile) - { - char *filenameop_buf = (char*)malloc (strlen (map_logfile_name) + 50); - sprintf (filenameop_buf, "gzip -f %s.%ld", map_logfile_name, - map_logfile_index); - - fclose (map_logfile); - - if (!system (filenameop_buf)) - perror (filenameop_buf); - - free (filenameop_buf); - } -} - -static void map_start_logfile (long suffix) -{ - char *filename_buf = (char*)malloc (strlen (map_logfile_name) + 50); - map_logfile_index = suffix >> LOGFILE_SECONDS_PER_CHUNK_SHIFT; - - sprintf (filename_buf, "%s.%ld", map_logfile_name, map_logfile_index); - map_logfile = fopen (filename_buf, "w+"); - if (!map_logfile) - perror (map_logfile_name); - - free (filename_buf); -} - -static void map_set_logfile (char *filename) -{ - struct timeval tv; - - map_logfile_name = strdup (filename); - gettimeofday (&tv, NULL); - - map_start_logfile (tv.tv_sec); - - MAP_LOG ("log-start v3"); -} - -void map_write_log (char *format, ...) -{ - struct timeval tv; - va_list args; - va_start (args, format); - - gettimeofday (&tv, NULL); - - if ((tv.tv_sec >> LOGFILE_SECONDS_PER_CHUNK_SHIFT) != map_logfile_index) - { - map_close_logfile (); - map_start_logfile (tv.tv_sec); - } - - fprintf (map_logfile, "%ld.%06ld ", (long) tv.tv_sec, (long) tv.tv_usec); - vfprintf (map_logfile, format, args); - fputc ('\n', map_logfile); -} - -/*========================================== - * 設定ファイルを読み込む - *------------------------------------------ - */ -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) - { - printf ("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 (strcasecmp (w1, "userid") == 0) - { - chrif_setuserid (w2); - } - else if (strcasecmp (w1, "passwd") == 0) - { - chrif_setpasswd (w2); - } - else if (strcasecmp (w1, "char_ip") == 0) - { - h = gethostbyname (w2); - if (h != NULL) - { - printf - ("Character server IP address : %s -> %d.%d.%d.%d\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 (strcasecmp (w1, "char_port") == 0) - { - chrif_setport (atoi (w2)); - } - else if (strcasecmp (w1, "map_ip") == 0) - { - h = gethostbyname (w2); - if (h != NULL) - { - printf ("Map server IP address : %s -> %d.%d.%d.%d\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 (strcasecmp (w1, "map_port") == 0) - { - clif_setport (atoi (w2)); - map_port = (atoi (w2)); - } - else if (strcasecmp (w1, "water_height") == 0) - { - map_readwater (w2); - } - else if (strcasecmp (w1, "map") == 0) - { - map_addmap (w2); - } - else if (strcasecmp (w1, "delmap") == 0) - { - map_delmap (w2); - } - else if (strcasecmp (w1, "npc") == 0) - { - npc_addsrcfile (w2); - } - else if (strcasecmp (w1, "delnpc") == 0) - { - npc_delsrcfile (w2); - } - else if (strcasecmp (w1, "autosave_time") == 0) - { - autosave_interval = atoi (w2) * 1000; - if (autosave_interval <= 0) - autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; - } - else if (strcasecmp (w1, "motd_txt") == 0) - { - strcpy (motd_txt, w2); - } - else if (strcasecmp (w1, "help_txt") == 0) - { - strcpy (help_txt, w2); - } - else if (strcasecmp (w1, "mapreg_txt") == 0) - { - strcpy (mapreg_txt, w2); - } - else if (strcasecmp (w1, "gm_log") == 0) - { - gm_logfile_name = strdup (w2); - } - else if (strcasecmp (w1, "log_file") == 0) - { - map_set_logfile (w2); - } - else if (strcasecmp (w1, "import") == 0) - { - map_config_read (w2); - } - } - } - fclose_ (fp); - - return 0; -} - -static int cleanup_sub (struct block_list *bl, va_list ap) -{ - nullpo_retr (0, bl); - - switch (bl->type) - { - case BL_PC: - map_delblock (bl); // There is something better... - break; - case BL_NPC: - npc_delete ((struct npc_data *) bl); - break; - case BL_MOB: - mob_delete ((struct mob_data *) bl); - break; - case BL_ITEM: - map_clearflooritem (bl->id); - break; - case BL_SKILL: - skill_delunit ((struct skill_unit *) bl); - break; - case BL_SPELL: - spell_free_invocation ((struct invocation *) bl); - break; - } - - return 0; -} - -/*========================================== - * map鯖終了時処理 - *------------------------------------------ - */ -void term_func (void) -{ - map_close_logfile (); - - int map_id, i; - - for (map_id = 0; map_id < map_num; map_id++) - { - if (map[map_id].m) - map_foreachinarea (cleanup_sub, map_id, 0, 0, map[map_id].xs, - map[map_id].ys, 0, 0); - } - - for (i = 0; i < fd_max; i++) - delete_session (i); - - map_removenpc (); - - numdb_final (id_db, NULL); - strdb_final (map_db, NULL); - strdb_final (nick_db, NULL); - numdb_final (charid_db, NULL); - - for (i = 0; i <= map_num; i++) - { - if (map[i].gat) - free (map[i].gat); - if (map[i].block) - free (map[i].block); - if (map[i].block_mob) - free (map[i].block_mob); - if (map[i].block_count) - free (map[i].block_count); - if (map[i].block_mob_count) - free (map[i].block_mob_count); - } - do_final_script (); - do_final_itemdb (); - do_final_storage (); - do_final_guild (); -} - -/// --help was passed -// FIXME this should produce output -void map_helpscreen (void) -{ - exit (1); -} - -int compare_item (struct item *a, struct item *b) -{ - return ((a->nameid == b->nameid) && - (a->identify == b->identify) && - (a->refine == b->refine) && - (a->attribute == b->attribute) && - (a->card[0] == b->card[0]) && - (a->card[1] == b->card[1]) && - (a->card[2] == b->card[2]) && (a->card[3] == b->card[3])); -} - -/*====================================================== - * Map-Server Init and Command-line Arguments [Valaris] - *------------------------------------------------------ - */ -int do_init (int argc, char *argv[]) -{ - int i; - - unsigned char *MAP_CONF_NAME = "conf/map_athena.conf"; - unsigned char *BATTLE_CONF_FILENAME = "conf/battle_athena.conf"; - unsigned char *ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf"; - unsigned char *SCRIPT_CONF_NAME = "conf/script_athena.conf"; - unsigned char *MSG_CONF_NAME = "conf/msg_athena.conf"; - - 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 (); - else if (strcmp (argv[i], "--map_config") == 0) - MAP_CONF_NAME = argv[i + 1]; - else if (strcmp (argv[i], "--battle_config") == 0) - BATTLE_CONF_FILENAME = argv[i + 1]; - else if (strcmp (argv[i], "--atcommand_config") == 0) - ATCOMMAND_CONF_FILENAME = argv[i + 1]; - else if (strcmp (argv[i], "--script_config") == 0) - SCRIPT_CONF_NAME = argv[i + 1]; - else if (strcmp (argv[i], "--msg_config") == 0) - MSG_CONF_NAME = argv[i + 1]; - } - - map_config_read (MAP_CONF_NAME); - battle_config_read (BATTLE_CONF_FILENAME); - atcommand_config_read (ATCOMMAND_CONF_FILENAME); - script_config_read (SCRIPT_CONF_NAME); - msg_config_read (MSG_CONF_NAME); - - id_db = numdb_init (); - map_db = strdb_init (16); - nick_db = strdb_init (24); - charid_db = numdb_init (); - - map_readallmap (); - -// add_timer_func_list (map_clearflooritem_timer, "map_clearflooritem_timer"); - - do_init_chrif (); - do_init_clif (); - do_init_itemdb (); - do_init_mob (); // npcの初期化時内でmob_spawnして、mob_dbを参照するのでinit_npcより先 - do_init_script (); - do_init_npc (); - do_init_pc (); - do_init_party (); - do_init_guild (); - do_init_storage (); - do_init_skill (); - do_init_magic (); - - npc_event_do_oninit (); // npcのOnInitイベント実行 - - if (battle_config.pk_mode == 1) - printf ("The server is running in \033[1;31mPK Mode\033[0m.\n"); - - printf - ("The map-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", - map_port); - - return 0; -} - -int map_scriptcont (struct map_session_data *sd, int id) -{ - struct block_list *bl = map_id2bl (id); - - if (!bl) - return 0; - - switch (bl->type) - { - case BL_NPC: - return npc_scriptcont (sd, id); - case BL_SPELL: - spell_execute_script ((struct invocation *) bl); - break; - } - - return 0; -} diff --git a/src/map/map.cpp b/src/map/map.cpp new file mode 100644 index 0000000..3c778df --- /dev/null +++ b/src/map/map.cpp @@ -0,0 +1,2215 @@ +// $Id: map.c,v 1.6 2004/09/25 17:37:01 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <netdb.h> +#endif + +#include "../common/core.hpp" +#include "../common/timer.hpp" +#include "../common/db.hpp" +#include "../common/grfio.hpp" +#include "../common/mt_rand.hpp" +#include "map.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "mob.hpp" +#include "chat.hpp" +#include "itemdb.hpp" +#include "storage.hpp" +#include "skill.hpp" +#include "trade.hpp" +#include "party.hpp" +#include "battle.hpp" +#include "script.hpp" +#include "guild.hpp" +#include "atcommand.hpp" +#include "../common/nullpo.hpp" +#include "../common/socket.hpp" +#include "magic.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +// 極力 staticでローカルに収める +static struct dbt *id_db = NULL; +static struct dbt *map_db = NULL; +static struct dbt *nick_db = NULL; +static struct dbt *charid_db = NULL; + +static int users = 0; +static struct block_list *object[MAX_FLOORITEM]; +static int first_free_object_id = 0, last_object_id = 0; + +#define block_free_max 1048576 +static void *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; + +struct map_data map[MAX_MAP_PER_SERVER]; +int map_num = 0; + +int map_port = 0; + +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int save_settings = 0xFFFF; +int agit_flag = 0; +int night_flag = 0; // 0=day, 1=night [Yor] + +struct charid2nick +{ + char nick[24]; + int req_id; +}; + +char motd_txt[256] = "conf/motd.txt"; +char help_txt[256] = "conf/help.txt"; + +char wisp_server_name[24] = "Server"; // can be modified in char-server configuration file + +/*========================================== + * 全map鯖総計での接続数設定 + * (char鯖から送られてくる) + *------------------------------------------ + */ +void map_setusers (int n) +{ + users = n; +} + +/*========================================== + * 全map鯖総計での接続数取得 (/wへの応答用) + *------------------------------------------ + */ +int map_getusers (void) +{ + return users; +} + +// +// block削除の安全性確保処理 +// + +/*========================================== + * blockをfreeするときfreeの変わりに呼ぶ + * ロックされているときはバッファにためる + *------------------------------------------ + */ +int map_freeblock (void *bl) +{ + if (block_free_lock == 0) + { + free (bl); + bl = NULL; + } + else + { + if (block_free_count >= block_free_max) + { + if (battle_config.error_log) + printf + ("map_freeblock: *WARNING* 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を一時的に禁止する + *------------------------------------------ + */ +int map_freeblock_lock (void) +{ + return ++block_free_lock; +} + +/*========================================== + * blockのfreeのロックを解除する + * このとき、ロックが完全になくなると + * バッファにたまっていたblockを全部削除 + *------------------------------------------ + */ +int map_freeblock_unlock (void) +{ + if ((--block_free_lock) == 0) + { + int i; +// if(block_free_count>0) { +// if(battle_config.error_log) +// printf("map_freeblock_unlock: free %d object\n",block_free_count); +// } + for (i = 0; i < block_free_count; i++) + { + free (block_free[i]); + block_free[i] = NULL; + } + block_free_count = 0; + } + else if (block_free_lock < 0) + { + if (battle_config.error_log) + printf ("map_freeblock_unlock: lock count < 0 !\n"); + } + return block_free_lock; +} + +// +// block化処理 +// +/*========================================== + * map[]のblock_listから繋がっている場合に + * bl->prevにbl_headのアドレスを入れておく + *------------------------------------------ + */ +static struct block_list bl_head; + +/*========================================== + * map[]のblock_listに追加 + * mobは数が多いので別リスト + * + * 既にlink済みかの確認が無い。危険かも + *------------------------------------------ + */ +int map_addblock (struct block_list *bl) +{ + int m, x, y; + + nullpo_retr (0, bl); + + if (bl->prev != NULL) + { + if (battle_config.error_log) + printf ("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; + + if (bl->type == BL_MOB) + { + bl->next = + map[m].block_mob[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]; + bl->prev = &bl_head; + if (bl->next) + bl->next->prev = bl; + map[m].block_mob[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs] = bl; + map[m].block_mob_count[x / BLOCK_SIZE + + (y / BLOCK_SIZE) * map[m].bxs]++; + } + else + { + bl->next = + map[m].block[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]; + bl->prev = &bl_head; + if (bl->next) + bl->next->prev = bl; + map[m].block[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs] = bl; + map[m].block_count[x / BLOCK_SIZE + (y / BLOCK_SIZE) * map[m].bxs]++; + if (bl->type == BL_PC) + map[m].users++; + } + + return 0; +} + +/*========================================== + * map[]のblock_listから外す + * prevがNULLの場合listに繋がってない + *------------------------------------------ + */ +int map_delblock (struct block_list *bl) +{ + int b; + nullpo_retr (0, bl); + + // 既にblocklistから抜けている + if (bl->prev == NULL) + { + if (bl->next != NULL) + { + // prevがNULLでnextがNULLでないのは有ってはならない + if (battle_config.error_log) + printf ("map_delblock error : bl->next!=NULL\n"); + } + return 0; + } + + b = bl->x / BLOCK_SIZE + (bl->y / BLOCK_SIZE) * map[bl->m].bxs; + + if (bl->type == BL_PC) + map[bl->m].users--; + + 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; +} + +/*========================================== + * 周囲のPC人数を数える (現在未使用) + *------------------------------------------ + */ +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]; + for (; bl; bl = bl->next) + { + if (bl->type == BL_PC) + c++; + } + } + } + return c; +} + +/*========================================== + * セル上のPCとMOBの数を数える (グランドクロス用) + *------------------------------------------ + */ +int map_count_oncell (int m, int x, int y) +{ + 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 1; + 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_PC) + count++; + } + 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++; + } + if (!count) + count = 1; + return count; +} + +/*========================================== + * map m (x0,y0)-(x1,y1)内の全objに対して + * funcを呼ぶ + * type!=0 ならその種類のみ + *------------------------------------------ + */ +void map_foreachinarea (int (*func) (struct block_list *, va_list), int m, + int x0, int y0, int x1, int y1, int type, ...) +{ + int bx, by; + struct block_list *bl = NULL; + va_list ap = NULL; + int blockcount = bl_list_count, i, c; + + if (m < 0) + return; + va_start (ap, type); + 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 == 0 || 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 && type && bl->type != type) + continue; + 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 (type == 0 || 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) + printf ("map_foreachinarea: *WARNING* block count too many!\n"); + } + + map_freeblock_lock (); // メモリからの解放を禁止する + + for (i = blockcount; i < bl_list_count; i++) + if (bl_list[i]->prev) // 有効かどうかチェック + func (bl_list[i], ap); + + map_freeblock_unlock (); // 解放を許可する + + va_end (ap); + bl_list_count = blockcount; +} + +/*========================================== + * 矩形(x0,y0)-(x1,y1)が(dx,dy)移動した時の + * 領域外になる領域(矩形かL字形)内のobjに + * 対してfuncを呼ぶ + * + * dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?) + *------------------------------------------ + */ +void 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; + struct block_list *bl = NULL; + va_list ap = NULL; + int blockcount = bl_list_count, i, c; + + va_start (ap, type); + 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++) + { + 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 && type && bl->type != type) + continue; + 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; + } + 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 && type && bl->type != type) + continue; + 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++) + { + 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 && type && bl->type != type) + continue; + 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; + } + 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 && type && bl->type != type) + continue; + 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) + printf ("map_foreachinarea: *WARNING* block count too many!\n"); + } + + map_freeblock_lock (); // メモリからの解放を禁止する + + for (i = blockcount; i < bl_list_count; i++) + if (bl_list[i]->prev) // 有効かどうかチェック + func (bl_list[i], ap); + + map_freeblock_unlock (); // 解放を許可する + + va_end (ap); + bl_list_count = blockcount; +} + +// -- 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) +// +void map_foreachincell (int (*func) (struct block_list *, va_list), int m, + int x, int y, int type, ...) +{ + int bx, by; + struct block_list *bl = NULL; + va_list ap = NULL; + int blockcount = bl_list_count, i, c; + + va_start (ap, type); + + by = y / BLOCK_SIZE; + bx = x / 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 (type && bl && bl->type != type) + continue; + if (bl && bl->x == x && bl->y == y && bl_list_count < BL_LIST_MAX) + bl_list[bl_list_count++] = bl; + } + } + + 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 && 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) + printf ("map_foreachincell: *WARNING* block count too many!\n"); + } + + map_freeblock_lock (); // メモリからの解放を禁止する + + for (i = blockcount; i < bl_list_count; i++) + if (bl_list[i]->prev) // 有効かどうかチェック + func (bl_list[i], ap); + + map_freeblock_unlock (); // 解放を許可する + + va_end (ap); + bl_list_count = blockcount; +} + +/*========================================== + * 床アイテムやエフェクト用の一時obj割り当て + * object[]への保存とid_db登録まで + * + * bl->idもこの中で設定して問題無い? + *------------------------------------------ + */ +int map_addobject (struct block_list *bl) +{ + int i; + if (bl == NULL) + { + printf ("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; i++) + if (object[i] == NULL) + break; + if (i >= MAX_FLOORITEM) + { + if (battle_config.error_log) + printf ("no free object id\n"); + return 0; + } + first_free_object_id = i; + if (last_object_id < i) + last_object_id = i; + object[i] = bl; + numdb_insert (id_db, i, bl); + return i; +} + +/*========================================== + * 一時objectの解放 + * map_delobjectのfreeしないバージョン + *------------------------------------------ + */ +int map_delobjectnofree (int id, int type) +{ + if (object[id] == NULL) + return 0; + + if (object[id]->type != type) + { + fprintf (stderr, "Incorrect type: expected %d, got %d\n", type, + object[id]->type); + *((char *) 0) = 0; // break for backtrace + } + + map_delblock (object[id]); + numdb_erase (id_db, id); +// map_freeblock(object[id]); + object[id] = NULL; + + if (first_free_object_id > id) + first_free_object_id = id; + + while (last_object_id > 2 && object[last_object_id] == NULL) + last_object_id--; + + return 0; +} + +/*========================================== + * 一時objectの解放 + * block_listからの削除、id_dbからの削除 + * object dataのfree、object[]へのNULL代入 + * + * addとの対称性が無いのが気になる + *------------------------------------------ + */ +int map_delobject (int id, int type) +{ + struct block_list *obj = object[id]; + + if (obj == NULL) + return 0; + + map_delobjectnofree (id, type); + if (obj->type == BL_PC) // [Fate] Not sure where else to put this... I'm not sure where delobject for PCs is called from + pc_cleanup ((struct map_session_data *) obj); + + map_freeblock (obj); + + return 0; +} + +/*========================================== + * 全一時obj相手にfuncを呼ぶ + * + *------------------------------------------ + */ +void map_foreachobject (int (*func) (struct block_list *, va_list), int type, + ...) +{ + int i; + int blockcount = bl_list_count; + va_list ap = NULL; + + va_start (ap, type); + + for (i = 2; i <= last_object_id; i++) + { + if (object[i]) + { + if (type && object[i]->type != type) + continue; + if (bl_list_count >= BL_LIST_MAX) + { + if (battle_config.error_log) + printf ("map_foreachobject: too many block !\n"); + } + else + bl_list[bl_list_count++] = object[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してある + *------------------------------------------ + */ +void map_clearflooritem_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct flooritem_data *fitem = NULL; + + fitem = (struct flooritem_data *) object[id]; + if (fitem == NULL || fitem->bl.type != BL_ITEM + || (!data && fitem->cleartimer != tid)) + { + if (battle_config.error_log) + printf ("map_clearflooritem_timer : error\n"); + return; + } + if (data) + delete_timer (fitem->cleartimer, map_clearflooritem_timer); + clif_clearflooritem (fitem, 0); + map_delobject (fitem->bl.id, BL_ITEM); +} + +/*========================================== + * (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, c; + + 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 ((c = read_gat (m, j + x, i + y)) == 1 || c == 5) + continue; + free_cell++; + } + } + if (free_cell == 0) + return -1; + free_cell = MRAND (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 ((c = read_gat (m, j + x, i + y)) == 1 || c == 5) + 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_any (struct item *item_data, int amount, int m, int x, + int y, struct map_session_data **owners, + int *owner_protection, int lifetime, int dispersal) +{ + int xy, r; + unsigned int tick; + struct flooritem_data *fitem = NULL; + + nullpo_retr (0, item_data); + + if ((xy = map_searchrandfreecell (m, x, y, dispersal)) < 0) + return 0; + r = mt_random (); + + CREATE (fitem, struct flooritem_data, 1); + 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->first_get_id = 0; + fitem->first_get_tick = 0; + fitem->second_get_id = 0; + fitem->second_get_tick = 0; + fitem->third_get_id = 0; + fitem->third_get_tick = 0; + + fitem->bl.id = map_addobject (&fitem->bl); + if (fitem->bl.id == 0) + { + free (fitem); + return 0; + } + + tick = gettick (); + + if (owners[0]) + fitem->first_get_id = owners[0]->bl.id; + fitem->first_get_tick = tick + owner_protection[0]; + + if (owners[1]) + fitem->second_get_id = owners[1]->bl.id; + fitem->second_get_tick = tick + owner_protection[1]; + + if (owners[2]) + fitem->third_get_id = owners[2]->bl.id; + fitem->third_get_tick = tick + owner_protection[2]; + + 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 () + lifetime, map_clearflooritem_timer, + fitem->bl.id, 0); + + map_addblock (&fitem->bl); + clif_dropflooritem (fitem); + + return fitem->bl.id; +} + +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) +{ + struct map_session_data *owners[3] = { first_sd, second_sd, third_sd }; + int owner_protection[3]; + + if (type) + { + owner_protection[0] = battle_config.mvp_item_first_get_time; + owner_protection[1] = + owner_protection[0] + battle_config.mvp_item_second_get_time; + owner_protection[2] = + owner_protection[1] + battle_config.mvp_item_third_get_time; + } + else + { + owner_protection[0] = battle_config.item_first_get_time; + owner_protection[1] = + owner_protection[0] + battle_config.item_second_get_time; + owner_protection[2] = + owner_protection[1] + battle_config.item_third_get_time; + } + + return map_addflooritem_any (item_data, amount, m, x, y, + owners, owner_protection, + battle_config.flooritem_lifetime, 1); +} + +/* 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->first_get_id = 0; */ +/* fitem->first_get_tick = 0; */ +/* fitem->second_get_id = 0; */ +/* fitem->second_get_tick = 0; */ +/* fitem->third_get_id = 0; */ +/* fitem->third_get_tick = 0; */ + +/* fitem->bl.id = map_addobject(&fitem->bl); */ +/* if(fitem->bl.id==0){ */ +/* free(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; */ +/* } */ + +/*========================================== + * charid_dbへ追加(返信待ちがあれば返信) + *------------------------------------------ + */ +void map_addchariddb (int charid, char *name) +{ + struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, charid); + if (p == NULL) + { // データベースにない + CREATE (p, struct charid2nick, 1); + p->req_id = 0; + } + else + numdb_erase (charid_db, charid); + + int req = p->req_id; + memcpy (p->nick, name, 24); + p->req_id = 0; + numdb_insert (charid_db, charid, p); + if (req) + { // 返信待ちがあれば返信 + struct map_session_data *sd = map_id2sd (req); + if (sd != NULL) + clif_solved_charname (sd, charid); + } +} + +/*========================================== + * charid_dbへ追加(返信要求のみ) + *------------------------------------------ + */ +int map_reqchariddb (struct map_session_data *sd, int charid) +{ + nullpo_retr (0, sd); + + struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, charid); + if (p != NULL) // データベースにすでにある + return 0; + CREATE (p, struct charid2nick, 1); + p->req_id = sd->bl.id; + numdb_insert (charid_db, charid, p); + return 0; +} + +/*========================================== + * id_dbへblを追加 + *------------------------------------------ + */ +void map_addiddb (struct block_list *bl) +{ + nullpo_retv (bl); + + numdb_insert (id_db, bl->id, bl); +} + +/*========================================== + * id_dbからblを削除 + *------------------------------------------ + */ +void map_deliddb (struct block_list *bl) +{ + nullpo_retv (bl); + + numdb_erase (id_db, bl->id); +} + +/*========================================== + * nick_dbへsdを追加 + *------------------------------------------ + */ +void map_addnickdb (struct map_session_data *sd) +{ + nullpo_retv (sd); + + strdb_insert (nick_db, sd->status.name, sd); +} + +/*========================================== + * PCのquit処理 map.c内分 + * + * quit処理の主体が違うような気もしてきた + *------------------------------------------ + */ +int map_quit (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + 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); + + party_send_logout (sd); // パーティのログアウトメッセージ送信 + + guild_send_memberinfoshort (sd, 0); // ギルドのログアウトメッセージ送信 + + pc_cleareventtimer (sd); // イベントタイマを破棄する + + skill_castcancel (&sd->bl, 0); // 詠唱を中断する + skill_stop_dancing (&sd->bl, 1); // ダンス/演奏中断 + + if (sd->sc_data && sd->sc_data[SC_BERSERK].timer != -1) //バーサーク中の終了はHPを100に + sd->status.hp = 100; + + skill_status_change_clear (&sd->bl, 1); // ステータス異常を解除する + skill_clear_unitgroup (&sd->bl); // スキルユニットグループの削除 + skill_cleartimerskill (&sd->bl); + pc_stop_walking (sd, 0); + pc_stopattack (sd); + pc_delinvincibletimer (sd); + pc_delspiritball (sd, sd->spiritball, 1); + skill_gangsterparadise (sd, 0); + + pc_calcstatus (sd, 4); + + clif_clearchar_area (&sd->bl, 2); + + if (pc_isdead (sd)) + pc_setrestartvalue (sd, 2); + pc_makesavestatus (sd); + //クローンスキルで覚えたスキルは消す + + //The storage closing routines will save the char if needed. [Skotlex] + if (!sd->state.storage_flag) + chrif_save (sd); + else if (sd->state.storage_flag == 1) + storage_storage_quit (sd); + else if (sd->state.storage_flag == 2) + storage_guild_storage_quit (sd, 1); + + if (sd->npc_stackbuf && sd->npc_stackbuf != NULL) + free (sd->npc_stackbuf); + + map_delblock (&sd->bl); + + numdb_erase (id_db, sd->bl.id); + strdb_erase (nick_db, sd->status.name); + numdb_erase (charid_db, sd->status.char_id); + + return 0; +} + +/*========================================== + * id番号のPCを探す。居なければNULL + *------------------------------------------ + */ +struct map_session_data *map_id2sd (int id) +{ +// remove search from db, because: +// 1 - all players, npc, items and mob are in this db (to search, it's not speed, and search in session is more sure) +// 2 - DB seems not always correct. Sometimes, when a player disconnects, its id (account value) is not removed and structure +// point to a memory area that is not more a session_data and value are incorrect (or out of available memory) -> crash +// replaced by searching in all session. +// by searching in session, we are sure that fd, session, and account exist. +/* + struct block_list *bl; + + bl=numdb_search(id_db,id); + if(bl && bl->type==BL_PC) + return (struct map_session_data*)bl; + return NULL; +*/ + int i; + struct map_session_data *sd = NULL; + + for (i = 0; i < fd_max; i++) + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->bl.id == id) + return sd; + + return NULL; +} + +/*========================================== + * char_id番号の名前を探す + *------------------------------------------ + */ +char *map_charid2nick (int id) +{ + struct charid2nick *p = (struct charid2nick *)numdb_search (charid_db, id); + + if (p == NULL) + return NULL; + if (p->req_id != 0) + return NULL; + return p->nick; +} + +/*========================================*/ +/* [Fate] Operations to iterate over active map sessions */ + +static struct map_session_data *map_get_session (int i) +{ + struct map_session_data *d; + + if (i >= 0 && i < fd_max + && session[i] && (d = (struct map_session_data *)session[i]->session_data) && d->state.auth) + return d; + + return NULL; +} + +static struct map_session_data *map_get_session_forward (int start) +{ + int i; + for (i = start; i < fd_max; i++) + { + struct map_session_data *d = map_get_session (i); + if (d) + return d; + } + + return NULL; +} + +static struct map_session_data *map_get_session_backward (int start) +{ + int i; + for (i = start; i >= 0; i--) + { + struct map_session_data *d = map_get_session (i); + if (d) + return d; + } + + return NULL; +} + +struct map_session_data *map_get_first_session (void) +{ + return map_get_session_forward (0); +} + +struct map_session_data *map_get_next_session (struct map_session_data *d) +{ + return map_get_session_forward (d->fd + 1); +} + +struct map_session_data *map_get_last_session (void) +{ + return map_get_session_backward (fd_max); +} + +struct map_session_data *map_get_prev_session (struct map_session_data *d) +{ + return map_get_session_backward (d->fd - 1); +} + +/*========================================== + * 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; + struct map_session_data *sd = NULL; + struct map_session_data *pl_sd = NULL; + + if (nick == NULL) + return NULL; + + nicklen = strlen (nick); + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (pl_sd = (struct map_session_data *)session[i]->session_data) + && pl_sd->state.auth) + { + // Without case sensitive check (increase the number of similar character names found) + if (strncasecmp (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番号の物を探す + * 一時objectの場合は配列を引くのみ + *------------------------------------------ + */ +struct block_list *map_id2bl (int id) +{ + struct block_list *bl = NULL; + if (id < sizeof (object) / sizeof (object[0])) + bl = object[id]; + else + bl = (struct block_list *)numdb_search (id_db, id); + + return bl; +} + +/*========================================== + * id_db内の全てにfuncを実行 + *------------------------------------------ + */ +int map_foreachiddb (db_func_t func, ...) +{ + va_list ap = NULL; + + va_start (ap, func); + numdb_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) + printf ("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; + numdb_insert (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); + numdb_erase (id_db, map[m].npc[i]->bl.id); + if (map[m].npc[i]->bl.subtype == SCRIPT) + { +// free(map[m].npc[i]->u.scr.script); +// free(map[m].npc[i]->u.scr.label_list); + } + free (map[m].npc[i]); + map[m].npc[i] = NULL; + n++; + } + } + } + printf ("%d NPCs removed.\n", n); +} + +/*========================================== + * map名からmap番号へ変換 + *------------------------------------------ + */ +int map_mapname2mapid (char *name) +{ + struct map_data *md = (struct map_data *)strdb_search (map_db, name); + if (md == NULL || md->gat == NULL) + return -1; + return md->m; +} + +/*========================================== + * 他鯖map名からip,port変換 + *------------------------------------------ + */ +int map_mapname2ipport (char *name, int *ip, int *port) +{ + struct map_data_other_server *mdos = (struct map_data_other_server *)strdb_search (map_db, name); + if (mdos == NULL || mdos->gat) + return -1; + *ip = mdos->ip; + *port = mdos->port; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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; +} + +/*========================================== + * 彼我の方向を計算 + *------------------------------------------ + */ +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 * 3 - 1 < dy) + dir = 0; // 上 + if (dx > dy * 3) + dir = 6; // 右 + } + else if (dx >= 0 && dy <= 0) + { // 方向的に右下 + dir = 5; // 右下 + if (dx * 3 - 1 < -dy) + dir = 4; // 下 + if (dx > -dy * 3) + dir = 6; // 右 + } + else if (dx <= 0 && dy <= 0) + { // 方向的に左下 + dir = 3; // 左下 + if (dx * 3 + 1 > dy) + dir = 4; // 下 + if (dx < dy * 3) + dir = 2; // 左 + } + else + { // 方向的に左上 + dir = 1; // 左上 + if (-dx * 3 - 1 < dy) + dir = 0; // 上 + if (-dx > dy * 3) + dir = 2; // 左 + } + return dir; +} + +// gat系 +/*========================================== + * (m,x,y)の状態を調べる + *------------------------------------------ + */ +int map_getcell (int m, int x, int y) +{ + if (x < 0 || x >= map[m].xs - 1 || y < 0 || y >= map[m].ys - 1) + return 1; + return map[m].gat[x + y * map[m].xs]; +} + +/*========================================== + * (m,x,y)の状態をtにする + *------------------------------------------ + */ +int map_setcell (int m, int x, int y, int t) +{ + if (x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys) + return t; + return map[m].gat[x + y * map[m].xs] = t; +} + +/*========================================== + * 他鯖管理のマップをdbに追加 + *------------------------------------------ + */ +int map_setipport (char *name, unsigned long ip, int port) +{ + struct map_data_other_server *mdos = NULL; + + struct map_data *md = (struct map_data *)strdb_search (map_db, name); + if (md == NULL) + { // not exist -> add new data + CREATE (mdos, struct map_data_other_server, 1); + memcpy (mdos->name, name, 24); + mdos->gat = NULL; + mdos->ip = ip; + mdos->port = port; + strdb_insert (map_db, mdos->name, mdos); + } + else + { + if (md->gat) + { // local -> check data + if (ip != clif_getip () || port != clif_getport ()) + { + printf ("from char server : %s -> %08lx:%d\n", name, ip, + port); + return 1; + } + } + else + { // update + mdos = (struct map_data_other_server *) md; + mdos->ip = ip; + mdos->port = port; + } + } + return 0; +} + +// 初期化周り +/*========================================== + * 水場高さ設定 + *------------------------------------------ + */ +static struct Waterlist +{ + char mapname[24]; + int waterheight; +} *waterlist = NULL; + +#define NO_WATER 1000000 + +static 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) + { + printf ("file not found: %s\n", watertxt); + return; + } + if (waterlist == NULL) + { + CREATE (waterlist, struct Waterlist, MAX_MAP_PER_SERVER); + } + 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; + } + strcpy (waterlist[n].mapname, w1); + if (count >= 2) + waterlist[n].waterheight = wh; + else + waterlist[n].waterheight = 3; + n++; + } + fclose_ (fp); +} + +/*========================================== + * マップ1枚読み込み + *------------------------------------------ + */ +static int map_readmap (int m, char *fn, char *alias) +{ + int s; + int x, y, xs, ys; + struct gat_1cell + { + char type; + } *p; + int wh; + size_t size; + + // read & convert fn + uint8_t *gat = (uint8_t *)grfio_read (fn); + if (gat == NULL) + return -1; + + printf ("\rLoading Maps [%d/%d]: %-50s ", m, map_num, fn); + fflush (stdout); + + map[m].m = m; + xs = map[m].xs = *(short *) (gat); + ys = map[m].ys = *(short *) (gat + 2); + printf ("\n%i %i\n", xs, ys); + map[m].gat = (uint8_t *)calloc (s = map[m].xs * map[m].ys, 1); + if (map[m].gat == NULL) + { + printf ("out of memory : map_readmap gat\n"); + exit (1); + } + + map[m].npc_num = 0; + map[m].users = 0; + memset (&map[m].flag, 0, sizeof (map[m].flag)); + if (battle_config.pk_mode) + map[m].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] + wh = map_waterheight (map[m].name); + for (y = 0; y < ys; y++) + { + p = (struct gat_1cell *) (gat + y * xs + 4); + for (x = 0; x < xs; x++) + { + /*if(wh!=NO_WATER && p->type==0){ + * // 水場判定 + * map[m].gat[x+y*xs]=(p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0; + * } else { */ + map[m].gat[x + y * xs] = p->type; + //} + p++; + } + } + free (gat); + + map[m].bxs = (xs + BLOCK_SIZE - 1) / BLOCK_SIZE; + map[m].bys = (ys + BLOCK_SIZE - 1) / BLOCK_SIZE; + size = map[m].bxs * map[m].bys; + + CREATE (map[m].block, struct block_list *, size); + + CREATE (map[m].block_mob, struct block_list *, size); + + CREATE (map[m].block_count, int, size); + + CREATE (map[m].block_mob_count, int, size); + + strdb_insert (map_db, map[m].name, &map[m]); + +// printf("%s read done\n",fn); + + return 0; +} + +/*========================================== + * 全てのmapデータを読み込む + *------------------------------------------ + */ +int map_readallmap (void) +{ + int i, maps_removed = 0; + char fn[256] = ""; + + // 先に全部のャbプの存在を確認 + for (i = 0; i < map_num; i++) + { + if (strstr (map[i].name, ".gat") == NULL) + continue; + sprintf (fn, "data\\%s", map[i].name); + // TODO - remove this, it is the last call to grfio_size, which is deprecated + if (!grfio_size (fn)) + { + map_delmap (map[i].name); + maps_removed++; + } + } + for (i = 0; i < map_num; i++) + { + if (strstr (map[i].name, ".gat") != NULL) + { + char *p = strstr (map[i].name, ">"); // [MouseJstr] + if (p != NULL) + { + char alias[64]; + *p = '\0'; + strcpy (alias, map[i].name); + strcpy (map[i].name, p + 1); + sprintf (fn, "data\\%s", map[i].name); + if (map_readmap (i, fn, alias) == -1) + { + map_delmap (map[i].name); + maps_removed++; + } + } + else + { + sprintf (fn, "data\\%s", map[i].name); + if (map_readmap (i, fn, NULL) == -1) + { + map_delmap (map[i].name); + maps_removed++; + } + } + } + } + + free (waterlist); + printf ("\rMaps Loaded: %d %60s\n", map_num, ""); + printf ("\rMaps Removed: %d \n", maps_removed); + return 0; +} + +/*========================================== + * 読み込むmapを追加する + *------------------------------------------ + */ +int map_addmap (char *mapname) +{ + if (strcasecmp (mapname, "clear") == 0) + { + map_num = 0; + return 0; + } + + if (map_num >= MAX_MAP_PER_SERVER - 1) + { + printf ("too many map\n"); + return 1; + } + memcpy (map[map_num].name, mapname, 24); + map_num++; + return 0; +} + +/*========================================== + * 読み込むmapを削除する + *------------------------------------------ + */ +int map_delmap (char *mapname) +{ + int i; + + if (strcasecmp (mapname, "all") == 0) + { + map_num = 0; + return 0; + } + + for (i = 0; i < map_num; i++) + { + if (strcmp (map[i].name, mapname) == 0) + { + printf ("Removing map [ %s ] from maplist\n", map[i].name); + memmove (map + i, map + i + 1, + sizeof (map[0]) * (map_num - i - 1)); + map_num--; + } + } + return 0; +} + +extern char *gm_logfile_name; + +#define LOGFILE_SECONDS_PER_CHUNK_SHIFT 10 + +FILE *map_logfile = NULL; +char *map_logfile_name = NULL; +static long map_logfile_index; + +static void map_close_logfile (void) +{ + if (map_logfile) + { + char *filenameop_buf = (char*)malloc (strlen (map_logfile_name) + 50); + sprintf (filenameop_buf, "gzip -f %s.%ld", map_logfile_name, + map_logfile_index); + + fclose (map_logfile); + + if (!system (filenameop_buf)) + perror (filenameop_buf); + + free (filenameop_buf); + } +} + +static void map_start_logfile (long suffix) +{ + char *filename_buf = (char*)malloc (strlen (map_logfile_name) + 50); + map_logfile_index = suffix >> LOGFILE_SECONDS_PER_CHUNK_SHIFT; + + sprintf (filename_buf, "%s.%ld", map_logfile_name, map_logfile_index); + map_logfile = fopen (filename_buf, "w+"); + if (!map_logfile) + perror (map_logfile_name); + + free (filename_buf); +} + +static void map_set_logfile (char *filename) +{ + struct timeval tv; + + map_logfile_name = strdup (filename); + gettimeofday (&tv, NULL); + + map_start_logfile (tv.tv_sec); + + MAP_LOG ("log-start v3"); +} + +void map_write_log (char *format, ...) +{ + struct timeval tv; + va_list args; + va_start (args, format); + + gettimeofday (&tv, NULL); + + if ((tv.tv_sec >> LOGFILE_SECONDS_PER_CHUNK_SHIFT) != map_logfile_index) + { + map_close_logfile (); + map_start_logfile (tv.tv_sec); + } + + fprintf (map_logfile, "%ld.%06ld ", (long) tv.tv_sec, (long) tv.tv_usec); + vfprintf (map_logfile, format, args); + fputc ('\n', map_logfile); +} + +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +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) + { + printf ("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 (strcasecmp (w1, "userid") == 0) + { + chrif_setuserid (w2); + } + else if (strcasecmp (w1, "passwd") == 0) + { + chrif_setpasswd (w2); + } + else if (strcasecmp (w1, "char_ip") == 0) + { + h = gethostbyname (w2); + if (h != NULL) + { + printf + ("Character server IP address : %s -> %d.%d.%d.%d\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 (strcasecmp (w1, "char_port") == 0) + { + chrif_setport (atoi (w2)); + } + else if (strcasecmp (w1, "map_ip") == 0) + { + h = gethostbyname (w2); + if (h != NULL) + { + printf ("Map server IP address : %s -> %d.%d.%d.%d\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 (strcasecmp (w1, "map_port") == 0) + { + clif_setport (atoi (w2)); + map_port = (atoi (w2)); + } + else if (strcasecmp (w1, "water_height") == 0) + { + map_readwater (w2); + } + else if (strcasecmp (w1, "map") == 0) + { + map_addmap (w2); + } + else if (strcasecmp (w1, "delmap") == 0) + { + map_delmap (w2); + } + else if (strcasecmp (w1, "npc") == 0) + { + npc_addsrcfile (w2); + } + else if (strcasecmp (w1, "delnpc") == 0) + { + npc_delsrcfile (w2); + } + else if (strcasecmp (w1, "autosave_time") == 0) + { + autosave_interval = atoi (w2) * 1000; + if (autosave_interval <= 0) + autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; + } + else if (strcasecmp (w1, "motd_txt") == 0) + { + strcpy (motd_txt, w2); + } + else if (strcasecmp (w1, "help_txt") == 0) + { + strcpy (help_txt, w2); + } + else if (strcasecmp (w1, "mapreg_txt") == 0) + { + strcpy (mapreg_txt, w2); + } + else if (strcasecmp (w1, "gm_log") == 0) + { + gm_logfile_name = strdup (w2); + } + else if (strcasecmp (w1, "log_file") == 0) + { + map_set_logfile (w2); + } + else if (strcasecmp (w1, "import") == 0) + { + map_config_read (w2); + } + } + } + fclose_ (fp); + + return 0; +} + +static int cleanup_sub (struct block_list *bl, va_list ap) +{ + nullpo_retr (0, bl); + + switch (bl->type) + { + case BL_PC: + map_delblock (bl); // There is something better... + break; + case BL_NPC: + npc_delete ((struct npc_data *) bl); + break; + case BL_MOB: + mob_delete ((struct mob_data *) bl); + break; + case BL_ITEM: + map_clearflooritem (bl->id); + break; + case BL_SKILL: + skill_delunit ((struct skill_unit *) bl); + break; + case BL_SPELL: + spell_free_invocation ((struct invocation *) bl); + break; + } + + return 0; +} + +/*========================================== + * map鯖終了時処理 + *------------------------------------------ + */ +void term_func (void) +{ + map_close_logfile (); + + int map_id, i; + + for (map_id = 0; map_id < map_num; map_id++) + { + if (map[map_id].m) + map_foreachinarea (cleanup_sub, map_id, 0, 0, map[map_id].xs, + map[map_id].ys, 0, 0); + } + + for (i = 0; i < fd_max; i++) + delete_session (i); + + map_removenpc (); + + numdb_final (id_db, NULL); + strdb_final (map_db, NULL); + strdb_final (nick_db, NULL); + numdb_final (charid_db, NULL); + + for (i = 0; i <= map_num; i++) + { + if (map[i].gat) + free (map[i].gat); + if (map[i].block) + free (map[i].block); + if (map[i].block_mob) + free (map[i].block_mob); + if (map[i].block_count) + free (map[i].block_count); + if (map[i].block_mob_count) + free (map[i].block_mob_count); + } + do_final_script (); + do_final_itemdb (); + do_final_storage (); + do_final_guild (); +} + +/// --help was passed +// FIXME this should produce output +void map_helpscreen (void) +{ + exit (1); +} + +int compare_item (struct item *a, struct item *b) +{ + return ((a->nameid == b->nameid) && + (a->identify == b->identify) && + (a->refine == b->refine) && + (a->attribute == b->attribute) && + (a->card[0] == b->card[0]) && + (a->card[1] == b->card[1]) && + (a->card[2] == b->card[2]) && (a->card[3] == b->card[3])); +} + +/*====================================================== + * Map-Server Init and Command-line Arguments [Valaris] + *------------------------------------------------------ + */ +int do_init (int argc, char *argv[]) +{ + int i; + + unsigned char *MAP_CONF_NAME = "conf/map_athena.conf"; + unsigned char *BATTLE_CONF_FILENAME = "conf/battle_athena.conf"; + unsigned char *ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf"; + unsigned char *SCRIPT_CONF_NAME = "conf/script_athena.conf"; + unsigned char *MSG_CONF_NAME = "conf/msg_athena.conf"; + + 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 (); + else if (strcmp (argv[i], "--map_config") == 0) + MAP_CONF_NAME = argv[i + 1]; + else if (strcmp (argv[i], "--battle_config") == 0) + BATTLE_CONF_FILENAME = argv[i + 1]; + else if (strcmp (argv[i], "--atcommand_config") == 0) + ATCOMMAND_CONF_FILENAME = argv[i + 1]; + else if (strcmp (argv[i], "--script_config") == 0) + SCRIPT_CONF_NAME = argv[i + 1]; + else if (strcmp (argv[i], "--msg_config") == 0) + MSG_CONF_NAME = argv[i + 1]; + } + + map_config_read (MAP_CONF_NAME); + battle_config_read (BATTLE_CONF_FILENAME); + atcommand_config_read (ATCOMMAND_CONF_FILENAME); + script_config_read (SCRIPT_CONF_NAME); + msg_config_read (MSG_CONF_NAME); + + id_db = numdb_init (); + map_db = strdb_init (16); + nick_db = strdb_init (24); + charid_db = numdb_init (); + + map_readallmap (); + +// add_timer_func_list (map_clearflooritem_timer, "map_clearflooritem_timer"); + + do_init_chrif (); + do_init_clif (); + do_init_itemdb (); + do_init_mob (); // npcの初期化時内でmob_spawnして、mob_dbを参照するのでinit_npcより先 + do_init_script (); + do_init_npc (); + do_init_pc (); + do_init_party (); + do_init_guild (); + do_init_storage (); + do_init_skill (); + do_init_magic (); + + npc_event_do_oninit (); // npcのOnInitイベント実行 + + if (battle_config.pk_mode == 1) + printf ("The server is running in \033[1;31mPK Mode\033[0m.\n"); + + printf + ("The map-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", + map_port); + + return 0; +} + +int map_scriptcont (struct map_session_data *sd, int id) +{ + struct block_list *bl = map_id2bl (id); + + if (!bl) + return 0; + + switch (bl->type) + { + case BL_NPC: + return npc_scriptcont (sd, id); + case BL_SPELL: + spell_execute_script ((struct invocation *) bl); + break; + } + + return 0; +} diff --git a/src/map/map.h b/src/map/map.h deleted file mode 100644 index a480cd0..0000000 --- a/src/map/map.h +++ /dev/null @@ -1,822 +0,0 @@ -// $Id: map.h,v 1.8 2004/09/25 11:39:17 MouseJstr Exp $ -#ifndef _MAP_H_ -#define _MAP_H_ - -#include <stdio.h> -#include <stdarg.h> -#include <time.h> -#include <sys/time.h> -#include <netinet/in.h> -#include "../common/mmo.h" -#include "../common/timer.h" -#include "../common/db.h" - -#ifndef MAX -# define MAX(x,y) (((x)>(y)) ? (x) : (y)) -#endif -#ifndef MIN -# define MIN(x,y) (((x)<(y)) ? (x) : (y)) -#endif - -#define MAX_PC_CLASS (1+6+6+1+6+1+1+1+1+4023) -#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 LOCAL_REG_NUM 16 -#define LIFETIME_FLOORITEM 60 -#define DAMAGELOG_SIZE 30 -#define LOOTITEM_SIZE 10 -#define MAX_SKILL_LEVEL 100 -#define MAX_STATUSCHANGE 200 -#define MAX_SKILLUNITGROUP 32 -#define MAX_MOBSKILLUNITGROUP 8 -#define MAX_SKILLUNITGROUPTICKSET 128 -#define MAX_SKILLTIMERSKILL 32 -#define MAX_MOBSKILLTIMERSKILL 10 -#define MAX_MOBSKILL 32 -#define MAX_EVENTQUEUE 2 -#define MAX_EVENTTIMER 32 -#define NATURAL_HEAL_INTERVAL 500 -#define MAX_FLOORITEM 500000 -#define MAX_LEVEL 255 -#define MAX_WALKPATH 48 -#define MAX_DROP_PER_MAP 48 - -#define DEFAULT_AUTOSAVE_INTERVAL 60*1000 - -// [Fate] status.option properties. These are persistent status changes. -// IDs that are not listed are not used in the code (to the best of my knowledge) -#define OPTION_HIDE2 0x0002 // apparently some weaker non-GM hide -#define OPTION_CLOAK 0x0004 -#define OPTION_10 0x0010 -#define OPTION_20 0x0020 -#define OPTION_HIDE 0x0040 // [Fate] This is the GM `@hide' flag -#define OPTION_800 0x0800 -#define OPTION_INVISIBILITY 0x1000 // [Fate] Complete invisibility to other clients -#define OPTION_SCRIBE 0x2000 // [Fate] Auto-logging of nearby comments -#define OPTION_CHASEWALK 0x4000 - -// Below are special clif_changestatus() IDs reserved for option updates -#define CLIF_OPTION_SC_BASE 0x1000 -#define CLIF_OPTION_SC_INVISIBILITY (CLIF_OPTION_SC_BASE) -#define CLIF_OPTION_SC_SCRIBE (CLIF_OPTION_SC_BASE + 1) - -enum -{ BL_NUL, BL_PC, BL_NPC, BL_MOB, BL_ITEM, BL_CHAT, BL_SKILL, BL_SPELL }; -enum -{ WARP, SHOP, SCRIPT, MONS, MESSAGE }; -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 script_reg -{ - int index; - int data; -}; -struct script_regstr -{ - int index; - char data[256]; -}; -struct status_change -{ - int timer; - int val1, val2, val3, val4; - int spell_invocation; /* [Fate] If triggered by a spell, record here */ -}; - -struct invocation; - -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, range; - int target_flag; - unsigned int tick; - int limit, interval; - - int skill_id, skill_lv; - int val1, val2; - 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 group_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 item_data; -struct square; - -struct quick_regeneration -{ // [Fate] - int amount; // Amount of HP/SP left to regenerate - unsigned char speed; // less is faster (number of half-second ticks to wait between updates) - unsigned char tickdelay; // number of ticks to next update -}; - -#define VERSION_2_SKILLINFO 0x02 // client supports full skillinfo blocks - -struct map_session_data -{ - struct block_list bl; - 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 produce_flag:1; - unsigned make_arrow_flag:1; - unsigned potionpitcher_flag:1; - unsigned storage_flag:2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex] - unsigned shroud_active:1; - unsigned shroud_hides_name_talking:1; - unsigned shroud_disappears_on_pickup:1; - unsigned shroud_disappears_on_talk:1; - } 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 unbreakable_weapon:1; - unsigned unbreakable_armor:1; - unsigned infinite_autospell:1; - unsigned deaf:1; - } special_state; - int char_id, login_id1, login_id2, sex; - unsigned char tmw_version; // tmw client version - struct mmo_charstatus status; - struct item_data *inventory_data[MAX_INVENTORY]; - short equip_index[11]; - int weight, max_weight; - int cart_weight, cart_max_weight, cart_num, cart_max_num; - char mapname[24]; - int fd, new_fd; - short to_x, to_y; - short speed, prev_speed; - short opt1, opt2, opt3; - 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_pos; - int npc_menu; - int npc_amount; - int npc_stack, npc_stackmax; - char *npc_script, *npc_scriptroot; - char *npc_stackbuf; - char npc_str[256]; - struct - { - unsigned storage:1; - unsigned divorce:1; - } npc_flags; - unsigned int chatID; - - int attacktimer; - int attacktarget; - short attacktarget_lv; - unsigned int attackabletime; - - int followtimer; // [MouseJstr] - int followtarget; - - unsigned int cast_tick; // [Fate] Next tick at which spellcasting is allowed - struct invocation *active_spells; // [Fate] Singly-linked list of active spells linked to this PC - int attack_spell_override; // [Fate] When an attack spell is active for this player, they trigger it - // like a weapon. Check pc_attack_timer() for details. - short attack_spell_icon_override; // Weapon equipment slot (slot 4) item override - short attack_spell_look_override; // Weapon `look' (attack animation) override - short attack_spell_charges; // [Fate] Remaining number of charges for the attack spell - short attack_spell_delay; // [Fate] ms delay after spell attack - short attack_spell_range; // [Fate] spell range - short spellpower_bonus_target, spellpower_bonus_current; // [Fate] Spellpower boni. _current is the active one. - //_current slowly approximates _target, and _target is determined by equipment. - - short attackrange, attackrange_; - 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]; - int cloneskill_id, cloneskill_lv; - int potion_hp, potion_sp, potion_per_hp, potion_per_sp; - - // [Fate] Used for gradual healing; amount of enqueued regeneration - struct quick_regeneration quick_regeneration_hp, quick_regeneration_sp; - // [Fate] XP that can be extracted from this player by healing - int heal_xp; // i.e., OTHER players (healers) can partake in this player's XP - - int invincible_timer; - unsigned int canact_tick; - unsigned int canmove_tick; - unsigned int canlog_tick; - int hp_sub, sp_sub; - int inchealhptick, inchealsptick, inchealspirithptick, - inchealspiritsptick; -// -- moonsoul (new tick for berserk self-damage) - int berserkdamagetick; - int fame; - - short view_class; - short weapontype1, weapontype2; - short disguiseflag, disguise; // [Valaris] - int paramb[6], paramc[6], parame[6], paramcard[6]; - int hit, flee, flee2, aspd, amotion, dmotion; - int watk, watk2, atkmods[3]; - int def, def2, mdef, mdef2, critical, matk1, matk2; - int atk_ele, def_ele, star, overrefine; - int castrate, hprate, sprate, dsprate; - int addele[10], addrace[12], addsize[3], subele[10], subrace[12]; - int addeff[10], addeff2[10], reseff[10]; - int watk_, watk_2, atkmods_[3], addele_[10], addrace_[12], addsize_[3]; //二刀流のために追加 - int atk_ele_, star_, overrefine_; //二刀流のために追加 - int base_atk, atk_rate; - int arrow_atk, arrow_ele, arrow_cri, arrow_hit, arrow_range; - int arrow_addele[10], arrow_addrace[12], arrow_addsize[3], - arrow_addeff[10], arrow_addeff2[10]; - int nhealhp, nhealsp, nshealhp, nshealsp, nsshealhp, nsshealsp; - int aspd_rate, speed_rate, hprecov_rate, sprecov_rate, critical_def, - double_rate; - int near_attack_def_rate, long_attack_def_rate, magic_def_rate, - misc_def_rate; - int matk_rate, ignore_def_ele, ignore_def_race, ignore_def_ele_, - ignore_def_race_; - int ignore_mdef_ele, ignore_mdef_race; - int magic_addele[10], magic_addrace[12], magic_subrace[12]; - int perfect_hit, get_zeny_num; - int critical_rate, hit_rate, flee_rate, flee2_rate, def_rate, def2_rate, - mdef_rate, mdef2_rate; - int def_ratio_atk_ele, def_ratio_atk_ele_, def_ratio_atk_race, - def_ratio_atk_race_; - int add_damage_class_count, add_damage_class_count_, - add_magic_damage_class_count; - short add_damage_classid[10], add_damage_classid_[10], - add_magic_damage_classid[10]; - int add_damage_classrate[10], add_damage_classrate_[10], - add_magic_damage_classrate[10]; - short add_def_class_count, add_mdef_class_count; - short add_def_classid[10], add_mdef_classid[10]; - int add_def_classrate[10], add_mdef_classrate[10]; - short monster_drop_item_count; - short monster_drop_itemid[10]; - int monster_drop_race[10], monster_drop_itemrate[10]; - int double_add_rate, speed_add_rate, aspd_add_rate, perfect_hit_add, - get_zeny_add_num; - short splash_range, splash_add_range; - short autospell_id, autospell_lv, autospell_rate; - short hp_drain_rate, hp_drain_per, sp_drain_rate, sp_drain_per; - short hp_drain_rate_, hp_drain_per_, sp_drain_rate_, sp_drain_per_; - int short_weapon_damage_return, long_weapon_damage_return; - int weapon_coma_ele[10], weapon_coma_race[12]; - short break_weapon_rate, break_armor_rate; - short add_steal_rate; - - short spiritball, spiritball_old; - int spirit_timer[MAX_SKILL_LEVEL]; - int magic_damage_return; // AppleGirl Was Here - int random_attack_increase_add, random_attack_increase_per; // [Valaris] - int perfect_hiding; // [Valaris] - int unbreakable; - - int die_counter; - short doridori_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; - struct square dev; - - int trade_partner; - int deal_item_index[10]; - int deal_item_amount[10]; - int deal_zeny; - short deal_locked; - - int party_sended, party_invite, party_invite_account; - int party_hp, party_x, party_y; - - int guild_sended, guild_invite, guild_invite_account; - int guild_emblem_id, guild_alliance, guild_alliance_account; - int guildspy; // [Syrus22] - int partyspy; // [Syrus22] - - char message[80]; - - int catch_target_class; - - int pvp_point, pvp_rank, pvp_timer, pvp_lastusers; - - char eventqueue[MAX_EVENTQUEUE][50]; - int eventtimer[MAX_EVENTTIMER]; - - int last_skillid, last_skilllv; // Added by RoVeRT - struct - { - char name[24]; - } ignore[80]; - int ignoreAll; - short sg_count; - - struct - { - unsigned in_progress:1; - } auto_ban_info; - - time_t chat_reset_due; - time_t chat_repeat_reset_due; - int chat_lines_in; - int chat_total_repeats; - char chat_lastmsg[513]; - - unsigned int flood_rates[0x220]; - time_t packet_flood_reset_due; - int packet_flood_in; - - in_addr_t ip; -}; - -struct npc_timerevent_list -{ - int timer, pos; -}; -struct npc_label_list -{ - char name[24]; - int pos; -}; -struct npc_item_list -{ - int nameid, value; -}; -struct npc_data -{ - struct block_list bl; - short n; - short npc_class, dir; - short speed; - char name[24]; - char exname[24]; - int chat_id; - short opt1, opt2, opt3, option; - short flag; - union - { - struct - { - char *script; - short xs, ys; - int guild_id; - int timer, timerid, timeramount, nexttimer; - 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; - char name[16]; - } warp; - char *message; // for MESSAGE: only send this message - } u; - // ここにメンバを追加してはならない(shop_itemが可変長の為) - - char eventqueue[MAX_EVENTQUEUE][50]; - int eventtimer[MAX_EVENTTIMER]; - short arenaflag; -}; - -#define MOB_MODE_SUMMONED 0x1000 -#define MOB_MODE_TURNS_AGAINST_BAD_MASTER 0x2000 - -#define MOB_SENSIBLE_MASK 0xf000 // fate: mob mode flags that I actually understand - -enum mob_stat -{ - MOB_LV, - MOB_MAX_HP, - MOB_STR, MOB_AGI, MOB_VIT, MOB_INT, MOB_DEX, MOB_LUK, - MOB_ATK1, MOB_ATK2, // low and high attacks - MOB_ADELAY, // attack delay - MOB_DEF, MOB_MDEF, - MOB_SPEED, - // These must come last: - MOB_XP_BONUS, /* [Fate] Encoded as base to 1024: 1024 means 100% */ - MOB_LAST -}; - -#define MOB_XP_BONUS_BASE 1024 -#define MOB_XP_BONUS_SHIFT 10 - -struct mob_data -{ - struct block_list bl; - short n; - short base_class, mob_class, dir, mode; - short m, x0, y0, xs, ys; - char name[24]; - int spawndelay1, spawndelay2; - struct - { - unsigned state:8; - unsigned skillstate:8; - unsigned targettype:1; - unsigned steal_flag:1; - unsigned steal_coin_flag:1; - unsigned skillcastcancel:1; - unsigned master_check:1; - unsigned change_walk_target:1; - unsigned walk_easy:1; - unsigned special_mob_ai:3; - } state; - int timer; - short to_x, to_y; - int hp; - int target_id, attacked_id; - short target_lv; - struct walkpath_data walkpath; - unsigned int next_walktime; - unsigned int attackabletime; - unsigned int last_deadtime, last_spawntime, last_thinktime; - unsigned int canmove_tick; - short move_fail_count; - struct - { - int id; - int dmg; - } dmglog[DAMAGELOG_SIZE]; - struct item *lootitem; - short lootitem_count; - - struct status_change sc_data[MAX_STATUSCHANGE]; - short sc_count; - short opt1, opt2, opt3, option; - short min_chase; - short sg_count; - int guild_id; - int deletetimer; - - int skilltimer; - int skilltarget; - short skillx, skilly; - short skillid, skilllv, skillidx; - unsigned int skilldelay[MAX_MOBSKILL]; - int def_ele; - int master_id, master_dist; - int exclusion_src, exclusion_party, exclusion_guild; - 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]; - unsigned short stats[MOB_LAST]; // [Fate] mob-specific stats - short size; -}; - -enum -{ MS_IDLE, MS_WALK, MS_ATTACK, MS_DEAD, MS_DELAY }; - -enum -{ NONE_ATTACKABLE, ATTACKABLE }; - -enum -{ ATK_LUCKY = 1, ATK_FLEE, ATK_DEF }; // 囲まれペナルティ計算用 - -struct map_data -{ - char name[24]; - char alias[24]; // [MouseJstr] - unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う - 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:1; - 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 fog:1; // [Valaris] - unsigned sakura:1; // [Valaris] - unsigned leaves:1; // [Valaris] - unsigned rain:1; // [Valaris] - unsigned no_player_drops:1; // [Jaxad0127] - unsigned town:1; // [remoitnane] - } 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 map_data_other_server -{ - char name[24]; - unsigned char *gat; // NULL固定にして判断 - unsigned long ip; - unsigned int port; -}; -#define read_gat(m,x,y) (map[m].gat[(x)+(y)*map[m].xs]) -#define read_gatp(m,x,y) (m->gat[(x)+(y)*m->xs]) - -struct flooritem_data -{ - struct block_list bl; - short 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-58 - SP_DEAF = 70, - SP_CARTINFO = 99, // 99 - SP_GM = 500, - - // 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_SUBRACE, // 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-1077 - SP_DISGUISE, // 1077 - - SP_RESTART_FULL_RECORVER = 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 // 2006-2009 -}; - -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, /* 9 */ - LOOK_GLOVES, - LOOK_CAPE, - LOOK_MISC1, - LOOK_MISC2 -}; - -enum -{ - EQUIP_SHIELD = 8, - EQUIP_WEAPON = 9 -}; - -#define LOOK_LAST LOOK_MISC2 - -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 save_settings; -extern int agit_flag; -extern int night_flag; // 0=day, 1=night [Yor] - -extern char motd_txt[]; -extern char help_txt[]; - -extern char talkie_mes[]; - -extern char wisp_server_name[]; - -// 鯖全体情報 -void map_setusers (int); -int map_getusers (void); -// block削除関連 -int map_freeblock (void *bl); -int map_freeblock_lock (void); -int map_freeblock_unlock (void); -// block関連 -int map_addblock (struct block_list *); -int map_delblock (struct block_list *); -void map_foreachinarea (int (*)(struct block_list *, va_list), int, int, int, - int, int, int, ...); -// -- moonsoul (added map_foreachincell) -void map_foreachincell (int (*)(struct block_list *, va_list), int, int, int, - int, ...); -void map_foreachinmovearea (int (*)(struct block_list *, va_list), int, int, - int, int, int, int, int, int, ...); -int map_countnearpc (int, int, int); -//block関連に追加 -int map_count_oncell (int m, int x, int y); -// 一時的object関連 -int map_addobject (struct block_list *); -int map_delobject (int, int type); -int map_delobjectnofree (int id, int type); -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 *); - -extern FILE *map_logfile; -void map_write_log (char *format, ...); -#define MAP_LOG(format, args...) {if (map_logfile) map_write_log(format, ##args);} - -#define MAP_LOG_PC(sd, fmt, args...) MAP_LOG("PC%d %d:%d,%d " fmt, sd->status.char_id, sd->bl.m, sd->bl.x, sd->bl.y, ## args) - -// 床アイテム関連 -void map_clearflooritem_timer (timer_id, tick_t, custom_id_t, custom_data_t); -#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1) -int map_addflooritem_any (struct item *, int amount, int m, int x, int y, - struct map_session_data **owners, - int *owner_protection, - int lifetime, int dispersal); -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_id2sd (int); -struct block_list *map_id2bl (int); -int map_mapname2mapid (char *); -int map_mapname2ipport (char *, int *, int *); -int map_setipport (char *name, unsigned long ip, int port); -int map_eraseipport (char *name, unsigned long ip, int port); -void map_addiddb (struct block_list *); -void map_deliddb (struct block_list *bl); -int map_foreachiddb (db_func_t, ...); -void map_addnickdb (struct map_session_data *); -int map_scriptcont (struct map_session_data *sd, int id); /* Continues a script either on a spell or on an NPC */ -struct map_session_data *map_nick2sd (char *); -int compare_item (struct item *a, struct item *b); - -struct map_session_data *map_get_first_session (void); -struct map_session_data *map_get_last_session (void); -struct map_session_data *map_get_next_session (struct map_session_data - *current); -struct map_session_data *map_get_prev_session (struct map_session_data - *current); - -// gat関連 -int map_getcell (int, int, int); -int map_setcell (int, int, int, int); - -// その他 -int map_check_dir (int s_dir, int t_dir); -int map_calc_dir (struct block_list *src, int x, int y); - -// path.cより -int path_search (struct walkpath_data *, int, int, int, int, int, int); -int path_blownpos (int m, int x0, int y0, int dx, int dy, int count); - -int map_who (int fd); - -void map_helpscreen (void); // [Valaris] -int map_delmap (char *mapname); - -#endif diff --git a/src/map/map.hpp b/src/map/map.hpp new file mode 100644 index 0000000..5d3e0a2 --- /dev/null +++ b/src/map/map.hpp @@ -0,0 +1,822 @@ +// $Id: map.h,v 1.8 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef MAP_HPP +#define MAP_HPP + +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <sys/time.h> +#include <netinet/in.h> +#include "../common/mmo.hpp" +#include "../common/timer.hpp" +#include "../common/db.hpp" + +#ifndef MAX +# define MAX(x,y) (((x)>(y)) ? (x) : (y)) +#endif +#ifndef MIN +# define MIN(x,y) (((x)<(y)) ? (x) : (y)) +#endif + +#define MAX_PC_CLASS (1+6+6+1+6+1+1+1+1+4023) +#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 LOCAL_REG_NUM 16 +#define LIFETIME_FLOORITEM 60 +#define DAMAGELOG_SIZE 30 +#define LOOTITEM_SIZE 10 +#define MAX_SKILL_LEVEL 100 +#define MAX_STATUSCHANGE 200 +#define MAX_SKILLUNITGROUP 32 +#define MAX_MOBSKILLUNITGROUP 8 +#define MAX_SKILLUNITGROUPTICKSET 128 +#define MAX_SKILLTIMERSKILL 32 +#define MAX_MOBSKILLTIMERSKILL 10 +#define MAX_MOBSKILL 32 +#define MAX_EVENTQUEUE 2 +#define MAX_EVENTTIMER 32 +#define NATURAL_HEAL_INTERVAL 500 +#define MAX_FLOORITEM 500000 +#define MAX_LEVEL 255 +#define MAX_WALKPATH 48 +#define MAX_DROP_PER_MAP 48 + +#define DEFAULT_AUTOSAVE_INTERVAL 60*1000 + +// [Fate] status.option properties. These are persistent status changes. +// IDs that are not listed are not used in the code (to the best of my knowledge) +#define OPTION_HIDE2 0x0002 // apparently some weaker non-GM hide +#define OPTION_CLOAK 0x0004 +#define OPTION_10 0x0010 +#define OPTION_20 0x0020 +#define OPTION_HIDE 0x0040 // [Fate] This is the GM `@hide' flag +#define OPTION_800 0x0800 +#define OPTION_INVISIBILITY 0x1000 // [Fate] Complete invisibility to other clients +#define OPTION_SCRIBE 0x2000 // [Fate] Auto-logging of nearby comments +#define OPTION_CHASEWALK 0x4000 + +// Below are special clif_changestatus() IDs reserved for option updates +#define CLIF_OPTION_SC_BASE 0x1000 +#define CLIF_OPTION_SC_INVISIBILITY (CLIF_OPTION_SC_BASE) +#define CLIF_OPTION_SC_SCRIBE (CLIF_OPTION_SC_BASE + 1) + +enum +{ BL_NUL, BL_PC, BL_NPC, BL_MOB, BL_ITEM, BL_CHAT, BL_SKILL, BL_SPELL }; +enum +{ WARP, SHOP, SCRIPT, MONS, MESSAGE }; +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 script_reg +{ + int index; + int data; +}; +struct script_regstr +{ + int index; + char data[256]; +}; +struct status_change +{ + int timer; + int val1, val2, val3, val4; + int spell_invocation; /* [Fate] If triggered by a spell, record here */ +}; + +struct invocation; + +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, range; + int target_flag; + unsigned int tick; + int limit, interval; + + int skill_id, skill_lv; + int val1, val2; + 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 group_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 item_data; +struct square; + +struct quick_regeneration +{ // [Fate] + int amount; // Amount of HP/SP left to regenerate + unsigned char speed; // less is faster (number of half-second ticks to wait between updates) + unsigned char tickdelay; // number of ticks to next update +}; + +#define VERSION_2_SKILLINFO 0x02 // client supports full skillinfo blocks + +struct map_session_data +{ + struct block_list bl; + 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 produce_flag:1; + unsigned make_arrow_flag:1; + unsigned potionpitcher_flag:1; + unsigned storage_flag:2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex] + unsigned shroud_active:1; + unsigned shroud_hides_name_talking:1; + unsigned shroud_disappears_on_pickup:1; + unsigned shroud_disappears_on_talk:1; + } 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 unbreakable_weapon:1; + unsigned unbreakable_armor:1; + unsigned infinite_autospell:1; + unsigned deaf:1; + } special_state; + int char_id, login_id1, login_id2, sex; + unsigned char tmw_version; // tmw client version + struct mmo_charstatus status; + struct item_data *inventory_data[MAX_INVENTORY]; + short equip_index[11]; + int weight, max_weight; + int cart_weight, cart_max_weight, cart_num, cart_max_num; + char mapname[24]; + int fd, new_fd; + short to_x, to_y; + short speed, prev_speed; + short opt1, opt2, opt3; + 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_pos; + int npc_menu; + int npc_amount; + int npc_stack, npc_stackmax; + char *npc_script, *npc_scriptroot; + char *npc_stackbuf; + char npc_str[256]; + struct + { + unsigned storage:1; + unsigned divorce:1; + } npc_flags; + unsigned int chatID; + + int attacktimer; + int attacktarget; + short attacktarget_lv; + unsigned int attackabletime; + + int followtimer; // [MouseJstr] + int followtarget; + + unsigned int cast_tick; // [Fate] Next tick at which spellcasting is allowed + struct invocation *active_spells; // [Fate] Singly-linked list of active spells linked to this PC + int attack_spell_override; // [Fate] When an attack spell is active for this player, they trigger it + // like a weapon. Check pc_attack_timer() for details. + short attack_spell_icon_override; // Weapon equipment slot (slot 4) item override + short attack_spell_look_override; // Weapon `look' (attack animation) override + short attack_spell_charges; // [Fate] Remaining number of charges for the attack spell + short attack_spell_delay; // [Fate] ms delay after spell attack + short attack_spell_range; // [Fate] spell range + short spellpower_bonus_target, spellpower_bonus_current; // [Fate] Spellpower boni. _current is the active one. + //_current slowly approximates _target, and _target is determined by equipment. + + short attackrange, attackrange_; + 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]; + int cloneskill_id, cloneskill_lv; + int potion_hp, potion_sp, potion_per_hp, potion_per_sp; + + // [Fate] Used for gradual healing; amount of enqueued regeneration + struct quick_regeneration quick_regeneration_hp, quick_regeneration_sp; + // [Fate] XP that can be extracted from this player by healing + int heal_xp; // i.e., OTHER players (healers) can partake in this player's XP + + int invincible_timer; + unsigned int canact_tick; + unsigned int canmove_tick; + unsigned int canlog_tick; + int hp_sub, sp_sub; + int inchealhptick, inchealsptick, inchealspirithptick, + inchealspiritsptick; +// -- moonsoul (new tick for berserk self-damage) + int berserkdamagetick; + int fame; + + short view_class; + short weapontype1, weapontype2; + short disguiseflag, disguise; // [Valaris] + int paramb[6], paramc[6], parame[6], paramcard[6]; + int hit, flee, flee2, aspd, amotion, dmotion; + int watk, watk2, atkmods[3]; + int def, def2, mdef, mdef2, critical, matk1, matk2; + int atk_ele, def_ele, star, overrefine; + int castrate, hprate, sprate, dsprate; + int addele[10], addrace[12], addsize[3], subele[10], subrace[12]; + int addeff[10], addeff2[10], reseff[10]; + int watk_, watk_2, atkmods_[3], addele_[10], addrace_[12], addsize_[3]; //二刀流のために追加 + int atk_ele_, star_, overrefine_; //二刀流のために追加 + int base_atk, atk_rate; + int arrow_atk, arrow_ele, arrow_cri, arrow_hit, arrow_range; + int arrow_addele[10], arrow_addrace[12], arrow_addsize[3], + arrow_addeff[10], arrow_addeff2[10]; + int nhealhp, nhealsp, nshealhp, nshealsp, nsshealhp, nsshealsp; + int aspd_rate, speed_rate, hprecov_rate, sprecov_rate, critical_def, + double_rate; + int near_attack_def_rate, long_attack_def_rate, magic_def_rate, + misc_def_rate; + int matk_rate, ignore_def_ele, ignore_def_race, ignore_def_ele_, + ignore_def_race_; + int ignore_mdef_ele, ignore_mdef_race; + int magic_addele[10], magic_addrace[12], magic_subrace[12]; + int perfect_hit, get_zeny_num; + int critical_rate, hit_rate, flee_rate, flee2_rate, def_rate, def2_rate, + mdef_rate, mdef2_rate; + int def_ratio_atk_ele, def_ratio_atk_ele_, def_ratio_atk_race, + def_ratio_atk_race_; + int add_damage_class_count, add_damage_class_count_, + add_magic_damage_class_count; + short add_damage_classid[10], add_damage_classid_[10], + add_magic_damage_classid[10]; + int add_damage_classrate[10], add_damage_classrate_[10], + add_magic_damage_classrate[10]; + short add_def_class_count, add_mdef_class_count; + short add_def_classid[10], add_mdef_classid[10]; + int add_def_classrate[10], add_mdef_classrate[10]; + short monster_drop_item_count; + short monster_drop_itemid[10]; + int monster_drop_race[10], monster_drop_itemrate[10]; + int double_add_rate, speed_add_rate, aspd_add_rate, perfect_hit_add, + get_zeny_add_num; + short splash_range, splash_add_range; + short autospell_id, autospell_lv, autospell_rate; + short hp_drain_rate, hp_drain_per, sp_drain_rate, sp_drain_per; + short hp_drain_rate_, hp_drain_per_, sp_drain_rate_, sp_drain_per_; + int short_weapon_damage_return, long_weapon_damage_return; + int weapon_coma_ele[10], weapon_coma_race[12]; + short break_weapon_rate, break_armor_rate; + short add_steal_rate; + + short spiritball, spiritball_old; + int spirit_timer[MAX_SKILL_LEVEL]; + int magic_damage_return; // AppleGirl Was Here + int random_attack_increase_add, random_attack_increase_per; // [Valaris] + int perfect_hiding; // [Valaris] + int unbreakable; + + int die_counter; + short doridori_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; + struct square dev; + + int trade_partner; + int deal_item_index[10]; + int deal_item_amount[10]; + int deal_zeny; + short deal_locked; + + int party_sended, party_invite, party_invite_account; + int party_hp, party_x, party_y; + + int guild_sended, guild_invite, guild_invite_account; + int guild_emblem_id, guild_alliance, guild_alliance_account; + int guildspy; // [Syrus22] + int partyspy; // [Syrus22] + + char message[80]; + + int catch_target_class; + + int pvp_point, pvp_rank, pvp_timer, pvp_lastusers; + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + + int last_skillid, last_skilllv; // Added by RoVeRT + struct + { + char name[24]; + } ignore[80]; + int ignoreAll; + short sg_count; + + struct + { + unsigned in_progress:1; + } auto_ban_info; + + time_t chat_reset_due; + time_t chat_repeat_reset_due; + int chat_lines_in; + int chat_total_repeats; + char chat_lastmsg[513]; + + unsigned int flood_rates[0x220]; + time_t packet_flood_reset_due; + int packet_flood_in; + + in_addr_t ip; +}; + +struct npc_timerevent_list +{ + int timer, pos; +}; +struct npc_label_list +{ + char name[24]; + int pos; +}; +struct npc_item_list +{ + int nameid, value; +}; +struct npc_data +{ + struct block_list bl; + short n; + short npc_class, dir; + short speed; + char name[24]; + char exname[24]; + int chat_id; + short opt1, opt2, opt3, option; + short flag; + union + { + struct + { + char *script; + short xs, ys; + int guild_id; + int timer, timerid, timeramount, nexttimer; + 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; + char name[16]; + } warp; + char *message; // for MESSAGE: only send this message + } u; + // ここにメンバを追加してはならない(shop_itemが可変長の為) + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + short arenaflag; +}; + +#define MOB_MODE_SUMMONED 0x1000 +#define MOB_MODE_TURNS_AGAINST_BAD_MASTER 0x2000 + +#define MOB_SENSIBLE_MASK 0xf000 // fate: mob mode flags that I actually understand + +enum mob_stat +{ + MOB_LV, + MOB_MAX_HP, + MOB_STR, MOB_AGI, MOB_VIT, MOB_INT, MOB_DEX, MOB_LUK, + MOB_ATK1, MOB_ATK2, // low and high attacks + MOB_ADELAY, // attack delay + MOB_DEF, MOB_MDEF, + MOB_SPEED, + // These must come last: + MOB_XP_BONUS, /* [Fate] Encoded as base to 1024: 1024 means 100% */ + MOB_LAST +}; + +#define MOB_XP_BONUS_BASE 1024 +#define MOB_XP_BONUS_SHIFT 10 + +struct mob_data +{ + struct block_list bl; + short n; + short base_class, mob_class, dir, mode; + short m, x0, y0, xs, ys; + char name[24]; + int spawndelay1, spawndelay2; + struct + { + unsigned state:8; + unsigned skillstate:8; + unsigned targettype:1; + unsigned steal_flag:1; + unsigned steal_coin_flag:1; + unsigned skillcastcancel:1; + unsigned master_check:1; + unsigned change_walk_target:1; + unsigned walk_easy:1; + unsigned special_mob_ai:3; + } state; + int timer; + short to_x, to_y; + int hp; + int target_id, attacked_id; + short target_lv; + struct walkpath_data walkpath; + unsigned int next_walktime; + unsigned int attackabletime; + unsigned int last_deadtime, last_spawntime, last_thinktime; + unsigned int canmove_tick; + short move_fail_count; + struct + { + int id; + int dmg; + } dmglog[DAMAGELOG_SIZE]; + struct item *lootitem; + short lootitem_count; + + struct status_change sc_data[MAX_STATUSCHANGE]; + short sc_count; + short opt1, opt2, opt3, option; + short min_chase; + short sg_count; + int guild_id; + int deletetimer; + + int skilltimer; + int skilltarget; + short skillx, skilly; + short skillid, skilllv, skillidx; + unsigned int skilldelay[MAX_MOBSKILL]; + int def_ele; + int master_id, master_dist; + int exclusion_src, exclusion_party, exclusion_guild; + 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]; + unsigned short stats[MOB_LAST]; // [Fate] mob-specific stats + short size; +}; + +enum +{ MS_IDLE, MS_WALK, MS_ATTACK, MS_DEAD, MS_DELAY }; + +enum +{ NONE_ATTACKABLE, ATTACKABLE }; + +enum +{ ATK_LUCKY = 1, ATK_FLEE, ATK_DEF }; // 囲まれペナルティ計算用 + +struct map_data +{ + char name[24]; + char alias[24]; // [MouseJstr] + unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う + 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:1; + 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 fog:1; // [Valaris] + unsigned sakura:1; // [Valaris] + unsigned leaves:1; // [Valaris] + unsigned rain:1; // [Valaris] + unsigned no_player_drops:1; // [Jaxad0127] + unsigned town:1; // [remoitnane] + } 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 map_data_other_server +{ + char name[24]; + unsigned char *gat; // NULL固定にして判断 + unsigned long ip; + unsigned int port; +}; +#define read_gat(m,x,y) (map[m].gat[(x)+(y)*map[m].xs]) +#define read_gatp(m,x,y) (m->gat[(x)+(y)*m->xs]) + +struct flooritem_data +{ + struct block_list bl; + short 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-58 + SP_DEAF = 70, + SP_CARTINFO = 99, // 99 + SP_GM = 500, + + // 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_SUBRACE, // 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-1077 + SP_DISGUISE, // 1077 + + SP_RESTART_FULL_RECORVER = 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 // 2006-2009 +}; + +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, /* 9 */ + LOOK_GLOVES, + LOOK_CAPE, + LOOK_MISC1, + LOOK_MISC2 +}; + +enum +{ + EQUIP_SHIELD = 8, + EQUIP_WEAPON = 9 +}; + +#define LOOK_LAST LOOK_MISC2 + +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 save_settings; +extern int agit_flag; +extern int night_flag; // 0=day, 1=night [Yor] + +extern char motd_txt[]; +extern char help_txt[]; + +extern char talkie_mes[]; + +extern char wisp_server_name[]; + +// 鯖全体情報 +void map_setusers (int); +int map_getusers (void); +// block削除関連 +int map_freeblock (void *bl); +int map_freeblock_lock (void); +int map_freeblock_unlock (void); +// block関連 +int map_addblock (struct block_list *); +int map_delblock (struct block_list *); +void map_foreachinarea (int (*)(struct block_list *, va_list), int, int, int, + int, int, int, ...); +// -- moonsoul (added map_foreachincell) +void map_foreachincell (int (*)(struct block_list *, va_list), int, int, int, + int, ...); +void map_foreachinmovearea (int (*)(struct block_list *, va_list), int, int, + int, int, int, int, int, int, ...); +int map_countnearpc (int, int, int); +//block関連に追加 +int map_count_oncell (int m, int x, int y); +// 一時的object関連 +int map_addobject (struct block_list *); +int map_delobject (int, int type); +int map_delobjectnofree (int id, int type); +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 *); + +extern FILE *map_logfile; +void map_write_log (char *format, ...); +#define MAP_LOG(format, args...) {if (map_logfile) map_write_log(format, ##args);} + +#define MAP_LOG_PC(sd, fmt, args...) MAP_LOG("PC%d %d:%d,%d " fmt, sd->status.char_id, sd->bl.m, sd->bl.x, sd->bl.y, ## args) + +// 床アイテム関連 +void map_clearflooritem_timer (timer_id, tick_t, custom_id_t, custom_data_t); +#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1) +int map_addflooritem_any (struct item *, int amount, int m, int x, int y, + struct map_session_data **owners, + int *owner_protection, + int lifetime, int dispersal); +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_id2sd (int); +struct block_list *map_id2bl (int); +int map_mapname2mapid (char *); +int map_mapname2ipport (char *, int *, int *); +int map_setipport (char *name, unsigned long ip, int port); +int map_eraseipport (char *name, unsigned long ip, int port); +void map_addiddb (struct block_list *); +void map_deliddb (struct block_list *bl); +int map_foreachiddb (db_func_t, ...); +void map_addnickdb (struct map_session_data *); +int map_scriptcont (struct map_session_data *sd, int id); /* Continues a script either on a spell or on an NPC */ +struct map_session_data *map_nick2sd (char *); +int compare_item (struct item *a, struct item *b); + +struct map_session_data *map_get_first_session (void); +struct map_session_data *map_get_last_session (void); +struct map_session_data *map_get_next_session (struct map_session_data + *current); +struct map_session_data *map_get_prev_session (struct map_session_data + *current); + +// gat関連 +int map_getcell (int, int, int); +int map_setcell (int, int, int, int); + +// その他 +int map_check_dir (int s_dir, int t_dir); +int map_calc_dir (struct block_list *src, int x, int y); + +// path.cより +int path_search (struct walkpath_data *, int, int, int, int, int, int); +int path_blownpos (int m, int x0, int y0, int dx, int dy, int count); + +int map_who (int fd); + +void map_helpscreen (void); // [Valaris] +int map_delmap (char *mapname); + +#endif diff --git a/src/map/mob.c b/src/map/mob.c deleted file mode 100644 index 7be069e..0000000 --- a/src/map/mob.c +++ /dev/null @@ -1,5070 +0,0 @@ -// $Id: mob.c,v 1.7 2004/09/25 05:32:18 MouseJstr Exp $ -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> - -#include "../common/timer.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/nullpo.h" -#include "../common/mt_rand.h" -#include "map.h" -#include "clif.h" -#include "intif.h" -#include "pc.h" -#include "mob.h" -#include "guild.h" -#include "itemdb.h" -#include "skill.h" -#include "battle.h" -#include "party.h" -#include "npc.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#ifndef max -#define max( a, b ) ( ((a) > (b)) ? (a) : (b) ) -#endif - -#define MIN_MOBTHINKTIME 100 - -#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) - -struct mob_db mob_db[2001]; - -/*========================================== - * Local prototype declaration (only required thing) - *------------------------------------------ - */ -static int distance (int, int, int, int); -static int mob_makedummymobdb (int); -static void mob_timer (timer_id, tick_t, custom_id_t, custom_data_t); -int mobskill_use (struct mob_data *md, unsigned int tick, int event); -int mobskill_deltimer (struct mob_data *md); -int mob_skillid2skillidx (int mob_class, int skillid); -int mobskill_use_id (struct mob_data *md, struct block_list *target, - int skill_idx); -static int mob_unlocktarget (struct mob_data *md, int tick); - -/*========================================== - * Mob is searched with a name. - *------------------------------------------ - */ -int mobdb_searchname (const char *str) -{ - int i; - - for (i = 0; i < sizeof (mob_db) / sizeof (mob_db[0]); i++) - { - if (strcasecmp (mob_db[i].name, str) == 0 - || strcmp (mob_db[i].jname, str) == 0 - || memcmp (mob_db[i].name, str, 24) == 0 - || memcmp (mob_db[i].jname, str, 24) == 0) - return i; - } - - return 0; -} - -/*========================================== - * Id Mob is checked. - *------------------------------------------ - */ -int mobdb_checkid (const int id) -{ - if (id <= 0 || id >= (sizeof (mob_db) / sizeof (mob_db[0])) - || mob_db[id].name[0] == '\0') - return 0; - - return id; -} - -static void mob_init (struct mob_data *md); - -/*========================================== - * The minimum data set for MOB spawning - *------------------------------------------ - */ -int mob_spawn_dataset (struct mob_data *md, const char *mobname, int mob_class) -{ - nullpo_retr (0, md); - - if (strcmp (mobname, "--en--") == 0) - memcpy (md->name, mob_db[mob_class].name, 24); - else if (strcmp (mobname, "--ja--") == 0) - memcpy (md->name, mob_db[mob_class].jname, 24); - else - memcpy (md->name, mobname, 24); - - md->bl.prev = NULL; - md->bl.next = NULL; - md->n = 0; - md->base_class = md->mob_class = mob_class; - 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; - - mob_init (md); - - return 0; -} - -// Mutation values indicate how `valuable' a change to each stat is, XP wise. -// For one 256th of change, we give out that many 1024th fractions of XP change -// (i.e., 1024 means a 100% XP increase for a single point of adjustment, 4 means 100% XP bonus for doubling the value) -static int mutation_value[MOB_XP_BONUS] = { - 2, // MOB_LV - 3, // MOB_MAX_HP - 1, // MOB_STR - 2, // MOB_AGI - 1, // MOB_VIT - 0, // MOB_INT - 2, // MOB_DEX - 2, // MOB_LUK - 1, // MOB_ATK1 - 1, // MOB_ATK2 - 2, // MOB_ADELAY - 2, // MOB_DEF - 2, // MOB_MDEF - 2, // MOB_SPEED -}; - -// The mutation scale indicates how far `up' we can go, with 256 indicating 100% Note that this may stack with multiple -// calls to `mutate'. -static int mutation_scale[MOB_XP_BONUS] = { - 16, // MOB_LV - 256, // MOB_MAX_HP - 32, // MOB_STR - 48, // MOB_AGI - 48, // MOB_VIT - 48, // MOB_INT - 48, // MOB_DEX - 64, // MOB_LUK - 48, // MOB_ATK1 - 48, // MOB_ATK2 - 80, // MOB_ADELAY - 48, // MOB_DEF - 48, // MOB_MDEF - 80, // MOB_SPEED -}; - -// The table below indicates the `average' value for each of the statistics, or -1 if there is none. -// This average is used to determine XP modifications for mutations. The experience point bonus is -// based on mutation_value and mutation_base as follows: -// (1) first, compute the percentage change of the attribute (p0) -// (2) second, determine the absolute stat change -// (3) third, compute the percentage stat change relative to mutation_base (p1) -// (4) fourth, compute the XP mofication based on the smaller of (p0, p1). -static int mutation_base[MOB_XP_BONUS] = { - 30, // MOB_LV - -1, // MOB_MAX_HP - 20, // MOB_STR - 20, // MOB_AGI - 20, // MOB_VIT - 20, // MOB_INT - 20, // MOB_DEX - 20, // MOB_LUK - -1, // MOB_ATK1 - -1, // MOB_ATK2 - -1, // MOB_ADELAY - -1, // MOB_DEF - 20, // MOB_MDEF - -1, // MOB_SPEED -}; - -/*======================================== - * Mutates a MOB. For large `direction' values, calling this multiple times will give bigger XP boni. - *---------------------------------------- - */ -static void mob_mutate (struct mob_data *md, int stat, int intensity) // intensity: positive: strengthen, negative: weaken. 256 = 100%. -{ - int old_stat; - int new_stat; - int real_intensity; // relative intensity - const int mut_base = mutation_base[stat]; - int sign = 1; - - if (!md || stat < 0 || stat >= MOB_XP_BONUS || intensity == 0) - return; - - while (intensity > mutation_scale[stat]) - { - mob_mutate (md, stat, mutation_scale[stat]); // give better XP assignments - intensity -= mutation_scale[stat]; - } - while (intensity < -mutation_scale[stat]) - { - mob_mutate (md, stat, mutation_scale[stat]); // give better XP assignments - intensity += mutation_scale[stat]; - } - - if (!intensity) - return; - - // MOB_ADELAY and MOB_SPEED are special because going DOWN is good here. - if (stat == MOB_ADELAY || stat == MOB_SPEED) - sign = -1; - - // Now compute the new stat - old_stat = md->stats[stat]; - new_stat = old_stat + ((old_stat * sign * intensity) / 256); - - if (new_stat < 0) - new_stat = 0; - - if (old_stat == 0) - real_intensity = 0; - else - real_intensity = (((new_stat - old_stat) << 8) / old_stat); - - if (mut_base != -1) - { - // Now compute the mutation intensity relative to an absolute value. - // Take the lesser of the two effects. - int real_intensity2 = (((new_stat - old_stat) << 8) / mut_base); - - if (real_intensity < 0) - if (real_intensity2 > real_intensity) - real_intensity = real_intensity2; - - if (real_intensity > 0) - if (real_intensity2 < real_intensity) - real_intensity = real_intensity2; - } - - real_intensity *= sign; - - md->stats[stat] = new_stat; - - // Adjust XP value - md->stats[MOB_XP_BONUS] += mutation_value[stat] * real_intensity; - if (md->stats[MOB_XP_BONUS] <= 0) - md->stats[MOB_XP_BONUS] = 1; - - // Sanitise - if (md->stats[MOB_ATK1] > md->stats[MOB_ATK2]) - { - int swap = md->stats[MOB_ATK2]; - md->stats[MOB_ATK2] = md->stats[MOB_ATK1]; - md->stats[MOB_ATK1] = swap; - } -} - -// This calculates the exp of a given mob -int mob_gen_exp (struct mob_db *mob) -{ - if (mob->max_hp <= 1) - return 1; - double mod_def = 100 - mob->def; - if (mod_def == 0) - mod_def = 1; - double effective_hp = - ((50 - mob->luk) * mob->max_hp / 50.0) + - (2 * mob->luk * mob->max_hp / mod_def); - double attack_factor = - (mob->atk1 + mob->atk2 + mob->str / 3.0 + mob->dex / 2.0 + - mob->luk) * (1872.0 / mob->adelay) / 4; - double dodge_factor = - pow (mob->lv + mob->agi + mob->luk / 2.0, 4.0 / 3.0); - double persuit_factor = - (3 + mob->range) * (mob->mode % 2) * 1000 / mob->speed; - double aggression_factor = (mob->mode & 4) == 4 ? 10.0 / 9.0 : 1.0; - int xp = - (int) floor (effective_hp * - pow (sqrt (attack_factor) + sqrt (dodge_factor) + - sqrt (persuit_factor) + 55, - 3) * aggression_factor / 2000000.0 * - (double) battle_config.base_exp_rate / 100.); - if (xp < 1) - xp = 1; - printf ("Exp for mob '%s' generated: %d\n", mob->name, xp); - return xp; -} - -static void mob_init (struct mob_data *md) -{ - int i; - const int mob_class = md->mob_class; - const int mutations_nr = mob_db[mob_class].mutations_nr; - const int mutation_power = mob_db[mob_class].mutation_power; - - md->stats[MOB_LV] = mob_db[mob_class].lv; - md->stats[MOB_MAX_HP] = mob_db[mob_class].max_hp; - md->stats[MOB_STR] = mob_db[mob_class].str; - md->stats[MOB_AGI] = mob_db[mob_class].agi; - md->stats[MOB_VIT] = mob_db[mob_class].vit; - md->stats[MOB_INT] = mob_db[mob_class].int_; - md->stats[MOB_DEX] = mob_db[mob_class].dex; - md->stats[MOB_LUK] = mob_db[mob_class].luk; - md->stats[MOB_ATK1] = mob_db[mob_class].atk1; - md->stats[MOB_ATK2] = mob_db[mob_class].atk2; - md->stats[MOB_ADELAY] = mob_db[mob_class].adelay; - md->stats[MOB_DEF] = mob_db[mob_class].def; - md->stats[MOB_MDEF] = mob_db[mob_class].mdef; - md->stats[MOB_SPEED] = mob_db[mob_class].speed; - md->stats[MOB_XP_BONUS] = MOB_XP_BONUS_BASE; - - for (i = 0; i < mutations_nr; i++) - { - int stat_nr = MRAND (MOB_XP_BONUS + 1); - int strength; - - if (stat_nr >= MOB_XP_BONUS) - stat_nr = MOB_MAX_HP; - - strength = - ((MRAND ((mutation_power >> 1)) + - (MRAND ((mutation_power >> 1))) + - 2) * mutation_scale[stat_nr]) / 100; - - strength = MRAND (2) ? strength : -strength; - - if (strength < -240) - strength = -240; /* Don't go too close to zero */ - - mob_mutate (md, stat_nr, strength); - } -} - -/*========================================== - * 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 mob_class, int amount, - const char *event) -{ - struct mob_data *md = NULL; - int m, count, lv = 255, r = mob_class; - - 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 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) // 値が異常なら召喚を止める - return 0; - - if (mob_class < 0) - { // ランダムに召喚 - int i = 0; - int j = -mob_class - 1; - int k; - if (j >= 0 && j < MAX_RANDOMMONSTER) - { - do - { - mob_class = MPRAND (1001, 1000); - k = MRAND (1000000); - } - while ((mob_db[mob_class].max_hp <= 0 - || mob_db[mob_class].summonper[j] <= k - || (lv < mob_db[mob_class].lv - && battle_config.random_monster_checklv == 1)) - && (i++) < 2000); - if (i >= 2000) - { - mob_class = mob_db[0].summonper[j]; - } - } - else - { - return 0; - } -// if(battle_config.etc_log==1) -// printf("mobmob_class=%d try=%d\n",mob_class,i); - } - if (sd) - { - if (x <= 0) - x = sd->bl.x; - if (y <= 0) - y = sd->bl.y; - } - else if (x <= 0 || y <= 0) - { - printf ("mob_once_spawn: ??\n"); - } - - for (count = 0; count < amount; count++) - { - md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); - if (mob_db[mob_class].mode & 0x02) - md->lootitem = - (struct item *) calloc (LOOTITEM_SIZE, sizeof (struct item)); - else - md->lootitem = NULL; - - mob_spawn_dataset (md, mobname, mob_class); - md->bl.m = m; - md->bl.x = x; - md->bl.y = y; - if (r < 0 && battle_config.dead_branch_active == 1) - md->mode = 0x1 + 0x4 + 0x80; //移動してアクティブで反撃する - 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. - - memcpy (md->npc_event, event, sizeof (md->npc_event)); - - md->bl.type = BL_MOB; - map_addiddb (&md->bl); - mob_spawn (md->bl.id); - - if (mob_class == 1288) - { // emperium hp based on defense level [Valaris] - struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); - if (gc) - { - mob_db[mob_class].max_hp += 2000 * gc->defense; - md->hp = mob_db[mob_class].max_hp; - } - } // 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 mob_class, int amount, - const char *event) -{ - int x, y, i, c, 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_class >= 0 && mob_class <= 1000) || mob_class > 2000) // A summon is stopped if a value is unusual - return 0; - - for (i = 0; i < amount; i++) - { - int j = 0; - do - { - x = MPRAND (x0, (x1 - x0 + 1)); - y = MPRAND (y0, (y1 - y0 + 1)); - } - 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. - } - id = mob_once_spawn (sd, mapname, x, y, mobname, mob_class, 1, event); - lx = x; - ly = y; - } - return id; -} - -/*========================================== - * Summoning Guardians [Valaris] - *------------------------------------------ - */ -int mob_spawn_guardian (struct map_session_data *sd, char *mapname, - int x, int y, const char *mobname, int mob_class, - int amount, const char *event, int guardian) -{ - struct mob_data *md = NULL; - int m, count = 1, lv = 255; - - 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 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) // 値が異常なら召喚を止める - return 0; - - if (mob_class < 0) - return 0; - - if (sd) - { - if (x <= 0) - x = sd->bl.x; - if (y <= 0) - y = sd->bl.y; - } - - else if (x <= 0 || y <= 0) - printf ("mob_spawn_guardian: ??\n"); - - for (count = 0; count < amount; count++) - { - struct guild_castle *gc; - CREATE (md, struct mob_data, 1); - - mob_spawn_dataset (md, mobname, mob_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. - - memcpy (md->npc_event, event, sizeof (md->npc_event)); - - md->bl.type = BL_MOB; - map_addiddb (&md->bl); - mob_spawn (md->bl.id); - - gc = guild_mapname2gc (map[md->bl.m].name); - if (gc) - { - mob_db[mob_class].max_hp += 2000 * gc->defense; - if (guardian == 0) - { - md->hp = gc->Ghp0; - gc->GID0 = md->bl.id; - } - if (guardian == 1) - { - md->hp = gc->Ghp1; - gc->GID1 = md->bl.id; - } - if (guardian == 2) - { - md->hp = gc->Ghp2; - gc->GID2 = md->bl.id; - } - if (guardian == 3) - { - md->hp = gc->Ghp3; - gc->GID3 = md->bl.id; - } - if (guardian == 4) - { - md->hp = gc->Ghp4; - gc->GID4 = md->bl.id; - } - if (guardian == 5) - { - md->hp = gc->Ghp5; - gc->GID5 = md->bl.id; - } - if (guardian == 6) - { - md->hp = gc->Ghp6; - gc->GID6 = md->bl.id; - } - if (guardian == 7) - { - md->hp = gc->Ghp7; - gc->GID7 = md->bl.id; - } - - } - } - - return (amount > 0) ? md->bl.id : 0; -} - -/*========================================== - * Appearance income of mob - *------------------------------------------ - */ -int mob_get_viewclass (int mob_class) -{ - return mob_db[mob_class].view_class; -} - -int mob_get_sex (int mob_class) -{ - return mob_db[mob_class].sex; -} - -short mob_get_hair (int mob_class) -{ - return mob_db[mob_class].hair; -} - -short mob_get_hair_color (int mob_class) -{ - return mob_db[mob_class].hair_color; -} - -short mob_get_weapon (int mob_class) -{ - return mob_db[mob_class].weapon; -} - -short mob_get_shield (int mob_class) -{ - return mob_db[mob_class].shield; -} - -short mob_get_head_top (int mob_class) -{ - return mob_db[mob_class].head_top; -} - -short mob_get_head_mid (int mob_class) -{ - return mob_db[mob_class].head_mid; -} - -short mob_get_head_buttom (int mob_class) -{ - return mob_db[mob_class].head_buttom; -} - -short mob_get_clothes_color (int mob_class) // Add for player monster dye - Valaris -{ - return mob_db[mob_class].clothes_color; // End -} - -int mob_get_equip (int mob_class) // mob equip [Valaris] -{ - return mob_db[mob_class].equip; -} - -/*========================================== - * 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 (md->canmove_tick > gettick () || (md->opt1 > 0 && md->opt1 != 6) - || md->option & 2) - 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 //スパイダーウェッブ - ) - 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 battle_get_speed (&md->bl) * 14 / 10; - return battle_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, ctype; - 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; - ctype = map_getcell (md->bl.m, x, y); - if (ctype == 1 || ctype == 5) - { - mob_stop_walking (md, 1); - return 0; - } - md->dir = md->walkpath.path[md->walkpath.path_pos]; - dx = dirx[md->dir]; - dy = diry[md->dir]; - - ctype = map_getcell (md->bl.m, x + dx, y + dy); - if (ctype == 1 || ctype == 5) - { - 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); - - x += dx; - y += dy; - if (md->min_chase > 13) - md->min_chase--; - - if (moveblock) - map_delblock (&md->bl); - md->bl.x = x; - md->bl.y = y; - if (moveblock) - map_addblock (&md->bl); - - 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); - - skill_unit_move (&md->bl, tick, 1); // Inspection of a skill unit - } - if ((i = calc_next_walk_step (md)) > 0) - { - i = i >> 1; - if (i < 1 && md->walkpath.path_half == 0) - i = 1; - md->timer = - add_timer (tick + i, mob_timer, md->bl.id, md->walkpath.path_pos); - md->state.state = MS_WALK; - - if (md->walkpath.path_pos >= md->walkpath.path_len) - clif_fixmobpos (md); // When mob stops, retransmission current of a position. - } - return 0; -} - -/*========================================== - * Check if mob should be attempting to attack - *------------------------------------------ - */ -static int mob_check_attack (struct mob_data *md) -{ - struct block_list *tbl = NULL; - struct map_session_data *tsd = NULL; - struct mob_data *tmd = NULL; - - int mode, race, range; - - nullpo_retr (0, md); - - md->min_chase = 13; - md->state.state = MS_IDLE; - md->state.skillstate = MSS_IDLE; - - if (md->skilltimer != -1) - return 0; - - if (md->opt1 > 0 || md->option & 2) - return 0; - - if (md->sc_data[SC_AUTOCOUNTER].timer != -1) - return 0; - - if (md->sc_data[SC_BLADESTOP].timer != -1) - return 0; - - if ((tbl = map_id2bl (md->target_id)) == NULL) - { - md->target_id = 0; - md->state.targettype = NONE_ATTACKABLE; - return 0; - } - - if (tbl->type == BL_PC) - tsd = (struct map_session_data *) tbl; - else if (tbl->type == BL_MOB) - tmd = (struct mob_data *) tbl; - else - return 0; - - if (tsd) - { - if (pc_isdead (tsd) || tsd->invincible_timer != -1 - || pc_isinvisible (tsd) || md->bl.m != tbl->m || tbl->prev == NULL - || distance (md->bl.x, md->bl.y, tbl->x, tbl->y) >= 13) - { - md->target_id = 0; - md->state.targettype = NONE_ATTACKABLE; - return 0; - } - } - if (tmd) - { - if (md->bl.m != tbl->m || tbl->prev == NULL - || distance (md->bl.x, md->bl.y, tbl->x, tbl->y) >= 13) - { - md->target_id = 0; - md->state.targettype = NONE_ATTACKABLE; - return 0; - } - } - - if (!md->mode) - mode = mob_db[md->mob_class].mode; - else - mode = md->mode; - - race = mob_db[md->mob_class].race; - if (!(mode & 0x80)) - { - md->target_id = 0; - md->state.targettype = NONE_ATTACKABLE; - return 0; - } - if (tsd && !(mode & 0x20) && (tsd->sc_data[SC_TRICKDEAD].timer != -1 || - ((pc_ishiding (tsd) - || tsd->state.gangsterparadise) - && race != 4 && race != 6))) - { - md->target_id = 0; - md->state.targettype = NONE_ATTACKABLE; - return 0; - } - - range = mob_db[md->mob_class].range; - if (mode & 1) - range++; - if (distance (md->bl.x, md->bl.y, tbl->x, tbl->y) > range) - return 0; - - return 1; -} - -static int mob_ancillary_attack(struct block_list *bl, va_list ap) -{ - struct block_list *mdbl = va_arg(ap, struct block_list *); - struct block_list *tbl = va_arg(ap, struct block_list *); - unsigned int tick = va_arg(ap, unsigned int); - if (bl != tbl) - battle_weapon_attack(mdbl, bl, tick, 0); - return 0; -} - -/*========================================== - * Attack processing of mob - *------------------------------------------ - */ -static int mob_attack (struct mob_data *md, unsigned int tick, int data) -{ - struct block_list *tbl = NULL; - - nullpo_retr (0, md); - - if ((tbl = map_id2bl (md->target_id)) == NULL) - return 0; - - if (!mob_check_attack (md)) - return 0; - - if (battle_config.monster_attack_direction_change) - md->dir = map_calc_dir (&md->bl, tbl->x, tbl->y); // 向き設定 - - //clif_fixmobpos(md); - - md->state.skillstate = MSS_ATTACK; - if (mobskill_use (md, tick, -2)) // スキル使用 - return 0; - - md->target_lv = battle_weapon_attack (&md->bl, tbl, tick, 0); - // If you are reading this, please note: - // it is highly platform-specific that this even works at all. - int radius = battle_config.mob_splash_radius; - if (radius >= 0 && tbl->type == BL_PC && !map[tbl->m].flag.town) - map_foreachinarea(mob_ancillary_attack, - tbl->m, tbl->x - radius, tbl->y - radius, tbl->x + radius, tbl->y + radius, - BL_PC, - &md->bl, tbl, tick); - - if (!(battle_config.monster_cloak_check_type & 2) - && md->sc_data[SC_CLOAKING].timer != -1) - skill_status_change_end (&md->bl, SC_CLOAKING, -1); - - md->attackabletime = tick + battle_get_adelay (&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 + battle_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_out_all (&md->bl, gettick (), 1); - skill_status_change_clear (&md->bl, 2); // The abnormalities in status are canceled. - skill_clear_unitgroup (&md->bl); // All skill unit groups are deleted. - 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 = 0; - md->state.targettype = NONE_ATTACKABLE; - break; - } - - return 0; -} - -/*========================================== - * timer processing of mob (timer function) - * It branches to a walk and an attack. - *------------------------------------------ - */ -static void mob_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct mob_data *md; - struct block_list *bl; - - if ((bl = map_id2bl (id)) == NULL) - { //攻撃してきた敵がもういないのは正常のようだ - return; - } - - if (!bl || !bl->type || bl->type != BL_MOB) - return; - - nullpo_retv (md = (struct mob_data *) bl); - - if (!md->bl.type || md->bl.type != BL_MOB) - return; - - if (md->timer != tid) - { - if (battle_config.error_log == 1) - printf ("mob_timer %d != %d\n", md->timer, tid); - return; - } - md->timer = -1; - if (md->bl.prev == NULL || md->state.state == MS_DEAD) - return; - - map_freeblock_lock (); - switch (md->state.state) - { - case MS_WALK: - mob_check_attack (md); - 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 == 1) - printf ("mob_timer : %d ?\n", md->state.state); - break; - } - map_freeblock_unlock (); - return; -} - -/*========================================== - * - *------------------------------------------ - */ -static int mob_walktoxy_sub (struct mob_data *md) -{ - struct walkpath_data wpd; - - nullpo_retr (0, md); - - 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; - 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->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->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 void mob_delayspawn (timer_id tid, tick_t tick, custom_id_t m, custom_data_t n) -{ - mob_spawn (m); -} - -/*========================================== - * 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) - return -1; - - if (!bl || !bl->type || bl->type != BL_MOB) - return -1; - - nullpo_retr (-1, md = (struct mob_data *) bl); - - if (!md || md->bl.type != BL_MOB) - return -1; - - // Processing of MOB which is not revitalized - if (md->spawndelay1 == -1 && md->spawndelay2 == -1 && md->n == 0) - { - map_deliddb (&md->bl); - if (md->lootitem) - { - map_freeblock (md->lootitem); - md->lootitem = NULL; - } - map_freeblock (md); // Instead of [ of free ] - return 0; - } - - spawntime1 = md->last_spawntime + md->spawndelay1; - spawntime2 = md->last_deadtime + md->spawndelay2; - spawntime3 = gettick () + 5000; - // 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; -} - -/*========================================== - * Mob spawning. Initialization is also variously here. - *------------------------------------------ - */ -int mob_spawn (int id) -{ - int x = 0, y = 0, i = 0, c; - unsigned int tick = gettick (); - struct mob_data *md; - struct block_list *bl; - - nullpo_retr (-1, bl = map_id2bl (id)); - - if (!bl || !bl->type || bl->type != BL_MOB) - return -1; - - nullpo_retr (-1, md = (struct mob_data *) bl); - - if (!md || !md->bl.type || md->bl.type != BL_MOB) - return -1; - - md->last_spawntime = tick; - if (md->bl.prev != NULL) - { -// clif_clearchar_area(&md->bl,3); - skill_unit_out_all (&md->bl, gettick (), 1); - map_delblock (&md->bl); - } - else - md->mob_class = md->base_class; - - md->bl.m = md->m; - do - { - if (md->x0 == 0 && md->y0 == 0) - { - x = MPRAND (1, (map[md->bl.m].xs - 2)); - y = MPRAND (1, (map[md->bl.m].ys - 2)); - } - else - { - x = MPRAND (md->x0, (md->xs + 1)) - md->xs / 2; - y = MPRAND (md->y0, (md->ys + 1)) - md->ys / 2; - } - i++; - } - while (((c = map_getcell (md->bl.m, x, y)) == 1 || c == 5) && i < 50); - - if (i >= 50) - { -// if(battle_config.error_log==1) -// printf("MOB spawn error %d @ %s\n",id,map[md->bl.m].name); - 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; - - map_addblock (&md->bl); - - memset (&md->state, 0, sizeof (md->state)); - md->attacked_id = 0; - md->target_id = 0; - md->move_fail_count = 0; - mob_init (md); - - if (!md->stats[MOB_SPEED]) - md->stats[MOB_SPEED] = mob_db[md->mob_class].speed; - md->def_ele = mob_db[md->mob_class].element; - 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 + MPRAND (5000, 50); - md->attackabletime = tick; - md->canmove_tick = tick; - - md->sg_count = 0; - 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)); - 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; - - memset (md->skillunit, 0, sizeof (md->skillunit)); - memset (md->skillunittick, 0, sizeof (md->skillunittick)); - - md->hp = battle_get_max_hp (&md->bl); - if (md->hp <= 0) - { - mob_makedummymobdb (md->mob_class); - md->hp = battle_get_max_hp (&md->bl); - } - - clif_spawnmob (md); - - return 0; -} - -/*========================================== - * Distance calculation between two points - *------------------------------------------ - */ -static int distance (int x0, int y0, int x1, int y1) -{ - int dx, dy; - - dx = abs (x0 - x1); - dy = abs (y0 - y1); - return dx > dy ? dx : dy; -} - -/*========================================== - * 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; - 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 & 4) - { - 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); - if (type & 0x02) - { - int delay = battle_get_dmotion (&md->bl); - unsigned int tick = gettick (); - if (md->canmove_tick < tick) - md->canmove_tick = tick + delay; - } - - return 0; -} - -/*========================================== - * Reachability to a Specification ID existence place - *------------------------------------------ - */ -int mob_can_reach (struct mob_data *md, struct block_list *bl, int range) -{ - int dx, dy; - struct walkpath_data wpd; - int i; - - nullpo_retr (0, md); - nullpo_retr (0, bl); - - dx = abs (bl->x - md->bl.x); - dy = abs (bl->y - md->bl.y); - - //=========== guildcastle guardian no search start=========== - //when players are the guild castle member not attack them ! - if (md->mob_class == 1285 || md->mob_class == 1286 || md->mob_class == 1287) - { - struct map_session_data *sd; - struct guild *g = NULL; - struct guild_castle *gc = guild_mapname2gc (map[bl->m].name); - - if (gc && agit_flag == 0) // Guardians will not attack during non-woe time [Valaris] - return 0; // end addition [Valaris] - - if (bl && bl->type == BL_PC) - { - if ((sd = (struct map_session_data *) bl) == NULL) - { - printf ("mob_can_reach nullpo\n"); - return 0; - } - - if (gc && sd && sd->status.guild_id && sd->status.guild_id > 0) - { - g = guild_search (sd->status.guild_id); // don't attack guild members [Valaris] - if (g && g->guild_id > 0 && g->guild_id == gc->guild_id) - return 0; - if (g && gc && guild_isallied (g, gc)) - return 0; - - } - } - } - //========== guildcastle guardian no search eof============== - - if (bl && bl->type == BL_PC && battle_config.monsters_ignore_gm == 1) - { // option to have monsters ignore GMs [Valaris] - struct map_session_data *sd; - if ((sd = (struct map_session_data *) bl) != NULL && pc_isGM (sd)) - return 0; - } - - if (md->bl.m != bl->m) // 違うャbプ - return 0; - - if (range > 0 && range < ((dx > dy) ? dx : dy)) // 遠すぎる - return 0; - - if (md->bl.x == bl->x && md->bl.y == bl->y) // 同じャX - return 1; - - // 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, 0) != - -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, 0) != -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, 0) != -1) - return 1; - } - return 0; -} - -/*========================================== - * Determination for an attack of a monster - *------------------------------------------ - */ -int mob_target (struct mob_data *md, struct block_list *bl, int dist) -{ - struct map_session_data *sd; - struct status_change *sc_data; - short *option; - int mode, race; - - nullpo_retr (0, md); - nullpo_retr (0, bl); - - sc_data = battle_get_sc_data (bl); - option = battle_get_option (bl); - race = mob_db[md->mob_class].race; - - if (!md->mode) - { - mode = mob_db[md->mob_class].mode; - } - else - { - mode = md->mode; - } - if (!(mode & 0x80)) - { - md->target_id = 0; - return 0; - } - // 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) - && (!(mode & 0x04) || MRAND (100) > 25)) - return 0; - - if (mode & 0x20 || // Coercion is exerted if it is MVPMOB. - (sc_data && sc_data[SC_TRICKDEAD].timer == -1 && - ((option && !(*option & 0x06)) || race == 4 || race == 6))) - { - if (bl->type == BL_PC) - { - nullpo_retr (0, sd = (struct map_session_data *) bl); - if (sd->invincible_timer != -1 || pc_isinvisible (sd)) - return 0; - if (!(mode & 0x20) && race != 4 && race != 6 - && sd->state.gangsterparadise) - 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; - md->min_chase = dist + 13; - 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 map_session_data *tsd = NULL; - struct mob_data *smd, *tmd = NULL; - int mode, race, dist, *pcc; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, smd = va_arg (ap, struct mob_data *)); - nullpo_retr (0, pcc = va_arg (ap, int *)); - - if (bl->type == BL_PC) - tsd = (struct map_session_data *) bl; - else if (bl->type == BL_MOB) - tmd = (struct mob_data *) bl; - else - return 0; - - //敵味方判定 - if (battle_check_target (&smd->bl, bl, BCT_ENEMY) == 0) - return 0; - - if (!smd->mode) - mode = mob_db[smd->mob_class].mode; - else - mode = smd->mode; - - // アクティブでターゲット射程内にいるなら、ロックする - if (mode & 0x04) - { - race = mob_db[smd->mob_class].race; - //対象がPCの場合 - if (tsd && - !pc_isdead (tsd) && - tsd->bl.m == smd->bl.m && - tsd->invincible_timer == -1 && - !pc_isinvisible (tsd) && - (dist = - distance (smd->bl.x, smd->bl.y, tsd->bl.x, tsd->bl.y)) < 9) - { - if (mode & 0x20 || - (tsd->sc_data[SC_TRICKDEAD].timer == -1 && - ((!pc_ishiding (tsd) && !tsd->state.gangsterparadise) - || race == 4 || race == 6))) - { // 妨害がないか判定 - if (mob_can_reach (smd, bl, 12) && // 到達可能性判定 - MRAND (1000) < 1000 / (++(*pcc))) - { // 範囲内PCで等確率にする - smd->target_id = tsd->bl.id; - smd->state.targettype = ATTACKABLE; - smd->min_chase = 13; - } - } - } - //対象がMobの場合 - else if (tmd && - tmd->bl.m == smd->bl.m && - (dist = - distance (smd->bl.x, smd->bl.y, tmd->bl.x, tmd->bl.y)) < 9) - { - if (mob_can_reach (smd, bl, 12) && // 到達可能性判定 - MRAND (1000) < 1000 / (++(*pcc))) - { // 範囲内で等確率にする - smd->target_id = bl->id; - smd->state.targettype = ATTACKABLE; - smd->min_chase = 13; - } - } - } - 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 mode, 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->mode) - { - mode = mob_db[md->mob_class].mode; - } - else - { - mode = md->mode; - } - - if (!md->target_id && mode & 0x02) - { - if (!md->lootitem - || (battle_config.monster_loot_type == 1 - && md->lootitem_count >= LOOTITEM_SIZE)) - return 0; - if (bl->m == md->bl.m - && (dist = distance (md->bl.x, md->bl.y, bl->x, bl->y)) < 9) - { - if (mob_can_reach (md, bl, 12) && // Reachability judging - MRAND (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 = 13; - } - } - } - return 0; -} - -/*========================================== - * The ?? routine of a link monster - *------------------------------------------ - */ -static int mob_ai_sub_hard_linksearch (struct block_list *bl, va_list ap) -{ - struct mob_data *tmd; - struct mob_data *md; - struct block_list *target; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, tmd = (struct mob_data *) bl); - nullpo_retr (0, md = va_arg (ap, struct mob_data *)); - nullpo_retr (0, target = va_arg (ap, struct block_list *)); - - // same family free in a range at a link monster -- it will be made to lock if MOB is -/* if( (md->target_id > 0 && md->state.targettype == ATTACKABLE) && mob_db[md->mob_class].mode&0x08){ - if( tmd->mob_class==md->mob_class && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE) && tmd->bl.m == md->bl.m){ - if( mob_can_reach(tmd,target,12) ){ // Reachability judging - tmd->target_id=md->target_id; - tmd->state.targettype = ATTACKABLE; - tmd->min_chase=13; - } - } - }*/ - if (md->attacked_id > 0 && mob_db[md->mob_class].mode & 0x08) - { - if (tmd->mob_class == md->mob_class && tmd->bl.m == md->bl.m - && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE)) - { - if (mob_can_reach (tmd, target, 12)) - { // Reachability judging - tmd->target_id = md->attacked_id; - tmd->state.targettype = ATTACKABLE; - tmd->min_chase = 13; - } - } - } - - return 0; -} - -/*========================================== - * Processing of slave monsters - *------------------------------------------ - */ -static int mob_ai_sub_hard_slavemob (struct mob_data *md, unsigned int tick) -{ - struct mob_data *mmd = NULL; - struct block_list *bl; - int mode, race, old_dist; - - nullpo_retr (0, md); - - if ((bl = map_id2bl (md->master_id)) != NULL) - mmd = (struct mob_data *) bl; - - mode = mob_db[md->mob_class].mode; - - // It is not main monster/leader. - if (!mmd || mmd->bl.type != BL_MOB || mmd->bl.id != md->master_id) - return 0; - - // Since it is in the map on which the master is not, teleport is carried out and it pursues. - if (mmd->bl.m != md->bl.m) - { - mob_warp (md, mmd->bl.m, mmd->bl.x, mmd->bl.y, 3); - md->state.master_check = 1; - return 0; - } - - // Distance with between slave and master is measured. - old_dist = md->master_dist; - md->master_dist = distance (md->bl.x, md->bl.y, mmd->bl.x, mmd->bl.y); - - // 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, mmd->bl.x, mmd->bl.y, 3); - md->state.master_check = 1; - 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->walkpath.path_pos >= md->walkpath.path_len - || md->walkpath.path_len == 0) && md->master_dist < 15) - { - int i = 0, dx, dy, ret; - if (md->master_dist > 4) - { - do - { - if (i <= 5) - { - dx = mmd->bl.x - md->bl.x; - dy = mmd->bl.y - md->bl.y; - if (dx < 0) - dx += (MPRAND (1, ((dx < -3) ? 3 : -dx))); - else if (dx > 0) - dx -= (MPRAND (1, ((dx > 3) ? 3 : dx))); - if (dy < 0) - dy += (MPRAND (1, ((dy < -3) ? 3 : -dy))); - else if (dy > 0) - dy -= (MPRAND (1, ((dy > 3) ? 3 : dy))); - } - else - { - dx = mmd->bl.x - md->bl.x + MRAND (7) - 3; - dy = mmd->bl.y - md->bl.y + MRAND (7) - 3; - } - - ret = mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, 0); - i++; - } - while (ret && i < 10); - } - else - { - do - { - dx = MRAND (9) - 5; - dy = MRAND (9) - 5; - if (dx == 0 && dy == 0) - { - dx = (MRAND (1)) ? 1 : -1; - dy = (MRAND (1)) ? 1 : -1; - } - dx += mmd->bl.x; - dy += mmd->bl.y; - - ret = mob_walktoxy (md, mmd->bl.x + dx, mmd->bl.y + dy, 0); - i++; - } - while (ret && i < 10); - } - - md->next_walktime = tick + 500; - md->state.master_check = 1; - } - - // There is the master, the master locks a target and he does not lock. - if ((mmd->target_id > 0 && mmd->state.targettype == ATTACKABLE) - && (!md->target_id || md->state.targettype == NONE_ATTACKABLE)) - { - struct map_session_data *sd = map_id2sd (mmd->target_id); - if (sd != NULL && !pc_isdead (sd) && sd->invincible_timer == -1 - && !pc_isinvisible (sd)) - { - - race = mob_db[md->mob_class].race; - if (mode & 0x20 || - (sd->sc_data[SC_TRICKDEAD].timer == -1 && - ((!pc_ishiding (sd) && !sd->state.gangsterparadise) - || race == 4 || race == 6))) - { // 妨害がないか判定 - - md->target_id = sd->bl.id; - md->state.targettype = ATTACKABLE; - md->min_chase = - 5 + distance (md->bl.x, md->bl.y, sd->bl.x, sd->bl.y); - md->state.master_check = 1; - } - } - } - - // There is the master, the master locks a target and he does not lock. -/* if( (md->target_id>0 && mmd->state.targettype == ATTACKABLE) && (!mmd->target_id || mmd->state.targettype == NONE_ATTACKABLE) ){ - struct map_session_data *sd=map_id2sd(md->target_id); - if(sd!=NULL && !pc_isdead(sd) && sd->invincible_timer == -1 && !pc_isinvisible(sd)){ - - race=mob_db[mmd->mob_class].race; - if(mode&0x20 || - (sd->sc_data[SC_TRICKDEAD].timer == -1 && - (!(sd->status.option&0x06) || race==4 || race==6) - ) ){ // It judges whether there is any disturbance. - - mmd->target_id=sd->bl.id; - mmd->state.targettype = ATTACKABLE; - mmd->min_chase=5+distance(mmd->bl.x,mmd->bl.y,sd->bl.x,sd->bl.y); - } - } - }*/ - - return 0; -} - -/*========================================== - * A lock of target is stopped and mob moves to a standby state. - *------------------------------------------ - */ -static 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 + MPRAND (3000, 3000); - return 0; -} - -/*========================================== - * Random walk - *------------------------------------------ - */ -static int mob_randomwalk (struct mob_data *md, int tick) -{ - const int retrycount = 20; - int speed; - - nullpo_retr (0, md); - - speed = battle_get_speed (&md->bl); - if (DIFF_TICK (md->next_walktime, tick) < 0) - { - int i, x, y, c, d = 12 - md->move_fail_count; - if (d < 5) - d = 5; - for (i = 0; i < retrycount; i++) - { // Search of a movable place - int r = mt_random (); - x = md->bl.x + r % (d * 2 + 1) - d; - y = md->bl.y + r / (d * 2 + 1) % (d * 2 + 1) - d; - if ((c = map_getcell (md->bl.m, x, y)) != 1 && c != 5 - && mob_walktoxy (md, x, y, 1) == 0) - { - md->move_fail_count = 0; - break; - } - if (i + 1 >= retrycount) - { - md->move_fail_count++; - if (md->move_fail_count > 1000) - { - if (battle_config.error_log == 1) - printf - ("MOB cant move. random spawn %d, mob_class = %d\n", - md->bl.id, md->mob_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 + MPRAND (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, *tmd = NULL; - struct map_session_data *tsd = NULL; - struct block_list *tbl = NULL; - struct flooritem_data *fitem; - unsigned int tick; - int i, dx, dy, ret, dist; - int attack_type = 0; - int mode, race; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, md = (struct mob_data *) bl); - - tick = va_arg (ap, unsigned int); - - if (DIFF_TICK (tick, md->last_thinktime) < MIN_MOBTHINKTIME) - return 0; - md->last_thinktime = tick; - - if (md->skilltimer != -1 || md->bl.prev == NULL) - { // Under a skill aria and death - if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME) - md->next_walktime = tick; - return 0; - } - - if (!md->mode) - mode = mob_db[md->mob_class].mode; - else - mode = md->mode; - - race = mob_db[md->mob_class].race; - - // Abnormalities - if ((md->opt1 > 0 && md->opt1 != 6) || md->state.state == MS_DELAY - || md->sc_data[SC_BLADESTOP].timer != -1) - return 0; - - if (!(mode & 0x80) && md->target_id > 0) - md->target_id = 0; - - if (md->attacked_id > 0 && mode & 0x08) - { // Link monster - struct map_session_data *asd = map_id2sd (md->attacked_id); - if (asd) - { - if (asd->invincible_timer == -1 && !pc_isinvisible (asd)) - { - map_foreachinarea (mob_ai_sub_hard_linksearch, md->bl.m, - md->bl.x - 13, md->bl.y - 13, - md->bl.x + 13, md->bl.y + 13, - BL_MOB, md, &asd->bl); - } - } - } - - // It checks to see it was attacked first (if active, it is target change at 25% of probability). - if (mode > 0 && md->attacked_id > 0 - && (!md->target_id || md->state.targettype == NONE_ATTACKABLE - || (mode & 0x04 && MRAND (100) < 25))) - { - struct block_list *abl = map_id2bl (md->attacked_id); - struct map_session_data *asd = NULL; - if (abl) - { - if (abl->type == BL_PC) - asd = (struct map_session_data *) abl; - if (asd == NULL || md->bl.m != abl->m || abl->prev == NULL - || asd->invincible_timer != -1 || pc_isinvisible (asd) - || (dist = - distance (md->bl.x, md->bl.y, abl->x, abl->y)) >= 32 - || battle_check_target (bl, abl, BCT_ENEMY) == 0) - md->attacked_id = 0; - else - { - md->target_id = md->attacked_id; // set target - md->state.targettype = ATTACKABLE; - attack_type = 1; - md->attacked_id = 0; - md->min_chase = dist + 13; - if (md->min_chase > 26) - md->min_chase = 26; - } - } - } - - md->state.master_check = 0; - // Processing of slave monster - if (md->master_id > 0 && md->state.special_mob_ai == 0) - mob_ai_sub_hard_slavemob (md, tick); - - // アクティヴモンスターの策敵 (?? of a bitter taste TIVU monster) - if ((!md->target_id || md->state.targettype == NONE_ATTACKABLE) - && mode & 0x04 && !md->state.master_check - && battle_config.monster_active_enable == 1) - { - i = 0; - if (md->state.special_mob_ai) - { - map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m, - md->bl.x - AREA_SIZE * 2, - md->bl.y - AREA_SIZE * 2, - md->bl.x + AREA_SIZE * 2, - md->bl.y + AREA_SIZE * 2, 0, md, &i); - } - else - { - map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m, - md->bl.x - AREA_SIZE * 2, - md->bl.y - AREA_SIZE * 2, - md->bl.x + AREA_SIZE * 2, - md->bl.y + AREA_SIZE * 2, BL_PC, md, &i); - } - } - - // The item search of a route monster - if (!md->target_id && mode & 0x02 && !md->state.master_check) - { - i = 0; - map_foreachinarea (mob_ai_sub_hard_lootsearch, md->bl.m, - md->bl.x - AREA_SIZE * 2, md->bl.y - AREA_SIZE * 2, - md->bl.x + AREA_SIZE * 2, md->bl.y + AREA_SIZE * 2, - BL_ITEM, md, &i); - } - - // It will attack, if the candidate for an attack is. - if (md->target_id > 0) - { - if ((tbl = map_id2bl (md->target_id))) - { - if (tbl->type == BL_PC) - tsd = (struct map_session_data *) tbl; - else if (tbl->type == BL_MOB) - tmd = (struct mob_data *) tbl; - if (tsd || tmd) - { - if (tbl->m != md->bl.m || tbl->prev == NULL - || (dist = - distance (md->bl.x, md->bl.y, tbl->x, - tbl->y)) >= md->min_chase) - mob_unlocktarget (md, tick); // 別マップか、視界外 - else if (tsd && !(mode & 0x20) - && (tsd->sc_data[SC_TRICKDEAD].timer != -1 - || - ((pc_ishiding (tsd) - || tsd->state.gangsterparadise) && race != 4 - && race != 6))) - mob_unlocktarget (md, tick); // スキルなどによる策敵妨害 - else if (!battle_check_range - (&md->bl, tbl, mob_db[md->mob_class].range)) - { - // 攻撃範囲外なので移動 - if (!(mode & 1)) - { // 移動しないモード - mob_unlocktarget (md, tick); - return 0; - } - if (!mob_can_move (md)) // 動けない状態にある - return 0; - md->state.skillstate = MSS_CHASE; // 突撃時スキル - mobskill_use (md, tick, -1); -// if(md->timer != -1 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tsd->bl.x,tsd->bl.y)<2) ) - if (md->timer != -1 && md->state.state != MS_ATTACK - && (DIFF_TICK (md->next_walktime, tick) < 0 - || distance (md->to_x, md->to_y, tbl->x, - tbl->y) < 2)) - return 0; // 既に移動中 - if (!mob_can_reach - (md, tbl, (md->min_chase > 13) ? md->min_chase : 13)) - mob_unlocktarget (md, tick); // 移動できないのでタゲ解除(IWとか?) - else - { - // 追跡 - md->next_walktime = tick + 500; - i = 0; - do - { - if (i == 0) - { // 最初はAEGISと同じ方法で検索 - 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--; - } - else - { // だめならAthena式(ランダム) - dx = tbl->x - md->bl.x + MRAND (3) - 1; - dy = tbl->y - md->bl.y + MRAND (3) - 1; - } - /* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ - * dx=tsd->bl.x - md->bl.x; - * dy=tsd->bl.y - md->bl.y; - * if(dx<0) dx--; - * else if(dx>0) dx++; - * if(dy<0) dy--; - * else if(dy>0) dy++; - * } */ - ret = - mob_walktoxy (md, md->bl.x + dx, - md->bl.y + dy, 0); - 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; - mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, - 0); - } - } - } - else - { // 攻撃射程範囲内 - md->state.skillstate = MSS_ATTACK; - if (md->state.state == MS_WALK) - mob_stop_walking (md, 1); // 歩行中なら停止 - if (md->state.state == MS_ATTACK) - return 0; // 既に攻撃中 - mob_changestate (md, MS_ATTACK, attack_type); - -/* if(mode&0x08){ // リンクモンスター - map_foreachinarea(mob_ai_sub_hard_linksearch,md->bl.m, - md->bl.x-13,md->bl.y-13, - md->bl.x+13,md->bl.y+13, - BL_MOB,md,&tsd->bl); - }*/ - } - return 0; - } - else - { // ルートモンスター処理 - if (tbl == NULL || tbl->type != BL_ITEM || tbl->m != md->bl.m - || (dist = - distance (md->bl.x, md->bl.y, tbl->x, - tbl->y)) >= md->min_chase || !md->lootitem) - { - // 遠すぎるかアイテムがなくなった - mob_unlocktarget (md, tick); - if (md->state.state == MS_WALK) - mob_stop_walking (md, 1); // 歩行中なら停止 - } - else if (dist) - { - if (!(mode & 1)) - { // 移動しないモード - 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 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tbl->x,tbl->y)<2) ) - if (md->timer != -1 && md->state.state != MS_ATTACK - && (DIFF_TICK (md->next_walktime, tick) < 0 - || distance (md->to_x, md->to_y, tbl->x, - tbl->y) <= 0)) - return 0; // 既に移動中 - md->next_walktime = tick + 500; - dx = tbl->x - md->bl.x; - dy = tbl->y - md->bl.y; -/* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ - dx=tbl->x - md->bl.x; - dy=tbl->y - md->bl.y; - }*/ - ret = mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, 0); - if (ret) - mob_unlocktarget (md, tick); // 移動できないのでタゲ解除(IWとか?) - } - else - { // アイテムまでたどり着いた - if (md->state.state == MS_ATTACK) - return 0; // 攻撃中 - if (md->state.state == MS_WALK) - mob_stop_walking (md, 1); // 歩行中なら停止 - fitem = (struct flooritem_data *) tbl; - if (md->lootitem_count < LOOTITEM_SIZE) - memcpy (&md->lootitem[md->lootitem_count++], - &fitem->item_data, sizeof (md->lootitem[0])); - else if (battle_config.monster_loot_type == 1 - && md->lootitem_count >= LOOTITEM_SIZE) - { - mob_unlocktarget (md, tick); - return 0; - } - else - { - 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])); - } - map_clearflooritem (tbl->id); - mob_unlocktarget (md, tick); - } - return 0; - } - } - else - { - mob_unlocktarget (md, tick); - if (md->state.state == MS_WALK) - mob_stop_walking (md, 4); // 歩行中なら停止 - return 0; - } - } - - // It is skill use at the time of /standby at the time of a walk. - if (mobskill_use (md, tick, -1)) - return 0; - - // 歩行処理 - if (mode & 1 && mob_can_move (md) && // 移動可能MOB&動ける状態にある - (md->master_id == 0 || md->state.special_mob_ai - || md->master_dist > 10)) - { //取り巻きMOBじゃない - - 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 * MRAND (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 void mob_ai_hard (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - clif_foreachclient (mob_ai_sub_foreachclient, tick); -} - -/*========================================== - * Negligent mode MOB AI (PC is not in near) - *------------------------------------------ - */ -static void mob_ai_sub_lazy (db_key_t key, db_val_t data, va_list app) -{ - struct mob_data *md = (struct mob_data *)data; - unsigned int tick; - va_list ap; - - nullpo_retv (md); - nullpo_retv (app); - nullpo_retv (ap = va_arg (app, va_list)); - - if (md == NULL) - return; - - if (!md->bl.type || md->bl.type != BL_MOB) - return; - - tick = va_arg (ap, unsigned int); - - if (DIFF_TICK (tick, md->last_thinktime) < MIN_MOBTHINKTIME * 10) - return; - md->last_thinktime = tick; - - if (md->bl.prev == NULL || md->skilltimer != -1) - { - if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME * 10) - md->next_walktime = tick; - return; - } - - if (DIFF_TICK (md->next_walktime, tick) < 0 && - (mob_db[md->mob_class].mode & 1) && 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 (MRAND (1000) < MOB_LAZYMOVEPERC) - mob_randomwalk (md, tick); - - // MOB which is not not the summons MOB but BOSS, either sometimes reboils. - else if (MRAND (1000) < MOB_LAZYWARPPERC && md->x0 <= 0 - && md->master_id != 0 && mob_db[md->mob_class].mexp <= 0 - && !(mob_db[md->mob_class].mode & 0x20)) - mob_spawn (md->bl.id); - - } - 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 (MRAND (1000) < MOB_LAZYWARPPERC && md->x0 <= 0 - && md->master_id != 0 && mob_db[md->mob_class].mexp <= 0 - && !(mob_db[md->mob_class].mode & 0x20)) - mob_warp (md, -1, -1, -1, -1); - } - - md->next_walktime = tick + MPRAND (5000, 10000); - } -} - -/*========================================== - * Negligent processing for mob outside PC field of view (interval timer function) - *------------------------------------------ - */ -static void mob_ai_lazy (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - map_foreachiddb (mob_ai_sub_lazy, tick); -} - -/*========================================== - * 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; - 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; -}; - -/*========================================== - * item drop with delay (timer function) - *------------------------------------------ - */ -static void mob_delay_item_drop (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct delay_item_drop *ditem; - struct item temp_item; - int flag; - - nullpo_retv (ditem = (struct delay_item_drop *) id); - - memset (&temp_item, 0, sizeof (temp_item)); - temp_item.nameid = ditem->nameid; - temp_item.amount = ditem->amount; - temp_item.identify = !itemdb_isequip3 (temp_item.nameid); - - if (battle_config.item_auto_get == 1) - { - if (ditem->first_sd - && (flag = - pc_additem (ditem->first_sd, &temp_item, ditem->amount))) - { - clif_additem (ditem->first_sd, 0, 0, flag); - map_addflooritem (&temp_item, 1, ditem->m, ditem->x, ditem->y, - ditem->first_sd, ditem->second_sd, - ditem->third_sd, 0); - } - free (ditem); - return; - } - - map_addflooritem (&temp_item, 1, ditem->m, ditem->x, ditem->y, - ditem->first_sd, ditem->second_sd, ditem->third_sd, 0); - - free (ditem); -} - -/*========================================== - * item drop (timer function)-lootitem with delay - *------------------------------------------ - */ -static void mob_delay_item_drop2 (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct delay_item_drop2 *ditem; - int flag; - - nullpo_retv (ditem = (struct delay_item_drop2 *) id); - - if (battle_config.item_auto_get == 1) - { - if (ditem->first_sd - && (flag = - pc_additem (ditem->first_sd, &ditem->item_data, - ditem->item_data.amount))) - { - clif_additem (ditem->first_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); - } - free (ditem); - return; - } - - 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); - - free (ditem); -} - -/*========================================== - * mob data is erased. - *------------------------------------------ - */ -int mob_delete (struct mob_data *md) -{ - nullpo_retr (1, md); - - if (md->bl.prev == NULL) - return 1; - mob_changestate (md, MS_DEAD, 0); - clif_clearchar_area (&md->bl, 1); - map_delblock (&md->bl); - if (mob_get_viewclass (md->mob_class) <= 1000) - clif_clearchar_delay (gettick () + 3000, &md->bl, 0); - mob_deleteslave (md); - mob_setdelayspawn (md->bl.id); - return 0; -} - -int mob_catch_delete (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); - mob_setdelayspawn (md->bl.id); - return 0; -} - -void mob_timer_delete (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct block_list *bl = map_id2bl (id); - struct mob_data *md; - - nullpo_retv (bl); - - md = (struct mob_data *) bl; - mob_catch_delete (md, 3); -} - -/*========================================== - * - *------------------------------------------ - */ -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_foreachinarea (mob_deleteslave_sub, md->bl.m, - 0, 0, map[md->bl.m].xs, map[md->bl.m].ys, - BL_MOB, md->bl.id); - return 0; -} - -#define DAMAGE_BONUS_COUNT 6 // max. number of players to account for -const static double damage_bonus_factor[DAMAGE_BONUS_COUNT + 1] = { - 1.0, 1.0, 2.0, 2.5, 2.75, 2.9, 3.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; - } 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; - double dmg_rate, tdmg, temp; - struct item item; - int ret; - int drop_rate; - int skill, sp; - - nullpo_retr (0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック - - if (src && src->id == md->master_id - && md->mode & MOB_MODE_TURNS_AGAINST_BAD_MASTER) - { - /* If the master hits a monster, have the monster turn against him */ - md->master_id = 0; - md->mode = 0x85; /* Regular war mode */ - md->target_id = src->id; - md->attacked_id = src->id; - } - - max_hp = battle_get_max_hp (&md->bl); - - if (src && src->type == BL_PC) - { - sd = (struct map_session_data *) src; - mvp_sd = sd; - } - -// if(battle_config.battle_log) -// printf("mob_damage %d %d %d\n",md->hp,max_hp,damage); - if (md->bl.prev == NULL) - { - if (battle_config.error_log == 1) - printf ("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; - } - - if (md->sc_data[SC_ENDURE].timer == -1) - mob_stop_walking (md, 3); - if (damage > max_hp >> 2) - skill_stop_dancing (&md->bl, 0); - - if (md->hp > max_hp) - md->hp = max_hp; - - // The amount of overkill rounds to hp. - if (damage > md->hp) - damage = md->hp; - - if (!(type & 2)) - { - if (sd != NULL) - { - for (i = 0, minpos = 0, mindmg = 0x7fffffff; i < DAMAGELOG_SIZE; - i++) - { - if (md->dmglog[i].id == sd->bl.id) - break; - if (md->dmglog[i].id == 0) - { - minpos = i; - mindmg = 0; - } - else 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 = sd->bl.id; - md->dmglog[minpos].dmg = damage; - } - - if (md->attacked_id <= 0 && md->state.special_mob_ai == 0) - md->attacked_id = sd->bl.id; - } - if (src && src->type == BL_MOB - && ((struct mob_data *) src)->state.special_mob_ai) - { - struct mob_data *md2 = (struct mob_data *) src; - struct block_list *master_bl = map_id2bl (md2->master_id); - if (master_bl && master_bl->type == BL_PC) - { - MAP_LOG_PC (((struct map_session_data *) master_bl), - "MOB-TO-MOB-DMG FROM MOB%d %d TO MOB%d %d FOR %d", - md2->bl.id, md2->mob_class, md->bl.id, md->mob_class, - damage); - } - - nullpo_retr (0, md2); - for (i = 0, minpos = 0, mindmg = 0x7fffffff; i < DAMAGELOG_SIZE; - i++) - { - if (md->dmglog[i].id == md2->master_id) - break; - if (md->dmglog[i].id == 0) - { - minpos = i; - mindmg = 0; - } - else 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 = md2->master_id; - md->dmglog[minpos].dmg = damage; - - if (md->attacked_id <= 0 && md->state.special_mob_ai == 0) - md->attacked_id = md2->master_id; - } - } - - } - - md->hp -= damage; - - if (md->mob_class >= 1285 && md->mob_class <= 1287) - { // guardian hp update [Valaris] - struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); - if (gc) - { - - if (md->bl.id == gc->GID0) - { - gc->Ghp0 = md->hp; - if (gc->Ghp0 <= 0) - { - guild_castledatasave (gc->castle_id, 10, 0); - guild_castledatasave (gc->castle_id, 18, 0); - } - } - if (md->bl.id == gc->GID1) - { - gc->Ghp1 = md->hp; - if (gc->Ghp1 <= 0) - { - guild_castledatasave (gc->castle_id, 11, 0); - guild_castledatasave (gc->castle_id, 19, 0); - } - } - if (md->bl.id == gc->GID2) - { - gc->Ghp2 = md->hp; - if (gc->Ghp2 <= 0) - { - guild_castledatasave (gc->castle_id, 12, 0); - guild_castledatasave (gc->castle_id, 20, 0); - } - } - if (md->bl.id == gc->GID3) - { - gc->Ghp3 = md->hp; - if (gc->Ghp3 <= 0) - { - guild_castledatasave (gc->castle_id, 13, 0); - guild_castledatasave (gc->castle_id, 21, 0); - } - } - if (md->bl.id == gc->GID4) - { - gc->Ghp4 = md->hp; - if (gc->Ghp4 <= 0) - { - guild_castledatasave (gc->castle_id, 14, 0); - guild_castledatasave (gc->castle_id, 22, 0); - } - } - if (md->bl.id == gc->GID5) - { - gc->Ghp5 = md->hp; - if (gc->Ghp5 <= 0) - { - guild_castledatasave (gc->castle_id, 15, 0); - guild_castledatasave (gc->castle_id, 23, 0); - } - } - if (md->bl.id == gc->GID6) - { - gc->Ghp6 = md->hp; - if (gc->Ghp6 <= 0) - { - guild_castledatasave (gc->castle_id, 16, 0); - guild_castledatasave (gc->castle_id, 24, 0); - } - } - if (md->bl.id == gc->GID7) - { - gc->Ghp7 = md->hp; - if (gc->Ghp7 <= 0) - { - guild_castledatasave (gc->castle_id, 17, 0); - guild_castledatasave (gc->castle_id, 25, 0); - - } - } - } - } // end addition [Valaris] - - if (md->option & 2) - skill_status_change_end (&md->bl, SC_HIDING, -1); - if (md->option & 4) - skill_status_change_end (&md->bl, SC_CLOAKING, -1); - - if (md->state.special_mob_ai == 2) - { //スフィアーマイン - int skillidx = 0; - - if ((skillidx = - mob_skillid2skillidx (md->mob_class, NPC_SELFDESTRUCTION2)) >= 0) - { - md->mode |= 0x1; - md->next_walktime = tick; - mobskill_use_id (md, &md->bl, skillidx); //自爆詠唱開始 - md->state.special_mob_ai++; - } - } - - if (md->hp > 0) - { - return 0; - } - - MAP_LOG ("MOB%d DEAD", md->bl.id); - - // ----- ここから死亡処理 ----- - - 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 = battle_get_max_hp (&md->bl); - - if (src && src->type == BL_MOB) - mob_unlocktarget ((struct mob_data *) src, tick); - - /* ソウルドレイン */ - if (sd && (skill = pc_checkskill (sd, HW_SOULDRAIN)) > 0) - { - clif_skill_nodamage (src, &md->bl, HW_SOULDRAIN, skill, 1); - sp = (battle_get_lv (&md->bl)) * (65 + 15 * skill) / 100; - if (sd->status.sp + sp > sd->status.max_sp) - sp = sd->status.max_sp - sd->status.sp; - sd->status.sp += sp; - clif_heal (sd->fd, SP_SP, sp); - } - - // map外に消えた人は計算から除くので - // overkill分は無いけどsumはmax_hpとは違う - - tdmg = 0; - for (i = 0, count = 0, mvp_damage = 0; i < DAMAGELOG_SIZE; i++) - { - if (md->dmglog[i].id == 0) - continue; - tmpsd[i] = map_id2sd (md->dmglog[i].id); - if (tmpsd[i] == NULL) - continue; - count++; - if (tmpsd[i]->bl.m != md->bl.m || pc_isdead (tmpsd[i])) - continue; - - tdmg += (double) md->dmglog[i].dmg; - 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)) - { - - if ((double) max_hp < tdmg) - dmg_rate = ((double) max_hp) / tdmg; - else - dmg_rate = 1; - - // 経験値の分配 - for (i = 0; i < DAMAGELOG_SIZE; i++) - { - - int pid, base_exp, job_exp, flag = 1; - double per; - struct party *p; - if (tmpsd[i] == NULL || tmpsd[i]->bl.m != md->bl.m) - continue; -/* jAthena's exp formula - per = ((double)md->dmglog[i].dmg)*(9.+(double)((count > 6)? 6:count))/10./((double)max_hp) * dmg_rate; - temp = ((double)mob_db[md->mob_class].base_exp * (double)battle_config.base_exp_rate / 100. * per); - base_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; - if(mob_db[md->mob_class].base_exp > 0 && base_exp < 1) base_exp = 1; - if(base_exp < 0) base_exp = 0; - temp = ((double)mob_db[md->mob_class].job_exp * (double)battle_config.job_exp_rate / 100. * per); - job_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; - if(mob_db[md->mob_class].job_exp > 0 && job_exp < 1) job_exp = 1; - if(job_exp < 0) job_exp = 0; -*/ -//eAthena's exp formula rather than jAthena's -// per=(double)md->dmglog[i].dmg*256*(9+(double)((count > 6)? 6:count))/10/(double)max_hp; - // [Fate] The above is the old formula. We do a more involved computation below. - per = (double) md->dmglog[i].dmg * 256 / (double) max_hp; // 256 = 100% of the score - per *= damage_bonus_factor[count > DAMAGE_BONUS_COUNT ? DAMAGE_BONUS_COUNT : count]; // Bonus for party attack - if (per > 512) - per = 512; // [Fate] Retained from before. The maximum a single individual can get is double the original value. - if (per < 1) - per = 1; - - base_exp = - ((mob_db[md->mob_class].base_exp * - md->stats[MOB_XP_BONUS]) >> MOB_XP_BONUS_SHIFT) * per / 256; - if (base_exp < 1) - base_exp = 1; - if (sd && md && battle_config.pk_mode == 1 - && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) - { - base_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris] - } - if (md->state.special_mob_ai >= 1 - && battle_config.alchemist_summon_reward != 1) - base_exp = 0; // Added [Valaris] - job_exp = mob_db[md->mob_class].job_exp * per / 256; - if (job_exp < 1) - job_exp = 1; - if (sd && md && battle_config.pk_mode == 1 - && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) - { - job_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris] - } - if (md->state.special_mob_ai >= 1 - && battle_config.alchemist_summon_reward != 1) - job_exp = 0; // Added [Valaris] - - if ((pid = tmpsd[i]->status.party_id) > 0) - { // パーティに入っている - int j = 0; - 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; - pnum++; - flag = 0; - } - } - else - { // いるときは公平 - pt[j].base_exp += base_exp; - pt[j].job_exp += job_exp; - flag = 0; - } - } - if (flag) // 各自所得 - pc_gainexp (tmpsd[i], base_exp, job_exp); - } - // 公平分配 - for (i = 0; i < pnum; i++) - party_exp_share (pt[i].p, md->bl.m, pt[i].base_exp, - pt[i].job_exp); - - // item drop - if (!(type & 1)) - { - for (i = 0; i < 8; i++) - { - struct delay_item_drop *ditem; - int drop_rate; - - if (md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) // Added [Valaris] - break; // End - - if (mob_db[md->mob_class].dropitem[i].nameid <= 0) - continue; - drop_rate = mob_db[md->mob_class].dropitem[i].p; - if (drop_rate <= 0 && battle_config.drop_rate0item == 1) - drop_rate = 1; - if (battle_config.drops_by_luk > 0 && sd && md) - drop_rate += (sd->status.luk * battle_config.drops_by_luk) / 100; // drops affected by luk [Valaris] - if (sd && md && battle_config.pk_mode == 1 - && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) - drop_rate *= 1.25; // pk_mode increase drops if 20 level difference [Valaris] - if (drop_rate <= MRAND (10000)) - continue; - - ditem = (struct delay_item_drop *) - calloc (1, sizeof (struct delay_item_drop)); - ditem->nameid = mob_db[md->mob_class].dropitem[i].nameid; - ditem->amount = 1; - ditem->m = md->bl.m; - ditem->x = md->bl.x; - ditem->y = md->bl.y; - ditem->first_sd = mvp_sd; - ditem->second_sd = second_sd; - ditem->third_sd = third_sd; - add_timer (tick + 500 + i, mob_delay_item_drop, (int) ditem, 0); - } - if (sd && sd->state.attack_type == BF_WEAPON) - { - for (i = 0; i < sd->monster_drop_item_count; i++) - { - struct delay_item_drop *ditem; - int race = battle_get_race (&md->bl); - if (sd->monster_drop_itemid[i] <= 0) - continue; - if (sd->monster_drop_race[i] & (1 << race) || - (mob_db[md->mob_class].mode & 0x20 - && sd->monster_drop_race[i] & 1 << 10) - || (!(mob_db[md->mob_class].mode & 0x20) - && sd->monster_drop_race[i] & 1 << 11)) - { - if (sd->monster_drop_itemrate[i] <= MRAND (10000)) - continue; - - ditem = (struct delay_item_drop *) - calloc (1, sizeof (struct delay_item_drop)); - ditem->nameid = sd->monster_drop_itemid[i]; - ditem->amount = 1; - ditem->m = md->bl.m; - ditem->x = md->bl.x; - ditem->y = md->bl.y; - ditem->first_sd = mvp_sd; - ditem->second_sd = second_sd; - ditem->third_sd = third_sd; - add_timer (tick + 520 + i, mob_delay_item_drop, - (int) ditem, 0); - } - } - if (sd->get_zeny_num > 0) - pc_getzeny (sd, - mob_db[md->mob_class].lv * 10 + - MRAND ((sd->get_zeny_num + 1))); - } - if (md->lootitem) - { - for (i = 0; i < md->lootitem_count; i++) - { - struct delay_item_drop2 *ditem; - - ditem = (struct delay_item_drop2 *) - calloc (1, sizeof (struct delay_item_drop2)); - memcpy (&ditem->item_data, &md->lootitem[i], - sizeof (md->lootitem[0])); - ditem->m = md->bl.m; - ditem->x = md->bl.x; - ditem->y = md->bl.y; - ditem->first_sd = mvp_sd; - ditem->second_sd = second_sd; - ditem->third_sd = third_sd; - add_timer (tick + 540 + i, mob_delay_item_drop2, - (int) ditem, 0); - } - } - } - - // mvp処理 - if (mvp_sd && mob_db[md->mob_class].mexp > 0) - { - int j; - int mexp = battle_get_mexp (&md->bl); - temp = - ((double) mexp * (double) battle_config.mvp_exp_rate * - (9. + (double) count) / 1000.); - mexp = (temp > 2147483647.) ? 0x7fffffff : (int) temp; - if (mexp < 1) - mexp = 1; - clif_mvp_effect (mvp_sd); // エフェクト - clif_mvp_exp (mvp_sd, mexp); - pc_gainexp (mvp_sd, mexp, 0); - for (j = 0; j < 3; j++) - { - i = MRAND (3); - if (mob_db[md->mob_class].mvpitem[i].nameid <= 0) - continue; - drop_rate = mob_db[md->mob_class].mvpitem[i].p; - if (drop_rate <= 0 && battle_config.drop_rate0item == 1) - drop_rate = 1; - if (drop_rate < battle_config.item_drop_mvp_min) - drop_rate = battle_config.item_drop_mvp_min; - if (drop_rate > battle_config.item_drop_mvp_max) - drop_rate = battle_config.item_drop_mvp_max; - if (drop_rate <= MRAND (10000)) - continue; - memset (&item, 0, sizeof (item)); - item.nameid = mob_db[md->mob_class].mvpitem[i].nameid; - item.identify = !itemdb_isequip3 (item.nameid); - clif_mvp_item (mvp_sd, 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); - } - break; - } - } - - } // [MouseJstr] - - // <Agit> NPC Event [OnAgitBreak] - if (md->npc_event[0] - && strcmp (((md->npc_event) + strlen (md->npc_event) - 13), - "::OnAgitBreak") == 0) - { - printf ("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 (sd == NULL) - { - if (mvp_sd != NULL) - sd = mvp_sd; - else - { - struct map_session_data *tmpsd; - int i; - for (i = 0; i < fd_max; i++) - { - if (session[i] && (tmpsd = (struct map_session_data *)session[i]->session_data) - && tmpsd->state.auth) - { - if (md->bl.m == tmpsd->bl.m) - { - sd = tmpsd; - break; - } - } - } - } - } - if (sd) - npc_event (sd, md->npc_event, 0); - } - - clif_clearchar_area (&md->bl, 1); - map_delblock (&md->bl); - if (mob_get_viewclass (md->mob_class) <= 1000) - clif_clearchar_delay (tick + 3000, &md->bl, 0); - mob_deleteslave (md); - mob_setdelayspawn (md->bl.id); - map_freeblock_unlock (); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int mob_class_change (struct mob_data *md, int *value) -{ - unsigned int tick = gettick (); - int i, c, hp_rate, max_hp, mob_class, count = 0; - - nullpo_retr (0, md); - nullpo_retr (0, value); - - if (value[0] <= 1000 || value[0] > 2000) - return 0; - if (md->bl.prev == NULL) - return 0; - - while (count < 5 && value[count] > 1000 && value[count] <= 2000) - count++; - if (count < 1) - return 0; - - mob_class = value[MRAND (count)]; - if (mob_class <= 1000 || mob_class > 2000) - return 0; - - max_hp = battle_get_max_hp (&md->bl); - hp_rate = md->hp * 100 / max_hp; - clif_mob_class_change (md, mob_class); - md->mob_class = mob_class; - max_hp = battle_get_max_hp (&md->bl); - if (battle_config.monster_class_change_full_recover == 1) - { - md->hp = max_hp; - memset (md->dmglog, 0, sizeof (md->dmglog)); - } - else - md->hp = max_hp * hp_rate / 100; - if (md->hp > max_hp) - md->hp = max_hp; - else if (md->hp < 1) - md->hp = 1; - - memcpy (md->name, mob_db[mob_class].jname, 24); - memset (&md->state, 0, sizeof (md->state)); - md->attacked_id = 0; - md->target_id = 0; - md->move_fail_count = 0; - - md->stats[MOB_SPEED] = mob_db[md->mob_class].speed; - md->def_ele = mob_db[md->mob_class].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 + MPRAND (5000, 50); - md->attackabletime = tick; - md->canmove_tick = tick; - md->sg_count = 0; - - 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 && mob_db[mob_class].mode & 0x02) - md->lootitem = (struct item *) - calloc (LOOTITEM_SIZE, sizeof (struct item)); - - skill_clear_unitgroup (&md->bl); - skill_cleartimerskill (&md->bl); - - clif_clearchar_area (&md->bl, 0); - clif_spawnmob (md); - - return 0; -} - -/*========================================== - * mob回復 - *------------------------------------------ - */ -int mob_heal (struct mob_data *md, int heal) -{ - int max_hp = battle_get_max_hp (&md->bl); - - nullpo_retr (0, md); - - md->hp += heal; - if (max_hp < md->hp) - md->hp = max_hp; - - if (md->mob_class >= 1285 && md->mob_class <= 1287) - { // guardian hp update [Valaris] - struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); - if (gc) - { - if (md->bl.id == gc->GID0) - gc->Ghp0 = md->hp; - if (md->bl.id == gc->GID1) - gc->Ghp1 = md->hp; - if (md->bl.id == gc->GID2) - gc->Ghp2 = md->hp; - if (md->bl.id == gc->GID3) - gc->Ghp3 = md->hp; - if (md->bl.id == gc->GID4) - gc->Ghp4 = md->hp; - if (md->bl.id == gc->GID5) - gc->Ghp5 = md->hp; - if (md->bl.id == gc->GID6) - gc->Ghp6 = md->hp; - if (md->bl.id == gc->GID7) - gc->Ghp7 = md->hp; - } - } // end addition [Valaris] - - return 0; -} - -/*========================================== - * Added by RoVeRT - *------------------------------------------ - */ -int mob_warpslave_sub (struct block_list *bl, va_list ap) -{ - struct mob_data *md = (struct mob_data *) bl; - int id, x, y; - id = va_arg (ap, int); - x = va_arg (ap, int); - y = va_arg (ap, int); - if (md->master_id == id) - { - mob_warp (md, -1, x, y, 2); - } - return 0; -} - -/*========================================== - * Added by RoVeRT - *------------------------------------------ - */ -int mob_warpslave (struct mob_data *md, int x, int y) -{ -//printf("warp slave\n"); - map_foreachinarea (mob_warpslave_sub, md->bl.m, - x - AREA_SIZE, y - AREA_SIZE, - x + AREA_SIZE, y + AREA_SIZE, BL_MOB, - md->bl.id, md->bl.x, md->bl.y); - return 0; -} - -/*========================================== - * mobワープ - *------------------------------------------ - */ -int mob_warp (struct mob_data *md, int m, int x, int y, int type) -{ - int i = 0, c, xs = 0, ys = 0, bx = x, by = y; - - 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; - clif_clearchar_area (&md->bl, type); - } - skill_unit_out_all (&md->bl, gettick (), 1); - map_delblock (&md->bl); - - if (bx > 0 && by > 0) - { // 位置指定の場合周囲9セルを探索 - xs = ys = 9; - } - - while ((x < 0 || y < 0 || ((c = read_gat (m, x, y)) == 1 || c == 5)) - && (i++) < 1000) - { - if (xs > 0 && ys > 0 && i < 250) - { // 指定位置付近の探索 - x = MPRAND (bx, xs) - xs / 2; - y = MPRAND (by, ys) - ys / 2; - } - else - { // 完全ランダム探索 - x = MPRAND (1, (map[m].xs - 2)); - y = MPRAND (1, (map[m].ys - 2)); - } - } - 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) - printf ("MOB %d warp failed, mob_class = %d\n", md->bl.id, md->mob_class); - } - - md->target_id = 0; // タゲを解除する - md->state.targettype = NONE_ATTACKABLE; - md->attacked_id = 0; - md->state.skillstate = MSS_IDLE; - mob_changestate (md, MS_IDLE, 0); - - if (type > 0 && i == 1000) - { - if (battle_config.battle_log == 1) - printf ("MOB %d warp to (%d,%d), mob_class = %d\n", md->bl.id, x, y, - md->mob_class); - } - - map_addblock (&md->bl); - if (type > 0) - { - clif_spawnmob (md); - mob_warpslave (md, md->bl.x, md->bl.y); - } - - 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); - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, c = va_arg (ap, int *)); - nullpo_retr (0, md = (struct mob_data *) bl); - - if (md->master_id == id) - (*c)++; - return 0; -} - -/*========================================== - * 画面内の取り巻きの数計算 - *------------------------------------------ - */ -int mob_countslave (struct mob_data *md) -{ - int c = 0; - - nullpo_retr (0, md); - - map_foreachinarea (mob_countslave_sub, md->bl.m, - 0, 0, map[md->bl.m].xs - 1, map[md->bl.m].ys - 1, - BL_MOB, md->bl.id, &c); - return c; -} - -/*========================================== - * 手下MOB召喚 - *------------------------------------------ - */ -int mob_summonslave (struct mob_data *md2, int *value, int amount, int flag) -{ - struct mob_data *md; - int bx, by, m, count = 0, mob_class, k, a = amount; - - nullpo_retr (0, md2); - nullpo_retr (0, value); - - bx = md2->bl.x; - by = md2->bl.y; - m = md2->bl.m; - - if (value[0] <= 1000 || value[0] > 2000) // 値が異常なら召喚を止める - return 0; - while (count < 5 && value[count] > 1000 && value[count] <= 2000) - count++; - if (count < 1) - return 0; - - for (k = 0; k < count; k++) - { - amount = a; - mob_class = value[k]; - if (mob_class <= 1000 || mob_class > 2000) - continue; - for (; amount > 0; amount--) - { - int x = 0, y = 0, c = 0, i = 0; - md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); - if (mob_db[mob_class].mode & 0x02) - md->lootitem = (struct item *) - calloc (LOOTITEM_SIZE, sizeof (struct item)); - else - md->lootitem = NULL; - - while ((x <= 0 || y <= 0 || (c = map_getcell (m, x, y)) == 1 - || c == 5) && (i++) < 100) - { - x = MPRAND (bx, 9) - 4; - y = MPRAND (by, 9) - 4; - } - if (i >= 100) - { - x = bx; - y = by; - } - - mob_spawn_dataset (md, "--ja--", mob_class); - md->bl.prev = NULL; - md->bl.next = NULL; - 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->stats[MOB_SPEED] = md2->stats[MOB_SPEED]; - md->spawndelay1 = -1; // 一度のみフラグ - md->spawndelay2 = -1; // 一度のみフラグ - - 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, - (flag) ? NPC_SUMMONSLAVE : NPC_SUMMONMONSTER, - a, 1); - - if (flag) - md->master_id = md2->bl.id; - } - } - return 0; -} - -/*========================================== - * 自分をロックしているPCの数を数える(foreachclient) - *------------------------------------------ - */ -static int mob_counttargeted_sub (struct block_list *bl, va_list ap) -{ - int id, *c, target_lv; - struct block_list *src; - - id = va_arg (ap, int); - nullpo_retr (0, bl); - nullpo_retr (0, ap); - 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)++; - } - return 0; -} - -/*========================================== - * 自分をロックしているPCの数を数える - *------------------------------------------ - */ -int mob_counttargeted (struct mob_data *md, struct block_list *src, - int target_lv) -{ - int c = 0; - - nullpo_retr (0, md); - - map_foreachinarea (mob_counttargeted_sub, md->bl.m, - md->bl.x - AREA_SIZE, md->bl.y - AREA_SIZE, - md->bl.x + AREA_SIZE, md->bl.y + AREA_SIZE, 0, - md->bl.id, &c, src, target_lv); - return c; -} - -/*========================================== - *MOBskillから該当skillidのskillidxを返す - *------------------------------------------ - */ -int mob_skillid2skillidx (int mob_class, int skillid) -{ - int i; - struct mob_skill *ms = mob_db[mob_class].skill; - - if (ms == NULL) - return -1; - - for (i = 0; i < mob_db[mob_class].maxskill; i++) - { - if (ms[i].skill_id == skillid) - return i; - } - return -1; - -} - -// -// MOBスキル -// - -/*========================================== - * スキル使用(詠唱完了、ID指定) - *------------------------------------------ - */ -void mobskill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct mob_data *md = NULL; - struct block_list *bl; - struct block_list *mbl; - int range; - - if ((mbl = map_id2bl (id)) == NULL) //詠唱したMobがもういないというのは良くある正常処理 - return; - if ((md = (struct mob_data *) mbl) == NULL) - { - printf ("mobskill_castend_id nullpo mbl->id:%d\n", mbl->id); - return; - } - if (md->bl.type != BL_MOB || md->bl.prev == NULL) - return; - if (md->skilltimer != tid) // タイマIDの確認 - return; - - md->skilltimer = -1; - //沈黙や状態異常など - if (md->sc_data) - { - if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 - || md->sc_data[SC_ROKISWEIL].timer != -1 - || md->sc_data[SC_STEELBODY].timer != -1) - return; - if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター - return; - if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り - return; - if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク - return; - } - if (md->skillid != NPC_EMOTION) - md->last_thinktime = tick + battle_get_adelay (&md->bl); - - if ((bl = map_id2bl (md->skilltarget)) == NULL || bl->prev == NULL) - { //スキルターゲットが存在しない - //printf("mobskill_castend_id nullpo\n");//ターゲットがいないときはnullpoじゃなくて普通に終了 - return; - } - if (md->bl.m != bl->m) - return; - - if (md->skillid == PR_LEXAETERNA) - { - struct status_change *sc_data = battle_get_sc_data (bl); - if (sc_data - && (sc_data[SC_FREEZE].timer != -1 - || (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0))) - return; - } - else if (md->skillid == RG_BACKSTAP) - { - int dir = map_calc_dir (&md->bl, bl->x, bl->y), t_dir = - battle_get_dir (bl); - int dist = distance (md->bl.x, md->bl.y, bl->x, bl->y); - if (bl->type != BL_SKILL && (dist == 0 || map_check_dir (dir, t_dir))) - return; - } - if (((skill_get_inf (md->skillid) & 1) || (skill_get_inf2 (md->skillid) & 4)) && // 彼我敵対関係チェック - battle_check_target (&md->bl, bl, BCT_ENEMY) <= 0) - return; - range = skill_get_range (md->skillid, md->skilllv); - if (range < 0) - range = battle_get_range (&md->bl) - (range + 1); - if (range + battle_config.mob_skill_add_range < - distance (md->bl.x, md->bl.y, bl->x, bl->y)) - return; - - md->skilldelay[md->skillidx] = tick; - - if (battle_config.mob_skill_log == 1) - printf ("MOB skill castend skill=%d, mob_class = %d\n", md->skillid, - md->mob_class); - mob_stop_walking (md, 0); - - switch (skill_get_nk (md->skillid)) - { - // 攻撃系/吹き飛ばし系 - case 0: - case 2: - skill_castend_damage_id (&md->bl, bl, md->skillid, md->skilllv, - tick, 0); - break; - case 1: // 支援系 - if (!mob_db[md->mob_class].skill[md->skillidx].val[0] && - (md->skillid == AL_HEAL - || (md->skillid == ALL_RESURRECTION && bl->type != BL_PC)) - && battle_check_undead (battle_get_race (bl), - battle_get_elem_type (bl))) - skill_castend_damage_id (&md->bl, bl, md->skillid, - md->skilllv, tick, 0); - else - skill_castend_nodamage_id (&md->bl, bl, md->skillid, - md->skilllv, tick, 0); - break; - } -} - -/*========================================== - * スキル使用(詠唱完了、場所指定) - *------------------------------------------ - */ -void mobskill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct mob_data *md = NULL; - struct block_list *bl; - int range, maxcount; - - //mobskill_castend_id同様詠唱したMobが詠唱完了時にもういないというのはありそうなのでnullpoから除外 - if ((bl = map_id2bl (id)) == NULL) - return; - - nullpo_retv (md = (struct mob_data *) bl); - - if (md->bl.type != BL_MOB || md->bl.prev == NULL) - return; - - if (md->skilltimer != tid) // タイマIDの確認 - return; - - md->skilltimer = -1; - if (md->sc_data) - { - if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 - || md->sc_data[SC_ROKISWEIL].timer != -1 - || md->sc_data[SC_STEELBODY].timer != -1) - return; - if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター - return; - if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り - return; - if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク - return; - } - - if (battle_config.monster_skill_reiteration == 0) - { - range = -1; - switch (md->skillid) - { - case MG_SAFETYWALL: - case WZ_FIREPILLAR: - 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 PF_SPIDERWEB: /* スパイダーウェッブ */ - range = 0; - break; - case AL_PNEUMA: - case AL_WARP: - range = 1; - break; - } - if (range >= 0) - { - if (skill_check_unit_range - (md->bl.m, md->skillx, md->skilly, range, md->skillid) > 0) - return; - } - } - if (battle_config.monster_skill_nofootset == 1) - { - range = -1; - switch (md->skillid) - { - case WZ_FIREPILLAR: - 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 AM_DEMONSTRATION: - case PF_SPIDERWEB: /* スパイダーウェッブ */ - range = 1; - break; - case AL_WARP: - range = 0; - break; - } - if (range >= 0) - { - if (skill_check_unit_range2 - (md->bl.m, md->skillx, md->skilly, range) > 0) - return; - } - } - - if (battle_config.monster_land_skill_limit == 1) - { - 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) - return; - } - } - - range = skill_get_range (md->skillid, md->skilllv); - if (range < 0) - range = battle_get_range (&md->bl) - (range + 1); - if (range + battle_config.mob_skill_add_range < - distance (md->bl.x, md->bl.y, md->skillx, md->skilly)) - return; - md->skilldelay[md->skillidx] = tick; - - if (battle_config.mob_skill_log == 1) - printf ("MOB skill castend skill=%d, mob_class = %d\n", md->skillid, - md->mob_class); - mob_stop_walking (md, 0); - - skill_castend_pos2 (&md->bl, md->skillx, md->skilly, md->skillid, - md->skilllv, tick, 0); - - return; -} - -/*========================================== - * Skill use (an aria start, ID specification) - *------------------------------------------ - */ -int mobskill_use_id (struct mob_data *md, struct block_list *target, - int skill_idx) -{ - int casttime, range; - struct mob_skill *ms; - int skill_id, skill_lv, forcecast = 0; - - nullpo_retr (0, md); - nullpo_retr (0, ms = &mob_db[md->mob_class].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 (md->sc_data) - { - if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 - || md->sc_data[SC_ROKISWEIL].timer != -1 - || md->sc_data[SC_STEELBODY].timer != -1) - return 0; - if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター - return 0; - if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り - return 0; - if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク - return 0; - } - - if (md->option & 4 && skill_id == TF_HIDING) - return 0; - if (md->option & 2 && skill_id != TF_HIDING && skill_id != AS_GRIMTOOTH - && skill_id != RG_BACKSTAP && skill_id != RG_RAID) - return 0; - - if (map[md->bl.m].flag.gvg - && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT - || skill_id == AL_WARP || skill_id == WZ_ICEWALL - || skill_id == TF_BACKSLIDING)) - return 0; - - if (skill_get_inf2 (skill_id) & 0x200 && md->bl.id == target->id) - return 0; - - // 射程と障害物チェック - range = skill_get_range (skill_id, skill_lv); - if (range < 0) - range = battle_get_range (&md->bl) - (range + 1); - - if (!battle_check_range (&md->bl, target, range)) - return 0; - -// delay=skill_delayfix(&md->bl, skill_get_delay( skill_id,skill_lv) ); - - casttime = skill_castfix (&md->bl, 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 (battle_get_race (target), - battle_get_elem_type (target))) - { /* 敵がアンデッドなら */ - forcecast = 1; /* ターンアンデットと同じ詠唱時間 */ - casttime = - skill_castfix (&md->bl, - skill_get_cast (PR_TURNUNDEAD, skill_lv)); - } - break; - case MO_EXTREMITYFIST: /*阿修羅覇鳳拳 */ - case SA_MAGICROD: - case SA_SPELLBREAKER: - forcecast = 1; - break; - } - - if (battle_config.mob_skill_log == 1) - printf - ("MOB skill use target_id=%d skill=%d lv=%d cast=%d, mob_class = %d\n", - target->id, skill_id, skill_lv, casttime, md->mob_class); - - if (casttime > 0 || forcecast) - { // 詠唱が必要 -// struct mob_data *md2; - clif_skillcasting (&md->bl, - md->bl.id, target->id, 0, 0, skill_id, casttime); - - // 詠唱反応モンスター -/* if( target->type==BL_MOB && mob_db[(md2=(struct mob_data *)target)->mob_class].mode&0x10 && - md2->state.state!=MS_ATTACK){ - md2->target_id=md->bl.id; - md->state.targettype = ATTACKABLE; - md2->min_chase=13; - }*/ - } - - 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) - skill_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, range; - struct mob_skill *ms; - struct block_list bl; - int skill_id, skill_lv; - - nullpo_retr (0, md); - nullpo_retr (0, ms = &mob_db[md->mob_class].skill[skill_idx]); - - if (md->bl.prev == NULL) - return 0; - - skill_id = ms->skill_id; - skill_lv = ms->skill_lv; - - //沈黙や状態異常など - if (md->sc_data) - { - if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 - || md->sc_data[SC_ROKISWEIL].timer != -1 - || md->sc_data[SC_STEELBODY].timer != -1) - return 0; - if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター - return 0; - if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り - return 0; - if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク - return 0; - } - - if (md->option & 2) - return 0; - - if (map[md->bl.m].flag.gvg - && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT - || skill_id == AL_WARP || skill_id == WZ_ICEWALL - || skill_id == TF_BACKSLIDING)) - return 0; - - // 射程と障害物チェック - bl.type = BL_NUL; - bl.m = md->bl.m; - bl.x = skill_x; - bl.y = skill_y; - range = skill_get_range (skill_id, skill_lv); - if (range < 0) - range = battle_get_range (&md->bl) - (range + 1); - if (!battle_check_range (&md->bl, &bl, range)) - return 0; - -// delay=skill_delayfix(&sd->bl, skill_get_delay( skill_id,skill_lv) ); - casttime = skill_castfix (&md->bl, ms->casttime); - md->skilldelay[skill_idx] = gettick (); - md->state.skillcastcancel = ms->cancel; - - if (battle_config.mob_skill_log == 1) - printf - ("MOB skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d, mob_class = %d\n", - skill_x, skill_y, skill_id, skill_lv, casttime, md->mob_class); - - if (casttime > 0) // A cast time is required. - 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) - skill_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 mob_data **fr, *md, *mmd; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, mmd = va_arg (ap, struct mob_data *)); - - md = (struct mob_data *) bl; - - if (mmd->bl.id == bl->id) - return 0; - rate = va_arg (ap, int); - fr = va_arg (ap, struct mob_data **); - if (md->hp < mob_db[md->mob_class].max_hp * rate / 100) - (*fr) = md; - return 0; -} - -struct mob_data *mob_getfriendhpltmaxrate (struct mob_data *md, int rate) -{ - struct mob_data *fr = NULL; - const int r = 8; - - nullpo_retr (NULL, md); - - map_foreachinarea (mob_getfriendhpltmaxrate_sub, md->bl.m, - md->bl.x - r, md->bl.y - r, md->bl.x + r, md->bl.y + r, - BL_MOB, md, rate, &fr); - return fr; -} - -/*========================================== - * 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; - 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_STONE; j <= SC_BLIND && !flag; j++) - { - flag = (md->sc_data[j].timer != -1); - } - } - 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 *target=NULL; - int i, max_hp; - - nullpo_retr (0, md); - nullpo_retr (0, ms = mob_db[md->mob_class].skill); - - max_hp = battle_get_max_hp (&md->bl); - - if (battle_config.mob_skill_use == 0 || md->skilltimer != -1) - return 0; - - if (md->state.special_mob_ai) - return 0; - - if (md->sc_data[SC_SELFDESTRUCTION].timer != -1) //自爆中はスキルを使わない - return 0; - - for (i = 0; i < mob_db[md->mob_class].maxskill; i++) - { - int c2 = ms[i].cond2, flag = 0; - struct mob_data *fmd = NULL; - - // ディレイ中 - if (DIFF_TICK (tick, md->skilldelay[i]) < ms[i].delay) - continue; - - // 状態判定 - if (ms[i].state >= 0 && ms[i].state != md->state.skillstate) - continue; - - // 条件判定 - flag = (event == ms[i].cond1); - if (!flag) - { - switch (ms[i].cond1) - { - case MSC_ALWAYS: - flag = 1; - break; - case MSC_MYHPLTMAXRATE: // HP< maxhp% - flag = (md->hp < max_hp * c2 / 100); - break; - case MSC_MYSTATUSON: // status[num] on - case MSC_MYSTATUSOFF: // status[num] off - if (ms[i].cond2 == -1) - { - int j; - for (j = SC_STONE; j <= SC_BLIND && !flag; j++) - { - flag = (md->sc_data[j].timer != -1); - } - } - else - flag = (md->sc_data[ms[i].cond2].timer != -1); - flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); - break; - case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% - flag = - ((fmd = - 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_NOTINTOWN: // Only outside of towns. - flag = !map[md->bl.m].flag.town; - break; - case MSC_SLAVELT: // slave < num - flag = (mob_countslave (md) < c2); - break; - case MSC_ATTACKPCGT: // attack pc > num - flag = (mob_counttargeted (md, NULL, 0) > c2); - break; - case MSC_SLAVELE: // slave <= num - flag = (mob_countslave (md) <= c2); - break; - case MSC_ATTACKPCGE: // attack pc >= num - flag = (mob_counttargeted (md, NULL, 0) >= c2); - break; - case MSC_SKILLUSED: // specificated skill used - flag = ((event & 0xffff) == MSC_SKILLUSED - && ((event >> 16) == c2 || c2 == 0)); - break; - } - } - - // 確率判定 - if (flag && MRAND (10000) < ms[i].permillage) - { - - if (skill_get_inf (ms[i].skill_id) & 2) - { - // 場所指定 - struct block_list *bl = NULL; - int x = 0, y = 0; - if (ms[i].target <= MST_AROUND) - { - if (ms[i].target == MST_MASTER) - { - bl = &md->bl; - if (md->master_id) - bl = map_id2bl (md->master_id); - } - else - { - bl = ((ms[i].target == MST_TARGET - || ms[i].target == - MST_AROUND5) ? map_id2bl (md-> - target_id) - : (ms[i].target == - MST_FRIEND) ? &fmd->bl : &md->bl); - } - - if (bl) - { - 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, c, m = bl->m, r = - ms[i].target - MST_AROUND1; - do - { - bx = x + MRAND ((r * 2 + 3)) - r; - by = y + MRAND ((r * 2 + 3)) - r; - } - while ((bx <= 0 || by <= 0 || bx >= map[m].xs - || by >= map[m].ys - || ((c = read_gat (m, bx, by)) == 1 || c == 5)) - && (i++) < 1000); - if (i < 1000) - { - x = bx; - y = by; - } - } - // 相手の周囲 - if (ms[i].target >= MST_AROUND5) - { - int bx = x, by = y, i = 0, c, m = bl->m, r = - (ms[i].target - MST_AROUND5) + 1; - do - { - bx = x + MRAND ((r * 2 + 1)) - r; - by = y + MRAND ((r * 2 + 1)) - r; - } - while ((bx <= 0 || by <= 0 || bx >= map[m].xs - || by >= map[m].ys - || ((c = read_gat (m, bx, by)) == 1 || c == 5)) - && (i++) < 1000); - if (i < 1000) - { - x = bx; - y = by; - } - } - if (!mobskill_use_pos (md, x, y, i)) - return 0; - - } - else - { - if (ms[i].target == MST_MASTER) - { - struct block_list *bl = &md->bl; - if (md->master_id) - bl = map_id2bl (md->master_id); - - if (bl && !mobskill_use_id (md, bl, i)) - return 0; - } - // ID指定 - if (ms[i].target <= MST_FRIEND) - { - struct block_list *bl = NULL; - bl = ((ms[i].target == - MST_TARGET) ? map_id2bl (md-> - target_id) : (ms[i].target - == - MST_FRIEND) - ? &fmd->bl : &md->bl); - if (bl && !mobskill_use_id (md, bl, i)) - return 0; - } - } - if (ms[i].emotion >= 0) - clif_emotion (&md->bl, ms[i].emotion); - return 1; - } - } - - return 0; -} - -/*========================================== - * Skill use event processing - *------------------------------------------ - */ -int mobskill_event (struct mob_data *md, int flag) -{ - nullpo_retr (0, md); - - if (flag == -1 && mobskill_use (md, gettick (), MSC_CASTTARGETED)) - return 1; - if ((flag & BF_SHORT) - && mobskill_use (md, gettick (), MSC_CLOSEDATTACKED)) - return 1; - if ((flag & BF_LONG) - && mobskill_use (md, gettick (), MSC_LONGRANGEATTACKED)) - return 1; - return 0; -} - -/*========================================== - * Mobがエンペリウムなどの場合の判定 - *------------------------------------------ - */ -int mob_gvmobcheck (struct map_session_data *sd, struct block_list *bl) -{ - struct mob_data *md = NULL; - - nullpo_retr (0, sd); - nullpo_retr (0, bl); - - if (bl->type == BL_MOB && (md = (struct mob_data *) bl) && - (md->mob_class == 1288 || md->mob_class == 1287 || md->mob_class == 1286 - || md->mob_class == 1285)) - { - struct guild_castle *gc = guild_mapname2gc (map[sd->bl.m].name); - struct guild *g = guild_search (sd->status.guild_id); - - if (g == NULL && md->mob_class == 1288) - return 0; //ギルド未加入ならダメージ無し - else if (gc != NULL && !map[sd->bl.m].flag.gvg) - return 0; //砦内でGvじゃないときはダメージなし - else if (g && gc != NULL && g->guild_id == gc->guild_id) - return 0; //自占領ギルドのエンペならダメージ無し - else if (g && guild_checkskill (g, GD_APPROVAL) <= 0 - && md->mob_class == 1288) - return 0; //正規ギルド承認がないとダメージ無し - - } - - return 1; -} - -/*========================================== - * スキル用タイマー削除 - *------------------------------------------ - */ -int mobskill_deltimer (struct mob_data *md) -{ - nullpo_retr (0, md); - - if (md->skilltimer != -1) - { - if (skill_get_inf (md->skillid) & 2) - delete_timer (md->skilltimer, mobskill_castend_pos); - else - delete_timer (md->skilltimer, mobskill_castend_id); - md->skilltimer = -1; - } - return 0; -} - -// -// 初期化 -// -/*========================================== - * Since un-setting [ mob ] up was used, it is an initial provisional value setup. - *------------------------------------------ - */ -static int mob_makedummymobdb (int mob_class) -{ - int i; - - sprintf (mob_db[mob_class].name, "mob%d", mob_class); - sprintf (mob_db[mob_class].jname, "mob%d", mob_class); - mob_db[mob_class].lv = 1; - mob_db[mob_class].max_hp = 1000; - mob_db[mob_class].max_sp = 1; - mob_db[mob_class].base_exp = 2; - mob_db[mob_class].job_exp = 1; - mob_db[mob_class].range = 1; - mob_db[mob_class].atk1 = 7; - mob_db[mob_class].atk2 = 10; - mob_db[mob_class].def = 0; - mob_db[mob_class].mdef = 0; - mob_db[mob_class].str = 1; - mob_db[mob_class].agi = 1; - mob_db[mob_class].vit = 1; - mob_db[mob_class].int_ = 1; - mob_db[mob_class].dex = 6; - mob_db[mob_class].luk = 2; - mob_db[mob_class].range2 = 10; - mob_db[mob_class].range3 = 10; - mob_db[mob_class].size = 0; - mob_db[mob_class].race = 0; - mob_db[mob_class].element = 0; - mob_db[mob_class].mode = 0; - mob_db[mob_class].speed = 300; - mob_db[mob_class].adelay = 1000; - mob_db[mob_class].amotion = 500; - mob_db[mob_class].dmotion = 500; - mob_db[mob_class].dropitem[0].nameid = 909; // Jellopy - mob_db[mob_class].dropitem[0].p = 1000; - for (i = 1; i < 8; i++) - { - mob_db[mob_class].dropitem[i].nameid = 0; - mob_db[mob_class].dropitem[i].p = 0; - } - // Item1,Item2 - mob_db[mob_class].mexp = 0; - mob_db[mob_class].mexpper = 0; - for (i = 0; i < 3; i++) - { - mob_db[mob_class].mvpitem[i].nameid = 0; - mob_db[mob_class].mvpitem[i].p = 0; - } - for (i = 0; i < MAX_RANDOMMONSTER; i++) - mob_db[mob_class].summonper[i] = 0; - return 0; -} - -/*========================================== - * db/mob_db.txt reading - *------------------------------------------ - */ -static int mob_readdb (void) -{ - FILE *fp; - char line[1024]; - char *filename[] = { "db/mob_db.txt", "db/mob_db2.txt" }; - int i; - - memset (mob_db, 0, sizeof (mob_db)); - - for (i = 0; i < 2; i++) - { - - fp = fopen_ (filename[i], "r"); - if (fp == NULL) - { - if (i > 0) - continue; - return -1; - } - while (fgets (line, 1020, fp)) - { - int mob_class, i; - char *str[57], *p, *np; - - if (line[0] == '/' && line[1] == '/') - continue; - - for (i = 0, p = line; i < 57; i++) - { - while (*p == '\t' || *p == ' ') - p++; - if ((np = strchr (p, ',')) != NULL) - { - str[i] = p; - *np = 0; - p = np + 1; - } - else - str[i] = p; - } - - mob_class = atoi (str[0]); - if (mob_class <= 1000 || mob_class > 2000) - continue; - - mob_db[mob_class].view_class = mob_class; - memcpy (mob_db[mob_class].name, str[1], 24); - memcpy (mob_db[mob_class].jname, str[2], 24); - mob_db[mob_class].lv = atoi (str[3]); - mob_db[mob_class].max_hp = atoi (str[4]); - mob_db[mob_class].max_sp = atoi (str[5]); - - mob_db[mob_class].base_exp = atoi (str[6]); - if (mob_db[mob_class].base_exp < 0) - mob_db[mob_class].base_exp = 0; - else if (mob_db[mob_class].base_exp > 0 - && (mob_db[mob_class].base_exp * - battle_config.base_exp_rate / 100 > 1000000000 - || mob_db[mob_class].base_exp * - battle_config.base_exp_rate / 100 < 0)) - mob_db[mob_class].base_exp = 1000000000; - else - mob_db[mob_class].base_exp *= battle_config.base_exp_rate / 100; - - mob_db[mob_class].job_exp = atoi (str[7]); - if (mob_db[mob_class].job_exp < 0) - mob_db[mob_class].job_exp = 0; - else if (mob_db[mob_class].job_exp > 0 - && (mob_db[mob_class].job_exp * battle_config.job_exp_rate / - 100 > 1000000000 - || mob_db[mob_class].job_exp * - battle_config.job_exp_rate / 100 < 0)) - mob_db[mob_class].job_exp = 1000000000; - else - mob_db[mob_class].job_exp *= battle_config.job_exp_rate / 100; - - mob_db[mob_class].range = atoi (str[8]); - mob_db[mob_class].atk1 = atoi (str[9]); - mob_db[mob_class].atk2 = atoi (str[10]); - mob_db[mob_class].def = atoi (str[11]); - mob_db[mob_class].mdef = atoi (str[12]); - mob_db[mob_class].str = atoi (str[13]); - mob_db[mob_class].agi = atoi (str[14]); - mob_db[mob_class].vit = atoi (str[15]); - mob_db[mob_class].int_ = atoi (str[16]); - mob_db[mob_class].dex = atoi (str[17]); - mob_db[mob_class].luk = atoi (str[18]); - mob_db[mob_class].range2 = atoi (str[19]); - mob_db[mob_class].range3 = atoi (str[20]); - mob_db[mob_class].size = atoi (str[21]); - mob_db[mob_class].race = atoi (str[22]); - mob_db[mob_class].element = atoi (str[23]); - mob_db[mob_class].mode = atoi (str[24]); - mob_db[mob_class].speed = atoi (str[25]); - mob_db[mob_class].adelay = atoi (str[26]); - mob_db[mob_class].amotion = atoi (str[27]); - mob_db[mob_class].dmotion = atoi (str[28]); - - for (i = 0; i < 8; i++) - { - int rate = 0, type, ratemin, ratemax; - mob_db[mob_class].dropitem[i].nameid = atoi (str[29 + i * 2]); - type = itemdb_type (mob_db[mob_class].dropitem[i].nameid); - if (type == 0) - { // Added [Valaris] - rate = battle_config.item_rate_heal; - ratemin = battle_config.item_drop_heal_min; - ratemax = battle_config.item_drop_heal_max; - } - else if (type == 2) - { - rate = battle_config.item_rate_use; - ratemin = battle_config.item_drop_use_min; - ratemax = battle_config.item_drop_use_max; // End - } - else if (type == 4 || type == 5 || type == 8) - { - rate = battle_config.item_rate_equip; - ratemin = battle_config.item_drop_equip_min; - ratemax = battle_config.item_drop_equip_max; - } - else if (type == 6) - { - rate = battle_config.item_rate_card; - ratemin = battle_config.item_drop_card_min; - ratemax = battle_config.item_drop_card_max; - } - else - { - rate = battle_config.item_rate_common; - ratemin = battle_config.item_drop_common_min; - ratemax = battle_config.item_drop_common_max; - } - rate = (rate / 100) * atoi (str[30 + i * 2]); - rate = - (rate < ratemin) ? ratemin : (rate > - ratemax) ? ratemax : rate; - mob_db[mob_class].dropitem[i].p = rate; - } - // Item1,Item2 - mob_db[mob_class].mexp = - atoi (str[45]) * battle_config.mvp_exp_rate / 100; - mob_db[mob_class].mexpper = atoi (str[46]); - for (i = 0; i < 3; i++) - { - mob_db[mob_class].mvpitem[i].nameid = atoi (str[47 + i * 2]); - mob_db[mob_class].mvpitem[i].p = - atoi (str[48 + i * 2]) * battle_config.mvp_item_rate / - 100; - } - mob_db[mob_class].mutations_nr = atoi (str[55]); - mob_db[mob_class].mutation_power = atoi (str[56]); - - for (i = 0; i < MAX_RANDOMMONSTER; i++) - mob_db[mob_class].summonper[i] = 0; - mob_db[mob_class].maxskill = 0; - - mob_db[mob_class].sex = 0; - mob_db[mob_class].hair = 0; - mob_db[mob_class].hair_color = 0; - mob_db[mob_class].weapon = 0; - mob_db[mob_class].shield = 0; - mob_db[mob_class].head_top = 0; - mob_db[mob_class].head_mid = 0; - mob_db[mob_class].head_buttom = 0; - mob_db[mob_class].clothes_color = 0; //Add for player monster dye - Valaris - - if (mob_db[mob_class].base_exp == 0) - mob_db[mob_class].base_exp = mob_gen_exp (&mob_db[mob_class]); - } - fclose_ (fp); - printf ("read %s done\n", filename[i]); - } - return 0; -} - -/*========================================== - * MOB display graphic change data reading - *------------------------------------------ - */ -static int mob_readdb_mobavail (void) -{ - FILE *fp; - char line[1024]; - int ln = 0; - int mob_class, j, k; - char *str[20], *p, *np; - - if ((fp = fopen_ ("db/mob_avail.txt", "r")) == NULL) - { - printf ("can't read db/mob_avail.txt\n"); - 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; - - mob_class = atoi (str[0]); - - if (mob_class <= 1000 || mob_class > 2000) // 値が異常なら処理しない。 - continue; - k = atoi (str[1]); - if (k >= 0) - mob_db[mob_class].view_class = k; - - if ((mob_db[mob_class].view_class < 24) - || (mob_db[mob_class].view_class > 4000)) - { - mob_db[mob_class].sex = atoi (str[2]); - mob_db[mob_class].hair = atoi (str[3]); - mob_db[mob_class].hair_color = atoi (str[4]); - mob_db[mob_class].weapon = atoi (str[5]); - mob_db[mob_class].shield = atoi (str[6]); - mob_db[mob_class].head_top = atoi (str[7]); - mob_db[mob_class].head_mid = atoi (str[8]); - mob_db[mob_class].head_buttom = atoi (str[9]); - mob_db[mob_class].option = atoi (str[10]) & ~0x46; - mob_db[mob_class].clothes_color = atoi (str[11]); // Monster player dye option - Valaris - } - - else if (atoi (str[2]) > 0) - mob_db[mob_class].equip = atoi (str[2]); // mob equipment [Valaris] - - ln++; - } - fclose_ (fp); - printf ("read db/mob_avail.txt done (count=%d)\n", ln); - 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[] = { - "db/mob_branch.txt", - "db/mob_poring.txt", - "db/mob_boss.txt" - }; - - for (i = 0; i < MAX_RANDOMMONSTER; i++) - { - mob_db[0].summonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく - fp = fopen_ (mobfile[i], "r"); - if (fp == NULL) - { - printf ("can't read %s\n", mobfile[i]); - return -1; - } - while (fgets (line, 1020, fp)) - { - int mob_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; - - mob_class = atoi (str[0]); - per = atoi (str[2]); - if ((mob_class > 1000 && mob_class <= 2000) || mob_class == 0) - mob_db[mob_class].summonper[i] = per; - } - fclose_ (fp); - printf ("read %s done\n", mobfile[i]); - } - return 0; -} - -/*========================================== - * db/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}, - { - "notintown", MSC_NOTINTOWN}, - { - "attackpcgt", MSC_ATTACKPCGT}, - { - "attackpcge", MSC_ATTACKPCGE}, - { - "slavelt", MSC_SLAVELT}, - { - "slavele", MSC_SLAVELE}, - { - "closedattacked", MSC_CLOSEDATTACKED}, - { - "longrangeattacked", MSC_LONGRANGEATTACKED}, - { - "skillused", MSC_SKILLUSED}, - { - "casttargeted", MSC_CASTTARGETED},}, 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}, - { - "attack", MSS_ATTACK}, - { - "dead", MSS_DEAD}, - { - "loot", MSS_LOOT}, - { - "chase", MSS_CHASE},}, 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[] = { "db/mob_skill_db.txt", "db/mob_skill_db2.txt" }; - - for (x = 0; x < 2; x++) - { - - fp = fopen_ (filename[x], "r"); - if (fp == NULL) - { - if (x == 0) - printf ("can't read %s\n", filename[x]); - continue; - } - while (fgets (line, 1020, fp)) - { - char *sp[20], *p; - int mob_id; - struct mob_skill *ms; - 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 ((mob_id = atoi (sp[0])) <= 0) - continue; - - if (strcmp (sp[1], "clear") == 0) - { - memset (mob_db[mob_id].skill, 0, - sizeof (mob_db[mob_id].skill)); - mob_db[mob_id].maxskill = 0; - continue; - } - - for (i = 0; i < MAX_MOBSKILL; i++) - if ((ms = &mob_db[mob_id].skill[i])->skill_id == 0) - break; - if (i == MAX_MOBSKILL) - { - printf - ("mob_skill: readdb: too many skill ! [%s] in %d[%s]\n", - sp[1], mob_id, mob_db[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; - } - ms->skill_id = atoi (sp[3]); - ms->skill_lv = atoi (sp[4]); - - ms->permillage = atoi (sp[5]); - ms->casttime = atoi (sp[6]); - ms->delay = atoi (sp[7]); - 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; - mob_db[mob_id].maxskill = i + 1; - } - fclose_ (fp); - printf ("read %s done\n", filename[x]); - } - return 0; -} - -void mob_reload (void) -{ - /* - * - * <empty monster database> - * mob_read(); - * - */ - - do_init_mob (); -} - -/*========================================== - * Circumference initialization of mob - *------------------------------------------ - */ -int do_init_mob (void) -{ - mob_readdb (); - - mob_readdb_mobavail (); - mob_read_randommonster (); - mob_readskilldb (); - - 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; -} diff --git a/src/map/mob.cpp b/src/map/mob.cpp new file mode 100644 index 0000000..a722920 --- /dev/null +++ b/src/map/mob.cpp @@ -0,0 +1,5070 @@ +// $Id: mob.c,v 1.7 2004/09/25 05:32:18 MouseJstr Exp $ +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "../common/timer.hpp" +#include "../common/socket.hpp" +#include "../common/db.hpp" +#include "../common/nullpo.hpp" +#include "../common/mt_rand.hpp" +#include "map.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "pc.hpp" +#include "mob.hpp" +#include "guild.hpp" +#include "itemdb.hpp" +#include "skill.hpp" +#include "battle.hpp" +#include "party.hpp" +#include "npc.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#ifndef max +#define max( a, b ) ( ((a) > (b)) ? (a) : (b) ) +#endif + +#define MIN_MOBTHINKTIME 100 + +#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) + +struct mob_db mob_db[2001]; + +/*========================================== + * Local prototype declaration (only required thing) + *------------------------------------------ + */ +static int distance (int, int, int, int); +static int mob_makedummymobdb (int); +static void mob_timer (timer_id, tick_t, custom_id_t, custom_data_t); +int mobskill_use (struct mob_data *md, unsigned int tick, int event); +int mobskill_deltimer (struct mob_data *md); +int mob_skillid2skillidx (int mob_class, int skillid); +int mobskill_use_id (struct mob_data *md, struct block_list *target, + int skill_idx); +static int mob_unlocktarget (struct mob_data *md, int tick); + +/*========================================== + * Mob is searched with a name. + *------------------------------------------ + */ +int mobdb_searchname (const char *str) +{ + int i; + + for (i = 0; i < sizeof (mob_db) / sizeof (mob_db[0]); i++) + { + if (strcasecmp (mob_db[i].name, str) == 0 + || strcmp (mob_db[i].jname, str) == 0 + || memcmp (mob_db[i].name, str, 24) == 0 + || memcmp (mob_db[i].jname, str, 24) == 0) + return i; + } + + return 0; +} + +/*========================================== + * Id Mob is checked. + *------------------------------------------ + */ +int mobdb_checkid (const int id) +{ + if (id <= 0 || id >= (sizeof (mob_db) / sizeof (mob_db[0])) + || mob_db[id].name[0] == '\0') + return 0; + + return id; +} + +static void mob_init (struct mob_data *md); + +/*========================================== + * The minimum data set for MOB spawning + *------------------------------------------ + */ +int mob_spawn_dataset (struct mob_data *md, const char *mobname, int mob_class) +{ + nullpo_retr (0, md); + + if (strcmp (mobname, "--en--") == 0) + memcpy (md->name, mob_db[mob_class].name, 24); + else if (strcmp (mobname, "--ja--") == 0) + memcpy (md->name, mob_db[mob_class].jname, 24); + else + memcpy (md->name, mobname, 24); + + md->bl.prev = NULL; + md->bl.next = NULL; + md->n = 0; + md->base_class = md->mob_class = mob_class; + 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; + + mob_init (md); + + return 0; +} + +// Mutation values indicate how `valuable' a change to each stat is, XP wise. +// For one 256th of change, we give out that many 1024th fractions of XP change +// (i.e., 1024 means a 100% XP increase for a single point of adjustment, 4 means 100% XP bonus for doubling the value) +static int mutation_value[MOB_XP_BONUS] = { + 2, // MOB_LV + 3, // MOB_MAX_HP + 1, // MOB_STR + 2, // MOB_AGI + 1, // MOB_VIT + 0, // MOB_INT + 2, // MOB_DEX + 2, // MOB_LUK + 1, // MOB_ATK1 + 1, // MOB_ATK2 + 2, // MOB_ADELAY + 2, // MOB_DEF + 2, // MOB_MDEF + 2, // MOB_SPEED +}; + +// The mutation scale indicates how far `up' we can go, with 256 indicating 100% Note that this may stack with multiple +// calls to `mutate'. +static int mutation_scale[MOB_XP_BONUS] = { + 16, // MOB_LV + 256, // MOB_MAX_HP + 32, // MOB_STR + 48, // MOB_AGI + 48, // MOB_VIT + 48, // MOB_INT + 48, // MOB_DEX + 64, // MOB_LUK + 48, // MOB_ATK1 + 48, // MOB_ATK2 + 80, // MOB_ADELAY + 48, // MOB_DEF + 48, // MOB_MDEF + 80, // MOB_SPEED +}; + +// The table below indicates the `average' value for each of the statistics, or -1 if there is none. +// This average is used to determine XP modifications for mutations. The experience point bonus is +// based on mutation_value and mutation_base as follows: +// (1) first, compute the percentage change of the attribute (p0) +// (2) second, determine the absolute stat change +// (3) third, compute the percentage stat change relative to mutation_base (p1) +// (4) fourth, compute the XP mofication based on the smaller of (p0, p1). +static int mutation_base[MOB_XP_BONUS] = { + 30, // MOB_LV + -1, // MOB_MAX_HP + 20, // MOB_STR + 20, // MOB_AGI + 20, // MOB_VIT + 20, // MOB_INT + 20, // MOB_DEX + 20, // MOB_LUK + -1, // MOB_ATK1 + -1, // MOB_ATK2 + -1, // MOB_ADELAY + -1, // MOB_DEF + 20, // MOB_MDEF + -1, // MOB_SPEED +}; + +/*======================================== + * Mutates a MOB. For large `direction' values, calling this multiple times will give bigger XP boni. + *---------------------------------------- + */ +static void mob_mutate (struct mob_data *md, int stat, int intensity) // intensity: positive: strengthen, negative: weaken. 256 = 100%. +{ + int old_stat; + int new_stat; + int real_intensity; // relative intensity + const int mut_base = mutation_base[stat]; + int sign = 1; + + if (!md || stat < 0 || stat >= MOB_XP_BONUS || intensity == 0) + return; + + while (intensity > mutation_scale[stat]) + { + mob_mutate (md, stat, mutation_scale[stat]); // give better XP assignments + intensity -= mutation_scale[stat]; + } + while (intensity < -mutation_scale[stat]) + { + mob_mutate (md, stat, mutation_scale[stat]); // give better XP assignments + intensity += mutation_scale[stat]; + } + + if (!intensity) + return; + + // MOB_ADELAY and MOB_SPEED are special because going DOWN is good here. + if (stat == MOB_ADELAY || stat == MOB_SPEED) + sign = -1; + + // Now compute the new stat + old_stat = md->stats[stat]; + new_stat = old_stat + ((old_stat * sign * intensity) / 256); + + if (new_stat < 0) + new_stat = 0; + + if (old_stat == 0) + real_intensity = 0; + else + real_intensity = (((new_stat - old_stat) << 8) / old_stat); + + if (mut_base != -1) + { + // Now compute the mutation intensity relative to an absolute value. + // Take the lesser of the two effects. + int real_intensity2 = (((new_stat - old_stat) << 8) / mut_base); + + if (real_intensity < 0) + if (real_intensity2 > real_intensity) + real_intensity = real_intensity2; + + if (real_intensity > 0) + if (real_intensity2 < real_intensity) + real_intensity = real_intensity2; + } + + real_intensity *= sign; + + md->stats[stat] = new_stat; + + // Adjust XP value + md->stats[MOB_XP_BONUS] += mutation_value[stat] * real_intensity; + if (md->stats[MOB_XP_BONUS] <= 0) + md->stats[MOB_XP_BONUS] = 1; + + // Sanitise + if (md->stats[MOB_ATK1] > md->stats[MOB_ATK2]) + { + int swap = md->stats[MOB_ATK2]; + md->stats[MOB_ATK2] = md->stats[MOB_ATK1]; + md->stats[MOB_ATK1] = swap; + } +} + +// This calculates the exp of a given mob +int mob_gen_exp (struct mob_db *mob) +{ + if (mob->max_hp <= 1) + return 1; + double mod_def = 100 - mob->def; + if (mod_def == 0) + mod_def = 1; + double effective_hp = + ((50 - mob->luk) * mob->max_hp / 50.0) + + (2 * mob->luk * mob->max_hp / mod_def); + double attack_factor = + (mob->atk1 + mob->atk2 + mob->str / 3.0 + mob->dex / 2.0 + + mob->luk) * (1872.0 / mob->adelay) / 4; + double dodge_factor = + pow (mob->lv + mob->agi + mob->luk / 2.0, 4.0 / 3.0); + double persuit_factor = + (3 + mob->range) * (mob->mode % 2) * 1000 / mob->speed; + double aggression_factor = (mob->mode & 4) == 4 ? 10.0 / 9.0 : 1.0; + int xp = + (int) floor (effective_hp * + pow (sqrt (attack_factor) + sqrt (dodge_factor) + + sqrt (persuit_factor) + 55, + 3) * aggression_factor / 2000000.0 * + (double) battle_config.base_exp_rate / 100.); + if (xp < 1) + xp = 1; + printf ("Exp for mob '%s' generated: %d\n", mob->name, xp); + return xp; +} + +static void mob_init (struct mob_data *md) +{ + int i; + const int mob_class = md->mob_class; + const int mutations_nr = mob_db[mob_class].mutations_nr; + const int mutation_power = mob_db[mob_class].mutation_power; + + md->stats[MOB_LV] = mob_db[mob_class].lv; + md->stats[MOB_MAX_HP] = mob_db[mob_class].max_hp; + md->stats[MOB_STR] = mob_db[mob_class].str; + md->stats[MOB_AGI] = mob_db[mob_class].agi; + md->stats[MOB_VIT] = mob_db[mob_class].vit; + md->stats[MOB_INT] = mob_db[mob_class].int_; + md->stats[MOB_DEX] = mob_db[mob_class].dex; + md->stats[MOB_LUK] = mob_db[mob_class].luk; + md->stats[MOB_ATK1] = mob_db[mob_class].atk1; + md->stats[MOB_ATK2] = mob_db[mob_class].atk2; + md->stats[MOB_ADELAY] = mob_db[mob_class].adelay; + md->stats[MOB_DEF] = mob_db[mob_class].def; + md->stats[MOB_MDEF] = mob_db[mob_class].mdef; + md->stats[MOB_SPEED] = mob_db[mob_class].speed; + md->stats[MOB_XP_BONUS] = MOB_XP_BONUS_BASE; + + for (i = 0; i < mutations_nr; i++) + { + int stat_nr = MRAND (MOB_XP_BONUS + 1); + int strength; + + if (stat_nr >= MOB_XP_BONUS) + stat_nr = MOB_MAX_HP; + + strength = + ((MRAND ((mutation_power >> 1)) + + (MRAND ((mutation_power >> 1))) + + 2) * mutation_scale[stat_nr]) / 100; + + strength = MRAND (2) ? strength : -strength; + + if (strength < -240) + strength = -240; /* Don't go too close to zero */ + + mob_mutate (md, stat_nr, strength); + } +} + +/*========================================== + * 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 mob_class, int amount, + const char *event) +{ + struct mob_data *md = NULL; + int m, count, lv = 255, r = mob_class; + + 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 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) // 値が異常なら召喚を止める + return 0; + + if (mob_class < 0) + { // ランダムに召喚 + int i = 0; + int j = -mob_class - 1; + int k; + if (j >= 0 && j < MAX_RANDOMMONSTER) + { + do + { + mob_class = MPRAND (1001, 1000); + k = MRAND (1000000); + } + while ((mob_db[mob_class].max_hp <= 0 + || mob_db[mob_class].summonper[j] <= k + || (lv < mob_db[mob_class].lv + && battle_config.random_monster_checklv == 1)) + && (i++) < 2000); + if (i >= 2000) + { + mob_class = mob_db[0].summonper[j]; + } + } + else + { + return 0; + } +// if(battle_config.etc_log==1) +// printf("mobmob_class=%d try=%d\n",mob_class,i); + } + if (sd) + { + if (x <= 0) + x = sd->bl.x; + if (y <= 0) + y = sd->bl.y; + } + else if (x <= 0 || y <= 0) + { + printf ("mob_once_spawn: ??\n"); + } + + for (count = 0; count < amount; count++) + { + md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); + if (mob_db[mob_class].mode & 0x02) + md->lootitem = + (struct item *) calloc (LOOTITEM_SIZE, sizeof (struct item)); + else + md->lootitem = NULL; + + mob_spawn_dataset (md, mobname, mob_class); + md->bl.m = m; + md->bl.x = x; + md->bl.y = y; + if (r < 0 && battle_config.dead_branch_active == 1) + md->mode = 0x1 + 0x4 + 0x80; //移動してアクティブで反撃する + 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. + + memcpy (md->npc_event, event, sizeof (md->npc_event)); + + md->bl.type = BL_MOB; + map_addiddb (&md->bl); + mob_spawn (md->bl.id); + + if (mob_class == 1288) + { // emperium hp based on defense level [Valaris] + struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); + if (gc) + { + mob_db[mob_class].max_hp += 2000 * gc->defense; + md->hp = mob_db[mob_class].max_hp; + } + } // 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 mob_class, int amount, + const char *event) +{ + int x, y, i, c, 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_class >= 0 && mob_class <= 1000) || mob_class > 2000) // A summon is stopped if a value is unusual + return 0; + + for (i = 0; i < amount; i++) + { + int j = 0; + do + { + x = MPRAND (x0, (x1 - x0 + 1)); + y = MPRAND (y0, (y1 - y0 + 1)); + } + 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. + } + id = mob_once_spawn (sd, mapname, x, y, mobname, mob_class, 1, event); + lx = x; + ly = y; + } + return id; +} + +/*========================================== + * Summoning Guardians [Valaris] + *------------------------------------------ + */ +int mob_spawn_guardian (struct map_session_data *sd, char *mapname, + int x, int y, const char *mobname, int mob_class, + int amount, const char *event, int guardian) +{ + struct mob_data *md = NULL; + int m, count = 1, lv = 255; + + 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 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) // 値が異常なら召喚を止める + return 0; + + if (mob_class < 0) + return 0; + + if (sd) + { + if (x <= 0) + x = sd->bl.x; + if (y <= 0) + y = sd->bl.y; + } + + else if (x <= 0 || y <= 0) + printf ("mob_spawn_guardian: ??\n"); + + for (count = 0; count < amount; count++) + { + struct guild_castle *gc; + CREATE (md, struct mob_data, 1); + + mob_spawn_dataset (md, mobname, mob_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. + + memcpy (md->npc_event, event, sizeof (md->npc_event)); + + md->bl.type = BL_MOB; + map_addiddb (&md->bl); + mob_spawn (md->bl.id); + + gc = guild_mapname2gc (map[md->bl.m].name); + if (gc) + { + mob_db[mob_class].max_hp += 2000 * gc->defense; + if (guardian == 0) + { + md->hp = gc->Ghp0; + gc->GID0 = md->bl.id; + } + if (guardian == 1) + { + md->hp = gc->Ghp1; + gc->GID1 = md->bl.id; + } + if (guardian == 2) + { + md->hp = gc->Ghp2; + gc->GID2 = md->bl.id; + } + if (guardian == 3) + { + md->hp = gc->Ghp3; + gc->GID3 = md->bl.id; + } + if (guardian == 4) + { + md->hp = gc->Ghp4; + gc->GID4 = md->bl.id; + } + if (guardian == 5) + { + md->hp = gc->Ghp5; + gc->GID5 = md->bl.id; + } + if (guardian == 6) + { + md->hp = gc->Ghp6; + gc->GID6 = md->bl.id; + } + if (guardian == 7) + { + md->hp = gc->Ghp7; + gc->GID7 = md->bl.id; + } + + } + } + + return (amount > 0) ? md->bl.id : 0; +} + +/*========================================== + * Appearance income of mob + *------------------------------------------ + */ +int mob_get_viewclass (int mob_class) +{ + return mob_db[mob_class].view_class; +} + +int mob_get_sex (int mob_class) +{ + return mob_db[mob_class].sex; +} + +short mob_get_hair (int mob_class) +{ + return mob_db[mob_class].hair; +} + +short mob_get_hair_color (int mob_class) +{ + return mob_db[mob_class].hair_color; +} + +short mob_get_weapon (int mob_class) +{ + return mob_db[mob_class].weapon; +} + +short mob_get_shield (int mob_class) +{ + return mob_db[mob_class].shield; +} + +short mob_get_head_top (int mob_class) +{ + return mob_db[mob_class].head_top; +} + +short mob_get_head_mid (int mob_class) +{ + return mob_db[mob_class].head_mid; +} + +short mob_get_head_buttom (int mob_class) +{ + return mob_db[mob_class].head_buttom; +} + +short mob_get_clothes_color (int mob_class) // Add for player monster dye - Valaris +{ + return mob_db[mob_class].clothes_color; // End +} + +int mob_get_equip (int mob_class) // mob equip [Valaris] +{ + return mob_db[mob_class].equip; +} + +/*========================================== + * 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 (md->canmove_tick > gettick () || (md->opt1 > 0 && md->opt1 != 6) + || md->option & 2) + 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 //スパイダーウェッブ + ) + 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 battle_get_speed (&md->bl) * 14 / 10; + return battle_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, ctype; + 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; + ctype = map_getcell (md->bl.m, x, y); + if (ctype == 1 || ctype == 5) + { + mob_stop_walking (md, 1); + return 0; + } + md->dir = md->walkpath.path[md->walkpath.path_pos]; + dx = dirx[md->dir]; + dy = diry[md->dir]; + + ctype = map_getcell (md->bl.m, x + dx, y + dy); + if (ctype == 1 || ctype == 5) + { + 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); + + x += dx; + y += dy; + if (md->min_chase > 13) + md->min_chase--; + + if (moveblock) + map_delblock (&md->bl); + md->bl.x = x; + md->bl.y = y; + if (moveblock) + map_addblock (&md->bl); + + 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); + + skill_unit_move (&md->bl, tick, 1); // Inspection of a skill unit + } + if ((i = calc_next_walk_step (md)) > 0) + { + i = i >> 1; + if (i < 1 && md->walkpath.path_half == 0) + i = 1; + md->timer = + add_timer (tick + i, mob_timer, md->bl.id, md->walkpath.path_pos); + md->state.state = MS_WALK; + + if (md->walkpath.path_pos >= md->walkpath.path_len) + clif_fixmobpos (md); // When mob stops, retransmission current of a position. + } + return 0; +} + +/*========================================== + * Check if mob should be attempting to attack + *------------------------------------------ + */ +static int mob_check_attack (struct mob_data *md) +{ + struct block_list *tbl = NULL; + struct map_session_data *tsd = NULL; + struct mob_data *tmd = NULL; + + int mode, race, range; + + nullpo_retr (0, md); + + md->min_chase = 13; + md->state.state = MS_IDLE; + md->state.skillstate = MSS_IDLE; + + if (md->skilltimer != -1) + return 0; + + if (md->opt1 > 0 || md->option & 2) + return 0; + + if (md->sc_data[SC_AUTOCOUNTER].timer != -1) + return 0; + + if (md->sc_data[SC_BLADESTOP].timer != -1) + return 0; + + if ((tbl = map_id2bl (md->target_id)) == NULL) + { + md->target_id = 0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + + if (tbl->type == BL_PC) + tsd = (struct map_session_data *) tbl; + else if (tbl->type == BL_MOB) + tmd = (struct mob_data *) tbl; + else + return 0; + + if (tsd) + { + if (pc_isdead (tsd) || tsd->invincible_timer != -1 + || pc_isinvisible (tsd) || md->bl.m != tbl->m || tbl->prev == NULL + || distance (md->bl.x, md->bl.y, tbl->x, tbl->y) >= 13) + { + md->target_id = 0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + } + if (tmd) + { + if (md->bl.m != tbl->m || tbl->prev == NULL + || distance (md->bl.x, md->bl.y, tbl->x, tbl->y) >= 13) + { + md->target_id = 0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + } + + if (!md->mode) + mode = mob_db[md->mob_class].mode; + else + mode = md->mode; + + race = mob_db[md->mob_class].race; + if (!(mode & 0x80)) + { + md->target_id = 0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + if (tsd && !(mode & 0x20) && (tsd->sc_data[SC_TRICKDEAD].timer != -1 || + ((pc_ishiding (tsd) + || tsd->state.gangsterparadise) + && race != 4 && race != 6))) + { + md->target_id = 0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + + range = mob_db[md->mob_class].range; + if (mode & 1) + range++; + if (distance (md->bl.x, md->bl.y, tbl->x, tbl->y) > range) + return 0; + + return 1; +} + +static int mob_ancillary_attack(struct block_list *bl, va_list ap) +{ + struct block_list *mdbl = va_arg(ap, struct block_list *); + struct block_list *tbl = va_arg(ap, struct block_list *); + unsigned int tick = va_arg(ap, unsigned int); + if (bl != tbl) + battle_weapon_attack(mdbl, bl, tick, 0); + return 0; +} + +/*========================================== + * Attack processing of mob + *------------------------------------------ + */ +static int mob_attack (struct mob_data *md, unsigned int tick, int data) +{ + struct block_list *tbl = NULL; + + nullpo_retr (0, md); + + if ((tbl = map_id2bl (md->target_id)) == NULL) + return 0; + + if (!mob_check_attack (md)) + return 0; + + if (battle_config.monster_attack_direction_change) + md->dir = map_calc_dir (&md->bl, tbl->x, tbl->y); // 向き設定 + + //clif_fixmobpos(md); + + md->state.skillstate = MSS_ATTACK; + if (mobskill_use (md, tick, -2)) // スキル使用 + return 0; + + md->target_lv = battle_weapon_attack (&md->bl, tbl, tick, 0); + // If you are reading this, please note: + // it is highly platform-specific that this even works at all. + int radius = battle_config.mob_splash_radius; + if (radius >= 0 && tbl->type == BL_PC && !map[tbl->m].flag.town) + map_foreachinarea(mob_ancillary_attack, + tbl->m, tbl->x - radius, tbl->y - radius, tbl->x + radius, tbl->y + radius, + BL_PC, + &md->bl, tbl, tick); + + if (!(battle_config.monster_cloak_check_type & 2) + && md->sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end (&md->bl, SC_CLOAKING, -1); + + md->attackabletime = tick + battle_get_adelay (&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 + battle_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_out_all (&md->bl, gettick (), 1); + skill_status_change_clear (&md->bl, 2); // The abnormalities in status are canceled. + skill_clear_unitgroup (&md->bl); // All skill unit groups are deleted. + 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 = 0; + md->state.targettype = NONE_ATTACKABLE; + break; + } + + return 0; +} + +/*========================================== + * timer processing of mob (timer function) + * It branches to a walk and an attack. + *------------------------------------------ + */ +static void mob_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct mob_data *md; + struct block_list *bl; + + if ((bl = map_id2bl (id)) == NULL) + { //攻撃してきた敵がもういないのは正常のようだ + return; + } + + if (!bl || !bl->type || bl->type != BL_MOB) + return; + + nullpo_retv (md = (struct mob_data *) bl); + + if (!md->bl.type || md->bl.type != BL_MOB) + return; + + if (md->timer != tid) + { + if (battle_config.error_log == 1) + printf ("mob_timer %d != %d\n", md->timer, tid); + return; + } + md->timer = -1; + if (md->bl.prev == NULL || md->state.state == MS_DEAD) + return; + + map_freeblock_lock (); + switch (md->state.state) + { + case MS_WALK: + mob_check_attack (md); + 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 == 1) + printf ("mob_timer : %d ?\n", md->state.state); + break; + } + map_freeblock_unlock (); + return; +} + +/*========================================== + * + *------------------------------------------ + */ +static int mob_walktoxy_sub (struct mob_data *md) +{ + struct walkpath_data wpd; + + nullpo_retr (0, md); + + 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; + 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->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->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 void mob_delayspawn (timer_id tid, tick_t tick, custom_id_t m, custom_data_t n) +{ + mob_spawn (m); +} + +/*========================================== + * 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) + return -1; + + if (!bl || !bl->type || bl->type != BL_MOB) + return -1; + + nullpo_retr (-1, md = (struct mob_data *) bl); + + if (!md || md->bl.type != BL_MOB) + return -1; + + // Processing of MOB which is not revitalized + if (md->spawndelay1 == -1 && md->spawndelay2 == -1 && md->n == 0) + { + map_deliddb (&md->bl); + if (md->lootitem) + { + map_freeblock (md->lootitem); + md->lootitem = NULL; + } + map_freeblock (md); // Instead of [ of free ] + return 0; + } + + spawntime1 = md->last_spawntime + md->spawndelay1; + spawntime2 = md->last_deadtime + md->spawndelay2; + spawntime3 = gettick () + 5000; + // 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; +} + +/*========================================== + * Mob spawning. Initialization is also variously here. + *------------------------------------------ + */ +int mob_spawn (int id) +{ + int x = 0, y = 0, i = 0, c; + unsigned int tick = gettick (); + struct mob_data *md; + struct block_list *bl; + + nullpo_retr (-1, bl = map_id2bl (id)); + + if (!bl || !bl->type || bl->type != BL_MOB) + return -1; + + nullpo_retr (-1, md = (struct mob_data *) bl); + + if (!md || !md->bl.type || md->bl.type != BL_MOB) + return -1; + + md->last_spawntime = tick; + if (md->bl.prev != NULL) + { +// clif_clearchar_area(&md->bl,3); + skill_unit_out_all (&md->bl, gettick (), 1); + map_delblock (&md->bl); + } + else + md->mob_class = md->base_class; + + md->bl.m = md->m; + do + { + if (md->x0 == 0 && md->y0 == 0) + { + x = MPRAND (1, (map[md->bl.m].xs - 2)); + y = MPRAND (1, (map[md->bl.m].ys - 2)); + } + else + { + x = MPRAND (md->x0, (md->xs + 1)) - md->xs / 2; + y = MPRAND (md->y0, (md->ys + 1)) - md->ys / 2; + } + i++; + } + while (((c = map_getcell (md->bl.m, x, y)) == 1 || c == 5) && i < 50); + + if (i >= 50) + { +// if(battle_config.error_log==1) +// printf("MOB spawn error %d @ %s\n",id,map[md->bl.m].name); + 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; + + map_addblock (&md->bl); + + memset (&md->state, 0, sizeof (md->state)); + md->attacked_id = 0; + md->target_id = 0; + md->move_fail_count = 0; + mob_init (md); + + if (!md->stats[MOB_SPEED]) + md->stats[MOB_SPEED] = mob_db[md->mob_class].speed; + md->def_ele = mob_db[md->mob_class].element; + 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 + MPRAND (5000, 50); + md->attackabletime = tick; + md->canmove_tick = tick; + + md->sg_count = 0; + 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)); + 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; + + memset (md->skillunit, 0, sizeof (md->skillunit)); + memset (md->skillunittick, 0, sizeof (md->skillunittick)); + + md->hp = battle_get_max_hp (&md->bl); + if (md->hp <= 0) + { + mob_makedummymobdb (md->mob_class); + md->hp = battle_get_max_hp (&md->bl); + } + + clif_spawnmob (md); + + return 0; +} + +/*========================================== + * Distance calculation between two points + *------------------------------------------ + */ +static int distance (int x0, int y0, int x1, int y1) +{ + int dx, dy; + + dx = abs (x0 - x1); + dy = abs (y0 - y1); + return dx > dy ? dx : dy; +} + +/*========================================== + * 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; + 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 & 4) + { + 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); + if (type & 0x02) + { + int delay = battle_get_dmotion (&md->bl); + unsigned int tick = gettick (); + if (md->canmove_tick < tick) + md->canmove_tick = tick + delay; + } + + return 0; +} + +/*========================================== + * Reachability to a Specification ID existence place + *------------------------------------------ + */ +int mob_can_reach (struct mob_data *md, struct block_list *bl, int range) +{ + int dx, dy; + struct walkpath_data wpd; + int i; + + nullpo_retr (0, md); + nullpo_retr (0, bl); + + dx = abs (bl->x - md->bl.x); + dy = abs (bl->y - md->bl.y); + + //=========== guildcastle guardian no search start=========== + //when players are the guild castle member not attack them ! + if (md->mob_class == 1285 || md->mob_class == 1286 || md->mob_class == 1287) + { + struct map_session_data *sd; + struct guild *g = NULL; + struct guild_castle *gc = guild_mapname2gc (map[bl->m].name); + + if (gc && agit_flag == 0) // Guardians will not attack during non-woe time [Valaris] + return 0; // end addition [Valaris] + + if (bl && bl->type == BL_PC) + { + if ((sd = (struct map_session_data *) bl) == NULL) + { + printf ("mob_can_reach nullpo\n"); + return 0; + } + + if (gc && sd && sd->status.guild_id && sd->status.guild_id > 0) + { + g = guild_search (sd->status.guild_id); // don't attack guild members [Valaris] + if (g && g->guild_id > 0 && g->guild_id == gc->guild_id) + return 0; + if (g && gc && guild_isallied (g, gc)) + return 0; + + } + } + } + //========== guildcastle guardian no search eof============== + + if (bl && bl->type == BL_PC && battle_config.monsters_ignore_gm == 1) + { // option to have monsters ignore GMs [Valaris] + struct map_session_data *sd; + if ((sd = (struct map_session_data *) bl) != NULL && pc_isGM (sd)) + return 0; + } + + if (md->bl.m != bl->m) // 違うャbプ + return 0; + + if (range > 0 && range < ((dx > dy) ? dx : dy)) // 遠すぎる + return 0; + + if (md->bl.x == bl->x && md->bl.y == bl->y) // 同じャX + return 1; + + // 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, 0) != + -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, 0) != -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, 0) != -1) + return 1; + } + return 0; +} + +/*========================================== + * Determination for an attack of a monster + *------------------------------------------ + */ +int mob_target (struct mob_data *md, struct block_list *bl, int dist) +{ + struct map_session_data *sd; + struct status_change *sc_data; + short *option; + int mode, race; + + nullpo_retr (0, md); + nullpo_retr (0, bl); + + sc_data = battle_get_sc_data (bl); + option = battle_get_option (bl); + race = mob_db[md->mob_class].race; + + if (!md->mode) + { + mode = mob_db[md->mob_class].mode; + } + else + { + mode = md->mode; + } + if (!(mode & 0x80)) + { + md->target_id = 0; + return 0; + } + // 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) + && (!(mode & 0x04) || MRAND (100) > 25)) + return 0; + + if (mode & 0x20 || // Coercion is exerted if it is MVPMOB. + (sc_data && sc_data[SC_TRICKDEAD].timer == -1 && + ((option && !(*option & 0x06)) || race == 4 || race == 6))) + { + if (bl->type == BL_PC) + { + nullpo_retr (0, sd = (struct map_session_data *) bl); + if (sd->invincible_timer != -1 || pc_isinvisible (sd)) + return 0; + if (!(mode & 0x20) && race != 4 && race != 6 + && sd->state.gangsterparadise) + 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; + md->min_chase = dist + 13; + 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 map_session_data *tsd = NULL; + struct mob_data *smd, *tmd = NULL; + int mode, race, dist, *pcc; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, smd = va_arg (ap, struct mob_data *)); + nullpo_retr (0, pcc = va_arg (ap, int *)); + + if (bl->type == BL_PC) + tsd = (struct map_session_data *) bl; + else if (bl->type == BL_MOB) + tmd = (struct mob_data *) bl; + else + return 0; + + //敵味方判定 + if (battle_check_target (&smd->bl, bl, BCT_ENEMY) == 0) + return 0; + + if (!smd->mode) + mode = mob_db[smd->mob_class].mode; + else + mode = smd->mode; + + // アクティブでターゲット射程内にいるなら、ロックする + if (mode & 0x04) + { + race = mob_db[smd->mob_class].race; + //対象がPCの場合 + if (tsd && + !pc_isdead (tsd) && + tsd->bl.m == smd->bl.m && + tsd->invincible_timer == -1 && + !pc_isinvisible (tsd) && + (dist = + distance (smd->bl.x, smd->bl.y, tsd->bl.x, tsd->bl.y)) < 9) + { + if (mode & 0x20 || + (tsd->sc_data[SC_TRICKDEAD].timer == -1 && + ((!pc_ishiding (tsd) && !tsd->state.gangsterparadise) + || race == 4 || race == 6))) + { // 妨害がないか判定 + if (mob_can_reach (smd, bl, 12) && // 到達可能性判定 + MRAND (1000) < 1000 / (++(*pcc))) + { // 範囲内PCで等確率にする + smd->target_id = tsd->bl.id; + smd->state.targettype = ATTACKABLE; + smd->min_chase = 13; + } + } + } + //対象がMobの場合 + else if (tmd && + tmd->bl.m == smd->bl.m && + (dist = + distance (smd->bl.x, smd->bl.y, tmd->bl.x, tmd->bl.y)) < 9) + { + if (mob_can_reach (smd, bl, 12) && // 到達可能性判定 + MRAND (1000) < 1000 / (++(*pcc))) + { // 範囲内で等確率にする + smd->target_id = bl->id; + smd->state.targettype = ATTACKABLE; + smd->min_chase = 13; + } + } + } + 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 mode, 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->mode) + { + mode = mob_db[md->mob_class].mode; + } + else + { + mode = md->mode; + } + + if (!md->target_id && mode & 0x02) + { + if (!md->lootitem + || (battle_config.monster_loot_type == 1 + && md->lootitem_count >= LOOTITEM_SIZE)) + return 0; + if (bl->m == md->bl.m + && (dist = distance (md->bl.x, md->bl.y, bl->x, bl->y)) < 9) + { + if (mob_can_reach (md, bl, 12) && // Reachability judging + MRAND (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 = 13; + } + } + } + return 0; +} + +/*========================================== + * The ?? routine of a link monster + *------------------------------------------ + */ +static int mob_ai_sub_hard_linksearch (struct block_list *bl, va_list ap) +{ + struct mob_data *tmd; + struct mob_data *md; + struct block_list *target; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, tmd = (struct mob_data *) bl); + nullpo_retr (0, md = va_arg (ap, struct mob_data *)); + nullpo_retr (0, target = va_arg (ap, struct block_list *)); + + // same family free in a range at a link monster -- it will be made to lock if MOB is +/* if( (md->target_id > 0 && md->state.targettype == ATTACKABLE) && mob_db[md->mob_class].mode&0x08){ + if( tmd->mob_class==md->mob_class && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE) && tmd->bl.m == md->bl.m){ + if( mob_can_reach(tmd,target,12) ){ // Reachability judging + tmd->target_id=md->target_id; + tmd->state.targettype = ATTACKABLE; + tmd->min_chase=13; + } + } + }*/ + if (md->attacked_id > 0 && mob_db[md->mob_class].mode & 0x08) + { + if (tmd->mob_class == md->mob_class && tmd->bl.m == md->bl.m + && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE)) + { + if (mob_can_reach (tmd, target, 12)) + { // Reachability judging + tmd->target_id = md->attacked_id; + tmd->state.targettype = ATTACKABLE; + tmd->min_chase = 13; + } + } + } + + return 0; +} + +/*========================================== + * Processing of slave monsters + *------------------------------------------ + */ +static int mob_ai_sub_hard_slavemob (struct mob_data *md, unsigned int tick) +{ + struct mob_data *mmd = NULL; + struct block_list *bl; + int mode, race, old_dist; + + nullpo_retr (0, md); + + if ((bl = map_id2bl (md->master_id)) != NULL) + mmd = (struct mob_data *) bl; + + mode = mob_db[md->mob_class].mode; + + // It is not main monster/leader. + if (!mmd || mmd->bl.type != BL_MOB || mmd->bl.id != md->master_id) + return 0; + + // Since it is in the map on which the master is not, teleport is carried out and it pursues. + if (mmd->bl.m != md->bl.m) + { + mob_warp (md, mmd->bl.m, mmd->bl.x, mmd->bl.y, 3); + md->state.master_check = 1; + return 0; + } + + // Distance with between slave and master is measured. + old_dist = md->master_dist; + md->master_dist = distance (md->bl.x, md->bl.y, mmd->bl.x, mmd->bl.y); + + // 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, mmd->bl.x, mmd->bl.y, 3); + md->state.master_check = 1; + 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->walkpath.path_pos >= md->walkpath.path_len + || md->walkpath.path_len == 0) && md->master_dist < 15) + { + int i = 0, dx, dy, ret; + if (md->master_dist > 4) + { + do + { + if (i <= 5) + { + dx = mmd->bl.x - md->bl.x; + dy = mmd->bl.y - md->bl.y; + if (dx < 0) + dx += (MPRAND (1, ((dx < -3) ? 3 : -dx))); + else if (dx > 0) + dx -= (MPRAND (1, ((dx > 3) ? 3 : dx))); + if (dy < 0) + dy += (MPRAND (1, ((dy < -3) ? 3 : -dy))); + else if (dy > 0) + dy -= (MPRAND (1, ((dy > 3) ? 3 : dy))); + } + else + { + dx = mmd->bl.x - md->bl.x + MRAND (7) - 3; + dy = mmd->bl.y - md->bl.y + MRAND (7) - 3; + } + + ret = mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, 0); + i++; + } + while (ret && i < 10); + } + else + { + do + { + dx = MRAND (9) - 5; + dy = MRAND (9) - 5; + if (dx == 0 && dy == 0) + { + dx = (MRAND (1)) ? 1 : -1; + dy = (MRAND (1)) ? 1 : -1; + } + dx += mmd->bl.x; + dy += mmd->bl.y; + + ret = mob_walktoxy (md, mmd->bl.x + dx, mmd->bl.y + dy, 0); + i++; + } + while (ret && i < 10); + } + + md->next_walktime = tick + 500; + md->state.master_check = 1; + } + + // There is the master, the master locks a target and he does not lock. + if ((mmd->target_id > 0 && mmd->state.targettype == ATTACKABLE) + && (!md->target_id || md->state.targettype == NONE_ATTACKABLE)) + { + struct map_session_data *sd = map_id2sd (mmd->target_id); + if (sd != NULL && !pc_isdead (sd) && sd->invincible_timer == -1 + && !pc_isinvisible (sd)) + { + + race = mob_db[md->mob_class].race; + if (mode & 0x20 || + (sd->sc_data[SC_TRICKDEAD].timer == -1 && + ((!pc_ishiding (sd) && !sd->state.gangsterparadise) + || race == 4 || race == 6))) + { // 妨害がないか判定 + + md->target_id = sd->bl.id; + md->state.targettype = ATTACKABLE; + md->min_chase = + 5 + distance (md->bl.x, md->bl.y, sd->bl.x, sd->bl.y); + md->state.master_check = 1; + } + } + } + + // There is the master, the master locks a target and he does not lock. +/* if( (md->target_id>0 && mmd->state.targettype == ATTACKABLE) && (!mmd->target_id || mmd->state.targettype == NONE_ATTACKABLE) ){ + struct map_session_data *sd=map_id2sd(md->target_id); + if(sd!=NULL && !pc_isdead(sd) && sd->invincible_timer == -1 && !pc_isinvisible(sd)){ + + race=mob_db[mmd->mob_class].race; + if(mode&0x20 || + (sd->sc_data[SC_TRICKDEAD].timer == -1 && + (!(sd->status.option&0x06) || race==4 || race==6) + ) ){ // It judges whether there is any disturbance. + + mmd->target_id=sd->bl.id; + mmd->state.targettype = ATTACKABLE; + mmd->min_chase=5+distance(mmd->bl.x,mmd->bl.y,sd->bl.x,sd->bl.y); + } + } + }*/ + + return 0; +} + +/*========================================== + * A lock of target is stopped and mob moves to a standby state. + *------------------------------------------ + */ +static 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 + MPRAND (3000, 3000); + return 0; +} + +/*========================================== + * Random walk + *------------------------------------------ + */ +static int mob_randomwalk (struct mob_data *md, int tick) +{ + const int retrycount = 20; + int speed; + + nullpo_retr (0, md); + + speed = battle_get_speed (&md->bl); + if (DIFF_TICK (md->next_walktime, tick) < 0) + { + int i, x, y, c, d = 12 - md->move_fail_count; + if (d < 5) + d = 5; + for (i = 0; i < retrycount; i++) + { // Search of a movable place + int r = mt_random (); + x = md->bl.x + r % (d * 2 + 1) - d; + y = md->bl.y + r / (d * 2 + 1) % (d * 2 + 1) - d; + if ((c = map_getcell (md->bl.m, x, y)) != 1 && c != 5 + && mob_walktoxy (md, x, y, 1) == 0) + { + md->move_fail_count = 0; + break; + } + if (i + 1 >= retrycount) + { + md->move_fail_count++; + if (md->move_fail_count > 1000) + { + if (battle_config.error_log == 1) + printf + ("MOB cant move. random spawn %d, mob_class = %d\n", + md->bl.id, md->mob_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 + MPRAND (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, *tmd = NULL; + struct map_session_data *tsd = NULL; + struct block_list *tbl = NULL; + struct flooritem_data *fitem; + unsigned int tick; + int i, dx, dy, ret, dist; + int attack_type = 0; + int mode, race; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, md = (struct mob_data *) bl); + + tick = va_arg (ap, unsigned int); + + if (DIFF_TICK (tick, md->last_thinktime) < MIN_MOBTHINKTIME) + return 0; + md->last_thinktime = tick; + + if (md->skilltimer != -1 || md->bl.prev == NULL) + { // Under a skill aria and death + if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME) + md->next_walktime = tick; + return 0; + } + + if (!md->mode) + mode = mob_db[md->mob_class].mode; + else + mode = md->mode; + + race = mob_db[md->mob_class].race; + + // Abnormalities + if ((md->opt1 > 0 && md->opt1 != 6) || md->state.state == MS_DELAY + || md->sc_data[SC_BLADESTOP].timer != -1) + return 0; + + if (!(mode & 0x80) && md->target_id > 0) + md->target_id = 0; + + if (md->attacked_id > 0 && mode & 0x08) + { // Link monster + struct map_session_data *asd = map_id2sd (md->attacked_id); + if (asd) + { + if (asd->invincible_timer == -1 && !pc_isinvisible (asd)) + { + map_foreachinarea (mob_ai_sub_hard_linksearch, md->bl.m, + md->bl.x - 13, md->bl.y - 13, + md->bl.x + 13, md->bl.y + 13, + BL_MOB, md, &asd->bl); + } + } + } + + // It checks to see it was attacked first (if active, it is target change at 25% of probability). + if (mode > 0 && md->attacked_id > 0 + && (!md->target_id || md->state.targettype == NONE_ATTACKABLE + || (mode & 0x04 && MRAND (100) < 25))) + { + struct block_list *abl = map_id2bl (md->attacked_id); + struct map_session_data *asd = NULL; + if (abl) + { + if (abl->type == BL_PC) + asd = (struct map_session_data *) abl; + if (asd == NULL || md->bl.m != abl->m || abl->prev == NULL + || asd->invincible_timer != -1 || pc_isinvisible (asd) + || (dist = + distance (md->bl.x, md->bl.y, abl->x, abl->y)) >= 32 + || battle_check_target (bl, abl, BCT_ENEMY) == 0) + md->attacked_id = 0; + else + { + md->target_id = md->attacked_id; // set target + md->state.targettype = ATTACKABLE; + attack_type = 1; + md->attacked_id = 0; + md->min_chase = dist + 13; + if (md->min_chase > 26) + md->min_chase = 26; + } + } + } + + md->state.master_check = 0; + // Processing of slave monster + if (md->master_id > 0 && md->state.special_mob_ai == 0) + mob_ai_sub_hard_slavemob (md, tick); + + // アクティヴモンスターの策敵 (?? of a bitter taste TIVU monster) + if ((!md->target_id || md->state.targettype == NONE_ATTACKABLE) + && mode & 0x04 && !md->state.master_check + && battle_config.monster_active_enable == 1) + { + i = 0; + if (md->state.special_mob_ai) + { + map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m, + md->bl.x - AREA_SIZE * 2, + md->bl.y - AREA_SIZE * 2, + md->bl.x + AREA_SIZE * 2, + md->bl.y + AREA_SIZE * 2, 0, md, &i); + } + else + { + map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m, + md->bl.x - AREA_SIZE * 2, + md->bl.y - AREA_SIZE * 2, + md->bl.x + AREA_SIZE * 2, + md->bl.y + AREA_SIZE * 2, BL_PC, md, &i); + } + } + + // The item search of a route monster + if (!md->target_id && mode & 0x02 && !md->state.master_check) + { + i = 0; + map_foreachinarea (mob_ai_sub_hard_lootsearch, md->bl.m, + md->bl.x - AREA_SIZE * 2, md->bl.y - AREA_SIZE * 2, + md->bl.x + AREA_SIZE * 2, md->bl.y + AREA_SIZE * 2, + BL_ITEM, md, &i); + } + + // It will attack, if the candidate for an attack is. + if (md->target_id > 0) + { + if ((tbl = map_id2bl (md->target_id))) + { + if (tbl->type == BL_PC) + tsd = (struct map_session_data *) tbl; + else if (tbl->type == BL_MOB) + tmd = (struct mob_data *) tbl; + if (tsd || tmd) + { + if (tbl->m != md->bl.m || tbl->prev == NULL + || (dist = + distance (md->bl.x, md->bl.y, tbl->x, + tbl->y)) >= md->min_chase) + mob_unlocktarget (md, tick); // 別マップか、視界外 + else if (tsd && !(mode & 0x20) + && (tsd->sc_data[SC_TRICKDEAD].timer != -1 + || + ((pc_ishiding (tsd) + || tsd->state.gangsterparadise) && race != 4 + && race != 6))) + mob_unlocktarget (md, tick); // スキルなどによる策敵妨害 + else if (!battle_check_range + (&md->bl, tbl, mob_db[md->mob_class].range)) + { + // 攻撃範囲外なので移動 + if (!(mode & 1)) + { // 移動しないモード + mob_unlocktarget (md, tick); + return 0; + } + if (!mob_can_move (md)) // 動けない状態にある + return 0; + md->state.skillstate = MSS_CHASE; // 突撃時スキル + mobskill_use (md, tick, -1); +// if(md->timer != -1 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tsd->bl.x,tsd->bl.y)<2) ) + if (md->timer != -1 && md->state.state != MS_ATTACK + && (DIFF_TICK (md->next_walktime, tick) < 0 + || distance (md->to_x, md->to_y, tbl->x, + tbl->y) < 2)) + return 0; // 既に移動中 + if (!mob_can_reach + (md, tbl, (md->min_chase > 13) ? md->min_chase : 13)) + mob_unlocktarget (md, tick); // 移動できないのでタゲ解除(IWとか?) + else + { + // 追跡 + md->next_walktime = tick + 500; + i = 0; + do + { + if (i == 0) + { // 最初はAEGISと同じ方法で検索 + 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--; + } + else + { // だめならAthena式(ランダム) + dx = tbl->x - md->bl.x + MRAND (3) - 1; + dy = tbl->y - md->bl.y + MRAND (3) - 1; + } + /* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ + * dx=tsd->bl.x - md->bl.x; + * dy=tsd->bl.y - md->bl.y; + * if(dx<0) dx--; + * else if(dx>0) dx++; + * if(dy<0) dy--; + * else if(dy>0) dy++; + * } */ + ret = + mob_walktoxy (md, md->bl.x + dx, + md->bl.y + dy, 0); + 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; + mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, + 0); + } + } + } + else + { // 攻撃射程範囲内 + md->state.skillstate = MSS_ATTACK; + if (md->state.state == MS_WALK) + mob_stop_walking (md, 1); // 歩行中なら停止 + if (md->state.state == MS_ATTACK) + return 0; // 既に攻撃中 + mob_changestate (md, MS_ATTACK, attack_type); + +/* if(mode&0x08){ // リンクモンスター + map_foreachinarea(mob_ai_sub_hard_linksearch,md->bl.m, + md->bl.x-13,md->bl.y-13, + md->bl.x+13,md->bl.y+13, + BL_MOB,md,&tsd->bl); + }*/ + } + return 0; + } + else + { // ルートモンスター処理 + if (tbl == NULL || tbl->type != BL_ITEM || tbl->m != md->bl.m + || (dist = + distance (md->bl.x, md->bl.y, tbl->x, + tbl->y)) >= md->min_chase || !md->lootitem) + { + // 遠すぎるかアイテムがなくなった + mob_unlocktarget (md, tick); + if (md->state.state == MS_WALK) + mob_stop_walking (md, 1); // 歩行中なら停止 + } + else if (dist) + { + if (!(mode & 1)) + { // 移動しないモード + 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 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tbl->x,tbl->y)<2) ) + if (md->timer != -1 && md->state.state != MS_ATTACK + && (DIFF_TICK (md->next_walktime, tick) < 0 + || distance (md->to_x, md->to_y, tbl->x, + tbl->y) <= 0)) + return 0; // 既に移動中 + md->next_walktime = tick + 500; + dx = tbl->x - md->bl.x; + dy = tbl->y - md->bl.y; +/* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ + dx=tbl->x - md->bl.x; + dy=tbl->y - md->bl.y; + }*/ + ret = mob_walktoxy (md, md->bl.x + dx, md->bl.y + dy, 0); + if (ret) + mob_unlocktarget (md, tick); // 移動できないのでタゲ解除(IWとか?) + } + else + { // アイテムまでたどり着いた + if (md->state.state == MS_ATTACK) + return 0; // 攻撃中 + if (md->state.state == MS_WALK) + mob_stop_walking (md, 1); // 歩行中なら停止 + fitem = (struct flooritem_data *) tbl; + if (md->lootitem_count < LOOTITEM_SIZE) + memcpy (&md->lootitem[md->lootitem_count++], + &fitem->item_data, sizeof (md->lootitem[0])); + else if (battle_config.monster_loot_type == 1 + && md->lootitem_count >= LOOTITEM_SIZE) + { + mob_unlocktarget (md, tick); + return 0; + } + else + { + 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])); + } + map_clearflooritem (tbl->id); + mob_unlocktarget (md, tick); + } + return 0; + } + } + else + { + mob_unlocktarget (md, tick); + if (md->state.state == MS_WALK) + mob_stop_walking (md, 4); // 歩行中なら停止 + return 0; + } + } + + // It is skill use at the time of /standby at the time of a walk. + if (mobskill_use (md, tick, -1)) + return 0; + + // 歩行処理 + if (mode & 1 && mob_can_move (md) && // 移動可能MOB&動ける状態にある + (md->master_id == 0 || md->state.special_mob_ai + || md->master_dist > 10)) + { //取り巻きMOBじゃない + + 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 * MRAND (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 void mob_ai_hard (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + clif_foreachclient (mob_ai_sub_foreachclient, tick); +} + +/*========================================== + * Negligent mode MOB AI (PC is not in near) + *------------------------------------------ + */ +static void mob_ai_sub_lazy (db_key_t key, db_val_t data, va_list app) +{ + struct mob_data *md = (struct mob_data *)data; + unsigned int tick; + va_list ap; + + nullpo_retv (md); + nullpo_retv (app); + nullpo_retv (ap = va_arg (app, va_list)); + + if (md == NULL) + return; + + if (!md->bl.type || md->bl.type != BL_MOB) + return; + + tick = va_arg (ap, unsigned int); + + if (DIFF_TICK (tick, md->last_thinktime) < MIN_MOBTHINKTIME * 10) + return; + md->last_thinktime = tick; + + if (md->bl.prev == NULL || md->skilltimer != -1) + { + if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME * 10) + md->next_walktime = tick; + return; + } + + if (DIFF_TICK (md->next_walktime, tick) < 0 && + (mob_db[md->mob_class].mode & 1) && 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 (MRAND (1000) < MOB_LAZYMOVEPERC) + mob_randomwalk (md, tick); + + // MOB which is not not the summons MOB but BOSS, either sometimes reboils. + else if (MRAND (1000) < MOB_LAZYWARPPERC && md->x0 <= 0 + && md->master_id != 0 && mob_db[md->mob_class].mexp <= 0 + && !(mob_db[md->mob_class].mode & 0x20)) + mob_spawn (md->bl.id); + + } + 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 (MRAND (1000) < MOB_LAZYWARPPERC && md->x0 <= 0 + && md->master_id != 0 && mob_db[md->mob_class].mexp <= 0 + && !(mob_db[md->mob_class].mode & 0x20)) + mob_warp (md, -1, -1, -1, -1); + } + + md->next_walktime = tick + MPRAND (5000, 10000); + } +} + +/*========================================== + * Negligent processing for mob outside PC field of view (interval timer function) + *------------------------------------------ + */ +static void mob_ai_lazy (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + map_foreachiddb (mob_ai_sub_lazy, tick); +} + +/*========================================== + * 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; + 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; +}; + +/*========================================== + * item drop with delay (timer function) + *------------------------------------------ + */ +static void mob_delay_item_drop (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct delay_item_drop *ditem; + struct item temp_item; + int flag; + + nullpo_retv (ditem = (struct delay_item_drop *) id); + + memset (&temp_item, 0, sizeof (temp_item)); + temp_item.nameid = ditem->nameid; + temp_item.amount = ditem->amount; + temp_item.identify = !itemdb_isequip3 (temp_item.nameid); + + if (battle_config.item_auto_get == 1) + { + if (ditem->first_sd + && (flag = + pc_additem (ditem->first_sd, &temp_item, ditem->amount))) + { + clif_additem (ditem->first_sd, 0, 0, flag); + map_addflooritem (&temp_item, 1, ditem->m, ditem->x, ditem->y, + ditem->first_sd, ditem->second_sd, + ditem->third_sd, 0); + } + free (ditem); + return; + } + + map_addflooritem (&temp_item, 1, ditem->m, ditem->x, ditem->y, + ditem->first_sd, ditem->second_sd, ditem->third_sd, 0); + + free (ditem); +} + +/*========================================== + * item drop (timer function)-lootitem with delay + *------------------------------------------ + */ +static void mob_delay_item_drop2 (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct delay_item_drop2 *ditem; + int flag; + + nullpo_retv (ditem = (struct delay_item_drop2 *) id); + + if (battle_config.item_auto_get == 1) + { + if (ditem->first_sd + && (flag = + pc_additem (ditem->first_sd, &ditem->item_data, + ditem->item_data.amount))) + { + clif_additem (ditem->first_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); + } + free (ditem); + return; + } + + 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); + + free (ditem); +} + +/*========================================== + * mob data is erased. + *------------------------------------------ + */ +int mob_delete (struct mob_data *md) +{ + nullpo_retr (1, md); + + if (md->bl.prev == NULL) + return 1; + mob_changestate (md, MS_DEAD, 0); + clif_clearchar_area (&md->bl, 1); + map_delblock (&md->bl); + if (mob_get_viewclass (md->mob_class) <= 1000) + clif_clearchar_delay (gettick () + 3000, &md->bl, 0); + mob_deleteslave (md); + mob_setdelayspawn (md->bl.id); + return 0; +} + +int mob_catch_delete (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); + mob_setdelayspawn (md->bl.id); + return 0; +} + +void mob_timer_delete (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct block_list *bl = map_id2bl (id); + struct mob_data *md; + + nullpo_retv (bl); + + md = (struct mob_data *) bl; + mob_catch_delete (md, 3); +} + +/*========================================== + * + *------------------------------------------ + */ +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_foreachinarea (mob_deleteslave_sub, md->bl.m, + 0, 0, map[md->bl.m].xs, map[md->bl.m].ys, + BL_MOB, md->bl.id); + return 0; +} + +#define DAMAGE_BONUS_COUNT 6 // max. number of players to account for +const static double damage_bonus_factor[DAMAGE_BONUS_COUNT + 1] = { + 1.0, 1.0, 2.0, 2.5, 2.75, 2.9, 3.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; + } 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; + double dmg_rate, tdmg, temp; + struct item item; + int ret; + int drop_rate; + int skill, sp; + + nullpo_retr (0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック + + if (src && src->id == md->master_id + && md->mode & MOB_MODE_TURNS_AGAINST_BAD_MASTER) + { + /* If the master hits a monster, have the monster turn against him */ + md->master_id = 0; + md->mode = 0x85; /* Regular war mode */ + md->target_id = src->id; + md->attacked_id = src->id; + } + + max_hp = battle_get_max_hp (&md->bl); + + if (src && src->type == BL_PC) + { + sd = (struct map_session_data *) src; + mvp_sd = sd; + } + +// if(battle_config.battle_log) +// printf("mob_damage %d %d %d\n",md->hp,max_hp,damage); + if (md->bl.prev == NULL) + { + if (battle_config.error_log == 1) + printf ("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; + } + + if (md->sc_data[SC_ENDURE].timer == -1) + mob_stop_walking (md, 3); + if (damage > max_hp >> 2) + skill_stop_dancing (&md->bl, 0); + + if (md->hp > max_hp) + md->hp = max_hp; + + // The amount of overkill rounds to hp. + if (damage > md->hp) + damage = md->hp; + + if (!(type & 2)) + { + if (sd != NULL) + { + for (i = 0, minpos = 0, mindmg = 0x7fffffff; i < DAMAGELOG_SIZE; + i++) + { + if (md->dmglog[i].id == sd->bl.id) + break; + if (md->dmglog[i].id == 0) + { + minpos = i; + mindmg = 0; + } + else 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 = sd->bl.id; + md->dmglog[minpos].dmg = damage; + } + + if (md->attacked_id <= 0 && md->state.special_mob_ai == 0) + md->attacked_id = sd->bl.id; + } + if (src && src->type == BL_MOB + && ((struct mob_data *) src)->state.special_mob_ai) + { + struct mob_data *md2 = (struct mob_data *) src; + struct block_list *master_bl = map_id2bl (md2->master_id); + if (master_bl && master_bl->type == BL_PC) + { + MAP_LOG_PC (((struct map_session_data *) master_bl), + "MOB-TO-MOB-DMG FROM MOB%d %d TO MOB%d %d FOR %d", + md2->bl.id, md2->mob_class, md->bl.id, md->mob_class, + damage); + } + + nullpo_retr (0, md2); + for (i = 0, minpos = 0, mindmg = 0x7fffffff; i < DAMAGELOG_SIZE; + i++) + { + if (md->dmglog[i].id == md2->master_id) + break; + if (md->dmglog[i].id == 0) + { + minpos = i; + mindmg = 0; + } + else 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 = md2->master_id; + md->dmglog[minpos].dmg = damage; + + if (md->attacked_id <= 0 && md->state.special_mob_ai == 0) + md->attacked_id = md2->master_id; + } + } + + } + + md->hp -= damage; + + if (md->mob_class >= 1285 && md->mob_class <= 1287) + { // guardian hp update [Valaris] + struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); + if (gc) + { + + if (md->bl.id == gc->GID0) + { + gc->Ghp0 = md->hp; + if (gc->Ghp0 <= 0) + { + guild_castledatasave (gc->castle_id, 10, 0); + guild_castledatasave (gc->castle_id, 18, 0); + } + } + if (md->bl.id == gc->GID1) + { + gc->Ghp1 = md->hp; + if (gc->Ghp1 <= 0) + { + guild_castledatasave (gc->castle_id, 11, 0); + guild_castledatasave (gc->castle_id, 19, 0); + } + } + if (md->bl.id == gc->GID2) + { + gc->Ghp2 = md->hp; + if (gc->Ghp2 <= 0) + { + guild_castledatasave (gc->castle_id, 12, 0); + guild_castledatasave (gc->castle_id, 20, 0); + } + } + if (md->bl.id == gc->GID3) + { + gc->Ghp3 = md->hp; + if (gc->Ghp3 <= 0) + { + guild_castledatasave (gc->castle_id, 13, 0); + guild_castledatasave (gc->castle_id, 21, 0); + } + } + if (md->bl.id == gc->GID4) + { + gc->Ghp4 = md->hp; + if (gc->Ghp4 <= 0) + { + guild_castledatasave (gc->castle_id, 14, 0); + guild_castledatasave (gc->castle_id, 22, 0); + } + } + if (md->bl.id == gc->GID5) + { + gc->Ghp5 = md->hp; + if (gc->Ghp5 <= 0) + { + guild_castledatasave (gc->castle_id, 15, 0); + guild_castledatasave (gc->castle_id, 23, 0); + } + } + if (md->bl.id == gc->GID6) + { + gc->Ghp6 = md->hp; + if (gc->Ghp6 <= 0) + { + guild_castledatasave (gc->castle_id, 16, 0); + guild_castledatasave (gc->castle_id, 24, 0); + } + } + if (md->bl.id == gc->GID7) + { + gc->Ghp7 = md->hp; + if (gc->Ghp7 <= 0) + { + guild_castledatasave (gc->castle_id, 17, 0); + guild_castledatasave (gc->castle_id, 25, 0); + + } + } + } + } // end addition [Valaris] + + if (md->option & 2) + skill_status_change_end (&md->bl, SC_HIDING, -1); + if (md->option & 4) + skill_status_change_end (&md->bl, SC_CLOAKING, -1); + + if (md->state.special_mob_ai == 2) + { //スフィアーマイン + int skillidx = 0; + + if ((skillidx = + mob_skillid2skillidx (md->mob_class, NPC_SELFDESTRUCTION2)) >= 0) + { + md->mode |= 0x1; + md->next_walktime = tick; + mobskill_use_id (md, &md->bl, skillidx); //自爆詠唱開始 + md->state.special_mob_ai++; + } + } + + if (md->hp > 0) + { + return 0; + } + + MAP_LOG ("MOB%d DEAD", md->bl.id); + + // ----- ここから死亡処理 ----- + + 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 = battle_get_max_hp (&md->bl); + + if (src && src->type == BL_MOB) + mob_unlocktarget ((struct mob_data *) src, tick); + + /* ソウルドレイン */ + if (sd && (skill = pc_checkskill (sd, HW_SOULDRAIN)) > 0) + { + clif_skill_nodamage (src, &md->bl, HW_SOULDRAIN, skill, 1); + sp = (battle_get_lv (&md->bl)) * (65 + 15 * skill) / 100; + if (sd->status.sp + sp > sd->status.max_sp) + sp = sd->status.max_sp - sd->status.sp; + sd->status.sp += sp; + clif_heal (sd->fd, SP_SP, sp); + } + + // map外に消えた人は計算から除くので + // overkill分は無いけどsumはmax_hpとは違う + + tdmg = 0; + for (i = 0, count = 0, mvp_damage = 0; i < DAMAGELOG_SIZE; i++) + { + if (md->dmglog[i].id == 0) + continue; + tmpsd[i] = map_id2sd (md->dmglog[i].id); + if (tmpsd[i] == NULL) + continue; + count++; + if (tmpsd[i]->bl.m != md->bl.m || pc_isdead (tmpsd[i])) + continue; + + tdmg += (double) md->dmglog[i].dmg; + 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)) + { + + if ((double) max_hp < tdmg) + dmg_rate = ((double) max_hp) / tdmg; + else + dmg_rate = 1; + + // 経験値の分配 + for (i = 0; i < DAMAGELOG_SIZE; i++) + { + + int pid, base_exp, job_exp, flag = 1; + double per; + struct party *p; + if (tmpsd[i] == NULL || tmpsd[i]->bl.m != md->bl.m) + continue; +/* jAthena's exp formula + per = ((double)md->dmglog[i].dmg)*(9.+(double)((count > 6)? 6:count))/10./((double)max_hp) * dmg_rate; + temp = ((double)mob_db[md->mob_class].base_exp * (double)battle_config.base_exp_rate / 100. * per); + base_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; + if(mob_db[md->mob_class].base_exp > 0 && base_exp < 1) base_exp = 1; + if(base_exp < 0) base_exp = 0; + temp = ((double)mob_db[md->mob_class].job_exp * (double)battle_config.job_exp_rate / 100. * per); + job_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; + if(mob_db[md->mob_class].job_exp > 0 && job_exp < 1) job_exp = 1; + if(job_exp < 0) job_exp = 0; +*/ +//eAthena's exp formula rather than jAthena's +// per=(double)md->dmglog[i].dmg*256*(9+(double)((count > 6)? 6:count))/10/(double)max_hp; + // [Fate] The above is the old formula. We do a more involved computation below. + per = (double) md->dmglog[i].dmg * 256 / (double) max_hp; // 256 = 100% of the score + per *= damage_bonus_factor[count > DAMAGE_BONUS_COUNT ? DAMAGE_BONUS_COUNT : count]; // Bonus for party attack + if (per > 512) + per = 512; // [Fate] Retained from before. The maximum a single individual can get is double the original value. + if (per < 1) + per = 1; + + base_exp = + ((mob_db[md->mob_class].base_exp * + md->stats[MOB_XP_BONUS]) >> MOB_XP_BONUS_SHIFT) * per / 256; + if (base_exp < 1) + base_exp = 1; + if (sd && md && battle_config.pk_mode == 1 + && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) + { + base_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris] + } + if (md->state.special_mob_ai >= 1 + && battle_config.alchemist_summon_reward != 1) + base_exp = 0; // Added [Valaris] + job_exp = mob_db[md->mob_class].job_exp * per / 256; + if (job_exp < 1) + job_exp = 1; + if (sd && md && battle_config.pk_mode == 1 + && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) + { + job_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris] + } + if (md->state.special_mob_ai >= 1 + && battle_config.alchemist_summon_reward != 1) + job_exp = 0; // Added [Valaris] + + if ((pid = tmpsd[i]->status.party_id) > 0) + { // パーティに入っている + int j = 0; + 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; + pnum++; + flag = 0; + } + } + else + { // いるときは公平 + pt[j].base_exp += base_exp; + pt[j].job_exp += job_exp; + flag = 0; + } + } + if (flag) // 各自所得 + pc_gainexp (tmpsd[i], base_exp, job_exp); + } + // 公平分配 + for (i = 0; i < pnum; i++) + party_exp_share (pt[i].p, md->bl.m, pt[i].base_exp, + pt[i].job_exp); + + // item drop + if (!(type & 1)) + { + for (i = 0; i < 8; i++) + { + struct delay_item_drop *ditem; + int drop_rate; + + if (md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) // Added [Valaris] + break; // End + + if (mob_db[md->mob_class].dropitem[i].nameid <= 0) + continue; + drop_rate = mob_db[md->mob_class].dropitem[i].p; + if (drop_rate <= 0 && battle_config.drop_rate0item == 1) + drop_rate = 1; + if (battle_config.drops_by_luk > 0 && sd && md) + drop_rate += (sd->status.luk * battle_config.drops_by_luk) / 100; // drops affected by luk [Valaris] + if (sd && md && battle_config.pk_mode == 1 + && (mob_db[md->mob_class].lv - sd->status.base_level >= 20)) + drop_rate *= 1.25; // pk_mode increase drops if 20 level difference [Valaris] + if (drop_rate <= MRAND (10000)) + continue; + + ditem = (struct delay_item_drop *) + calloc (1, sizeof (struct delay_item_drop)); + ditem->nameid = mob_db[md->mob_class].dropitem[i].nameid; + ditem->amount = 1; + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer (tick + 500 + i, mob_delay_item_drop, (int) ditem, 0); + } + if (sd && sd->state.attack_type == BF_WEAPON) + { + for (i = 0; i < sd->monster_drop_item_count; i++) + { + struct delay_item_drop *ditem; + int race = battle_get_race (&md->bl); + if (sd->monster_drop_itemid[i] <= 0) + continue; + if (sd->monster_drop_race[i] & (1 << race) || + (mob_db[md->mob_class].mode & 0x20 + && sd->monster_drop_race[i] & 1 << 10) + || (!(mob_db[md->mob_class].mode & 0x20) + && sd->monster_drop_race[i] & 1 << 11)) + { + if (sd->monster_drop_itemrate[i] <= MRAND (10000)) + continue; + + ditem = (struct delay_item_drop *) + calloc (1, sizeof (struct delay_item_drop)); + ditem->nameid = sd->monster_drop_itemid[i]; + ditem->amount = 1; + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer (tick + 520 + i, mob_delay_item_drop, + (int) ditem, 0); + } + } + if (sd->get_zeny_num > 0) + pc_getzeny (sd, + mob_db[md->mob_class].lv * 10 + + MRAND ((sd->get_zeny_num + 1))); + } + if (md->lootitem) + { + for (i = 0; i < md->lootitem_count; i++) + { + struct delay_item_drop2 *ditem; + + ditem = (struct delay_item_drop2 *) + calloc (1, sizeof (struct delay_item_drop2)); + memcpy (&ditem->item_data, &md->lootitem[i], + sizeof (md->lootitem[0])); + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer (tick + 540 + i, mob_delay_item_drop2, + (int) ditem, 0); + } + } + } + + // mvp処理 + if (mvp_sd && mob_db[md->mob_class].mexp > 0) + { + int j; + int mexp = battle_get_mexp (&md->bl); + temp = + ((double) mexp * (double) battle_config.mvp_exp_rate * + (9. + (double) count) / 1000.); + mexp = (temp > 2147483647.) ? 0x7fffffff : (int) temp; + if (mexp < 1) + mexp = 1; + clif_mvp_effect (mvp_sd); // エフェクト + clif_mvp_exp (mvp_sd, mexp); + pc_gainexp (mvp_sd, mexp, 0); + for (j = 0; j < 3; j++) + { + i = MRAND (3); + if (mob_db[md->mob_class].mvpitem[i].nameid <= 0) + continue; + drop_rate = mob_db[md->mob_class].mvpitem[i].p; + if (drop_rate <= 0 && battle_config.drop_rate0item == 1) + drop_rate = 1; + if (drop_rate < battle_config.item_drop_mvp_min) + drop_rate = battle_config.item_drop_mvp_min; + if (drop_rate > battle_config.item_drop_mvp_max) + drop_rate = battle_config.item_drop_mvp_max; + if (drop_rate <= MRAND (10000)) + continue; + memset (&item, 0, sizeof (item)); + item.nameid = mob_db[md->mob_class].mvpitem[i].nameid; + item.identify = !itemdb_isequip3 (item.nameid); + clif_mvp_item (mvp_sd, 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); + } + break; + } + } + + } // [MouseJstr] + + // <Agit> NPC Event [OnAgitBreak] + if (md->npc_event[0] + && strcmp (((md->npc_event) + strlen (md->npc_event) - 13), + "::OnAgitBreak") == 0) + { + printf ("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 (sd == NULL) + { + if (mvp_sd != NULL) + sd = mvp_sd; + else + { + struct map_session_data *tmpsd; + int i; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (tmpsd = (struct map_session_data *)session[i]->session_data) + && tmpsd->state.auth) + { + if (md->bl.m == tmpsd->bl.m) + { + sd = tmpsd; + break; + } + } + } + } + } + if (sd) + npc_event (sd, md->npc_event, 0); + } + + clif_clearchar_area (&md->bl, 1); + map_delblock (&md->bl); + if (mob_get_viewclass (md->mob_class) <= 1000) + clif_clearchar_delay (tick + 3000, &md->bl, 0); + mob_deleteslave (md); + mob_setdelayspawn (md->bl.id); + map_freeblock_unlock (); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int mob_class_change (struct mob_data *md, int *value) +{ + unsigned int tick = gettick (); + int i, c, hp_rate, max_hp, mob_class, count = 0; + + nullpo_retr (0, md); + nullpo_retr (0, value); + + if (value[0] <= 1000 || value[0] > 2000) + return 0; + if (md->bl.prev == NULL) + return 0; + + while (count < 5 && value[count] > 1000 && value[count] <= 2000) + count++; + if (count < 1) + return 0; + + mob_class = value[MRAND (count)]; + if (mob_class <= 1000 || mob_class > 2000) + return 0; + + max_hp = battle_get_max_hp (&md->bl); + hp_rate = md->hp * 100 / max_hp; + clif_mob_class_change (md, mob_class); + md->mob_class = mob_class; + max_hp = battle_get_max_hp (&md->bl); + if (battle_config.monster_class_change_full_recover == 1) + { + md->hp = max_hp; + memset (md->dmglog, 0, sizeof (md->dmglog)); + } + else + md->hp = max_hp * hp_rate / 100; + if (md->hp > max_hp) + md->hp = max_hp; + else if (md->hp < 1) + md->hp = 1; + + memcpy (md->name, mob_db[mob_class].jname, 24); + memset (&md->state, 0, sizeof (md->state)); + md->attacked_id = 0; + md->target_id = 0; + md->move_fail_count = 0; + + md->stats[MOB_SPEED] = mob_db[md->mob_class].speed; + md->def_ele = mob_db[md->mob_class].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 + MPRAND (5000, 50); + md->attackabletime = tick; + md->canmove_tick = tick; + md->sg_count = 0; + + 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 && mob_db[mob_class].mode & 0x02) + md->lootitem = (struct item *) + calloc (LOOTITEM_SIZE, sizeof (struct item)); + + skill_clear_unitgroup (&md->bl); + skill_cleartimerskill (&md->bl); + + clif_clearchar_area (&md->bl, 0); + clif_spawnmob (md); + + return 0; +} + +/*========================================== + * mob回復 + *------------------------------------------ + */ +int mob_heal (struct mob_data *md, int heal) +{ + int max_hp = battle_get_max_hp (&md->bl); + + nullpo_retr (0, md); + + md->hp += heal; + if (max_hp < md->hp) + md->hp = max_hp; + + if (md->mob_class >= 1285 && md->mob_class <= 1287) + { // guardian hp update [Valaris] + struct guild_castle *gc = guild_mapname2gc (map[md->bl.m].name); + if (gc) + { + if (md->bl.id == gc->GID0) + gc->Ghp0 = md->hp; + if (md->bl.id == gc->GID1) + gc->Ghp1 = md->hp; + if (md->bl.id == gc->GID2) + gc->Ghp2 = md->hp; + if (md->bl.id == gc->GID3) + gc->Ghp3 = md->hp; + if (md->bl.id == gc->GID4) + gc->Ghp4 = md->hp; + if (md->bl.id == gc->GID5) + gc->Ghp5 = md->hp; + if (md->bl.id == gc->GID6) + gc->Ghp6 = md->hp; + if (md->bl.id == gc->GID7) + gc->Ghp7 = md->hp; + } + } // end addition [Valaris] + + return 0; +} + +/*========================================== + * Added by RoVeRT + *------------------------------------------ + */ +int mob_warpslave_sub (struct block_list *bl, va_list ap) +{ + struct mob_data *md = (struct mob_data *) bl; + int id, x, y; + id = va_arg (ap, int); + x = va_arg (ap, int); + y = va_arg (ap, int); + if (md->master_id == id) + { + mob_warp (md, -1, x, y, 2); + } + return 0; +} + +/*========================================== + * Added by RoVeRT + *------------------------------------------ + */ +int mob_warpslave (struct mob_data *md, int x, int y) +{ +//printf("warp slave\n"); + map_foreachinarea (mob_warpslave_sub, md->bl.m, + x - AREA_SIZE, y - AREA_SIZE, + x + AREA_SIZE, y + AREA_SIZE, BL_MOB, + md->bl.id, md->bl.x, md->bl.y); + return 0; +} + +/*========================================== + * mobワープ + *------------------------------------------ + */ +int mob_warp (struct mob_data *md, int m, int x, int y, int type) +{ + int i = 0, c, xs = 0, ys = 0, bx = x, by = y; + + 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; + clif_clearchar_area (&md->bl, type); + } + skill_unit_out_all (&md->bl, gettick (), 1); + map_delblock (&md->bl); + + if (bx > 0 && by > 0) + { // 位置指定の場合周囲9セルを探索 + xs = ys = 9; + } + + while ((x < 0 || y < 0 || ((c = read_gat (m, x, y)) == 1 || c == 5)) + && (i++) < 1000) + { + if (xs > 0 && ys > 0 && i < 250) + { // 指定位置付近の探索 + x = MPRAND (bx, xs) - xs / 2; + y = MPRAND (by, ys) - ys / 2; + } + else + { // 完全ランダム探索 + x = MPRAND (1, (map[m].xs - 2)); + y = MPRAND (1, (map[m].ys - 2)); + } + } + 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) + printf ("MOB %d warp failed, mob_class = %d\n", md->bl.id, md->mob_class); + } + + md->target_id = 0; // タゲを解除する + md->state.targettype = NONE_ATTACKABLE; + md->attacked_id = 0; + md->state.skillstate = MSS_IDLE; + mob_changestate (md, MS_IDLE, 0); + + if (type > 0 && i == 1000) + { + if (battle_config.battle_log == 1) + printf ("MOB %d warp to (%d,%d), mob_class = %d\n", md->bl.id, x, y, + md->mob_class); + } + + map_addblock (&md->bl); + if (type > 0) + { + clif_spawnmob (md); + mob_warpslave (md, md->bl.x, md->bl.y); + } + + 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); + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, c = va_arg (ap, int *)); + nullpo_retr (0, md = (struct mob_data *) bl); + + if (md->master_id == id) + (*c)++; + return 0; +} + +/*========================================== + * 画面内の取り巻きの数計算 + *------------------------------------------ + */ +int mob_countslave (struct mob_data *md) +{ + int c = 0; + + nullpo_retr (0, md); + + map_foreachinarea (mob_countslave_sub, md->bl.m, + 0, 0, map[md->bl.m].xs - 1, map[md->bl.m].ys - 1, + BL_MOB, md->bl.id, &c); + return c; +} + +/*========================================== + * 手下MOB召喚 + *------------------------------------------ + */ +int mob_summonslave (struct mob_data *md2, int *value, int amount, int flag) +{ + struct mob_data *md; + int bx, by, m, count = 0, mob_class, k, a = amount; + + nullpo_retr (0, md2); + nullpo_retr (0, value); + + bx = md2->bl.x; + by = md2->bl.y; + m = md2->bl.m; + + if (value[0] <= 1000 || value[0] > 2000) // 値が異常なら召喚を止める + return 0; + while (count < 5 && value[count] > 1000 && value[count] <= 2000) + count++; + if (count < 1) + return 0; + + for (k = 0; k < count; k++) + { + amount = a; + mob_class = value[k]; + if (mob_class <= 1000 || mob_class > 2000) + continue; + for (; amount > 0; amount--) + { + int x = 0, y = 0, c = 0, i = 0; + md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); + if (mob_db[mob_class].mode & 0x02) + md->lootitem = (struct item *) + calloc (LOOTITEM_SIZE, sizeof (struct item)); + else + md->lootitem = NULL; + + while ((x <= 0 || y <= 0 || (c = map_getcell (m, x, y)) == 1 + || c == 5) && (i++) < 100) + { + x = MPRAND (bx, 9) - 4; + y = MPRAND (by, 9) - 4; + } + if (i >= 100) + { + x = bx; + y = by; + } + + mob_spawn_dataset (md, "--ja--", mob_class); + md->bl.prev = NULL; + md->bl.next = NULL; + 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->stats[MOB_SPEED] = md2->stats[MOB_SPEED]; + md->spawndelay1 = -1; // 一度のみフラグ + md->spawndelay2 = -1; // 一度のみフラグ + + 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, + (flag) ? NPC_SUMMONSLAVE : NPC_SUMMONMONSTER, + a, 1); + + if (flag) + md->master_id = md2->bl.id; + } + } + return 0; +} + +/*========================================== + * 自分をロックしているPCの数を数える(foreachclient) + *------------------------------------------ + */ +static int mob_counttargeted_sub (struct block_list *bl, va_list ap) +{ + int id, *c, target_lv; + struct block_list *src; + + id = va_arg (ap, int); + nullpo_retr (0, bl); + nullpo_retr (0, ap); + 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)++; + } + return 0; +} + +/*========================================== + * 自分をロックしているPCの数を数える + *------------------------------------------ + */ +int mob_counttargeted (struct mob_data *md, struct block_list *src, + int target_lv) +{ + int c = 0; + + nullpo_retr (0, md); + + map_foreachinarea (mob_counttargeted_sub, md->bl.m, + md->bl.x - AREA_SIZE, md->bl.y - AREA_SIZE, + md->bl.x + AREA_SIZE, md->bl.y + AREA_SIZE, 0, + md->bl.id, &c, src, target_lv); + return c; +} + +/*========================================== + *MOBskillから該当skillidのskillidxを返す + *------------------------------------------ + */ +int mob_skillid2skillidx (int mob_class, int skillid) +{ + int i; + struct mob_skill *ms = mob_db[mob_class].skill; + + if (ms == NULL) + return -1; + + for (i = 0; i < mob_db[mob_class].maxskill; i++) + { + if (ms[i].skill_id == skillid) + return i; + } + return -1; + +} + +// +// MOBスキル +// + +/*========================================== + * スキル使用(詠唱完了、ID指定) + *------------------------------------------ + */ +void mobskill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct mob_data *md = NULL; + struct block_list *bl; + struct block_list *mbl; + int range; + + if ((mbl = map_id2bl (id)) == NULL) //詠唱したMobがもういないというのは良くある正常処理 + return; + if ((md = (struct mob_data *) mbl) == NULL) + { + printf ("mobskill_castend_id nullpo mbl->id:%d\n", mbl->id); + return; + } + if (md->bl.type != BL_MOB || md->bl.prev == NULL) + return; + if (md->skilltimer != tid) // タイマIDの確認 + return; + + md->skilltimer = -1; + //沈黙や状態異常など + if (md->sc_data) + { + if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 + || md->sc_data[SC_ROKISWEIL].timer != -1 + || md->sc_data[SC_STEELBODY].timer != -1) + return; + if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return; + if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return; + if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return; + } + if (md->skillid != NPC_EMOTION) + md->last_thinktime = tick + battle_get_adelay (&md->bl); + + if ((bl = map_id2bl (md->skilltarget)) == NULL || bl->prev == NULL) + { //スキルターゲットが存在しない + //printf("mobskill_castend_id nullpo\n");//ターゲットがいないときはnullpoじゃなくて普通に終了 + return; + } + if (md->bl.m != bl->m) + return; + + if (md->skillid == PR_LEXAETERNA) + { + struct status_change *sc_data = battle_get_sc_data (bl); + if (sc_data + && (sc_data[SC_FREEZE].timer != -1 + || (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0))) + return; + } + else if (md->skillid == RG_BACKSTAP) + { + int dir = map_calc_dir (&md->bl, bl->x, bl->y), t_dir = + battle_get_dir (bl); + int dist = distance (md->bl.x, md->bl.y, bl->x, bl->y); + if (bl->type != BL_SKILL && (dist == 0 || map_check_dir (dir, t_dir))) + return; + } + if (((skill_get_inf (md->skillid) & 1) || (skill_get_inf2 (md->skillid) & 4)) && // 彼我敵対関係チェック + battle_check_target (&md->bl, bl, BCT_ENEMY) <= 0) + return; + range = skill_get_range (md->skillid, md->skilllv); + if (range < 0) + range = battle_get_range (&md->bl) - (range + 1); + if (range + battle_config.mob_skill_add_range < + distance (md->bl.x, md->bl.y, bl->x, bl->y)) + return; + + md->skilldelay[md->skillidx] = tick; + + if (battle_config.mob_skill_log == 1) + printf ("MOB skill castend skill=%d, mob_class = %d\n", md->skillid, + md->mob_class); + mob_stop_walking (md, 0); + + switch (skill_get_nk (md->skillid)) + { + // 攻撃系/吹き飛ばし系 + case 0: + case 2: + skill_castend_damage_id (&md->bl, bl, md->skillid, md->skilllv, + tick, 0); + break; + case 1: // 支援系 + if (!mob_db[md->mob_class].skill[md->skillidx].val[0] && + (md->skillid == AL_HEAL + || (md->skillid == ALL_RESURRECTION && bl->type != BL_PC)) + && battle_check_undead (battle_get_race (bl), + battle_get_elem_type (bl))) + skill_castend_damage_id (&md->bl, bl, md->skillid, + md->skilllv, tick, 0); + else + skill_castend_nodamage_id (&md->bl, bl, md->skillid, + md->skilllv, tick, 0); + break; + } +} + +/*========================================== + * スキル使用(詠唱完了、場所指定) + *------------------------------------------ + */ +void mobskill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct mob_data *md = NULL; + struct block_list *bl; + int range, maxcount; + + //mobskill_castend_id同様詠唱したMobが詠唱完了時にもういないというのはありそうなのでnullpoから除外 + if ((bl = map_id2bl (id)) == NULL) + return; + + nullpo_retv (md = (struct mob_data *) bl); + + if (md->bl.type != BL_MOB || md->bl.prev == NULL) + return; + + if (md->skilltimer != tid) // タイマIDの確認 + return; + + md->skilltimer = -1; + if (md->sc_data) + { + if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 + || md->sc_data[SC_ROKISWEIL].timer != -1 + || md->sc_data[SC_STEELBODY].timer != -1) + return; + if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return; + if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return; + if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return; + } + + if (battle_config.monster_skill_reiteration == 0) + { + range = -1; + switch (md->skillid) + { + case MG_SAFETYWALL: + case WZ_FIREPILLAR: + 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 PF_SPIDERWEB: /* スパイダーウェッブ */ + range = 0; + break; + case AL_PNEUMA: + case AL_WARP: + range = 1; + break; + } + if (range >= 0) + { + if (skill_check_unit_range + (md->bl.m, md->skillx, md->skilly, range, md->skillid) > 0) + return; + } + } + if (battle_config.monster_skill_nofootset == 1) + { + range = -1; + switch (md->skillid) + { + case WZ_FIREPILLAR: + 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 AM_DEMONSTRATION: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + range = 1; + break; + case AL_WARP: + range = 0; + break; + } + if (range >= 0) + { + if (skill_check_unit_range2 + (md->bl.m, md->skillx, md->skilly, range) > 0) + return; + } + } + + if (battle_config.monster_land_skill_limit == 1) + { + 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) + return; + } + } + + range = skill_get_range (md->skillid, md->skilllv); + if (range < 0) + range = battle_get_range (&md->bl) - (range + 1); + if (range + battle_config.mob_skill_add_range < + distance (md->bl.x, md->bl.y, md->skillx, md->skilly)) + return; + md->skilldelay[md->skillidx] = tick; + + if (battle_config.mob_skill_log == 1) + printf ("MOB skill castend skill=%d, mob_class = %d\n", md->skillid, + md->mob_class); + mob_stop_walking (md, 0); + + skill_castend_pos2 (&md->bl, md->skillx, md->skilly, md->skillid, + md->skilllv, tick, 0); + + return; +} + +/*========================================== + * Skill use (an aria start, ID specification) + *------------------------------------------ + */ +int mobskill_use_id (struct mob_data *md, struct block_list *target, + int skill_idx) +{ + int casttime, range; + struct mob_skill *ms; + int skill_id, skill_lv, forcecast = 0; + + nullpo_retr (0, md); + nullpo_retr (0, ms = &mob_db[md->mob_class].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 (md->sc_data) + { + if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 + || md->sc_data[SC_ROKISWEIL].timer != -1 + || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + + if (md->option & 4 && skill_id == TF_HIDING) + return 0; + if (md->option & 2 && skill_id != TF_HIDING && skill_id != AS_GRIMTOOTH + && skill_id != RG_BACKSTAP && skill_id != RG_RAID) + return 0; + + if (map[md->bl.m].flag.gvg + && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT + || skill_id == AL_WARP || skill_id == WZ_ICEWALL + || skill_id == TF_BACKSLIDING)) + return 0; + + if (skill_get_inf2 (skill_id) & 0x200 && md->bl.id == target->id) + return 0; + + // 射程と障害物チェック + range = skill_get_range (skill_id, skill_lv); + if (range < 0) + range = battle_get_range (&md->bl) - (range + 1); + + if (!battle_check_range (&md->bl, target, range)) + return 0; + +// delay=skill_delayfix(&md->bl, skill_get_delay( skill_id,skill_lv) ); + + casttime = skill_castfix (&md->bl, 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 (battle_get_race (target), + battle_get_elem_type (target))) + { /* 敵がアンデッドなら */ + forcecast = 1; /* ターンアンデットと同じ詠唱時間 */ + casttime = + skill_castfix (&md->bl, + skill_get_cast (PR_TURNUNDEAD, skill_lv)); + } + break; + case MO_EXTREMITYFIST: /*阿修羅覇鳳拳 */ + case SA_MAGICROD: + case SA_SPELLBREAKER: + forcecast = 1; + break; + } + + if (battle_config.mob_skill_log == 1) + printf + ("MOB skill use target_id=%d skill=%d lv=%d cast=%d, mob_class = %d\n", + target->id, skill_id, skill_lv, casttime, md->mob_class); + + if (casttime > 0 || forcecast) + { // 詠唱が必要 +// struct mob_data *md2; + clif_skillcasting (&md->bl, + md->bl.id, target->id, 0, 0, skill_id, casttime); + + // 詠唱反応モンスター +/* if( target->type==BL_MOB && mob_db[(md2=(struct mob_data *)target)->mob_class].mode&0x10 && + md2->state.state!=MS_ATTACK){ + md2->target_id=md->bl.id; + md->state.targettype = ATTACKABLE; + md2->min_chase=13; + }*/ + } + + 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) + skill_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, range; + struct mob_skill *ms; + struct block_list bl; + int skill_id, skill_lv; + + nullpo_retr (0, md); + nullpo_retr (0, ms = &mob_db[md->mob_class].skill[skill_idx]); + + if (md->bl.prev == NULL) + return 0; + + skill_id = ms->skill_id; + skill_lv = ms->skill_lv; + + //沈黙や状態異常など + if (md->sc_data) + { + if (md->opt1 > 0 || md->sc_data[SC_DIVINA].timer != -1 + || md->sc_data[SC_ROKISWEIL].timer != -1 + || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if (md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if (md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if (md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + + if (md->option & 2) + return 0; + + if (map[md->bl.m].flag.gvg + && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT + || skill_id == AL_WARP || skill_id == WZ_ICEWALL + || skill_id == TF_BACKSLIDING)) + return 0; + + // 射程と障害物チェック + bl.type = BL_NUL; + bl.m = md->bl.m; + bl.x = skill_x; + bl.y = skill_y; + range = skill_get_range (skill_id, skill_lv); + if (range < 0) + range = battle_get_range (&md->bl) - (range + 1); + if (!battle_check_range (&md->bl, &bl, range)) + return 0; + +// delay=skill_delayfix(&sd->bl, skill_get_delay( skill_id,skill_lv) ); + casttime = skill_castfix (&md->bl, ms->casttime); + md->skilldelay[skill_idx] = gettick (); + md->state.skillcastcancel = ms->cancel; + + if (battle_config.mob_skill_log == 1) + printf + ("MOB skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d, mob_class = %d\n", + skill_x, skill_y, skill_id, skill_lv, casttime, md->mob_class); + + if (casttime > 0) // A cast time is required. + 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) + skill_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 mob_data **fr, *md, *mmd; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, mmd = va_arg (ap, struct mob_data *)); + + md = (struct mob_data *) bl; + + if (mmd->bl.id == bl->id) + return 0; + rate = va_arg (ap, int); + fr = va_arg (ap, struct mob_data **); + if (md->hp < mob_db[md->mob_class].max_hp * rate / 100) + (*fr) = md; + return 0; +} + +struct mob_data *mob_getfriendhpltmaxrate (struct mob_data *md, int rate) +{ + struct mob_data *fr = NULL; + const int r = 8; + + nullpo_retr (NULL, md); + + map_foreachinarea (mob_getfriendhpltmaxrate_sub, md->bl.m, + md->bl.x - r, md->bl.y - r, md->bl.x + r, md->bl.y + r, + BL_MOB, md, rate, &fr); + return fr; +} + +/*========================================== + * 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; + 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_STONE; j <= SC_BLIND && !flag; j++) + { + flag = (md->sc_data[j].timer != -1); + } + } + 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 *target=NULL; + int i, max_hp; + + nullpo_retr (0, md); + nullpo_retr (0, ms = mob_db[md->mob_class].skill); + + max_hp = battle_get_max_hp (&md->bl); + + if (battle_config.mob_skill_use == 0 || md->skilltimer != -1) + return 0; + + if (md->state.special_mob_ai) + return 0; + + if (md->sc_data[SC_SELFDESTRUCTION].timer != -1) //自爆中はスキルを使わない + return 0; + + for (i = 0; i < mob_db[md->mob_class].maxskill; i++) + { + int c2 = ms[i].cond2, flag = 0; + struct mob_data *fmd = NULL; + + // ディレイ中 + if (DIFF_TICK (tick, md->skilldelay[i]) < ms[i].delay) + continue; + + // 状態判定 + if (ms[i].state >= 0 && ms[i].state != md->state.skillstate) + continue; + + // 条件判定 + flag = (event == ms[i].cond1); + if (!flag) + { + switch (ms[i].cond1) + { + case MSC_ALWAYS: + flag = 1; + break; + case MSC_MYHPLTMAXRATE: // HP< maxhp% + flag = (md->hp < max_hp * c2 / 100); + break; + case MSC_MYSTATUSON: // status[num] on + case MSC_MYSTATUSOFF: // status[num] off + if (ms[i].cond2 == -1) + { + int j; + for (j = SC_STONE; j <= SC_BLIND && !flag; j++) + { + flag = (md->sc_data[j].timer != -1); + } + } + else + flag = (md->sc_data[ms[i].cond2].timer != -1); + flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); + break; + case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% + flag = + ((fmd = + 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_NOTINTOWN: // Only outside of towns. + flag = !map[md->bl.m].flag.town; + break; + case MSC_SLAVELT: // slave < num + flag = (mob_countslave (md) < c2); + break; + case MSC_ATTACKPCGT: // attack pc > num + flag = (mob_counttargeted (md, NULL, 0) > c2); + break; + case MSC_SLAVELE: // slave <= num + flag = (mob_countslave (md) <= c2); + break; + case MSC_ATTACKPCGE: // attack pc >= num + flag = (mob_counttargeted (md, NULL, 0) >= c2); + break; + case MSC_SKILLUSED: // specificated skill used + flag = ((event & 0xffff) == MSC_SKILLUSED + && ((event >> 16) == c2 || c2 == 0)); + break; + } + } + + // 確率判定 + if (flag && MRAND (10000) < ms[i].permillage) + { + + if (skill_get_inf (ms[i].skill_id) & 2) + { + // 場所指定 + struct block_list *bl = NULL; + int x = 0, y = 0; + if (ms[i].target <= MST_AROUND) + { + if (ms[i].target == MST_MASTER) + { + bl = &md->bl; + if (md->master_id) + bl = map_id2bl (md->master_id); + } + else + { + bl = ((ms[i].target == MST_TARGET + || ms[i].target == + MST_AROUND5) ? map_id2bl (md-> + target_id) + : (ms[i].target == + MST_FRIEND) ? &fmd->bl : &md->bl); + } + + if (bl) + { + 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, c, m = bl->m, r = + ms[i].target - MST_AROUND1; + do + { + bx = x + MRAND ((r * 2 + 3)) - r; + by = y + MRAND ((r * 2 + 3)) - r; + } + while ((bx <= 0 || by <= 0 || bx >= map[m].xs + || by >= map[m].ys + || ((c = read_gat (m, bx, by)) == 1 || c == 5)) + && (i++) < 1000); + if (i < 1000) + { + x = bx; + y = by; + } + } + // 相手の周囲 + if (ms[i].target >= MST_AROUND5) + { + int bx = x, by = y, i = 0, c, m = bl->m, r = + (ms[i].target - MST_AROUND5) + 1; + do + { + bx = x + MRAND ((r * 2 + 1)) - r; + by = y + MRAND ((r * 2 + 1)) - r; + } + while ((bx <= 0 || by <= 0 || bx >= map[m].xs + || by >= map[m].ys + || ((c = read_gat (m, bx, by)) == 1 || c == 5)) + && (i++) < 1000); + if (i < 1000) + { + x = bx; + y = by; + } + } + if (!mobskill_use_pos (md, x, y, i)) + return 0; + + } + else + { + if (ms[i].target == MST_MASTER) + { + struct block_list *bl = &md->bl; + if (md->master_id) + bl = map_id2bl (md->master_id); + + if (bl && !mobskill_use_id (md, bl, i)) + return 0; + } + // ID指定 + if (ms[i].target <= MST_FRIEND) + { + struct block_list *bl = NULL; + bl = ((ms[i].target == + MST_TARGET) ? map_id2bl (md-> + target_id) : (ms[i].target + == + MST_FRIEND) + ? &fmd->bl : &md->bl); + if (bl && !mobskill_use_id (md, bl, i)) + return 0; + } + } + if (ms[i].emotion >= 0) + clif_emotion (&md->bl, ms[i].emotion); + return 1; + } + } + + return 0; +} + +/*========================================== + * Skill use event processing + *------------------------------------------ + */ +int mobskill_event (struct mob_data *md, int flag) +{ + nullpo_retr (0, md); + + if (flag == -1 && mobskill_use (md, gettick (), MSC_CASTTARGETED)) + return 1; + if ((flag & BF_SHORT) + && mobskill_use (md, gettick (), MSC_CLOSEDATTACKED)) + return 1; + if ((flag & BF_LONG) + && mobskill_use (md, gettick (), MSC_LONGRANGEATTACKED)) + return 1; + return 0; +} + +/*========================================== + * Mobがエンペリウムなどの場合の判定 + *------------------------------------------ + */ +int mob_gvmobcheck (struct map_session_data *sd, struct block_list *bl) +{ + struct mob_data *md = NULL; + + nullpo_retr (0, sd); + nullpo_retr (0, bl); + + if (bl->type == BL_MOB && (md = (struct mob_data *) bl) && + (md->mob_class == 1288 || md->mob_class == 1287 || md->mob_class == 1286 + || md->mob_class == 1285)) + { + struct guild_castle *gc = guild_mapname2gc (map[sd->bl.m].name); + struct guild *g = guild_search (sd->status.guild_id); + + if (g == NULL && md->mob_class == 1288) + return 0; //ギルド未加入ならダメージ無し + else if (gc != NULL && !map[sd->bl.m].flag.gvg) + return 0; //砦内でGvじゃないときはダメージなし + else if (g && gc != NULL && g->guild_id == gc->guild_id) + return 0; //自占領ギルドのエンペならダメージ無し + else if (g && guild_checkskill (g, GD_APPROVAL) <= 0 + && md->mob_class == 1288) + return 0; //正規ギルド承認がないとダメージ無し + + } + + return 1; +} + +/*========================================== + * スキル用タイマー削除 + *------------------------------------------ + */ +int mobskill_deltimer (struct mob_data *md) +{ + nullpo_retr (0, md); + + if (md->skilltimer != -1) + { + if (skill_get_inf (md->skillid) & 2) + delete_timer (md->skilltimer, mobskill_castend_pos); + else + delete_timer (md->skilltimer, mobskill_castend_id); + md->skilltimer = -1; + } + return 0; +} + +// +// 初期化 +// +/*========================================== + * Since un-setting [ mob ] up was used, it is an initial provisional value setup. + *------------------------------------------ + */ +static int mob_makedummymobdb (int mob_class) +{ + int i; + + sprintf (mob_db[mob_class].name, "mob%d", mob_class); + sprintf (mob_db[mob_class].jname, "mob%d", mob_class); + mob_db[mob_class].lv = 1; + mob_db[mob_class].max_hp = 1000; + mob_db[mob_class].max_sp = 1; + mob_db[mob_class].base_exp = 2; + mob_db[mob_class].job_exp = 1; + mob_db[mob_class].range = 1; + mob_db[mob_class].atk1 = 7; + mob_db[mob_class].atk2 = 10; + mob_db[mob_class].def = 0; + mob_db[mob_class].mdef = 0; + mob_db[mob_class].str = 1; + mob_db[mob_class].agi = 1; + mob_db[mob_class].vit = 1; + mob_db[mob_class].int_ = 1; + mob_db[mob_class].dex = 6; + mob_db[mob_class].luk = 2; + mob_db[mob_class].range2 = 10; + mob_db[mob_class].range3 = 10; + mob_db[mob_class].size = 0; + mob_db[mob_class].race = 0; + mob_db[mob_class].element = 0; + mob_db[mob_class].mode = 0; + mob_db[mob_class].speed = 300; + mob_db[mob_class].adelay = 1000; + mob_db[mob_class].amotion = 500; + mob_db[mob_class].dmotion = 500; + mob_db[mob_class].dropitem[0].nameid = 909; // Jellopy + mob_db[mob_class].dropitem[0].p = 1000; + for (i = 1; i < 8; i++) + { + mob_db[mob_class].dropitem[i].nameid = 0; + mob_db[mob_class].dropitem[i].p = 0; + } + // Item1,Item2 + mob_db[mob_class].mexp = 0; + mob_db[mob_class].mexpper = 0; + for (i = 0; i < 3; i++) + { + mob_db[mob_class].mvpitem[i].nameid = 0; + mob_db[mob_class].mvpitem[i].p = 0; + } + for (i = 0; i < MAX_RANDOMMONSTER; i++) + mob_db[mob_class].summonper[i] = 0; + return 0; +} + +/*========================================== + * db/mob_db.txt reading + *------------------------------------------ + */ +static int mob_readdb (void) +{ + FILE *fp; + char line[1024]; + char *filename[] = { "db/mob_db.txt", "db/mob_db2.txt" }; + int i; + + memset (mob_db, 0, sizeof (mob_db)); + + for (i = 0; i < 2; i++) + { + + fp = fopen_ (filename[i], "r"); + if (fp == NULL) + { + if (i > 0) + continue; + return -1; + } + while (fgets (line, 1020, fp)) + { + int mob_class, i; + char *str[57], *p, *np; + + if (line[0] == '/' && line[1] == '/') + continue; + + for (i = 0, p = line; i < 57; i++) + { + while (*p == '\t' || *p == ' ') + p++; + if ((np = strchr (p, ',')) != NULL) + { + str[i] = p; + *np = 0; + p = np + 1; + } + else + str[i] = p; + } + + mob_class = atoi (str[0]); + if (mob_class <= 1000 || mob_class > 2000) + continue; + + mob_db[mob_class].view_class = mob_class; + memcpy (mob_db[mob_class].name, str[1], 24); + memcpy (mob_db[mob_class].jname, str[2], 24); + mob_db[mob_class].lv = atoi (str[3]); + mob_db[mob_class].max_hp = atoi (str[4]); + mob_db[mob_class].max_sp = atoi (str[5]); + + mob_db[mob_class].base_exp = atoi (str[6]); + if (mob_db[mob_class].base_exp < 0) + mob_db[mob_class].base_exp = 0; + else if (mob_db[mob_class].base_exp > 0 + && (mob_db[mob_class].base_exp * + battle_config.base_exp_rate / 100 > 1000000000 + || mob_db[mob_class].base_exp * + battle_config.base_exp_rate / 100 < 0)) + mob_db[mob_class].base_exp = 1000000000; + else + mob_db[mob_class].base_exp *= battle_config.base_exp_rate / 100; + + mob_db[mob_class].job_exp = atoi (str[7]); + if (mob_db[mob_class].job_exp < 0) + mob_db[mob_class].job_exp = 0; + else if (mob_db[mob_class].job_exp > 0 + && (mob_db[mob_class].job_exp * battle_config.job_exp_rate / + 100 > 1000000000 + || mob_db[mob_class].job_exp * + battle_config.job_exp_rate / 100 < 0)) + mob_db[mob_class].job_exp = 1000000000; + else + mob_db[mob_class].job_exp *= battle_config.job_exp_rate / 100; + + mob_db[mob_class].range = atoi (str[8]); + mob_db[mob_class].atk1 = atoi (str[9]); + mob_db[mob_class].atk2 = atoi (str[10]); + mob_db[mob_class].def = atoi (str[11]); + mob_db[mob_class].mdef = atoi (str[12]); + mob_db[mob_class].str = atoi (str[13]); + mob_db[mob_class].agi = atoi (str[14]); + mob_db[mob_class].vit = atoi (str[15]); + mob_db[mob_class].int_ = atoi (str[16]); + mob_db[mob_class].dex = atoi (str[17]); + mob_db[mob_class].luk = atoi (str[18]); + mob_db[mob_class].range2 = atoi (str[19]); + mob_db[mob_class].range3 = atoi (str[20]); + mob_db[mob_class].size = atoi (str[21]); + mob_db[mob_class].race = atoi (str[22]); + mob_db[mob_class].element = atoi (str[23]); + mob_db[mob_class].mode = atoi (str[24]); + mob_db[mob_class].speed = atoi (str[25]); + mob_db[mob_class].adelay = atoi (str[26]); + mob_db[mob_class].amotion = atoi (str[27]); + mob_db[mob_class].dmotion = atoi (str[28]); + + for (i = 0; i < 8; i++) + { + int rate = 0, type, ratemin, ratemax; + mob_db[mob_class].dropitem[i].nameid = atoi (str[29 + i * 2]); + type = itemdb_type (mob_db[mob_class].dropitem[i].nameid); + if (type == 0) + { // Added [Valaris] + rate = battle_config.item_rate_heal; + ratemin = battle_config.item_drop_heal_min; + ratemax = battle_config.item_drop_heal_max; + } + else if (type == 2) + { + rate = battle_config.item_rate_use; + ratemin = battle_config.item_drop_use_min; + ratemax = battle_config.item_drop_use_max; // End + } + else if (type == 4 || type == 5 || type == 8) + { + rate = battle_config.item_rate_equip; + ratemin = battle_config.item_drop_equip_min; + ratemax = battle_config.item_drop_equip_max; + } + else if (type == 6) + { + rate = battle_config.item_rate_card; + ratemin = battle_config.item_drop_card_min; + ratemax = battle_config.item_drop_card_max; + } + else + { + rate = battle_config.item_rate_common; + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + } + rate = (rate / 100) * atoi (str[30 + i * 2]); + rate = + (rate < ratemin) ? ratemin : (rate > + ratemax) ? ratemax : rate; + mob_db[mob_class].dropitem[i].p = rate; + } + // Item1,Item2 + mob_db[mob_class].mexp = + atoi (str[45]) * battle_config.mvp_exp_rate / 100; + mob_db[mob_class].mexpper = atoi (str[46]); + for (i = 0; i < 3; i++) + { + mob_db[mob_class].mvpitem[i].nameid = atoi (str[47 + i * 2]); + mob_db[mob_class].mvpitem[i].p = + atoi (str[48 + i * 2]) * battle_config.mvp_item_rate / + 100; + } + mob_db[mob_class].mutations_nr = atoi (str[55]); + mob_db[mob_class].mutation_power = atoi (str[56]); + + for (i = 0; i < MAX_RANDOMMONSTER; i++) + mob_db[mob_class].summonper[i] = 0; + mob_db[mob_class].maxskill = 0; + + mob_db[mob_class].sex = 0; + mob_db[mob_class].hair = 0; + mob_db[mob_class].hair_color = 0; + mob_db[mob_class].weapon = 0; + mob_db[mob_class].shield = 0; + mob_db[mob_class].head_top = 0; + mob_db[mob_class].head_mid = 0; + mob_db[mob_class].head_buttom = 0; + mob_db[mob_class].clothes_color = 0; //Add for player monster dye - Valaris + + if (mob_db[mob_class].base_exp == 0) + mob_db[mob_class].base_exp = mob_gen_exp (&mob_db[mob_class]); + } + fclose_ (fp); + printf ("read %s done\n", filename[i]); + } + return 0; +} + +/*========================================== + * MOB display graphic change data reading + *------------------------------------------ + */ +static int mob_readdb_mobavail (void) +{ + FILE *fp; + char line[1024]; + int ln = 0; + int mob_class, j, k; + char *str[20], *p, *np; + + if ((fp = fopen_ ("db/mob_avail.txt", "r")) == NULL) + { + printf ("can't read db/mob_avail.txt\n"); + 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; + + mob_class = atoi (str[0]); + + if (mob_class <= 1000 || mob_class > 2000) // 値が異常なら処理しない。 + continue; + k = atoi (str[1]); + if (k >= 0) + mob_db[mob_class].view_class = k; + + if ((mob_db[mob_class].view_class < 24) + || (mob_db[mob_class].view_class > 4000)) + { + mob_db[mob_class].sex = atoi (str[2]); + mob_db[mob_class].hair = atoi (str[3]); + mob_db[mob_class].hair_color = atoi (str[4]); + mob_db[mob_class].weapon = atoi (str[5]); + mob_db[mob_class].shield = atoi (str[6]); + mob_db[mob_class].head_top = atoi (str[7]); + mob_db[mob_class].head_mid = atoi (str[8]); + mob_db[mob_class].head_buttom = atoi (str[9]); + mob_db[mob_class].option = atoi (str[10]) & ~0x46; + mob_db[mob_class].clothes_color = atoi (str[11]); // Monster player dye option - Valaris + } + + else if (atoi (str[2]) > 0) + mob_db[mob_class].equip = atoi (str[2]); // mob equipment [Valaris] + + ln++; + } + fclose_ (fp); + printf ("read db/mob_avail.txt done (count=%d)\n", ln); + 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[] = { + "db/mob_branch.txt", + "db/mob_poring.txt", + "db/mob_boss.txt" + }; + + for (i = 0; i < MAX_RANDOMMONSTER; i++) + { + mob_db[0].summonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく + fp = fopen_ (mobfile[i], "r"); + if (fp == NULL) + { + printf ("can't read %s\n", mobfile[i]); + return -1; + } + while (fgets (line, 1020, fp)) + { + int mob_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; + + mob_class = atoi (str[0]); + per = atoi (str[2]); + if ((mob_class > 1000 && mob_class <= 2000) || mob_class == 0) + mob_db[mob_class].summonper[i] = per; + } + fclose_ (fp); + printf ("read %s done\n", mobfile[i]); + } + return 0; +} + +/*========================================== + * db/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}, + { + "notintown", MSC_NOTINTOWN}, + { + "attackpcgt", MSC_ATTACKPCGT}, + { + "attackpcge", MSC_ATTACKPCGE}, + { + "slavelt", MSC_SLAVELT}, + { + "slavele", MSC_SLAVELE}, + { + "closedattacked", MSC_CLOSEDATTACKED}, + { + "longrangeattacked", MSC_LONGRANGEATTACKED}, + { + "skillused", MSC_SKILLUSED}, + { + "casttargeted", MSC_CASTTARGETED},}, 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}, + { + "attack", MSS_ATTACK}, + { + "dead", MSS_DEAD}, + { + "loot", MSS_LOOT}, + { + "chase", MSS_CHASE},}, 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[] = { "db/mob_skill_db.txt", "db/mob_skill_db2.txt" }; + + for (x = 0; x < 2; x++) + { + + fp = fopen_ (filename[x], "r"); + if (fp == NULL) + { + if (x == 0) + printf ("can't read %s\n", filename[x]); + continue; + } + while (fgets (line, 1020, fp)) + { + char *sp[20], *p; + int mob_id; + struct mob_skill *ms; + 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 ((mob_id = atoi (sp[0])) <= 0) + continue; + + if (strcmp (sp[1], "clear") == 0) + { + memset (mob_db[mob_id].skill, 0, + sizeof (mob_db[mob_id].skill)); + mob_db[mob_id].maxskill = 0; + continue; + } + + for (i = 0; i < MAX_MOBSKILL; i++) + if ((ms = &mob_db[mob_id].skill[i])->skill_id == 0) + break; + if (i == MAX_MOBSKILL) + { + printf + ("mob_skill: readdb: too many skill ! [%s] in %d[%s]\n", + sp[1], mob_id, mob_db[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; + } + ms->skill_id = atoi (sp[3]); + ms->skill_lv = atoi (sp[4]); + + ms->permillage = atoi (sp[5]); + ms->casttime = atoi (sp[6]); + ms->delay = atoi (sp[7]); + 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; + mob_db[mob_id].maxskill = i + 1; + } + fclose_ (fp); + printf ("read %s done\n", filename[x]); + } + return 0; +} + +void mob_reload (void) +{ + /* + * + * <empty monster database> + * mob_read(); + * + */ + + do_init_mob (); +} + +/*========================================== + * Circumference initialization of mob + *------------------------------------------ + */ +int do_init_mob (void) +{ + mob_readdb (); + + mob_readdb_mobavail (); + mob_read_randommonster (); + mob_readskilldb (); + + 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; +} diff --git a/src/map/mob.h b/src/map/mob.h deleted file mode 100644 index b5d642a..0000000 --- a/src/map/mob.h +++ /dev/null @@ -1,151 +0,0 @@ -// $Id: mob.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ -#ifndef _MOB_H_ -#define _MOB_H_ -#include "../common/timer.h" -#define MAX_RANDOMMONSTER 3 - -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[24], jname[24]; - int 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; - int speed, adelay, amotion, dmotion; - int mexp, mexpper; - int mutations_nr, mutation_power; - struct - { - int nameid, p; - } dropitem[8]; - 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]; -}; -extern struct mob_db mob_db[]; - -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 = 0x0001, - MSC_FRIENDHPLTMAXRATE = 0x0010, - MSC_MYSTATUSON = 0x0020, - MSC_MYSTATUSOFF = 0x0021, - MSC_FRIENDSTATUSON = 0x0030, - MSC_FRIENDSTATUSOFF = 0x0031, - MSC_NOTINTOWN = 0x0032, - - MSC_ATTACKPCGT = 0x0100, - MSC_ATTACKPCGE = 0x0101, - MSC_SLAVELT = 0x0110, - MSC_SLAVELE = 0x0111, - MSC_CLOSEDATTACKED = 0x1000, - MSC_LONGRANGEATTACKED = 0x1001, - MSC_SKILLUSED = 0x1010, - MSC_CASTTARGETED = 0x1011, -}; - -enum -{ - MSS_IDLE, // 待機 - MSS_WALK, // 移動 - MSS_ATTACK, // 攻撃 - MSS_DEAD, // 死亡 - MSS_LOOT, // ルート - MSS_CHASE, // 突撃 -}; - -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_walktoxy (struct mob_data *md, int x, int y, int easy); - -int mob_target (struct mob_data *md, struct block_list *bl, int dist); -int mob_stop_walking (struct mob_data *md, int type); -int mob_stopattack (struct mob_data *); -int mob_spawn (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); -int mob_get_viewclass (int); -int mob_get_sex (int); -short mob_get_hair (int); -short mob_get_hair_color (int); -short mob_get_weapon (int); -short mob_get_shield (int); -short mob_get_head_top (int); -short mob_get_head_mid (int); -short mob_get_head_buttom (int); -short mob_get_clothes_color (int); //player mob dye [Valaris] -int mob_get_equip (int); // mob equip [Valaris] -int do_init_mob (void); - -int mob_delete (struct mob_data *md); -int mob_catch_delete (struct mob_data *md, int type); -void mob_timer_delete (timer_id, tick_t, custom_id_t, custom_data_t); - -int mob_deleteslave (struct mob_data *md); - -int mob_counttargeted (struct mob_data *md, struct block_list *src, - int target_lv); - -int mob_class_change (struct mob_data *md, int *value); -int mob_warp (struct mob_data *md, int m, int x, int y, int type); - -int mobskill_use (struct mob_data *md, unsigned int tick, int event); -int mobskill_event (struct mob_data *md, int flag); -void mobskill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); -void mobskill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); -int mob_summonslave (struct mob_data *md2, int *value, int amount, int flag); - -int mob_gvmobcheck (struct map_session_data *sd, struct block_list *bl); -void mob_reload (void); - -#endif diff --git a/src/map/mob.hpp b/src/map/mob.hpp new file mode 100644 index 0000000..6161bab --- /dev/null +++ b/src/map/mob.hpp @@ -0,0 +1,151 @@ +// $Id: mob.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef MOB_HPP +#define MOB_HPP +#include "../common/timer.hpp" +#define MAX_RANDOMMONSTER 3 + +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[24], jname[24]; + int 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; + int speed, adelay, amotion, dmotion; + int mexp, mexpper; + int mutations_nr, mutation_power; + struct + { + int nameid, p; + } dropitem[8]; + 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]; +}; +extern struct mob_db mob_db[]; + +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 = 0x0001, + MSC_FRIENDHPLTMAXRATE = 0x0010, + MSC_MYSTATUSON = 0x0020, + MSC_MYSTATUSOFF = 0x0021, + MSC_FRIENDSTATUSON = 0x0030, + MSC_FRIENDSTATUSOFF = 0x0031, + MSC_NOTINTOWN = 0x0032, + + MSC_ATTACKPCGT = 0x0100, + MSC_ATTACKPCGE = 0x0101, + MSC_SLAVELT = 0x0110, + MSC_SLAVELE = 0x0111, + MSC_CLOSEDATTACKED = 0x1000, + MSC_LONGRANGEATTACKED = 0x1001, + MSC_SKILLUSED = 0x1010, + MSC_CASTTARGETED = 0x1011, +}; + +enum +{ + MSS_IDLE, // 待機 + MSS_WALK, // 移動 + MSS_ATTACK, // 攻撃 + MSS_DEAD, // 死亡 + MSS_LOOT, // ルート + MSS_CHASE, // 突撃 +}; + +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_walktoxy (struct mob_data *md, int x, int y, int easy); + +int mob_target (struct mob_data *md, struct block_list *bl, int dist); +int mob_stop_walking (struct mob_data *md, int type); +int mob_stopattack (struct mob_data *); +int mob_spawn (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); +int mob_get_viewclass (int); +int mob_get_sex (int); +short mob_get_hair (int); +short mob_get_hair_color (int); +short mob_get_weapon (int); +short mob_get_shield (int); +short mob_get_head_top (int); +short mob_get_head_mid (int); +short mob_get_head_buttom (int); +short mob_get_clothes_color (int); //player mob dye [Valaris] +int mob_get_equip (int); // mob equip [Valaris] +int do_init_mob (void); + +int mob_delete (struct mob_data *md); +int mob_catch_delete (struct mob_data *md, int type); +void mob_timer_delete (timer_id, tick_t, custom_id_t, custom_data_t); + +int mob_deleteslave (struct mob_data *md); + +int mob_counttargeted (struct mob_data *md, struct block_list *src, + int target_lv); + +int mob_class_change (struct mob_data *md, int *value); +int mob_warp (struct mob_data *md, int m, int x, int y, int type); + +int mobskill_use (struct mob_data *md, unsigned int tick, int event); +int mobskill_event (struct mob_data *md, int flag); +void mobskill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); +void mobskill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); +int mob_summonslave (struct mob_data *md2, int *value, int amount, int flag); + +int mob_gvmobcheck (struct map_session_data *sd, struct block_list *bl); +void mob_reload (void); + +#endif diff --git a/src/map/npc.c b/src/map/npc.c deleted file mode 100644 index a925430..0000000 --- a/src/map/npc.c +++ /dev/null @@ -1,2378 +0,0 @@ -// $Id: npc.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <math.h> -#include <time.h> - -#include "../common/nullpo.h" -#include "../common/timer.h" - -#include "battle.h" -#include "clif.h" -#include "../common/db.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "../common/socket.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -struct npc_src_list -{ - struct npc_src_list *next; - struct npc_src_list *prev; - char name[4]; -}; - -static struct npc_src_list *npc_src_first, *npc_src_last; -static int npc_id = START_NPC_NUM; -static int npc_warp, npc_shop, npc_script, npc_mob; - -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; // 時計イベント用 - -/*========================================== - * 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 *) calloc (50, sizeof (char)); - - 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)) - { - - if (nd->flag & 1) // 無効化されている - return 1; - - memcpy (name, nd->name, sizeof(nd->name)); - if (sd->areanpc_id == nd->bl.id) - return 1; - sd->areanpc_id = nd->bl.id; - npc_event (sd, strcat (name, "::OnTouch"), 0); - } - free (name); - return 0; -} - -int npc_enable (const char *name, int flag) -{ - struct npc_data *nd = (struct npc_data *)strdb_search (npcname_db, 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_search (npcname_db, name); -} - -/*========================================== - * イベントキューのイベント処理 - *------------------------------------------ - */ -int npc_event_dequeue (struct map_session_data *sd) -{ - nullpo_retr (0, sd); - - sd->npc_id = 0; - - if (sd->eventqueue[0][0]) // キューのイベント処理 - { - if (!pc_addeventtimer(sd, 100, sd->eventqueue[0])) - { - printf ("npc_event_dequeue(): Event timer is full.\n"); - return 0; - } - - if (MAX_EVENTQUEUE > 1) - memmove (sd->eventqueue[0], sd->eventqueue[1], - (MAX_EVENTQUEUE - 1) * sizeof (sd->eventqueue[0])); - sd->eventqueue[MAX_EVENTQUEUE - 1][0] = '\0'; - return 1; - } - - return 0; -} - -int npc_delete (struct npc_data *nd) -{ - nullpo_retr (1, nd); - - if (nd->bl.prev == NULL) - return 1; - - clif_clearchar_area (&nd->bl, 1); - map_delblock (&nd->bl); - return 0; -} - -/*========================================== - * イベントの遅延実行 - *------------------------------------------ - */ -void npc_event_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = map_id2sd (id); - if (sd == NULL) - return; - - npc_event (sd, (const char *) data, 0); - free ((void *) data); -} - -int npc_timer_event (const char *eventname) // Added by RoVeRT -{ - struct event_data *ev = (struct event_data *)strdb_search (ev_db, eventname); - struct npc_data *nd; -// int xs,ys; - - if ((ev == NULL || (nd = ev->nd) == NULL)) - { - printf ("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(void *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(void *key,void *data,va_list ap) // Added by RoVeRT -{ - struct npc_data *nd=(struct npc_data*)data; - - if(nd->timer == -1) - return 0; - - strdb_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 -{ - strdb_foreach(npcname_db,npc_timer_sub); - - free((void*)data); - return 0; -}*/ -/*========================================== - * イベント用ラベルのエクスポート - * npc_parse_script->strdb_foreachから呼ばれる - *------------------------------------------ - */ -int npc_event_export (void *key, void *data, va_list ap) -{ - char *lname = (char *) key; - 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; - char *buf; - char *p = strchr (lname, ':'); - // エクスポートされる - CREATE (ev, struct event_data, 1); - CREATE (buf, char, 50); - if (p == NULL || (p - lname) > 24) - { - printf ("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_insert (ev_db, buf, ev); -// if (battle_config.etc_log) -// printf("npc_event_export: export [%s]\n",buf); - } - } - return 0; -} - -/*========================================== - * 全てのNPCのOn*イベント実行 - *------------------------------------------ - */ -void npc_event_doall_sub (db_key_t key, db_val_t data, va_list ap) -{ - const char *p = key.s; - int rid, argc; - argrec_t *argv; - struct event_data *ev; - int *c; - const char *name; - - nullpo_retv (ev = (struct event_data *) data); - nullpo_retv (ap); - nullpo_retv (c = va_arg (ap, int *)); - - name = va_arg (ap, const char *); - rid = va_arg (ap, int); - argc = va_arg (ap, int); - argv = va_arg (ap, argrec_t *); - - if ((p = strchr (p, ':')) && p && strcasecmp (name, p) == 0) - { - run_script_l (ev->nd->u.scr.script, ev->pos, rid, ev->nd->bl.id, argc, - argv); - (*c)++; - } -} - -int npc_event_doall_l (const char *name, int rid, int argc, argrec_t * args) -{ - int c = 0; - char buf[64] = "::"; - - strncpy (buf + 2, name, sizeof(buf)-3); - buf[sizeof(buf)-1] = '\0'; - strdb_foreach (ev_db, npc_event_doall_sub, &c, buf, rid, argc, args); - return c; -} - -void npc_event_do_sub (db_key_t key, db_val_t data, va_list ap) -{ - const char *p = key.s; - struct event_data *ev; - int *c; - const char *name; - int rid, argc; - argrec_t *argv; - - nullpo_retv (ev = (struct event_data *) data); - nullpo_retv (ap); - nullpo_retv (c = va_arg (ap, int *)); - - name = va_arg (ap, const char *); - rid = va_arg (ap, int); - argc = va_arg (ap, int); - argv = va_arg (ap, argrec_t *); - - if (p && strcasecmp (name, p) == 0) - { - run_script_l (ev->nd->u.scr.script, ev->pos, rid, ev->nd->bl.id, argc, - argv); - (*c)++; - } -} - -int npc_event_do_l (const char *name, int rid, int argc, argrec_t * args) -{ - int c = 0; - - if (*name == ':' && name[1] == ':') - { - return npc_event_doall_l (name + 2, rid, argc, args); - } - - strdb_foreach (ev_db, npc_event_do_sub, &c, name, rid, argc, args); - return c; -} - -/*========================================== - * 時計イベント実行 - *------------------------------------------ - */ -void npc_event_do_clock (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - time_t timer; - struct tm *t; - char buf[64]; - int c = 0; - - time (&timer); - t = gmtime (&timer); - - 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); - } - 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)); -} - -/*========================================== - * OnInitイベント実行(&時計イベント開始) - *------------------------------------------ - */ -int npc_event_do_oninit (void) -{ - int c = npc_event_doall ("OnInit"); - printf ("npc: OnInit Event done. (%d npc)\n", c); - - 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) - { - char *evname; - CREATE (evname, char, 24); - memcpy (evname, name, 24); - nd->eventtimer[i] = add_timer (gettick () + tick, - npc_event_timer, nd->bl.id, - (int) evname); - } - else - printf ("npc_addtimer: event timer is full !\n"); - - return 0; -} - -int npc_deleventtimer (struct npc_data *nd, const char *name) -{ - int i; - for (i = 0; i < MAX_EVENTTIMER; i++) - if (nd->eventtimer[i] != -1 && strcmp ((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; -} - -void npc_do_ontimer_sub (db_key_t key, db_val_t data, va_list ap) -{ - const char *p = key.s; - 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, ':')) && p - && strncasecmp ("::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); - } - } -} - -int npc_do_ontimer (int npc_id, struct map_session_data *sd, int option) -{ - strdb_foreach (ev_db, npc_do_ontimer_sub, &npc_id, sd, option); - return 0; -} - -/*========================================== - * タイマーイベント用ラベルの取り込み - * npc_parse_script->strdb_foreachから呼ばれる - *------------------------------------------ - */ -int npc_timerevent_import (void *key, void *data, va_list ap) -{ - char *lname = (char *) key; - 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; - RECREATE (te, struct npc_timerevent_list, i+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; -} - -/*========================================== - * タイマーイベント実行 - *------------------------------------------ - */ -void npc_timerevent (timer_id tid, tick_t tick, custom_id_t id, custom_data_t 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) - { - printf ("npc_timerevent: ??\n"); - return; - } - 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, 0, nd->bl.id); -} - -/*========================================== - * タイマーイベント開始 - *------------------------------------------ - */ -int npc_timerevent_start (struct npc_data *nd) -{ - 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; - } - nd->u.scr.nexttimer = j; - nd->u.scr.timertick = gettick (); - - if (j >= n) - return 0; - - 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; - } - 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); - return 0; -} - -/*========================================== - * イベント型のNPC処理 - *------------------------------------------ - */ -int npc_event (struct map_session_data *sd, const char *eventname, - int mob_kill) -{ - struct event_data *ev = (struct event_data *)strdb_search (ev_db, eventname); - struct npc_data *nd; - int xs, ys; - char mobevent[100]; - - if (sd == NULL) - { - printf ("npc_event nullpo?\n"); - } - - if (ev == NULL && eventname - && strcmp (((eventname) + strlen (eventname) - 9), "::OnTouch") == 0) - return 1; - - if (ev == NULL || (nd = ev->nd) == NULL) - { - if (mob_kill && (ev == NULL || (nd = ev->nd) == NULL)) - { - strcpy (mobevent, eventname); - strcat (mobevent, "::OnMyMobDead"); - ev = (struct event_data *)strdb_search (ev_db, mobevent); - if (ev == NULL || (nd = ev->nd) == NULL) - { - if (strncasecmp (eventname, "GM_MONSTER", 10) != 0) - printf ("npc_event: event not found [%s]\n", mobevent); - return 0; - } - } - else - { - if (battle_config.error_log) - printf ("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) - { - 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) - printf ("npc_event: event queue is full !\n"); - } - else - { -// if (battle_config.etc_log) -// printf("npc_event: enqueue\n"); - strncpy (sd->eventqueue[i], eventname, 50); - sd->eventqueue[i][49] = '\0'; - } - 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; -} - -void npc_command_sub (db_key_t key, db_val_t data, va_list ap) -{ - const char *p = key.s; - struct event_data *ev = (struct event_data *) data; - char *npcname = va_arg (ap, char *); - char *command = va_arg (ap, char *); - char temp[100]; - - if (strcmp (ev->nd->name, npcname) == 0 && (p = strchr (p, ':')) && p - && strncasecmp ("::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); - } -} - -int npc_command (struct map_session_data *sd, char *npcname, char *command) -{ - strdb_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 MESSAGE: - 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) - printf ("npc_touch_areanpc : some bug \n"); - } - return 1; - } - switch (map[m].npc[i]->bl.subtype) - { - case WARP: - skill_stop_dancing (&sd->bl, 0); - pc_setpos (sd, map[m].npc[i]->u.warp.name, - map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, 0); - break; - case MESSAGE: - case SCRIPT: - { - char *name = (char *)malloc (50); - - memcpy (name, map[m].npc[i]->name, 50); - if (sd->areanpc_id == map[m].npc[i]->bl.id) - return 1; - sd->areanpc_id = map[m].npc[i]->bl.id; - if (npc_event (sd, strcat (name, "::OnTouch"), 0) > 0) - npc_click (sd, map[m].npc[i]->bl.id); - free (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) - printf ("no such npc : %d\n", id); - return 1; - } - - if (nd->npc_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_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) - printf ("npc_click: npc_id != 0\n"); - return 1; - } - - if (npc_checknear (sd, id)) { - clif_scriptclose (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; - case MESSAGE: - if (nd->u.message) - { - clif_scriptmes (sd, id, nd->u.message); - clif_scriptclose (sd, 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)) { - clif_scriptclose (sd, id); - return 1; - } - - nd = (struct npc_data *) map_id2bl (id); - - if (!nd /* NPC was disposed? */ || nd->bl.subtype == MESSAGE) - { - clif_scriptclose (sd, id); - npc_event_dequeue (sd); - return 0; - } - - 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) - printf ("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_stacks = 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_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: - if (itemdb_isequip (item_list[i * 2 + 1])) - new_stacks += item_list[i * 2]; - else - new_stacks++; - 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_stacks) - return 3; // 種類数超過 - if (sd->trade_partner != 0) - return 4; // cant buy while trading - - pc_payzeny (sd, (int) z); - - for (i = 0; i < n; i++) - { - struct item_data *item_data; - if ((item_data = itemdb_exists (item_list[i * 2 + 1])) != NULL) - { - int amount = item_list[i * 2]; - struct item item_tmp; - memset (&item_tmp, 0, sizeof (item_tmp)); - - item_tmp.nameid = item_data->nameid; - item_tmp.identify = 1; // npc販売アイテムは鑑定済み - - if (amount > 1 - && (item_data->type == 4 || item_data->type == 5 - || item_data->type == 7 || item_data->type == 8)) - { - for (j = 0; j < amount; j++) - { - pc_additem (sd, &item_tmp, 1); - } - } - else - { - pc_additem (sd, &item_tmp, amount); - } - } - } - - //商人経験値 -/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { - z = z * pc_checkskill(sd,MC_DISCOUNT) / ((1 + 300 / itemamount) * 4000) * battle_config.shop_exp; - pc_gainexp(sd,0,z); - }*/ - if (battle_config.shop_exp > 0 && z > 0 - && (skill = pc_checkskill (sd, MC_DISCOUNT)) > 0) - { - if (skill > 0) - { - z = (log (z * (double) skill) * (double) battle_config.shop_exp / - 100.); - 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 (sd->trade_partner != 0) - return 2; // cant sell while trading - 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; - pc_getzeny (sd, (int) z); - for (i = 0; i < n; i++) - { - int item_id = item_list[i * 2] - 2; - pc_delitem (sd, item_id, item_list[i * 2 + 1], 0); - } - - //商人経験値 -/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { - z = z * pc_checkskill(sd,MC_OVERCHARGE) / ((1 + 500 / itemamount) * 4000) * battle_config.shop_exp ; - pc_gainexp(sd,0,z); - }*/ - if (battle_config.shop_exp > 0 && z > 0 - && (skill = pc_checkskill (sd, MC_OVERCHARGE)) > 0) - { - if (skill > 0) - { - z = (log (z * (double) skill) * (double) battle_config.shop_exp / - 100.); - if (z < 1) - z = 1; - pc_gainexp (sd, 0, (int) z); - } - } - - return 0; - -} - -// -// 初期化関係 -// - -/*========================================== - * 読み込むnpcファイルのクリア - *------------------------------------------ - */ -void npc_clearsrcfile (void) -{ - struct npc_src_list *p = npc_src_first; - - while (p) - { - struct npc_src_list *p2 = p; - p = p->next; - free (p2); - } - npc_src_first = NULL; - npc_src_last = NULL; -} - -/*========================================== - * 読み込むnpcファイルの追加 - *------------------------------------------ - */ -void npc_addsrcfile (char *name) -{ - struct npc_src_list *new_src; - size_t len; - - if (strcasecmp (name, "clear") == 0) - { - npc_clearsrcfile (); - return; - } - - len = sizeof (*new_src) + strlen (name); - new_src = (struct npc_src_list *) calloc (1, len); - new_src->next = NULL; - strncpy (new_src->name, name, strlen (name) + 1); - if (npc_src_first == NULL) - npc_src_first = new_src; - if (npc_src_last) - npc_src_last->next = new_src; - - npc_src_last = new_src; -} - -/*========================================== - * 読み込むnpcファイルの削除 - *------------------------------------------ - */ -void npc_delsrcfile (char *name) -{ - struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first; - - if (strcasecmp (name, "all") == 0) - { - npc_clearsrcfile (); - return; - } - - for (; p; lp = &p->next, pp = p, p = p->next) - { - if (strcmp (p->name, name) == 0) - { - *lp = p->next; - if (npc_src_last == p) - npc_src_last = pp; - free (p); - break; - } - } -} - -/*========================================== - * 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[24], to_mapname[24]; - struct npc_data *nd; - - // 引数の個数チェック - if (sscanf (w1, "%[^,],%d,%d", mapname, &x, &y) != 3 || - sscanf (w4, "%d,%d,%[^,],%d,%d", &xs, &ys, to_mapname, &to_x, - &to_y) != 5) - { - printf ("bad warp line : %s\n", w3); - return 1; - } - - m = map_mapname2mapid (mapname); - - nd = (struct npc_data *) calloc (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; - nd->dir = 0; - nd->flag = 0; - memcpy (nd->name, w3, 24); - memcpy (nd->exname, w3, 24); - - nd->chat_id = 0; - if (!battle_config.warp_point_debug) - nd->npc_class = WARP_CLASS; - else - nd->npc_class = WARP_DEBUG_CLASS; - nd->speed = 200; - nd->option = 0; - nd->opt1 = 0; - nd->opt2 = 0; - nd->opt3 = 0; - memcpy (nd->u.warp.name, to_mapname, 16); - 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++) - { - int t; - t = map_getcell (m, x - xs / 2 + j, y - ys / 2 + i); - if (t == 1 || t == 5) - continue; - map_setcell (m, x - xs / 2 + j, y - ys / 2 + i, t | 0x80); - } - } - -// printf("warp npc %s %d read done\n",mapname,nd->bl.id); - npc_warp++; - nd->bl.type = BL_NPC; - nd->bl.subtype = WARP; - map_addblock (&nd->bl); - clif_spawnnpc (nd); - strdb_insert (npcname_db, nd->name, nd); - - return 0; -} - -/*========================================== - * shop行解析 - *------------------------------------------ - */ -static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4) -{ - char *p; - int x, y, dir, m; - int max = 100, pos = 0; - char mapname[24]; - struct npc_data *nd; - - // 引数の個数チェック - if (sscanf (w1, "%[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || - strchr (w4, ',') == NULL) - { - printf ("bad shop line : %s\n", w3); - return 1; - } - m = map_mapname2mapid (mapname); - - nd = (struct npc_data *) calloc (1, sizeof (struct npc_data) + - sizeof (nd->u.shop_item[0]) * (max + - 1)); - p = strchr (w4, ','); - - while (p && pos < max) - { - int nameid, value; - char name[24]; - struct item_data *id = NULL; - p++; - if (sscanf (p, "%d:%d", &nameid, &value) == 2) - { - } - else if (sscanf (p, "%s :%d", name, &value) == 2) - { - id = itemdb_searchname (name); - if (id == NULL) - nameid = -1; - else - nameid = id->nameid; - } - else - break; - - if (nameid > 0) - { - nd->u.shop_item[pos].nameid = nameid; - if (value < 0) - { - if (id == NULL) - id = itemdb_search (nameid); - value = id->value_buy * abs (value); - - } - nd->u.shop_item[pos].value = value; - pos++; - } - p = strchr (p, ','); - } - if (pos == 0) - { - free (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, 24); - nd->npc_class = atoi (w4); - nd->speed = 200; - nd->chat_id = 0; - nd->option = 0; - nd->opt1 = 0; - nd->opt2 = 0; - nd->opt3 = 0; - - nd = (struct npc_data *) - realloc (nd, sizeof (struct npc_data) + sizeof (nd->u.shop_item[0]) * pos); - - //printf("shop npc %s %d read done\n",mapname,nd->bl.id); - npc_shop++; - nd->bl.type = BL_NPC; - nd->bl.subtype = SHOP; - nd->n = map_addnpc (m, nd); - map_addblock (&nd->bl); - clif_spawnnpc (nd); - strdb_insert (npcname_db, nd->name, nd); - - return 0; -} - -/*========================================== - * NPCのラベルデータコンバート - *------------------------------------------ - */ -void npc_convertlabel_db (db_key_t key, db_val_t data, va_list ap) -{ - const char *lname = key.s; - int pos = (int) data; - struct npc_data *nd; - struct npc_label_list *lst; - int num; - char *p = strchr (lname, ':'); - - nullpo_retv (ap); - nullpo_retv (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 *) - calloc (1, sizeof (struct npc_label_list)); - num = 0; - } - else - lst = (struct npc_label_list *) - realloc (lst, sizeof (struct npc_label_list) * (num + 1)); - - *p = '\0'; - strncpy (lst[num].name, lname, sizeof(lst[num].name)-1); - lst[num].name[sizeof(lst[num].name)-1] = '\0'; - *p = ':'; - lst[num].pos = pos; - nd->u.scr.label_list = lst; - nd->u.scr.label_list_num = num + 1; -} - -/*========================================== - * script行解析 - *------------------------------------------ - */ -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, npc_class = 0; // [Valaris] thanks to fov - char mapname[24]; - 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, "%[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || - (strcmp (w2, "script") == 0 && strchr (w4, ',') == NULL)) - { - printf ("bad script line : %s\n", w3); - return 1; - } - m = map_mapname2mapid (mapname); - } - - if (strcmp (w2, "script") == 0) - { - // スクリプトの解析 - srcbuf = (char *) calloc (srcsize, sizeof (char)); - if (strchr (first_line, '{')) - { - strcpy (srcbuf, strchr (first_line, '{')); - startline = *lines; - } - else - srcbuf[0] = 0; - while (1) - { - for (i = strlen (srcbuf) - 1; i >= 0 && isspace (srcbuf[i]); i--); - if (i >= 0 && srcbuf[i] == '}') - break; - if (!fgets (line, 1020, fp)) - break; - (*lines)++; - if (feof (fp)) - break; - if (strlen (srcbuf) + strlen (line) + 1 >= srcsize) - { - srcsize += 65536; - srcbuf = (char *) realloc (srcbuf, srcsize); - memset (srcbuf + srcsize - 65536, '\0', 65536); - } - if (srcbuf[0] != '{') - { - if (strchr (line, '{')) - { - strcpy (srcbuf, strchr (line, '{')); - startline = *lines; - } - } - else - strcat (srcbuf, line); - } - script = parse_script (srcbuf, startline); - if (script == NULL) - { - // script parse error? - free (srcbuf); - return 1; - } - - } - else - { - // duplicateする - - char srcname[128]; - struct npc_data *nd2; - if (sscanf (w2, "duplicate(%[^)])", srcname) != 1) - { - printf ("bad duplicate name! : %s", w2); - return 0; - } - if ((nd2 = npc_name2id (srcname)) == NULL) - { - printf ("bad duplicate name! (not exist) : %s\n", srcname); - return 0; - } - script = 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 *) calloc (1, sizeof (struct npc_data)); - - if (m == -1) - { - // スクリプトコピー用のダミーNPC - - } - else if (sscanf (w4, "%d,%d,%d", &npc_class, &xs, &ys) == 3) - { - // 接触型NPC - int i, j; - - if (xs >= 0) - xs = xs * 2 + 1; - if (ys >= 0) - ys = ys * 2 + 1; - - if (npc_class >= 0) - { - - for (i = 0; i < ys; i++) - { - for (j = 0; j < xs; j++) - { - int t; - t = map_getcell (m, x - xs / 2 + j, y - ys / 2 + i); - if (t == 1 || t == 5) - continue; - map_setcell (m, x - xs / 2 + j, y - ys / 2 + i, t | 0x80); - } - } - } - - nd->u.scr.xs = xs; - nd->u.scr.ys = ys; - } - else - { // クリック型NPC - npc_class = atoi (w4); - nd->u.scr.xs = 0; - nd->u.scr.ys = 0; - } - - if (npc_class < 0 && m >= 0) - { // イベント型NPC - evflag = 1; - } - - while ((p = strchr (w3, ':'))) - { - if (p[1] == ':') - break; - } - if (p) - { - *p = 0; - memcpy (nd->name, w3, 24); - memcpy (nd->exname, p + 2, 24); - } - else - { - memcpy (nd->name, w3, 24); - memcpy (nd->exname, w3, 24); - } - - 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->npc_class = npc_class; - nd->speed = 200; - nd->u.scr.script = script; - nd->u.scr.src_id = src_id; - nd->chat_id = 0; - nd->option = 0; - nd->opt1 = 0; - nd->opt2 = 0; - nd->opt3 = 0; - - //printf("script npc %s %d %d read done\n",mapname,nd->bl.id,nd->class); - npc_script++; - nd->bl.type = BL_NPC; - nd->bl.subtype = SCRIPT; - if (m >= 0) - { - nd->n = map_addnpc (m, nd); - map_addblock (&nd->bl); - - if (evflag) - { // イベント型 - struct event_data *ev = - (struct event_data *) calloc (1, sizeof (struct event_data)); - ev->nd = nd; - ev->pos = 0; - strdb_insert (ev_db, nd->exname, ev); - } - else - clif_spawnnpc (nd); - } - strdb_insert (npcname_db, nd->exname, nd); - - //----------------------------------------- - // ラベルデータの準備 - if (srcbuf) - { - // script本体がある場合の処理 - - // ラベルデータのコンバート - label_db = script_get_label_db (); - strdb_foreach (label_db, npc_convertlabel_db, nd); - - // もう使わないのでバッファ解放 - free (srcbuf); - - } - else - { - // duplicate - -// nd->u.scr.label_list=malloc(sizeof(struct npc_label_list)*label_dupnum); -// memcpy(nd->u.scr.label_list,label_dup,sizeof(struct npc_label_list)*label_dupnum); - - 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')) - { - struct event_data *ev; - char *buf; - // エクスポートされる - ev = (struct event_data *) calloc (1, - sizeof (struct event_data)); - buf = (char *) calloc (50, sizeof (char)); - if (strlen (lname) > 24) - { - printf ("npc_parse_script: label name error !\n"); - exit (1); - } - else - { - ev->nd = nd; - ev->pos = pos; - sprintf (buf, "%s::%s", nd->exname, lname); - strdb_insert (ev_db, buf, ev); - } - } - } - - //----------------------------------------- - // ラベルデータからタイマーイベント取り込み - 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 *) calloc (1, - sizeof (struct - npc_timerevent_list)); - else - te = (struct npc_timerevent_list *) realloc (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) -{ - char *srcbuf = NULL, *script; - int srcsize = 65536; - int startline = 0; - char line[1024]; - int i; -// struct dbt *label_db; - char *p; - - // スクリプトの解析 - srcbuf = (char *) calloc (srcsize, sizeof (char)); - if (strchr (first_line, '{')) - { - strcpy (srcbuf, strchr (first_line, '{')); - startline = *lines; - } - else - srcbuf[0] = 0; - while (1) - { - for (i = strlen (srcbuf) - 1; i >= 0 && isspace (srcbuf[i]); i--); - if (i >= 0 && srcbuf[i] == '}') - break; - if (!fgets (line, 1020, fp)) - break; - (*lines)++; - if (feof (fp)) - break; - if (strlen (srcbuf) + strlen (line) + 1 >= srcsize) - { - srcsize += 65536; - srcbuf = (char *) realloc (srcbuf, srcsize); - memset (srcbuf + srcsize - 65536, '\0', 65536); - } - if (srcbuf[0] != '{') - { - if (strchr (line, '{')) - { - strcpy (srcbuf, strchr (line, '{')); - startline = *lines; - } - } - else - strcat (srcbuf, line); - } - script = parse_script (srcbuf, startline); - if (script == NULL) - { - // script parse error? - free (srcbuf); - return 1; - } - - p = (char *) calloc (50, sizeof (char)); - - strncpy (p, w3, 49); - strdb_insert (script_get_userfunc_db (), p, script); - -// label_db=script_get_label_db(); - - // もう使わないのでバッファ解放 - free (srcbuf); - -// printf("function %s => %p\n",p,script); - - return 0; -} - -/*========================================== - * mob行解析 - *------------------------------------------ - */ -int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) -{ - int m, x, y, xs, ys, mob_class, num, delay1, delay2; - int i; - char mapname[24]; - char eventname[24] = ""; - struct mob_data *md; - - xs = ys = 0; - delay1 = delay2 = 0; - // 引数の個数チェック - if (sscanf (w1, "%[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 || - sscanf (w4, "%d,%d,%d,%d,%s", &mob_class, &num, &delay1, &delay2, - eventname) < 2) - { - printf ("bad monster line : %s\n", w3); - return 1; - } - - m = map_mapname2mapid (mapname); - - if (num > 1 && battle_config.mob_count_rate != 100) - { - if ((num = num * battle_config.mob_count_rate / 100) < 1) - num = 1; - } - - for (i = 0; i < num; i++) - { - md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); - - md->bl.prev = NULL; - md->bl.next = NULL; - md->bl.m = m; - md->bl.x = x; - md->bl.y = y; - if (strcmp (w3, "--en--") == 0) - memcpy (md->name, mob_db[mob_class].name, 24); - else if (strcmp (w3, "--ja--") == 0) - memcpy (md->name, mob_db[mob_class].jname, 24); - else - memcpy (md->name, w3, 24); - - md->n = i; - md->base_class = md->mob_class = mob_class; - md->bl.id = npc_get_new_npc_id (); - md->m = m; - md->x0 = x; - md->y0 = y; - md->xs = xs; - md->ys = ys; - md->spawndelay1 = delay1; - md->spawndelay2 = delay2; - - memset (&md->state, 0, sizeof (md->state)); - md->timer = -1; - md->target_id = 0; - md->attacked_id = 0; - - if (mob_db[mob_class].mode & 0x02) - md->lootitem = - (struct item *) calloc (LOOTITEM_SIZE, sizeof (struct item)); - else - md->lootitem = NULL; - - if (strlen (eventname) >= 4) - { - memcpy (md->npc_event, eventname, 24); - } - else - memset (md->npc_event, 0, 24); - - md->bl.type = BL_MOB; - map_addiddb (&md->bl); - mob_spawn (md->bl.id); - - npc_mob++; - } - //printf("warp npc %s %d read done\n",mapname,nd->bl.id); - - return 0; -} - -/*========================================== - * マップフラグ行の解析 - *------------------------------------------ - */ -static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) -{ - int m; - char mapname[24], savemap[16]; - int savex, savey; - char drop_arg1[16], drop_arg2[16]; - int drop_id = 0, drop_type = 0, drop_per = 0; - - // 引数の個数チェック -// if ( sscanf(w1,"%[^,],%d,%d,%d",mapname,&x,&y,&dir) != 4 ) - if (sscanf (w1, "%[^,]", mapname) != 1) - return 1; - - m = map_mapname2mapid (mapname); - if (m < 0) - return 1; - -//マップフラグ - if (strcasecmp (w3, "nosave") == 0) - { - if (strcmp (w4, "SavePoint") == 0) - { - memcpy (map[m].save.map, "SavePoint", 16); - map[m].save.x = -1; - map[m].save.y = -1; - } - else if (sscanf (w4, "%[^,],%d,%d", savemap, &savex, &savey) == 3) - { - memcpy (map[m].save.map, savemap, 16); - map[m].save.x = savex; - map[m].save.y = savey; - } - map[m].flag.nosave = 1; - } - else if (strcasecmp (w3, "nomemo") == 0) - { - map[m].flag.nomemo = 1; - } - else if (strcasecmp (w3, "noteleport") == 0) - { - map[m].flag.noteleport = 1; - } - else if (strcasecmp (w3, "nowarp") == 0) - { - map[m].flag.nowarp = 1; - } - else if (strcasecmp (w3, "nowarpto") == 0) - { - map[m].flag.nowarpto = 1; - } - else if (strcasecmp (w3, "noreturn") == 0) - { - map[m].flag.noreturn = 1; - } - else if (strcasecmp (w3, "monster_noteleport") == 0) - { - map[m].flag.monster_noteleport = 1; - } - else if (strcasecmp (w3, "nobranch") == 0) - { - map[m].flag.nobranch = 1; - } - else if (strcasecmp (w3, "nopenalty") == 0) - { - map[m].flag.nopenalty = 1; - } - else if (strcasecmp (w3, "pvp") == 0) - { - map[m].flag.pvp = 1; - } - else if (strcasecmp (w3, "pvp_noparty") == 0) - { - map[m].flag.pvp_noparty = 1; - } - else if (strcasecmp (w3, "pvp_noguild") == 0) - { - map[m].flag.pvp_noguild = 1; - } - else if (strcasecmp (w3, "pvp_nightmaredrop") == 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 (strcasecmp (w3, "pvp_nocalcrank") == 0) - { - map[m].flag.pvp_nocalcrank = 1; - } - else if (strcasecmp (w3, "gvg") == 0) - { - map[m].flag.gvg = 1; - } - else if (strcasecmp (w3, "gvg_noparty") == 0) - { - map[m].flag.gvg_noparty = 1; - } - else if (strcasecmp (w3, "nozenypenalty") == 0) - { - map[m].flag.nozenypenalty = 1; - } - else if (strcasecmp (w3, "notrade") == 0) - { - map[m].flag.notrade = 1; - } - else if (strcasecmp (w3, "noskill") == 0) - { - map[m].flag.noskill = 1; - } - else if (battle_config.pk_mode && strcasecmp (w3, "nopvp") == 0) - { // nopvp for pk mode [Valaris] - map[m].flag.nopvp = 1; - map[m].flag.pvp = 0; - } - else if (strcasecmp (w3, "noicewall") == 0) - { // noicewall [Valaris] - map[m].flag.noicewall = 1; - } - else if (strcasecmp (w3, "snow") == 0) - { // snow [Valaris] - map[m].flag.snow = 1; - } - else if (strcasecmp (w3, "fog") == 0) - { // fog [Valaris] - map[m].flag.fog = 1; - } - else if (strcasecmp (w3, "sakura") == 0) - { // sakura [Valaris] - map[m].flag.sakura = 1; - } - else if (strcasecmp (w3, "leaves") == 0) - { // leaves [Valaris] - map[m].flag.leaves = 1; - } - else if (strcasecmp (w3, "rain") == 0) - { // rain [Valaris] - map[m].flag.rain = 1; - } - else if (strcasecmp (w3, "no_player_drops") == 0) - { // no player drops [Jaxad0127] - map[m].flag.no_player_drops = 1; - } - else if (strcasecmp (w3, "town") == 0) - { // town/safe zone [remoitnane] - map[m].flag.town = 1; - } - - return 0; -} - -static void ev_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); - if (strstr (key.s, "::") != NULL) - free ((char*)key.s); -} - -struct npc_data *npc_spawn_text (int m, int x, int y, - int npc_class, char *name, char *message) -{ - struct npc_data *retval = - (struct npc_data *) calloc (1, sizeof (struct npc_data)); - retval->bl.id = npc_get_new_npc_id (); - retval->bl.x = x; - retval->bl.y = y; - retval->bl.m = m; - retval->bl.type = BL_NPC; - retval->bl.subtype = MESSAGE; - - strncpy (retval->name, name, 23); - strncpy (retval->exname, name, 23); - retval->name[15] = 0; - retval->exname[15] = 0; - retval->u.message = message ? strdup (message) : NULL; - - retval->npc_class = npc_class; - retval->speed = 200; - - clif_spawnnpc (retval); - map_addblock (&retval->bl); - map_addiddb (&retval->bl); - if (retval->name && retval->name[0]) - strdb_insert (npcname_db, retval->name, retval); - - return retval; -} - -static void npc_free_internal (struct npc_data *nd) -{ - struct chat_data *cd; - - if (nd->chat_id && (cd = (struct chat_data *) map_id2bl (nd->chat_id))) - { - free (cd); - cd = NULL; - } - if (nd->bl.subtype == SCRIPT) - { - if (nd->u.scr.timer_event) - free (nd->u.scr.timer_event); - if (nd->u.scr.src_id == 0) - { - if (nd->u.scr.script) - { - free (nd->u.scr.script); - nd->u.scr.script = NULL; - } - if (nd->u.scr.label_list) - { - free (nd->u.scr.label_list); - nd->u.scr.label_list = NULL; - } - } - } - else if (nd->bl.subtype == MESSAGE && nd->u.message) - { - free (nd->u.message); - } - free (nd); -} - -void npc_propagate_update (struct npc_data *nd) -{ - 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); -} - -void npc_free (struct npc_data *nd) -{ - clif_clearchar (&nd->bl, 0); - npc_propagate_update (nd); - map_deliddb (&nd->bl); - map_delblock (&nd->bl); - npc_free_internal (nd); -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -int do_final_npc (void) -{ - int i; - struct block_list *bl; - struct npc_data *nd; - struct mob_data *md; - - if (ev_db) - strdb_final (ev_db, ev_db_final); - if (npcname_db) - strdb_final (npcname_db, NULL); - - 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_free_internal (nd); - else if (bl->type == BL_MOB && (md = (struct mob_data *) bl)) - { - if (md->lootitem) - { - free (md->lootitem); - md->lootitem = NULL; - } - free (md); - md = NULL; - } - } - } - - return 0; -} - -void ev_release (db_key_t key, db_val_t val) -{ - free ((char*)key.s); - free (val); -} - -/*========================================== - * npc初期化 - *------------------------------------------ - */ -int do_init_npc (void) -{ - struct npc_src_list *nsl; - FILE *fp; - char line[1024]; - int m, lines; - - ev_db = strdb_init (24); - npcname_db = strdb_init (24); - - ev_db->release = ev_release; - - memset (&ev_tm_b, -1, sizeof (ev_tm_b)); - - for (nsl = npc_src_first; nsl; nsl = nsl->next) - { - if (nsl->prev) - { - free (nsl->prev); - nsl->prev = NULL; - } - fp = fopen_ (nsl->name, "r"); - if (fp == NULL) - { - printf ("file not found : %s\n", nsl->name); - exit (1); - } - lines = 0; - while (fgets (line, 1020, 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 (line[i + 1]) || line[i + 1] == ',')) - || (j && line[j - 1] == ','))) - line[j++] = ' '; - } - else if (line[i] == '\t' || line[i] == '|') - { - if (!(j && (line[j - 1] == '\t' || line[j - 1] == '|'))) - line[j++] = '\t'; - } - else - line[j++] = line[i]; - } - // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認 - 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 && strcasecmp (w1, "function") != 0) - { - sscanf (w1, "%[^,]", mapname); - m = map_mapname2mapid (mapname); - if (strlen (mapname) > 16 || m < 0) - { - // "mapname" is not assigned to this server - continue; - } - } - if (strcasecmp (w2, "warp") == 0 && count > 3) - { - npc_parse_warp (w1, w2, w3, w4); - } - else if (strcasecmp (w2, "shop") == 0 && count > 3) - { - npc_parse_shop (w1, w2, w3, w4); - } - else if (strcasecmp (w2, "script") == 0 && count > 3) - { - if (strcasecmp (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 (strcasecmp (w2, "monster") == 0 && count > 3) - { - npc_parse_mob (w1, w2, w3, w4); - } - else if (strcasecmp (w2, "mapflag") == 0 && count >= 3) - { - npc_parse_mapflag (w1, w2, w3, w4); - } - } - fclose_ (fp); - printf ("\rLoading NPCs [%d]: %-54s", npc_id - START_NPC_NUM, - nsl->name); - fflush (stdout); - } - printf ("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d]\n", - npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob); - - return 0; -} diff --git a/src/map/npc.cpp b/src/map/npc.cpp new file mode 100644 index 0000000..b2612f0 --- /dev/null +++ b/src/map/npc.cpp @@ -0,0 +1,2378 @@ +// $Id: npc.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <math.h> +#include <time.h> + +#include "../common/nullpo.hpp" +#include "../common/timer.hpp" + +#include "battle.hpp" +#include "clif.hpp" +#include "../common/db.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "../common/socket.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +struct npc_src_list +{ + struct npc_src_list *next; + struct npc_src_list *prev; + char name[4]; +}; + +static struct npc_src_list *npc_src_first, *npc_src_last; +static int npc_id = START_NPC_NUM; +static int npc_warp, npc_shop, npc_script, npc_mob; + +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; // 時計イベント用 + +/*========================================== + * 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 *) calloc (50, sizeof (char)); + + 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)) + { + + if (nd->flag & 1) // 無効化されている + return 1; + + memcpy (name, nd->name, sizeof(nd->name)); + if (sd->areanpc_id == nd->bl.id) + return 1; + sd->areanpc_id = nd->bl.id; + npc_event (sd, strcat (name, "::OnTouch"), 0); + } + free (name); + return 0; +} + +int npc_enable (const char *name, int flag) +{ + struct npc_data *nd = (struct npc_data *)strdb_search (npcname_db, 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_search (npcname_db, name); +} + +/*========================================== + * イベントキューのイベント処理 + *------------------------------------------ + */ +int npc_event_dequeue (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + sd->npc_id = 0; + + if (sd->eventqueue[0][0]) // キューのイベント処理 + { + if (!pc_addeventtimer(sd, 100, sd->eventqueue[0])) + { + printf ("npc_event_dequeue(): Event timer is full.\n"); + return 0; + } + + if (MAX_EVENTQUEUE > 1) + memmove (sd->eventqueue[0], sd->eventqueue[1], + (MAX_EVENTQUEUE - 1) * sizeof (sd->eventqueue[0])); + sd->eventqueue[MAX_EVENTQUEUE - 1][0] = '\0'; + return 1; + } + + return 0; +} + +int npc_delete (struct npc_data *nd) +{ + nullpo_retr (1, nd); + + if (nd->bl.prev == NULL) + return 1; + + clif_clearchar_area (&nd->bl, 1); + map_delblock (&nd->bl); + return 0; +} + +/*========================================== + * イベントの遅延実行 + *------------------------------------------ + */ +void npc_event_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = map_id2sd (id); + if (sd == NULL) + return; + + npc_event (sd, (const char *) data, 0); + free ((void *) data); +} + +int npc_timer_event (const char *eventname) // Added by RoVeRT +{ + struct event_data *ev = (struct event_data *)strdb_search (ev_db, eventname); + struct npc_data *nd; +// int xs,ys; + + if ((ev == NULL || (nd = ev->nd) == NULL)) + { + printf ("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(void *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(void *key,void *data,va_list ap) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data*)data; + + if(nd->timer == -1) + return 0; + + strdb_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 +{ + strdb_foreach(npcname_db,npc_timer_sub); + + free((void*)data); + return 0; +}*/ +/*========================================== + * イベント用ラベルのエクスポート + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_event_export (void *key, void *data, va_list ap) +{ + char *lname = (char *) key; + 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; + char *buf; + char *p = strchr (lname, ':'); + // エクスポートされる + CREATE (ev, struct event_data, 1); + CREATE (buf, char, 50); + if (p == NULL || (p - lname) > 24) + { + printf ("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_insert (ev_db, buf, ev); +// if (battle_config.etc_log) +// printf("npc_event_export: export [%s]\n",buf); + } + } + return 0; +} + +/*========================================== + * 全てのNPCのOn*イベント実行 + *------------------------------------------ + */ +void npc_event_doall_sub (db_key_t key, db_val_t data, va_list ap) +{ + const char *p = key.s; + int rid, argc; + argrec_t *argv; + struct event_data *ev; + int *c; + const char *name; + + nullpo_retv (ev = (struct event_data *) data); + nullpo_retv (ap); + nullpo_retv (c = va_arg (ap, int *)); + + name = va_arg (ap, const char *); + rid = va_arg (ap, int); + argc = va_arg (ap, int); + argv = va_arg (ap, argrec_t *); + + if ((p = strchr (p, ':')) && p && strcasecmp (name, p) == 0) + { + run_script_l (ev->nd->u.scr.script, ev->pos, rid, ev->nd->bl.id, argc, + argv); + (*c)++; + } +} + +int npc_event_doall_l (const char *name, int rid, int argc, argrec_t * args) +{ + int c = 0; + char buf[64] = "::"; + + strncpy (buf + 2, name, sizeof(buf)-3); + buf[sizeof(buf)-1] = '\0'; + strdb_foreach (ev_db, npc_event_doall_sub, &c, buf, rid, argc, args); + return c; +} + +void npc_event_do_sub (db_key_t key, db_val_t data, va_list ap) +{ + const char *p = key.s; + struct event_data *ev; + int *c; + const char *name; + int rid, argc; + argrec_t *argv; + + nullpo_retv (ev = (struct event_data *) data); + nullpo_retv (ap); + nullpo_retv (c = va_arg (ap, int *)); + + name = va_arg (ap, const char *); + rid = va_arg (ap, int); + argc = va_arg (ap, int); + argv = va_arg (ap, argrec_t *); + + if (p && strcasecmp (name, p) == 0) + { + run_script_l (ev->nd->u.scr.script, ev->pos, rid, ev->nd->bl.id, argc, + argv); + (*c)++; + } +} + +int npc_event_do_l (const char *name, int rid, int argc, argrec_t * args) +{ + int c = 0; + + if (*name == ':' && name[1] == ':') + { + return npc_event_doall_l (name + 2, rid, argc, args); + } + + strdb_foreach (ev_db, npc_event_do_sub, &c, name, rid, argc, args); + return c; +} + +/*========================================== + * 時計イベント実行 + *------------------------------------------ + */ +void npc_event_do_clock (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + time_t timer; + struct tm *t; + char buf[64]; + int c = 0; + + time (&timer); + t = gmtime (&timer); + + 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); + } + 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)); +} + +/*========================================== + * OnInitイベント実行(&時計イベント開始) + *------------------------------------------ + */ +int npc_event_do_oninit (void) +{ + int c = npc_event_doall ("OnInit"); + printf ("npc: OnInit Event done. (%d npc)\n", c); + + 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) + { + char *evname; + CREATE (evname, char, 24); + memcpy (evname, name, 24); + nd->eventtimer[i] = add_timer (gettick () + tick, + npc_event_timer, nd->bl.id, + (int) evname); + } + else + printf ("npc_addtimer: event timer is full !\n"); + + return 0; +} + +int npc_deleventtimer (struct npc_data *nd, const char *name) +{ + int i; + for (i = 0; i < MAX_EVENTTIMER; i++) + if (nd->eventtimer[i] != -1 && strcmp ((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; +} + +void npc_do_ontimer_sub (db_key_t key, db_val_t data, va_list ap) +{ + const char *p = key.s; + 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, ':')) && p + && strncasecmp ("::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); + } + } +} + +int npc_do_ontimer (int npc_id, struct map_session_data *sd, int option) +{ + strdb_foreach (ev_db, npc_do_ontimer_sub, &npc_id, sd, option); + return 0; +} + +/*========================================== + * タイマーイベント用ラベルの取り込み + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_timerevent_import (void *key, void *data, va_list ap) +{ + char *lname = (char *) key; + 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; + RECREATE (te, struct npc_timerevent_list, i+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; +} + +/*========================================== + * タイマーイベント実行 + *------------------------------------------ + */ +void npc_timerevent (timer_id tid, tick_t tick, custom_id_t id, custom_data_t 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) + { + printf ("npc_timerevent: ??\n"); + return; + } + 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, 0, nd->bl.id); +} + +/*========================================== + * タイマーイベント開始 + *------------------------------------------ + */ +int npc_timerevent_start (struct npc_data *nd) +{ + 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; + } + nd->u.scr.nexttimer = j; + nd->u.scr.timertick = gettick (); + + if (j >= n) + return 0; + + 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; + } + 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); + return 0; +} + +/*========================================== + * イベント型のNPC処理 + *------------------------------------------ + */ +int npc_event (struct map_session_data *sd, const char *eventname, + int mob_kill) +{ + struct event_data *ev = (struct event_data *)strdb_search (ev_db, eventname); + struct npc_data *nd; + int xs, ys; + char mobevent[100]; + + if (sd == NULL) + { + printf ("npc_event nullpo?\n"); + } + + if (ev == NULL && eventname + && strcmp (((eventname) + strlen (eventname) - 9), "::OnTouch") == 0) + return 1; + + if (ev == NULL || (nd = ev->nd) == NULL) + { + if (mob_kill && (ev == NULL || (nd = ev->nd) == NULL)) + { + strcpy (mobevent, eventname); + strcat (mobevent, "::OnMyMobDead"); + ev = (struct event_data *)strdb_search (ev_db, mobevent); + if (ev == NULL || (nd = ev->nd) == NULL) + { + if (strncasecmp (eventname, "GM_MONSTER", 10) != 0) + printf ("npc_event: event not found [%s]\n", mobevent); + return 0; + } + } + else + { + if (battle_config.error_log) + printf ("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) + { + 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) + printf ("npc_event: event queue is full !\n"); + } + else + { +// if (battle_config.etc_log) +// printf("npc_event: enqueue\n"); + strncpy (sd->eventqueue[i], eventname, 50); + sd->eventqueue[i][49] = '\0'; + } + 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; +} + +void npc_command_sub (db_key_t key, db_val_t data, va_list ap) +{ + const char *p = key.s; + struct event_data *ev = (struct event_data *) data; + char *npcname = va_arg (ap, char *); + char *command = va_arg (ap, char *); + char temp[100]; + + if (strcmp (ev->nd->name, npcname) == 0 && (p = strchr (p, ':')) && p + && strncasecmp ("::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); + } +} + +int npc_command (struct map_session_data *sd, char *npcname, char *command) +{ + strdb_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 MESSAGE: + 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) + printf ("npc_touch_areanpc : some bug \n"); + } + return 1; + } + switch (map[m].npc[i]->bl.subtype) + { + case WARP: + skill_stop_dancing (&sd->bl, 0); + pc_setpos (sd, map[m].npc[i]->u.warp.name, + map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, 0); + break; + case MESSAGE: + case SCRIPT: + { + char *name = (char *)malloc (50); + + memcpy (name, map[m].npc[i]->name, 50); + if (sd->areanpc_id == map[m].npc[i]->bl.id) + return 1; + sd->areanpc_id = map[m].npc[i]->bl.id; + if (npc_event (sd, strcat (name, "::OnTouch"), 0) > 0) + npc_click (sd, map[m].npc[i]->bl.id); + free (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) + printf ("no such npc : %d\n", id); + return 1; + } + + if (nd->npc_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_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) + printf ("npc_click: npc_id != 0\n"); + return 1; + } + + if (npc_checknear (sd, id)) { + clif_scriptclose (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; + case MESSAGE: + if (nd->u.message) + { + clif_scriptmes (sd, id, nd->u.message); + clif_scriptclose (sd, 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)) { + clif_scriptclose (sd, id); + return 1; + } + + nd = (struct npc_data *) map_id2bl (id); + + if (!nd /* NPC was disposed? */ || nd->bl.subtype == MESSAGE) + { + clif_scriptclose (sd, id); + npc_event_dequeue (sd); + return 0; + } + + 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) + printf ("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_stacks = 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_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: + if (itemdb_isequip (item_list[i * 2 + 1])) + new_stacks += item_list[i * 2]; + else + new_stacks++; + 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_stacks) + return 3; // 種類数超過 + if (sd->trade_partner != 0) + return 4; // cant buy while trading + + pc_payzeny (sd, (int) z); + + for (i = 0; i < n; i++) + { + struct item_data *item_data; + if ((item_data = itemdb_exists (item_list[i * 2 + 1])) != NULL) + { + int amount = item_list[i * 2]; + struct item item_tmp; + memset (&item_tmp, 0, sizeof (item_tmp)); + + item_tmp.nameid = item_data->nameid; + item_tmp.identify = 1; // npc販売アイテムは鑑定済み + + if (amount > 1 + && (item_data->type == 4 || item_data->type == 5 + || item_data->type == 7 || item_data->type == 8)) + { + for (j = 0; j < amount; j++) + { + pc_additem (sd, &item_tmp, 1); + } + } + else + { + pc_additem (sd, &item_tmp, amount); + } + } + } + + //商人経験値 +/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { + z = z * pc_checkskill(sd,MC_DISCOUNT) / ((1 + 300 / itemamount) * 4000) * battle_config.shop_exp; + pc_gainexp(sd,0,z); + }*/ + if (battle_config.shop_exp > 0 && z > 0 + && (skill = pc_checkskill (sd, MC_DISCOUNT)) > 0) + { + if (skill > 0) + { + z = (log (z * (double) skill) * (double) battle_config.shop_exp / + 100.); + 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 (sd->trade_partner != 0) + return 2; // cant sell while trading + 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; + pc_getzeny (sd, (int) z); + for (i = 0; i < n; i++) + { + int item_id = item_list[i * 2] - 2; + pc_delitem (sd, item_id, item_list[i * 2 + 1], 0); + } + + //商人経験値 +/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { + z = z * pc_checkskill(sd,MC_OVERCHARGE) / ((1 + 500 / itemamount) * 4000) * battle_config.shop_exp ; + pc_gainexp(sd,0,z); + }*/ + if (battle_config.shop_exp > 0 && z > 0 + && (skill = pc_checkskill (sd, MC_OVERCHARGE)) > 0) + { + if (skill > 0) + { + z = (log (z * (double) skill) * (double) battle_config.shop_exp / + 100.); + if (z < 1) + z = 1; + pc_gainexp (sd, 0, (int) z); + } + } + + return 0; + +} + +// +// 初期化関係 +// + +/*========================================== + * 読み込むnpcファイルのクリア + *------------------------------------------ + */ +void npc_clearsrcfile (void) +{ + struct npc_src_list *p = npc_src_first; + + while (p) + { + struct npc_src_list *p2 = p; + p = p->next; + free (p2); + } + npc_src_first = NULL; + npc_src_last = NULL; +} + +/*========================================== + * 読み込むnpcファイルの追加 + *------------------------------------------ + */ +void npc_addsrcfile (char *name) +{ + struct npc_src_list *new_src; + size_t len; + + if (strcasecmp (name, "clear") == 0) + { + npc_clearsrcfile (); + return; + } + + len = sizeof (*new_src) + strlen (name); + new_src = (struct npc_src_list *) calloc (1, len); + new_src->next = NULL; + strncpy (new_src->name, name, strlen (name) + 1); + if (npc_src_first == NULL) + npc_src_first = new_src; + if (npc_src_last) + npc_src_last->next = new_src; + + npc_src_last = new_src; +} + +/*========================================== + * 読み込むnpcファイルの削除 + *------------------------------------------ + */ +void npc_delsrcfile (char *name) +{ + struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first; + + if (strcasecmp (name, "all") == 0) + { + npc_clearsrcfile (); + return; + } + + for (; p; lp = &p->next, pp = p, p = p->next) + { + if (strcmp (p->name, name) == 0) + { + *lp = p->next; + if (npc_src_last == p) + npc_src_last = pp; + free (p); + break; + } + } +} + +/*========================================== + * 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[24], to_mapname[24]; + struct npc_data *nd; + + // 引数の個数チェック + if (sscanf (w1, "%[^,],%d,%d", mapname, &x, &y) != 3 || + sscanf (w4, "%d,%d,%[^,],%d,%d", &xs, &ys, to_mapname, &to_x, + &to_y) != 5) + { + printf ("bad warp line : %s\n", w3); + return 1; + } + + m = map_mapname2mapid (mapname); + + nd = (struct npc_data *) calloc (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; + nd->dir = 0; + nd->flag = 0; + memcpy (nd->name, w3, 24); + memcpy (nd->exname, w3, 24); + + nd->chat_id = 0; + if (!battle_config.warp_point_debug) + nd->npc_class = WARP_CLASS; + else + nd->npc_class = WARP_DEBUG_CLASS; + nd->speed = 200; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + memcpy (nd->u.warp.name, to_mapname, 16); + 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++) + { + int t; + t = map_getcell (m, x - xs / 2 + j, y - ys / 2 + i); + if (t == 1 || t == 5) + continue; + map_setcell (m, x - xs / 2 + j, y - ys / 2 + i, t | 0x80); + } + } + +// printf("warp npc %s %d read done\n",mapname,nd->bl.id); + npc_warp++; + nd->bl.type = BL_NPC; + nd->bl.subtype = WARP; + map_addblock (&nd->bl); + clif_spawnnpc (nd); + strdb_insert (npcname_db, nd->name, nd); + + return 0; +} + +/*========================================== + * shop行解析 + *------------------------------------------ + */ +static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4) +{ + char *p; + int x, y, dir, m; + int max = 100, pos = 0; + char mapname[24]; + struct npc_data *nd; + + // 引数の個数チェック + if (sscanf (w1, "%[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || + strchr (w4, ',') == NULL) + { + printf ("bad shop line : %s\n", w3); + return 1; + } + m = map_mapname2mapid (mapname); + + nd = (struct npc_data *) calloc (1, sizeof (struct npc_data) + + sizeof (nd->u.shop_item[0]) * (max + + 1)); + p = strchr (w4, ','); + + while (p && pos < max) + { + int nameid, value; + char name[24]; + struct item_data *id = NULL; + p++; + if (sscanf (p, "%d:%d", &nameid, &value) == 2) + { + } + else if (sscanf (p, "%s :%d", name, &value) == 2) + { + id = itemdb_searchname (name); + if (id == NULL) + nameid = -1; + else + nameid = id->nameid; + } + else + break; + + if (nameid > 0) + { + nd->u.shop_item[pos].nameid = nameid; + if (value < 0) + { + if (id == NULL) + id = itemdb_search (nameid); + value = id->value_buy * abs (value); + + } + nd->u.shop_item[pos].value = value; + pos++; + } + p = strchr (p, ','); + } + if (pos == 0) + { + free (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, 24); + nd->npc_class = atoi (w4); + nd->speed = 200; + nd->chat_id = 0; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + + nd = (struct npc_data *) + realloc (nd, sizeof (struct npc_data) + sizeof (nd->u.shop_item[0]) * pos); + + //printf("shop npc %s %d read done\n",mapname,nd->bl.id); + npc_shop++; + nd->bl.type = BL_NPC; + nd->bl.subtype = SHOP; + nd->n = map_addnpc (m, nd); + map_addblock (&nd->bl); + clif_spawnnpc (nd); + strdb_insert (npcname_db, nd->name, nd); + + return 0; +} + +/*========================================== + * NPCのラベルデータコンバート + *------------------------------------------ + */ +void npc_convertlabel_db (db_key_t key, db_val_t data, va_list ap) +{ + const char *lname = key.s; + int pos = (int) data; + struct npc_data *nd; + struct npc_label_list *lst; + int num; + char *p = strchr (lname, ':'); + + nullpo_retv (ap); + nullpo_retv (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 *) + calloc (1, sizeof (struct npc_label_list)); + num = 0; + } + else + lst = (struct npc_label_list *) + realloc (lst, sizeof (struct npc_label_list) * (num + 1)); + + *p = '\0'; + strncpy (lst[num].name, lname, sizeof(lst[num].name)-1); + lst[num].name[sizeof(lst[num].name)-1] = '\0'; + *p = ':'; + lst[num].pos = pos; + nd->u.scr.label_list = lst; + nd->u.scr.label_list_num = num + 1; +} + +/*========================================== + * script行解析 + *------------------------------------------ + */ +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, npc_class = 0; // [Valaris] thanks to fov + char mapname[24]; + 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, "%[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || + (strcmp (w2, "script") == 0 && strchr (w4, ',') == NULL)) + { + printf ("bad script line : %s\n", w3); + return 1; + } + m = map_mapname2mapid (mapname); + } + + if (strcmp (w2, "script") == 0) + { + // スクリプトの解析 + srcbuf = (char *) calloc (srcsize, sizeof (char)); + if (strchr (first_line, '{')) + { + strcpy (srcbuf, strchr (first_line, '{')); + startline = *lines; + } + else + srcbuf[0] = 0; + while (1) + { + for (i = strlen (srcbuf) - 1; i >= 0 && isspace (srcbuf[i]); i--); + if (i >= 0 && srcbuf[i] == '}') + break; + if (!fgets (line, 1020, fp)) + break; + (*lines)++; + if (feof (fp)) + break; + if (strlen (srcbuf) + strlen (line) + 1 >= srcsize) + { + srcsize += 65536; + srcbuf = (char *) realloc (srcbuf, srcsize); + memset (srcbuf + srcsize - 65536, '\0', 65536); + } + if (srcbuf[0] != '{') + { + if (strchr (line, '{')) + { + strcpy (srcbuf, strchr (line, '{')); + startline = *lines; + } + } + else + strcat (srcbuf, line); + } + script = parse_script (srcbuf, startline); + if (script == NULL) + { + // script parse error? + free (srcbuf); + return 1; + } + + } + else + { + // duplicateする + + char srcname[128]; + struct npc_data *nd2; + if (sscanf (w2, "duplicate(%[^)])", srcname) != 1) + { + printf ("bad duplicate name! : %s", w2); + return 0; + } + if ((nd2 = npc_name2id (srcname)) == NULL) + { + printf ("bad duplicate name! (not exist) : %s\n", srcname); + return 0; + } + script = 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 *) calloc (1, sizeof (struct npc_data)); + + if (m == -1) + { + // スクリプトコピー用のダミーNPC + + } + else if (sscanf (w4, "%d,%d,%d", &npc_class, &xs, &ys) == 3) + { + // 接触型NPC + int i, j; + + if (xs >= 0) + xs = xs * 2 + 1; + if (ys >= 0) + ys = ys * 2 + 1; + + if (npc_class >= 0) + { + + for (i = 0; i < ys; i++) + { + for (j = 0; j < xs; j++) + { + int t; + t = map_getcell (m, x - xs / 2 + j, y - ys / 2 + i); + if (t == 1 || t == 5) + continue; + map_setcell (m, x - xs / 2 + j, y - ys / 2 + i, t | 0x80); + } + } + } + + nd->u.scr.xs = xs; + nd->u.scr.ys = ys; + } + else + { // クリック型NPC + npc_class = atoi (w4); + nd->u.scr.xs = 0; + nd->u.scr.ys = 0; + } + + if (npc_class < 0 && m >= 0) + { // イベント型NPC + evflag = 1; + } + + while ((p = strchr (w3, ':'))) + { + if (p[1] == ':') + break; + } + if (p) + { + *p = 0; + memcpy (nd->name, w3, 24); + memcpy (nd->exname, p + 2, 24); + } + else + { + memcpy (nd->name, w3, 24); + memcpy (nd->exname, w3, 24); + } + + 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->npc_class = npc_class; + nd->speed = 200; + nd->u.scr.script = script; + nd->u.scr.src_id = src_id; + nd->chat_id = 0; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + + //printf("script npc %s %d %d read done\n",mapname,nd->bl.id,nd->class); + npc_script++; + nd->bl.type = BL_NPC; + nd->bl.subtype = SCRIPT; + if (m >= 0) + { + nd->n = map_addnpc (m, nd); + map_addblock (&nd->bl); + + if (evflag) + { // イベント型 + struct event_data *ev = + (struct event_data *) calloc (1, sizeof (struct event_data)); + ev->nd = nd; + ev->pos = 0; + strdb_insert (ev_db, nd->exname, ev); + } + else + clif_spawnnpc (nd); + } + strdb_insert (npcname_db, nd->exname, nd); + + //----------------------------------------- + // ラベルデータの準備 + if (srcbuf) + { + // script本体がある場合の処理 + + // ラベルデータのコンバート + label_db = script_get_label_db (); + strdb_foreach (label_db, npc_convertlabel_db, nd); + + // もう使わないのでバッファ解放 + free (srcbuf); + + } + else + { + // duplicate + +// nd->u.scr.label_list=malloc(sizeof(struct npc_label_list)*label_dupnum); +// memcpy(nd->u.scr.label_list,label_dup,sizeof(struct npc_label_list)*label_dupnum); + + 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')) + { + struct event_data *ev; + char *buf; + // エクスポートされる + ev = (struct event_data *) calloc (1, + sizeof (struct event_data)); + buf = (char *) calloc (50, sizeof (char)); + if (strlen (lname) > 24) + { + printf ("npc_parse_script: label name error !\n"); + exit (1); + } + else + { + ev->nd = nd; + ev->pos = pos; + sprintf (buf, "%s::%s", nd->exname, lname); + strdb_insert (ev_db, buf, ev); + } + } + } + + //----------------------------------------- + // ラベルデータからタイマーイベント取り込み + 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 *) calloc (1, + sizeof (struct + npc_timerevent_list)); + else + te = (struct npc_timerevent_list *) realloc (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) +{ + char *srcbuf = NULL, *script; + int srcsize = 65536; + int startline = 0; + char line[1024]; + int i; +// struct dbt *label_db; + char *p; + + // スクリプトの解析 + srcbuf = (char *) calloc (srcsize, sizeof (char)); + if (strchr (first_line, '{')) + { + strcpy (srcbuf, strchr (first_line, '{')); + startline = *lines; + } + else + srcbuf[0] = 0; + while (1) + { + for (i = strlen (srcbuf) - 1; i >= 0 && isspace (srcbuf[i]); i--); + if (i >= 0 && srcbuf[i] == '}') + break; + if (!fgets (line, 1020, fp)) + break; + (*lines)++; + if (feof (fp)) + break; + if (strlen (srcbuf) + strlen (line) + 1 >= srcsize) + { + srcsize += 65536; + srcbuf = (char *) realloc (srcbuf, srcsize); + memset (srcbuf + srcsize - 65536, '\0', 65536); + } + if (srcbuf[0] != '{') + { + if (strchr (line, '{')) + { + strcpy (srcbuf, strchr (line, '{')); + startline = *lines; + } + } + else + strcat (srcbuf, line); + } + script = parse_script (srcbuf, startline); + if (script == NULL) + { + // script parse error? + free (srcbuf); + return 1; + } + + p = (char *) calloc (50, sizeof (char)); + + strncpy (p, w3, 49); + strdb_insert (script_get_userfunc_db (), p, script); + +// label_db=script_get_label_db(); + + // もう使わないのでバッファ解放 + free (srcbuf); + +// printf("function %s => %p\n",p,script); + + return 0; +} + +/*========================================== + * mob行解析 + *------------------------------------------ + */ +int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) +{ + int m, x, y, xs, ys, mob_class, num, delay1, delay2; + int i; + char mapname[24]; + char eventname[24] = ""; + struct mob_data *md; + + xs = ys = 0; + delay1 = delay2 = 0; + // 引数の個数チェック + if (sscanf (w1, "%[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 || + sscanf (w4, "%d,%d,%d,%d,%s", &mob_class, &num, &delay1, &delay2, + eventname) < 2) + { + printf ("bad monster line : %s\n", w3); + return 1; + } + + m = map_mapname2mapid (mapname); + + if (num > 1 && battle_config.mob_count_rate != 100) + { + if ((num = num * battle_config.mob_count_rate / 100) < 1) + num = 1; + } + + for (i = 0; i < num; i++) + { + md = (struct mob_data *) calloc (1, sizeof (struct mob_data)); + + md->bl.prev = NULL; + md->bl.next = NULL; + md->bl.m = m; + md->bl.x = x; + md->bl.y = y; + if (strcmp (w3, "--en--") == 0) + memcpy (md->name, mob_db[mob_class].name, 24); + else if (strcmp (w3, "--ja--") == 0) + memcpy (md->name, mob_db[mob_class].jname, 24); + else + memcpy (md->name, w3, 24); + + md->n = i; + md->base_class = md->mob_class = mob_class; + md->bl.id = npc_get_new_npc_id (); + md->m = m; + md->x0 = x; + md->y0 = y; + md->xs = xs; + md->ys = ys; + md->spawndelay1 = delay1; + md->spawndelay2 = delay2; + + memset (&md->state, 0, sizeof (md->state)); + md->timer = -1; + md->target_id = 0; + md->attacked_id = 0; + + if (mob_db[mob_class].mode & 0x02) + md->lootitem = + (struct item *) calloc (LOOTITEM_SIZE, sizeof (struct item)); + else + md->lootitem = NULL; + + if (strlen (eventname) >= 4) + { + memcpy (md->npc_event, eventname, 24); + } + else + memset (md->npc_event, 0, 24); + + md->bl.type = BL_MOB; + map_addiddb (&md->bl); + mob_spawn (md->bl.id); + + npc_mob++; + } + //printf("warp npc %s %d read done\n",mapname,nd->bl.id); + + return 0; +} + +/*========================================== + * マップフラグ行の解析 + *------------------------------------------ + */ +static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) +{ + int m; + char mapname[24], savemap[16]; + int savex, savey; + char drop_arg1[16], drop_arg2[16]; + int drop_id = 0, drop_type = 0, drop_per = 0; + + // 引数の個数チェック +// if ( sscanf(w1,"%[^,],%d,%d,%d",mapname,&x,&y,&dir) != 4 ) + if (sscanf (w1, "%[^,]", mapname) != 1) + return 1; + + m = map_mapname2mapid (mapname); + if (m < 0) + return 1; + +//マップフラグ + if (strcasecmp (w3, "nosave") == 0) + { + if (strcmp (w4, "SavePoint") == 0) + { + memcpy (map[m].save.map, "SavePoint", 16); + map[m].save.x = -1; + map[m].save.y = -1; + } + else if (sscanf (w4, "%[^,],%d,%d", savemap, &savex, &savey) == 3) + { + memcpy (map[m].save.map, savemap, 16); + map[m].save.x = savex; + map[m].save.y = savey; + } + map[m].flag.nosave = 1; + } + else if (strcasecmp (w3, "nomemo") == 0) + { + map[m].flag.nomemo = 1; + } + else if (strcasecmp (w3, "noteleport") == 0) + { + map[m].flag.noteleport = 1; + } + else if (strcasecmp (w3, "nowarp") == 0) + { + map[m].flag.nowarp = 1; + } + else if (strcasecmp (w3, "nowarpto") == 0) + { + map[m].flag.nowarpto = 1; + } + else if (strcasecmp (w3, "noreturn") == 0) + { + map[m].flag.noreturn = 1; + } + else if (strcasecmp (w3, "monster_noteleport") == 0) + { + map[m].flag.monster_noteleport = 1; + } + else if (strcasecmp (w3, "nobranch") == 0) + { + map[m].flag.nobranch = 1; + } + else if (strcasecmp (w3, "nopenalty") == 0) + { + map[m].flag.nopenalty = 1; + } + else if (strcasecmp (w3, "pvp") == 0) + { + map[m].flag.pvp = 1; + } + else if (strcasecmp (w3, "pvp_noparty") == 0) + { + map[m].flag.pvp_noparty = 1; + } + else if (strcasecmp (w3, "pvp_noguild") == 0) + { + map[m].flag.pvp_noguild = 1; + } + else if (strcasecmp (w3, "pvp_nightmaredrop") == 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 (strcasecmp (w3, "pvp_nocalcrank") == 0) + { + map[m].flag.pvp_nocalcrank = 1; + } + else if (strcasecmp (w3, "gvg") == 0) + { + map[m].flag.gvg = 1; + } + else if (strcasecmp (w3, "gvg_noparty") == 0) + { + map[m].flag.gvg_noparty = 1; + } + else if (strcasecmp (w3, "nozenypenalty") == 0) + { + map[m].flag.nozenypenalty = 1; + } + else if (strcasecmp (w3, "notrade") == 0) + { + map[m].flag.notrade = 1; + } + else if (strcasecmp (w3, "noskill") == 0) + { + map[m].flag.noskill = 1; + } + else if (battle_config.pk_mode && strcasecmp (w3, "nopvp") == 0) + { // nopvp for pk mode [Valaris] + map[m].flag.nopvp = 1; + map[m].flag.pvp = 0; + } + else if (strcasecmp (w3, "noicewall") == 0) + { // noicewall [Valaris] + map[m].flag.noicewall = 1; + } + else if (strcasecmp (w3, "snow") == 0) + { // snow [Valaris] + map[m].flag.snow = 1; + } + else if (strcasecmp (w3, "fog") == 0) + { // fog [Valaris] + map[m].flag.fog = 1; + } + else if (strcasecmp (w3, "sakura") == 0) + { // sakura [Valaris] + map[m].flag.sakura = 1; + } + else if (strcasecmp (w3, "leaves") == 0) + { // leaves [Valaris] + map[m].flag.leaves = 1; + } + else if (strcasecmp (w3, "rain") == 0) + { // rain [Valaris] + map[m].flag.rain = 1; + } + else if (strcasecmp (w3, "no_player_drops") == 0) + { // no player drops [Jaxad0127] + map[m].flag.no_player_drops = 1; + } + else if (strcasecmp (w3, "town") == 0) + { // town/safe zone [remoitnane] + map[m].flag.town = 1; + } + + return 0; +} + +static void ev_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); + if (strstr (key.s, "::") != NULL) + free ((char*)key.s); +} + +struct npc_data *npc_spawn_text (int m, int x, int y, + int npc_class, char *name, char *message) +{ + struct npc_data *retval = + (struct npc_data *) calloc (1, sizeof (struct npc_data)); + retval->bl.id = npc_get_new_npc_id (); + retval->bl.x = x; + retval->bl.y = y; + retval->bl.m = m; + retval->bl.type = BL_NPC; + retval->bl.subtype = MESSAGE; + + strncpy (retval->name, name, 23); + strncpy (retval->exname, name, 23); + retval->name[15] = 0; + retval->exname[15] = 0; + retval->u.message = message ? strdup (message) : NULL; + + retval->npc_class = npc_class; + retval->speed = 200; + + clif_spawnnpc (retval); + map_addblock (&retval->bl); + map_addiddb (&retval->bl); + if (retval->name && retval->name[0]) + strdb_insert (npcname_db, retval->name, retval); + + return retval; +} + +static void npc_free_internal (struct npc_data *nd) +{ + struct chat_data *cd; + + if (nd->chat_id && (cd = (struct chat_data *) map_id2bl (nd->chat_id))) + { + free (cd); + cd = NULL; + } + if (nd->bl.subtype == SCRIPT) + { + if (nd->u.scr.timer_event) + free (nd->u.scr.timer_event); + if (nd->u.scr.src_id == 0) + { + if (nd->u.scr.script) + { + free (nd->u.scr.script); + nd->u.scr.script = NULL; + } + if (nd->u.scr.label_list) + { + free (nd->u.scr.label_list); + nd->u.scr.label_list = NULL; + } + } + } + else if (nd->bl.subtype == MESSAGE && nd->u.message) + { + free (nd->u.message); + } + free (nd); +} + +void npc_propagate_update (struct npc_data *nd) +{ + 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); +} + +void npc_free (struct npc_data *nd) +{ + clif_clearchar (&nd->bl, 0); + npc_propagate_update (nd); + map_deliddb (&nd->bl); + map_delblock (&nd->bl); + npc_free_internal (nd); +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_npc (void) +{ + int i; + struct block_list *bl; + struct npc_data *nd; + struct mob_data *md; + + if (ev_db) + strdb_final (ev_db, ev_db_final); + if (npcname_db) + strdb_final (npcname_db, NULL); + + 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_free_internal (nd); + else if (bl->type == BL_MOB && (md = (struct mob_data *) bl)) + { + if (md->lootitem) + { + free (md->lootitem); + md->lootitem = NULL; + } + free (md); + md = NULL; + } + } + } + + return 0; +} + +void ev_release (db_key_t key, db_val_t val) +{ + free ((char*)key.s); + free (val); +} + +/*========================================== + * npc初期化 + *------------------------------------------ + */ +int do_init_npc (void) +{ + struct npc_src_list *nsl; + FILE *fp; + char line[1024]; + int m, lines; + + ev_db = strdb_init (24); + npcname_db = strdb_init (24); + + ev_db->release = ev_release; + + memset (&ev_tm_b, -1, sizeof (ev_tm_b)); + + for (nsl = npc_src_first; nsl; nsl = nsl->next) + { + if (nsl->prev) + { + free (nsl->prev); + nsl->prev = NULL; + } + fp = fopen_ (nsl->name, "r"); + if (fp == NULL) + { + printf ("file not found : %s\n", nsl->name); + exit (1); + } + lines = 0; + while (fgets (line, 1020, 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 (line[i + 1]) || line[i + 1] == ',')) + || (j && line[j - 1] == ','))) + line[j++] = ' '; + } + else if (line[i] == '\t' || line[i] == '|') + { + if (!(j && (line[j - 1] == '\t' || line[j - 1] == '|'))) + line[j++] = '\t'; + } + else + line[j++] = line[i]; + } + // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認 + 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 && strcasecmp (w1, "function") != 0) + { + sscanf (w1, "%[^,]", mapname); + m = map_mapname2mapid (mapname); + if (strlen (mapname) > 16 || m < 0) + { + // "mapname" is not assigned to this server + continue; + } + } + if (strcasecmp (w2, "warp") == 0 && count > 3) + { + npc_parse_warp (w1, w2, w3, w4); + } + else if (strcasecmp (w2, "shop") == 0 && count > 3) + { + npc_parse_shop (w1, w2, w3, w4); + } + else if (strcasecmp (w2, "script") == 0 && count > 3) + { + if (strcasecmp (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 (strcasecmp (w2, "monster") == 0 && count > 3) + { + npc_parse_mob (w1, w2, w3, w4); + } + else if (strcasecmp (w2, "mapflag") == 0 && count >= 3) + { + npc_parse_mapflag (w1, w2, w3, w4); + } + } + fclose_ (fp); + printf ("\rLoading NPCs [%d]: %-54s", npc_id - START_NPC_NUM, + nsl->name); + fflush (stdout); + } + printf ("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d]\n", + npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob); + + return 0; +} diff --git a/src/map/npc.h b/src/map/npc.h deleted file mode 100644 index 757c7ab..0000000 --- a/src/map/npc.h +++ /dev/null @@ -1,64 +0,0 @@ -// $Id: npc.h,v 1.5 2004/09/25 11:39:17 MouseJstr Exp $ -#ifndef _NPC_H_ -#define _NPC_H_ - -#define START_NPC_NUM 110000000 - -#define WARP_CLASS 45 -#define WARP_DEBUG_CLASS 722 -#define INVISIBLE_CLASS 32767 - -int npc_event_dequeue (struct map_session_data *sd); -void npc_event_timer (timer_id, tick_t, custom_id_t, custom_data_t); -int npc_event (struct map_session_data *sd, const char *npcname, int); -int npc_timer_event (const char *eventname); // Added by RoVeRT -int npc_command (struct map_session_data *sd, 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_warp (char *w1, char *w2, char *w3, char *w4); - -int npc_enable (const char *name, int flag); -struct npc_data *npc_name2id (const char *name); - -int npc_get_new_npc_id (void); - -/** - * Spawns and installs a talk-only NPC - * - * \param message The message to speak. If message is NULL, the NPC will not do anything at all. - */ -struct npc_data *npc_spawn_text (int m, int x, int y, int class_, char *name, char *message); // message is strdup'd within - -/** - * Uninstalls and frees an NPC - */ -void npc_free (struct npc_data *npc); - -void npc_addsrcfile (char *); -void npc_delsrcfile (char *); -int do_final_npc (void); -int do_init_npc (void); -int npc_event_do_oninit (void); -int npc_do_ontimer (int, struct map_session_data *, int); - -struct argrec; -int npc_event_doall_l (const char *name, int rid, int argc, - struct argrec *argv); -int npc_event_do_l (const char *name, int rid, int argc, - struct argrec *argv); -#define npc_event_doall(name) npc_event_doall_l(name, 0, 0, NULL) -#define npc_event_do(name) npc_event_do_l(name, 0, 0, NULL) - -int npc_timerevent_start (struct npc_data *nd); -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_delete (struct npc_data *nd); - -#endif diff --git a/src/map/npc.hpp b/src/map/npc.hpp new file mode 100644 index 0000000..6bb9370 --- /dev/null +++ b/src/map/npc.hpp @@ -0,0 +1,64 @@ +// $Id: npc.h,v 1.5 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef NPC_HPP +#define NPC_HPP + +#define START_NPC_NUM 110000000 + +#define WARP_CLASS 45 +#define WARP_DEBUG_CLASS 722 +#define INVISIBLE_CLASS 32767 + +int npc_event_dequeue (struct map_session_data *sd); +void npc_event_timer (timer_id, tick_t, custom_id_t, custom_data_t); +int npc_event (struct map_session_data *sd, const char *npcname, int); +int npc_timer_event (const char *eventname); // Added by RoVeRT +int npc_command (struct map_session_data *sd, 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_warp (char *w1, char *w2, char *w3, char *w4); + +int npc_enable (const char *name, int flag); +struct npc_data *npc_name2id (const char *name); + +int npc_get_new_npc_id (void); + +/** + * Spawns and installs a talk-only NPC + * + * \param message The message to speak. If message is NULL, the NPC will not do anything at all. + */ +struct npc_data *npc_spawn_text (int m, int x, int y, int class_, char *name, char *message); // message is strdup'd within + +/** + * Uninstalls and frees an NPC + */ +void npc_free (struct npc_data *npc); + +void npc_addsrcfile (char *); +void npc_delsrcfile (char *); +int do_final_npc (void); +int do_init_npc (void); +int npc_event_do_oninit (void); +int npc_do_ontimer (int, struct map_session_data *, int); + +struct argrec; +int npc_event_doall_l (const char *name, int rid, int argc, + struct argrec *argv); +int npc_event_do_l (const char *name, int rid, int argc, + struct argrec *argv); +#define npc_event_doall(name) npc_event_doall_l(name, 0, 0, NULL) +#define npc_event_do(name) npc_event_do_l(name, 0, 0, NULL) + +int npc_timerevent_start (struct npc_data *nd); +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_delete (struct npc_data *nd); + +#endif diff --git a/src/map/party.c b/src/map/party.c deleted file mode 100644 index 6c2e627..0000000 --- a/src/map/party.c +++ /dev/null @@ -1,790 +0,0 @@ -// $Id: party.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "party.h" -#include "../common/db.h" -#include "../common/timer.h" -#include "../common/socket.h" -#include "../common/nullpo.h" -#include "pc.h" -#include "map.h" -#include "battle.h" -#include "intif.h" -#include "clif.h" -#include "skill.h" -#include "tmw.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define PARTY_SEND_XYHP_INVERVAL 1000 // 座標やHP送信の間隔 - -static struct dbt *party_db; - -void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); -/*========================================== - * 終了 - *------------------------------------------ - */ -static void party_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -void do_final_party (void) -{ - if (party_db) - numdb_final (party_db, party_db_final); -} - -// 初期化 -void do_init_party (void) -{ - party_db = numdb_init (); - add_timer_interval (gettick () + PARTY_SEND_XYHP_INVERVAL, - party_send_xyhp_timer, 0, 0, - PARTY_SEND_XYHP_INVERVAL); -} - -// 検索 -struct party *party_search (int party_id) -{ - return (struct party *)numdb_search (party_db, party_id); -} - -void party_searchname_sub (db_key_t key, db_val_t 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 (strcasecmp (p->name, str) == 0) - *dst = p; -} - -// パーティ名検索 -struct party *party_searchname (char *str) -{ - struct party *p = NULL; - numdb_foreach (party_db, party_searchname_sub, str, &p); - return p; -} - -/* Process a party creation request. */ -int party_create (struct map_session_data *sd, char *name) -{ - char pname[24]; - nullpo_retr (0, sd); - - strncpy (pname, name, 24); - pname[23] = '\0'; - tmw_TrimStr (pname); - - /* The party name is empty/invalid. */ - if (!*pname) - clif_party_created (sd, 1); - - /* Make sure the character isn't already in a party. */ - if (sd->status.party_id == 0) - intif_create_party (sd, pname); - else - clif_party_created (sd, 2); - - return 0; -} - -/* Relay the result of a party creation request. */ -int party_created (int account_id, int fail, int party_id, char *name) -{ - struct map_session_data *sd; - sd = map_id2sd (account_id); - - nullpo_retr (0, sd); - - /* The party name is valid and not already taken. */ - if (!fail) - { - struct party *p; - sd->status.party_id = party_id; - - if ((p = (struct party *)numdb_search (party_db, party_id)) != NULL) - { - printf ("party_created(): ID already exists!\n"); - exit (1); - } - - CREATE (p, struct party, 1); - p->party_id = party_id; - memcpy (p->name, name, 24); - numdb_insert (party_db, party_id, p); - - /* The party was created successfully. */ - clif_party_created (sd, 0); - } - - 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; - struct map_session_data *sd; - - nullpo_retr (0, p); - - for (i = 0; i < fd_max; i++) - { - if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) - { - if (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) - { - if (strcmp (p->member[j].name, sd->status.name) == 0) - f = 0; // データがある - else - p->member[j].sd = NULL; // 同垢別キャラだった - } - } - if (f) - { - sd->status.party_id = 0; - if (battle_config.error_log) - printf ("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; - 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) - { - if (sd->status.party_id == party_id) - sd->status.party_id = 0; - } - } - return 0; -} - -// 情報所得 -int party_recv_info (struct party *sp) -{ - struct party *p; - int i; - - nullpo_retr (0, sp); - - if ((p = (struct party *)numdb_search (party_db, sp->party_id)) == NULL) - { - CREATE (p, struct party, 1); - numdb_insert (party_db, sp->party_id, p); - - // 最初のロードなのでユーザーのチェックを行う - party_check_member (sp); - } - memcpy (p, sp, sizeof (struct party)); - - for (i = 0; i < MAX_PARTY; i++) - { // sdの設定 - struct map_session_data *sd = map_id2sd (p->member[i].account_id); - p->member[i].sd = (sd != NULL - && sd->status.party_id == p->party_id) ? sd : NULL; - } - - clif_party_info (p, -1); - - for (i = 0; i < MAX_PARTY; i++) - { // 設定情報の送信 -// struct map_session_data *sd = map_id2sd(p->member[i].account_id); - struct map_session_data *sd = p->member[i].sd; - if (sd != NULL && sd->party_sended == 0) - { - clif_party_option (p, sd, 0x100); - sd->party_sended = 1; - } - } - - return 0; -} - -/* Process party invitation from sd to account_id. */ -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; - int full = 1; /* Indicates whether or not there's room for one more. */ - - nullpo_retr (0, sd); - - if (!tsd || !p || !tsd->fd) - return 0; - - if (!battle_config.invite_request_check) - { - /* Disallow the invitation under these conditions. */ - if (tsd->guild_invite > 0 || tsd->trade_partner || tsd->npc_id - || tsd->npc_shopid || pc_checkskill (tsd, NV_PARTY) < 1) - { - clif_party_inviteack (sd, tsd->status.name, 1); - return 0; - } - } - - /* The target player is already in a party, or has a pending invitation. */ - 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++) - { - /* - * A character from the target account is already in the same party. - * The response isn't strictly accurate, as they're separate - * characters, but we're making do with what was already in place and - * leaving this (mostly) alone for now. - */ - if (p->member[i].account_id == account_id) - { - clif_party_inviteack (sd, tsd->status.name, 1); - return 0; - } - - if (!p->member[i].account_id) - full = 0; - } - - /* There isn't enough room for a new member. */ - if (full) - { - clif_party_inviteack (sd, tsd->status.name, 3); - return 0; - } - - /* Otherwise, relay the invitation to the target player. */ - tsd->party_invite = sd->status.party_id; - tsd->party_invite_account = sd->status.account_id; - - clif_party_invite (sd, tsd); - return 0; -} - -/* Process response to party invitation. */ -int party_reply_invite (struct map_session_data *sd, int account_id, int flag) -{ - nullpo_retr (0, sd); - - /* There is no pending invitation. */ - if (!sd->party_invite || !sd->party_invite_account) - return 0; - - /* - * Only one invitation can be pending, so these have to be the same. Since - * the client continues to send the wrong ID, and it's already managed on - * this side of things, the sent ID is being ignored. - */ - account_id = sd->party_invite_account; - - /* The invitation was accepted. */ - if (flag == 1) - intif_party_addmember (sd->party_invite, sd->status.account_id); - /* The invitation was rejected. */ - else - { - /* This is the player who sent the invitation. */ - struct map_session_data *tsd = NULL; - - sd->party_invite = 0; - sd->party_invite_account = 0; - - if ((tsd = map_id2sd (account_id))) - clif_party_inviteack (tsd, sd->status.name, 1); - } - return 0; -} - -// パーティが追加された -int party_member_added (int party_id, int account_id, int flag) -{ - struct map_session_data *sd = map_id2sd (account_id), *sd2; - struct party *p = party_search (party_id); - - if (sd == NULL) - { - if (flag == 0) - { - if (battle_config.error_log) - printf ("party: member added error %d is not online\n", - account_id); - intif_party_leave (party_id, account_id); // キャラ側に登録できなかったため脱退要求を出す - } - return 0; - } - sd2 = map_id2sd (sd->party_invite_account); - sd->party_invite = 0; - sd->party_invite_account = 0; - - if (p == NULL) - { - printf ("party_member_added: party %d not found.\n", party_id); - intif_party_leave (party_id, account_id); - return 0; - } - - if (flag == 1) - { // 失敗 - if (sd2 != NULL) - clif_party_inviteack (sd2, sd->status.name, 0); - return 0; - } - - // 成功 - sd->party_sended = 0; - sd->status.party_id = party_id; - - if (sd2 != NULL) - clif_party_inviteack (sd2, sd->status.name, 2); - - // いちおう競合確認 - party_check_conflict (sd); - - party_send_xy_clear (p); - - 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) - if (p->member[i].leader == 0) - return 0; - } - - for (i = 0; i < MAX_PARTY; i++) - { // 所属しているか調べる - if (p->member[i].account_id == account_id) - { - intif_party_leave (p->party_id, account_id); - return 0; - } - } - 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) - { - intif_party_leave (p->party_id, sd->status.account_id); - return 0; - } - } - return 0; -} - -// パーティメンバが脱退した -int party_member_leaved (int party_id, int account_id, char *name) -{ - struct map_session_data *sd = map_id2sd (account_id); - struct party *p = party_search (party_id); - if (p != NULL) - { - int i; - for (i = 0; i < MAX_PARTY; i++) - if (p->member[i].account_id == account_id) - { - clif_party_leaved (p, sd, account_id, name, 0x00); - p->member[i].account_id = 0; - p->member[i].sd = NULL; - } - } - if (sd != NULL && sd->status.party_id == party_id) - { - sd->status.party_id = 0; - sd->party_sended = 0; - } - 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->party_sended = 0; - } - } - numdb_erase (party_db, party_id); - return 0; -} - -// パーティの設定変更要求 -int party_changeoption (struct map_session_data *sd, int exp, int item) -{ - struct party *p; - - nullpo_retr (0, sd); - - if (sd->status.party_id == 0 - || (p = party_search (sd->status.party_id)) == NULL) - return 0; - intif_party_changeoption (sd->status.party_id, sd->status.account_id, exp, - item); - 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, char *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 party_member *m = &p->member[i]; - if (m == NULL) - { - printf ("party_recv_movemap nullpo?\n"); - return 0; - } - if (m->account_id == account_id) - { - memcpy (m->map, map, 16); - m->online = online; - m->lv = lv; - break; - } - } - if (i == MAX_PARTY) - { - if (battle_config.error_log) - printf ("party: not found member %d on %d[%s]", account_id, - party_id, p->name); - return 0; - } - - for (i = 0; i < MAX_PARTY; i++) - { // sd再設定 - struct map_session_data *sd = map_id2sd (p->member[i].account_id); - p->member[i].sd = (sd != NULL - && sd->status.party_id == p->party_id) ? sd : NULL; - } - - party_send_xy_clear (p); // 座標再通知要請 - - 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->party_sended != 0) // もうパーティデータは送信済み - 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_info (p, sd->fd); - clif_party_option (p, sd, 0x100); - sd->party_sended = 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); - 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.name); - return 0; -} - -// 位置やHP通知用 -void party_send_xyhp_timer_sub (db_key_t key, db_val_t data, va_list ap) -{ - struct party *p = (struct party *) data; - int i; - - nullpo_retv (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 (p, sd); - sd->party_x = sd->bl.x; - sd->party_y = sd->bl.y; - } - // HP通知 - if (sd->party_hp != sd->status.hp) - { - clif_party_hp (p, sd); - sd->party_hp = sd->status.hp; - } - - } - } -} - -// 位置やHP通知 -void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - numdb_foreach (party_db, party_send_xyhp_timer_sub, tick); -} - -// 位置通知クリア -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; - sd->party_hp = -1; - } - } - return 0; -} - -// HP通知の必要性検査用(map_foreachinmoveareaから呼ばれる) -int party_send_hp_check (struct block_list *bl, va_list ap) -{ - int party_id; - int *flag; - struct map_session_data *sd; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, sd = (struct map_session_data *) bl); - - party_id = va_arg (ap, int); - flag = va_arg (ap, int *); - - if (sd->status.party_id == party_id) - { - *flag = 1; - sd->party_hp = -1; - } - return 0; -} - -// 経験値公平分配 -int party_exp_share (struct party *p, int map, int base_exp, int job_exp) -{ - struct map_session_data *sd; - int i, c; - - nullpo_retr (0, p); - - for (i = c = 0; i < MAX_PARTY; i++) - if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) - c++; - if (c == 0) - return 0; - for (i = 0; i < MAX_PARTY; i++) - if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) - pc_gainexp (sd, base_exp / c + 1, job_exp / c + 1); - return 0; -} - -// 同じマップのパーティメンバー全体に処理をかける -// 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.cpp b/src/map/party.cpp new file mode 100644 index 0000000..1c4088a --- /dev/null +++ b/src/map/party.cpp @@ -0,0 +1,790 @@ +// $Id: party.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "party.hpp" +#include "../common/db.hpp" +#include "../common/timer.hpp" +#include "../common/socket.hpp" +#include "../common/nullpo.hpp" +#include "pc.hpp" +#include "map.hpp" +#include "battle.hpp" +#include "intif.hpp" +#include "clif.hpp" +#include "skill.hpp" +#include "tmw.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define PARTY_SEND_XYHP_INVERVAL 1000 // 座標やHP送信の間隔 + +static struct dbt *party_db; + +void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); +/*========================================== + * 終了 + *------------------------------------------ + */ +static void party_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +void do_final_party (void) +{ + if (party_db) + numdb_final (party_db, party_db_final); +} + +// 初期化 +void do_init_party (void) +{ + party_db = numdb_init (); + add_timer_interval (gettick () + PARTY_SEND_XYHP_INVERVAL, + party_send_xyhp_timer, 0, 0, + PARTY_SEND_XYHP_INVERVAL); +} + +// 検索 +struct party *party_search (int party_id) +{ + return (struct party *)numdb_search (party_db, party_id); +} + +void party_searchname_sub (db_key_t key, db_val_t 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 (strcasecmp (p->name, str) == 0) + *dst = p; +} + +// パーティ名検索 +struct party *party_searchname (char *str) +{ + struct party *p = NULL; + numdb_foreach (party_db, party_searchname_sub, str, &p); + return p; +} + +/* Process a party creation request. */ +int party_create (struct map_session_data *sd, char *name) +{ + char pname[24]; + nullpo_retr (0, sd); + + strncpy (pname, name, 24); + pname[23] = '\0'; + tmw_TrimStr (pname); + + /* The party name is empty/invalid. */ + if (!*pname) + clif_party_created (sd, 1); + + /* Make sure the character isn't already in a party. */ + if (sd->status.party_id == 0) + intif_create_party (sd, pname); + else + clif_party_created (sd, 2); + + return 0; +} + +/* Relay the result of a party creation request. */ +int party_created (int account_id, int fail, int party_id, char *name) +{ + struct map_session_data *sd; + sd = map_id2sd (account_id); + + nullpo_retr (0, sd); + + /* The party name is valid and not already taken. */ + if (!fail) + { + struct party *p; + sd->status.party_id = party_id; + + if ((p = (struct party *)numdb_search (party_db, party_id)) != NULL) + { + printf ("party_created(): ID already exists!\n"); + exit (1); + } + + CREATE (p, struct party, 1); + p->party_id = party_id; + memcpy (p->name, name, 24); + numdb_insert (party_db, party_id, p); + + /* The party was created successfully. */ + clif_party_created (sd, 0); + } + + 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; + struct map_session_data *sd; + + nullpo_retr (0, p); + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) + { + if (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) + { + if (strcmp (p->member[j].name, sd->status.name) == 0) + f = 0; // データがある + else + p->member[j].sd = NULL; // 同垢別キャラだった + } + } + if (f) + { + sd->status.party_id = 0; + if (battle_config.error_log) + printf ("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; + 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) + { + if (sd->status.party_id == party_id) + sd->status.party_id = 0; + } + } + return 0; +} + +// 情報所得 +int party_recv_info (struct party *sp) +{ + struct party *p; + int i; + + nullpo_retr (0, sp); + + if ((p = (struct party *)numdb_search (party_db, sp->party_id)) == NULL) + { + CREATE (p, struct party, 1); + numdb_insert (party_db, sp->party_id, p); + + // 最初のロードなのでユーザーのチェックを行う + party_check_member (sp); + } + memcpy (p, sp, sizeof (struct party)); + + for (i = 0; i < MAX_PARTY; i++) + { // sdの設定 + struct map_session_data *sd = map_id2sd (p->member[i].account_id); + p->member[i].sd = (sd != NULL + && sd->status.party_id == p->party_id) ? sd : NULL; + } + + clif_party_info (p, -1); + + for (i = 0; i < MAX_PARTY; i++) + { // 設定情報の送信 +// struct map_session_data *sd = map_id2sd(p->member[i].account_id); + struct map_session_data *sd = p->member[i].sd; + if (sd != NULL && sd->party_sended == 0) + { + clif_party_option (p, sd, 0x100); + sd->party_sended = 1; + } + } + + return 0; +} + +/* Process party invitation from sd to account_id. */ +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; + int full = 1; /* Indicates whether or not there's room for one more. */ + + nullpo_retr (0, sd); + + if (!tsd || !p || !tsd->fd) + return 0; + + if (!battle_config.invite_request_check) + { + /* Disallow the invitation under these conditions. */ + if (tsd->guild_invite > 0 || tsd->trade_partner || tsd->npc_id + || tsd->npc_shopid || pc_checkskill (tsd, NV_PARTY) < 1) + { + clif_party_inviteack (sd, tsd->status.name, 1); + return 0; + } + } + + /* The target player is already in a party, or has a pending invitation. */ + 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++) + { + /* + * A character from the target account is already in the same party. + * The response isn't strictly accurate, as they're separate + * characters, but we're making do with what was already in place and + * leaving this (mostly) alone for now. + */ + if (p->member[i].account_id == account_id) + { + clif_party_inviteack (sd, tsd->status.name, 1); + return 0; + } + + if (!p->member[i].account_id) + full = 0; + } + + /* There isn't enough room for a new member. */ + if (full) + { + clif_party_inviteack (sd, tsd->status.name, 3); + return 0; + } + + /* Otherwise, relay the invitation to the target player. */ + tsd->party_invite = sd->status.party_id; + tsd->party_invite_account = sd->status.account_id; + + clif_party_invite (sd, tsd); + return 0; +} + +/* Process response to party invitation. */ +int party_reply_invite (struct map_session_data *sd, int account_id, int flag) +{ + nullpo_retr (0, sd); + + /* There is no pending invitation. */ + if (!sd->party_invite || !sd->party_invite_account) + return 0; + + /* + * Only one invitation can be pending, so these have to be the same. Since + * the client continues to send the wrong ID, and it's already managed on + * this side of things, the sent ID is being ignored. + */ + account_id = sd->party_invite_account; + + /* The invitation was accepted. */ + if (flag == 1) + intif_party_addmember (sd->party_invite, sd->status.account_id); + /* The invitation was rejected. */ + else + { + /* This is the player who sent the invitation. */ + struct map_session_data *tsd = NULL; + + sd->party_invite = 0; + sd->party_invite_account = 0; + + if ((tsd = map_id2sd (account_id))) + clif_party_inviteack (tsd, sd->status.name, 1); + } + return 0; +} + +// パーティが追加された +int party_member_added (int party_id, int account_id, int flag) +{ + struct map_session_data *sd = map_id2sd (account_id), *sd2; + struct party *p = party_search (party_id); + + if (sd == NULL) + { + if (flag == 0) + { + if (battle_config.error_log) + printf ("party: member added error %d is not online\n", + account_id); + intif_party_leave (party_id, account_id); // キャラ側に登録できなかったため脱退要求を出す + } + return 0; + } + sd2 = map_id2sd (sd->party_invite_account); + sd->party_invite = 0; + sd->party_invite_account = 0; + + if (p == NULL) + { + printf ("party_member_added: party %d not found.\n", party_id); + intif_party_leave (party_id, account_id); + return 0; + } + + if (flag == 1) + { // 失敗 + if (sd2 != NULL) + clif_party_inviteack (sd2, sd->status.name, 0); + return 0; + } + + // 成功 + sd->party_sended = 0; + sd->status.party_id = party_id; + + if (sd2 != NULL) + clif_party_inviteack (sd2, sd->status.name, 2); + + // いちおう競合確認 + party_check_conflict (sd); + + party_send_xy_clear (p); + + 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) + if (p->member[i].leader == 0) + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) + { // 所属しているか調べる + if (p->member[i].account_id == account_id) + { + intif_party_leave (p->party_id, account_id); + return 0; + } + } + 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) + { + intif_party_leave (p->party_id, sd->status.account_id); + return 0; + } + } + return 0; +} + +// パーティメンバが脱退した +int party_member_leaved (int party_id, int account_id, char *name) +{ + struct map_session_data *sd = map_id2sd (account_id); + struct party *p = party_search (party_id); + if (p != NULL) + { + int i; + for (i = 0; i < MAX_PARTY; i++) + if (p->member[i].account_id == account_id) + { + clif_party_leaved (p, sd, account_id, name, 0x00); + p->member[i].account_id = 0; + p->member[i].sd = NULL; + } + } + if (sd != NULL && sd->status.party_id == party_id) + { + sd->status.party_id = 0; + sd->party_sended = 0; + } + 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->party_sended = 0; + } + } + numdb_erase (party_db, party_id); + return 0; +} + +// パーティの設定変更要求 +int party_changeoption (struct map_session_data *sd, int exp, int item) +{ + struct party *p; + + nullpo_retr (0, sd); + + if (sd->status.party_id == 0 + || (p = party_search (sd->status.party_id)) == NULL) + return 0; + intif_party_changeoption (sd->status.party_id, sd->status.account_id, exp, + item); + 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, char *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 party_member *m = &p->member[i]; + if (m == NULL) + { + printf ("party_recv_movemap nullpo?\n"); + return 0; + } + if (m->account_id == account_id) + { + memcpy (m->map, map, 16); + m->online = online; + m->lv = lv; + break; + } + } + if (i == MAX_PARTY) + { + if (battle_config.error_log) + printf ("party: not found member %d on %d[%s]", account_id, + party_id, p->name); + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) + { // sd再設定 + struct map_session_data *sd = map_id2sd (p->member[i].account_id); + p->member[i].sd = (sd != NULL + && sd->status.party_id == p->party_id) ? sd : NULL; + } + + party_send_xy_clear (p); // 座標再通知要請 + + 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->party_sended != 0) // もうパーティデータは送信済み + 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_info (p, sd->fd); + clif_party_option (p, sd, 0x100); + sd->party_sended = 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); + 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.name); + return 0; +} + +// 位置やHP通知用 +void party_send_xyhp_timer_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct party *p = (struct party *) data; + int i; + + nullpo_retv (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 (p, sd); + sd->party_x = sd->bl.x; + sd->party_y = sd->bl.y; + } + // HP通知 + if (sd->party_hp != sd->status.hp) + { + clif_party_hp (p, sd); + sd->party_hp = sd->status.hp; + } + + } + } +} + +// 位置やHP通知 +void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + numdb_foreach (party_db, party_send_xyhp_timer_sub, tick); +} + +// 位置通知クリア +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; + sd->party_hp = -1; + } + } + return 0; +} + +// HP通知の必要性検査用(map_foreachinmoveareaから呼ばれる) +int party_send_hp_check (struct block_list *bl, va_list ap) +{ + int party_id; + int *flag; + struct map_session_data *sd; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, sd = (struct map_session_data *) bl); + + party_id = va_arg (ap, int); + flag = va_arg (ap, int *); + + if (sd->status.party_id == party_id) + { + *flag = 1; + sd->party_hp = -1; + } + return 0; +} + +// 経験値公平分配 +int party_exp_share (struct party *p, int map, int base_exp, int job_exp) +{ + struct map_session_data *sd; + int i, c; + + nullpo_retr (0, p); + + for (i = c = 0; i < MAX_PARTY; i++) + if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) + c++; + if (c == 0) + return 0; + for (i = 0; i < MAX_PARTY; i++) + if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) + pc_gainexp (sd, base_exp / c + 1, job_exp / c + 1); + return 0; +} + +// 同じマップのパーティメンバー全体に処理をかける +// 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 deleted file mode 100644 index 2e8bf78..0000000 --- a/src/map/party.h +++ /dev/null @@ -1,52 +0,0 @@ -// $Id: party.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ -#ifndef _PARTY_H_ -#define _PARTY_H_ - -#include <stdarg.h> - -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 party_created (int account_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 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, char *name); -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, char *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_send_xy_clear (struct party *p); -int party_send_hp_check (struct block_list *bl, va_list ap); - -int party_exp_share (struct party *p, int map, int base_exp, int job_exp); - -void party_foreachsamemap (int (*func) (struct block_list *, va_list), - struct map_session_data *sd, int type, ...); - -#endif diff --git a/src/map/party.hpp b/src/map/party.hpp new file mode 100644 index 0000000..e40609a --- /dev/null +++ b/src/map/party.hpp @@ -0,0 +1,52 @@ +// $Id: party.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef PARTY_HPP +#define PARTY_HPP + +#include <stdarg.h> + +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 party_created (int account_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 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, char *name); +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, char *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_send_xy_clear (struct party *p); +int party_send_hp_check (struct block_list *bl, va_list ap); + +int party_exp_share (struct party *p, int map, int base_exp, int job_exp); + +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 deleted file mode 100644 index 7a864ed..0000000 --- a/src/map/path.c +++ /dev/null @@ -1,445 +0,0 @@ -// $Id: path.c,v 1.1.1.1 2004/09/10 17:27:00 MagicalTux Exp $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "map.h" -#include "battle.h" -#include "../common/nullpo.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -//#define PATH_STANDALONETEST - -#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) - { - printf ("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]) - { - fprintf (stderr, "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) -{ - int c; - - nullpo_retr (0, m); - - c = read_gatp (m, x, y); - - if (c == 1) - return 0; - if (!(flag & 0x10000) && c == 5) - return 0; - return 1; -} - -/*========================================== - * (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 (!can_place (m, x0, y0, flag)) - return 0; - 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) - printf ("path_blownpos: count too many %d !\n", count); - count = 15; - } - if (dx > 1 || dx < -1 || dy > 1 || dy < -1) - { - if (battle_config.error_log) - printf ("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; -} - -/*========================================== - * 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 - || (i = read_gatp (md, x1, y1)) == 1 || i == 5) - 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 - { // 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) - { - 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; -} - -#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/path.cpp b/src/map/path.cpp new file mode 100644 index 0000000..b68dae6 --- /dev/null +++ b/src/map/path.cpp @@ -0,0 +1,445 @@ +// $Id: path.c,v 1.1.1.1 2004/09/10 17:27:00 MagicalTux Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "map.hpp" +#include "battle.hpp" +#include "../common/nullpo.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +//#define PATH_STANDALONETEST + +#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) + { + printf ("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]) + { + fprintf (stderr, "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) +{ + int c; + + nullpo_retr (0, m); + + c = read_gatp (m, x, y); + + if (c == 1) + return 0; + if (!(flag & 0x10000) && c == 5) + return 0; + return 1; +} + +/*========================================== + * (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 (!can_place (m, x0, y0, flag)) + return 0; + 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) + printf ("path_blownpos: count too many %d !\n", count); + count = 15; + } + if (dx > 1 || dx < -1 || dy > 1 || dy < -1) + { + if (battle_config.error_log) + printf ("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; +} + +/*========================================== + * 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 + || (i = read_gatp (md, x1, y1)) == 1 || i == 5) + 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 + { // 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) + { + 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; +} + +#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 deleted file mode 100644 index 8096679..0000000 --- a/src/map/pc.c +++ /dev/null @@ -1,9032 +0,0 @@ -// $Id: pc.c 101 2004-09-25 17:57:22Z Valaris $ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <time.h> - -#include "../common/socket.h" // [Valaris] -#include "../common/timer.h" -#include "../common/db.h" - -#include "../common/nullpo.h" -#include "../common/mt_rand.h" - -#include "atcommand.h" -#include "battle.h" -#include "chat.h" -#include "chrif.h" -#include "clif.h" -#include "guild.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "storage.h" -#include "trade.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define PVP_CALCRANK_INTERVAL 1000 // PVP順位計算の間隔 - -//define it here, since the ifdef only occurs in this file -#define USE_ASTRAL_SOUL_SKILL - -#define STATE_BLIND 0x10 - -#ifdef USE_ASTRAL_SOUL_SKILL -#define MAGIC_SKILL_THRESHOLD 200 // [fate] At this threshold, the Astral Soul skill kicks in -#endif - -#define MAP_LOG_STATS(sd, suffix) \ - MAP_LOG_PC(sd, "STAT %d %d %d %d %d %d " suffix, \ - sd->status.str, sd->status.agi, sd->status.vit, sd->status.int_, sd->status.dex, sd->status.luk) - -#define MAP_LOG_XP(sd, suffix) \ - MAP_LOG_PC(sd, "XP %d %d JOB %d %d %d ZENY %d + %d " suffix, \ - sd->status.base_level, sd->status.base_exp, sd->status.job_level, sd->status.job_exp, sd->status.skill_point, sd->status.zeny, pc_readaccountreg(sd, "BankAccount")) - -#define MAP_LOG_MAGIC(sd, suffix) \ - MAP_LOG_PC(sd, "MAGIC %d %d %d %d %d %d EXP %d %d " suffix, \ - sd->status.skill[TMW_MAGIC].lv, \ - sd->status.skill[TMW_MAGIC_LIFE].lv, \ - sd->status.skill[TMW_MAGIC_WAR].lv, \ - sd->status.skill[TMW_MAGIC_TRANSMUTE].lv, \ - sd->status.skill[TMW_MAGIC_NATURE].lv, \ - sd->status.skill[TMW_MAGIC_ETHER].lv, \ - pc_readglobalreg(sd, "MAGIC_EXPERIENCE") & 0xffff, \ - (pc_readglobalreg(sd, "MAGIC_EXPERIENCE") >> 24) & 0xff) - -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]; -static char job_bonus[3][MAX_PC_CLASS][MAX_LEVEL]; -static int exp_table[14][MAX_LEVEL]; -static char statp[255][7]; -static struct -{ - int id; - int max; - struct - { - short id, lv; - } need[6]; -} skill_tree[3][MAX_PC_CLASS][100]; - -static int atkmods[3][20]; // 武器ATKサイズ修正(size_fix.txt) -static int refinebonus[5][3]; // 精錬ボーナステーブル(refine_db.txt) -static int percentrefinery[5][10]; // 精錬成功率(refine_db.txt) - -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 unsigned int equip_pos[11] = - { 0x0080, 0x0008, 0x0040, 0x0004, 0x0001, 0x0200, 0x0100, 0x0010, 0x0020, - 0x0002, 0x8000 -}; - -//static struct dbt *gm_account_db; -static struct gm_account *gm_account = NULL; -static int GM_num = 0; - -int pc_isGM (struct map_session_data *sd) -{ -// struct gm_account *p; - int i; - - nullpo_retr (0, sd); - -/* p = numdb_search(gm_account_db, sd->status.account_id); - if (p == NULL) - return 0; - return p->level;*/ - - 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++; - RECREATE (gm_account, struct gm_account, GM_num); - gm_account[GM_num - 1].account_id = account_id; - gm_account[GM_num - 1].level = level; - return 0; -} - -int pc_getrefinebonus (int lv, int type) -{ - if (lv >= 0 && lv < 5 && type >= 0 && type < 3) - return refinebonus[lv][type]; - return 0; -} - -static int distance (int x0, int y0, int x1, int y1) -{ - int dx, dy; - - dx = abs (x0 - x1); - dy = abs (y0 - y1); - return dx > dy ? dx : dy; -} - -static void pc_invincible_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd; - - if ((sd = (struct map_session_data *) map_id2sd (id)) == NULL - || sd->bl.type != BL_PC) - return; - - if (sd->invincible_timer != tid) - { - if (battle_config.error_log) - printf ("invincible_timer %d != %d\n", sd->invincible_timer, tid); - return; - } - sd->invincible_timer = -1; -} - -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; - } - return 0; -} - -static void pc_spiritball_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd; - int i; - - if ((sd = (struct map_session_data *) map_id2sd (id)) == NULL - || sd->bl.type != BL_PC) - return; - - if (sd->spirit_timer[0] != tid) - { - if (battle_config.error_log) - printf ("spirit_timer %d != %d\n", sd->spirit_timer[0], tid); - return; - } - sd->spirit_timer[0] = -1; - for (i = 1; i < sd->spiritball; i++) - { - sd->spirit_timer[i - 1] = sd->spirit_timer[i]; - sd->spirit_timer[i] = -1; - } - sd->spiritball--; - if (sd->spiritball < 0) - sd->spiritball = 0; - clif_spiritball (sd); -} - -int pc_addspiritball (struct map_session_data *sd, int interval, int max) -{ - int i; - - 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); - sd->spirit_timer[0] = -1; - } - for (i = 1; i < max; i++) - { - sd->spirit_timer[i - 1] = sd->spirit_timer[i]; - sd->spirit_timer[i] = -1; - } - } - 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; -} - -int pc_setrestartvalue (struct map_session_data *sd, int type) -{ - //転生や養子の場合の元の職業を算出する - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - s_class = pc_calc_base_job (sd->status.pc_class); - - //----------------------- - // 死亡した - if (sd->special_state.restart_full_recover) - { // オシリスカード - sd->status.hp = sd->status.max_hp; - sd->status.sp = sd->status.max_sp; - } - else - { - if (s_class.job == 0 && 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->status.pc_class != 0 && 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); - } - sd->heal_xp = 0; // [Fate] Set gainable xp for healing this player to 0 - - return 0; -} - -/*========================================== - * 自分をロックしているMOBの数を数える(foreachclient) - *------------------------------------------ - */ -static int pc_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",((struct mob_data *)bl)->target_lv,target_lv); - } - return 0; -} - -int pc_counttargeted (struct map_session_data *sd, struct block_list *src, - int target_lv) -{ - int c = 0; - map_foreachinarea (pc_counttargeted_sub, sd->bl.m, - sd->bl.x - AREA_SIZE, sd->bl.y - AREA_SIZE, - sd->bl.x + AREA_SIZE, sd->bl.y + AREA_SIZE, 0, - sd->bl.id, &c, src, target_lv); - return c; -} - -/*========================================== - * ローカルプロトタイプ宣言 (必要な物のみ) - *------------------------------------------ - */ -static int pc_walktoxy_sub (struct map_session_data *); - -/*========================================== - * saveに必要なステータス修正を行なう - *------------------------------------------ - */ -int pc_makesavestatus (struct map_session_data *sd) -{ - nullpo_retr (0, sd); - - // 服の色は色々弊害が多いので保存対象にはしない - if (!battle_config.save_clothcolor) - sd->status.clothes_color = 0; - - // 死亡状態だったのでhpを1、位置をセーブ場所に変更 - if (pc_isdead (sd)) - { - pc_setrestartvalue (sd, 0); - memcpy (&sd->status.last_point, &sd->status.save_point, - sizeof (sd->status.last_point)); - } - else - { - memcpy (sd->status.last_point.map, sd->mapname, 24); - 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 (strcmp (m->save.map, "SavePoint") == 0) - memcpy (&sd->status.last_point, &sd->status.save_point, - sizeof (sd->status.last_point)); - else - memcpy (&sd->status.last_point, &m->save, - sizeof (sd->status.last_point)); - } - - //マナーポイントがプラスだった場合0に - if (battle_config.muting_players && sd->status.manner > 0) - sd->status.manner = 0; - return 0; -} - -/*========================================== - * 接続時の初期化 - *------------------------------------------ - */ -int pc_setnewpc (struct map_session_data *sd, int account_id, int char_id, - int login_id1, 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; - //転生や養子の場合の元の職業を算出する - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - if (!sd->inventory_data[n]) - return 0; - - s_class = pc_calc_base_job (sd->status.pc_class); - - 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) && (ep == 2 - && - (pc_checkskill (sd, AS_LEFT) - > 0 || s_class.job == 12))) - { - 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; - struct status_change *sc_data; - //転生や養子の場合の元の職業を算出する - - nullpo_retr (0, sd); - - item = sd->inventory_data[n]; - sc_data = battle_get_sc_data (&sd->bl); - //s_class = pc_calc_base_job(sd->status.class); - - if (battle_config.gm_allequip > 0 - && pc_isGM (sd) >= battle_config.gm_allequip) - return 1; - - if (item == NULL) - return 0; - if (item->sex != 2 && sd->status.sex != item->sex) - return 0; - if (item->elv > 0 && sd->status.base_level < item->elv) - return 0; - - if (map[sd->bl.m].flag.pvp - && (item->flag.no_equip == 1 || item->flag.no_equip == 3)) - return 0; - if (map[sd->bl.m].flag.gvg - && (item->flag.no_equip == 2 || item->flag.no_equip == 3)) - return 0; - if (item->equip & 0x0002 && sc_data - && sc_data[SC_STRIPWEAPON].timer != -1) - return 0; - if (item->equip & 0x0020 && sc_data - && sc_data[SC_STRIPSHIELD].timer != -1) - return 0; - if (item->equip & 0x0010 && sc_data && sc_data[SC_STRIPARMOR].timer != -1) - return 0; - if (item->equip & 0x0100 && sc_data && sc_data[SC_STRIPHELM].timer != -1) - return 0; - return 1; -} - -/*========================================== - * Weapon Breaking [Valaris] - *------------------------------------------ - */ -int pc_breakweapon (struct map_session_data *sd) -{ - struct item_data *item; - char output[255]; - int i; - - if (sd == NULL) - return -1; - if (sd->unbreakable >= MRAND (100)) - return 0; - if (sd->sc_data && sd->sc_data[SC_CP_WEAPON].timer != -1) - return 0; - - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0002 - && !sd->status.inventory[i].broken) - { - item = sd->inventory_data[i]; - sd->status.inventory[i].broken = 1; - //pc_unequipitem(sd,i,0); - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0002 - && sd->status.inventory[i].broken == 1) - { - sprintf (output, "%s has broken.", item->jname); - clif_emotion (&sd->bl, 23); - clif_displaymessage (sd->fd, output); - clif_equiplist (sd); - skill_status_change_start (&sd->bl, SC_BROKNWEAPON, 0, 0, 0, - 0, 0, 0); - } - } - if (sd->status.inventory[i].broken == 1) - return 0; - } - - return 0; -} - -/*========================================== - * Armor Breaking [Valaris] - *------------------------------------------ - */ -int pc_breakarmor (struct map_session_data *sd) -{ - struct item_data *item; - char output[255]; - int i; - - if (sd == NULL) - return -1; - if (sd->unbreakable >= MRAND (100)) - return 0; - if (sd->sc_data && sd->sc_data[SC_CP_ARMOR].timer != -1) - return 0; - - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0010 - && !sd->status.inventory[i].broken) - { - item = sd->inventory_data[i]; - sd->status.inventory[i].broken = 1; - //pc_unequipitem(sd,i,0); - if (sd->status.inventory[i].equip - && sd->status.inventory[i].equip & 0x0010 - && sd->status.inventory[i].broken == 1) - { - sprintf (output, "%s has broken.", item->jname); - clif_emotion (&sd->bl, 23); - clif_displaymessage (sd->fd, output); - clif_equiplist (sd); - skill_status_change_start (&sd->bl, SC_BROKNARMOR, 0, 0, 0, 0, - 0, 0); - } - } - if (sd->status.inventory[i].broken == 1) - return 0; - } - return 0; -} - -/*========================================== - * session idに問題無し - * char鯖から送られてきたステータスを設定 - *------------------------------------------ - */ -int pc_authok (int id, int login_id2, time_t connect_until_time, - short tmw_version, struct mmo_charstatus *st) -{ - struct map_session_data *sd = NULL; - - struct party *p; - struct guild *g; - int i; - unsigned long tick = gettick (); - struct sockaddr_in sai; - socklen_t sa_len = sizeof(struct sockaddr); - - sd = map_id2sd (id); - if (sd == NULL) - return 1; - - sd->login_id2 = login_id2; - sd->tmw_version = tmw_version; - - memcpy (&sd->status, st, sizeof (*st)); - - if (sd->status.sex != sd->sex) - { - clif_authfail_fd (sd->fd, 0); - return 1; - } - - MAP_LOG_STATS (sd, "LOGIN"); - MAP_LOG_XP (sd, "LOGIN"); - MAP_LOG_MAGIC (sd, "LOGIN"); - - memset (&sd->state, 0, sizeof (sd->state)); - // 基本的な初期化 - sd->state.connect_new = 1; - sd->bl.prev = sd->bl.next = NULL; - - sd->weapontype1 = sd->weapontype2 = 0; - sd->view_class = sd->status.pc_class; - sd->speed = DEFAULT_WALK_SPEED; - sd->state.dead_sit = 0; - sd->dir = 0; - sd->head_dir = 0; - sd->state.auth = 1; - sd->walktimer = -1; - sd->attacktimer = -1; - sd->followtimer = -1; // [MouseJstr] - sd->skilltimer = -1; - sd->skillitem = -1; - sd->skillitemlv = -1; - sd->invincible_timer = -1; - sd->sg_count = 0; - - sd->deal_locked = 0; - sd->trade_partner = 0; - - sd->inchealhptick = 0; - sd->inchealsptick = 0; - sd->hp_sub = 0; - sd->sp_sub = 0; - sd->quick_regeneration_hp.amount = 0; - sd->quick_regeneration_sp.amount = 0; - sd->heal_xp = 0; - sd->inchealspirithptick = 0; - sd->inchealspiritsptick = 0; - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->attackabletime = tick; - /* We don't want players bypassing spell restrictions. [remoitnane] */ - // Removed because it was buggy with the ~50 day wraparound, - // and there's already a limit on how fast you can log in and log out. - // -o11c - sd->cast_tick = tick; // + pc_readglobalreg (sd, "MAGIC_CAST_TICK"); - - sd->doridori_counter = 0; - - sd->spiritball = 0; - 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; - - memset (&sd->dev, 0, sizeof (struct square)); - for (i = 0; i < 5; i++) - { - sd->dev.val1[i] = 0; - sd->dev.val2[i] = 0; - } - - // アカウント変数の送信要求 - intif_request_accountreg (sd); - - // アイテムチェック - pc_setinventorydata (sd); - pc_checkitem (sd); - - // ステータス異常の初期化 - for (i = 0; i < MAX_STATUSCHANGE; i++) - { - sd->sc_data[i].timer = -1; - sd->sc_data[i].val1 = sd->sc_data[i].val2 = sd->sc_data[i].val3 = - sd->sc_data[i].val4 = 0; - } - 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_HIDE); - else - sd->status.option &= OPTION_MASK; - - // スキルユニット関係の初期化 - memset (sd->skillunit, 0, sizeof (sd->skillunit)); - memset (sd->skillunittick, 0, sizeof (sd->skillunittick)); - - // init ignore list - memset (sd->ignore, 0, sizeof (sd->ignore)); - - // パーティー関係の初期化 - sd->party_sended = 0; - sd->party_invite = 0; - sd->party_x = -1; - sd->party_y = -1; - sd->party_hp = -1; - - // ギルド関係の初期化 - sd->guild_sended = 0; - sd->guild_invite = 0; - sd->guild_alliance = 0; - - // イベント関係の初期化 - memset (sd->eventqueue, 0, sizeof (sd->eventqueue)); - for (i = 0; i < MAX_EVENTTIMER; i++) - sd->eventtimer[i] = -1; - - // 位置の設定 - pc_setpos (sd, sd->status.last_point.map, sd->status.last_point.x, - sd->status.last_point.y, 0); - - // パーティ、ギルドデータの要求 - 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 - && (g = guild_search (sd->status.guild_id)) == NULL) - guild_request_info (sd->status.guild_id); - - // pvpの設定 - sd->pvp_rank = 0; - sd->pvp_point = 0; - sd->pvp_timer = -1; - - // 通知 - - clif_authok (sd); - map_addnickdb (sd); - if (map_charid2nick (sd->status.char_id) == NULL) - map_addchariddb (sd->status.char_id, sd->status.name); - - //スパノビ用死にカウンターのスクリプト変数からの読み出しとsdへのセット - sd->die_counter = pc_readglobalreg (sd, "PC_DIE_COUNTER"); - - if (night_flag == 1) - { - 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); - sd->opt2 |= STATE_BLIND; - } - - // ステータス初期計算など - pc_calcstatus (sd, 1); - - if (pc_isGM (sd)) - { - printf - ("Connection accepted: character '%s' (account: %d; GM level %d).\n", - sd->status.name, sd->status.account_id, pc_isGM (sd)); - clif_updatestatus (sd, SP_GM); - } - else - printf ("Connection accepted: Character '%s' (account: %d).\n", - sd->status.name, sd->status.account_id); - - // Message of the Dayの送信 - { - char buf[256]; - FILE *fp; - if ((fp = fopen_ (motd_txt, "r")) != NULL) - { - while (fgets (buf, sizeof (buf) - 1, fp) != NULL) - { - int i; - for (i = 0; buf[i]; i++) - { - if (buf[i] == '\r' || buf[i] == '\n') - { - buf[i] = 0; - break; - } - } - clif_displaymessage (sd->fd, buf); - } - fclose_ (fp); - } - } - - sd->auto_ban_info.in_progress = 0; - - // Initialize antispam vars - sd->chat_reset_due = sd->chat_lines_in = sd->chat_total_repeats = - sd->chat_repeat_reset_due = 0; - sd->chat_lastmsg[0] = '\0'; - - memset(sd->flood_rates, 0, sizeof(sd->flood_rates)); - sd->packet_flood_reset_due = sd->packet_flood_in = 0; - - // Obtain IP address (if they are still connected) - if (!getpeername(sd->fd, (struct sockaddr *)&sai, &sa_len)) - sd->ip = sai.sin_addr.s_addr; - - // 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), gmtime (&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); - } - pc_calcstatus (sd, 1); - - return 0; -} - -/*========================================== - * session idに問題ありなので後始末 - *------------------------------------------ - */ -int pc_authfail (int id) -{ - struct map_session_data *sd; - - sd = map_id2sd (id); - if (sd == NULL) - return 1; - - clif_authfail_fd (sd->fd, 0); - - return 0; -} - -static int pc_calc_skillpoint (struct map_session_data *sd) -{ - int i, skill_points = 0; - - nullpo_retr (0, sd); - - for (i = 0; i < skill_pool_skills_size; i++) { - int lv = sd->status.skill[skill_pool_skills[i]].lv; - if (lv) - skill_points += ((lv * (lv - 1)) >> 1) - 1; - } - - return skill_points; -} - -/*========================================== - * 覚えられるスキルの計算 - *------------------------------------------ - */ -int pc_calc_skilltree (struct map_session_data *sd) -{ - int i, id = 0, flag; - int c = 0, s = 0; - //転生や養子の場合の元の職業を算出する - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - s_class = pc_calc_base_job (sd->status.pc_class); - c = s_class.job; - s = (s_class.upper == 1) ? 1 : 0; //ソ転生以外は通常のスキル? - - if ((battle_config.skillup_limit) - && ((c >= 0 && c < 23) || (c >= 4001 && c < 4023) - || (c >= 4023 && c < 4045))) - { - int skill_point = pc_calc_skillpoint (sd); - if (skill_point < 9) - c = 0; - else if ((sd->status.skill_point >= sd->status.job_level - && skill_point < 58) && ((c > 6 && c < 23) || (c > 4007 - && c < 4023) - || (c > 4029 && c < 4045))) - { - switch (c) - { - case 7: - case 14: - c = 1; - break; - case 8: - case 15: - c = 4; - break; - case 9: - case 16: - c = 2; - break; - case 10: - case 18: - c = 5; - break; - case 11: - case 19: - case 20: - c = 3; - break; - case 12: - case 17: - c = 6; - break; - case 4008: - case 4015: - c = 4002; - break; - case 4009: - case 4016: - c = 4005; - break; - case 4010: - case 4017: - c = 4003; - break; - case 4011: - case 4019: - c = 4006; - break; - case 4012: - case 4020: - case 4021: - c = 4004; - break; - case 4013: - case 4018: - c = 4007; - break; - case 4030: - case 4037: - c = 4024; - break; - case 4031: - case 4038: - c = 4027; - break; - case 4032: - case 4039: - c = 4025; - break; - case 4033: - case 4040: - c = 4028; - break; - case 4034: - case 4041: - case 4042: - c = 4026; - break; - case 4035: - case 4043: - c = 4029; - break; - - } - } - } - - /*Comment this out for now, as we manage skills differently - * for(i=0;i<MAX_SKILL;i++) - * if (i < TMW_MAGIC || i > TMW_MAGIC_END){ // [Fate] This hack gets TMW magic working and persisted without bothering about the skill tree. - * if (sd->status.skill[i].flag != 13) 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 = 1; i < 158; i++) - sd->status.skill[i].id = i; - for (i = 210; i < 291; i++) - sd->status.skill[i].id = i; - for (i = 304; i < 337; i++) - sd->status.skill[i].id = i; - if (battle_config.enable_upper_class) - { //confで無効でなければ読み込む - for (i = 355; i < MAX_SKILL; i++) - sd->status.skill[i].id = i; - } - - } - else - { - // 通常の計算 - do - { - flag = 0; - for (i = 0; (id = skill_tree[s][c][i].id) > 0; i++) - { - int j, f = 1; - if (!battle_config.skillfree) - { - for (j = 0; j < 5; j++) - { - if (skill_tree[s][c][i].need[j].id && - pc_checkskill (sd, - skill_tree[s][c][i].need[j].id) < - skill_tree[s][c][i].need[j].lv) - f = 0; - } - } - if (f && sd->status.skill[id].id == 0) - { - sd->status.skill[id].id = id; - flag = 1; - } - } - } - while (flag); - } -// if(battle_config.etc_log) -// printf("calc skill_tree\n"); - return 0; -} - -/*========================================== - * 重量アイコンの確認 - *------------------------------------------ - */ -int pc_checkweighticon (struct map_session_data *sd) -{ - int flag = 0; - - nullpo_retr (0, sd); - - if (sd->weight * 2 >= sd->max_weight - && sd->sc_data[SC_FLYING_BACKPACK].timer == -1) - flag = 1; - if (sd->weight * 10 >= sd->max_weight * 9) - flag = 2; - - if (flag == 1) - { - if (sd->sc_data[SC_WEIGHT50].timer == -1) - skill_status_change_start (&sd->bl, SC_WEIGHT50, 0, 0, 0, 0, 0, - 0); - } - else - { - skill_status_change_end (&sd->bl, SC_WEIGHT50, -1); - } - if (flag == 2) - { - if (sd->sc_data[SC_WEIGHT90].timer == -1) - skill_status_change_start (&sd->bl, SC_WEIGHT90, 0, 0, 0, 0, 0, - 0); - } - else - { - skill_status_change_end (&sd->bl, SC_WEIGHT90, -1); - } - return 0; -} - -void pc_set_weapon_look (struct map_session_data *sd) -{ - if (sd->attack_spell_override) - clif_changelook (&sd->bl, LOOK_WEAPON, - sd->attack_spell_look_override); - else - clif_changelook (&sd->bl, LOOK_WEAPON, sd->status.weapon); -} - -/*========================================== - * パラメータ計算 - * first==0の時、計算対象のパラメータが呼び出し前から - * 変 化した場合自動でsendするが、 - * 能動的に変化させたパラメータは自前でsendするように - *------------------------------------------ - */ -int pc_calcstatus (struct map_session_data *sd, int first) -{ - 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, aspd_rate, wele, wele_, def_ele, refinedef = 0; - int str, dstr, dex; - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - //転生や養子の場合の元の職業を算出する - s_class = pc_calc_base_job (sd->status.pc_class); - - 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->watk; - b_def = sd->def; - b_watk2 = sd->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.pc_class; - b_base_atk = sd->base_atk; - - pc_calc_skilltree (sd); // スキルツリーの計算 - - sd->max_weight = max_weight_base[s_class.job] + 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++; - } - } - - memset (sd->paramb, 0, sizeof (sd->paramb)); - memset (sd->parame, 0, sizeof (sd->parame)); - sd->hit = 0; - sd->flee = 0; - sd->flee2 = 0; - sd->critical = 0; - sd->aspd = 0; - sd->watk = 0; - sd->def = 0; - sd->mdef = 0; - sd->watk2 = 0; - sd->def2 = 0; - sd->mdef2 = 0; - sd->status.max_hp = 0; - sd->status.max_sp = 0; - sd->attackrange = 0; - sd->attackrange_ = 0; - sd->atk_ele = 0; - sd->def_ele = 0; - sd->star = 0; - sd->overrefine = 0; - sd->matk1 = 0; - sd->matk2 = 0; - sd->speed = DEFAULT_WALK_SPEED; - sd->hprate = 100; - sd->sprate = 100; - sd->castrate = 100; - sd->dsprate = 100; - sd->base_atk = 0; - sd->arrow_atk = 0; - sd->arrow_ele = 0; - sd->arrow_hit = 0; - sd->arrow_range = 0; - sd->nhealhp = sd->nhealsp = sd->nshealhp = sd->nshealsp = sd->nsshealhp = - sd->nsshealsp = 0; - memset (sd->addele, 0, sizeof (sd->addele)); - memset (sd->addrace, 0, sizeof (sd->addrace)); - memset (sd->addsize, 0, sizeof (sd->addsize)); - memset (sd->addele_, 0, sizeof (sd->addele_)); - memset (sd->addrace_, 0, sizeof (sd->addrace_)); - memset (sd->addsize_, 0, sizeof (sd->addsize_)); - memset (sd->subele, 0, sizeof (sd->subele)); - memset (sd->subrace, 0, sizeof (sd->subrace)); - memset (sd->addeff, 0, sizeof (sd->addeff)); - memset (sd->addeff2, 0, sizeof (sd->addeff2)); - memset (sd->reseff, 0, sizeof (sd->reseff)); - memset (&sd->special_state, 0, sizeof (sd->special_state)); - memset (sd->weapon_coma_ele, 0, sizeof (sd->weapon_coma_ele)); - memset (sd->weapon_coma_race, 0, sizeof (sd->weapon_coma_race)); - - sd->watk_ = 0; //二刀流用(仮) - sd->watk_2 = 0; - sd->atk_ele_ = 0; - sd->star_ = 0; - sd->overrefine_ = 0; - - sd->aspd_rate = 100; - sd->speed_rate = 100; - sd->hprecov_rate = 100; - sd->sprecov_rate = 100; - sd->critical_def = 0; - sd->double_rate = 0; - sd->near_attack_def_rate = sd->long_attack_def_rate = 0; - sd->atk_rate = sd->matk_rate = 100; - sd->ignore_def_ele = sd->ignore_def_race = 0; - sd->ignore_def_ele_ = sd->ignore_def_race_ = 0; - sd->ignore_mdef_ele = sd->ignore_mdef_race = 0; - sd->arrow_cri = 0; - sd->magic_def_rate = sd->misc_def_rate = 0; - memset (sd->arrow_addele, 0, sizeof (sd->arrow_addele)); - memset (sd->arrow_addrace, 0, sizeof (sd->arrow_addrace)); - memset (sd->arrow_addsize, 0, sizeof (sd->arrow_addsize)); - memset (sd->arrow_addeff, 0, sizeof (sd->arrow_addeff)); - memset (sd->arrow_addeff2, 0, sizeof (sd->arrow_addeff2)); - memset (sd->magic_addele, 0, sizeof (sd->magic_addele)); - memset (sd->magic_addrace, 0, sizeof (sd->magic_addrace)); - memset (sd->magic_subrace, 0, sizeof (sd->magic_subrace)); - sd->perfect_hit = 0; - 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->def_ratio_atk_ele = sd->def_ratio_atk_ele_ = 0; - sd->def_ratio_atk_race = sd->def_ratio_atk_race_ = 0; - sd->get_zeny_num = 0; - sd->add_damage_class_count = sd->add_damage_class_count_ = - sd->add_magic_damage_class_count = 0; - sd->add_def_class_count = sd->add_mdef_class_count = 0; - sd->monster_drop_item_count = 0; - memset (sd->add_damage_classrate, 0, sizeof (sd->add_damage_classrate)); - memset (sd->add_damage_classrate_, 0, sizeof (sd->add_damage_classrate_)); - memset (sd->add_magic_damage_classrate, 0, - sizeof (sd->add_magic_damage_classrate)); - memset (sd->add_def_classrate, 0, sizeof (sd->add_def_classrate)); - memset (sd->add_mdef_classrate, 0, sizeof (sd->add_mdef_classrate)); - memset (sd->monster_drop_race, 0, sizeof (sd->monster_drop_race)); - memset (sd->monster_drop_itemrate, 0, sizeof (sd->monster_drop_itemrate)); - sd->speed_add_rate = sd->aspd_add_rate = 100; - sd->double_add_rate = sd->perfect_hit_add = sd->get_zeny_add_num = 0; - sd->splash_range = sd->splash_add_range = 0; - sd->autospell_id = sd->autospell_lv = sd->autospell_rate = 0; - sd->hp_drain_rate = sd->hp_drain_per = sd->sp_drain_rate = - sd->sp_drain_per = 0; - sd->hp_drain_rate_ = sd->hp_drain_per_ = sd->sp_drain_rate_ = - sd->sp_drain_per_ = 0; - sd->short_weapon_damage_return = sd->long_weapon_damage_return = 0; - sd->magic_damage_return = 0; //AppleGirl Was Here - sd->random_attack_increase_add = sd->random_attack_increase_per = 0; - - if (!sd->disguiseflag && sd->disguise) - { - sd->disguise = 0; - pc_set_weapon_look (sd); - clif_changelook (&sd->bl, LOOK_SHIELD, sd->status.shield); - 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); - clif_clearchar (&sd->bl, 9); - pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); - } - - sd->spellpower_bonus_target = 0; - - for (i = 0; i < 10; i++) - { - index = sd->equip_index[i]; - 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->spellpower_bonus_target += - sd->inventory_data[index]->magic_bonus; - - if (sd->inventory_data[index]->type == 4) - { - 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) - { - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - if (i == 8 - && sd->status.inventory[index].equip == 0x20) - sd->state.lr_flag = 1; - run_script_l (itemdb_equipscript (c), 0, sd->bl.id, - 0, 2, arg); - sd->state.lr_flag = 0; - } - } - } - } - else if (sd->inventory_data[index]->type == 5) - { // 防具 - 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) { - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - run_script_l (itemdb_equipscript (c), 0, sd->bl.id, - 0, 2, arg); - } - } - } - } - } - } - -#ifdef USE_ASTRAL_SOUL_SKILL - if (sd->spellpower_bonus_target < 0) - sd->spellpower_bonus_target = - (sd->spellpower_bonus_target * 256) / - (MIN (128 + skill_power (sd, TMW_ASTRAL_SOUL), 256)); -#endif - - if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) - sd->spellpower_bonus_current = sd->spellpower_bonus_target; - - wele = sd->atk_ele; - wele_ = sd->atk_ele_; - def_ele = sd->def_ele; - memcpy (sd->paramcard, sd->parame, sizeof (sd->paramcard)); - - // 装備品によるステータス変化はここで実行 - for (i = 0; i < 10; i++) - { - index = sd->equip_index[i]; - 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 (i == 8 && sd->status.inventory[index].equip == 0x20) - { - //二刀流用データ入力 - sd->watk_ += sd->inventory_data[index]->atk; - sd->watk_2 = (r = sd->status.inventory[index].refine) * // 精錬攻撃力 - refinebonus[wlv][0]; - if ((r -= refinebonus[wlv][2]) > 0) // 過剰精錬ボーナス - sd->overrefine_ = r * refinebonus[wlv][1]; - - if (sd->status.inventory[index].card[0] == 0x00ff) - { // 製造武器 - sd->star_ = (sd->status.inventory[index].card[1] >> 8); // 星のかけら - wele_ = (sd->status.inventory[index].card[1] & 0x0f); // 属 性 - } - sd->attackrange_ += sd->inventory_data[index]->range; - sd->state.lr_flag = 1; - { - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - run_script_l (sd->inventory_data[index]->equip_script, 0, - sd->bl.id, 0, 2, arg); - } - sd->state.lr_flag = 0; - } - else - { //二刀流武器以外 - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - sd->watk += sd->inventory_data[index]->atk; - sd->watk2 += (r = sd->status.inventory[index].refine) * // 精錬攻撃力 - refinebonus[wlv][0]; - if ((r -= refinebonus[wlv][2]) > 0) // 過剰精錬ボーナス - sd->overrefine += r * refinebonus[wlv][1]; - - if (sd->status.inventory[index].card[0] == 0x00ff) - { // 製造武器 - sd->star += (sd->status.inventory[index].card[1] >> 8); // 星のかけら - wele = (sd->status.inventory[index].card[1] & 0x0f); // 属 性 - } - sd->attackrange += sd->inventory_data[index]->range; - run_script_l (sd->inventory_data[index]->equip_script, 0, - sd->bl.id, 0, 2, arg); - } - } - else if (sd->inventory_data[index]->type == 5) - { - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - sd->watk += sd->inventory_data[index]->atk; - refinedef += - sd->status.inventory[index].refine * refinebonus[0][0]; - run_script_l (sd->inventory_data[index]->equip_script, 0, - sd->bl.id, 0, 2, arg); - } - } - } - - if (battle_is_unarmed (&sd->bl)) - { - sd->watk += skill_power (sd, TMW_BRAWLING) / 3; // +66 for 200 - sd->watk2 += skill_power (sd, TMW_BRAWLING) >> 3; // +25 for 200 - sd->watk_ += skill_power (sd, TMW_BRAWLING) / 3; // +66 for 200 - sd->watk_2 += skill_power (sd, TMW_BRAWLING) >> 3; // +25 for 200 - } - - if (sd->equip_index[10] >= 0) - { // 矢 - index = sd->equip_index[10]; - if (sd->inventory_data[index]) - { //まだ属性が入っていない - argrec_t arg[2]; - arg[0].name = "@slotId"; - arg[0].v.i = i; - arg[1].name = "@itemId"; - arg[1].v.i = sd->inventory_data[index]->nameid; - sd->state.lr_flag = 2; - run_script_l (sd->inventory_data[index]->equip_script, 0, sd->bl.id, - 0, 2, arg); - 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->atk_ele = wele; - if (wele_ > 0) - sd->atk_ele_ = wele_; - if (def_ele > 0) - sd->def_ele = def_ele; - sd->double_rate += sd->double_add_rate; - sd->perfect_hit += sd->perfect_hit_add; - sd->get_zeny_num += sd->get_zeny_add_num; - sd->splash_range += sd->splash_add_range; - if (sd->speed_add_rate != 100) - sd->speed_rate += sd->speed_add_rate - 100; - if (sd->aspd_add_rate != 100) - sd->aspd_rate += sd->aspd_add_rate - 100; - - // 武器ATKサイズ補正 (右手) - sd->atkmods[0] = atkmods[0][sd->weapontype1]; - sd->atkmods[1] = atkmods[1][sd->weapontype1]; - sd->atkmods[2] = atkmods[2][sd->weapontype1]; - //武器ATKサイズ補正 (左手) - sd->atkmods_[0] = atkmods[0][sd->weapontype2]; - sd->atkmods_[1] = atkmods[1][sd->weapontype2]; - sd->atkmods_[2] = atkmods[2][sd->weapontype2]; - -/* - // jobボーナス分 - for(i=0;i<sd->status.job_level && i<MAX_LEVEL;i++){ - if(job_bonus[s_class.upper][s_class.job][i]) - sd->paramb[job_bonus[s_class.upper][s_class.job][i]-1]++; - } -*/ - - if ((skill = pc_checkskill (sd, MC_INCCARRY)) > 0) // skill can be used with an item now, thanks to orn [Valaris] - sd->max_weight += skill * 1000; - - // ステータス変化による基本パラメータ補正 - if (sd->sc_count) - { - if (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; - } - if (sd->sc_data[SC_INCREASEAGI].timer != -1 - && sd->sc_data[SC_QUAGMIRE].timer == -1 - && sd->sc_data[SC_DONTFORGETME].timer == -1) - { // 速度増加 - sd->paramb[1] += 2 + sd->sc_data[SC_INCREASEAGI].val1; - sd->speed -= sd->speed * 25 / 100; - } - if (sd->sc_data[SC_DECREASEAGI].timer != -1) // 速度減少(agiはbattle.cで) - sd->speed = sd->speed * 125 / 100; - if (sd->sc_data[SC_CLOAKING].timer != -1) - sd->speed = - (sd->speed * (76 + (sd->sc_data[SC_INCREASEAGI].val1 * 3))) / - 100; - if (sd->sc_data[SC_BLESSING].timer != -1) - { // ブレッシング - sd->paramb[0] += sd->sc_data[SC_BLESSING].val1; - sd->paramb[3] += sd->sc_data[SC_BLESSING].val1; - sd->paramb[4] += sd->sc_data[SC_BLESSING].val1; - } - if (sd->sc_data[SC_GLORIA].timer != -1) // グロリア - sd->paramb[5] += 30; - if (sd->sc_data[SC_LOUD].timer != -1 && sd->sc_data[SC_QUAGMIRE].timer == -1) // ラウドボイス - sd->paramb[0] += 4; - if (sd->sc_data[SC_QUAGMIRE].timer != -1) - { // クァグマイア - sd->speed = sd->speed * 3 / 2; - sd->paramb[1] -= - (sd->status.agi + sd->paramb[1] + sd->parame[1]) / 2; - sd->paramb[4] -= - (sd->status.dex + sd->paramb[4] + sd->parame[4]) / 2; - } - if (sd->sc_data[SC_TRUESIGHT].timer != -1) - { // トゥルーサイト - sd->paramb[0] += 5; - sd->paramb[1] += 5; - sd->paramb[2] += 5; - sd->paramb[3] += 5; - sd->paramb[4] += 5; - sd->paramb[5] += 5; - } - } - sd->speed -= skill_power (sd, TMW_SPEED) >> 3; - sd->aspd_rate -= skill_power (sd, TMW_SPEED) / 10; - if (sd->aspd_rate < 20) - sd->aspd_rate = 20; - -/* - //1度も死んでないJob70スパノビに+10 - if(s_class.job == 23 && sd->die_counter == 0 && sd->status.job_level >= 70){ - sd->paramb[0]+= 15; - sd->paramb[1]+= 15; - sd->paramb[2]+= 15; - sd->paramb[3]+= 15; - sd->paramb[4]+= 15; - sd->paramb[5]+= 15; - } -*/ - 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]; - for (i = 0; i < 6; i++) - if (sd->paramc[i] < 0) - sd->paramc[i] = 0; - - if (sd->status.weapon == 11 || sd->status.weapon == 13 - || sd->status.weapon == 14) - { - str = sd->paramc[4]; - dex = sd->paramc[0]; - } - else - { - str = sd->paramc[0]; - dex = sd->paramc[4]; - sd->critical += ((dex * 3) >> 1); - } - dstr = str / 10; - sd->base_atk += str + dstr * dstr + dex / 5 + sd->paramc[5] / 5; -//fprintf(stderr, "baseatk = %d = x + %d + %d + %d + %d\n", sd->base_atk, str, dstr*dstr, dex/5, sd->paramc[5]/5); - 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; - } - // [Fate] New tmw magic system - sd->matk1 += sd->status.base_level + sd->spellpower_bonus_current; -#ifdef USE_ASTRAL_SOUL_SKILL - if (sd->matk1 > MAGIC_SKILL_THRESHOLD) - { - int bonus = sd->matk1 - MAGIC_SKILL_THRESHOLD; - // Ok if you are above a certain threshold, you get only (1/8) of that matk1 - // if you have Astral soul skill you can get the whole power again (and additionally the 1/8 added) - sd->matk1 = MAGIC_SKILL_THRESHOLD + (bonus>>3) + ((3*bonus*skill_power(sd, TMW_ASTRAL_SOUL))>>9); - } -#endif - sd->matk2 = 0; - if (sd->matk1 < 0) - sd->matk1 = 0; - - sd->hit += sd->paramc[4] + sd->status.base_level; - sd->flee += sd->paramc[1] + sd->status.base_level; - sd->def2 += sd->paramc[2]; - sd->mdef2 += sd->paramc[3]; - sd->flee2 += sd->paramc[5] + 10; - sd->critical += (sd->paramc[5] * 3) + 10; - - // 200 is the maximum of the skill - // def2 is the defence gained by vit, whereas "def", which is gained by armor, stays as is - int spbsk = skill_power (sd, TMW_RAGING); - if (spbsk != 0 && sd->attackrange <= 2) - { - sd->critical += sd->critical * spbsk / 100; - sd->def2 = (sd->def2 * 256) / (256 + spbsk); - } - - if (sd->base_atk < 1) - sd->base_atk = 1; - if (sd->critical_rate != 100) - sd->critical = (sd->critical * sd->critical_rate) / 100; - if (sd->critical < 10) - sd->critical = 10; - if (sd->hit_rate != 100) - sd->hit = (sd->hit * sd->hit_rate) / 100; - if (sd->hit < 1) - sd->hit = 1; - if (sd->flee_rate != 100) - sd->flee = (sd->flee * sd->flee_rate) / 100; - if (sd->flee < 1) - sd->flee = 1; - if (sd->flee2_rate != 100) - sd->flee2 = (sd->flee2 * sd->flee2_rate) / 100; - if (sd->flee2 < 10) - sd->flee2 = 10; - if (sd->def_rate != 100) - sd->def = (sd->def * sd->def_rate) / 100; - if (sd->def < 0) - sd->def = 0; - if (sd->def2_rate != 100) - sd->def2 = (sd->def2 * sd->def2_rate) / 100; - if (sd->def2 < 1) - sd->def2 = 1; - if (sd->mdef_rate != 100) - sd->mdef = (sd->mdef * sd->mdef_rate) / 100; - if (sd->mdef < 0) - sd->mdef = 0; - if (sd->mdef2_rate != 100) - sd->mdef2 = (sd->mdef2 * sd->mdef2_rate) / 100; - if (sd->mdef2 < 1) - sd->mdef2 = 1; - - // 二刀流 ASPD 修正 - if (sd->status.weapon <= 16) - sd->aspd += - aspd_base[s_class.job][sd->status.weapon] - (sd->paramc[1] * 4 + - sd->paramc[4]) * - aspd_base[s_class.job][sd->status.weapon] / 1000; - else - sd->aspd += ((aspd_base[s_class.job][sd->weapontype1] - - (sd->paramc[1] * 4 + - sd->paramc[4]) * - aspd_base[s_class.job][sd->weapontype1] / 1000) + - (aspd_base[s_class.job][sd->weapontype2] - - (sd->paramc[1] * 4 + - sd->paramc[4]) * - aspd_base[s_class.job][sd->weapontype2] / 1000)) * 140 / - 200; - - aspd_rate = sd->aspd_rate; - - //攻撃速度増加 - - if ((skill = pc_checkskill (sd, AC_VULTURE)) > 0) - { // ワシの目 - sd->hit += skill; - if (sd->status.weapon == 11) - sd->attackrange += skill; - } - - if (sd->attackrange > 2) - { // [fate] ranged weapon? - sd->attackrange += MIN (skill_power (sd, AC_OWL) / 60, 3); - sd->hit += skill_power (sd, AC_OWL) / 10; // 20 for 200 - } - - if ((skill = pc_checkskill (sd, BS_WEAPONRESEARCH)) > 0) // 武器研究の命中率増加 - sd->hit += skill * 2; - if (sd->status.option & 2 && (skill = pc_checkskill (sd, RG_TUNNELDRIVE)) > 0) // トンネルドライブ - sd->speed += (1.2 * DEFAULT_WALK_SPEED - skill * 9); - if (pc_iscarton (sd) && (skill = pc_checkskill (sd, MC_PUSHCART)) > 0) // カートによる速度低下 - sd->speed += (10 - skill) * (DEFAULT_WALK_SPEED * 0.1); - else if (pc_isriding (sd)) // ペコペコ乗りによる速度増加 - sd->speed -= (0.25 * DEFAULT_WALK_SPEED); - sd->max_weight += 1000; - if (sd->sc_count) - { - if (sd->sc_data[SC_WINDWALK].timer != -1) //ウィンドウォーク時はLv*2%減算 - sd->speed -= - sd->speed * (sd->sc_data[SC_WINDWALK].val1 * 2) / 100; - if (sd->sc_data[SC_CARTBOOST].timer != -1) // カートブースト - sd->speed -= (DEFAULT_WALK_SPEED * 20) / 100; - if (sd->sc_data[SC_BERSERK].timer != -1) //バーサーク中はIAと同じぐらい速い? - sd->speed -= sd->speed * 25 / 100; - if (sd->sc_data[SC_WEDDING].timer != -1) //結婚中は歩くのが遅い - sd->speed = 2 * DEFAULT_WALK_SPEED; - } - - if ((skill = pc_checkskill (sd, CR_TRUST)) > 0) - { // フェイス - sd->status.max_hp += skill * 200; - sd->subele[6] += skill * 5; - } - if ((skill = pc_checkskill (sd, BS_SKINTEMPER)) > 0) - sd->subele[3] += skill * 4; - - bl = sd->status.base_level; - - sd->status.max_hp += - (3500 + bl * hp_coefficient2[s_class.job] + - hp_sigma_val[s_class.job][(bl > 0) ? bl - 1 : 0]) / 100 * (100 + - sd->paramc - [2]) / - 100 + (sd->parame[2] - sd->paramcard[2]); - if (s_class.upper == 1) // [MouseJstr] - sd->status.max_hp = sd->status.max_hp * 130 / 100; - if (sd->hprate != 100) - sd->status.max_hp = sd->status.max_hp * sd->hprate / 100; - - if (sd->sc_data && sd->sc_data[SC_BERSERK].timer != -1) - { // バーサーク - sd->status.max_hp = sd->status.max_hp * 3; - sd->status.hp = sd->status.hp * 3; - if (sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris - sd->status.max_hp = battle_config.max_hp; - if (sd->status.hp > battle_config.max_hp) // removed negative max hp bug by Valaris - sd->status.hp = battle_config.max_hp; - } - if (s_class.job == 23 && sd->status.base_level >= 99) - { - sd->status.max_hp = sd->status.max_hp + 2000; - } - - if (sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris - sd->status.max_hp = battle_config.max_hp; - if (sd->status.max_hp <= 0) - sd->status.max_hp = 1; // end - - // 最大SP計算 - sd->status.max_sp += - ((sp_coefficient[s_class.job] * bl) + 1000) / 100 * (100 + - sd->paramc[3]) / - 100 + (sd->parame[3] - sd->paramcard[3]); - if (s_class.upper == 1) // [MouseJstr] - sd->status.max_sp = sd->status.max_sp * 130 / 100; - if (sd->sprate != 100) - sd->status.max_sp = sd->status.max_sp * sd->sprate / 100; - - 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; - - if (sd->status.max_sp < 0 || sd->status.max_sp > battle_config.max_sp) - sd->status.max_sp = battle_config.max_sp; - - //自然回復HP - sd->nhealhp = 1 + (sd->paramc[2] / 5) + (sd->status.max_hp / 200); - if ((skill = pc_checkskill (sd, SM_RECOVERY)) > 0) - { // HP回復力向上 - sd->nshealhp = skill * 5 + (sd->status.max_hp * skill / 500); - if (sd->nshealhp > 0x7fff) - sd->nshealhp = 0x7fff; - } - //自然回復SP - 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; - if ((skill = pc_checkskill (sd, MG_SRECOVERY)) > 0) - { // SP回復力向上 - sd->nshealsp = skill * 3 + (sd->status.max_sp * skill / 500); - if (sd->nshealsp > 0x7fff) - sd->nshealsp = 0x7fff; - } - - if ((skill = pc_checkskill (sd, MO_SPIRITSRECOVERY)) > 0) - { - sd->nsshealhp = skill * 4 + (sd->status.max_hp * skill / 500); - sd->nsshealsp = skill * 2 + (sd->status.max_sp * skill / 500); - if (sd->nsshealhp > 0x7fff) - sd->nsshealhp = 0x7fff; - if (sd->nsshealsp > 0x7fff) - sd->nsshealsp = 0x7fff; - } - if (sd->hprecov_rate != 100) - { - sd->nhealhp = sd->nhealhp * sd->hprecov_rate / 100; - if (sd->nhealhp < 1) - sd->nhealhp = 1; - } - if (sd->sprecov_rate != 100) - { - sd->nhealsp = sd->nhealsp * sd->sprecov_rate / 100; - if (sd->nhealsp < 1) - sd->nhealsp = 1; - } - if ((skill = pc_checkskill (sd, HP_MEDITATIO)) > 0) - { // メディテイティオはSPRではなく自然回復にかかる - sd->nhealsp += 3 * skill * (sd->status.max_sp) / 100; - if (sd->nhealsp > 0x7fff) - sd->nhealsp = 0x7fff; - } - - // 種族耐性(これでいいの? ディバインプロテクションと同じ処理がいるかも) - if ((skill = pc_checkskill (sd, SA_DRAGONOLOGY)) > 0) - { // ドラゴノロジー - skill = skill * 4; - sd->addrace[9] += skill; - sd->addrace_[9] += skill; - sd->subrace[9] += skill; - sd->magic_addrace[9] += skill; - sd->magic_subrace[9] -= skill; - } - - //Flee上昇 - if ((skill = pc_checkskill (sd, TF_MISS)) > 0) - { // 回避率増加 - if (sd->status.pc_class == 6 || sd->status.pc_class == 4007 - || sd->status.pc_class == 23) - { - sd->flee += skill * 3; - } - if (sd->status.pc_class == 12 || sd->status.pc_class == 17 - || sd->status.pc_class == 4013 || sd->status.pc_class == 4018) - sd->flee += skill * 4; - if (sd->status.pc_class == 12 || sd->status.pc_class == 4013) - sd->speed -= sd->speed * (skill * .5) / 100; - } - if ((skill = pc_checkskill (sd, MO_DODGE)) > 0) // 見切り - sd->flee += (skill * 3) >> 1; - - // スキルやステータス異常による残りのパラメータ補正 - if (sd->sc_count) - { - // ATK/DEF変化形 - if (sd->sc_data[SC_ANGELUS].timer != -1) // エンジェラス - sd->def2 = - sd->def2 * (110 + 5 * sd->sc_data[SC_ANGELUS].val1) / 100; - if (sd->sc_data[SC_IMPOSITIO].timer != -1) - { // インポシティオマヌス - sd->watk += sd->sc_data[SC_IMPOSITIO].val1 * 5; - index = sd->equip_index[8]; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->type == 4) - sd->watk_ += sd->sc_data[SC_IMPOSITIO].val1 * 5; - } - if (sd->sc_data[SC_PROVOKE].timer != -1) - { // プロボック - sd->def2 = - sd->def2 * (100 - 6 * sd->sc_data[SC_PROVOKE].val1) / 100; - sd->base_atk = - sd->base_atk * (100 + 2 * sd->sc_data[SC_PROVOKE].val1) / 100; - sd->watk = - sd->watk * (100 + 2 * sd->sc_data[SC_PROVOKE].val1) / 100; - index = sd->equip_index[8]; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->type == 4) - sd->watk_ = - sd->watk_ * (100 + - 2 * sd->sc_data[SC_PROVOKE].val1) / 100; - } - if (sd->sc_data[SC_ENDURE].timer != -1) - sd->mdef2 += sd->sc_data[SC_ENDURE].val1; - if (sd->sc_data[SC_MINDBREAKER].timer != -1) - { // プロボック - sd->mdef2 = - sd->mdef2 * (100 - - 6 * sd->sc_data[SC_MINDBREAKER].val1) / 100; - sd->matk1 = - sd->matk1 * (100 + - 2 * sd->sc_data[SC_MINDBREAKER].val1) / 100; - sd->matk2 = - sd->matk2 * (100 + - 2 * sd->sc_data[SC_MINDBREAKER].val1) / 100; - } - if (sd->sc_data[SC_POISON].timer != -1) // 毒状態 - sd->def2 = sd->def2 * 75 / 100; - if (sd->sc_data[SC_DRUMBATTLE].timer != -1) - { // 戦太鼓の響き - sd->watk += sd->sc_data[SC_DRUMBATTLE].val2; - sd->def += sd->sc_data[SC_DRUMBATTLE].val3; - index = sd->equip_index[8]; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->type == 4) - sd->watk_ += sd->sc_data[SC_DRUMBATTLE].val2; - } - 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 == 3) - sd->watk += sd->sc_data[SC_NIBELUNGEN].val3; - index = sd->equip_index[8]; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->wlv == 3) - sd->watk_ += sd->sc_data[SC_NIBELUNGEN].val3; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->wlv == 4) - sd->watk += 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->watk_ += sd->sc_data[SC_NIBELUNGEN].val2; - } - - if (sd->sc_data[SC_VOLCANO].timer != -1 && sd->def_ele == 3) - { // ボルケーノ - sd->watk += sd->sc_data[SC_VIOLENTGALE].val3; - } - - if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1) - sd->def = - sd->def * (100 - sd->sc_data[SC_SIGNUMCRUCIS].val2) / 100; - if (sd->sc_data[SC_ETERNALCHAOS].timer != -1) // エターナルカオス - sd->def = 0; - - if (sd->sc_data[SC_CONCENTRATION].timer != -1) - { //コンセントレーション - sd->watk = - sd->watk * (100 + - 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; - index = sd->equip_index[8]; - if (index >= 0 && sd->inventory_data[index] - && sd->inventory_data[index]->type == 4) - sd->watk_ = - sd->watk * (100 + - 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; - sd->def = - sd->def * (100 - - 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; - } - - if (sd->sc_data[SC_MAGICPOWER].timer != -1) - { //魔法力増幅 - sd->matk1 = - sd->matk1 * (100 + 2 * sd->sc_data[SC_MAGICPOWER].val1) / 100; - sd->matk2 = - sd->matk2 * (100 + 2 * sd->sc_data[SC_MAGICPOWER].val1) / 100; - } - if (sd->sc_data[SC_ATKPOT].timer != -1) - sd->watk += sd->sc_data[SC_ATKPOT].val1; - if (sd->sc_data[SC_MATKPOT].timer != -1) - { - sd->matk1 += sd->sc_data[SC_MATKPOT].val1; - sd->matk2 += sd->sc_data[SC_MATKPOT].val1; - } - - // ASPD/移動速度変化系 - if (sd->sc_data[SC_TWOHANDQUICKEN].timer != -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // 2HQ - aspd_rate -= 30; - if (sd->sc_data[SC_ADRENALINE].timer != -1 - && sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sd->sc_data[SC_QUAGMIRE].timer == -1 - && sd->sc_data[SC_DONTFORGETME].timer == -1) - { // アドレナリンラッシュ - if (sd->sc_data[SC_ADRENALINE].val2 - || !battle_config.party_skill_penaly) - aspd_rate -= 30; - else - aspd_rate -= 25; - } - if (sd->sc_data[SC_SPEARSQUICKEN].timer != -1 && sd->sc_data[SC_ADRENALINE].timer == -1 && sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン - aspd_rate -= sd->sc_data[SC_SPEARSQUICKEN].val2; - if (sd->sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス - sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 - && sd->sc_data[SC_ADRENALINE].timer == -1 - && sd->sc_data[SC_SPEARSQUICKEN].timer == -1 - && sd->sc_data[SC_DONTFORGETME].timer == -1) - aspd_rate -= - 5 + sd->sc_data[SC_ASSNCROS].val1 + - sd->sc_data[SC_ASSNCROS].val2 + sd->sc_data[SC_ASSNCROS].val3; - if (sd->sc_data[SC_DONTFORGETME].timer != -1) - { // 私を忘れないで - aspd_rate += - sd->sc_data[SC_DONTFORGETME].val1 * 3 + - sd->sc_data[SC_DONTFORGETME].val2 + - (sd->sc_data[SC_DONTFORGETME].val3 >> 16); - sd->speed = - sd->speed * (100 + sd->sc_data[SC_DONTFORGETME].val1 * 2 + - sd->sc_data[SC_DONTFORGETME].val2 + - (sd->sc_data[SC_DONTFORGETME].val3 & 0xffff)) / - 100; - } - if (sd->sc_data[i = SC_SPEEDPOTION2].timer != -1 || sd->sc_data[i = SC_SPEEDPOTION1].timer != -1 || sd->sc_data[i = SC_SPEEDPOTION0].timer != -1) // 増 速ポーション - aspd_rate -= sd->sc_data[i].val1; - - if (sd->sc_data[SC_HASTE].timer != -1) - aspd_rate -= sd->sc_data[SC_HASTE].val1; - - /* Slow down if protected */ - - if (sd->sc_data[SC_PHYS_SHIELD].timer != -1) - aspd_rate += sd->sc_data[SC_PHYS_SHIELD].val1; - - // HIT/FLEE変化系 - if (sd->sc_data[SC_WHISTLE].timer != -1) - { // 口笛 - sd->flee += sd->flee * (sd->sc_data[SC_WHISTLE].val1 - + sd->sc_data[SC_WHISTLE].val2 + - (sd->sc_data[SC_WHISTLE].val3 >> 16)) / - 100; - sd->flee2 += - (sd->sc_data[SC_WHISTLE].val1 + sd->sc_data[SC_WHISTLE].val2 + - (sd->sc_data[SC_WHISTLE].val3 & 0xffff)) * 10; - } - if (sd->sc_data[SC_HUMMING].timer != -1) // ハミング - sd->hit += - (sd->sc_data[SC_HUMMING].val1 * 2 + - sd->sc_data[SC_HUMMING].val2 + - sd->sc_data[SC_HUMMING].val3) * sd->hit / 100; - if (sd->sc_data[SC_VIOLENTGALE].timer != -1 && sd->def_ele == 4) - { // バイオレントゲイル - sd->flee += sd->flee * sd->sc_data[SC_VIOLENTGALE].val3 / 100; - } - if (sd->sc_data[SC_BLIND].timer != -1) - { // 暗黒 - sd->hit -= sd->hit * 25 / 100; - sd->flee -= sd->flee * 25 / 100; - } - if (sd->sc_data[SC_WINDWALK].timer != -1) // ウィンドウォーク - sd->flee += sd->flee * (sd->sc_data[SC_WINDWALK].val2) / 100; - if (sd->sc_data[SC_SPIDERWEB].timer != -1) //スパイダーウェブ - sd->flee -= sd->flee * 50 / 100; - if (sd->sc_data[SC_TRUESIGHT].timer != -1) //トゥルーサイト - sd->hit += 3 * (sd->sc_data[SC_TRUESIGHT].val1); - if (sd->sc_data[SC_CONCENTRATION].timer != -1) //コンセントレーション - sd->hit += (10 * (sd->sc_data[SC_CONCENTRATION].val1)); - - // 耐性 - 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; // 対 悪魔 - } - - // その他 - if (sd->sc_data[SC_APPLEIDUN].timer != -1) - { // イドゥンの林檎 - sd->status.max_hp += - ((5 + sd->sc_data[SC_APPLEIDUN].val1 * 2 + - ((sd->sc_data[SC_APPLEIDUN].val2 + 1) >> 1) + - sd->sc_data[SC_APPLEIDUN].val3 / 10) * sd->status.max_hp) / - 100; - if (sd->status.max_hp < 0 - || sd->status.max_hp > battle_config.max_hp) - sd->status.max_hp = battle_config.max_hp; - } - if (sd->sc_data[SC_DELUGE].timer != -1 && sd->def_ele == 1) - { // デリュージ - sd->status.max_hp += - sd->status.max_hp * sd->sc_data[SC_DELUGE].val3 / 100; - if (sd->status.max_hp < 0 - || sd->status.max_hp > battle_config.max_hp) - sd->status.max_hp = battle_config.max_hp; - } - if (sd->sc_data[SC_SERVICE4U].timer != -1) - { // サービスフォーユー - sd->status.max_sp += - sd->status.max_sp * (10 + sd->sc_data[SC_SERVICE4U].val1 + - sd->sc_data[SC_SERVICE4U].val2 + - sd->sc_data[SC_SERVICE4U].val3) / 100; - if (sd->status.max_sp < 0 - || sd->status.max_sp > battle_config.max_sp) - sd->status.max_sp = battle_config.max_sp; - sd->dsprate -= - (10 + sd->sc_data[SC_SERVICE4U].val1 * 3 + - sd->sc_data[SC_SERVICE4U].val2 + - sd->sc_data[SC_SERVICE4U].val3); - if (sd->dsprate < 0) - sd->dsprate = 0; - } - - if (sd->sc_data[SC_FORTUNE].timer != -1) // 幸運のキス - sd->critical += - (10 + sd->sc_data[SC_FORTUNE].val1 + - sd->sc_data[SC_FORTUNE].val2 + - sd->sc_data[SC_FORTUNE].val3) * 10; - - if (sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) - { // 爆裂波動 - if (s_class.job == 23) - sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val1 * 100; - else - sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val2; - } - - if (sd->sc_data[SC_STEELBODY].timer != -1) - { // 金剛 - sd->def = 90; - sd->mdef = 90; - aspd_rate += 25; - sd->speed = (sd->speed * 125) / 100; - } - if (sd->sc_data[SC_DEFENDER].timer != -1) - { - sd->aspd += (550 - sd->sc_data[SC_DEFENDER].val1 * 50); - sd->speed = - (sd->speed * (155 - sd->sc_data[SC_DEFENDER].val1 * 5)) / 100; - } - if (sd->sc_data[SC_ENCPOISON].timer != -1) - sd->addeff[4] += sd->sc_data[SC_ENCPOISON].val2; - - if (sd->sc_data[SC_DANCING].timer != -1) - { // 演奏/ダンス使用中 - sd->speed *= 4; - sd->nhealsp = 0; - sd->nshealsp = 0; - sd->nsshealsp = 0; - } - if (sd->sc_data[SC_CURSE].timer != -1) - sd->speed += 450; - - if (sd->sc_data[SC_TRUESIGHT].timer != -1) //トゥルーサイト - sd->critical += - sd->critical * (sd->sc_data[SC_TRUESIGHT].val1) / 100; - -/* if(sd->sc_data[SC_VOLCANO].timer!=-1) // エンチャントポイズン(属性はbattle.cで) - sd->addeff[2]+=sd->sc_data[SC_VOLCANO].val2;//% of granting - if(sd->sc_data[SC_DELUGE].timer!=-1) // エンチャントポイズン(属性はbattle.cで) - sd->addeff[0]+=sd->sc_data[SC_DELUGE].val2;//% of granting - */ - } - - if (sd->speed_rate != 100) - sd->speed = sd->speed * sd->speed_rate / 100; - if (sd->speed < 1) - sd->speed = 1; - if (aspd_rate != 100) - sd->aspd = sd->aspd * aspd_rate / 100; - if (pc_isriding (sd)) // 騎兵修練 - sd->aspd = - sd->aspd * (100 + - 10 * (5 - - pc_checkskill (sd, KN_CAVALIERMASTERY))) / 100; - - if (sd->attack_spell_override) - sd->aspd = sd->attack_spell_delay; - - if (sd->aspd < battle_config.max_aspd) - sd->aspd = battle_config.max_aspd; - sd->amotion = sd->aspd; - sd->dmotion = 800 - sd->paramc[1] * 4; - if (sd->dmotion < 400) - sd->dmotion = 400; - if (sd->skilltimer != -1 && (skill = pc_checkskill (sd, SA_FREECAST)) > 0) - { - sd->prev_speed = sd->speed; - sd->speed = sd->speed * (175 - skill * 5) / 100; - } - - if (sd->status.hp > sd->status.max_hp) - sd->status.hp = sd->status.max_hp; - if (sd->status.sp > sd->status.max_sp) - sd->status.sp = sd->status.max_sp; - - if (first & 4) - 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); - } - return 0; - } - - if (b_class != sd->view_class) - { - clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); - clif_changelook (&sd->bl, LOOK_WEAPON, 0); - } - - 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->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->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); - -/* if(before.cart_num != before.cart_num || before.cart_max_num != before.cart_max_num || - before.cart_weight != before.cart_weight || before.cart_max_weight != before.cart_max_weight ) - clif_updatestatus(sd,SP_CARTINFO);*/ - - if (sd->status.hp < sd->status.max_hp >> 2 - && pc_checkskill (sd, SM_AUTOBERSERK) > 0 - && (sd->sc_data[SC_PROVOKE].timer == -1 - || sd->sc_data[SC_PROVOKE].val2 == 0) && !pc_isdead (sd)) - // オートバーサーク発動 - skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, 0); - - return 0; -} - -/*========================================== - * 装 備品による能力等のボーナス設定 - *------------------------------------------ - */ -int pc_bonus (struct map_session_data *sd, int type, int val) -{ - 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->watk += val; - else if (sd->state.lr_flag == 1) - sd->watk_ += val; - break; - case SP_ATK2: - if (!sd->state.lr_flag) - sd->watk2 += val; - else if (sd->state.lr_flag == 1) - sd->watk_2 += 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_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->atk_ele = val; - else if (sd->state.lr_flag == 1) - sd->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: - if (sd->state.lr_flag != 2) - sd->speed -= val; - break; - case SP_SPEED_RATE: - if (sd->state.lr_flag != 2) - { - if (sd->speed_rate > 100 - val) - sd->speed_rate = 100 - val; - } - break; - case SP_SPEED_ADDRATE: - if (sd->state.lr_flag != 2) - sd->speed_add_rate = sd->speed_add_rate * (100 - val) / 100; - break; - case SP_ASPD: - if (sd->state.lr_flag != 2) - sd->aspd -= val * 10; - break; - case SP_ASPD_RATE: - if (sd->state.lr_flag != 2) - { - if (sd->aspd_rate > 100 - val) - sd->aspd_rate = 100 - val; - } - break; - case SP_ASPD_ADDRATE: - if (sd->state.lr_flag != 2) - sd->aspd_add_rate = sd->aspd_add_rate * (100 - val) / 100; - 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->ignore_def_ele |= 1 << val; - else if (sd->state.lr_flag == 1) - sd->ignore_def_ele_ |= 1 << val; - break; - case SP_IGNORE_DEF_RACE: - if (!sd->state.lr_flag) - sd->ignore_def_race |= 1 << val; - else if (sd->state.lr_flag == 1) - sd->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_GET_ZENY_NUM: - if (sd->state.lr_flag != 2 && sd->get_zeny_num < val) - sd->get_zeny_num = val; - break; - case SP_ADD_GET_ZENY_NUM: - if (sd->state.lr_flag != 2) - sd->get_zeny_add_num += val; - break; - case SP_DEF_RATIO_ATK_ELE: - if (!sd->state.lr_flag) - sd->def_ratio_atk_ele |= 1 << val; - else if (sd->state.lr_flag == 1) - sd->def_ratio_atk_ele_ |= 1 << val; - break; - case SP_DEF_RATIO_ATK_RACE: - if (!sd->state.lr_flag) - sd->def_ratio_atk_race |= 1 << val; - else if (sd->state.lr_flag == 1) - sd->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_RECORVER: - 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_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; - clif_updatestatus (sd, 13); - clif_updatestatus (sd, 14); - clif_updatestatus (sd, 15); - clif_updatestatus (sd, 16); - clif_updatestatus (sd, 17); - clif_updatestatus (sd, 18); - } - 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; - clif_updatestatus (sd, 14); - clif_updatestatus (sd, 15); - } - 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; - clif_updatestatus (sd, 14); - clif_updatestatus (sd, 17); - clif_updatestatus (sd, 13); - } - break; - case SP_PERFECT_HIDE: // [Valaris] - if (sd->state.lr_flag != 2) - { - sd->perfect_hiding = 1; - } - break; - case SP_DISGUISE: // Disguise script for items [Valaris] - if (sd->state.lr_flag != 2 && sd->disguiseflag == 0) - { - if (pc_isriding (sd)) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (sd->fd, - "Cannot wear disguise when riding a Peco."); - break; - } - sd->disguise = val; - clif_clearchar (&sd->bl, 9); - pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); - } - break; - case SP_UNBREAKABLE: - if (sd->state.lr_flag != 2) - { - sd->unbreakable += val; - } - break; - case SP_DEAF: - sd->special_state.deaf = 1; - break; - default: - if (battle_config.error_log) - printf ("pc_bonus: unknown type %d %d !\n", type, val); - break; - } - return 0; -} - -/*========================================== - * ソスソス ソスソスソスiソスノゑソスソスソスソス\ソスヘ難ソスソスフボソス[ソスiソスXソスン抵ソス - *------------------------------------------ - */ -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->addele[type2] += val; - else if (sd->state.lr_flag == 1) - sd->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->addrace[type2] += val; - else if (sd->state.lr_flag == 1) - sd->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->addsize[type2] += val; - else if (sd->state.lr_flag == 1) - sd->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 (sd->state.lr_flag != 2) - sd->addeff[type2] += val; - else - sd->arrow_addeff[type2] += val; - break; - case SP_ADDEFF2: - if (sd->state.lr_flag != 2) - sd->addeff2[type2] += val; - else - sd->arrow_addeff2[type2] += val; - break; - case SP_RESEFF: - if (sd->state.lr_flag != 2) - sd->reseff[type2] += 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_SUBRACE: - if (sd->state.lr_flag != 2) - sd->magic_subrace[type2] += val; - break; - case SP_ADD_DAMAGE_CLASS: - if (!sd->state.lr_flag) - { - for (i = 0; i < sd->add_damage_class_count; i++) - { - if (sd->add_damage_classid[i] == type2) - { - sd->add_damage_classrate[i] += val; - break; - } - } - if (i >= sd->add_damage_class_count - && sd->add_damage_class_count < 10) - { - sd->add_damage_classid[sd->add_damage_class_count] = - type2; - sd->add_damage_classrate[sd->add_damage_class_count] += - val; - sd->add_damage_class_count++; - } - } - else if (sd->state.lr_flag == 1) - { - for (i = 0; i < sd->add_damage_class_count_; i++) - { - if (sd->add_damage_classid_[i] == type2) - { - sd->add_damage_classrate_[i] += val; - break; - } - } - if (i >= sd->add_damage_class_count_ - && sd->add_damage_class_count_ < 10) - { - sd->add_damage_classid_[sd->add_damage_class_count_] = - type2; - sd->add_damage_classrate_[sd->add_damage_class_count_] += - val; - sd->add_damage_class_count_++; - } - } - break; - case SP_ADD_MAGIC_DAMAGE_CLASS: - if (sd->state.lr_flag != 2) - { - for (i = 0; i < sd->add_magic_damage_class_count; i++) - { - if (sd->add_magic_damage_classid[i] == type2) - { - sd->add_magic_damage_classrate[i] += val; - break; - } - } - if (i >= sd->add_magic_damage_class_count - && sd->add_magic_damage_class_count < 10) - { - sd->add_magic_damage_classid - [sd->add_magic_damage_class_count] = type2; - sd->add_magic_damage_classrate - [sd->add_magic_damage_class_count] += val; - sd->add_magic_damage_class_count++; - } - } - break; - case SP_ADD_DEF_CLASS: - if (sd->state.lr_flag != 2) - { - for (i = 0; i < sd->add_def_class_count; i++) - { - if (sd->add_def_classid[i] == type2) - { - sd->add_def_classrate[i] += val; - break; - } - } - if (i >= sd->add_def_class_count - && sd->add_def_class_count < 10) - { - sd->add_def_classid[sd->add_def_class_count] = type2; - sd->add_def_classrate[sd->add_def_class_count] += val; - sd->add_def_class_count++; - } - } - break; - case SP_ADD_MDEF_CLASS: - if (sd->state.lr_flag != 2) - { - for (i = 0; i < sd->add_mdef_class_count; i++) - { - if (sd->add_mdef_classid[i] == type2) - { - sd->add_mdef_classrate[i] += val; - break; - } - } - if (i >= sd->add_mdef_class_count - && sd->add_mdef_class_count < 10) - { - sd->add_mdef_classid[sd->add_mdef_class_count] = type2; - sd->add_mdef_classrate[sd->add_mdef_class_count] += val; - sd->add_mdef_class_count++; - } - } - break; - case SP_HP_DRAIN_RATE: - if (!sd->state.lr_flag) - { - sd->hp_drain_rate += type2; - sd->hp_drain_per += val; - } - else if (sd->state.lr_flag == 1) - { - sd->hp_drain_rate_ += type2; - sd->hp_drain_per_ += val; - } - break; - case SP_SP_DRAIN_RATE: - if (!sd->state.lr_flag) - { - sd->sp_drain_rate += type2; - sd->sp_drain_per += val; - } - else if (sd->state.lr_flag == 1) - { - sd->sp_drain_rate_ += type2; - sd->sp_drain_per_ += val; - } - 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; - } // end addition - default: - if (battle_config.error_log) - printf ("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; - switch (type) - { - case SP_ADD_MONSTER_DROP_ITEM: - if (sd->state.lr_flag != 2) - { - for (i = 0; i < sd->monster_drop_item_count; i++) - { - if (sd->monster_drop_itemid[i] == type2) - { - sd->monster_drop_race[i] |= 1 << type3; - if (sd->monster_drop_itemrate[i] < val) - sd->monster_drop_itemrate[i] = val; - break; - } - } - if (i >= sd->monster_drop_item_count - && sd->monster_drop_item_count < 10) - { - sd->monster_drop_itemid[sd->monster_drop_item_count] = - type2; - sd->monster_drop_race[sd->monster_drop_item_count] |= - 1 << type3; - sd->monster_drop_itemrate[sd->monster_drop_item_count] = - val; - sd->monster_drop_item_count++; - } - } - break; - case SP_AUTOSPELL: - if (sd->state.lr_flag != 2) - { - sd->autospell_id = type2; - sd->autospell_lv = type3; - sd->autospell_rate = val; - } - break; - default: - if (battle_config.error_log) - printf ("pc_bonus3: unknown type %d %d %d %d!\n", type, type2, - type3, 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) - printf ("support card skill only!\n"); - return 0; - } - if (!flag && (sd->status.skill[id].id == id || level == 0)) - { // クエスト所得ならここで条件を確認して送信する - sd->status.skill[id].lv = level; - pc_calcstatus (sd, 0); - clif_skillinfoblock (sd); - } - else if (sd->status.skill[id].lv < level) - { // 覚えられるがlvが小さいなら - sd->status.skill[id].id = id; - sd->status.skill[id].lv = level; - } - - return 0; -} - -/*========================================== - * カード挿入 - *------------------------------------------ - */ -int pc_insert_card (struct map_session_data *sd, int idx_card, int idx_equip) -{ - nullpo_retr (0, sd); - - if (idx_card >= 0 && idx_card < MAX_INVENTORY && idx_equip >= 0 - && idx_equip < MAX_INVENTORY && sd->inventory_data[idx_card]) - { - int i; - int nameid = sd->status.inventory[idx_equip].nameid; - int cardid = sd->status.inventory[idx_card].nameid; - int ep = sd->inventory_data[idx_card]->equip; - - if (nameid <= 0 || sd->inventory_data[idx_equip] == NULL || - (sd->inventory_data[idx_equip]->type != 4 && sd->inventory_data[idx_equip]->type != 5) || // 装 備じゃない - (sd->status.inventory[idx_equip].identify == 0) || // 未鑑定 - (sd->status.inventory[idx_equip].card[0] == 0x00ff) || // 製造武器 - (sd->status.inventory[idx_equip].card[0] == 0x00fe) || - ((sd->inventory_data[idx_equip]->equip & ep) == 0) || // 装 備個所違い - (sd->inventory_data[idx_equip]->type == 4 && ep == 32) || // 両 手武器と盾カード - (sd->status.inventory[idx_equip].card[0] == (short) 0xff00) - || 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; - } - } - } - else - 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); - - 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; -} - -int pc_count_all_items (struct map_session_data *player, int item_id) -{ - int i; - int count = 0; - - nullpo_retr (0, player); - - for (i = 0; i < MAX_INVENTORY; i++) - { - if (player->status.inventory[i].nameid == item_id) - count += player->status.inventory[i].amount; - } - - return count; -} - -int pc_remove_items (struct map_session_data *player, int item_id, int count) -{ - int i; - - nullpo_retr (0, player); - - for (i = 0; i < MAX_INVENTORY && count; i++) - { - if (player->status.inventory[i].nameid == item_id) - { - int to_delete = count; - /* only delete as much as we have */ - if (to_delete > player->status.inventory[i].amount) - to_delete = player->status.inventory[i].amount; - - count -= to_delete; - - pc_delitem (player, i, to_delete, - 0 /* means `really delete and update status' */ ); - - if (!count) - return 0; - } - } - return 0; -} - -/*========================================== - * アイテム追加。個数のみitem構造体の数字を無視 - *------------------------------------------ - */ -int pc_additem (struct map_session_data *sd, struct item *item_data, - int amount) -{ - struct item_data *data; - int i, w; - - MAP_LOG_PC (sd, "PICKUP %d %d", item_data->nameid, amount); - - 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 ((w = data->weight * amount) + sd->weight > sd->max_weight) - 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 (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) - { - memcpy (&sd->status.inventory[i], item_data, - sizeof (sd->status.inventory[0])); - - if (item_data->equip) - sd->status.inventory[i].equip = 0; - - sd->status.inventory[i].amount = amount; - sd->inventory_data[i] = data; - clif_additem (sd, i, amount, 0); - } - else - return 4; - } - sd->weight += 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->trade_partner != 0) - trade_tradecancel (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, 0); - 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 (sd->trade_partner != 0 || sd->npc_id != 0 || sd->state.storage_flag) - return 0; // no dropping while trading/npc/storage - - if (n < 0 || n >= MAX_INVENTORY) - return 0; - - if (amount <= 0) - return 0; - - pc_unequipinvyitem (sd, n, 0); - - if (sd->status.inventory[n].nameid <= 0 || - sd->status.inventory[n].amount < amount || - sd->trade_partner != 0 || sd->status.inventory[n].amount <= 0) - return 1; - map_addflooritem (&sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, - sd->bl.y, NULL, NULL, NULL, 0); - pc_delitem (sd, n, amount, 0); - - return 0; -} - -/*========================================== - * アイテムを拾う - *------------------------------------------ - */ - -static int can_pick_item_up_from (struct map_session_data *self, int other_id) -{ - struct party *p = party_search (self->status.party_id); - - /* From ourselves or from no-one? */ - if (!self || self->bl.id == other_id || !other_id) - return 1; - - struct map_session_data *other = map_id2sd (other_id); - - /* Other no longer exists? */ - if (!other) - return 1; - - /* From our partner? */ - if (self->status.partner_id == other->status.char_id) - return 1; - - /* From a party member? */ - if (self->status.party_id - && self->status.party_id == other->status.party_id - && p && p->item != 0) - return 1; - - /* From someone who is far away? */ - /* On another map? */ - if (other->bl.m != self->bl.m) - return 1; - else - { - int distance_x = abs (other->bl.x - self->bl.x); - int distance_y = abs (other->bl.y - self->bl.y); - int distance = (distance_x > distance_y) ? distance_x : distance_y; - - return distance > battle_config.drop_pickup_safety_zone; - } -} - -int pc_takeitem (struct map_session_data *sd, struct flooritem_data *fitem) -{ - int flag; - unsigned int tick = gettick (); - int can_take; - - nullpo_retr (0, sd); - nullpo_retr (0, fitem); - - /* Sometimes the owners reported to us are buggy: */ - - if (fitem->first_get_id == fitem->third_get_id - || fitem->second_get_id == fitem->third_get_id) - fitem->third_get_id = 0; - - if (fitem->first_get_id == fitem->second_get_id) - { - fitem->second_get_id = fitem->third_get_id; - fitem->third_get_id = 0; - } - - can_take = can_pick_item_up_from (sd, fitem->first_get_id); - if (!can_take) - can_take = fitem->first_get_tick <= tick - && can_pick_item_up_from (sd, fitem->second_get_id); - - if (!can_take) - can_take = fitem->second_get_tick <= tick - && can_pick_item_up_from (sd, fitem->third_get_id); - - if (!can_take) - can_take = fitem->third_get_tick <= tick; - - if (can_take) - { - /* Can pick up */ - - if ((flag = - pc_additem (sd, &fitem->item_data, fitem->item_data.amount))) - // 重量overで取得失敗 - clif_additem (sd, 0, 0, flag); - else - { - // 取得成功 - if (sd->attacktimer != -1) - pc_stopattack (sd); - clif_takeitem (&sd->bl, &fitem->bl); - map_clearflooritem (fitem->bl.id); - } - return 0; - } - - /* Otherwise, we can't pick up */ - clif_additem (sd, 0, 0, 6); - 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; - if (itemdb_type (nameid) != 0) - return 0; - if ((nameid == 605) && map[sd->bl.m].flag.gvg) - return 0; - if (nameid == 601 - && (map[sd->bl.m].flag.noteleport || map[sd->bl.m].flag.gvg)) - { - clif_skill_teleportmessage (sd, 0); - return 0; - } - - if (nameid == 602 && map[sd->bl.m].flag.noreturn) - return 0; - if (nameid == 604 - && (map[sd->bl.m].flag.nobranch || map[sd->bl.m].flag.gvg)) - return 0; - if (item->sex != 2 && sd->status.sex != item->sex) - return 0; - if (item->elv > 0 && sd->status.base_level < item->elv) - return 0; - - return 1; -} - -/*========================================== - * アイテムを使う - *------------------------------------------ - */ -int pc_useitem (struct map_session_data *sd, int n) -{ - int nameid, amount; - - nullpo_retr (1, sd); - - if (n >= 0 && n < MAX_INVENTORY && sd->inventory_data[n]) - { - nameid = sd->status.inventory[n].nameid; - amount = sd->status.inventory[n].amount; - if (sd->status.inventory[n].nameid <= 0 - || sd->status.inventory[n].amount <= 0 - || sd->sc_data[SC_BERSERK].timer != -1 || !pc_isUseitem (sd, n)) - { - clif_useitemack (sd, n, 0, 0); - return 1; - } - - run_script (sd->inventory_data[n]->use_script, 0, sd->bl.id, 0); - - clif_useitemack (sd, n, amount - 1, 1); - pc_delitem (sd, n, 1, 1); - } - - 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 ((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_INVENTORY) - return 1; - - nullpo_retr (0, item_data = &sd->status.inventory[idx]); - - if (!pc_iscarton (sd)) - return 1; - if (item_data->nameid == 0 || item_data->amount < amount) - 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); - - if (idx < 0 || idx >= MAX_CART) - return -1; - - nullpo_retr (-1, item_data = &sd->status.cart[idx]); - - if (!pc_iscarton (sd)) - return -1; - 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) - return 1; - - nullpo_retr (0, item_data = &sd->status.cart[idx]); - - if (!pc_iscarton (sd)) - return 1; - if (item_data->nameid == 0 || item_data->amount < amount) - 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_item_identify (struct map_session_data *sd, int idx) -{ - int flag = 1; - - nullpo_retr (0, 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); - } - else - clif_item_identified (sd, idx, flag); - - return !flag; -} - -/*========================================== - * スティル品公開 - *------------------------------------------ - */ -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) -{ - if (sd != NULL && bl != NULL && bl->type == BL_MOB) - { - int i, skill, rate, itemid, flag, count; - struct mob_data *md; - md = (struct mob_data *) bl; - if (!md->state.steal_flag && mob_db[md->mob_class].mexp <= 0 && - !(mob_db[md->mob_class].mode & 0x20) && - md->sc_data[SC_STONE].timer == -1 && - md->sc_data[SC_FREEZE].timer == -1 && - (!(md->mob_class > 1324 && md->mob_class < 1364))) // prevent stealing from treasure boxes [Valaris] - { - skill = sd->paramc[4] - mob_db[md->mob_class].dex - + pc_checkskill (sd, TF_STEAL) + 10; - - if (0 < skill) - { - for (count = 8; count <= 8 && count != 0; count--) - { - i = rand () % 8; - itemid = mob_db[md->mob_class].dropitem[i].nameid; - - if (itemid > 0 && itemdb_type (itemid) != 6) - { - rate = - (mob_db[md->mob_class].dropitem[i].p / - battle_config.item_rate_common * 100 * skill) / - 100; - - if (rand () % 10000 < rate) - { - struct item tmp_item; - memset (&tmp_item, 0, sizeof (tmp_item)); - tmp_item.nameid = itemid; - tmp_item.amount = 1; - tmp_item.identify = 1; - 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, 0); - } - - if (flag) - { - if (battle_config.show_steal_in_same_party) - { - party_foreachsamemap (pc_show_steal, sd, - 1, sd, - tmp_item.nameid, 1); - } - - clif_additem (sd, 0, 0, flag); - } - md->state.steal_flag = 1; - return 1; - } - } - } - } - } - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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 && md->sc_data - && md->sc_data[SC_STONE].timer == -1 - && md->sc_data[SC_FREEZE].timer == -1) - { - skill = pc_checkskill (sd, RG_STEALCOIN) * 10; - rate = - skill + (sd->status.base_level - mob_db[md->mob_class].lv) * 3 + - sd->paramc[4] * 2 + sd->paramc[5] * 2; - if (MRAND (1000) < rate) - { - pc_getzeny (sd, mob_db[md->mob_class].lv * 10 + MRAND (100)); - md->state.steal_coin_flag = 1; - return 1; - } - } - } - - return 0; -} - -// -// -// -/*========================================== - * PCの位置設定 - *------------------------------------------ - */ -int pc_setpos (struct map_session_data *sd, char *mapname_org, int x, int y, - int clrtype) -{ - char mapname[24]; - int m = 0, c = 0, disguise = 0; - - nullpo_retr (0, sd); - - if (sd->chatID) // チャットから出る - chat_leavechat (sd); - if (sd->trade_partner) // 取引を中断する - trade_tradecancel (sd); - if (sd->state.storage_flag == 1) - storage_storage_quit (sd); // 倉庫を開いてるなら保存する - 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); - - skill_castcancel (&sd->bl, 0); // 詠唱中断 - pc_stop_walking (sd, 0); // 歩行中断 - pc_stopattack (sd); // 攻撃中断 - - if (pc_issit (sd)) - { -// pc_setstand (sd); // [fate] Nothing wrong with warping while sitting - skill_gangsterparadise (sd, 0); - } - - if (sd->sc_data[SC_TRICKDEAD].timer != -1) - skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); - if (sd->status.option & 2) - skill_status_change_end (&sd->bl, SC_HIDING, -1); - if (sd->status.option & 4) - skill_status_change_end (&sd->bl, SC_CLOAKING, -1); - if (sd->status.option & 16386) - skill_status_change_end (&sd->bl, SC_CHASEWALK, -1); - if (sd->sc_data[SC_BLADESTOP].timer != -1) - skill_status_change_end (&sd->bl, SC_BLADESTOP, -1); - if (sd->sc_data[SC_DANCING].timer != -1) // clear dance effect when warping [Valaris] - skill_stop_dancing (&sd->bl, 0); - - if (sd->disguise) - { // clear disguises when warping [Valaris] - clif_clearchar (&sd->bl, 9); - disguise = sd->disguise; - sd->disguise = 0; - } - - memcpy (mapname, mapname_org, 24); - mapname[16] = 0; - if (strstr (mapname, ".gat") == NULL && strlen (mapname) < 16) - { - strcat (mapname, ".gat"); - } - - m = map_mapname2mapid (mapname); - if (m < 0) - { - if (sd->mapname[0]) - { - int ip, port; - if (map_mapname2ipport (mapname, &ip, &port) == 0) - { - skill_stop_dancing (&sd->bl, 1); - skill_unit_out_all (&sd->bl, gettick (), 1); - clif_clearchar_area (&sd->bl, clrtype & 0xffff); - skill_gangsterparadise (sd, 0); - map_delblock (&sd->bl); - memcpy (sd->mapname, mapname, 24); - sd->bl.x = x; - sd->bl.y = y; - sd->state.waitingdisconnect = 1; - pc_makesavestatus (sd); - //The storage close routines save the char data. [Skotlex] - if (!sd->state.storage_flag) - chrif_save (sd); - else if (sd->state.storage_flag == 1) - storage_storage_quit (sd); - else if (sd->state.storage_flag == 2) - storage_guild_storage_quit (sd, 1); - - chrif_changemapserver (sd, mapname, x, y, ip, port); - return 0; - } - } -#if 0 - clif_authfail_fd (sd->fd, 0); // cancel - clif_setwaitclose (sd->fd); -#endif - return 1; - } - - if (x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys) - x = y = 0; - if ((x == 0 && y == 0) || (c = read_gat (m, x, y)) == 1 || c == 5) - { - if (x || y) - { - if (battle_config.error_log) - printf ("stacked (%d,%d)\n", x, y); - } - do - { - x = MRAND (map[m].xs - 2) + 1; - y = MRAND (map[m].ys - 2) + 1; - } - while ((c = read_gat (m, x, y)) == 1 || c == 5); - } - - if (sd->mapname[0] && sd->bl.prev != NULL) - { - skill_unit_out_all (&sd->bl, gettick (), 1); - clif_clearchar_area (&sd->bl, clrtype & 0xffff); - skill_gangsterparadise (sd, 0); - map_delblock (&sd->bl); - clif_changemap (sd, map[m].name, x, y); // [MouseJstr] - } - - if (disguise) // disguise teleport fix [Valaris] - sd->disguise = disguise; - - memcpy (sd->mapname, mapname, 24); - 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; - -// map_addblock(&sd->bl); // ブロック登録とspawnは -// clif_spawnpc(sd); - - return 0; -} - -/*========================================== - * PCのランダムワープ - *------------------------------------------ - */ -int pc_randomwarp (struct map_session_data *sd, int type) -{ - int x, y, c, i = 0; - int m; - - nullpo_retr (0, sd); - - m = sd->bl.m; - - if (map[sd->bl.m].flag.noteleport) // テレポート禁止 - return 0; - - do - { - x = MRAND (map[m].xs - 2) + 1; - y = MRAND (map[m].ys - 2) + 1; - } - while (((c = read_gat (m, x, y)) == 1 || c == 5) && (i++) < 1000); - - if (i < 1000) - pc_setpos (sd, map[m].name, 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 (strcmp (sd->status.memo_point[j].map, map[sd->bl.m].name) == 0) - { - 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; - } - memcpy (sd->status.memo_point[i].map, map[sd->bl.m].name, 24); - 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; -} - -/*========================================== - * - *------------------------------------------ - */ -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 void pc_walk (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd; - int i, ctype; - int moveblock; - int x, y, dx, dy; - - sd = map_id2sd (id); - if (sd == NULL) - return; - - if (sd->walktimer != tid) - { - if (battle_config.error_log) - printf ("pc_walk %d != %d\n", sd->walktimer, tid); - return; - } - sd->walktimer = -1; - if (sd->walkpath.path_pos >= sd->walkpath.path_len - || sd->walkpath.path_pos != data) - return; - - //歩いたので息吹のタイマーを初期化 - 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; - } - } - else - { // マス目境界へ到着 - if (sd->walkpath.path[sd->walkpath.path_pos] >= 8) - return; - - x = sd->bl.x; - y = sd->bl.y; - ctype = map_getcell (sd->bl.m, x, y); - if (ctype == 1 || ctype == 5) - { - pc_stop_walking (sd, 1); - return; - } - sd->dir = sd->head_dir = sd->walkpath.path[sd->walkpath.path_pos]; - dx = dirx[(int) sd->dir]; - dy = diry[(int) sd->dir]; - ctype = map_getcell (sd->bl.m, x + dx, y + dy); - if (ctype == 1 || ctype == 5) - { - pc_walktoxy_sub (sd); - return; - } - - moveblock = (x / BLOCK_SIZE != (x + dx) / BLOCK_SIZE - || y / BLOCK_SIZE != (y + dy) / BLOCK_SIZE); - - sd->walktimer = 1; - map_foreachinmovearea (clif_pcoutsight, sd->bl.m, x - AREA_SIZE, - y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, - dx, dy, 0, sd); - - x += dx; - y += dy; - - if (moveblock) - map_delblock (&sd->bl); - sd->bl.x = x; - sd->bl.y = y; - if (moveblock) - map_addblock (&sd->bl); - - 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); - - map_foreachinmovearea (clif_pcinsight, sd->bl.m, x - AREA_SIZE, - y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, - -dx, -dy, 0, sd); - sd->walktimer = -1; - - if (sd->status.party_id > 0) - { // パーティのHP情報通知検査 - struct party *p = party_search (sd->status.party_id); - if (p != NULL) - { - int p_flag = 0; - map_foreachinmovearea (party_send_hp_check, sd->bl.m, - x - AREA_SIZE, y - AREA_SIZE, - x + AREA_SIZE, y + AREA_SIZE, -dx, -dy, - BL_PC, sd->status.party_id, &p_flag); - if (p_flag) - sd->party_hp = -1; - } - } - if (sd->status.option & 4) // クローキングの消滅検査 - skill_check_cloaking (&sd->bl); - // ディボーション検査 - for (i = 0; i < 5; i++) - if (sd->dev.val1[i]) - { - skill_devotion3 (&sd->bl, sd->dev.val1[i]); - break; - } - // 被ディボーション検査 - if (sd->sc_data && sd->sc_data[SC_DEVOTION].val1) - { - skill_devotion2 (&sd->bl, sd->sc_data[SC_DEVOTION].val1); - } - - skill_unit_move (&sd->bl, tick, 1); // スキルユニットの検査 - - if (map_getcell (sd->bl.m, x, y) & 0x80) - 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); - } -} - -/*========================================== - * 移動可能か確認して、可能なら歩行開始 - *------------------------------------------ - */ -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 (pc_issit (sd)) - pc_setstand (sd); - - if (sd->walktimer != -1 && sd->state.change_walk_target == 0) - { - // 現在歩いている最中の目的地変更なのでマス目の中心に来た時に - // timer関数からpc_walktoxy_subを呼ぶようにする - sd->state.change_walk_target = 1; - } - else - { - pc_walktoxy_sub (sd); - } - - return 0; -} - -/*========================================== - * 歩 行停止 - *------------------------------------------ - */ -int pc_stop_walking (struct map_session_data *sd, int type) -{ - nullpo_retr (0, sd); - - if (sd->walktimer != -1) - { - 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 (type & 0x02 && battle_config.pc_damage_delay) - { - unsigned int tick = gettick (); - int delay = battle_get_dmotion (&sd->bl); - if (sd->canmove_tick < tick) - sd->canmove_tick = tick + delay; - } - - return 0; -} - -void pc_touch_all_relevant_npcs (struct map_session_data *sd) -{ - if (map_getcell (sd->bl.m, sd->bl.x, sd->bl.y) & 0x80) - npc_touch_areanpc (sd, sd->bl.m, sd->bl.x, sd->bl.y); - else - sd->areanpc_id = 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int pc_movepos (struct map_session_data *sd, int dst_x, int dst_y) -{ - int moveblock; - int dx, dy, dist; - - struct walkpath_data wpd; - - nullpo_retr (0, sd); - - if (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; - dist = distance (sd->bl.x, sd->bl.y, dst_x, dst_y); - - moveblock = (sd->bl.x / BLOCK_SIZE != dst_x / BLOCK_SIZE - || sd->bl.y / BLOCK_SIZE != dst_y / BLOCK_SIZE); - - 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, 0, sd); - - if (moveblock) - map_delblock (&sd->bl); - sd->bl.x = dst_x; - sd->bl.y = dst_y; - if (moveblock) - map_addblock (&sd->bl); - - 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, 0, sd); - - if (sd->status.party_id > 0) - { // パーティのHP情報通知検査 - struct party *p = party_search (sd->status.party_id); - if (p != NULL) - { - int flag = 0; - map_foreachinmovearea (party_send_hp_check, 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_PC, sd->status.party_id, - &flag); - if (flag) - sd->party_hp = -1; - } - } - - if (sd->status.option & 4) // クローキングの消滅検査 - skill_check_cloaking (&sd->bl); - - skill_unit_move (&sd->bl, gettick (), dist + 7); // スキルユニットの検査 - - pc_touch_all_relevant_npcs (sd); - 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_data == NULL) - return 0; - - if (!(skill_get_weapontype (KN_TWOHANDQUICKEN) & (1 << sd->status.weapon)) - && sd->sc_data[SC_TWOHANDQUICKEN].timer != -1) - { // 2HQ - skill_status_change_end (&sd->bl, SC_TWOHANDQUICKEN, -1); // 2HQを解除 - return -1; - } - if (!(skill_get_weapontype (LK_AURABLADE) & (1 << sd->status.weapon)) - && sd->sc_data[SC_AURABLADE].timer != -1) - { // オーラブレード - skill_status_change_end (&sd->bl, SC_AURABLADE, -1); // オーラブレードを解除 - return -1; - } - if (!(skill_get_weapontype (LK_PARRYING) & (1 << sd->status.weapon)) - && sd->sc_data[SC_PARRYING].timer != -1) - { // パリイング - skill_status_change_end (&sd->bl, SC_PARRYING, -1); // パリイングを解除 - return -1; - } - if (!(skill_get_weapontype (LK_CONCENTRATION) & (1 << sd->status.weapon)) - && sd->sc_data[SC_CONCENTRATION].timer != -1) - { // コンセントレーション - skill_status_change_end (&sd->bl, SC_CONCENTRATION, -1); // コンセントレーションを解除 - return -1; - } - if (!(skill_get_weapontype (CR_SPEARQUICKEN) & (1 << sd->status.weapon)) - && sd->sc_data[SC_SPEARSQUICKEN].timer != -1) - { // スピアクィッケン - skill_status_change_end (&sd->bl, SC_SPEARSQUICKEN, -1); // スピアクイッケンを解除 - return -1; - } - if (!(skill_get_weapontype (BS_ADRENALINE) & (1 << sd->status.weapon)) - && sd->sc_data[SC_ADRENALINE].timer != -1) - { // アドレナリンラッシュ - skill_status_change_end (&sd->bl, SC_ADRENALINE, -1); // アドレナリンラッシュを解除 - return -1; - } - - if (sd->status.shield <= 0) - { - if (sd->sc_data[SC_AUTOGUARD].timer != -1) - { // オートガード - skill_status_change_end (&sd->bl, SC_AUTOGUARD, -1); - return -1; - } - if (sd->sc_data[SC_DEFENDER].timer != -1) - { // ディフェンダー - skill_status_change_end (&sd->bl, SC_DEFENDER, -1); - return -1; - } - if (sd->sc_data[SC_REFLECTSHIELD].timer != -1) - { //リフレクトシールド - skill_status_change_end (&sd->bl, SC_REFLECTSHIELD, -1); - return -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 < MAX_PC_CLASS) - { //通常 - bj.job = b_class; - bj.upper = 0; - } - else if (b_class >= 4001 && b_class < 4023) - { //転生職 - bj.job = b_class - 4001; - bj.upper = 1; - } - else if (b_class == 23 + 4023 - 1) - { //養子スパノビ - bj.job = b_class - (4023 - 1); - bj.upper = 2; - } - else - { //養子スパノビ以外の養子 - bj.job = b_class - 4023; - bj.upper = 2; - } - - if (battle_config.enable_upper_class == 0) - { //confで無効になっていたらupper=0 - bj.upper = 0; - } - - if (bj.job == 0) - { - bj.type = 0; - } - else if (bj.job < 7) - { - bj.type = 1; - } - else - { - bj.type = 2; - } - - return bj; -} - -/*========================================== - * PCの攻撃 (timer関数) - *------------------------------------------ - */ -void pc_attack_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd; - struct block_list *bl; - struct status_change *sc_data; - short *opt; - int dist, skill, range; - int attack_spell_delay; - - sd = map_id2sd (id); - if (sd == NULL) - return; - if (sd->attacktimer != tid) - { - if (battle_config.error_log) - printf ("pc_attack_timer %d != %d\n", sd->attacktimer, tid); - return; - } - sd->attacktimer = -1; - - if (sd->bl.prev == NULL) - return; - - bl = map_id2bl (sd->attacktarget); - if (bl == NULL || bl->prev == NULL) - return; - - if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) - return; - - // 同じmapでないなら攻撃しない - // PCが死んでても攻撃しない - if (sd->bl.m != bl->m || pc_isdead (sd)) - return; - - if (sd->opt1 > 0 || sd->status.option & 2 || sd->status.option & 16388) // 異常などで攻撃できない - return; - - if (sd->sc_data[SC_AUTOCOUNTER].timer != -1) - return; - if (sd->sc_data[SC_BLADESTOP].timer != -1) - return; - - if ((opt = battle_get_option (bl)) != NULL && *opt & 0x46) - return; - if (((sc_data = battle_get_sc_data (bl)) != NULL - && sc_data[SC_TRICKDEAD].timer != -1) - || ((sc_data = battle_get_sc_data (bl)) != NULL - && sc_data[SC_BASILICA].timer != -1)) - return; - - if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) <= 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->attackabletime > tick) - return; // cannot attack yet - - attack_spell_delay = sd->attack_spell_delay; - if (sd->attack_spell_override // [Fate] If we have an active attack spell, use that - && spell_attack (id, sd->attacktarget)) - { - // Return if the spell succeeded. If the spell had disspiated, spell_attack() may fail. - sd->attackabletime = tick + attack_spell_delay; - - } - else - { - dist = distance (sd->bl.x, sd->bl.y, bl->x, bl->y); - range = sd->attackrange; - if (sd->status.weapon != 11) - range++; - if (dist > range) - { // 届 かないので移動 - //if(pc_can_reach(sd,bl->x,bl->y)) - //clif_movetoattack(sd,bl); - return; - } - - if (dist <= range && !battle_check_range (&sd->bl, bl, range)) - { - if (pc_can_reach (sd, bl->x, bl->y) && sd->canmove_tick < tick - && (sd->sc_data[SC_ANKLE].timer == -1 - || sd->sc_data[SC_SPIDERWEB].timer == -1)) - // TMW client doesn't support this - //pc_walktoxy(sd,bl->x,bl->y); - clif_movetoattack (sd, bl); - sd->attackabletime = tick + (sd->aspd << 1); - } - else - { - 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 (sd->sc_data[SC_COMBO].timer == -1) - { - map_freeblock_lock (); - pc_stop_walking (sd, 0); - 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) - skill_status_change_end (&sd->bl, SC_CLOAKING, -1); - 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); - } - else if (sd->attackabletime <= tick) - { - 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->attackabletime <= tick) - sd->attackabletime = tick + (battle_config.max_aspd << 1); - } - } - - if (sd->state.attack_continue) - { - sd->attacktimer = - add_timer (sd->attackabletime, pc_attack_timer, sd->bl.id, 0); - } -} - -/*========================================== - * 攻撃要求 - * typeが1なら継続攻撃 - *------------------------------------------ - */ -int pc_attack (struct map_session_data *sd, int target_id, int type) -{ - struct block_list *bl; - int d; - - 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, RFIFOL (sd->fd, 2)); - return 0; - } - - if (!battle_check_target (&sd->bl, bl, BCT_ENEMY)) - return 1; - if (sd->attacktimer != -1) - pc_stopattack (sd); - sd->attacktarget = target_id; - sd->state.attack_continue = type; - - d = DIFF_TICK (sd->attackabletime, gettick ()); - if (d > 0 && d < 2000) - { // 攻撃delay中 - sd->attacktimer = - add_timer (sd->attackabletime, pc_attack_timer, sd->bl.id, 0); - } - else - { - // 本来timer関数なので引数を合わせる - 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; -} - -void pc_follow_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd, *bl; - - sd = map_id2sd (id); - if (sd == NULL || sd->followtimer != tid) - return; - - sd->followtimer = -1; - - do - { - if (sd->bl.prev == NULL) - break; - - bl = (struct map_session_data *) map_id2bl (sd->followtarget); - - if (bl == NULL) - return; - - if (bl->bl.prev == NULL) - break; - - if (bl->bl.type == BL_PC - && pc_isdead ((struct map_session_data *) bl)) - return; - - if (sd->skilltimer == -1 && sd->attacktimer == -1 - && sd->walktimer == -1) - { - if ((sd->bl.m == bl->bl.m) - && pc_can_reach (sd, bl->bl.x, bl->bl.y)) - { - if (distance (sd->bl.x, sd->bl.y, bl->bl.x, bl->bl.y) > 5) - pc_walktoxy (sd, bl->bl.x, bl->bl.y); - } - else - pc_setpos ((struct map_session_data *) sd, bl->mapname, - bl->bl.x, bl->bl.y, 3); - } - } - while (0); - - sd->followtimer = - add_timer (tick + sd->aspd, pc_follow_timer, sd->bl.id, 0); -} - -int pc_follow (struct map_session_data *sd, int target_id) -{ - struct block_list *bl; - - bl = map_id2bl (target_id); - if (bl == NULL) - return 1; - sd->followtarget = target_id; - if (sd->followtimer != -1) - { - delete_timer (sd->followtimer, pc_follow_timer); - sd->followtimer = -1; - } - - 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) - { - struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); - - // base側レベルアップ処理 - sd->status.base_exp -= next; - - sd->status.base_level++; - sd->status.status_point += (sd->status.base_level + 14) / 4; - clif_updatestatus (sd, SP_STATUSPOINT); - clif_updatestatus (sd, SP_BASELEVEL); - clif_updatestatus (sd, SP_NEXTBASEEXP); - pc_calcstatus (sd, 0); - pc_heal (sd, sd->status.max_hp, sd->status.max_sp); - - //スパノビはキリエ、イムポ、マニピ、グロ、サフラLv1がかかる - if (s_class.job == 23) - { - skill_status_change_start (&sd->bl, - SkillStatusChangeTable[PR_KYRIE], 1, 0, - 0, 0, skill_get_time (PR_KYRIE, 1), 0); - skill_status_change_start (&sd->bl, - SkillStatusChangeTable[PR_IMPOSITIO], - 1, 0, 0, 0, - skill_get_time (PR_IMPOSITIO, 1), 0); - skill_status_change_start (&sd->bl, - SkillStatusChangeTable[PR_MAGNIFICAT], - 1, 0, 0, 0, - skill_get_time (PR_MAGNIFICAT, 1), 0); - skill_status_change_start (&sd->bl, - SkillStatusChangeTable[PR_GLORIA], 1, - 0, 0, 0, skill_get_time (PR_GLORIA, 1), - 0); - skill_status_change_start (&sd->bl, - SkillStatusChangeTable[PR_SUFFRAGIUM], - 1, 0, 0, 0, - skill_get_time (PR_SUFFRAGIUM, 1), 0); - } - - clif_misceffect (&sd->bl, 0); - //レベルアップしたのでパーティー情報を更新する - //(公平範囲チェック) - party_send_movemap (sd); - MAP_LOG_XP (sd, "LEVELUP") return 1; - } - - return 0; -} - -/*======================================== - * Compute the maximum for sd->skill_point, i.e., the max. number of skill points that can still be filled in - *---------------------------------------- - */ -int pc_skillpt_potential (struct map_session_data *sd) -{ - int skill_id; - int potential = 0; - -#define RAISE_COST(x) (((x)*((x)-1))>>1) - - for (skill_id = 0; skill_id < MAX_SKILL; skill_id++) - if (sd->status.skill[skill_id].id != 0 - && sd->status.skill[skill_id].lv < skill_db[skill_id].max_raise) - potential += RAISE_COST (skill_db[skill_id].max_raise) - - RAISE_COST (sd->status.skill[skill_id].lv); -#undef RAISE_COST - - return potential; -} - -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) - { - if (pc_skillpt_potential (sd) < sd->status.skill_point) - { // [Fate] Bah, this is is painful. - // But the alternative is quite error-prone, and eAthena has far worse performance issues... - sd->status.job_exp = next - 1; - pc_calcstatus(sd,0); - return 0; - } - - // job側レベルアップ処理 - sd->status.job_exp -= next; - clif_updatestatus (sd, SP_NEXTJOBEXP); - sd->status.skill_point++; - clif_updatestatus (sd, SP_SKILLPOINT); - pc_calcstatus (sd, 0); - - MAP_LOG_PC (sd, "SKILLPOINTS-UP %d", sd->status.skill_point); - - if (sd->status.job_level < 250 - && sd->status.job_level < sd->status.base_level * 2) - sd->status.job_level++; // Make levelling up a little harder - - clif_misceffect (&sd->bl, 1); - return 1; - } - - return 0; -} - -/*========================================== - * 経験値取得 - *------------------------------------------ - */ -int pc_gainexp (struct map_session_data *sd, int base_exp, int job_exp) -{ - return pc_gainexp_reason (sd, base_exp, job_exp, - PC_GAINEXP_REASON_KILLING); -} - -int pc_gainexp_reason (struct map_session_data *sd, int base_exp, int job_exp, - int reason) -{ - char output[256]; - 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 - - MAP_LOG_PC (sd, "GAINXP %d %d %s", base_exp, job_exp, - ((reason == - 2) ? "SCRIPTXP" : ((reason == 1) ? "HEALXP" : "KILLXP"))); - - if (sd->sc_data[SC_RICHMANKIM].timer != -1) - { // added bounds checking [Vaalris] - base_exp += - base_exp * (25 + sd->sc_data[SC_RICHMANKIM].val1 * 25) / 100; - job_exp += - job_exp * (25 + sd->sc_data[SC_RICHMANKIM].val1 * 25) / 100; - } - - 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)) - { - while (sd->status.base_exp + base_exp >= pc_nextbaseafter (sd) - && sd->status.base_exp <= pc_nextbaseexp (sd) - && pc_nextbaseafter (sd) > 0) - { - base_exp *= .90; - } - } - - sd->status.base_exp += base_exp; - - // [Fate] Adjust experience points that healers can extract from this character - if (reason != PC_GAINEXP_REASON_HEALING) - { - const int max_heal_xp = - 20 + (sd->status.base_level * sd->status.base_level); - - sd->heal_xp += base_exp; - if (sd->heal_xp > max_heal_xp) - sd->heal_xp = max_heal_xp; - } - - 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)) - { - while (sd->status.job_exp + job_exp >= pc_nextjobafter (sd) - && sd->status.job_exp <= pc_nextjobexp (sd) - && pc_nextjobafter (sd) > 0) - { - job_exp *= .90; - } - } - - 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 (battle_config.disp_experience) - { - sprintf (output, - "Experienced Gained Base:%d Job:%d", base_exp, job_exp); - clif_disp_onlyself (sd, output, strlen (output)); - } - - return 0; -} - -int pc_extract_healer_exp (struct map_session_data *sd, int max) -{ - int amount; - nullpo_retr (0, sd); - - amount = sd->heal_xp; - if (max < amount) - amount = max; - - sd->heal_xp -= amount; - return amount; -} - -/*========================================== - * base level側必要経験値計算 - *------------------------------------------ - */ -int pc_nextbaseexp (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; - - if (sd->status.pc_class == 0) - i = 0; - else if (sd->status.pc_class <= 6) - i = 1; - else if (sd->status.pc_class <= 22) - i = 2; - else if (sd->status.pc_class == 23) - i = 3; - else if (sd->status.pc_class == 4001) - i = 4; - else if (sd->status.pc_class <= 4007) - i = 5; - else - i = 6; - - return exp_table[i][sd->status.base_level - 1]; -} - -/*========================================== - * job level側必要経験値計算 - *------------------------------------------ - */ -int pc_nextjobexp (struct map_session_data *sd) -{ - // [fate] For normal levels, this ranges from 20k to 50k, depending on job level. - // Job level is at most twice the player's experience level (base_level). Levelling - // from 2 to 9 is 44 points, i.e., 880k to 2.2M job experience points (this is per - // skill, obviously.) - - return 20000 + sd->status.job_level * 150; -} - -/*========================================== - * 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; - - if (sd->status.pc_class == 0) - i = 0; - else if (sd->status.pc_class <= 6) - i = 1; - else if (sd->status.pc_class <= 22) - i = 2; - else if (sd->status.pc_class == 23) - i = 3; - else if (sd->status.pc_class == 4001) - i = 4; - else if (sd->status.pc_class <= 4007) - i = 5; - else - i = 6; - - 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; - - if (sd->status.pc_class == 0) - i = 7; - else if (sd->status.pc_class <= 6) - i = 8; - else if (sd->status.pc_class <= 22) - i = 9; - else if (sd->status.pc_class == 23) - i = 10; - else if (sd->status.pc_class == 4001) - i = 11; - else if (sd->status.pc_class <= 4007) - i = 12; - else - i = 13; - - 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 need, val = 0; - - nullpo_retr (0, sd); - - switch (type) - { - 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; - } - - need = pc_need_status_point (sd, type); - if (type < SP_STR || type > SP_LUK || need < 0 - || need > sd->status.status_point - || val >= battle_config.max_parameter) - { - clif_statusupack (sd, type, 0, val); - clif_updatestatus (sd, SP_STATUSPOINT); - return 1; - } - switch (type) - { - 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; - } - 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); - pc_calcstatus (sd, 0); - clif_statusupack (sd, type, 1, val); - - MAP_LOG_STATS (sd, "STATUP"); - - return 0; -} - -/*========================================== - * 能力値成長 - *------------------------------------------ - */ -int pc_statusup2 (struct map_session_data *sd, int type, int val) -{ - nullpo_retr (0, 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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 >= battle_config.max_parameter) - val = battle_config.max_parameter; - 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); - pc_calcstatus (sd, 0); - clif_statusupack (sd, type, 1, val); - MAP_LOG_STATS (sd, "STATUP2"); - - return 0; -} - -/*========================================== - * スキルポイント割り振り - *------------------------------------------ - */ -int pc_skillup (struct map_session_data *sd, int skill_num) -{ - nullpo_retr (0, sd); - - if (sd->status.skill[skill_num].id != 0 - && sd->status.skill_point >= sd->status.skill[skill_num].lv - && sd->status.skill[skill_num].lv < skill_db[skill_num].max_raise) - { - sd->status.skill_point -= sd->status.skill[skill_num].lv; - sd->status.skill[skill_num].lv++; - - pc_calcstatus (sd, 0); - clif_skillup (sd, skill_num); - clif_updatestatus (sd, SP_SKILLPOINT); - clif_skillinfoblock (sd); - MAP_LOG_PC(sd, "SKILLUP %d %d %d", - skill_num, sd->status.skill[skill_num].lv, skill_power(sd, skill_num)); - } - - return 0; -} - -/*========================================== - * /allskill - *------------------------------------------ - */ -int pc_allskillup (struct map_session_data *sd) -{ - int i, id; - int c = 0, s = 0; - //転生や養子の場合の元の職業を算出する - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - s_class = pc_calc_base_job (sd->status.pc_class); - c = s_class.job; - s = (s_class.upper == 1) ? 1 : 0; //転生以外は通常のスキル? - - for (i = 0; i < MAX_SKILL; i++) - sd->status.skill[i].id = 0; - - if (battle_config.gm_allskill > 0 - && pc_isGM (sd) >= battle_config.gm_allskill) - { - // 全てのスキル - for (i = 1; i < 158; i++) - sd->status.skill[i].lv = skill_get_max (i); - for (i = 210; i < 291; i++) - sd->status.skill[i].lv = skill_get_max (i); - for (i = 304; i < MAX_SKILL; i++) - sd->status.skill[i].lv = skill_get_max (i); - } - else - { - for (i = 0; (id = skill_tree[s][c][i].id) > 0; i++) - { - if (sd->status.skill[id].id == 0 && skill_get_inf2 (id) & 0x01) - sd->status.skill[id].lv = skill_get_max (id); - } - } - pc_calcstatus (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 = 0; - 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.pc_class == 4001) - sd->status.status_point = 100; - } - - 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], 1); - sd->equip_index[i] = -1; - } - } - - clif_skillinfoblock (sd); - pc_calcstatus (sd, 0); - - MAP_LOG_STATS (sd, "STATRESET"); - - return 0; -} - -/*========================================== - * /resetstate - *------------------------------------------ - */ -int pc_resetstate (struct map_session_data *sd) -{ -#define sumsp(a) ((a)*((a-2)/10+2) - 5*((a-2)/10)*((a-2)/10) - 6*((a-2)/10) -2) -// int add=0; // Removed by Dexity - - nullpo_retr (0, sd); - -// New statpoint table used here - Dexity - sd->status.status_point = atoi (statp[sd->status.base_level - 1]); -// End addition - -// Removed by Dexity - old count -// 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; - - clif_updatestatus (sd, SP_STATUSPOINT); - - 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 - - pc_calcstatus (sd, 0); - - return 0; -} - -/*========================================== - * /resetskill - *------------------------------------------ - */ -int pc_resetskill (struct map_session_data *sd) -{ - int i, skill; - - nullpo_retr (0, sd); - - sd->status.skill_point += pc_calc_skillpoint (sd); - - for (i = 1; i < MAX_SKILL; i++) - if ((skill = pc_checkskill (sd, i)) > 0) - { - sd->status.skill[i].lv = 0; - sd->status.skill[i].flags = 0; - } - - clif_updatestatus (sd, SP_SKILLPOINT); - clif_skillinfoblock (sd); - pc_calcstatus (sd, 0); - - return 0; -} - -/*========================================== - * pcにダメージを与える - *------------------------------------------ - */ -int pc_damage (struct block_list *src, struct map_session_data *sd, - int damage) -{ - int i = 0, j = 0; - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - //転生や養子の場合の元の職業を算出する - s_class = pc_calc_base_job (sd->status.pc_class); - // 既に死んでいたら無効 - if (pc_isdead (sd)) - return 0; - // 座ってたら立ち上がる - if (pc_issit (sd)) - { - pc_setstand (sd); - skill_gangsterparadise (sd, 0); - } - - if (src) - { - if (src->type == BL_PC) - { - MAP_LOG_PC (sd, "INJURED-BY PC%d FOR %d", - ((struct map_session_data *) src)->status.char_id, - damage); - } - else - { - MAP_LOG_PC (sd, "INJURED-BY MOB%d FOR %d", src->id, damage); - } - } - else - MAP_LOG_PC (sd, "INJURED-BY null FOR %d", damage); - - // 歩 いていたら足を止める - if (sd->sc_data[SC_ENDURE].timer == -1 - && !sd->special_state.infinite_endure) - pc_stop_walking (sd, 3); - // 演奏/ダンスの中断 - if (damage > sd->status.max_hp >> 2) - skill_stop_dancing (&sd->bl, 0); - - sd->status.hp -= damage; - - if (sd->sc_data[SC_TRICKDEAD].timer != -1) - skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); - if (sd->status.option & 2) - skill_status_change_end (&sd->bl, SC_HIDING, -1); - if (sd->status.option & 4) - skill_status_change_end (&sd->bl, SC_CLOAKING, -1); - if (sd->status.option & 16386) - skill_status_change_end (&sd->bl, SC_CHASEWALK, -1); - - if (sd->status.hp > 0) - { - // まだ生きているならHP更新 - clif_updatestatus (sd, SP_HP); - - if (sd->status.hp < sd->status.max_hp >> 2 - && pc_checkskill (sd, SM_AUTOBERSERK) > 0 - && (sd->sc_data[SC_PROVOKE].timer == -1 - || sd->sc_data[SC_PROVOKE].val2 == 0)) - // オートバーサーク発動 - skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, - 0); - - sd->canlog_tick = gettick (); - - if (sd->status.party_id > 0) - { // on-the-fly party hp updates [Valaris] - struct party *p = party_search (sd->status.party_id); - if (p != NULL) - clif_party_hp (p, sd); - } // end addition [Valaris] - - return 0; - } - - MAP_LOG_PC (sd, "DEAD%s", ""); - - // Character is dead! - - sd->status.hp = 0; - // [Fate] Stop quickregen - sd->quick_regeneration_hp.amount = 0; - sd->quick_regeneration_sp.amount = 0; - skill_update_heal_animation (sd); - - pc_setdead (sd); - - pc_stop_walking (sd, 0); - skill_castcancel (&sd->bl, 0); // 詠唱の中止 - clif_clearchar_area (&sd->bl, 1); - skill_unit_out_all (&sd->bl, gettick (), 1); - if (sd->sc_data[SC_BLADESTOP].timer != -1) //白刃は事前に解除 - skill_status_change_end (&sd->bl, SC_BLADESTOP, -1); - pc_setglobalreg (sd, "PC_DIE_COUNTER", ++sd->die_counter); //死にカウンター書き込み - skill_status_change_clear (&sd->bl, 0); // ステータス異常を解除する - clif_updatestatus (sd, SP_HP); - pc_calcstatus (sd, 0); - // [Fate] Reset magic - sd->cast_tick = gettick (); - magic_stop_completely (sd); - - for (i = 0; i < 5; i++) - if (sd->dev.val1[i]) - { - skill_status_change_end (&map_id2sd (sd->dev.val1[i])->bl, - SC_DEVOTION, -1); - sd->dev.val1[i] = sd->dev.val2[i] = 0; - } - - if (battle_config.death_penalty_type > 0 && sd->status.base_level >= 20) - { // changed penalty options, added death by player if pk_mode [Valaris] - if (!map[sd->bl.m].flag.nopenalty && !map[sd->bl.m].flag.gvg) - { - if (battle_config.death_penalty_type == 1 - && battle_config.death_penalty_base > 0) - sd->status.base_exp -= - (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 -= - (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 -= - (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 -= - (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 -= - (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 -= - (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 -= - (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 -= - (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 (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[MRAND (eq_num)]; //該当アイテムの中からランダム - if (MRAND (10000) < per) - { - if (sd->status.inventory[n].equip) - pc_unequipitem (sd, n, 0); - pc_dropitem (sd, n, 1); - } - } - } - else if (id > 0) - { - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].nameid == id //ItemIDが一致していて - && MRAND (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, 0); - 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; - if (src && src->type == BL_PC) - ((struct map_session_data *) src)->pvp_point++; - //} //fixed wrong '{' placement by Lupus - pc_setdead (sd); - } - // 強制送還 - if (sd->pvp_point < 0) - { - sd->pvp_point = 0; - 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); - } - } - //GvG - if (map[sd->bl.m].flag.gvg) - { - 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); - } - - if (src && src->type == BL_PC) - { - // [Fate] PK death, trigger scripts - argrec_t arg[3]; - arg[0].name = "@killerrid"; - arg[0].v.i = src->id; - arg[1].name = "@victimrid"; - arg[1].v.i = sd->bl.id; - arg[2].name = "@victimlvl"; - arg[2].v.i = sd->status.base_level; - npc_event_doall_l ("OnPCKilledEvent", sd->bl.id, 3, arg); - npc_event_doall_l ("OnPCKillEvent", src->id, 3, arg); - } - npc_event_doall_l ("OnPCDieEvent", sd->bl.id, 0, NULL); - - return 0; -} - -// -// script関 連 -// -/*========================================== - * script用PCステータス読み出し - *------------------------------------------ - */ -int pc_readparam (struct map_session_data *sd, int type) -{ - int val = 0; - struct pc_base_job s_class; - - s_class = pc_calc_base_job (sd->status.pc_class); - - 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.pc_class; - break; - case SP_UPPER: - val = s_class.upper; - 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_FAME: - val = sd->fame; - break; - } - - return val; -} - -/*========================================== - * script用PCステータス設定 - *------------------------------------------ - */ -int pc_setparam (struct map_session_data *sd, int type, int val) -{ - int i = 0, up_level = 50; - struct pc_base_job s_class; - - nullpo_retr (0, sd); - - s_class = pc_calc_base_job (sd->status.pc_class); - - switch (type) - { - case SP_BASELEVEL: - if (val > sd->status.base_level) - { - for (i = 1; i <= (val - sd->status.base_level); i++) - sd->status.status_point += - (sd->status.base_level + i + 14) / 4; - } - 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); - pc_calcstatus (sd, 0); - pc_heal (sd, sd->status.max_hp, sd->status.max_sp); - break; - case SP_JOBLEVEL: - if (sd->status.pc_class == 0) - up_level -= 40; - if ((sd->status.pc_class == 23) - || (sd->status.pc_class >= 4001 && sd->status.pc_class <= 4022)) - up_level += 20; - if (val >= 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); - pc_calcstatus (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); - pc_calcstatus (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: - sd->status.zeny = val; - 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_FAME: - sd->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_data && 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.party_id > 0) - { // on-the-fly party hp updates [Valaris] - struct party *p = party_search (sd->status.party_id); - if (p != NULL) - clif_party_hp (p, sd); - } // end addition [Valaris] - - return hp + sp; -} - -/*========================================== - * HP/SP回復 - *------------------------------------------ - */ -static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp); - -static int // Compute how quickly we regenerate (less is faster) for that amount -pc_heal_quick_speed (int amount) -{ - if (amount >= 100) - { - if (amount >= 500) - return 0; - if (amount >= 250) - return 1; - return 2; - } - else - { // < 100 - if (amount >= 50) - return 3; - if (amount >= 20) - return 4; - return 5; - } -} - -static void -pc_heal_quick_accumulate (int new_amount, - struct quick_regeneration *quick_regen, int max) -{ - int current_amount = quick_regen->amount; - int current_speed = quick_regen->speed; - int new_speed = pc_heal_quick_speed (new_amount); - - int average_speed = ((new_speed * new_amount) + (current_speed * current_amount)) / (current_amount + new_amount); // new_amount > 0, current_amount >= 0 - - quick_regen->speed = average_speed; - quick_regen->amount = MIN (current_amount + new_amount, max); - - quick_regen->tickdelay = MIN (quick_regen->speed, quick_regen->tickdelay); -} - -int pc_itemheal (struct map_session_data *sd, int hp, int sp) -{ - /* defer healing */ - if (hp > 0) - { - pc_heal_quick_accumulate (hp, - &sd->quick_regeneration_hp, - sd->status.max_hp - sd->status.hp); - hp = 0; - } - if (sp > 0) - { - pc_heal_quick_accumulate (sp, - &sd->quick_regeneration_sp, - sd->status.max_sp - sd->status.sp); - - sp = 0; - } - - /* Hurt right away, if necessary */ - if (hp < 0 || sp < 0) - pc_itemheal_effect (sd, hp, sp); - - return 0; -} - -/* pc_itemheal_effect is invoked once every 0.5s whenever the pc - * has health recovery queued up (cf. pc_natural_heal_sub). - */ -static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp) -{ - int bonus; -// if(battle_config.battle_log) -// printf("heal %d %d\n",hp,sp); - - nullpo_retr (0, sd); - - if (sd->sc_data && sd->sc_data[SC_GOSPEL].timer != -1) //バーサーク中は回復させないらしい - return 0; - - if (sd->state.potionpitcher_flag) - { - sd->potion_hp = hp; - sd->potion_sp = sp; - 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; - if (bonus != 100) - hp = hp * bonus / 100; - bonus = 100 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5; - if (bonus != 100) - hp = hp * bonus / 100; - } - if (sp > 0) - { - bonus = - (sd->paramc[3] << 1) + 100 + pc_checkskill (sd, - MG_SRECOVERY) * 10; - if (bonus != 100) - sp = sp * bonus / 100; - bonus = 100 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5; - 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 (sd->state.potionpitcher_flag) - { - sd->potion_per_hp = hp; - sd->potion_per_sp = sp; - return 0; - } - - 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 - *------------------------------------------ - */ -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.pc_class); - - nullpo_retr (0, sd); - - if ((job > 23) && (job < 68)) - job += 3977; - - if ((job > 69) && (job < 4000)) - return 1; - - if (upper < 0) //現在転生かどうかを判断する - upper = s_class.upper; - - if (upper == 0) - { //通常職ならjobそのまんま - b_class = job; - } - else if (upper == 1) - { - if (job == 23) - { //転生にスパノビは存在しないのでお断り - return 1; - } - else - { - b_class = job + 4001; - } - } - else if (upper == 2) - { //養子に結婚はないけどどうせ次で蹴られるからいいや - b_class = (job == 23) ? job + 4022 : job + 4023; - } - else - { - return 1; - } - - if ((sd->status.sex == 0 && job == 19) || (sd->status.sex == 1 && job == 20) || - (sd->status.sex == 0 && job == 4020) || (sd->status.sex == 1 && job == 4021) || - job == 22 || sd->status.pc_class == b_class) //♀はバードになれない、♂はダンサーになれない、結婚衣裳もお断り - return 1; - - sd->status.pc_class = sd->view_class = b_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], 1); // 装備外し - } - - clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); // move sprite update to prevent client crashes with incompatible equipment [Valaris] - if (sd->status.clothes_color > 0) - clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, - sd->status.clothes_color); - if (battle_config.muting_players && sd->status.manner < 0) - clif_changestatus (&sd->bl, SP_MANNER, sd->status.manner); - - pc_calcstatus (sd, 0); - pc_checkallowskill (sd); - pc_equiplookall (sd); - clif_equiplist (sd); - - 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 | -0x0000); - if (pc_checkskill (sd, KN_RIDING) > 0) - pc_setriding (sd); - } - - return 0; -} - -/*========================================== - * 見た目変更 - *------------------------------------------ - */ -int pc_equiplookall (struct map_session_data *sd) -{ - nullpo_retr (0, sd); - - clif_changelook (&sd->bl, LOOK_WEAPON, 0); -// clif_changelook(&sd->bl,LOOK_SHOES,0); - 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); - - clif_changelook_accessories (&sd->bl, NULL); - - return 0; -} - -/*========================================== - * 見た目変更 - *------------------------------------------ - */ -int pc_changelook (struct map_session_data *sd, int type, int val) -{ - nullpo_retr (0, sd); - - switch (type) - { - case LOOK_HAIR: - sd->status.hair = val; - 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: - sd->status.hair_color = val; - break; - case LOOK_CLOTHES_COLOR: - sd->status.clothes_color = val; - break; - case LOOK_SHIELD: - sd->status.shield = val; - break; - case LOOK_SHOES: - break; - } - clif_changelook (&sd->bl, type, val); - - return 0; -} - -/*========================================== - * 付属品(鷹,ペコ,カート)設定 - *------------------------------------------ - */ -int pc_setoption (struct map_session_data *sd, int type) -{ - nullpo_retr (0, sd); - - sd->status.option = type; - clif_changeoption (&sd->bl); - pc_calcstatus (sd, 0); - - return 0; -} - -/*========================================== - * カート設定 - *------------------------------------------ - */ -int pc_setcart (struct map_session_data *sd, int type) -{ - int cart[6] = { 0x0000, 0x0008, 0x0080, 0x0100, 0x0200, 0x0400 }; - - nullpo_retr (0, sd); - - if (pc_checkskill (sd, MC_PUSHCART) > 0) - { // プッシュカートスキル所持 - if (!pc_iscarton (sd)) - { // カートを付けていない - pc_setoption (sd, cart[type]); - clif_cart_itemlist (sd); - clif_cart_equiplist (sd); - clif_updatestatus (sd, SP_CARTINFO); - clif_status_change (&sd->bl, 0x0c, 0); - } - else - { - pc_setoption (sd, cart[type]); - } - } - - 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 (sd->disguise > 0) - { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] - clif_displaymessage (sd->fd, - "Cannot mount a Peco while in disguise."); - return 0; - } - - if ((pc_checkskill (sd, KN_RIDING) > 0)) - { // ライディングスキル所持 - pc_setoption (sd, sd->status.option | 0x0020); - - if (sd->status.pc_class == 7) - sd->status.pc_class = sd->view_class = 13; - - if (sd->status.pc_class == 14) - sd->status.pc_class = sd->view_class = 21; - - if (sd->status.pc_class == 4008) - sd->status.pc_class = sd->view_class = 4014; - - if (sd->status.pc_class == 4015) - sd->status.pc_class = sd->view_class = 4022; - } - - return 0; -} - -/*========================================== - * 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++; - RECREATE (sd->reg, struct script_reg, sd->reg_num); - 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)) - { - printf ("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++; - RECREATE (sd->regstr, struct script_regstr, sd->regstr_num); - sd->regstr[i].index = reg; - strcpy (sd->regstr[i].data, str); - - return 0; -} - -/*========================================== - * script用グローバル変数の値を読む - *------------------------------------------ - */ -int pc_readglobalreg (struct map_session_data *sd, char *reg) -{ - int i; - - nullpo_retr (0, sd); - - for (i = 0; i < sd->status.global_reg_num; i++) - { - if (strcmp (sd->status.global_reg[i].str, reg) == 0) - return sd->status.global_reg[i].value; - } - - return 0; -} - -/*========================================== - * script用グローバル変数の値を設定 - *------------------------------------------ - */ -int pc_setglobalreg (struct map_session_data *sd, char *reg, int val) -{ - int i; - - nullpo_retr (0, sd); - - //PC_DIE_COUNTERがスクリプトなどで変更された時の処理 - if (strcmp (reg, "PC_DIE_COUNTER") == 0 && sd->die_counter != val) - { - sd->die_counter = val; - pc_calcstatus (sd, 0); - } - if (val == 0) - { - for (i = 0; i < sd->status.global_reg_num; i++) - { - if (strcmp (sd->status.global_reg[i].str, reg) == 0) - { - sd->status.global_reg[i] = - sd->status.global_reg[sd->status.global_reg_num - 1]; - sd->status.global_reg_num--; - break; - } - } - return 0; - } - for (i = 0; i < sd->status.global_reg_num; i++) - { - if (strcmp (sd->status.global_reg[i].str, reg) == 0) - { - sd->status.global_reg[i].value = val; - return 0; - } - } - if (sd->status.global_reg_num < GLOBAL_REG_NUM) - { - strcpy (sd->status.global_reg[i].str, reg); - sd->status.global_reg[i].value = val; - sd->status.global_reg_num++; - return 0; - } - if (battle_config.error_log) - printf ("pc_setglobalreg : couldn't set %s (GLOBAL_REG_NUM = %d)\n", - reg, GLOBAL_REG_NUM); - - return 1; -} - -/*========================================== - * script用アカウント変数の値を読む - *------------------------------------------ - */ -int pc_readaccountreg (struct map_session_data *sd, char *reg) -{ - int i; - - nullpo_retr (0, sd); - - for (i = 0; i < sd->status.account_reg_num; i++) - { - if (strcmp (sd->status.account_reg[i].str, reg) == 0) - return sd->status.account_reg[i].value; - } - - return 0; -} - -/*========================================== - * script用アカウント変数の値を設定 - *------------------------------------------ - */ -int pc_setaccountreg (struct map_session_data *sd, char *reg, int val) -{ - int i; - - nullpo_retr (0, sd); - - if (val == 0) - { - for (i = 0; i < sd->status.account_reg_num; i++) - { - if (strcmp (sd->status.account_reg[i].str, reg) == 0) - { - sd->status.account_reg[i] = - sd->status.account_reg[sd->status.account_reg_num - 1]; - sd->status.account_reg_num--; - break; - } - } - intif_saveaccountreg (sd); - return 0; - } - for (i = 0; i < sd->status.account_reg_num; i++) - { - if (strcmp (sd->status.account_reg[i].str, reg) == 0) - { - sd->status.account_reg[i].value = val; - intif_saveaccountreg (sd); - return 0; - } - } - if (sd->status.account_reg_num < ACCOUNT_REG_NUM) - { - strcpy (sd->status.account_reg[i].str, reg); - sd->status.account_reg[i].value = val; - sd->status.account_reg_num++; - intif_saveaccountreg (sd); - return 0; - } - if (battle_config.error_log) - printf ("pc_setaccountreg : couldn't set %s (ACCOUNT_REG_NUM = %d)\n", - reg, ACCOUNT_REG_NUM); - - return 1; -} - -/*========================================== - * script用アカウント変数2の値を読む - *------------------------------------------ - */ -int pc_readaccountreg2 (struct map_session_data *sd, char *reg) -{ - int i; - - nullpo_retr (0, sd); - - for (i = 0; i < sd->status.account_reg2_num; i++) - { - if (strcmp (sd->status.account_reg2[i].str, reg) == 0) - return sd->status.account_reg2[i].value; - } - - return 0; -} - -/*========================================== - * script用アカウント変数2の値を設定 - *------------------------------------------ - */ -int pc_setaccountreg2 (struct map_session_data *sd, char *reg, int val) -{ - int i; - - nullpo_retr (1, sd); - - if (val == 0) - { - for (i = 0; i < sd->status.account_reg2_num; i++) - { - if (strcmp (sd->status.account_reg2[i].str, reg) == 0) - { - sd->status.account_reg2[i] = - sd->status.account_reg2[sd->status.account_reg2_num - 1]; - sd->status.account_reg2_num--; - break; - } - } - chrif_saveaccountreg2 (sd); - return 0; - } - for (i = 0; i < sd->status.account_reg2_num; i++) - { - if (strcmp (sd->status.account_reg2[i].str, reg) == 0) - { - sd->status.account_reg2[i].value = val; - chrif_saveaccountreg2 (sd); - return 0; - } - } - if (sd->status.account_reg2_num < ACCOUNT_REG2_NUM) - { - strcpy (sd->status.account_reg2[i].str, reg); - sd->status.account_reg2[i].value = val; - sd->status.account_reg2_num++; - chrif_saveaccountreg2 (sd); - return 0; - } - if (battle_config.error_log) - printf - ("pc_setaccountreg2 : couldn't set %s (ACCOUNT_REG2_NUM = %d)\n", - reg, ACCOUNT_REG2_NUM); - - return 1; -} - -/*========================================== - * 精錬成功率 - *------------------------------------------ - */ -int pc_percentrefinery (struct map_session_data *sd, struct item *item) -{ - int percent; - - nullpo_retr (0, item); - percent = percentrefinery[itemdb_wlv (item->nameid)][(int) item->refine]; - - percent += pc_checkskill (sd, BS_WEAPONRESEARCH); // 武器研究スキル所持 - - // 確率の有効範囲チェック - if (percent > 100) - { - percent = 100; - } - if (percent < 0) - { - percent = 0; - } - - return percent; -} - -/*========================================== - * イベントタイマー処理 - *------------------------------------------ - */ -void pc_eventtimer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = map_id2sd (id); - int i; - if (sd == NULL) - return; - - for (i = 0; i < MAX_EVENTTIMER; i++) - { - if (sd->eventtimer[i] == tid) - { - sd->eventtimer[i] = -1; - npc_event (sd, (const char *) data, 0); - break; - } - } - free ((void *) data); - if (i == MAX_EVENTTIMER) - { - if (battle_config.error_log) - printf ("pc_eventtimer: no such event timer\n"); - } -} - -/*========================================== - * イベントタイマー追加 - *------------------------------------------ - */ -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 = (char *) calloc (24, 1); - strncpy (evname, name, 24); - evname[23] = '\0'; - sd->eventtimer[i] = add_timer (gettick () + tick, - pc_eventtimer, sd->bl.id, - (int) evname); - return 1; - } - - return 0; -} - -/*========================================== - * イベントタイマー削除 - *------------------------------------------ - */ -int pc_deleventtimer (struct map_session_data *sd, const char *name) -{ - 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) - { - delete_timer (sd->eventtimer[i], pc_eventtimer); - sd->eventtimer[i] = -1; - 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); - - for (i = 0; i < MAX_EVENTTIMER; i++) - if (sd->eventtimer[i] != -1) - { - delete_timer (sd->eventtimer[i], pc_eventtimer); - sd->eventtimer[i] = -1; - } - - return 0; -} - -// -// 装 備物 -// -/*========================================== - * アイテムを装備する - *------------------------------------------ - */ -static int -pc_signal_advanced_equipment_change (struct map_session_data *sd, int n) -{ - if (sd->status.inventory[n].equip & 0x0040) - clif_changelook (&sd->bl, LOOK_SHOES, 0); - if (sd->status.inventory[n].equip & 0x0004) - clif_changelook (&sd->bl, LOOK_GLOVES, 0); - if (sd->status.inventory[n].equip & 0x0008) - clif_changelook (&sd->bl, LOOK_CAPE, 0); - if (sd->status.inventory[n].equip & 0x0010) - clif_changelook (&sd->bl, LOOK_MISC1, 0); - if (sd->status.inventory[n].equip & 0x0080) - clif_changelook (&sd->bl, LOOK_MISC2, 0); - return 0; -} - -int pc_equipitem (struct map_session_data *sd, int n, int pos) -{ - int i, nameid, arrow, view; - struct item_data *id; - //ソス]ソスソスソスソスソス{ソスqソスフ場合ソスフ鯉ソスソスフ職ソスニゑソスソスZソスoソスソスソスソス - - nullpo_retr (0, sd); - - if (n < 0 || n >= MAX_INVENTORY) - { - clif_equipitemack (sd, 0, 0, 0); - return 0; - } - - nameid = sd->status.inventory[n].nameid; - id = sd->inventory_data[n]; - pos = pc_equippoint (sd, n); - - if (battle_config.battle_log) - printf ("equip %d(%d) %x:%x\n", nameid, n, id->equip, pos); - if (!pc_isequip (sd, n) || !pos || sd->status.inventory[n].broken == 1) - { // [Valaris] - clif_equipitemack (sd, n, 0, 0); // fail - return 0; - } - -// -- moonsoul (if player is berserk then cannot equip) -// - if (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->status.pc_class == 12)) // 左手修錬有 - { - 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 (pos & equip_pos[i]) - { - if (sd->equip_index[i] >= 0) //Slot taken, remove item from there. - pc_unequipitem (sd, sd->equip_index[i], 1); - sd->equip_index[i] = n; - } - } - // 弓矢装備 - if (pos == 0x8000) - { - clif_arrowequip (sd, n); - clif_arrow_fail (sd, 3); // 3=矢が装備できました - } - else - { - /* Don't update re-equipping if we're using a spell */ - if (!(pos == 4 && sd->attack_spell_override)) - 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->inventory_data[n]) - { - view = sd->inventory_data[n]->look; - if (view == 0) - view = sd->inventory_data[n]->nameid; - } - else - { - view = 0; - } - - if (sd->status.inventory[n].equip & 0x0002) - { - sd->weapontype1 = view; - pc_calcweapontype (sd); - pc_set_weapon_look (sd); - } - 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 = view; - } - else if (sd->inventory_data[n]->type == 5) - { - sd->status.shield = view; - 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) - { - sd->status.head_bottom = view; - clif_changelook (&sd->bl, LOOK_HEAD_BOTTOM, sd->status.head_bottom); - } - if (sd->status.inventory[n].equip & 0x0100) - { - sd->status.head_top = view; - clif_changelook (&sd->bl, LOOK_HEAD_TOP, sd->status.head_top); - } - if (sd->status.inventory[n].equip & 0x0200) - { - sd->status.head_mid = view; - clif_changelook (&sd->bl, LOOK_HEAD_MID, sd->status.head_mid); - } - pc_signal_advanced_equipment_change (sd, n); - - pc_checkallowskill (sd); // 装備品でスキルか解除されるかチェック - if (itemdb_look (sd->status.inventory[n].nameid) == 11 && arrow) - { // Added by RoVeRT - clif_arrowequip (sd, arrow); - sd->status.inventory[arrow].equip = 32768; - } - pc_calcstatus (sd, 0); - - if (sd->special_state.infinite_endure) - { - if (sd->sc_data[SC_ENDURE].timer == -1) - skill_status_change_start (&sd->bl, SC_ENDURE, 10, 1, 0, 0, 0, 0); - } - else - { - if (sd->sc_data[SC_ENDURE].timer != -1 && sd->sc_data[SC_ENDURE].val2) - skill_status_change_end (&sd->bl, SC_ENDURE, -1); - } - - if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 - && !battle_check_undead (7, sd->def_ele)) - skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); - if (sd->sc_data[SC_DANCING].timer != -1 - && (sd->status.weapon != 13 && sd->status.weapon != 14)) - skill_stop_dancing (&sd->bl, 0); - - return 0; -} - -/*========================================== - * 装 備した物を外す - *------------------------------------------ - */ -int pc_unequipitem (struct map_session_data *sd, int n, int type) -{ - nullpo_retr (0, sd); - -// -- moonsoul (if player is berserk then cannot unequip) -// - if (sd->sc_data[SC_BERSERK].timer != -1) - { - clif_unequipitemack (sd, n, 0, 0); - return 0; - } - - if (battle_config.battle_log) - printf ("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->status.inventory[n].equip & 0x0002) - { - sd->weapontype1 = 0; - sd->status.weapon = sd->weapontype2; - pc_calcweapontype (sd); - pc_set_weapon_look (sd); - } - 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); - } - pc_signal_advanced_equipment_change (sd, n); - - if (sd->sc_data[SC_BROKNWEAPON].timer != -1 - && sd->status.inventory[n].equip & 0x0002 - && sd->status.inventory[i].broken == 1) - skill_status_change_end (&sd->bl, SC_BROKNWEAPON, -1); - - clif_unequipitemack (sd, n, sd->status.inventory[n].equip, 1); - sd->status.inventory[n].equip = 0; - if (!type) - pc_checkallowskill (sd); - if (sd->weapontype1 == 0 && sd->weapontype2 == 0) - skill_encchant_eremental_end (&sd->bl, -1); //武器持ち誓えは無条件で属性付与解除 - } - else - { - clif_unequipitemack (sd, n, 0, 0); - } - if (!type) - { - pc_calcstatus (sd, 0); - if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 - && !battle_check_undead (7, sd->def_ele)) - skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); - } - - return 0; -} - -int pc_unequipinvyitem (struct map_session_data *sd, int n, int type) -{ - int i; - - nullpo_retr (1, sd); - - for (i = 0; i < 11; i++) - { - if (equip_pos[i] > 0 && sd->equip_index[i] == n) - { //Slot taken, remove item from there. - pc_unequipitem (sd, sd->equip_index[i], type); - sd->equip_index[i] = -1; - } - } - - 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); - - // 所持品空き詰め - 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) - printf ("illeagal 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) - printf ("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 - && (it->flag.no_equip == 1 || it->flag.no_equip == 3)) - { //PvP制限 - sd->status.inventory[i].equip = 0; - calc_flag = 1; - } - else if (sd->status.inventory[i].equip && map[sd->bl.m].flag.gvg - && (it->flag.no_equip == 2 || it->flag.no_equip == 3)) - { //GvG制限 - sd->status.inventory[i].equip = 0; - calc_flag = 1; - } - } - - pc_setequipindex (sd); - if (calc_flag) - pc_calcstatus (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_foreachinarea (pc_calc_pvprank_sub, sd->bl.m, 0, 0, m->xs, m->ys, - 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) - *------------------------------------------ - */ -void pc_calc_pvprank_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = NULL; - if (battle_config.pk_mode) // disable pvp ranking if pk_mode on [Valaris] - return; - - sd = map_id2sd (id); - if (sd == NULL) - return; - 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); -} - -/*========================================== - * 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) - 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 = NULL; - if (sd == NULL || !pc_ismarried (sd)) - return -1; - - // If both are on map server we don't need to bother the char server - if ((p_sd = - map_nick2sd (map_charid2nick (sd->status.partner_id))) != NULL) - { - if (p_sd->status.partner_id != sd->status.char_id - || sd->status.partner_id != p_sd->status.char_id) - { - printf ("pc_divorce: Illegal partner_id sd=%d p_sd=%d\n", - sd->status.partner_id, p_sd->status.partner_id); - return -1; - } - p_sd->status.partner_id = 0; - sd->status.partner_id = 0; - - if (sd->npc_flags.divorce) - { - sd->npc_flags.divorce = 0; - map_scriptcont (sd, sd->npc_id); - } - } - else - chrif_send_divorce (sd->status.char_id); - - 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; - - return p_sd; -} - -// -// 自然回復物 -// -/*========================================== - * 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; - struct guild_castle *gc = NULL; - - nullpo_retr (0, sd); - - a = natural_heal_diff_tick; - if (pc_issit (sd)) - a += a; - if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // マグニフィカート - a += a; - - gc = guild_mapname2gc (sd->mapname); // Increased guild castle regen [Valaris] - if (gc) - { - struct guild *g; - g = guild_search (sd->status.guild_id); - if (g && g->guild_id == gc->guild_id) - a += a; - } // end addition [Valaris] - - return a; -} - -/*========================================== - * HP回復量計算 - *------------------------------------------ - */ -static int pc_hpheal (struct map_session_data *sd) -{ - int a; - struct guild_castle *gc; - - nullpo_retr (0, sd); - - a = natural_heal_diff_tick; - if (pc_issit (sd)) - a += a; - if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // Modified by RoVeRT - a += a; - - gc = guild_mapname2gc (sd->mapname); // Increased guild castle regen [Valaris] - if (gc) - { - struct guild *g; - g = guild_search (sd->status.guild_id); - if (g && g->guild_id == gc->guild_id) - a += a; - } // end addition [Valaris] - - return a; -} - -static int pc_natural_heal_hp (struct map_session_data *sd) -{ - int bhp; - int inc_num, bonus, skill, hp_flag; - - nullpo_retr (0, sd); - - if (sd->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT - 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; - - if (sd->sc_data[SC_APPLEIDUN].timer != -1) - { // Apple of Idun - if (sd->inchealhptick >= 6000 && sd->status.hp < sd->status.max_hp) - { - bonus = skill * 20; - while (sd->inchealhptick >= 6000) - { - sd->inchealhptick -= 6000; - 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->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT - 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->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) - { - struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); - if (sd->doridori_counter && s_class.job == 23) - bonus = sd->nshealsp * 2; - else - bonus = sd->nshealsp; - sd->doridori_counter = 0; - 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 level) -{ - int bonus_hp, interval = battle_config.natural_heal_skill_interval; - struct status_change *sc_data = battle_get_sc_data (&sd->bl); - - 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 - && sc_data[SC_FLYING_BACKPACK].timer == -1) - interval += interval; - - if (sd->inchealspirithptick >= interval) - { - bonus_hp = sd->nsshealhp; - 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 level) -{ - 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; - 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; -} - -/*========================================== - * HP/SP 自然回復 各クライアント - *------------------------------------------ - */ -static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp); - -static int -pc_quickregenerate_effect (struct quick_regeneration *quick_regen, - int heal_speed) -{ - if (!(quick_regen->tickdelay--)) - { - int bonus = - MIN (heal_speed * battle_config.itemheal_regeneration_factor, - quick_regen->amount); - - quick_regen->amount -= bonus; - - quick_regen->tickdelay = quick_regen->speed; - - return bonus; - } - - return 0; -} - -static int pc_natural_heal_sub (struct map_session_data *sd, va_list ap) -{ - int skill; - - nullpo_retr (0, sd); - - if (sd->heal_xp > 0) - { - if (sd->heal_xp < 64) - --sd->heal_xp; // [Fate] Slowly reduce XP that healers can get for healing this char - else - sd->heal_xp -= (sd->heal_xp >> 6); - } - - // Hijack this callback: Adjust spellpower bonus - if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) - { - sd->spellpower_bonus_current = sd->spellpower_bonus_target; - pc_calcstatus (sd, 0); - } - else if (sd->spellpower_bonus_target > sd->spellpower_bonus_current) - { - sd->spellpower_bonus_current += - 1 + - ((sd->spellpower_bonus_target - - sd->spellpower_bonus_current) >> 5); - pc_calcstatus (sd, 0); - } - - if (sd->sc_data[SC_HALT_REGENERATE].timer != -1) - return 0; - - if (sd->quick_regeneration_hp.amount || sd->quick_regeneration_sp.amount) - { - int hp_bonus = pc_quickregenerate_effect (&sd->quick_regeneration_hp, - (sd->sc_data[SC_POISON].timer == -1 || sd->sc_data[SC_SLOWPOISON].timer != -1) ? sd->nhealhp : 1); // [fate] slow down when poisoned - int sp_bonus = pc_quickregenerate_effect (&sd->quick_regeneration_sp, - sd->nhealsp); - - pc_itemheal_effect (sd, hp_bonus, sp_bonus); - } - skill_update_heal_animation (sd); // if needed. - -// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status) - if ((sd->sc_data[SC_FLYING_BACKPACK].timer != -1 - || battle_config.natural_heal_weight_rate > 100 - || sd->weight * 100 / sd->max_weight < - battle_config.natural_heal_weight_rate) && !pc_isdead (sd) - && !pc_ishiding (sd) && sd->sc_data[SC_POISON].timer == -1) - { - pc_natural_heal_hp (sd); - if (sd->sc_data && sd->sc_data[SC_EXTREMITYFIST].timer == -1 && //阿修羅状態ではSPが回復しない - sd->sc_data[SC_DANCING].timer == -1 && //ダンス状態ではSPが回復しない - sd->sc_data[SC_BERSERK].timer == -1 //バーサーク状態ではSPが回復しない - ) - pc_natural_heal_sp (sd); - } - else - { - sd->hp_sub = sd->inchealhptick = 0; - sd->sp_sub = sd->inchealsptick = 0; - } - if ((skill = pc_checkskill (sd, MO_SPIRITSRECOVERY)) > 0 - && !pc_ishiding (sd) && sd->sc_data[SC_POISON].timer == -1 - && sd->sc_data[SC_BERSERK].timer == -1) - { - pc_spirit_heal_hp (sd, skill); - pc_spirit_heal_sp (sd, skill); - } - else - { - sd->inchealspirithptick = 0; - sd->inchealspiritsptick = 0; - } - return 0; -} - -/*========================================== - * HP/SP自然回復 (interval timer関数) - *------------------------------------------ - */ -void pc_natural_heal (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - natural_heal_tick = tick; - natural_heal_diff_tick = - DIFF_TICK (natural_heal_tick, natural_heal_prev_tick); - clif_foreachclient (pc_natural_heal_sub); - - natural_heal_prev_tick = tick; -} - -/*========================================== - * セーブポイントの保存 - *------------------------------------------ - */ -int pc_setsavepoint (struct map_session_data *sd, char *mapname, int x, int y) -{ - nullpo_retr (0, sd); - - strncpy (sd->status.save_point.map, mapname, 23); - sd->status.save_point.map[23] = '\0'; - 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); - - if (save_flag == 0 && sd->fd > last_save_fd) - { - struct guild_castle *gc = NULL; - int i; - - pc_makesavestatus (sd); - chrif_save (sd); - - for (i = 0; i < MAX_GUILDCASTLE; i++) - { - gc = guild_castle_search (i); - if (!gc) - continue; - if (gc->visibleG0 == 1) - guild_castledatasave (gc->castle_id, 18, gc->Ghp0); - if (gc->visibleG1 == 1) - guild_castledatasave (gc->castle_id, 19, gc->Ghp1); - if (gc->visibleG2 == 1) - guild_castledatasave (gc->castle_id, 20, gc->Ghp2); - if (gc->visibleG3 == 1) - guild_castledatasave (gc->castle_id, 21, gc->Ghp3); - if (gc->visibleG4 == 1) - guild_castledatasave (gc->castle_id, 22, gc->Ghp4); - if (gc->visibleG5 == 1) - guild_castledatasave (gc->castle_id, 23, gc->Ghp5); - if (gc->visibleG6 == 1) - guild_castledatasave (gc->castle_id, 24, gc->Ghp6); - if (gc->visibleG7 == 1) - guild_castledatasave (gc->castle_id, 25, gc->Ghp7); - } - - save_flag = 1; - last_save_fd = sd->fd; - } - - return 0; -} - -/*========================================== - * 自動セーブ (timer関数) - *------------------------------------------ - */ -void pc_autosave (timer_id tid, tick_t tick, custom_id_t id, custom_data_t 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); -} - -int pc_read_gm_account (int fd) -{ - int i = 0; - if (gm_account != NULL) - free (gm_account); - GM_num = 0; - - CREATE (gm_account, struct gm_account, (RFIFOW (fd, 2) - 4) / 5); - 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++; - } - return GM_num; -} - -/*========================================== - * timer to do the day - *------------------------------------------ - */ -void map_day_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ // by [yor] - struct map_session_data *pl_sd = NULL; - int i; - char tmpstr[1024]; - - if (battle_config.day_duration > 0) - { // if we want a day - if (night_flag != 0) - { - strcpy (tmpstr, msg_txt (502)); // The day has arrived! - 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->opt2 &= ~STATE_BLIND; - clif_changeoption (&pl_sd->bl); - clif_wis_message (pl_sd->fd, wisp_server_name, tmpstr, - strlen (tmpstr) + 1); - } - } - } - } -} - -/*========================================== - * timer to do the night - *------------------------------------------ - */ -void map_night_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ // by [yor] - struct map_session_data *pl_sd = NULL; - int i; - char tmpstr[1024]; - - if (battle_config.night_duration > 0) - { // if we want a night - if (night_flag == 0) - { - strcpy (tmpstr, msg_txt (503)); // The night has fallen... - 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->opt2 |= STATE_BLIND; - clif_changeoption (&pl_sd->bl); - clif_wis_message (pl_sd->fd, wisp_server_name, tmpstr, - strlen (tmpstr) + 1); - } - } - } - } -} - -void pc_setstand (struct map_session_data *sd) -{ - nullpo_retv (sd); - - if (sd->sc_data && sd->sc_data[SC_TENSIONRELAX].timer != -1) - skill_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; - - // 必要経験値読み込み - - fp = fopen_ ("db/exp.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/exp.txt\n"); - 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.maximum_level) - break; - } - fclose_ (fp); - printf ("read db/exp.txt done\n"); - - // JOB補正数値1 - fp = fopen_ ("db/job_db1.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/job_db1.txt\n"); - return 1; - } - i = 0; - while (fgets (line, sizeof (line) - 1, fp)) - { - char *split[50]; - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 21 && p; j++) - { - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - if (j < 21) - continue; - max_weight_base[i] = atoi (split[0]); - hp_coefficient[i] = atoi (split[1]); - hp_coefficient2[i] = atoi (split[2]); - sp_coefficient[i] = atoi (split[3]); - for (j = 0; j < 17; j++) - aspd_base[i][j] = atoi (split[j + 4]); - i++; -// -- moonsoul (below two lines added to accommodate high numbered new class ids) - if (i == 24) - i = 4001; - if (i == MAX_PC_CLASS) - break; - } - fclose_ (fp); - printf ("read db/job_db1.txt done\n"); - - // JOBボーナス - fp = fopen_ ("db/job_db2.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/job_db2.txt\n"); - return 1; - } - i = 0; - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < MAX_LEVEL && p; j++) - { - if (sscanf (p, "%d", &k) == 0) - break; - job_bonus[0][i][j] = k; - job_bonus[2][i][j] = k; //養子職のボーナスは分からないので仮 - p = strchr (p, ','); - if (p) - p++; - } - i++; -// -- moonsoul (below two lines added to accommodate high numbered new class ids) - if (i == 24) - i = 4001; - if (i == MAX_PC_CLASS) - break; - } - fclose_ (fp); - printf ("read db/job_db2.txt done\n"); - - // JOBボーナス2 転生職用 - fp = fopen_ ("db/job_db2-2.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/job_db2-2.txt\n"); - return 1; - } - i = 0; - while (fgets (line, sizeof (line) - 1, fp)) - { - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < MAX_LEVEL && p; j++) - { - if (sscanf (p, "%d", &k) == 0) - break; - job_bonus[1][i][j] = k; - p = strchr (p, ','); - if (p) - p++; - } - i++; - if (i == MAX_PC_CLASS) - break; - } - fclose_ (fp); - printf ("read db/job_db2-2.txt done\n"); - - // スキルツリー - memset (skill_tree, 0, sizeof (skill_tree)); - fp = fopen_ ("db/skill_tree.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/skill_tree.txt\n"); - return 1; - } - while (fgets (line, sizeof (line) - 1, fp)) - { - char *split[50]; - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 13 && p; j++) - { - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - if (j < 13) - continue; - i = atoi (split[0]); - for (j = 0; skill_tree[0][i][j].id; j++); - skill_tree[0][i][j].id = atoi (split[1]); - skill_tree[0][i][j].max = atoi (split[2]); - skill_tree[2][i][j].id = atoi (split[1]); //養子職は良く分からないので暫定 - skill_tree[2][i][j].max = atoi (split[2]); //養子職は良く分からないので暫定 - for (k = 0; k < 5; k++) - { - skill_tree[0][i][j].need[k].id = atoi (split[k * 2 + 3]); - skill_tree[0][i][j].need[k].lv = atoi (split[k * 2 + 4]); - skill_tree[2][i][j].need[k].id = atoi (split[k * 2 + 3]); //養子職は良く分からないので暫定 - skill_tree[2][i][j].need[k].lv = atoi (split[k * 2 + 4]); //養子職は良く分からないので暫定 - } - } - fclose_ (fp); - printf ("read db/skill_tree.txt done\n"); - - // 属性修正テーブル - 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; - fp = fopen_ ("db/attr_fix.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/attr_fix.txt\n"); - 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]); -// printf("%d %d\n",lv,n); - - 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); - printf ("read db/attr_fix.txt done\n"); - - // サイズ補正テーブル - for (i = 0; i < 3; i++) - for (j = 0; j < 20; j++) - atkmods[i][j] = 100; - fp = fopen_ ("db/size_fix.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/size_fix.txt\n"); - 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); - printf ("read db/size_fix.txt done\n"); - - // 精錬データテーブル - for (i = 0; i < 5; i++) - { - for (j = 0; j < 10; j++) - percentrefinery[i][j] = 100; - refinebonus[i][0] = 0; - refinebonus[i][1] = 0; - refinebonus[i][2] = 10; - } - fp = fopen_ ("db/refine_db.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/refine_db.txt\n"); - 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 < 10 && split[j]; j++) - percentrefinery[i][j] = atoi (split[j + 3]); - i++; - } - fclose_ (fp); //Lupus. close this file!!! - printf ("read db/refine_db.txt done\n"); - - return 0; -} - -static int pc_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; -} - -static void pc_statpointdb (void) -{ - char *buf_stat; - int i = 0, j = 0, k = 0, l = 0, end = 0; - - FILE *stp; - - stp = fopen_ ("db/statpoint.txt", "r"); - - if (stp == NULL) - { - printf ("can't read db/statpoint.txt\n"); - return; - } - - fseek (stp, 0, SEEK_END); - end = ftell (stp); - rewind (stp); - - buf_stat = (char *) malloc (end + 1); - l = fread (buf_stat, 1, end, stp); - fclose_ (stp); - printf ("read db/statpoint.txt done (size=%d)\n", l); - - for (i = 0; i < 255; i++) - { - j = 0; - while (*(buf_stat + k) != '\n') - { - statp[i][j] = *(buf_stat + k); - j++; - k++; - } - statp[i][j + 1] = '\0'; - k++; - } - - free (buf_stat); -} - -/*========================================== - * pc関 係初期化 - *------------------------------------------ - */ -int do_init_pc (void) -{ - pc_readdb (); - pc_statpointdb (); - pc_calc_sigma (); - -// gm_account_db = numdb_init(); - - 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); - - { - int day_duration = battle_config.day_duration; - int night_duration = battle_config.night_duration; - if (day_duration < 60000) - day_duration = 60000; - if (night_duration < 60000) - night_duration = 60000; - if (battle_config.night_at_start == 0) - { - 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; -} - -void pc_cleanup (struct map_session_data *sd) -{ - magic_stop_completely (sd); -} - -void pc_invisibility (struct map_session_data *sd, int enabled) -{ - if (enabled && !(sd->status.option & OPTION_INVISIBILITY)) - { - clif_clearchar_area (&sd->bl, 3); - sd->status.option |= OPTION_INVISIBILITY; - clif_status_change (&sd->bl, CLIF_OPTION_SC_INVISIBILITY, 1); - } - else if (!enabled) - { - sd->status.option &= ~OPTION_INVISIBILITY; - clif_status_change (&sd->bl, CLIF_OPTION_SC_INVISIBILITY, 0); - pc_setpos (sd, map[sd->bl.m].name, sd->bl.x, sd->bl.y, 3); - } -} - -int pc_logout (struct map_session_data *sd) // [fate] Player logs out -{ - unsigned int tick = gettick (); - - if (!sd) - return 0; - - if (sd->sc_data[SC_POISON].timer != -1) - sd->status.hp = 1; // Logging out while poisoned -> bad - - /* - * Trying to rapidly sign out/in or switch characters to avoid a spell's - * cast time is also bad. [remoitnane] - */ -#if 0 - // Removed because it's buggy, see above. - if (sd->cast_tick > tick) - { - if (pc_setglobalreg (sd, "MAGIC_CAST_TICK", sd->cast_tick - tick)) - sd->status.sp = 1; - } - else -#endif - pc_setglobalreg (sd, "MAGIC_CAST_TICK", 0); - - MAP_LOG_STATS (sd, "LOGOUT") return 0; -} diff --git a/src/map/pc.cpp b/src/map/pc.cpp new file mode 100644 index 0000000..7d02545 --- /dev/null +++ b/src/map/pc.cpp @@ -0,0 +1,9032 @@ +// $Id: pc.c 101 2004-09-25 17:57:22Z Valaris $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "../common/socket.hpp" // [Valaris] +#include "../common/timer.hpp" +#include "../common/db.hpp" + +#include "../common/nullpo.hpp" +#include "../common/mt_rand.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "chat.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "storage.hpp" +#include "trade.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define PVP_CALCRANK_INTERVAL 1000 // PVP順位計算の間隔 + +//define it here, since the ifdef only occurs in this file +#define USE_ASTRAL_SOUL_SKILL + +#define STATE_BLIND 0x10 + +#ifdef USE_ASTRAL_SOUL_SKILL +#define MAGIC_SKILL_THRESHOLD 200 // [fate] At this threshold, the Astral Soul skill kicks in +#endif + +#define MAP_LOG_STATS(sd, suffix) \ + MAP_LOG_PC(sd, "STAT %d %d %d %d %d %d " suffix, \ + sd->status.str, sd->status.agi, sd->status.vit, sd->status.int_, sd->status.dex, sd->status.luk) + +#define MAP_LOG_XP(sd, suffix) \ + MAP_LOG_PC(sd, "XP %d %d JOB %d %d %d ZENY %d + %d " suffix, \ + sd->status.base_level, sd->status.base_exp, sd->status.job_level, sd->status.job_exp, sd->status.skill_point, sd->status.zeny, pc_readaccountreg(sd, "BankAccount")) + +#define MAP_LOG_MAGIC(sd, suffix) \ + MAP_LOG_PC(sd, "MAGIC %d %d %d %d %d %d EXP %d %d " suffix, \ + sd->status.skill[TMW_MAGIC].lv, \ + sd->status.skill[TMW_MAGIC_LIFE].lv, \ + sd->status.skill[TMW_MAGIC_WAR].lv, \ + sd->status.skill[TMW_MAGIC_TRANSMUTE].lv, \ + sd->status.skill[TMW_MAGIC_NATURE].lv, \ + sd->status.skill[TMW_MAGIC_ETHER].lv, \ + pc_readglobalreg(sd, "MAGIC_EXPERIENCE") & 0xffff, \ + (pc_readglobalreg(sd, "MAGIC_EXPERIENCE") >> 24) & 0xff) + +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]; +static char job_bonus[3][MAX_PC_CLASS][MAX_LEVEL]; +static int exp_table[14][MAX_LEVEL]; +static char statp[255][7]; +static struct +{ + int id; + int max; + struct + { + short id, lv; + } need[6]; +} skill_tree[3][MAX_PC_CLASS][100]; + +static int atkmods[3][20]; // 武器ATKサイズ修正(size_fix.txt) +static int refinebonus[5][3]; // 精錬ボーナステーブル(refine_db.txt) +static int percentrefinery[5][10]; // 精錬成功率(refine_db.txt) + +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 unsigned int equip_pos[11] = + { 0x0080, 0x0008, 0x0040, 0x0004, 0x0001, 0x0200, 0x0100, 0x0010, 0x0020, + 0x0002, 0x8000 +}; + +//static struct dbt *gm_account_db; +static struct gm_account *gm_account = NULL; +static int GM_num = 0; + +int pc_isGM (struct map_session_data *sd) +{ +// struct gm_account *p; + int i; + + nullpo_retr (0, sd); + +/* p = numdb_search(gm_account_db, sd->status.account_id); + if (p == NULL) + return 0; + return p->level;*/ + + 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++; + RECREATE (gm_account, struct gm_account, GM_num); + gm_account[GM_num - 1].account_id = account_id; + gm_account[GM_num - 1].level = level; + return 0; +} + +int pc_getrefinebonus (int lv, int type) +{ + if (lv >= 0 && lv < 5 && type >= 0 && type < 3) + return refinebonus[lv][type]; + return 0; +} + +static int distance (int x0, int y0, int x1, int y1) +{ + int dx, dy; + + dx = abs (x0 - x1); + dy = abs (y0 - y1); + return dx > dy ? dx : dy; +} + +static void pc_invincible_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd; + + if ((sd = (struct map_session_data *) map_id2sd (id)) == NULL + || sd->bl.type != BL_PC) + return; + + if (sd->invincible_timer != tid) + { + if (battle_config.error_log) + printf ("invincible_timer %d != %d\n", sd->invincible_timer, tid); + return; + } + sd->invincible_timer = -1; +} + +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; + } + return 0; +} + +static void pc_spiritball_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd; + int i; + + if ((sd = (struct map_session_data *) map_id2sd (id)) == NULL + || sd->bl.type != BL_PC) + return; + + if (sd->spirit_timer[0] != tid) + { + if (battle_config.error_log) + printf ("spirit_timer %d != %d\n", sd->spirit_timer[0], tid); + return; + } + sd->spirit_timer[0] = -1; + for (i = 1; i < sd->spiritball; i++) + { + sd->spirit_timer[i - 1] = sd->spirit_timer[i]; + sd->spirit_timer[i] = -1; + } + sd->spiritball--; + if (sd->spiritball < 0) + sd->spiritball = 0; + clif_spiritball (sd); +} + +int pc_addspiritball (struct map_session_data *sd, int interval, int max) +{ + int i; + + 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); + sd->spirit_timer[0] = -1; + } + for (i = 1; i < max; i++) + { + sd->spirit_timer[i - 1] = sd->spirit_timer[i]; + sd->spirit_timer[i] = -1; + } + } + 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; +} + +int pc_setrestartvalue (struct map_session_data *sd, int type) +{ + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + s_class = pc_calc_base_job (sd->status.pc_class); + + //----------------------- + // 死亡した + if (sd->special_state.restart_full_recover) + { // オシリスカード + sd->status.hp = sd->status.max_hp; + sd->status.sp = sd->status.max_sp; + } + else + { + if (s_class.job == 0 && 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->status.pc_class != 0 && 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); + } + sd->heal_xp = 0; // [Fate] Set gainable xp for healing this player to 0 + + return 0; +} + +/*========================================== + * 自分をロックしているMOBの数を数える(foreachclient) + *------------------------------------------ + */ +static int pc_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",((struct mob_data *)bl)->target_lv,target_lv); + } + return 0; +} + +int pc_counttargeted (struct map_session_data *sd, struct block_list *src, + int target_lv) +{ + int c = 0; + map_foreachinarea (pc_counttargeted_sub, sd->bl.m, + sd->bl.x - AREA_SIZE, sd->bl.y - AREA_SIZE, + sd->bl.x + AREA_SIZE, sd->bl.y + AREA_SIZE, 0, + sd->bl.id, &c, src, target_lv); + return c; +} + +/*========================================== + * ローカルプロトタイプ宣言 (必要な物のみ) + *------------------------------------------ + */ +static int pc_walktoxy_sub (struct map_session_data *); + +/*========================================== + * saveに必要なステータス修正を行なう + *------------------------------------------ + */ +int pc_makesavestatus (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + // 服の色は色々弊害が多いので保存対象にはしない + if (!battle_config.save_clothcolor) + sd->status.clothes_color = 0; + + // 死亡状態だったのでhpを1、位置をセーブ場所に変更 + if (pc_isdead (sd)) + { + pc_setrestartvalue (sd, 0); + memcpy (&sd->status.last_point, &sd->status.save_point, + sizeof (sd->status.last_point)); + } + else + { + memcpy (sd->status.last_point.map, sd->mapname, 24); + 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 (strcmp (m->save.map, "SavePoint") == 0) + memcpy (&sd->status.last_point, &sd->status.save_point, + sizeof (sd->status.last_point)); + else + memcpy (&sd->status.last_point, &m->save, + sizeof (sd->status.last_point)); + } + + //マナーポイントがプラスだった場合0に + if (battle_config.muting_players && sd->status.manner > 0) + sd->status.manner = 0; + return 0; +} + +/*========================================== + * 接続時の初期化 + *------------------------------------------ + */ +int pc_setnewpc (struct map_session_data *sd, int account_id, int char_id, + int login_id1, 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; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + if (!sd->inventory_data[n]) + return 0; + + s_class = pc_calc_base_job (sd->status.pc_class); + + 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) && (ep == 2 + && + (pc_checkskill (sd, AS_LEFT) + > 0 || s_class.job == 12))) + { + 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; + struct status_change *sc_data; + //転生や養子の場合の元の職業を算出する + + nullpo_retr (0, sd); + + item = sd->inventory_data[n]; + sc_data = battle_get_sc_data (&sd->bl); + //s_class = pc_calc_base_job(sd->status.class); + + if (battle_config.gm_allequip > 0 + && pc_isGM (sd) >= battle_config.gm_allequip) + return 1; + + if (item == NULL) + return 0; + if (item->sex != 2 && sd->status.sex != item->sex) + return 0; + if (item->elv > 0 && sd->status.base_level < item->elv) + return 0; + + if (map[sd->bl.m].flag.pvp + && (item->flag.no_equip == 1 || item->flag.no_equip == 3)) + return 0; + if (map[sd->bl.m].flag.gvg + && (item->flag.no_equip == 2 || item->flag.no_equip == 3)) + return 0; + if (item->equip & 0x0002 && sc_data + && sc_data[SC_STRIPWEAPON].timer != -1) + return 0; + if (item->equip & 0x0020 && sc_data + && sc_data[SC_STRIPSHIELD].timer != -1) + return 0; + if (item->equip & 0x0010 && sc_data && sc_data[SC_STRIPARMOR].timer != -1) + return 0; + if (item->equip & 0x0100 && sc_data && sc_data[SC_STRIPHELM].timer != -1) + return 0; + return 1; +} + +/*========================================== + * Weapon Breaking [Valaris] + *------------------------------------------ + */ +int pc_breakweapon (struct map_session_data *sd) +{ + struct item_data *item; + char output[255]; + int i; + + if (sd == NULL) + return -1; + if (sd->unbreakable >= MRAND (100)) + return 0; + if (sd->sc_data && sd->sc_data[SC_CP_WEAPON].timer != -1) + return 0; + + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0002 + && !sd->status.inventory[i].broken) + { + item = sd->inventory_data[i]; + sd->status.inventory[i].broken = 1; + //pc_unequipitem(sd,i,0); + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0002 + && sd->status.inventory[i].broken == 1) + { + sprintf (output, "%s has broken.", item->jname); + clif_emotion (&sd->bl, 23); + clif_displaymessage (sd->fd, output); + clif_equiplist (sd); + skill_status_change_start (&sd->bl, SC_BROKNWEAPON, 0, 0, 0, + 0, 0, 0); + } + } + if (sd->status.inventory[i].broken == 1) + return 0; + } + + return 0; +} + +/*========================================== + * Armor Breaking [Valaris] + *------------------------------------------ + */ +int pc_breakarmor (struct map_session_data *sd) +{ + struct item_data *item; + char output[255]; + int i; + + if (sd == NULL) + return -1; + if (sd->unbreakable >= MRAND (100)) + return 0; + if (sd->sc_data && sd->sc_data[SC_CP_ARMOR].timer != -1) + return 0; + + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0010 + && !sd->status.inventory[i].broken) + { + item = sd->inventory_data[i]; + sd->status.inventory[i].broken = 1; + //pc_unequipitem(sd,i,0); + if (sd->status.inventory[i].equip + && sd->status.inventory[i].equip & 0x0010 + && sd->status.inventory[i].broken == 1) + { + sprintf (output, "%s has broken.", item->jname); + clif_emotion (&sd->bl, 23); + clif_displaymessage (sd->fd, output); + clif_equiplist (sd); + skill_status_change_start (&sd->bl, SC_BROKNARMOR, 0, 0, 0, 0, + 0, 0); + } + } + if (sd->status.inventory[i].broken == 1) + return 0; + } + return 0; +} + +/*========================================== + * session idに問題無し + * char鯖から送られてきたステータスを設定 + *------------------------------------------ + */ +int pc_authok (int id, int login_id2, time_t connect_until_time, + short tmw_version, struct mmo_charstatus *st) +{ + struct map_session_data *sd = NULL; + + struct party *p; + struct guild *g; + int i; + unsigned long tick = gettick (); + struct sockaddr_in sai; + socklen_t sa_len = sizeof(struct sockaddr); + + sd = map_id2sd (id); + if (sd == NULL) + return 1; + + sd->login_id2 = login_id2; + sd->tmw_version = tmw_version; + + memcpy (&sd->status, st, sizeof (*st)); + + if (sd->status.sex != sd->sex) + { + clif_authfail_fd (sd->fd, 0); + return 1; + } + + MAP_LOG_STATS (sd, "LOGIN"); + MAP_LOG_XP (sd, "LOGIN"); + MAP_LOG_MAGIC (sd, "LOGIN"); + + memset (&sd->state, 0, sizeof (sd->state)); + // 基本的な初期化 + sd->state.connect_new = 1; + sd->bl.prev = sd->bl.next = NULL; + + sd->weapontype1 = sd->weapontype2 = 0; + sd->view_class = sd->status.pc_class; + sd->speed = DEFAULT_WALK_SPEED; + sd->state.dead_sit = 0; + sd->dir = 0; + sd->head_dir = 0; + sd->state.auth = 1; + sd->walktimer = -1; + sd->attacktimer = -1; + sd->followtimer = -1; // [MouseJstr] + sd->skilltimer = -1; + sd->skillitem = -1; + sd->skillitemlv = -1; + sd->invincible_timer = -1; + sd->sg_count = 0; + + sd->deal_locked = 0; + sd->trade_partner = 0; + + sd->inchealhptick = 0; + sd->inchealsptick = 0; + sd->hp_sub = 0; + sd->sp_sub = 0; + sd->quick_regeneration_hp.amount = 0; + sd->quick_regeneration_sp.amount = 0; + sd->heal_xp = 0; + sd->inchealspirithptick = 0; + sd->inchealspiritsptick = 0; + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->attackabletime = tick; + /* We don't want players bypassing spell restrictions. [remoitnane] */ + // Removed because it was buggy with the ~50 day wraparound, + // and there's already a limit on how fast you can log in and log out. + // -o11c + sd->cast_tick = tick; // + pc_readglobalreg (sd, "MAGIC_CAST_TICK"); + + sd->doridori_counter = 0; + + sd->spiritball = 0; + 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; + + memset (&sd->dev, 0, sizeof (struct square)); + for (i = 0; i < 5; i++) + { + sd->dev.val1[i] = 0; + sd->dev.val2[i] = 0; + } + + // アカウント変数の送信要求 + intif_request_accountreg (sd); + + // アイテムチェック + pc_setinventorydata (sd); + pc_checkitem (sd); + + // ステータス異常の初期化 + for (i = 0; i < MAX_STATUSCHANGE; i++) + { + sd->sc_data[i].timer = -1; + sd->sc_data[i].val1 = sd->sc_data[i].val2 = sd->sc_data[i].val3 = + sd->sc_data[i].val4 = 0; + } + 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_HIDE); + else + sd->status.option &= OPTION_MASK; + + // スキルユニット関係の初期化 + memset (sd->skillunit, 0, sizeof (sd->skillunit)); + memset (sd->skillunittick, 0, sizeof (sd->skillunittick)); + + // init ignore list + memset (sd->ignore, 0, sizeof (sd->ignore)); + + // パーティー関係の初期化 + sd->party_sended = 0; + sd->party_invite = 0; + sd->party_x = -1; + sd->party_y = -1; + sd->party_hp = -1; + + // ギルド関係の初期化 + sd->guild_sended = 0; + sd->guild_invite = 0; + sd->guild_alliance = 0; + + // イベント関係の初期化 + memset (sd->eventqueue, 0, sizeof (sd->eventqueue)); + for (i = 0; i < MAX_EVENTTIMER; i++) + sd->eventtimer[i] = -1; + + // 位置の設定 + pc_setpos (sd, sd->status.last_point.map, sd->status.last_point.x, + sd->status.last_point.y, 0); + + // パーティ、ギルドデータの要求 + 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 + && (g = guild_search (sd->status.guild_id)) == NULL) + guild_request_info (sd->status.guild_id); + + // pvpの設定 + sd->pvp_rank = 0; + sd->pvp_point = 0; + sd->pvp_timer = -1; + + // 通知 + + clif_authok (sd); + map_addnickdb (sd); + if (map_charid2nick (sd->status.char_id) == NULL) + map_addchariddb (sd->status.char_id, sd->status.name); + + //スパノビ用死にカウンターのスクリプト変数からの読み出しとsdへのセット + sd->die_counter = pc_readglobalreg (sd, "PC_DIE_COUNTER"); + + if (night_flag == 1) + { + 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); + sd->opt2 |= STATE_BLIND; + } + + // ステータス初期計算など + pc_calcstatus (sd, 1); + + if (pc_isGM (sd)) + { + printf + ("Connection accepted: character '%s' (account: %d; GM level %d).\n", + sd->status.name, sd->status.account_id, pc_isGM (sd)); + clif_updatestatus (sd, SP_GM); + } + else + printf ("Connection accepted: Character '%s' (account: %d).\n", + sd->status.name, sd->status.account_id); + + // Message of the Dayの送信 + { + char buf[256]; + FILE *fp; + if ((fp = fopen_ (motd_txt, "r")) != NULL) + { + while (fgets (buf, sizeof (buf) - 1, fp) != NULL) + { + int i; + for (i = 0; buf[i]; i++) + { + if (buf[i] == '\r' || buf[i] == '\n') + { + buf[i] = 0; + break; + } + } + clif_displaymessage (sd->fd, buf); + } + fclose_ (fp); + } + } + + sd->auto_ban_info.in_progress = 0; + + // Initialize antispam vars + sd->chat_reset_due = sd->chat_lines_in = sd->chat_total_repeats = + sd->chat_repeat_reset_due = 0; + sd->chat_lastmsg[0] = '\0'; + + memset(sd->flood_rates, 0, sizeof(sd->flood_rates)); + sd->packet_flood_reset_due = sd->packet_flood_in = 0; + + // Obtain IP address (if they are still connected) + if (!getpeername(sd->fd, (struct sockaddr *)&sai, &sa_len)) + sd->ip = sai.sin_addr.s_addr; + + // 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), gmtime (&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); + } + pc_calcstatus (sd, 1); + + return 0; +} + +/*========================================== + * session idに問題ありなので後始末 + *------------------------------------------ + */ +int pc_authfail (int id) +{ + struct map_session_data *sd; + + sd = map_id2sd (id); + if (sd == NULL) + return 1; + + clif_authfail_fd (sd->fd, 0); + + return 0; +} + +static int pc_calc_skillpoint (struct map_session_data *sd) +{ + int i, skill_points = 0; + + nullpo_retr (0, sd); + + for (i = 0; i < skill_pool_skills_size; i++) { + int lv = sd->status.skill[skill_pool_skills[i]].lv; + if (lv) + skill_points += ((lv * (lv - 1)) >> 1) - 1; + } + + return skill_points; +} + +/*========================================== + * 覚えられるスキルの計算 + *------------------------------------------ + */ +int pc_calc_skilltree (struct map_session_data *sd) +{ + int i, id = 0, flag; + int c = 0, s = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + s_class = pc_calc_base_job (sd->status.pc_class); + c = s_class.job; + s = (s_class.upper == 1) ? 1 : 0; //ソ転生以外は通常のスキル? + + if ((battle_config.skillup_limit) + && ((c >= 0 && c < 23) || (c >= 4001 && c < 4023) + || (c >= 4023 && c < 4045))) + { + int skill_point = pc_calc_skillpoint (sd); + if (skill_point < 9) + c = 0; + else if ((sd->status.skill_point >= sd->status.job_level + && skill_point < 58) && ((c > 6 && c < 23) || (c > 4007 + && c < 4023) + || (c > 4029 && c < 4045))) + { + switch (c) + { + case 7: + case 14: + c = 1; + break; + case 8: + case 15: + c = 4; + break; + case 9: + case 16: + c = 2; + break; + case 10: + case 18: + c = 5; + break; + case 11: + case 19: + case 20: + c = 3; + break; + case 12: + case 17: + c = 6; + break; + case 4008: + case 4015: + c = 4002; + break; + case 4009: + case 4016: + c = 4005; + break; + case 4010: + case 4017: + c = 4003; + break; + case 4011: + case 4019: + c = 4006; + break; + case 4012: + case 4020: + case 4021: + c = 4004; + break; + case 4013: + case 4018: + c = 4007; + break; + case 4030: + case 4037: + c = 4024; + break; + case 4031: + case 4038: + c = 4027; + break; + case 4032: + case 4039: + c = 4025; + break; + case 4033: + case 4040: + c = 4028; + break; + case 4034: + case 4041: + case 4042: + c = 4026; + break; + case 4035: + case 4043: + c = 4029; + break; + + } + } + } + + /*Comment this out for now, as we manage skills differently + * for(i=0;i<MAX_SKILL;i++) + * if (i < TMW_MAGIC || i > TMW_MAGIC_END){ // [Fate] This hack gets TMW magic working and persisted without bothering about the skill tree. + * if (sd->status.skill[i].flag != 13) 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 = 1; i < 158; i++) + sd->status.skill[i].id = i; + for (i = 210; i < 291; i++) + sd->status.skill[i].id = i; + for (i = 304; i < 337; i++) + sd->status.skill[i].id = i; + if (battle_config.enable_upper_class) + { //confで無効でなければ読み込む + for (i = 355; i < MAX_SKILL; i++) + sd->status.skill[i].id = i; + } + + } + else + { + // 通常の計算 + do + { + flag = 0; + for (i = 0; (id = skill_tree[s][c][i].id) > 0; i++) + { + int j, f = 1; + if (!battle_config.skillfree) + { + for (j = 0; j < 5; j++) + { + if (skill_tree[s][c][i].need[j].id && + pc_checkskill (sd, + skill_tree[s][c][i].need[j].id) < + skill_tree[s][c][i].need[j].lv) + f = 0; + } + } + if (f && sd->status.skill[id].id == 0) + { + sd->status.skill[id].id = id; + flag = 1; + } + } + } + while (flag); + } +// if(battle_config.etc_log) +// printf("calc skill_tree\n"); + return 0; +} + +/*========================================== + * 重量アイコンの確認 + *------------------------------------------ + */ +int pc_checkweighticon (struct map_session_data *sd) +{ + int flag = 0; + + nullpo_retr (0, sd); + + if (sd->weight * 2 >= sd->max_weight + && sd->sc_data[SC_FLYING_BACKPACK].timer == -1) + flag = 1; + if (sd->weight * 10 >= sd->max_weight * 9) + flag = 2; + + if (flag == 1) + { + if (sd->sc_data[SC_WEIGHT50].timer == -1) + skill_status_change_start (&sd->bl, SC_WEIGHT50, 0, 0, 0, 0, 0, + 0); + } + else + { + skill_status_change_end (&sd->bl, SC_WEIGHT50, -1); + } + if (flag == 2) + { + if (sd->sc_data[SC_WEIGHT90].timer == -1) + skill_status_change_start (&sd->bl, SC_WEIGHT90, 0, 0, 0, 0, 0, + 0); + } + else + { + skill_status_change_end (&sd->bl, SC_WEIGHT90, -1); + } + return 0; +} + +void pc_set_weapon_look (struct map_session_data *sd) +{ + if (sd->attack_spell_override) + clif_changelook (&sd->bl, LOOK_WEAPON, + sd->attack_spell_look_override); + else + clif_changelook (&sd->bl, LOOK_WEAPON, sd->status.weapon); +} + +/*========================================== + * パラメータ計算 + * first==0の時、計算対象のパラメータが呼び出し前から + * 変 化した場合自動でsendするが、 + * 能動的に変化させたパラメータは自前でsendするように + *------------------------------------------ + */ +int pc_calcstatus (struct map_session_data *sd, int first) +{ + 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, aspd_rate, wele, wele_, def_ele, refinedef = 0; + int str, dstr, dex; + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + //転生や養子の場合の元の職業を算出する + s_class = pc_calc_base_job (sd->status.pc_class); + + 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->watk; + b_def = sd->def; + b_watk2 = sd->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.pc_class; + b_base_atk = sd->base_atk; + + pc_calc_skilltree (sd); // スキルツリーの計算 + + sd->max_weight = max_weight_base[s_class.job] + 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++; + } + } + + memset (sd->paramb, 0, sizeof (sd->paramb)); + memset (sd->parame, 0, sizeof (sd->parame)); + sd->hit = 0; + sd->flee = 0; + sd->flee2 = 0; + sd->critical = 0; + sd->aspd = 0; + sd->watk = 0; + sd->def = 0; + sd->mdef = 0; + sd->watk2 = 0; + sd->def2 = 0; + sd->mdef2 = 0; + sd->status.max_hp = 0; + sd->status.max_sp = 0; + sd->attackrange = 0; + sd->attackrange_ = 0; + sd->atk_ele = 0; + sd->def_ele = 0; + sd->star = 0; + sd->overrefine = 0; + sd->matk1 = 0; + sd->matk2 = 0; + sd->speed = DEFAULT_WALK_SPEED; + sd->hprate = 100; + sd->sprate = 100; + sd->castrate = 100; + sd->dsprate = 100; + sd->base_atk = 0; + sd->arrow_atk = 0; + sd->arrow_ele = 0; + sd->arrow_hit = 0; + sd->arrow_range = 0; + sd->nhealhp = sd->nhealsp = sd->nshealhp = sd->nshealsp = sd->nsshealhp = + sd->nsshealsp = 0; + memset (sd->addele, 0, sizeof (sd->addele)); + memset (sd->addrace, 0, sizeof (sd->addrace)); + memset (sd->addsize, 0, sizeof (sd->addsize)); + memset (sd->addele_, 0, sizeof (sd->addele_)); + memset (sd->addrace_, 0, sizeof (sd->addrace_)); + memset (sd->addsize_, 0, sizeof (sd->addsize_)); + memset (sd->subele, 0, sizeof (sd->subele)); + memset (sd->subrace, 0, sizeof (sd->subrace)); + memset (sd->addeff, 0, sizeof (sd->addeff)); + memset (sd->addeff2, 0, sizeof (sd->addeff2)); + memset (sd->reseff, 0, sizeof (sd->reseff)); + memset (&sd->special_state, 0, sizeof (sd->special_state)); + memset (sd->weapon_coma_ele, 0, sizeof (sd->weapon_coma_ele)); + memset (sd->weapon_coma_race, 0, sizeof (sd->weapon_coma_race)); + + sd->watk_ = 0; //二刀流用(仮) + sd->watk_2 = 0; + sd->atk_ele_ = 0; + sd->star_ = 0; + sd->overrefine_ = 0; + + sd->aspd_rate = 100; + sd->speed_rate = 100; + sd->hprecov_rate = 100; + sd->sprecov_rate = 100; + sd->critical_def = 0; + sd->double_rate = 0; + sd->near_attack_def_rate = sd->long_attack_def_rate = 0; + sd->atk_rate = sd->matk_rate = 100; + sd->ignore_def_ele = sd->ignore_def_race = 0; + sd->ignore_def_ele_ = sd->ignore_def_race_ = 0; + sd->ignore_mdef_ele = sd->ignore_mdef_race = 0; + sd->arrow_cri = 0; + sd->magic_def_rate = sd->misc_def_rate = 0; + memset (sd->arrow_addele, 0, sizeof (sd->arrow_addele)); + memset (sd->arrow_addrace, 0, sizeof (sd->arrow_addrace)); + memset (sd->arrow_addsize, 0, sizeof (sd->arrow_addsize)); + memset (sd->arrow_addeff, 0, sizeof (sd->arrow_addeff)); + memset (sd->arrow_addeff2, 0, sizeof (sd->arrow_addeff2)); + memset (sd->magic_addele, 0, sizeof (sd->magic_addele)); + memset (sd->magic_addrace, 0, sizeof (sd->magic_addrace)); + memset (sd->magic_subrace, 0, sizeof (sd->magic_subrace)); + sd->perfect_hit = 0; + 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->def_ratio_atk_ele = sd->def_ratio_atk_ele_ = 0; + sd->def_ratio_atk_race = sd->def_ratio_atk_race_ = 0; + sd->get_zeny_num = 0; + sd->add_damage_class_count = sd->add_damage_class_count_ = + sd->add_magic_damage_class_count = 0; + sd->add_def_class_count = sd->add_mdef_class_count = 0; + sd->monster_drop_item_count = 0; + memset (sd->add_damage_classrate, 0, sizeof (sd->add_damage_classrate)); + memset (sd->add_damage_classrate_, 0, sizeof (sd->add_damage_classrate_)); + memset (sd->add_magic_damage_classrate, 0, + sizeof (sd->add_magic_damage_classrate)); + memset (sd->add_def_classrate, 0, sizeof (sd->add_def_classrate)); + memset (sd->add_mdef_classrate, 0, sizeof (sd->add_mdef_classrate)); + memset (sd->monster_drop_race, 0, sizeof (sd->monster_drop_race)); + memset (sd->monster_drop_itemrate, 0, sizeof (sd->monster_drop_itemrate)); + sd->speed_add_rate = sd->aspd_add_rate = 100; + sd->double_add_rate = sd->perfect_hit_add = sd->get_zeny_add_num = 0; + sd->splash_range = sd->splash_add_range = 0; + sd->autospell_id = sd->autospell_lv = sd->autospell_rate = 0; + sd->hp_drain_rate = sd->hp_drain_per = sd->sp_drain_rate = + sd->sp_drain_per = 0; + sd->hp_drain_rate_ = sd->hp_drain_per_ = sd->sp_drain_rate_ = + sd->sp_drain_per_ = 0; + sd->short_weapon_damage_return = sd->long_weapon_damage_return = 0; + sd->magic_damage_return = 0; //AppleGirl Was Here + sd->random_attack_increase_add = sd->random_attack_increase_per = 0; + + if (!sd->disguiseflag && sd->disguise) + { + sd->disguise = 0; + pc_set_weapon_look (sd); + clif_changelook (&sd->bl, LOOK_SHIELD, sd->status.shield); + 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); + clif_clearchar (&sd->bl, 9); + pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + } + + sd->spellpower_bonus_target = 0; + + for (i = 0; i < 10; i++) + { + index = sd->equip_index[i]; + 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->spellpower_bonus_target += + sd->inventory_data[index]->magic_bonus; + + if (sd->inventory_data[index]->type == 4) + { + 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) + { + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + if (i == 8 + && sd->status.inventory[index].equip == 0x20) + sd->state.lr_flag = 1; + run_script_l (itemdb_equipscript (c), 0, sd->bl.id, + 0, 2, arg); + sd->state.lr_flag = 0; + } + } + } + } + else if (sd->inventory_data[index]->type == 5) + { // 防具 + 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) { + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + run_script_l (itemdb_equipscript (c), 0, sd->bl.id, + 0, 2, arg); + } + } + } + } + } + } + +#ifdef USE_ASTRAL_SOUL_SKILL + if (sd->spellpower_bonus_target < 0) + sd->spellpower_bonus_target = + (sd->spellpower_bonus_target * 256) / + (MIN (128 + skill_power (sd, TMW_ASTRAL_SOUL), 256)); +#endif + + if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) + sd->spellpower_bonus_current = sd->spellpower_bonus_target; + + wele = sd->atk_ele; + wele_ = sd->atk_ele_; + def_ele = sd->def_ele; + memcpy (sd->paramcard, sd->parame, sizeof (sd->paramcard)); + + // 装備品によるステータス変化はここで実行 + for (i = 0; i < 10; i++) + { + index = sd->equip_index[i]; + 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 (i == 8 && sd->status.inventory[index].equip == 0x20) + { + //二刀流用データ入力 + sd->watk_ += sd->inventory_data[index]->atk; + sd->watk_2 = (r = sd->status.inventory[index].refine) * // 精錬攻撃力 + refinebonus[wlv][0]; + if ((r -= refinebonus[wlv][2]) > 0) // 過剰精錬ボーナス + sd->overrefine_ = r * refinebonus[wlv][1]; + + if (sd->status.inventory[index].card[0] == 0x00ff) + { // 製造武器 + sd->star_ = (sd->status.inventory[index].card[1] >> 8); // 星のかけら + wele_ = (sd->status.inventory[index].card[1] & 0x0f); // 属 性 + } + sd->attackrange_ += sd->inventory_data[index]->range; + sd->state.lr_flag = 1; + { + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + run_script_l (sd->inventory_data[index]->equip_script, 0, + sd->bl.id, 0, 2, arg); + } + sd->state.lr_flag = 0; + } + else + { //二刀流武器以外 + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + sd->watk += sd->inventory_data[index]->atk; + sd->watk2 += (r = sd->status.inventory[index].refine) * // 精錬攻撃力 + refinebonus[wlv][0]; + if ((r -= refinebonus[wlv][2]) > 0) // 過剰精錬ボーナス + sd->overrefine += r * refinebonus[wlv][1]; + + if (sd->status.inventory[index].card[0] == 0x00ff) + { // 製造武器 + sd->star += (sd->status.inventory[index].card[1] >> 8); // 星のかけら + wele = (sd->status.inventory[index].card[1] & 0x0f); // 属 性 + } + sd->attackrange += sd->inventory_data[index]->range; + run_script_l (sd->inventory_data[index]->equip_script, 0, + sd->bl.id, 0, 2, arg); + } + } + else if (sd->inventory_data[index]->type == 5) + { + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + sd->watk += sd->inventory_data[index]->atk; + refinedef += + sd->status.inventory[index].refine * refinebonus[0][0]; + run_script_l (sd->inventory_data[index]->equip_script, 0, + sd->bl.id, 0, 2, arg); + } + } + } + + if (battle_is_unarmed (&sd->bl)) + { + sd->watk += skill_power (sd, TMW_BRAWLING) / 3; // +66 for 200 + sd->watk2 += skill_power (sd, TMW_BRAWLING) >> 3; // +25 for 200 + sd->watk_ += skill_power (sd, TMW_BRAWLING) / 3; // +66 for 200 + sd->watk_2 += skill_power (sd, TMW_BRAWLING) >> 3; // +25 for 200 + } + + if (sd->equip_index[10] >= 0) + { // 矢 + index = sd->equip_index[10]; + if (sd->inventory_data[index]) + { //まだ属性が入っていない + argrec_t arg[2]; + arg[0].name = "@slotId"; + arg[0].v.i = i; + arg[1].name = "@itemId"; + arg[1].v.i = sd->inventory_data[index]->nameid; + sd->state.lr_flag = 2; + run_script_l (sd->inventory_data[index]->equip_script, 0, sd->bl.id, + 0, 2, arg); + 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->atk_ele = wele; + if (wele_ > 0) + sd->atk_ele_ = wele_; + if (def_ele > 0) + sd->def_ele = def_ele; + sd->double_rate += sd->double_add_rate; + sd->perfect_hit += sd->perfect_hit_add; + sd->get_zeny_num += sd->get_zeny_add_num; + sd->splash_range += sd->splash_add_range; + if (sd->speed_add_rate != 100) + sd->speed_rate += sd->speed_add_rate - 100; + if (sd->aspd_add_rate != 100) + sd->aspd_rate += sd->aspd_add_rate - 100; + + // 武器ATKサイズ補正 (右手) + sd->atkmods[0] = atkmods[0][sd->weapontype1]; + sd->atkmods[1] = atkmods[1][sd->weapontype1]; + sd->atkmods[2] = atkmods[2][sd->weapontype1]; + //武器ATKサイズ補正 (左手) + sd->atkmods_[0] = atkmods[0][sd->weapontype2]; + sd->atkmods_[1] = atkmods[1][sd->weapontype2]; + sd->atkmods_[2] = atkmods[2][sd->weapontype2]; + +/* + // jobボーナス分 + for(i=0;i<sd->status.job_level && i<MAX_LEVEL;i++){ + if(job_bonus[s_class.upper][s_class.job][i]) + sd->paramb[job_bonus[s_class.upper][s_class.job][i]-1]++; + } +*/ + + if ((skill = pc_checkskill (sd, MC_INCCARRY)) > 0) // skill can be used with an item now, thanks to orn [Valaris] + sd->max_weight += skill * 1000; + + // ステータス変化による基本パラメータ補正 + if (sd->sc_count) + { + if (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; + } + if (sd->sc_data[SC_INCREASEAGI].timer != -1 + && sd->sc_data[SC_QUAGMIRE].timer == -1 + && sd->sc_data[SC_DONTFORGETME].timer == -1) + { // 速度増加 + sd->paramb[1] += 2 + sd->sc_data[SC_INCREASEAGI].val1; + sd->speed -= sd->speed * 25 / 100; + } + if (sd->sc_data[SC_DECREASEAGI].timer != -1) // 速度減少(agiはbattle.cで) + sd->speed = sd->speed * 125 / 100; + if (sd->sc_data[SC_CLOAKING].timer != -1) + sd->speed = + (sd->speed * (76 + (sd->sc_data[SC_INCREASEAGI].val1 * 3))) / + 100; + if (sd->sc_data[SC_BLESSING].timer != -1) + { // ブレッシング + sd->paramb[0] += sd->sc_data[SC_BLESSING].val1; + sd->paramb[3] += sd->sc_data[SC_BLESSING].val1; + sd->paramb[4] += sd->sc_data[SC_BLESSING].val1; + } + if (sd->sc_data[SC_GLORIA].timer != -1) // グロリア + sd->paramb[5] += 30; + if (sd->sc_data[SC_LOUD].timer != -1 && sd->sc_data[SC_QUAGMIRE].timer == -1) // ラウドボイス + sd->paramb[0] += 4; + if (sd->sc_data[SC_QUAGMIRE].timer != -1) + { // クァグマイア + sd->speed = sd->speed * 3 / 2; + sd->paramb[1] -= + (sd->status.agi + sd->paramb[1] + sd->parame[1]) / 2; + sd->paramb[4] -= + (sd->status.dex + sd->paramb[4] + sd->parame[4]) / 2; + } + if (sd->sc_data[SC_TRUESIGHT].timer != -1) + { // トゥルーサイト + sd->paramb[0] += 5; + sd->paramb[1] += 5; + sd->paramb[2] += 5; + sd->paramb[3] += 5; + sd->paramb[4] += 5; + sd->paramb[5] += 5; + } + } + sd->speed -= skill_power (sd, TMW_SPEED) >> 3; + sd->aspd_rate -= skill_power (sd, TMW_SPEED) / 10; + if (sd->aspd_rate < 20) + sd->aspd_rate = 20; + +/* + //1度も死んでないJob70スパノビに+10 + if(s_class.job == 23 && sd->die_counter == 0 && sd->status.job_level >= 70){ + sd->paramb[0]+= 15; + sd->paramb[1]+= 15; + sd->paramb[2]+= 15; + sd->paramb[3]+= 15; + sd->paramb[4]+= 15; + sd->paramb[5]+= 15; + } +*/ + 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]; + for (i = 0; i < 6; i++) + if (sd->paramc[i] < 0) + sd->paramc[i] = 0; + + if (sd->status.weapon == 11 || sd->status.weapon == 13 + || sd->status.weapon == 14) + { + str = sd->paramc[4]; + dex = sd->paramc[0]; + } + else + { + str = sd->paramc[0]; + dex = sd->paramc[4]; + sd->critical += ((dex * 3) >> 1); + } + dstr = str / 10; + sd->base_atk += str + dstr * dstr + dex / 5 + sd->paramc[5] / 5; +//fprintf(stderr, "baseatk = %d = x + %d + %d + %d + %d\n", sd->base_atk, str, dstr*dstr, dex/5, sd->paramc[5]/5); + 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; + } + // [Fate] New tmw magic system + sd->matk1 += sd->status.base_level + sd->spellpower_bonus_current; +#ifdef USE_ASTRAL_SOUL_SKILL + if (sd->matk1 > MAGIC_SKILL_THRESHOLD) + { + int bonus = sd->matk1 - MAGIC_SKILL_THRESHOLD; + // Ok if you are above a certain threshold, you get only (1/8) of that matk1 + // if you have Astral soul skill you can get the whole power again (and additionally the 1/8 added) + sd->matk1 = MAGIC_SKILL_THRESHOLD + (bonus>>3) + ((3*bonus*skill_power(sd, TMW_ASTRAL_SOUL))>>9); + } +#endif + sd->matk2 = 0; + if (sd->matk1 < 0) + sd->matk1 = 0; + + sd->hit += sd->paramc[4] + sd->status.base_level; + sd->flee += sd->paramc[1] + sd->status.base_level; + sd->def2 += sd->paramc[2]; + sd->mdef2 += sd->paramc[3]; + sd->flee2 += sd->paramc[5] + 10; + sd->critical += (sd->paramc[5] * 3) + 10; + + // 200 is the maximum of the skill + // def2 is the defence gained by vit, whereas "def", which is gained by armor, stays as is + int spbsk = skill_power (sd, TMW_RAGING); + if (spbsk != 0 && sd->attackrange <= 2) + { + sd->critical += sd->critical * spbsk / 100; + sd->def2 = (sd->def2 * 256) / (256 + spbsk); + } + + if (sd->base_atk < 1) + sd->base_atk = 1; + if (sd->critical_rate != 100) + sd->critical = (sd->critical * sd->critical_rate) / 100; + if (sd->critical < 10) + sd->critical = 10; + if (sd->hit_rate != 100) + sd->hit = (sd->hit * sd->hit_rate) / 100; + if (sd->hit < 1) + sd->hit = 1; + if (sd->flee_rate != 100) + sd->flee = (sd->flee * sd->flee_rate) / 100; + if (sd->flee < 1) + sd->flee = 1; + if (sd->flee2_rate != 100) + sd->flee2 = (sd->flee2 * sd->flee2_rate) / 100; + if (sd->flee2 < 10) + sd->flee2 = 10; + if (sd->def_rate != 100) + sd->def = (sd->def * sd->def_rate) / 100; + if (sd->def < 0) + sd->def = 0; + if (sd->def2_rate != 100) + sd->def2 = (sd->def2 * sd->def2_rate) / 100; + if (sd->def2 < 1) + sd->def2 = 1; + if (sd->mdef_rate != 100) + sd->mdef = (sd->mdef * sd->mdef_rate) / 100; + if (sd->mdef < 0) + sd->mdef = 0; + if (sd->mdef2_rate != 100) + sd->mdef2 = (sd->mdef2 * sd->mdef2_rate) / 100; + if (sd->mdef2 < 1) + sd->mdef2 = 1; + + // 二刀流 ASPD 修正 + if (sd->status.weapon <= 16) + sd->aspd += + aspd_base[s_class.job][sd->status.weapon] - (sd->paramc[1] * 4 + + sd->paramc[4]) * + aspd_base[s_class.job][sd->status.weapon] / 1000; + else + sd->aspd += ((aspd_base[s_class.job][sd->weapontype1] - + (sd->paramc[1] * 4 + + sd->paramc[4]) * + aspd_base[s_class.job][sd->weapontype1] / 1000) + + (aspd_base[s_class.job][sd->weapontype2] - + (sd->paramc[1] * 4 + + sd->paramc[4]) * + aspd_base[s_class.job][sd->weapontype2] / 1000)) * 140 / + 200; + + aspd_rate = sd->aspd_rate; + + //攻撃速度増加 + + if ((skill = pc_checkskill (sd, AC_VULTURE)) > 0) + { // ワシの目 + sd->hit += skill; + if (sd->status.weapon == 11) + sd->attackrange += skill; + } + + if (sd->attackrange > 2) + { // [fate] ranged weapon? + sd->attackrange += MIN (skill_power (sd, AC_OWL) / 60, 3); + sd->hit += skill_power (sd, AC_OWL) / 10; // 20 for 200 + } + + if ((skill = pc_checkskill (sd, BS_WEAPONRESEARCH)) > 0) // 武器研究の命中率増加 + sd->hit += skill * 2; + if (sd->status.option & 2 && (skill = pc_checkskill (sd, RG_TUNNELDRIVE)) > 0) // トンネルドライブ + sd->speed += (1.2 * DEFAULT_WALK_SPEED - skill * 9); + if (pc_iscarton (sd) && (skill = pc_checkskill (sd, MC_PUSHCART)) > 0) // カートによる速度低下 + sd->speed += (10 - skill) * (DEFAULT_WALK_SPEED * 0.1); + else if (pc_isriding (sd)) // ペコペコ乗りによる速度増加 + sd->speed -= (0.25 * DEFAULT_WALK_SPEED); + sd->max_weight += 1000; + if (sd->sc_count) + { + if (sd->sc_data[SC_WINDWALK].timer != -1) //ウィンドウォーク時はLv*2%減算 + sd->speed -= + sd->speed * (sd->sc_data[SC_WINDWALK].val1 * 2) / 100; + if (sd->sc_data[SC_CARTBOOST].timer != -1) // カートブースト + sd->speed -= (DEFAULT_WALK_SPEED * 20) / 100; + if (sd->sc_data[SC_BERSERK].timer != -1) //バーサーク中はIAと同じぐらい速い? + sd->speed -= sd->speed * 25 / 100; + if (sd->sc_data[SC_WEDDING].timer != -1) //結婚中は歩くのが遅い + sd->speed = 2 * DEFAULT_WALK_SPEED; + } + + if ((skill = pc_checkskill (sd, CR_TRUST)) > 0) + { // フェイス + sd->status.max_hp += skill * 200; + sd->subele[6] += skill * 5; + } + if ((skill = pc_checkskill (sd, BS_SKINTEMPER)) > 0) + sd->subele[3] += skill * 4; + + bl = sd->status.base_level; + + sd->status.max_hp += + (3500 + bl * hp_coefficient2[s_class.job] + + hp_sigma_val[s_class.job][(bl > 0) ? bl - 1 : 0]) / 100 * (100 + + sd->paramc + [2]) / + 100 + (sd->parame[2] - sd->paramcard[2]); + if (s_class.upper == 1) // [MouseJstr] + sd->status.max_hp = sd->status.max_hp * 130 / 100; + if (sd->hprate != 100) + sd->status.max_hp = sd->status.max_hp * sd->hprate / 100; + + if (sd->sc_data && sd->sc_data[SC_BERSERK].timer != -1) + { // バーサーク + sd->status.max_hp = sd->status.max_hp * 3; + sd->status.hp = sd->status.hp * 3; + if (sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.max_hp = battle_config.max_hp; + if (sd->status.hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.hp = battle_config.max_hp; + } + if (s_class.job == 23 && sd->status.base_level >= 99) + { + sd->status.max_hp = sd->status.max_hp + 2000; + } + + if (sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.max_hp = battle_config.max_hp; + if (sd->status.max_hp <= 0) + sd->status.max_hp = 1; // end + + // 最大SP計算 + sd->status.max_sp += + ((sp_coefficient[s_class.job] * bl) + 1000) / 100 * (100 + + sd->paramc[3]) / + 100 + (sd->parame[3] - sd->paramcard[3]); + if (s_class.upper == 1) // [MouseJstr] + sd->status.max_sp = sd->status.max_sp * 130 / 100; + if (sd->sprate != 100) + sd->status.max_sp = sd->status.max_sp * sd->sprate / 100; + + 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; + + if (sd->status.max_sp < 0 || sd->status.max_sp > battle_config.max_sp) + sd->status.max_sp = battle_config.max_sp; + + //自然回復HP + sd->nhealhp = 1 + (sd->paramc[2] / 5) + (sd->status.max_hp / 200); + if ((skill = pc_checkskill (sd, SM_RECOVERY)) > 0) + { // HP回復力向上 + sd->nshealhp = skill * 5 + (sd->status.max_hp * skill / 500); + if (sd->nshealhp > 0x7fff) + sd->nshealhp = 0x7fff; + } + //自然回復SP + 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; + if ((skill = pc_checkskill (sd, MG_SRECOVERY)) > 0) + { // SP回復力向上 + sd->nshealsp = skill * 3 + (sd->status.max_sp * skill / 500); + if (sd->nshealsp > 0x7fff) + sd->nshealsp = 0x7fff; + } + + if ((skill = pc_checkskill (sd, MO_SPIRITSRECOVERY)) > 0) + { + sd->nsshealhp = skill * 4 + (sd->status.max_hp * skill / 500); + sd->nsshealsp = skill * 2 + (sd->status.max_sp * skill / 500); + if (sd->nsshealhp > 0x7fff) + sd->nsshealhp = 0x7fff; + if (sd->nsshealsp > 0x7fff) + sd->nsshealsp = 0x7fff; + } + if (sd->hprecov_rate != 100) + { + sd->nhealhp = sd->nhealhp * sd->hprecov_rate / 100; + if (sd->nhealhp < 1) + sd->nhealhp = 1; + } + if (sd->sprecov_rate != 100) + { + sd->nhealsp = sd->nhealsp * sd->sprecov_rate / 100; + if (sd->nhealsp < 1) + sd->nhealsp = 1; + } + if ((skill = pc_checkskill (sd, HP_MEDITATIO)) > 0) + { // メディテイティオはSPRではなく自然回復にかかる + sd->nhealsp += 3 * skill * (sd->status.max_sp) / 100; + if (sd->nhealsp > 0x7fff) + sd->nhealsp = 0x7fff; + } + + // 種族耐性(これでいいの? ディバインプロテクションと同じ処理がいるかも) + if ((skill = pc_checkskill (sd, SA_DRAGONOLOGY)) > 0) + { // ドラゴノロジー + skill = skill * 4; + sd->addrace[9] += skill; + sd->addrace_[9] += skill; + sd->subrace[9] += skill; + sd->magic_addrace[9] += skill; + sd->magic_subrace[9] -= skill; + } + + //Flee上昇 + if ((skill = pc_checkskill (sd, TF_MISS)) > 0) + { // 回避率増加 + if (sd->status.pc_class == 6 || sd->status.pc_class == 4007 + || sd->status.pc_class == 23) + { + sd->flee += skill * 3; + } + if (sd->status.pc_class == 12 || sd->status.pc_class == 17 + || sd->status.pc_class == 4013 || sd->status.pc_class == 4018) + sd->flee += skill * 4; + if (sd->status.pc_class == 12 || sd->status.pc_class == 4013) + sd->speed -= sd->speed * (skill * .5) / 100; + } + if ((skill = pc_checkskill (sd, MO_DODGE)) > 0) // 見切り + sd->flee += (skill * 3) >> 1; + + // スキルやステータス異常による残りのパラメータ補正 + if (sd->sc_count) + { + // ATK/DEF変化形 + if (sd->sc_data[SC_ANGELUS].timer != -1) // エンジェラス + sd->def2 = + sd->def2 * (110 + 5 * sd->sc_data[SC_ANGELUS].val1) / 100; + if (sd->sc_data[SC_IMPOSITIO].timer != -1) + { // インポシティオマヌス + sd->watk += sd->sc_data[SC_IMPOSITIO].val1 * 5; + index = sd->equip_index[8]; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->type == 4) + sd->watk_ += sd->sc_data[SC_IMPOSITIO].val1 * 5; + } + if (sd->sc_data[SC_PROVOKE].timer != -1) + { // プロボック + sd->def2 = + sd->def2 * (100 - 6 * sd->sc_data[SC_PROVOKE].val1) / 100; + sd->base_atk = + sd->base_atk * (100 + 2 * sd->sc_data[SC_PROVOKE].val1) / 100; + sd->watk = + sd->watk * (100 + 2 * sd->sc_data[SC_PROVOKE].val1) / 100; + index = sd->equip_index[8]; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->type == 4) + sd->watk_ = + sd->watk_ * (100 + + 2 * sd->sc_data[SC_PROVOKE].val1) / 100; + } + if (sd->sc_data[SC_ENDURE].timer != -1) + sd->mdef2 += sd->sc_data[SC_ENDURE].val1; + if (sd->sc_data[SC_MINDBREAKER].timer != -1) + { // プロボック + sd->mdef2 = + sd->mdef2 * (100 - + 6 * sd->sc_data[SC_MINDBREAKER].val1) / 100; + sd->matk1 = + sd->matk1 * (100 + + 2 * sd->sc_data[SC_MINDBREAKER].val1) / 100; + sd->matk2 = + sd->matk2 * (100 + + 2 * sd->sc_data[SC_MINDBREAKER].val1) / 100; + } + if (sd->sc_data[SC_POISON].timer != -1) // 毒状態 + sd->def2 = sd->def2 * 75 / 100; + if (sd->sc_data[SC_DRUMBATTLE].timer != -1) + { // 戦太鼓の響き + sd->watk += sd->sc_data[SC_DRUMBATTLE].val2; + sd->def += sd->sc_data[SC_DRUMBATTLE].val3; + index = sd->equip_index[8]; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->type == 4) + sd->watk_ += sd->sc_data[SC_DRUMBATTLE].val2; + } + 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 == 3) + sd->watk += sd->sc_data[SC_NIBELUNGEN].val3; + index = sd->equip_index[8]; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->wlv == 3) + sd->watk_ += sd->sc_data[SC_NIBELUNGEN].val3; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->wlv == 4) + sd->watk += 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->watk_ += sd->sc_data[SC_NIBELUNGEN].val2; + } + + if (sd->sc_data[SC_VOLCANO].timer != -1 && sd->def_ele == 3) + { // ボルケーノ + sd->watk += sd->sc_data[SC_VIOLENTGALE].val3; + } + + if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1) + sd->def = + sd->def * (100 - sd->sc_data[SC_SIGNUMCRUCIS].val2) / 100; + if (sd->sc_data[SC_ETERNALCHAOS].timer != -1) // エターナルカオス + sd->def = 0; + + if (sd->sc_data[SC_CONCENTRATION].timer != -1) + { //コンセントレーション + sd->watk = + sd->watk * (100 + + 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; + index = sd->equip_index[8]; + if (index >= 0 && sd->inventory_data[index] + && sd->inventory_data[index]->type == 4) + sd->watk_ = + sd->watk * (100 + + 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; + sd->def = + sd->def * (100 - + 5 * sd->sc_data[SC_CONCENTRATION].val1) / 100; + } + + if (sd->sc_data[SC_MAGICPOWER].timer != -1) + { //魔法力増幅 + sd->matk1 = + sd->matk1 * (100 + 2 * sd->sc_data[SC_MAGICPOWER].val1) / 100; + sd->matk2 = + sd->matk2 * (100 + 2 * sd->sc_data[SC_MAGICPOWER].val1) / 100; + } + if (sd->sc_data[SC_ATKPOT].timer != -1) + sd->watk += sd->sc_data[SC_ATKPOT].val1; + if (sd->sc_data[SC_MATKPOT].timer != -1) + { + sd->matk1 += sd->sc_data[SC_MATKPOT].val1; + sd->matk2 += sd->sc_data[SC_MATKPOT].val1; + } + + // ASPD/移動速度変化系 + if (sd->sc_data[SC_TWOHANDQUICKEN].timer != -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + if (sd->sc_data[SC_ADRENALINE].timer != -1 + && sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sd->sc_data[SC_QUAGMIRE].timer == -1 + && sd->sc_data[SC_DONTFORGETME].timer == -1) + { // アドレナリンラッシュ + if (sd->sc_data[SC_ADRENALINE].val2 + || !battle_config.party_skill_penaly) + aspd_rate -= 30; + else + aspd_rate -= 25; + } + if (sd->sc_data[SC_SPEARSQUICKEN].timer != -1 && sd->sc_data[SC_ADRENALINE].timer == -1 && sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sd->sc_data[SC_SPEARSQUICKEN].val2; + if (sd->sc_data[SC_ASSNCROS].timer != -1 && // 夕陽のアサシンクロス + sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 + && sd->sc_data[SC_ADRENALINE].timer == -1 + && sd->sc_data[SC_SPEARSQUICKEN].timer == -1 + && sd->sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= + 5 + sd->sc_data[SC_ASSNCROS].val1 + + sd->sc_data[SC_ASSNCROS].val2 + sd->sc_data[SC_ASSNCROS].val3; + if (sd->sc_data[SC_DONTFORGETME].timer != -1) + { // 私を忘れないで + aspd_rate += + sd->sc_data[SC_DONTFORGETME].val1 * 3 + + sd->sc_data[SC_DONTFORGETME].val2 + + (sd->sc_data[SC_DONTFORGETME].val3 >> 16); + sd->speed = + sd->speed * (100 + sd->sc_data[SC_DONTFORGETME].val1 * 2 + + sd->sc_data[SC_DONTFORGETME].val2 + + (sd->sc_data[SC_DONTFORGETME].val3 & 0xffff)) / + 100; + } + if (sd->sc_data[i = SC_SPEEDPOTION2].timer != -1 || sd->sc_data[i = SC_SPEEDPOTION1].timer != -1 || sd->sc_data[i = SC_SPEEDPOTION0].timer != -1) // 増 速ポーション + aspd_rate -= sd->sc_data[i].val1; + + if (sd->sc_data[SC_HASTE].timer != -1) + aspd_rate -= sd->sc_data[SC_HASTE].val1; + + /* Slow down if protected */ + + if (sd->sc_data[SC_PHYS_SHIELD].timer != -1) + aspd_rate += sd->sc_data[SC_PHYS_SHIELD].val1; + + // HIT/FLEE変化系 + if (sd->sc_data[SC_WHISTLE].timer != -1) + { // 口笛 + sd->flee += sd->flee * (sd->sc_data[SC_WHISTLE].val1 + + sd->sc_data[SC_WHISTLE].val2 + + (sd->sc_data[SC_WHISTLE].val3 >> 16)) / + 100; + sd->flee2 += + (sd->sc_data[SC_WHISTLE].val1 + sd->sc_data[SC_WHISTLE].val2 + + (sd->sc_data[SC_WHISTLE].val3 & 0xffff)) * 10; + } + if (sd->sc_data[SC_HUMMING].timer != -1) // ハミング + sd->hit += + (sd->sc_data[SC_HUMMING].val1 * 2 + + sd->sc_data[SC_HUMMING].val2 + + sd->sc_data[SC_HUMMING].val3) * sd->hit / 100; + if (sd->sc_data[SC_VIOLENTGALE].timer != -1 && sd->def_ele == 4) + { // バイオレントゲイル + sd->flee += sd->flee * sd->sc_data[SC_VIOLENTGALE].val3 / 100; + } + if (sd->sc_data[SC_BLIND].timer != -1) + { // 暗黒 + sd->hit -= sd->hit * 25 / 100; + sd->flee -= sd->flee * 25 / 100; + } + if (sd->sc_data[SC_WINDWALK].timer != -1) // ウィンドウォーク + sd->flee += sd->flee * (sd->sc_data[SC_WINDWALK].val2) / 100; + if (sd->sc_data[SC_SPIDERWEB].timer != -1) //スパイダーウェブ + sd->flee -= sd->flee * 50 / 100; + if (sd->sc_data[SC_TRUESIGHT].timer != -1) //トゥルーサイト + sd->hit += 3 * (sd->sc_data[SC_TRUESIGHT].val1); + if (sd->sc_data[SC_CONCENTRATION].timer != -1) //コンセントレーション + sd->hit += (10 * (sd->sc_data[SC_CONCENTRATION].val1)); + + // 耐性 + 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; // 対 悪魔 + } + + // その他 + if (sd->sc_data[SC_APPLEIDUN].timer != -1) + { // イドゥンの林檎 + sd->status.max_hp += + ((5 + sd->sc_data[SC_APPLEIDUN].val1 * 2 + + ((sd->sc_data[SC_APPLEIDUN].val2 + 1) >> 1) + + sd->sc_data[SC_APPLEIDUN].val3 / 10) * sd->status.max_hp) / + 100; + if (sd->status.max_hp < 0 + || sd->status.max_hp > battle_config.max_hp) + sd->status.max_hp = battle_config.max_hp; + } + if (sd->sc_data[SC_DELUGE].timer != -1 && sd->def_ele == 1) + { // デリュージ + sd->status.max_hp += + sd->status.max_hp * sd->sc_data[SC_DELUGE].val3 / 100; + if (sd->status.max_hp < 0 + || sd->status.max_hp > battle_config.max_hp) + sd->status.max_hp = battle_config.max_hp; + } + if (sd->sc_data[SC_SERVICE4U].timer != -1) + { // サービスフォーユー + sd->status.max_sp += + sd->status.max_sp * (10 + sd->sc_data[SC_SERVICE4U].val1 + + sd->sc_data[SC_SERVICE4U].val2 + + sd->sc_data[SC_SERVICE4U].val3) / 100; + if (sd->status.max_sp < 0 + || sd->status.max_sp > battle_config.max_sp) + sd->status.max_sp = battle_config.max_sp; + sd->dsprate -= + (10 + sd->sc_data[SC_SERVICE4U].val1 * 3 + + sd->sc_data[SC_SERVICE4U].val2 + + sd->sc_data[SC_SERVICE4U].val3); + if (sd->dsprate < 0) + sd->dsprate = 0; + } + + if (sd->sc_data[SC_FORTUNE].timer != -1) // 幸運のキス + sd->critical += + (10 + sd->sc_data[SC_FORTUNE].val1 + + sd->sc_data[SC_FORTUNE].val2 + + sd->sc_data[SC_FORTUNE].val3) * 10; + + if (sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) + { // 爆裂波動 + if (s_class.job == 23) + sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val1 * 100; + else + sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val2; + } + + if (sd->sc_data[SC_STEELBODY].timer != -1) + { // 金剛 + sd->def = 90; + sd->mdef = 90; + aspd_rate += 25; + sd->speed = (sd->speed * 125) / 100; + } + if (sd->sc_data[SC_DEFENDER].timer != -1) + { + sd->aspd += (550 - sd->sc_data[SC_DEFENDER].val1 * 50); + sd->speed = + (sd->speed * (155 - sd->sc_data[SC_DEFENDER].val1 * 5)) / 100; + } + if (sd->sc_data[SC_ENCPOISON].timer != -1) + sd->addeff[4] += sd->sc_data[SC_ENCPOISON].val2; + + if (sd->sc_data[SC_DANCING].timer != -1) + { // 演奏/ダンス使用中 + sd->speed *= 4; + sd->nhealsp = 0; + sd->nshealsp = 0; + sd->nsshealsp = 0; + } + if (sd->sc_data[SC_CURSE].timer != -1) + sd->speed += 450; + + if (sd->sc_data[SC_TRUESIGHT].timer != -1) //トゥルーサイト + sd->critical += + sd->critical * (sd->sc_data[SC_TRUESIGHT].val1) / 100; + +/* if(sd->sc_data[SC_VOLCANO].timer!=-1) // エンチャントポイズン(属性はbattle.cで) + sd->addeff[2]+=sd->sc_data[SC_VOLCANO].val2;//% of granting + if(sd->sc_data[SC_DELUGE].timer!=-1) // エンチャントポイズン(属性はbattle.cで) + sd->addeff[0]+=sd->sc_data[SC_DELUGE].val2;//% of granting + */ + } + + if (sd->speed_rate != 100) + sd->speed = sd->speed * sd->speed_rate / 100; + if (sd->speed < 1) + sd->speed = 1; + if (aspd_rate != 100) + sd->aspd = sd->aspd * aspd_rate / 100; + if (pc_isriding (sd)) // 騎兵修練 + sd->aspd = + sd->aspd * (100 + + 10 * (5 - + pc_checkskill (sd, KN_CAVALIERMASTERY))) / 100; + + if (sd->attack_spell_override) + sd->aspd = sd->attack_spell_delay; + + if (sd->aspd < battle_config.max_aspd) + sd->aspd = battle_config.max_aspd; + sd->amotion = sd->aspd; + sd->dmotion = 800 - sd->paramc[1] * 4; + if (sd->dmotion < 400) + sd->dmotion = 400; + if (sd->skilltimer != -1 && (skill = pc_checkskill (sd, SA_FREECAST)) > 0) + { + sd->prev_speed = sd->speed; + sd->speed = sd->speed * (175 - skill * 5) / 100; + } + + if (sd->status.hp > sd->status.max_hp) + sd->status.hp = sd->status.max_hp; + if (sd->status.sp > sd->status.max_sp) + sd->status.sp = sd->status.max_sp; + + if (first & 4) + 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); + } + return 0; + } + + if (b_class != sd->view_class) + { + clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); + clif_changelook (&sd->bl, LOOK_WEAPON, 0); + } + + 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->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->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); + +/* if(before.cart_num != before.cart_num || before.cart_max_num != before.cart_max_num || + before.cart_weight != before.cart_weight || before.cart_max_weight != before.cart_max_weight ) + clif_updatestatus(sd,SP_CARTINFO);*/ + + if (sd->status.hp < sd->status.max_hp >> 2 + && pc_checkskill (sd, SM_AUTOBERSERK) > 0 + && (sd->sc_data[SC_PROVOKE].timer == -1 + || sd->sc_data[SC_PROVOKE].val2 == 0) && !pc_isdead (sd)) + // オートバーサーク発動 + skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, 0); + + return 0; +} + +/*========================================== + * 装 備品による能力等のボーナス設定 + *------------------------------------------ + */ +int pc_bonus (struct map_session_data *sd, int type, int val) +{ + 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->watk += val; + else if (sd->state.lr_flag == 1) + sd->watk_ += val; + break; + case SP_ATK2: + if (!sd->state.lr_flag) + sd->watk2 += val; + else if (sd->state.lr_flag == 1) + sd->watk_2 += 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_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->atk_ele = val; + else if (sd->state.lr_flag == 1) + sd->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: + if (sd->state.lr_flag != 2) + sd->speed -= val; + break; + case SP_SPEED_RATE: + if (sd->state.lr_flag != 2) + { + if (sd->speed_rate > 100 - val) + sd->speed_rate = 100 - val; + } + break; + case SP_SPEED_ADDRATE: + if (sd->state.lr_flag != 2) + sd->speed_add_rate = sd->speed_add_rate * (100 - val) / 100; + break; + case SP_ASPD: + if (sd->state.lr_flag != 2) + sd->aspd -= val * 10; + break; + case SP_ASPD_RATE: + if (sd->state.lr_flag != 2) + { + if (sd->aspd_rate > 100 - val) + sd->aspd_rate = 100 - val; + } + break; + case SP_ASPD_ADDRATE: + if (sd->state.lr_flag != 2) + sd->aspd_add_rate = sd->aspd_add_rate * (100 - val) / 100; + 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->ignore_def_ele |= 1 << val; + else if (sd->state.lr_flag == 1) + sd->ignore_def_ele_ |= 1 << val; + break; + case SP_IGNORE_DEF_RACE: + if (!sd->state.lr_flag) + sd->ignore_def_race |= 1 << val; + else if (sd->state.lr_flag == 1) + sd->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_GET_ZENY_NUM: + if (sd->state.lr_flag != 2 && sd->get_zeny_num < val) + sd->get_zeny_num = val; + break; + case SP_ADD_GET_ZENY_NUM: + if (sd->state.lr_flag != 2) + sd->get_zeny_add_num += val; + break; + case SP_DEF_RATIO_ATK_ELE: + if (!sd->state.lr_flag) + sd->def_ratio_atk_ele |= 1 << val; + else if (sd->state.lr_flag == 1) + sd->def_ratio_atk_ele_ |= 1 << val; + break; + case SP_DEF_RATIO_ATK_RACE: + if (!sd->state.lr_flag) + sd->def_ratio_atk_race |= 1 << val; + else if (sd->state.lr_flag == 1) + sd->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_RECORVER: + 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_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; + clif_updatestatus (sd, 13); + clif_updatestatus (sd, 14); + clif_updatestatus (sd, 15); + clif_updatestatus (sd, 16); + clif_updatestatus (sd, 17); + clif_updatestatus (sd, 18); + } + 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; + clif_updatestatus (sd, 14); + clif_updatestatus (sd, 15); + } + 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; + clif_updatestatus (sd, 14); + clif_updatestatus (sd, 17); + clif_updatestatus (sd, 13); + } + break; + case SP_PERFECT_HIDE: // [Valaris] + if (sd->state.lr_flag != 2) + { + sd->perfect_hiding = 1; + } + break; + case SP_DISGUISE: // Disguise script for items [Valaris] + if (sd->state.lr_flag != 2 && sd->disguiseflag == 0) + { + if (pc_isriding (sd)) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (sd->fd, + "Cannot wear disguise when riding a Peco."); + break; + } + sd->disguise = val; + clif_clearchar (&sd->bl, 9); + pc_setpos (sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + } + break; + case SP_UNBREAKABLE: + if (sd->state.lr_flag != 2) + { + sd->unbreakable += val; + } + break; + case SP_DEAF: + sd->special_state.deaf = 1; + break; + default: + if (battle_config.error_log) + printf ("pc_bonus: unknown type %d %d !\n", type, val); + break; + } + return 0; +} + +/*========================================== + * ソスソス ソスソスソスiソスノゑソスソスソスソス\ソスヘ難ソスソスフボソス[ソスiソスXソスン抵ソス + *------------------------------------------ + */ +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->addele[type2] += val; + else if (sd->state.lr_flag == 1) + sd->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->addrace[type2] += val; + else if (sd->state.lr_flag == 1) + sd->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->addsize[type2] += val; + else if (sd->state.lr_flag == 1) + sd->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 (sd->state.lr_flag != 2) + sd->addeff[type2] += val; + else + sd->arrow_addeff[type2] += val; + break; + case SP_ADDEFF2: + if (sd->state.lr_flag != 2) + sd->addeff2[type2] += val; + else + sd->arrow_addeff2[type2] += val; + break; + case SP_RESEFF: + if (sd->state.lr_flag != 2) + sd->reseff[type2] += 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_SUBRACE: + if (sd->state.lr_flag != 2) + sd->magic_subrace[type2] += val; + break; + case SP_ADD_DAMAGE_CLASS: + if (!sd->state.lr_flag) + { + for (i = 0; i < sd->add_damage_class_count; i++) + { + if (sd->add_damage_classid[i] == type2) + { + sd->add_damage_classrate[i] += val; + break; + } + } + if (i >= sd->add_damage_class_count + && sd->add_damage_class_count < 10) + { + sd->add_damage_classid[sd->add_damage_class_count] = + type2; + sd->add_damage_classrate[sd->add_damage_class_count] += + val; + sd->add_damage_class_count++; + } + } + else if (sd->state.lr_flag == 1) + { + for (i = 0; i < sd->add_damage_class_count_; i++) + { + if (sd->add_damage_classid_[i] == type2) + { + sd->add_damage_classrate_[i] += val; + break; + } + } + if (i >= sd->add_damage_class_count_ + && sd->add_damage_class_count_ < 10) + { + sd->add_damage_classid_[sd->add_damage_class_count_] = + type2; + sd->add_damage_classrate_[sd->add_damage_class_count_] += + val; + sd->add_damage_class_count_++; + } + } + break; + case SP_ADD_MAGIC_DAMAGE_CLASS: + if (sd->state.lr_flag != 2) + { + for (i = 0; i < sd->add_magic_damage_class_count; i++) + { + if (sd->add_magic_damage_classid[i] == type2) + { + sd->add_magic_damage_classrate[i] += val; + break; + } + } + if (i >= sd->add_magic_damage_class_count + && sd->add_magic_damage_class_count < 10) + { + sd->add_magic_damage_classid + [sd->add_magic_damage_class_count] = type2; + sd->add_magic_damage_classrate + [sd->add_magic_damage_class_count] += val; + sd->add_magic_damage_class_count++; + } + } + break; + case SP_ADD_DEF_CLASS: + if (sd->state.lr_flag != 2) + { + for (i = 0; i < sd->add_def_class_count; i++) + { + if (sd->add_def_classid[i] == type2) + { + sd->add_def_classrate[i] += val; + break; + } + } + if (i >= sd->add_def_class_count + && sd->add_def_class_count < 10) + { + sd->add_def_classid[sd->add_def_class_count] = type2; + sd->add_def_classrate[sd->add_def_class_count] += val; + sd->add_def_class_count++; + } + } + break; + case SP_ADD_MDEF_CLASS: + if (sd->state.lr_flag != 2) + { + for (i = 0; i < sd->add_mdef_class_count; i++) + { + if (sd->add_mdef_classid[i] == type2) + { + sd->add_mdef_classrate[i] += val; + break; + } + } + if (i >= sd->add_mdef_class_count + && sd->add_mdef_class_count < 10) + { + sd->add_mdef_classid[sd->add_mdef_class_count] = type2; + sd->add_mdef_classrate[sd->add_mdef_class_count] += val; + sd->add_mdef_class_count++; + } + } + break; + case SP_HP_DRAIN_RATE: + if (!sd->state.lr_flag) + { + sd->hp_drain_rate += type2; + sd->hp_drain_per += val; + } + else if (sd->state.lr_flag == 1) + { + sd->hp_drain_rate_ += type2; + sd->hp_drain_per_ += val; + } + break; + case SP_SP_DRAIN_RATE: + if (!sd->state.lr_flag) + { + sd->sp_drain_rate += type2; + sd->sp_drain_per += val; + } + else if (sd->state.lr_flag == 1) + { + sd->sp_drain_rate_ += type2; + sd->sp_drain_per_ += val; + } + 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; + } // end addition + default: + if (battle_config.error_log) + printf ("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; + switch (type) + { + case SP_ADD_MONSTER_DROP_ITEM: + if (sd->state.lr_flag != 2) + { + for (i = 0; i < sd->monster_drop_item_count; i++) + { + if (sd->monster_drop_itemid[i] == type2) + { + sd->monster_drop_race[i] |= 1 << type3; + if (sd->monster_drop_itemrate[i] < val) + sd->monster_drop_itemrate[i] = val; + break; + } + } + if (i >= sd->monster_drop_item_count + && sd->monster_drop_item_count < 10) + { + sd->monster_drop_itemid[sd->monster_drop_item_count] = + type2; + sd->monster_drop_race[sd->monster_drop_item_count] |= + 1 << type3; + sd->monster_drop_itemrate[sd->monster_drop_item_count] = + val; + sd->monster_drop_item_count++; + } + } + break; + case SP_AUTOSPELL: + if (sd->state.lr_flag != 2) + { + sd->autospell_id = type2; + sd->autospell_lv = type3; + sd->autospell_rate = val; + } + break; + default: + if (battle_config.error_log) + printf ("pc_bonus3: unknown type %d %d %d %d!\n", type, type2, + type3, 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) + printf ("support card skill only!\n"); + return 0; + } + if (!flag && (sd->status.skill[id].id == id || level == 0)) + { // クエスト所得ならここで条件を確認して送信する + sd->status.skill[id].lv = level; + pc_calcstatus (sd, 0); + clif_skillinfoblock (sd); + } + else if (sd->status.skill[id].lv < level) + { // 覚えられるがlvが小さいなら + sd->status.skill[id].id = id; + sd->status.skill[id].lv = level; + } + + return 0; +} + +/*========================================== + * カード挿入 + *------------------------------------------ + */ +int pc_insert_card (struct map_session_data *sd, int idx_card, int idx_equip) +{ + nullpo_retr (0, sd); + + if (idx_card >= 0 && idx_card < MAX_INVENTORY && idx_equip >= 0 + && idx_equip < MAX_INVENTORY && sd->inventory_data[idx_card]) + { + int i; + int nameid = sd->status.inventory[idx_equip].nameid; + int cardid = sd->status.inventory[idx_card].nameid; + int ep = sd->inventory_data[idx_card]->equip; + + if (nameid <= 0 || sd->inventory_data[idx_equip] == NULL || + (sd->inventory_data[idx_equip]->type != 4 && sd->inventory_data[idx_equip]->type != 5) || // 装 備じゃない + (sd->status.inventory[idx_equip].identify == 0) || // 未鑑定 + (sd->status.inventory[idx_equip].card[0] == 0x00ff) || // 製造武器 + (sd->status.inventory[idx_equip].card[0] == 0x00fe) || + ((sd->inventory_data[idx_equip]->equip & ep) == 0) || // 装 備個所違い + (sd->inventory_data[idx_equip]->type == 4 && ep == 32) || // 両 手武器と盾カード + (sd->status.inventory[idx_equip].card[0] == (short) 0xff00) + || 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; + } + } + } + else + 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); + + 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; +} + +int pc_count_all_items (struct map_session_data *player, int item_id) +{ + int i; + int count = 0; + + nullpo_retr (0, player); + + for (i = 0; i < MAX_INVENTORY; i++) + { + if (player->status.inventory[i].nameid == item_id) + count += player->status.inventory[i].amount; + } + + return count; +} + +int pc_remove_items (struct map_session_data *player, int item_id, int count) +{ + int i; + + nullpo_retr (0, player); + + for (i = 0; i < MAX_INVENTORY && count; i++) + { + if (player->status.inventory[i].nameid == item_id) + { + int to_delete = count; + /* only delete as much as we have */ + if (to_delete > player->status.inventory[i].amount) + to_delete = player->status.inventory[i].amount; + + count -= to_delete; + + pc_delitem (player, i, to_delete, + 0 /* means `really delete and update status' */ ); + + if (!count) + return 0; + } + } + return 0; +} + +/*========================================== + * アイテム追加。個数のみitem構造体の数字を無視 + *------------------------------------------ + */ +int pc_additem (struct map_session_data *sd, struct item *item_data, + int amount) +{ + struct item_data *data; + int i, w; + + MAP_LOG_PC (sd, "PICKUP %d %d", item_data->nameid, amount); + + 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 ((w = data->weight * amount) + sd->weight > sd->max_weight) + 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 (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) + { + memcpy (&sd->status.inventory[i], item_data, + sizeof (sd->status.inventory[0])); + + if (item_data->equip) + sd->status.inventory[i].equip = 0; + + sd->status.inventory[i].amount = amount; + sd->inventory_data[i] = data; + clif_additem (sd, i, amount, 0); + } + else + return 4; + } + sd->weight += 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->trade_partner != 0) + trade_tradecancel (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, 0); + 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 (sd->trade_partner != 0 || sd->npc_id != 0 || sd->state.storage_flag) + return 0; // no dropping while trading/npc/storage + + if (n < 0 || n >= MAX_INVENTORY) + return 0; + + if (amount <= 0) + return 0; + + pc_unequipinvyitem (sd, n, 0); + + if (sd->status.inventory[n].nameid <= 0 || + sd->status.inventory[n].amount < amount || + sd->trade_partner != 0 || sd->status.inventory[n].amount <= 0) + return 1; + map_addflooritem (&sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, + sd->bl.y, NULL, NULL, NULL, 0); + pc_delitem (sd, n, amount, 0); + + return 0; +} + +/*========================================== + * アイテムを拾う + *------------------------------------------ + */ + +static int can_pick_item_up_from (struct map_session_data *self, int other_id) +{ + struct party *p = party_search (self->status.party_id); + + /* From ourselves or from no-one? */ + if (!self || self->bl.id == other_id || !other_id) + return 1; + + struct map_session_data *other = map_id2sd (other_id); + + /* Other no longer exists? */ + if (!other) + return 1; + + /* From our partner? */ + if (self->status.partner_id == other->status.char_id) + return 1; + + /* From a party member? */ + if (self->status.party_id + && self->status.party_id == other->status.party_id + && p && p->item != 0) + return 1; + + /* From someone who is far away? */ + /* On another map? */ + if (other->bl.m != self->bl.m) + return 1; + else + { + int distance_x = abs (other->bl.x - self->bl.x); + int distance_y = abs (other->bl.y - self->bl.y); + int distance = (distance_x > distance_y) ? distance_x : distance_y; + + return distance > battle_config.drop_pickup_safety_zone; + } +} + +int pc_takeitem (struct map_session_data *sd, struct flooritem_data *fitem) +{ + int flag; + unsigned int tick = gettick (); + int can_take; + + nullpo_retr (0, sd); + nullpo_retr (0, fitem); + + /* Sometimes the owners reported to us are buggy: */ + + if (fitem->first_get_id == fitem->third_get_id + || fitem->second_get_id == fitem->third_get_id) + fitem->third_get_id = 0; + + if (fitem->first_get_id == fitem->second_get_id) + { + fitem->second_get_id = fitem->third_get_id; + fitem->third_get_id = 0; + } + + can_take = can_pick_item_up_from (sd, fitem->first_get_id); + if (!can_take) + can_take = fitem->first_get_tick <= tick + && can_pick_item_up_from (sd, fitem->second_get_id); + + if (!can_take) + can_take = fitem->second_get_tick <= tick + && can_pick_item_up_from (sd, fitem->third_get_id); + + if (!can_take) + can_take = fitem->third_get_tick <= tick; + + if (can_take) + { + /* Can pick up */ + + if ((flag = + pc_additem (sd, &fitem->item_data, fitem->item_data.amount))) + // 重量overで取得失敗 + clif_additem (sd, 0, 0, flag); + else + { + // 取得成功 + if (sd->attacktimer != -1) + pc_stopattack (sd); + clif_takeitem (&sd->bl, &fitem->bl); + map_clearflooritem (fitem->bl.id); + } + return 0; + } + + /* Otherwise, we can't pick up */ + clif_additem (sd, 0, 0, 6); + 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; + if (itemdb_type (nameid) != 0) + return 0; + if ((nameid == 605) && map[sd->bl.m].flag.gvg) + return 0; + if (nameid == 601 + && (map[sd->bl.m].flag.noteleport || map[sd->bl.m].flag.gvg)) + { + clif_skill_teleportmessage (sd, 0); + return 0; + } + + if (nameid == 602 && map[sd->bl.m].flag.noreturn) + return 0; + if (nameid == 604 + && (map[sd->bl.m].flag.nobranch || map[sd->bl.m].flag.gvg)) + return 0; + if (item->sex != 2 && sd->status.sex != item->sex) + return 0; + if (item->elv > 0 && sd->status.base_level < item->elv) + return 0; + + return 1; +} + +/*========================================== + * アイテムを使う + *------------------------------------------ + */ +int pc_useitem (struct map_session_data *sd, int n) +{ + int nameid, amount; + + nullpo_retr (1, sd); + + if (n >= 0 && n < MAX_INVENTORY && sd->inventory_data[n]) + { + nameid = sd->status.inventory[n].nameid; + amount = sd->status.inventory[n].amount; + if (sd->status.inventory[n].nameid <= 0 + || sd->status.inventory[n].amount <= 0 + || sd->sc_data[SC_BERSERK].timer != -1 || !pc_isUseitem (sd, n)) + { + clif_useitemack (sd, n, 0, 0); + return 1; + } + + run_script (sd->inventory_data[n]->use_script, 0, sd->bl.id, 0); + + clif_useitemack (sd, n, amount - 1, 1); + pc_delitem (sd, n, 1, 1); + } + + 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 ((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_INVENTORY) + return 1; + + nullpo_retr (0, item_data = &sd->status.inventory[idx]); + + if (!pc_iscarton (sd)) + return 1; + if (item_data->nameid == 0 || item_data->amount < amount) + 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); + + if (idx < 0 || idx >= MAX_CART) + return -1; + + nullpo_retr (-1, item_data = &sd->status.cart[idx]); + + if (!pc_iscarton (sd)) + return -1; + 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) + return 1; + + nullpo_retr (0, item_data = &sd->status.cart[idx]); + + if (!pc_iscarton (sd)) + return 1; + if (item_data->nameid == 0 || item_data->amount < amount) + 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_item_identify (struct map_session_data *sd, int idx) +{ + int flag = 1; + + nullpo_retr (0, 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); + } + else + clif_item_identified (sd, idx, flag); + + return !flag; +} + +/*========================================== + * スティル品公開 + *------------------------------------------ + */ +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) +{ + if (sd != NULL && bl != NULL && bl->type == BL_MOB) + { + int i, skill, rate, itemid, flag, count; + struct mob_data *md; + md = (struct mob_data *) bl; + if (!md->state.steal_flag && mob_db[md->mob_class].mexp <= 0 && + !(mob_db[md->mob_class].mode & 0x20) && + md->sc_data[SC_STONE].timer == -1 && + md->sc_data[SC_FREEZE].timer == -1 && + (!(md->mob_class > 1324 && md->mob_class < 1364))) // prevent stealing from treasure boxes [Valaris] + { + skill = sd->paramc[4] - mob_db[md->mob_class].dex + + pc_checkskill (sd, TF_STEAL) + 10; + + if (0 < skill) + { + for (count = 8; count <= 8 && count != 0; count--) + { + i = rand () % 8; + itemid = mob_db[md->mob_class].dropitem[i].nameid; + + if (itemid > 0 && itemdb_type (itemid) != 6) + { + rate = + (mob_db[md->mob_class].dropitem[i].p / + battle_config.item_rate_common * 100 * skill) / + 100; + + if (rand () % 10000 < rate) + { + struct item tmp_item; + memset (&tmp_item, 0, sizeof (tmp_item)); + tmp_item.nameid = itemid; + tmp_item.amount = 1; + tmp_item.identify = 1; + 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, 0); + } + + if (flag) + { + if (battle_config.show_steal_in_same_party) + { + party_foreachsamemap (pc_show_steal, sd, + 1, sd, + tmp_item.nameid, 1); + } + + clif_additem (sd, 0, 0, flag); + } + md->state.steal_flag = 1; + return 1; + } + } + } + } + } + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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 && md->sc_data + && md->sc_data[SC_STONE].timer == -1 + && md->sc_data[SC_FREEZE].timer == -1) + { + skill = pc_checkskill (sd, RG_STEALCOIN) * 10; + rate = + skill + (sd->status.base_level - mob_db[md->mob_class].lv) * 3 + + sd->paramc[4] * 2 + sd->paramc[5] * 2; + if (MRAND (1000) < rate) + { + pc_getzeny (sd, mob_db[md->mob_class].lv * 10 + MRAND (100)); + md->state.steal_coin_flag = 1; + return 1; + } + } + } + + return 0; +} + +// +// +// +/*========================================== + * PCの位置設定 + *------------------------------------------ + */ +int pc_setpos (struct map_session_data *sd, char *mapname_org, int x, int y, + int clrtype) +{ + char mapname[24]; + int m = 0, c = 0, disguise = 0; + + nullpo_retr (0, sd); + + if (sd->chatID) // チャットから出る + chat_leavechat (sd); + if (sd->trade_partner) // 取引を中断する + trade_tradecancel (sd); + if (sd->state.storage_flag == 1) + storage_storage_quit (sd); // 倉庫を開いてるなら保存する + 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); + + skill_castcancel (&sd->bl, 0); // 詠唱中断 + pc_stop_walking (sd, 0); // 歩行中断 + pc_stopattack (sd); // 攻撃中断 + + if (pc_issit (sd)) + { +// pc_setstand (sd); // [fate] Nothing wrong with warping while sitting + skill_gangsterparadise (sd, 0); + } + + if (sd->sc_data[SC_TRICKDEAD].timer != -1) + skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); + if (sd->status.option & 2) + skill_status_change_end (&sd->bl, SC_HIDING, -1); + if (sd->status.option & 4) + skill_status_change_end (&sd->bl, SC_CLOAKING, -1); + if (sd->status.option & 16386) + skill_status_change_end (&sd->bl, SC_CHASEWALK, -1); + if (sd->sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end (&sd->bl, SC_BLADESTOP, -1); + if (sd->sc_data[SC_DANCING].timer != -1) // clear dance effect when warping [Valaris] + skill_stop_dancing (&sd->bl, 0); + + if (sd->disguise) + { // clear disguises when warping [Valaris] + clif_clearchar (&sd->bl, 9); + disguise = sd->disguise; + sd->disguise = 0; + } + + memcpy (mapname, mapname_org, 24); + mapname[16] = 0; + if (strstr (mapname, ".gat") == NULL && strlen (mapname) < 16) + { + strcat (mapname, ".gat"); + } + + m = map_mapname2mapid (mapname); + if (m < 0) + { + if (sd->mapname[0]) + { + int ip, port; + if (map_mapname2ipport (mapname, &ip, &port) == 0) + { + skill_stop_dancing (&sd->bl, 1); + skill_unit_out_all (&sd->bl, gettick (), 1); + clif_clearchar_area (&sd->bl, clrtype & 0xffff); + skill_gangsterparadise (sd, 0); + map_delblock (&sd->bl); + memcpy (sd->mapname, mapname, 24); + sd->bl.x = x; + sd->bl.y = y; + sd->state.waitingdisconnect = 1; + pc_makesavestatus (sd); + //The storage close routines save the char data. [Skotlex] + if (!sd->state.storage_flag) + chrif_save (sd); + else if (sd->state.storage_flag == 1) + storage_storage_quit (sd); + else if (sd->state.storage_flag == 2) + storage_guild_storage_quit (sd, 1); + + chrif_changemapserver (sd, mapname, x, y, ip, port); + return 0; + } + } +#if 0 + clif_authfail_fd (sd->fd, 0); // cancel + clif_setwaitclose (sd->fd); +#endif + return 1; + } + + if (x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys) + x = y = 0; + if ((x == 0 && y == 0) || (c = read_gat (m, x, y)) == 1 || c == 5) + { + if (x || y) + { + if (battle_config.error_log) + printf ("stacked (%d,%d)\n", x, y); + } + do + { + x = MRAND (map[m].xs - 2) + 1; + y = MRAND (map[m].ys - 2) + 1; + } + while ((c = read_gat (m, x, y)) == 1 || c == 5); + } + + if (sd->mapname[0] && sd->bl.prev != NULL) + { + skill_unit_out_all (&sd->bl, gettick (), 1); + clif_clearchar_area (&sd->bl, clrtype & 0xffff); + skill_gangsterparadise (sd, 0); + map_delblock (&sd->bl); + clif_changemap (sd, map[m].name, x, y); // [MouseJstr] + } + + if (disguise) // disguise teleport fix [Valaris] + sd->disguise = disguise; + + memcpy (sd->mapname, mapname, 24); + 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; + +// map_addblock(&sd->bl); // ブロック登録とspawnは +// clif_spawnpc(sd); + + return 0; +} + +/*========================================== + * PCのランダムワープ + *------------------------------------------ + */ +int pc_randomwarp (struct map_session_data *sd, int type) +{ + int x, y, c, i = 0; + int m; + + nullpo_retr (0, sd); + + m = sd->bl.m; + + if (map[sd->bl.m].flag.noteleport) // テレポート禁止 + return 0; + + do + { + x = MRAND (map[m].xs - 2) + 1; + y = MRAND (map[m].ys - 2) + 1; + } + while (((c = read_gat (m, x, y)) == 1 || c == 5) && (i++) < 1000); + + if (i < 1000) + pc_setpos (sd, map[m].name, 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 (strcmp (sd->status.memo_point[j].map, map[sd->bl.m].name) == 0) + { + 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; + } + memcpy (sd->status.memo_point[i].map, map[sd->bl.m].name, 24); + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +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 void pc_walk (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd; + int i, ctype; + int moveblock; + int x, y, dx, dy; + + sd = map_id2sd (id); + if (sd == NULL) + return; + + if (sd->walktimer != tid) + { + if (battle_config.error_log) + printf ("pc_walk %d != %d\n", sd->walktimer, tid); + return; + } + sd->walktimer = -1; + if (sd->walkpath.path_pos >= sd->walkpath.path_len + || sd->walkpath.path_pos != data) + return; + + //歩いたので息吹のタイマーを初期化 + 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; + } + } + else + { // マス目境界へ到着 + if (sd->walkpath.path[sd->walkpath.path_pos] >= 8) + return; + + x = sd->bl.x; + y = sd->bl.y; + ctype = map_getcell (sd->bl.m, x, y); + if (ctype == 1 || ctype == 5) + { + pc_stop_walking (sd, 1); + return; + } + sd->dir = sd->head_dir = sd->walkpath.path[sd->walkpath.path_pos]; + dx = dirx[(int) sd->dir]; + dy = diry[(int) sd->dir]; + ctype = map_getcell (sd->bl.m, x + dx, y + dy); + if (ctype == 1 || ctype == 5) + { + pc_walktoxy_sub (sd); + return; + } + + moveblock = (x / BLOCK_SIZE != (x + dx) / BLOCK_SIZE + || y / BLOCK_SIZE != (y + dy) / BLOCK_SIZE); + + sd->walktimer = 1; + map_foreachinmovearea (clif_pcoutsight, sd->bl.m, x - AREA_SIZE, + y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, + dx, dy, 0, sd); + + x += dx; + y += dy; + + if (moveblock) + map_delblock (&sd->bl); + sd->bl.x = x; + sd->bl.y = y; + if (moveblock) + map_addblock (&sd->bl); + + 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); + + map_foreachinmovearea (clif_pcinsight, sd->bl.m, x - AREA_SIZE, + y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, + -dx, -dy, 0, sd); + sd->walktimer = -1; + + if (sd->status.party_id > 0) + { // パーティのHP情報通知検査 + struct party *p = party_search (sd->status.party_id); + if (p != NULL) + { + int p_flag = 0; + map_foreachinmovearea (party_send_hp_check, sd->bl.m, + x - AREA_SIZE, y - AREA_SIZE, + x + AREA_SIZE, y + AREA_SIZE, -dx, -dy, + BL_PC, sd->status.party_id, &p_flag); + if (p_flag) + sd->party_hp = -1; + } + } + if (sd->status.option & 4) // クローキングの消滅検査 + skill_check_cloaking (&sd->bl); + // ディボーション検査 + for (i = 0; i < 5; i++) + if (sd->dev.val1[i]) + { + skill_devotion3 (&sd->bl, sd->dev.val1[i]); + break; + } + // 被ディボーション検査 + if (sd->sc_data && sd->sc_data[SC_DEVOTION].val1) + { + skill_devotion2 (&sd->bl, sd->sc_data[SC_DEVOTION].val1); + } + + skill_unit_move (&sd->bl, tick, 1); // スキルユニットの検査 + + if (map_getcell (sd->bl.m, x, y) & 0x80) + 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); + } +} + +/*========================================== + * 移動可能か確認して、可能なら歩行開始 + *------------------------------------------ + */ +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 (pc_issit (sd)) + pc_setstand (sd); + + if (sd->walktimer != -1 && sd->state.change_walk_target == 0) + { + // 現在歩いている最中の目的地変更なのでマス目の中心に来た時に + // timer関数からpc_walktoxy_subを呼ぶようにする + sd->state.change_walk_target = 1; + } + else + { + pc_walktoxy_sub (sd); + } + + return 0; +} + +/*========================================== + * 歩 行停止 + *------------------------------------------ + */ +int pc_stop_walking (struct map_session_data *sd, int type) +{ + nullpo_retr (0, sd); + + if (sd->walktimer != -1) + { + 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 (type & 0x02 && battle_config.pc_damage_delay) + { + unsigned int tick = gettick (); + int delay = battle_get_dmotion (&sd->bl); + if (sd->canmove_tick < tick) + sd->canmove_tick = tick + delay; + } + + return 0; +} + +void pc_touch_all_relevant_npcs (struct map_session_data *sd) +{ + if (map_getcell (sd->bl.m, sd->bl.x, sd->bl.y) & 0x80) + npc_touch_areanpc (sd, sd->bl.m, sd->bl.x, sd->bl.y); + else + sd->areanpc_id = 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int pc_movepos (struct map_session_data *sd, int dst_x, int dst_y) +{ + int moveblock; + int dx, dy, dist; + + struct walkpath_data wpd; + + nullpo_retr (0, sd); + + if (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; + dist = distance (sd->bl.x, sd->bl.y, dst_x, dst_y); + + moveblock = (sd->bl.x / BLOCK_SIZE != dst_x / BLOCK_SIZE + || sd->bl.y / BLOCK_SIZE != dst_y / BLOCK_SIZE); + + 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, 0, sd); + + if (moveblock) + map_delblock (&sd->bl); + sd->bl.x = dst_x; + sd->bl.y = dst_y; + if (moveblock) + map_addblock (&sd->bl); + + 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, 0, sd); + + if (sd->status.party_id > 0) + { // パーティのHP情報通知検査 + struct party *p = party_search (sd->status.party_id); + if (p != NULL) + { + int flag = 0; + map_foreachinmovearea (party_send_hp_check, 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_PC, sd->status.party_id, + &flag); + if (flag) + sd->party_hp = -1; + } + } + + if (sd->status.option & 4) // クローキングの消滅検査 + skill_check_cloaking (&sd->bl); + + skill_unit_move (&sd->bl, gettick (), dist + 7); // スキルユニットの検査 + + pc_touch_all_relevant_npcs (sd); + 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_data == NULL) + return 0; + + if (!(skill_get_weapontype (KN_TWOHANDQUICKEN) & (1 << sd->status.weapon)) + && sd->sc_data[SC_TWOHANDQUICKEN].timer != -1) + { // 2HQ + skill_status_change_end (&sd->bl, SC_TWOHANDQUICKEN, -1); // 2HQを解除 + return -1; + } + if (!(skill_get_weapontype (LK_AURABLADE) & (1 << sd->status.weapon)) + && sd->sc_data[SC_AURABLADE].timer != -1) + { // オーラブレード + skill_status_change_end (&sd->bl, SC_AURABLADE, -1); // オーラブレードを解除 + return -1; + } + if (!(skill_get_weapontype (LK_PARRYING) & (1 << sd->status.weapon)) + && sd->sc_data[SC_PARRYING].timer != -1) + { // パリイング + skill_status_change_end (&sd->bl, SC_PARRYING, -1); // パリイングを解除 + return -1; + } + if (!(skill_get_weapontype (LK_CONCENTRATION) & (1 << sd->status.weapon)) + && sd->sc_data[SC_CONCENTRATION].timer != -1) + { // コンセントレーション + skill_status_change_end (&sd->bl, SC_CONCENTRATION, -1); // コンセントレーションを解除 + return -1; + } + if (!(skill_get_weapontype (CR_SPEARQUICKEN) & (1 << sd->status.weapon)) + && sd->sc_data[SC_SPEARSQUICKEN].timer != -1) + { // スピアクィッケン + skill_status_change_end (&sd->bl, SC_SPEARSQUICKEN, -1); // スピアクイッケンを解除 + return -1; + } + if (!(skill_get_weapontype (BS_ADRENALINE) & (1 << sd->status.weapon)) + && sd->sc_data[SC_ADRENALINE].timer != -1) + { // アドレナリンラッシュ + skill_status_change_end (&sd->bl, SC_ADRENALINE, -1); // アドレナリンラッシュを解除 + return -1; + } + + if (sd->status.shield <= 0) + { + if (sd->sc_data[SC_AUTOGUARD].timer != -1) + { // オートガード + skill_status_change_end (&sd->bl, SC_AUTOGUARD, -1); + return -1; + } + if (sd->sc_data[SC_DEFENDER].timer != -1) + { // ディフェンダー + skill_status_change_end (&sd->bl, SC_DEFENDER, -1); + return -1; + } + if (sd->sc_data[SC_REFLECTSHIELD].timer != -1) + { //リフレクトシールド + skill_status_change_end (&sd->bl, SC_REFLECTSHIELD, -1); + return -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 < MAX_PC_CLASS) + { //通常 + bj.job = b_class; + bj.upper = 0; + } + else if (b_class >= 4001 && b_class < 4023) + { //転生職 + bj.job = b_class - 4001; + bj.upper = 1; + } + else if (b_class == 23 + 4023 - 1) + { //養子スパノビ + bj.job = b_class - (4023 - 1); + bj.upper = 2; + } + else + { //養子スパノビ以外の養子 + bj.job = b_class - 4023; + bj.upper = 2; + } + + if (battle_config.enable_upper_class == 0) + { //confで無効になっていたらupper=0 + bj.upper = 0; + } + + if (bj.job == 0) + { + bj.type = 0; + } + else if (bj.job < 7) + { + bj.type = 1; + } + else + { + bj.type = 2; + } + + return bj; +} + +/*========================================== + * PCの攻撃 (timer関数) + *------------------------------------------ + */ +void pc_attack_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd; + struct block_list *bl; + struct status_change *sc_data; + short *opt; + int dist, skill, range; + int attack_spell_delay; + + sd = map_id2sd (id); + if (sd == NULL) + return; + if (sd->attacktimer != tid) + { + if (battle_config.error_log) + printf ("pc_attack_timer %d != %d\n", sd->attacktimer, tid); + return; + } + sd->attacktimer = -1; + + if (sd->bl.prev == NULL) + return; + + bl = map_id2bl (sd->attacktarget); + if (bl == NULL || bl->prev == NULL) + return; + + if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) + return; + + // 同じmapでないなら攻撃しない + // PCが死んでても攻撃しない + if (sd->bl.m != bl->m || pc_isdead (sd)) + return; + + if (sd->opt1 > 0 || sd->status.option & 2 || sd->status.option & 16388) // 異常などで攻撃できない + return; + + if (sd->sc_data[SC_AUTOCOUNTER].timer != -1) + return; + if (sd->sc_data[SC_BLADESTOP].timer != -1) + return; + + if ((opt = battle_get_option (bl)) != NULL && *opt & 0x46) + return; + if (((sc_data = battle_get_sc_data (bl)) != NULL + && sc_data[SC_TRICKDEAD].timer != -1) + || ((sc_data = battle_get_sc_data (bl)) != NULL + && sc_data[SC_BASILICA].timer != -1)) + return; + + if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) <= 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->attackabletime > tick) + return; // cannot attack yet + + attack_spell_delay = sd->attack_spell_delay; + if (sd->attack_spell_override // [Fate] If we have an active attack spell, use that + && spell_attack (id, sd->attacktarget)) + { + // Return if the spell succeeded. If the spell had disspiated, spell_attack() may fail. + sd->attackabletime = tick + attack_spell_delay; + + } + else + { + dist = distance (sd->bl.x, sd->bl.y, bl->x, bl->y); + range = sd->attackrange; + if (sd->status.weapon != 11) + range++; + if (dist > range) + { // 届 かないので移動 + //if(pc_can_reach(sd,bl->x,bl->y)) + //clif_movetoattack(sd,bl); + return; + } + + if (dist <= range && !battle_check_range (&sd->bl, bl, range)) + { + if (pc_can_reach (sd, bl->x, bl->y) && sd->canmove_tick < tick + && (sd->sc_data[SC_ANKLE].timer == -1 + || sd->sc_data[SC_SPIDERWEB].timer == -1)) + // TMW client doesn't support this + //pc_walktoxy(sd,bl->x,bl->y); + clif_movetoattack (sd, bl); + sd->attackabletime = tick + (sd->aspd << 1); + } + else + { + 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 (sd->sc_data[SC_COMBO].timer == -1) + { + map_freeblock_lock (); + pc_stop_walking (sd, 0); + 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) + skill_status_change_end (&sd->bl, SC_CLOAKING, -1); + 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); + } + else if (sd->attackabletime <= tick) + { + 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->attackabletime <= tick) + sd->attackabletime = tick + (battle_config.max_aspd << 1); + } + } + + if (sd->state.attack_continue) + { + sd->attacktimer = + add_timer (sd->attackabletime, pc_attack_timer, sd->bl.id, 0); + } +} + +/*========================================== + * 攻撃要求 + * typeが1なら継続攻撃 + *------------------------------------------ + */ +int pc_attack (struct map_session_data *sd, int target_id, int type) +{ + struct block_list *bl; + int d; + + 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, RFIFOL (sd->fd, 2)); + return 0; + } + + if (!battle_check_target (&sd->bl, bl, BCT_ENEMY)) + return 1; + if (sd->attacktimer != -1) + pc_stopattack (sd); + sd->attacktarget = target_id; + sd->state.attack_continue = type; + + d = DIFF_TICK (sd->attackabletime, gettick ()); + if (d > 0 && d < 2000) + { // 攻撃delay中 + sd->attacktimer = + add_timer (sd->attackabletime, pc_attack_timer, sd->bl.id, 0); + } + else + { + // 本来timer関数なので引数を合わせる + 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; +} + +void pc_follow_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd, *bl; + + sd = map_id2sd (id); + if (sd == NULL || sd->followtimer != tid) + return; + + sd->followtimer = -1; + + do + { + if (sd->bl.prev == NULL) + break; + + bl = (struct map_session_data *) map_id2bl (sd->followtarget); + + if (bl == NULL) + return; + + if (bl->bl.prev == NULL) + break; + + if (bl->bl.type == BL_PC + && pc_isdead ((struct map_session_data *) bl)) + return; + + if (sd->skilltimer == -1 && sd->attacktimer == -1 + && sd->walktimer == -1) + { + if ((sd->bl.m == bl->bl.m) + && pc_can_reach (sd, bl->bl.x, bl->bl.y)) + { + if (distance (sd->bl.x, sd->bl.y, bl->bl.x, bl->bl.y) > 5) + pc_walktoxy (sd, bl->bl.x, bl->bl.y); + } + else + pc_setpos ((struct map_session_data *) sd, bl->mapname, + bl->bl.x, bl->bl.y, 3); + } + } + while (0); + + sd->followtimer = + add_timer (tick + sd->aspd, pc_follow_timer, sd->bl.id, 0); +} + +int pc_follow (struct map_session_data *sd, int target_id) +{ + struct block_list *bl; + + bl = map_id2bl (target_id); + if (bl == NULL) + return 1; + sd->followtarget = target_id; + if (sd->followtimer != -1) + { + delete_timer (sd->followtimer, pc_follow_timer); + sd->followtimer = -1; + } + + 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) + { + struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); + + // base側レベルアップ処理 + sd->status.base_exp -= next; + + sd->status.base_level++; + sd->status.status_point += (sd->status.base_level + 14) / 4; + clif_updatestatus (sd, SP_STATUSPOINT); + clif_updatestatus (sd, SP_BASELEVEL); + clif_updatestatus (sd, SP_NEXTBASEEXP); + pc_calcstatus (sd, 0); + pc_heal (sd, sd->status.max_hp, sd->status.max_sp); + + //スパノビはキリエ、イムポ、マニピ、グロ、サフラLv1がかかる + if (s_class.job == 23) + { + skill_status_change_start (&sd->bl, + SkillStatusChangeTable[PR_KYRIE], 1, 0, + 0, 0, skill_get_time (PR_KYRIE, 1), 0); + skill_status_change_start (&sd->bl, + SkillStatusChangeTable[PR_IMPOSITIO], + 1, 0, 0, 0, + skill_get_time (PR_IMPOSITIO, 1), 0); + skill_status_change_start (&sd->bl, + SkillStatusChangeTable[PR_MAGNIFICAT], + 1, 0, 0, 0, + skill_get_time (PR_MAGNIFICAT, 1), 0); + skill_status_change_start (&sd->bl, + SkillStatusChangeTable[PR_GLORIA], 1, + 0, 0, 0, skill_get_time (PR_GLORIA, 1), + 0); + skill_status_change_start (&sd->bl, + SkillStatusChangeTable[PR_SUFFRAGIUM], + 1, 0, 0, 0, + skill_get_time (PR_SUFFRAGIUM, 1), 0); + } + + clif_misceffect (&sd->bl, 0); + //レベルアップしたのでパーティー情報を更新する + //(公平範囲チェック) + party_send_movemap (sd); + MAP_LOG_XP (sd, "LEVELUP") return 1; + } + + return 0; +} + +/*======================================== + * Compute the maximum for sd->skill_point, i.e., the max. number of skill points that can still be filled in + *---------------------------------------- + */ +int pc_skillpt_potential (struct map_session_data *sd) +{ + int skill_id; + int potential = 0; + +#define RAISE_COST(x) (((x)*((x)-1))>>1) + + for (skill_id = 0; skill_id < MAX_SKILL; skill_id++) + if (sd->status.skill[skill_id].id != 0 + && sd->status.skill[skill_id].lv < skill_db[skill_id].max_raise) + potential += RAISE_COST (skill_db[skill_id].max_raise) + - RAISE_COST (sd->status.skill[skill_id].lv); +#undef RAISE_COST + + return potential; +} + +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) + { + if (pc_skillpt_potential (sd) < sd->status.skill_point) + { // [Fate] Bah, this is is painful. + // But the alternative is quite error-prone, and eAthena has far worse performance issues... + sd->status.job_exp = next - 1; + pc_calcstatus(sd,0); + return 0; + } + + // job側レベルアップ処理 + sd->status.job_exp -= next; + clif_updatestatus (sd, SP_NEXTJOBEXP); + sd->status.skill_point++; + clif_updatestatus (sd, SP_SKILLPOINT); + pc_calcstatus (sd, 0); + + MAP_LOG_PC (sd, "SKILLPOINTS-UP %d", sd->status.skill_point); + + if (sd->status.job_level < 250 + && sd->status.job_level < sd->status.base_level * 2) + sd->status.job_level++; // Make levelling up a little harder + + clif_misceffect (&sd->bl, 1); + return 1; + } + + return 0; +} + +/*========================================== + * 経験値取得 + *------------------------------------------ + */ +int pc_gainexp (struct map_session_data *sd, int base_exp, int job_exp) +{ + return pc_gainexp_reason (sd, base_exp, job_exp, + PC_GAINEXP_REASON_KILLING); +} + +int pc_gainexp_reason (struct map_session_data *sd, int base_exp, int job_exp, + int reason) +{ + char output[256]; + 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 + + MAP_LOG_PC (sd, "GAINXP %d %d %s", base_exp, job_exp, + ((reason == + 2) ? "SCRIPTXP" : ((reason == 1) ? "HEALXP" : "KILLXP"))); + + if (sd->sc_data[SC_RICHMANKIM].timer != -1) + { // added bounds checking [Vaalris] + base_exp += + base_exp * (25 + sd->sc_data[SC_RICHMANKIM].val1 * 25) / 100; + job_exp += + job_exp * (25 + sd->sc_data[SC_RICHMANKIM].val1 * 25) / 100; + } + + 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)) + { + while (sd->status.base_exp + base_exp >= pc_nextbaseafter (sd) + && sd->status.base_exp <= pc_nextbaseexp (sd) + && pc_nextbaseafter (sd) > 0) + { + base_exp *= .90; + } + } + + sd->status.base_exp += base_exp; + + // [Fate] Adjust experience points that healers can extract from this character + if (reason != PC_GAINEXP_REASON_HEALING) + { + const int max_heal_xp = + 20 + (sd->status.base_level * sd->status.base_level); + + sd->heal_xp += base_exp; + if (sd->heal_xp > max_heal_xp) + sd->heal_xp = max_heal_xp; + } + + 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)) + { + while (sd->status.job_exp + job_exp >= pc_nextjobafter (sd) + && sd->status.job_exp <= pc_nextjobexp (sd) + && pc_nextjobafter (sd) > 0) + { + job_exp *= .90; + } + } + + 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 (battle_config.disp_experience) + { + sprintf (output, + "Experienced Gained Base:%d Job:%d", base_exp, job_exp); + clif_disp_onlyself (sd, output, strlen (output)); + } + + return 0; +} + +int pc_extract_healer_exp (struct map_session_data *sd, int max) +{ + int amount; + nullpo_retr (0, sd); + + amount = sd->heal_xp; + if (max < amount) + amount = max; + + sd->heal_xp -= amount; + return amount; +} + +/*========================================== + * base level側必要経験値計算 + *------------------------------------------ + */ +int pc_nextbaseexp (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; + + if (sd->status.pc_class == 0) + i = 0; + else if (sd->status.pc_class <= 6) + i = 1; + else if (sd->status.pc_class <= 22) + i = 2; + else if (sd->status.pc_class == 23) + i = 3; + else if (sd->status.pc_class == 4001) + i = 4; + else if (sd->status.pc_class <= 4007) + i = 5; + else + i = 6; + + return exp_table[i][sd->status.base_level - 1]; +} + +/*========================================== + * job level側必要経験値計算 + *------------------------------------------ + */ +int pc_nextjobexp (struct map_session_data *sd) +{ + // [fate] For normal levels, this ranges from 20k to 50k, depending on job level. + // Job level is at most twice the player's experience level (base_level). Levelling + // from 2 to 9 is 44 points, i.e., 880k to 2.2M job experience points (this is per + // skill, obviously.) + + return 20000 + sd->status.job_level * 150; +} + +/*========================================== + * 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; + + if (sd->status.pc_class == 0) + i = 0; + else if (sd->status.pc_class <= 6) + i = 1; + else if (sd->status.pc_class <= 22) + i = 2; + else if (sd->status.pc_class == 23) + i = 3; + else if (sd->status.pc_class == 4001) + i = 4; + else if (sd->status.pc_class <= 4007) + i = 5; + else + i = 6; + + 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; + + if (sd->status.pc_class == 0) + i = 7; + else if (sd->status.pc_class <= 6) + i = 8; + else if (sd->status.pc_class <= 22) + i = 9; + else if (sd->status.pc_class == 23) + i = 10; + else if (sd->status.pc_class == 4001) + i = 11; + else if (sd->status.pc_class <= 4007) + i = 12; + else + i = 13; + + 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 need, val = 0; + + nullpo_retr (0, sd); + + switch (type) + { + 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; + } + + need = pc_need_status_point (sd, type); + if (type < SP_STR || type > SP_LUK || need < 0 + || need > sd->status.status_point + || val >= battle_config.max_parameter) + { + clif_statusupack (sd, type, 0, val); + clif_updatestatus (sd, SP_STATUSPOINT); + return 1; + } + switch (type) + { + 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; + } + 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); + pc_calcstatus (sd, 0); + clif_statusupack (sd, type, 1, val); + + MAP_LOG_STATS (sd, "STATUP"); + + return 0; +} + +/*========================================== + * 能力値成長 + *------------------------------------------ + */ +int pc_statusup2 (struct map_session_data *sd, int type, int val) +{ + nullpo_retr (0, 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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 >= battle_config.max_parameter) + val = battle_config.max_parameter; + 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); + pc_calcstatus (sd, 0); + clif_statusupack (sd, type, 1, val); + MAP_LOG_STATS (sd, "STATUP2"); + + return 0; +} + +/*========================================== + * スキルポイント割り振り + *------------------------------------------ + */ +int pc_skillup (struct map_session_data *sd, int skill_num) +{ + nullpo_retr (0, sd); + + if (sd->status.skill[skill_num].id != 0 + && sd->status.skill_point >= sd->status.skill[skill_num].lv + && sd->status.skill[skill_num].lv < skill_db[skill_num].max_raise) + { + sd->status.skill_point -= sd->status.skill[skill_num].lv; + sd->status.skill[skill_num].lv++; + + pc_calcstatus (sd, 0); + clif_skillup (sd, skill_num); + clif_updatestatus (sd, SP_SKILLPOINT); + clif_skillinfoblock (sd); + MAP_LOG_PC(sd, "SKILLUP %d %d %d", + skill_num, sd->status.skill[skill_num].lv, skill_power(sd, skill_num)); + } + + return 0; +} + +/*========================================== + * /allskill + *------------------------------------------ + */ +int pc_allskillup (struct map_session_data *sd) +{ + int i, id; + int c = 0, s = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + s_class = pc_calc_base_job (sd->status.pc_class); + c = s_class.job; + s = (s_class.upper == 1) ? 1 : 0; //転生以外は通常のスキル? + + for (i = 0; i < MAX_SKILL; i++) + sd->status.skill[i].id = 0; + + if (battle_config.gm_allskill > 0 + && pc_isGM (sd) >= battle_config.gm_allskill) + { + // 全てのスキル + for (i = 1; i < 158; i++) + sd->status.skill[i].lv = skill_get_max (i); + for (i = 210; i < 291; i++) + sd->status.skill[i].lv = skill_get_max (i); + for (i = 304; i < MAX_SKILL; i++) + sd->status.skill[i].lv = skill_get_max (i); + } + else + { + for (i = 0; (id = skill_tree[s][c][i].id) > 0; i++) + { + if (sd->status.skill[id].id == 0 && skill_get_inf2 (id) & 0x01) + sd->status.skill[id].lv = skill_get_max (id); + } + } + pc_calcstatus (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 = 0; + 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.pc_class == 4001) + sd->status.status_point = 100; + } + + 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], 1); + sd->equip_index[i] = -1; + } + } + + clif_skillinfoblock (sd); + pc_calcstatus (sd, 0); + + MAP_LOG_STATS (sd, "STATRESET"); + + return 0; +} + +/*========================================== + * /resetstate + *------------------------------------------ + */ +int pc_resetstate (struct map_session_data *sd) +{ +#define sumsp(a) ((a)*((a-2)/10+2) - 5*((a-2)/10)*((a-2)/10) - 6*((a-2)/10) -2) +// int add=0; // Removed by Dexity + + nullpo_retr (0, sd); + +// New statpoint table used here - Dexity + sd->status.status_point = atoi (statp[sd->status.base_level - 1]); +// End addition + +// Removed by Dexity - old count +// 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; + + clif_updatestatus (sd, SP_STATUSPOINT); + + 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 + + pc_calcstatus (sd, 0); + + return 0; +} + +/*========================================== + * /resetskill + *------------------------------------------ + */ +int pc_resetskill (struct map_session_data *sd) +{ + int i, skill; + + nullpo_retr (0, sd); + + sd->status.skill_point += pc_calc_skillpoint (sd); + + for (i = 1; i < MAX_SKILL; i++) + if ((skill = pc_checkskill (sd, i)) > 0) + { + sd->status.skill[i].lv = 0; + sd->status.skill[i].flags = 0; + } + + clif_updatestatus (sd, SP_SKILLPOINT); + clif_skillinfoblock (sd); + pc_calcstatus (sd, 0); + + return 0; +} + +/*========================================== + * pcにダメージを与える + *------------------------------------------ + */ +int pc_damage (struct block_list *src, struct map_session_data *sd, + int damage) +{ + int i = 0, j = 0; + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + //転生や養子の場合の元の職業を算出する + s_class = pc_calc_base_job (sd->status.pc_class); + // 既に死んでいたら無効 + if (pc_isdead (sd)) + return 0; + // 座ってたら立ち上がる + if (pc_issit (sd)) + { + pc_setstand (sd); + skill_gangsterparadise (sd, 0); + } + + if (src) + { + if (src->type == BL_PC) + { + MAP_LOG_PC (sd, "INJURED-BY PC%d FOR %d", + ((struct map_session_data *) src)->status.char_id, + damage); + } + else + { + MAP_LOG_PC (sd, "INJURED-BY MOB%d FOR %d", src->id, damage); + } + } + else + MAP_LOG_PC (sd, "INJURED-BY null FOR %d", damage); + + // 歩 いていたら足を止める + if (sd->sc_data[SC_ENDURE].timer == -1 + && !sd->special_state.infinite_endure) + pc_stop_walking (sd, 3); + // 演奏/ダンスの中断 + if (damage > sd->status.max_hp >> 2) + skill_stop_dancing (&sd->bl, 0); + + sd->status.hp -= damage; + + if (sd->sc_data[SC_TRICKDEAD].timer != -1) + skill_status_change_end (&sd->bl, SC_TRICKDEAD, -1); + if (sd->status.option & 2) + skill_status_change_end (&sd->bl, SC_HIDING, -1); + if (sd->status.option & 4) + skill_status_change_end (&sd->bl, SC_CLOAKING, -1); + if (sd->status.option & 16386) + skill_status_change_end (&sd->bl, SC_CHASEWALK, -1); + + if (sd->status.hp > 0) + { + // まだ生きているならHP更新 + clif_updatestatus (sd, SP_HP); + + if (sd->status.hp < sd->status.max_hp >> 2 + && pc_checkskill (sd, SM_AUTOBERSERK) > 0 + && (sd->sc_data[SC_PROVOKE].timer == -1 + || sd->sc_data[SC_PROVOKE].val2 == 0)) + // オートバーサーク発動 + skill_status_change_start (&sd->bl, SC_PROVOKE, 10, 1, 0, 0, 0, + 0); + + sd->canlog_tick = gettick (); + + if (sd->status.party_id > 0) + { // on-the-fly party hp updates [Valaris] + struct party *p = party_search (sd->status.party_id); + if (p != NULL) + clif_party_hp (p, sd); + } // end addition [Valaris] + + return 0; + } + + MAP_LOG_PC (sd, "DEAD%s", ""); + + // Character is dead! + + sd->status.hp = 0; + // [Fate] Stop quickregen + sd->quick_regeneration_hp.amount = 0; + sd->quick_regeneration_sp.amount = 0; + skill_update_heal_animation (sd); + + pc_setdead (sd); + + pc_stop_walking (sd, 0); + skill_castcancel (&sd->bl, 0); // 詠唱の中止 + clif_clearchar_area (&sd->bl, 1); + skill_unit_out_all (&sd->bl, gettick (), 1); + if (sd->sc_data[SC_BLADESTOP].timer != -1) //白刃は事前に解除 + skill_status_change_end (&sd->bl, SC_BLADESTOP, -1); + pc_setglobalreg (sd, "PC_DIE_COUNTER", ++sd->die_counter); //死にカウンター書き込み + skill_status_change_clear (&sd->bl, 0); // ステータス異常を解除する + clif_updatestatus (sd, SP_HP); + pc_calcstatus (sd, 0); + // [Fate] Reset magic + sd->cast_tick = gettick (); + magic_stop_completely (sd); + + for (i = 0; i < 5; i++) + if (sd->dev.val1[i]) + { + skill_status_change_end (&map_id2sd (sd->dev.val1[i])->bl, + SC_DEVOTION, -1); + sd->dev.val1[i] = sd->dev.val2[i] = 0; + } + + if (battle_config.death_penalty_type > 0 && sd->status.base_level >= 20) + { // changed penalty options, added death by player if pk_mode [Valaris] + if (!map[sd->bl.m].flag.nopenalty && !map[sd->bl.m].flag.gvg) + { + if (battle_config.death_penalty_type == 1 + && battle_config.death_penalty_base > 0) + sd->status.base_exp -= + (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 -= + (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 -= + (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 -= + (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 -= + (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 -= + (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 -= + (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 -= + (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 (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[MRAND (eq_num)]; //該当アイテムの中からランダム + if (MRAND (10000) < per) + { + if (sd->status.inventory[n].equip) + pc_unequipitem (sd, n, 0); + pc_dropitem (sd, n, 1); + } + } + } + else if (id > 0) + { + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].nameid == id //ItemIDが一致していて + && MRAND (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, 0); + 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; + if (src && src->type == BL_PC) + ((struct map_session_data *) src)->pvp_point++; + //} //fixed wrong '{' placement by Lupus + pc_setdead (sd); + } + // 強制送還 + if (sd->pvp_point < 0) + { + sd->pvp_point = 0; + 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); + } + } + //GvG + if (map[sd->bl.m].flag.gvg) + { + 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); + } + + if (src && src->type == BL_PC) + { + // [Fate] PK death, trigger scripts + argrec_t arg[3]; + arg[0].name = "@killerrid"; + arg[0].v.i = src->id; + arg[1].name = "@victimrid"; + arg[1].v.i = sd->bl.id; + arg[2].name = "@victimlvl"; + arg[2].v.i = sd->status.base_level; + npc_event_doall_l ("OnPCKilledEvent", sd->bl.id, 3, arg); + npc_event_doall_l ("OnPCKillEvent", src->id, 3, arg); + } + npc_event_doall_l ("OnPCDieEvent", sd->bl.id, 0, NULL); + + return 0; +} + +// +// script関 連 +// +/*========================================== + * script用PCステータス読み出し + *------------------------------------------ + */ +int pc_readparam (struct map_session_data *sd, int type) +{ + int val = 0; + struct pc_base_job s_class; + + s_class = pc_calc_base_job (sd->status.pc_class); + + 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.pc_class; + break; + case SP_UPPER: + val = s_class.upper; + 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_FAME: + val = sd->fame; + break; + } + + return val; +} + +/*========================================== + * script用PCステータス設定 + *------------------------------------------ + */ +int pc_setparam (struct map_session_data *sd, int type, int val) +{ + int i = 0, up_level = 50; + struct pc_base_job s_class; + + nullpo_retr (0, sd); + + s_class = pc_calc_base_job (sd->status.pc_class); + + switch (type) + { + case SP_BASELEVEL: + if (val > sd->status.base_level) + { + for (i = 1; i <= (val - sd->status.base_level); i++) + sd->status.status_point += + (sd->status.base_level + i + 14) / 4; + } + 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); + pc_calcstatus (sd, 0); + pc_heal (sd, sd->status.max_hp, sd->status.max_sp); + break; + case SP_JOBLEVEL: + if (sd->status.pc_class == 0) + up_level -= 40; + if ((sd->status.pc_class == 23) + || (sd->status.pc_class >= 4001 && sd->status.pc_class <= 4022)) + up_level += 20; + if (val >= 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); + pc_calcstatus (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); + pc_calcstatus (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: + sd->status.zeny = val; + 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_FAME: + sd->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_data && 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.party_id > 0) + { // on-the-fly party hp updates [Valaris] + struct party *p = party_search (sd->status.party_id); + if (p != NULL) + clif_party_hp (p, sd); + } // end addition [Valaris] + + return hp + sp; +} + +/*========================================== + * HP/SP回復 + *------------------------------------------ + */ +static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp); + +static int // Compute how quickly we regenerate (less is faster) for that amount +pc_heal_quick_speed (int amount) +{ + if (amount >= 100) + { + if (amount >= 500) + return 0; + if (amount >= 250) + return 1; + return 2; + } + else + { // < 100 + if (amount >= 50) + return 3; + if (amount >= 20) + return 4; + return 5; + } +} + +static void +pc_heal_quick_accumulate (int new_amount, + struct quick_regeneration *quick_regen, int max) +{ + int current_amount = quick_regen->amount; + int current_speed = quick_regen->speed; + int new_speed = pc_heal_quick_speed (new_amount); + + int average_speed = ((new_speed * new_amount) + (current_speed * current_amount)) / (current_amount + new_amount); // new_amount > 0, current_amount >= 0 + + quick_regen->speed = average_speed; + quick_regen->amount = MIN (current_amount + new_amount, max); + + quick_regen->tickdelay = MIN (quick_regen->speed, quick_regen->tickdelay); +} + +int pc_itemheal (struct map_session_data *sd, int hp, int sp) +{ + /* defer healing */ + if (hp > 0) + { + pc_heal_quick_accumulate (hp, + &sd->quick_regeneration_hp, + sd->status.max_hp - sd->status.hp); + hp = 0; + } + if (sp > 0) + { + pc_heal_quick_accumulate (sp, + &sd->quick_regeneration_sp, + sd->status.max_sp - sd->status.sp); + + sp = 0; + } + + /* Hurt right away, if necessary */ + if (hp < 0 || sp < 0) + pc_itemheal_effect (sd, hp, sp); + + return 0; +} + +/* pc_itemheal_effect is invoked once every 0.5s whenever the pc + * has health recovery queued up (cf. pc_natural_heal_sub). + */ +static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp) +{ + int bonus; +// if(battle_config.battle_log) +// printf("heal %d %d\n",hp,sp); + + nullpo_retr (0, sd); + + if (sd->sc_data && sd->sc_data[SC_GOSPEL].timer != -1) //バーサーク中は回復させないらしい + return 0; + + if (sd->state.potionpitcher_flag) + { + sd->potion_hp = hp; + sd->potion_sp = sp; + 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; + if (bonus != 100) + hp = hp * bonus / 100; + bonus = 100 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5; + if (bonus != 100) + hp = hp * bonus / 100; + } + if (sp > 0) + { + bonus = + (sd->paramc[3] << 1) + 100 + pc_checkskill (sd, + MG_SRECOVERY) * 10; + if (bonus != 100) + sp = sp * bonus / 100; + bonus = 100 + pc_checkskill (sd, AM_LEARNINGPOTION) * 5; + 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 (sd->state.potionpitcher_flag) + { + sd->potion_per_hp = hp; + sd->potion_per_sp = sp; + return 0; + } + + 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 + *------------------------------------------ + */ +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.pc_class); + + nullpo_retr (0, sd); + + if ((job > 23) && (job < 68)) + job += 3977; + + if ((job > 69) && (job < 4000)) + return 1; + + if (upper < 0) //現在転生かどうかを判断する + upper = s_class.upper; + + if (upper == 0) + { //通常職ならjobそのまんま + b_class = job; + } + else if (upper == 1) + { + if (job == 23) + { //転生にスパノビは存在しないのでお断り + return 1; + } + else + { + b_class = job + 4001; + } + } + else if (upper == 2) + { //養子に結婚はないけどどうせ次で蹴られるからいいや + b_class = (job == 23) ? job + 4022 : job + 4023; + } + else + { + return 1; + } + + if ((sd->status.sex == 0 && job == 19) || (sd->status.sex == 1 && job == 20) || + (sd->status.sex == 0 && job == 4020) || (sd->status.sex == 1 && job == 4021) || + job == 22 || sd->status.pc_class == b_class) //♀はバードになれない、♂はダンサーになれない、結婚衣裳もお断り + return 1; + + sd->status.pc_class = sd->view_class = b_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], 1); // 装備外し + } + + clif_changelook (&sd->bl, LOOK_BASE, sd->view_class); // move sprite update to prevent client crashes with incompatible equipment [Valaris] + if (sd->status.clothes_color > 0) + clif_changelook (&sd->bl, LOOK_CLOTHES_COLOR, + sd->status.clothes_color); + if (battle_config.muting_players && sd->status.manner < 0) + clif_changestatus (&sd->bl, SP_MANNER, sd->status.manner); + + pc_calcstatus (sd, 0); + pc_checkallowskill (sd); + pc_equiplookall (sd); + clif_equiplist (sd); + + 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 | -0x0000); + if (pc_checkskill (sd, KN_RIDING) > 0) + pc_setriding (sd); + } + + return 0; +} + +/*========================================== + * 見た目変更 + *------------------------------------------ + */ +int pc_equiplookall (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + clif_changelook (&sd->bl, LOOK_WEAPON, 0); +// clif_changelook(&sd->bl,LOOK_SHOES,0); + 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); + + clif_changelook_accessories (&sd->bl, NULL); + + return 0; +} + +/*========================================== + * 見た目変更 + *------------------------------------------ + */ +int pc_changelook (struct map_session_data *sd, int type, int val) +{ + nullpo_retr (0, sd); + + switch (type) + { + case LOOK_HAIR: + sd->status.hair = val; + 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: + sd->status.hair_color = val; + break; + case LOOK_CLOTHES_COLOR: + sd->status.clothes_color = val; + break; + case LOOK_SHIELD: + sd->status.shield = val; + break; + case LOOK_SHOES: + break; + } + clif_changelook (&sd->bl, type, val); + + return 0; +} + +/*========================================== + * 付属品(鷹,ペコ,カート)設定 + *------------------------------------------ + */ +int pc_setoption (struct map_session_data *sd, int type) +{ + nullpo_retr (0, sd); + + sd->status.option = type; + clif_changeoption (&sd->bl); + pc_calcstatus (sd, 0); + + return 0; +} + +/*========================================== + * カート設定 + *------------------------------------------ + */ +int pc_setcart (struct map_session_data *sd, int type) +{ + int cart[6] = { 0x0000, 0x0008, 0x0080, 0x0100, 0x0200, 0x0400 }; + + nullpo_retr (0, sd); + + if (pc_checkskill (sd, MC_PUSHCART) > 0) + { // プッシュカートスキル所持 + if (!pc_iscarton (sd)) + { // カートを付けていない + pc_setoption (sd, cart[type]); + clif_cart_itemlist (sd); + clif_cart_equiplist (sd); + clif_updatestatus (sd, SP_CARTINFO); + clif_status_change (&sd->bl, 0x0c, 0); + } + else + { + pc_setoption (sd, cart[type]); + } + } + + 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 (sd->disguise > 0) + { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage (sd->fd, + "Cannot mount a Peco while in disguise."); + return 0; + } + + if ((pc_checkskill (sd, KN_RIDING) > 0)) + { // ライディングスキル所持 + pc_setoption (sd, sd->status.option | 0x0020); + + if (sd->status.pc_class == 7) + sd->status.pc_class = sd->view_class = 13; + + if (sd->status.pc_class == 14) + sd->status.pc_class = sd->view_class = 21; + + if (sd->status.pc_class == 4008) + sd->status.pc_class = sd->view_class = 4014; + + if (sd->status.pc_class == 4015) + sd->status.pc_class = sd->view_class = 4022; + } + + return 0; +} + +/*========================================== + * 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++; + RECREATE (sd->reg, struct script_reg, sd->reg_num); + 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)) + { + printf ("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++; + RECREATE (sd->regstr, struct script_regstr, sd->regstr_num); + sd->regstr[i].index = reg; + strcpy (sd->regstr[i].data, str); + + return 0; +} + +/*========================================== + * script用グローバル変数の値を読む + *------------------------------------------ + */ +int pc_readglobalreg (struct map_session_data *sd, char *reg) +{ + int i; + + nullpo_retr (0, sd); + + for (i = 0; i < sd->status.global_reg_num; i++) + { + if (strcmp (sd->status.global_reg[i].str, reg) == 0) + return sd->status.global_reg[i].value; + } + + return 0; +} + +/*========================================== + * script用グローバル変数の値を設定 + *------------------------------------------ + */ +int pc_setglobalreg (struct map_session_data *sd, char *reg, int val) +{ + int i; + + nullpo_retr (0, sd); + + //PC_DIE_COUNTERがスクリプトなどで変更された時の処理 + if (strcmp (reg, "PC_DIE_COUNTER") == 0 && sd->die_counter != val) + { + sd->die_counter = val; + pc_calcstatus (sd, 0); + } + if (val == 0) + { + for (i = 0; i < sd->status.global_reg_num; i++) + { + if (strcmp (sd->status.global_reg[i].str, reg) == 0) + { + sd->status.global_reg[i] = + sd->status.global_reg[sd->status.global_reg_num - 1]; + sd->status.global_reg_num--; + break; + } + } + return 0; + } + for (i = 0; i < sd->status.global_reg_num; i++) + { + if (strcmp (sd->status.global_reg[i].str, reg) == 0) + { + sd->status.global_reg[i].value = val; + return 0; + } + } + if (sd->status.global_reg_num < GLOBAL_REG_NUM) + { + strcpy (sd->status.global_reg[i].str, reg); + sd->status.global_reg[i].value = val; + sd->status.global_reg_num++; + return 0; + } + if (battle_config.error_log) + printf ("pc_setglobalreg : couldn't set %s (GLOBAL_REG_NUM = %d)\n", + reg, GLOBAL_REG_NUM); + + return 1; +} + +/*========================================== + * script用アカウント変数の値を読む + *------------------------------------------ + */ +int pc_readaccountreg (struct map_session_data *sd, char *reg) +{ + int i; + + nullpo_retr (0, sd); + + for (i = 0; i < sd->status.account_reg_num; i++) + { + if (strcmp (sd->status.account_reg[i].str, reg) == 0) + return sd->status.account_reg[i].value; + } + + return 0; +} + +/*========================================== + * script用アカウント変数の値を設定 + *------------------------------------------ + */ +int pc_setaccountreg (struct map_session_data *sd, char *reg, int val) +{ + int i; + + nullpo_retr (0, sd); + + if (val == 0) + { + for (i = 0; i < sd->status.account_reg_num; i++) + { + if (strcmp (sd->status.account_reg[i].str, reg) == 0) + { + sd->status.account_reg[i] = + sd->status.account_reg[sd->status.account_reg_num - 1]; + sd->status.account_reg_num--; + break; + } + } + intif_saveaccountreg (sd); + return 0; + } + for (i = 0; i < sd->status.account_reg_num; i++) + { + if (strcmp (sd->status.account_reg[i].str, reg) == 0) + { + sd->status.account_reg[i].value = val; + intif_saveaccountreg (sd); + return 0; + } + } + if (sd->status.account_reg_num < ACCOUNT_REG_NUM) + { + strcpy (sd->status.account_reg[i].str, reg); + sd->status.account_reg[i].value = val; + sd->status.account_reg_num++; + intif_saveaccountreg (sd); + return 0; + } + if (battle_config.error_log) + printf ("pc_setaccountreg : couldn't set %s (ACCOUNT_REG_NUM = %d)\n", + reg, ACCOUNT_REG_NUM); + + return 1; +} + +/*========================================== + * script用アカウント変数2の値を読む + *------------------------------------------ + */ +int pc_readaccountreg2 (struct map_session_data *sd, char *reg) +{ + int i; + + nullpo_retr (0, sd); + + for (i = 0; i < sd->status.account_reg2_num; i++) + { + if (strcmp (sd->status.account_reg2[i].str, reg) == 0) + return sd->status.account_reg2[i].value; + } + + return 0; +} + +/*========================================== + * script用アカウント変数2の値を設定 + *------------------------------------------ + */ +int pc_setaccountreg2 (struct map_session_data *sd, char *reg, int val) +{ + int i; + + nullpo_retr (1, sd); + + if (val == 0) + { + for (i = 0; i < sd->status.account_reg2_num; i++) + { + if (strcmp (sd->status.account_reg2[i].str, reg) == 0) + { + sd->status.account_reg2[i] = + sd->status.account_reg2[sd->status.account_reg2_num - 1]; + sd->status.account_reg2_num--; + break; + } + } + chrif_saveaccountreg2 (sd); + return 0; + } + for (i = 0; i < sd->status.account_reg2_num; i++) + { + if (strcmp (sd->status.account_reg2[i].str, reg) == 0) + { + sd->status.account_reg2[i].value = val; + chrif_saveaccountreg2 (sd); + return 0; + } + } + if (sd->status.account_reg2_num < ACCOUNT_REG2_NUM) + { + strcpy (sd->status.account_reg2[i].str, reg); + sd->status.account_reg2[i].value = val; + sd->status.account_reg2_num++; + chrif_saveaccountreg2 (sd); + return 0; + } + if (battle_config.error_log) + printf + ("pc_setaccountreg2 : couldn't set %s (ACCOUNT_REG2_NUM = %d)\n", + reg, ACCOUNT_REG2_NUM); + + return 1; +} + +/*========================================== + * 精錬成功率 + *------------------------------------------ + */ +int pc_percentrefinery (struct map_session_data *sd, struct item *item) +{ + int percent; + + nullpo_retr (0, item); + percent = percentrefinery[itemdb_wlv (item->nameid)][(int) item->refine]; + + percent += pc_checkskill (sd, BS_WEAPONRESEARCH); // 武器研究スキル所持 + + // 確率の有効範囲チェック + if (percent > 100) + { + percent = 100; + } + if (percent < 0) + { + percent = 0; + } + + return percent; +} + +/*========================================== + * イベントタイマー処理 + *------------------------------------------ + */ +void pc_eventtimer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = map_id2sd (id); + int i; + if (sd == NULL) + return; + + for (i = 0; i < MAX_EVENTTIMER; i++) + { + if (sd->eventtimer[i] == tid) + { + sd->eventtimer[i] = -1; + npc_event (sd, (const char *) data, 0); + break; + } + } + free ((void *) data); + if (i == MAX_EVENTTIMER) + { + if (battle_config.error_log) + printf ("pc_eventtimer: no such event timer\n"); + } +} + +/*========================================== + * イベントタイマー追加 + *------------------------------------------ + */ +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 = (char *) calloc (24, 1); + strncpy (evname, name, 24); + evname[23] = '\0'; + sd->eventtimer[i] = add_timer (gettick () + tick, + pc_eventtimer, sd->bl.id, + (int) evname); + return 1; + } + + return 0; +} + +/*========================================== + * イベントタイマー削除 + *------------------------------------------ + */ +int pc_deleventtimer (struct map_session_data *sd, const char *name) +{ + 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) + { + delete_timer (sd->eventtimer[i], pc_eventtimer); + sd->eventtimer[i] = -1; + 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); + + for (i = 0; i < MAX_EVENTTIMER; i++) + if (sd->eventtimer[i] != -1) + { + delete_timer (sd->eventtimer[i], pc_eventtimer); + sd->eventtimer[i] = -1; + } + + return 0; +} + +// +// 装 備物 +// +/*========================================== + * アイテムを装備する + *------------------------------------------ + */ +static int +pc_signal_advanced_equipment_change (struct map_session_data *sd, int n) +{ + if (sd->status.inventory[n].equip & 0x0040) + clif_changelook (&sd->bl, LOOK_SHOES, 0); + if (sd->status.inventory[n].equip & 0x0004) + clif_changelook (&sd->bl, LOOK_GLOVES, 0); + if (sd->status.inventory[n].equip & 0x0008) + clif_changelook (&sd->bl, LOOK_CAPE, 0); + if (sd->status.inventory[n].equip & 0x0010) + clif_changelook (&sd->bl, LOOK_MISC1, 0); + if (sd->status.inventory[n].equip & 0x0080) + clif_changelook (&sd->bl, LOOK_MISC2, 0); + return 0; +} + +int pc_equipitem (struct map_session_data *sd, int n, int pos) +{ + int i, nameid, arrow, view; + struct item_data *id; + //ソス]ソスソスソスソスソス{ソスqソスフ場合ソスフ鯉ソスソスフ職ソスニゑソスソスZソスoソスソスソスソス + + nullpo_retr (0, sd); + + if (n < 0 || n >= MAX_INVENTORY) + { + clif_equipitemack (sd, 0, 0, 0); + return 0; + } + + nameid = sd->status.inventory[n].nameid; + id = sd->inventory_data[n]; + pos = pc_equippoint (sd, n); + + if (battle_config.battle_log) + printf ("equip %d(%d) %x:%x\n", nameid, n, id->equip, pos); + if (!pc_isequip (sd, n) || !pos || sd->status.inventory[n].broken == 1) + { // [Valaris] + clif_equipitemack (sd, n, 0, 0); // fail + return 0; + } + +// -- moonsoul (if player is berserk then cannot equip) +// + if (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->status.pc_class == 12)) // 左手修錬有 + { + 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 (pos & equip_pos[i]) + { + if (sd->equip_index[i] >= 0) //Slot taken, remove item from there. + pc_unequipitem (sd, sd->equip_index[i], 1); + sd->equip_index[i] = n; + } + } + // 弓矢装備 + if (pos == 0x8000) + { + clif_arrowequip (sd, n); + clif_arrow_fail (sd, 3); // 3=矢が装備できました + } + else + { + /* Don't update re-equipping if we're using a spell */ + if (!(pos == 4 && sd->attack_spell_override)) + 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->inventory_data[n]) + { + view = sd->inventory_data[n]->look; + if (view == 0) + view = sd->inventory_data[n]->nameid; + } + else + { + view = 0; + } + + if (sd->status.inventory[n].equip & 0x0002) + { + sd->weapontype1 = view; + pc_calcweapontype (sd); + pc_set_weapon_look (sd); + } + 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 = view; + } + else if (sd->inventory_data[n]->type == 5) + { + sd->status.shield = view; + 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) + { + sd->status.head_bottom = view; + clif_changelook (&sd->bl, LOOK_HEAD_BOTTOM, sd->status.head_bottom); + } + if (sd->status.inventory[n].equip & 0x0100) + { + sd->status.head_top = view; + clif_changelook (&sd->bl, LOOK_HEAD_TOP, sd->status.head_top); + } + if (sd->status.inventory[n].equip & 0x0200) + { + sd->status.head_mid = view; + clif_changelook (&sd->bl, LOOK_HEAD_MID, sd->status.head_mid); + } + pc_signal_advanced_equipment_change (sd, n); + + pc_checkallowskill (sd); // 装備品でスキルか解除されるかチェック + if (itemdb_look (sd->status.inventory[n].nameid) == 11 && arrow) + { // Added by RoVeRT + clif_arrowequip (sd, arrow); + sd->status.inventory[arrow].equip = 32768; + } + pc_calcstatus (sd, 0); + + if (sd->special_state.infinite_endure) + { + if (sd->sc_data[SC_ENDURE].timer == -1) + skill_status_change_start (&sd->bl, SC_ENDURE, 10, 1, 0, 0, 0, 0); + } + else + { + if (sd->sc_data[SC_ENDURE].timer != -1 && sd->sc_data[SC_ENDURE].val2) + skill_status_change_end (&sd->bl, SC_ENDURE, -1); + } + + if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 + && !battle_check_undead (7, sd->def_ele)) + skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); + if (sd->sc_data[SC_DANCING].timer != -1 + && (sd->status.weapon != 13 && sd->status.weapon != 14)) + skill_stop_dancing (&sd->bl, 0); + + return 0; +} + +/*========================================== + * 装 備した物を外す + *------------------------------------------ + */ +int pc_unequipitem (struct map_session_data *sd, int n, int type) +{ + nullpo_retr (0, sd); + +// -- moonsoul (if player is berserk then cannot unequip) +// + if (sd->sc_data[SC_BERSERK].timer != -1) + { + clif_unequipitemack (sd, n, 0, 0); + return 0; + } + + if (battle_config.battle_log) + printf ("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->status.inventory[n].equip & 0x0002) + { + sd->weapontype1 = 0; + sd->status.weapon = sd->weapontype2; + pc_calcweapontype (sd); + pc_set_weapon_look (sd); + } + 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); + } + pc_signal_advanced_equipment_change (sd, n); + + if (sd->sc_data[SC_BROKNWEAPON].timer != -1 + && sd->status.inventory[n].equip & 0x0002 + && sd->status.inventory[i].broken == 1) + skill_status_change_end (&sd->bl, SC_BROKNWEAPON, -1); + + clif_unequipitemack (sd, n, sd->status.inventory[n].equip, 1); + sd->status.inventory[n].equip = 0; + if (!type) + pc_checkallowskill (sd); + if (sd->weapontype1 == 0 && sd->weapontype2 == 0) + skill_encchant_eremental_end (&sd->bl, -1); //武器持ち誓えは無条件で属性付与解除 + } + else + { + clif_unequipitemack (sd, n, 0, 0); + } + if (!type) + { + pc_calcstatus (sd, 0); + if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 + && !battle_check_undead (7, sd->def_ele)) + skill_status_change_end (&sd->bl, SC_SIGNUMCRUCIS, -1); + } + + return 0; +} + +int pc_unequipinvyitem (struct map_session_data *sd, int n, int type) +{ + int i; + + nullpo_retr (1, sd); + + for (i = 0; i < 11; i++) + { + if (equip_pos[i] > 0 && sd->equip_index[i] == n) + { //Slot taken, remove item from there. + pc_unequipitem (sd, sd->equip_index[i], type); + sd->equip_index[i] = -1; + } + } + + 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); + + // 所持品空き詰め + 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) + printf ("illeagal 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) + printf ("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 + && (it->flag.no_equip == 1 || it->flag.no_equip == 3)) + { //PvP制限 + sd->status.inventory[i].equip = 0; + calc_flag = 1; + } + else if (sd->status.inventory[i].equip && map[sd->bl.m].flag.gvg + && (it->flag.no_equip == 2 || it->flag.no_equip == 3)) + { //GvG制限 + sd->status.inventory[i].equip = 0; + calc_flag = 1; + } + } + + pc_setequipindex (sd); + if (calc_flag) + pc_calcstatus (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_foreachinarea (pc_calc_pvprank_sub, sd->bl.m, 0, 0, m->xs, m->ys, + 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) + *------------------------------------------ + */ +void pc_calc_pvprank_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = NULL; + if (battle_config.pk_mode) // disable pvp ranking if pk_mode on [Valaris] + return; + + sd = map_id2sd (id); + if (sd == NULL) + return; + 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); +} + +/*========================================== + * 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) + 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 = NULL; + if (sd == NULL || !pc_ismarried (sd)) + return -1; + + // If both are on map server we don't need to bother the char server + if ((p_sd = + map_nick2sd (map_charid2nick (sd->status.partner_id))) != NULL) + { + if (p_sd->status.partner_id != sd->status.char_id + || sd->status.partner_id != p_sd->status.char_id) + { + printf ("pc_divorce: Illegal partner_id sd=%d p_sd=%d\n", + sd->status.partner_id, p_sd->status.partner_id); + return -1; + } + p_sd->status.partner_id = 0; + sd->status.partner_id = 0; + + if (sd->npc_flags.divorce) + { + sd->npc_flags.divorce = 0; + map_scriptcont (sd, sd->npc_id); + } + } + else + chrif_send_divorce (sd->status.char_id); + + 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; + + return p_sd; +} + +// +// 自然回復物 +// +/*========================================== + * 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; + struct guild_castle *gc = NULL; + + nullpo_retr (0, sd); + + a = natural_heal_diff_tick; + if (pc_issit (sd)) + a += a; + if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // マグニフィカート + a += a; + + gc = guild_mapname2gc (sd->mapname); // Increased guild castle regen [Valaris] + if (gc) + { + struct guild *g; + g = guild_search (sd->status.guild_id); + if (g && g->guild_id == gc->guild_id) + a += a; + } // end addition [Valaris] + + return a; +} + +/*========================================== + * HP回復量計算 + *------------------------------------------ + */ +static int pc_hpheal (struct map_session_data *sd) +{ + int a; + struct guild_castle *gc; + + nullpo_retr (0, sd); + + a = natural_heal_diff_tick; + if (pc_issit (sd)) + a += a; + if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // Modified by RoVeRT + a += a; + + gc = guild_mapname2gc (sd->mapname); // Increased guild castle regen [Valaris] + if (gc) + { + struct guild *g; + g = guild_search (sd->status.guild_id); + if (g && g->guild_id == gc->guild_id) + a += a; + } // end addition [Valaris] + + return a; +} + +static int pc_natural_heal_hp (struct map_session_data *sd) +{ + int bhp; + int inc_num, bonus, skill, hp_flag; + + nullpo_retr (0, sd); + + if (sd->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT + 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; + + if (sd->sc_data[SC_APPLEIDUN].timer != -1) + { // Apple of Idun + if (sd->inchealhptick >= 6000 && sd->status.hp < sd->status.max_hp) + { + bonus = skill * 20; + while (sd->inchealhptick >= 6000) + { + sd->inchealhptick -= 6000; + 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->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT + 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->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) + { + struct pc_base_job s_class = pc_calc_base_job (sd->status.pc_class); + if (sd->doridori_counter && s_class.job == 23) + bonus = sd->nshealsp * 2; + else + bonus = sd->nshealsp; + sd->doridori_counter = 0; + 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 level) +{ + int bonus_hp, interval = battle_config.natural_heal_skill_interval; + struct status_change *sc_data = battle_get_sc_data (&sd->bl); + + 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 + && sc_data[SC_FLYING_BACKPACK].timer == -1) + interval += interval; + + if (sd->inchealspirithptick >= interval) + { + bonus_hp = sd->nsshealhp; + 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 level) +{ + 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; + 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; +} + +/*========================================== + * HP/SP 自然回復 各クライアント + *------------------------------------------ + */ +static int pc_itemheal_effect (struct map_session_data *sd, int hp, int sp); + +static int +pc_quickregenerate_effect (struct quick_regeneration *quick_regen, + int heal_speed) +{ + if (!(quick_regen->tickdelay--)) + { + int bonus = + MIN (heal_speed * battle_config.itemheal_regeneration_factor, + quick_regen->amount); + + quick_regen->amount -= bonus; + + quick_regen->tickdelay = quick_regen->speed; + + return bonus; + } + + return 0; +} + +static int pc_natural_heal_sub (struct map_session_data *sd, va_list ap) +{ + int skill; + + nullpo_retr (0, sd); + + if (sd->heal_xp > 0) + { + if (sd->heal_xp < 64) + --sd->heal_xp; // [Fate] Slowly reduce XP that healers can get for healing this char + else + sd->heal_xp -= (sd->heal_xp >> 6); + } + + // Hijack this callback: Adjust spellpower bonus + if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) + { + sd->spellpower_bonus_current = sd->spellpower_bonus_target; + pc_calcstatus (sd, 0); + } + else if (sd->spellpower_bonus_target > sd->spellpower_bonus_current) + { + sd->spellpower_bonus_current += + 1 + + ((sd->spellpower_bonus_target - + sd->spellpower_bonus_current) >> 5); + pc_calcstatus (sd, 0); + } + + if (sd->sc_data[SC_HALT_REGENERATE].timer != -1) + return 0; + + if (sd->quick_regeneration_hp.amount || sd->quick_regeneration_sp.amount) + { + int hp_bonus = pc_quickregenerate_effect (&sd->quick_regeneration_hp, + (sd->sc_data[SC_POISON].timer == -1 || sd->sc_data[SC_SLOWPOISON].timer != -1) ? sd->nhealhp : 1); // [fate] slow down when poisoned + int sp_bonus = pc_quickregenerate_effect (&sd->quick_regeneration_sp, + sd->nhealsp); + + pc_itemheal_effect (sd, hp_bonus, sp_bonus); + } + skill_update_heal_animation (sd); // if needed. + +// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status) + if ((sd->sc_data[SC_FLYING_BACKPACK].timer != -1 + || battle_config.natural_heal_weight_rate > 100 + || sd->weight * 100 / sd->max_weight < + battle_config.natural_heal_weight_rate) && !pc_isdead (sd) + && !pc_ishiding (sd) && sd->sc_data[SC_POISON].timer == -1) + { + pc_natural_heal_hp (sd); + if (sd->sc_data && sd->sc_data[SC_EXTREMITYFIST].timer == -1 && //阿修羅状態ではSPが回復しない + sd->sc_data[SC_DANCING].timer == -1 && //ダンス状態ではSPが回復しない + sd->sc_data[SC_BERSERK].timer == -1 //バーサーク状態ではSPが回復しない + ) + pc_natural_heal_sp (sd); + } + else + { + sd->hp_sub = sd->inchealhptick = 0; + sd->sp_sub = sd->inchealsptick = 0; + } + if ((skill = pc_checkskill (sd, MO_SPIRITSRECOVERY)) > 0 + && !pc_ishiding (sd) && sd->sc_data[SC_POISON].timer == -1 + && sd->sc_data[SC_BERSERK].timer == -1) + { + pc_spirit_heal_hp (sd, skill); + pc_spirit_heal_sp (sd, skill); + } + else + { + sd->inchealspirithptick = 0; + sd->inchealspiritsptick = 0; + } + return 0; +} + +/*========================================== + * HP/SP自然回復 (interval timer関数) + *------------------------------------------ + */ +void pc_natural_heal (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + natural_heal_tick = tick; + natural_heal_diff_tick = + DIFF_TICK (natural_heal_tick, natural_heal_prev_tick); + clif_foreachclient (pc_natural_heal_sub); + + natural_heal_prev_tick = tick; +} + +/*========================================== + * セーブポイントの保存 + *------------------------------------------ + */ +int pc_setsavepoint (struct map_session_data *sd, char *mapname, int x, int y) +{ + nullpo_retr (0, sd); + + strncpy (sd->status.save_point.map, mapname, 23); + sd->status.save_point.map[23] = '\0'; + 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); + + if (save_flag == 0 && sd->fd > last_save_fd) + { + struct guild_castle *gc = NULL; + int i; + + pc_makesavestatus (sd); + chrif_save (sd); + + for (i = 0; i < MAX_GUILDCASTLE; i++) + { + gc = guild_castle_search (i); + if (!gc) + continue; + if (gc->visibleG0 == 1) + guild_castledatasave (gc->castle_id, 18, gc->Ghp0); + if (gc->visibleG1 == 1) + guild_castledatasave (gc->castle_id, 19, gc->Ghp1); + if (gc->visibleG2 == 1) + guild_castledatasave (gc->castle_id, 20, gc->Ghp2); + if (gc->visibleG3 == 1) + guild_castledatasave (gc->castle_id, 21, gc->Ghp3); + if (gc->visibleG4 == 1) + guild_castledatasave (gc->castle_id, 22, gc->Ghp4); + if (gc->visibleG5 == 1) + guild_castledatasave (gc->castle_id, 23, gc->Ghp5); + if (gc->visibleG6 == 1) + guild_castledatasave (gc->castle_id, 24, gc->Ghp6); + if (gc->visibleG7 == 1) + guild_castledatasave (gc->castle_id, 25, gc->Ghp7); + } + + save_flag = 1; + last_save_fd = sd->fd; + } + + return 0; +} + +/*========================================== + * 自動セーブ (timer関数) + *------------------------------------------ + */ +void pc_autosave (timer_id tid, tick_t tick, custom_id_t id, custom_data_t 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); +} + +int pc_read_gm_account (int fd) +{ + int i = 0; + if (gm_account != NULL) + free (gm_account); + GM_num = 0; + + CREATE (gm_account, struct gm_account, (RFIFOW (fd, 2) - 4) / 5); + 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++; + } + return GM_num; +} + +/*========================================== + * timer to do the day + *------------------------------------------ + */ +void map_day_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ // by [yor] + struct map_session_data *pl_sd = NULL; + int i; + char tmpstr[1024]; + + if (battle_config.day_duration > 0) + { // if we want a day + if (night_flag != 0) + { + strcpy (tmpstr, msg_txt (502)); // The day has arrived! + 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->opt2 &= ~STATE_BLIND; + clif_changeoption (&pl_sd->bl); + clif_wis_message (pl_sd->fd, wisp_server_name, tmpstr, + strlen (tmpstr) + 1); + } + } + } + } +} + +/*========================================== + * timer to do the night + *------------------------------------------ + */ +void map_night_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ // by [yor] + struct map_session_data *pl_sd = NULL; + int i; + char tmpstr[1024]; + + if (battle_config.night_duration > 0) + { // if we want a night + if (night_flag == 0) + { + strcpy (tmpstr, msg_txt (503)); // The night has fallen... + 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->opt2 |= STATE_BLIND; + clif_changeoption (&pl_sd->bl); + clif_wis_message (pl_sd->fd, wisp_server_name, tmpstr, + strlen (tmpstr) + 1); + } + } + } + } +} + +void pc_setstand (struct map_session_data *sd) +{ + nullpo_retv (sd); + + if (sd->sc_data && sd->sc_data[SC_TENSIONRELAX].timer != -1) + skill_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; + + // 必要経験値読み込み + + fp = fopen_ ("db/exp.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/exp.txt\n"); + 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.maximum_level) + break; + } + fclose_ (fp); + printf ("read db/exp.txt done\n"); + + // JOB補正数値1 + fp = fopen_ ("db/job_db1.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/job_db1.txt\n"); + return 1; + } + i = 0; + while (fgets (line, sizeof (line) - 1, fp)) + { + char *split[50]; + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 21 && p; j++) + { + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + if (j < 21) + continue; + max_weight_base[i] = atoi (split[0]); + hp_coefficient[i] = atoi (split[1]); + hp_coefficient2[i] = atoi (split[2]); + sp_coefficient[i] = atoi (split[3]); + for (j = 0; j < 17; j++) + aspd_base[i][j] = atoi (split[j + 4]); + i++; +// -- moonsoul (below two lines added to accommodate high numbered new class ids) + if (i == 24) + i = 4001; + if (i == MAX_PC_CLASS) + break; + } + fclose_ (fp); + printf ("read db/job_db1.txt done\n"); + + // JOBボーナス + fp = fopen_ ("db/job_db2.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/job_db2.txt\n"); + return 1; + } + i = 0; + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < MAX_LEVEL && p; j++) + { + if (sscanf (p, "%d", &k) == 0) + break; + job_bonus[0][i][j] = k; + job_bonus[2][i][j] = k; //養子職のボーナスは分からないので仮 + p = strchr (p, ','); + if (p) + p++; + } + i++; +// -- moonsoul (below two lines added to accommodate high numbered new class ids) + if (i == 24) + i = 4001; + if (i == MAX_PC_CLASS) + break; + } + fclose_ (fp); + printf ("read db/job_db2.txt done\n"); + + // JOBボーナス2 転生職用 + fp = fopen_ ("db/job_db2-2.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/job_db2-2.txt\n"); + return 1; + } + i = 0; + while (fgets (line, sizeof (line) - 1, fp)) + { + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < MAX_LEVEL && p; j++) + { + if (sscanf (p, "%d", &k) == 0) + break; + job_bonus[1][i][j] = k; + p = strchr (p, ','); + if (p) + p++; + } + i++; + if (i == MAX_PC_CLASS) + break; + } + fclose_ (fp); + printf ("read db/job_db2-2.txt done\n"); + + // スキルツリー + memset (skill_tree, 0, sizeof (skill_tree)); + fp = fopen_ ("db/skill_tree.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/skill_tree.txt\n"); + return 1; + } + while (fgets (line, sizeof (line) - 1, fp)) + { + char *split[50]; + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 13 && p; j++) + { + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + if (j < 13) + continue; + i = atoi (split[0]); + for (j = 0; skill_tree[0][i][j].id; j++); + skill_tree[0][i][j].id = atoi (split[1]); + skill_tree[0][i][j].max = atoi (split[2]); + skill_tree[2][i][j].id = atoi (split[1]); //養子職は良く分からないので暫定 + skill_tree[2][i][j].max = atoi (split[2]); //養子職は良く分からないので暫定 + for (k = 0; k < 5; k++) + { + skill_tree[0][i][j].need[k].id = atoi (split[k * 2 + 3]); + skill_tree[0][i][j].need[k].lv = atoi (split[k * 2 + 4]); + skill_tree[2][i][j].need[k].id = atoi (split[k * 2 + 3]); //養子職は良く分からないので暫定 + skill_tree[2][i][j].need[k].lv = atoi (split[k * 2 + 4]); //養子職は良く分からないので暫定 + } + } + fclose_ (fp); + printf ("read db/skill_tree.txt done\n"); + + // 属性修正テーブル + 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; + fp = fopen_ ("db/attr_fix.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/attr_fix.txt\n"); + 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]); +// printf("%d %d\n",lv,n); + + 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); + printf ("read db/attr_fix.txt done\n"); + + // サイズ補正テーブル + for (i = 0; i < 3; i++) + for (j = 0; j < 20; j++) + atkmods[i][j] = 100; + fp = fopen_ ("db/size_fix.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/size_fix.txt\n"); + 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); + printf ("read db/size_fix.txt done\n"); + + // 精錬データテーブル + for (i = 0; i < 5; i++) + { + for (j = 0; j < 10; j++) + percentrefinery[i][j] = 100; + refinebonus[i][0] = 0; + refinebonus[i][1] = 0; + refinebonus[i][2] = 10; + } + fp = fopen_ ("db/refine_db.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/refine_db.txt\n"); + 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 < 10 && split[j]; j++) + percentrefinery[i][j] = atoi (split[j + 3]); + i++; + } + fclose_ (fp); //Lupus. close this file!!! + printf ("read db/refine_db.txt done\n"); + + return 0; +} + +static int pc_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; +} + +static void pc_statpointdb (void) +{ + char *buf_stat; + int i = 0, j = 0, k = 0, l = 0, end = 0; + + FILE *stp; + + stp = fopen_ ("db/statpoint.txt", "r"); + + if (stp == NULL) + { + printf ("can't read db/statpoint.txt\n"); + return; + } + + fseek (stp, 0, SEEK_END); + end = ftell (stp); + rewind (stp); + + buf_stat = (char *) malloc (end + 1); + l = fread (buf_stat, 1, end, stp); + fclose_ (stp); + printf ("read db/statpoint.txt done (size=%d)\n", l); + + for (i = 0; i < 255; i++) + { + j = 0; + while (*(buf_stat + k) != '\n') + { + statp[i][j] = *(buf_stat + k); + j++; + k++; + } + statp[i][j + 1] = '\0'; + k++; + } + + free (buf_stat); +} + +/*========================================== + * pc関 係初期化 + *------------------------------------------ + */ +int do_init_pc (void) +{ + pc_readdb (); + pc_statpointdb (); + pc_calc_sigma (); + +// gm_account_db = numdb_init(); + + 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); + + { + int day_duration = battle_config.day_duration; + int night_duration = battle_config.night_duration; + if (day_duration < 60000) + day_duration = 60000; + if (night_duration < 60000) + night_duration = 60000; + if (battle_config.night_at_start == 0) + { + 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; +} + +void pc_cleanup (struct map_session_data *sd) +{ + magic_stop_completely (sd); +} + +void pc_invisibility (struct map_session_data *sd, int enabled) +{ + if (enabled && !(sd->status.option & OPTION_INVISIBILITY)) + { + clif_clearchar_area (&sd->bl, 3); + sd->status.option |= OPTION_INVISIBILITY; + clif_status_change (&sd->bl, CLIF_OPTION_SC_INVISIBILITY, 1); + } + else if (!enabled) + { + sd->status.option &= ~OPTION_INVISIBILITY; + clif_status_change (&sd->bl, CLIF_OPTION_SC_INVISIBILITY, 0); + pc_setpos (sd, map[sd->bl.m].name, sd->bl.x, sd->bl.y, 3); + } +} + +int pc_logout (struct map_session_data *sd) // [fate] Player logs out +{ + unsigned int tick = gettick (); + + if (!sd) + return 0; + + if (sd->sc_data[SC_POISON].timer != -1) + sd->status.hp = 1; // Logging out while poisoned -> bad + + /* + * Trying to rapidly sign out/in or switch characters to avoid a spell's + * cast time is also bad. [remoitnane] + */ +#if 0 + // Removed because it's buggy, see above. + if (sd->cast_tick > tick) + { + if (pc_setglobalreg (sd, "MAGIC_CAST_TICK", sd->cast_tick - tick)) + sd->status.sp = 1; + } + else +#endif + pc_setglobalreg (sd, "MAGIC_CAST_TICK", 0); + + MAP_LOG_STATS (sd, "LOGOUT") return 0; +} diff --git a/src/map/pc.h b/src/map/pc.h deleted file mode 100644 index 742d9d4..0000000 --- a/src/map/pc.h +++ /dev/null @@ -1,210 +0,0 @@ -// $Id: pc.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ - -#ifndef _PC_H_ -#define _PC_H_ - -#include "map.h" - -#define OPTION_MASK 0xd7b8 -#define CART_MASK 0x788 - -#define pc_setdead(sd) ((sd)->state.dead_sit = 1) -#define pc_setsit(sd) ((sd)->state.dead_sit = 2) -//#define pc_setstand(sd) ((sd)->state.dead_sit = 0) -#define pc_isdead(sd) ((sd)->state.dead_sit == 1) -#define pc_issit(sd) ((sd)->state.dead_sit == 2) -#define pc_setdir(sd,b) ((sd)->dir = (b)) -#define pc_setchatid(sd,n) ((sd)->chatID = n) -#define pc_ishiding(sd) ((sd)->status.option&0x4006) -#define pc_iscarton(sd) ((sd)->status.option&CART_MASK) -#define pc_isfalcon(sd) ((sd)->status.option&0x0010) -#define pc_isriding(sd) ((sd)->status.option&0x0020) -#define pc_isinvisible(sd) ((sd)->status.option&0x0040) -#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight) -#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9) - -void pc_touch_all_relevant_npcs (struct map_session_data *sd); /* Checks all npcs/warps at the same location to see whether they - ** should do something with the specified player. */ - -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); - -void pc_invisibility (struct map_session_data *sd, int enabled); // [Fate] -int pc_counttargeted (struct map_session_data *sd, struct block_list *src, - int target_lv); -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, int, int, int); -int pc_authok (int, int, time_t, short tmw_version, struct mmo_charstatus *); -int pc_authfail (int); - -int pc_isequip (struct map_session_data *sd, int n); -int pc_equippoint (struct map_session_data *sd, int n); - -int pc_breakweapon (struct map_session_data *sd); // weapon breaking [Valaris] -int pc_breakarmor (struct map_session_data *sd); // armor breaking [Valaris] - -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_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 pc_setpos (struct map_session_data *, char *, int, int, int); -int pc_setsavepoint (struct map_session_data *, char *, int, int); -int pc_randomwarp (struct map_session_data *sd, int type); -int pc_memo (struct map_session_data *sd, int i); - -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_count_all_items (struct map_session_data *player, int item_id); -int pc_remove_items (struct map_session_data *player, int item_id, - int count); - -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_calcstatus (struct map_session_data *, int); -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_skill (struct map_session_data *, int, int, int); - -int pc_insert_card (struct map_session_data *sd, int idx_card, - int idx_equip); - -int pc_item_identify (struct map_session_data *sd, int idx); -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_checkbaselevelup (struct map_session_data *sd); -int pc_checkjoblevelup (struct map_session_data *sd); -int pc_gainexp (struct map_session_data *, int, int); - -#define PC_GAINEXP_REASON_KILLING 0 -#define PC_GAINEXP_REASON_HEALING 1 -#define PC_GAINEXP_REASON_SCRIPT 2 -int pc_gainexp_reason (struct map_session_data *, int, int, int reason); -int pc_extract_healer_exp (struct map_session_data *, int max); // [Fate] Used by healers: extract healer-xp from the target, return result (up to max) - -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_equipitem (struct map_session_data *, int, int); -int pc_unequipitem (struct map_session_data *, int, int); -int pc_unequipinvyitem (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); -int pc_readglobalreg (struct map_session_data *, char *); -int pc_setglobalreg (struct map_session_data *, char *, int); -int pc_readaccountreg (struct map_session_data *, char *); -int pc_setaccountreg (struct map_session_data *, char *, int); -int pc_readaccountreg2 (struct map_session_data *, char *); -int pc_setaccountreg2 (struct map_session_data *, char *, int); -int pc_percentrefinery (struct map_session_data *sd, struct item *item); - -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); -void pc_calc_pvprank_timer (timer_id, tick_t, custom_id_t, custom_data_t); - -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); -struct map_session_data *pc_get_partner (struct map_session_data *sd); -int pc_set_gm_level (int account_id, int level); -void pc_setstand (struct map_session_data *sd); -void pc_cleanup (struct map_session_data *sd); // [Fate] Clean up after a logged-out PC - -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_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); -int pc_logout (struct map_session_data *sd); // [fate] Player logs out - -int do_init_pc (void); - -enum -{ ADDITEM_EXIST, ADDITEM_NEW, ADDITEM_OVERAMOUNT }; - -// timer for night.day -timer_id day_timer_tid; -timer_id night_timer_tid; -void map_day_timer (timer_id, tick_t, custom_id_t, custom_data_t); // by [yor] -void map_night_timer (timer_id, tick_t, custom_id_t, custom_data_t); // by [yor] - -#endif diff --git a/src/map/pc.hpp b/src/map/pc.hpp new file mode 100644 index 0000000..7237957 --- /dev/null +++ b/src/map/pc.hpp @@ -0,0 +1,210 @@ +// $Id: pc.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ + +#ifndef PC_HPP +#define PC_HPP + +#include "map.hpp" + +#define OPTION_MASK 0xd7b8 +#define CART_MASK 0x788 + +#define pc_setdead(sd) ((sd)->state.dead_sit = 1) +#define pc_setsit(sd) ((sd)->state.dead_sit = 2) +//#define pc_setstand(sd) ((sd)->state.dead_sit = 0) +#define pc_isdead(sd) ((sd)->state.dead_sit == 1) +#define pc_issit(sd) ((sd)->state.dead_sit == 2) +#define pc_setdir(sd,b) ((sd)->dir = (b)) +#define pc_setchatid(sd,n) ((sd)->chatID = n) +#define pc_ishiding(sd) ((sd)->status.option&0x4006) +#define pc_iscarton(sd) ((sd)->status.option&CART_MASK) +#define pc_isfalcon(sd) ((sd)->status.option&0x0010) +#define pc_isriding(sd) ((sd)->status.option&0x0020) +#define pc_isinvisible(sd) ((sd)->status.option&0x0040) +#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight) +#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9) + +void pc_touch_all_relevant_npcs (struct map_session_data *sd); /* Checks all npcs/warps at the same location to see whether they + ** should do something with the specified player. */ + +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); + +void pc_invisibility (struct map_session_data *sd, int enabled); // [Fate] +int pc_counttargeted (struct map_session_data *sd, struct block_list *src, + int target_lv); +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, int, int, int); +int pc_authok (int, int, time_t, short tmw_version, struct mmo_charstatus *); +int pc_authfail (int); + +int pc_isequip (struct map_session_data *sd, int n); +int pc_equippoint (struct map_session_data *sd, int n); + +int pc_breakweapon (struct map_session_data *sd); // weapon breaking [Valaris] +int pc_breakarmor (struct map_session_data *sd); // armor breaking [Valaris] + +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_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 pc_setpos (struct map_session_data *, char *, int, int, int); +int pc_setsavepoint (struct map_session_data *, char *, int, int); +int pc_randomwarp (struct map_session_data *sd, int type); +int pc_memo (struct map_session_data *sd, int i); + +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_count_all_items (struct map_session_data *player, int item_id); +int pc_remove_items (struct map_session_data *player, int item_id, + int count); + +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_calcstatus (struct map_session_data *, int); +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_skill (struct map_session_data *, int, int, int); + +int pc_insert_card (struct map_session_data *sd, int idx_card, + int idx_equip); + +int pc_item_identify (struct map_session_data *sd, int idx); +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_checkbaselevelup (struct map_session_data *sd); +int pc_checkjoblevelup (struct map_session_data *sd); +int pc_gainexp (struct map_session_data *, int, int); + +#define PC_GAINEXP_REASON_KILLING 0 +#define PC_GAINEXP_REASON_HEALING 1 +#define PC_GAINEXP_REASON_SCRIPT 2 +int pc_gainexp_reason (struct map_session_data *, int, int, int reason); +int pc_extract_healer_exp (struct map_session_data *, int max); // [Fate] Used by healers: extract healer-xp from the target, return result (up to max) + +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_equipitem (struct map_session_data *, int, int); +int pc_unequipitem (struct map_session_data *, int, int); +int pc_unequipinvyitem (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); +int pc_readglobalreg (struct map_session_data *, char *); +int pc_setglobalreg (struct map_session_data *, char *, int); +int pc_readaccountreg (struct map_session_data *, char *); +int pc_setaccountreg (struct map_session_data *, char *, int); +int pc_readaccountreg2 (struct map_session_data *, char *); +int pc_setaccountreg2 (struct map_session_data *, char *, int); +int pc_percentrefinery (struct map_session_data *sd, struct item *item); + +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); +void pc_calc_pvprank_timer (timer_id, tick_t, custom_id_t, custom_data_t); + +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); +struct map_session_data *pc_get_partner (struct map_session_data *sd); +int pc_set_gm_level (int account_id, int level); +void pc_setstand (struct map_session_data *sd); +void pc_cleanup (struct map_session_data *sd); // [Fate] Clean up after a logged-out PC + +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_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); +int pc_logout (struct map_session_data *sd); // [fate] Player logs out + +int do_init_pc (void); + +enum +{ ADDITEM_EXIST, ADDITEM_NEW, ADDITEM_OVERAMOUNT }; + +// timer for night.day +timer_id day_timer_tid; +timer_id night_timer_tid; +void map_day_timer (timer_id, tick_t, custom_id_t, custom_data_t); // by [yor] +void map_night_timer (timer_id, tick_t, custom_id_t, custom_data_t); // by [yor] + +#endif diff --git a/src/map/script.c b/src/map/script.c deleted file mode 100644 index 12fbab2..0000000 --- a/src/map/script.c +++ /dev/null @@ -1,8145 +0,0 @@ -// $Id: script.c 148 2004-09-30 14:05:37Z MouseJstr $ -//#define DEBUG_FUNCIN -//#define DEBUG_DISP -//#define DEBUG_RUN - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#ifndef LCCWIN32 -#include <sys/time.h> -#endif - -#include <time.h> -#include <math.h> - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/lock.h" -#include "../common/mt_rand.h" - -#include "atcommand.h" -#include "battle.h" -#include "chat.h" -#include "chrif.h" -#include "clif.h" -#include "../common/db.h" -#include "guild.h" -#include "intif.h" -#include "itemdb.h" -#include "../common/lock.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "storage.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define SCRIPT_BLOCK_SIZE 256 -enum -{ LABEL_NEXTLINE = 1, LABEL_START }; -static unsigned char *script_buf; -static int script_pos, script_size; - -char *str_buf; -int str_pos, str_size; -static struct str_data_t -{ - int type; - int str; - int backpatch; - int label; - int (*func) (struct script_state *); - int val; - int next; -} *str_data; -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 (void) -{ - return scriptlabel_db; -} - -struct dbt *script_get_userfunc_db (void) -{ - if (!userfunc_db) - userfunc_db = strdb_init (50); - return userfunc_db; -} - -static char pos[11][100] = - { "頭", "体", "左手", "右手", "ローブ", "靴", "アクセサリー1", - "アクセサリー2", "頭2", "頭3", "装着していない" -}; - -static struct Script_Config -{ - int warn_func_no_comma; - int warn_cmd_no_comma; - int warn_func_mismatch_paramnum; - int warn_cmd_mismatch_paramnum; - int check_cmdcount; - int check_gotocount; -} script_config; -static int parse_cmd_if = 0; -static int parse_cmd; - -/*========================================== - * ローカルプロトタイプ宣言 (必要な物のみ) - *------------------------------------------ - */ -unsigned char *parse_subexpr (unsigned char *, int); -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_pow (struct script_state *st); -int buildin_warp (struct script_state *st); -int buildin_isat (struct script_state *st); -int buildin_areawarp (struct script_state *st); -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_if (struct script_state *st); -int buildin_getitem (struct script_state *st); -int buildin_getitem2 (struct script_state *st); -int buildin_makeitem (struct script_state *st); -int buildin_delitem (struct script_state *st); -int buildin_viewpoint (struct script_state *st); -int buildin_countitem (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_skill (struct script_state *st); -int buildin_setskill (struct script_state *st); -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_getopt2 (struct script_state *st); -int buildin_setopt2 (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_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_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_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_end (struct script_state *st); -int buildin_sc_check (struct script_state *st); // [Fate] -int buildin_getscrate (struct script_state *st); -int buildin_debugmes (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_getitemname (struct script_state *st); -int buildin_getspellinvocation (struct script_state *st); // [Fate] -int buildin_getanchorinvocation (struct script_state *st); // [Fate] -int buildin_getexp (struct script_state *st); -int buildin_getinventorylist (struct script_state *st); -int buildin_getskilllist (struct script_state *st); -int buildin_get_pool_skills (struct script_state *st); // [fate] -int buildin_get_activated_pool_skills (struct script_state *st); // [fate] -int buildin_get_unactivated_pool_skills (struct script_state *st); // [PO] -int buildin_activate_pool_skill (struct script_state *st); // [fate] -int buildin_deactivate_pool_skill (struct script_state *st); // [fate] -int buildin_check_pool_skill (struct script_state *st); // [fate] -int buildin_getskilllist (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_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_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_gmcommand (struct script_state *st); // [MouseJstr] -int buildin_movenpc (struct script_state *st); // [MouseJstr] -int buildin_npcwarp (struct script_state *st); // [remoitnane] -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_getpartnerid (struct script_state *st); // [Fate] -int buildin_areatimer (struct script_state *st); // [Jaxad0127] -int buildin_isin (struct script_state *st); // [Jaxad0127] -int buildin_shop (struct script_state *st); // [MadCamel] -int buildin_isdead (struct script_state *st); // [Jaxad0127] -int buildin_fakenpcname (struct script_state *st); //[Kage] -int buildin_unequip_by_id (struct script_state *st); // [Freeyorp] -int buildin_getx (struct script_state *st); // [Kage] -int buildin_gety (struct script_state *st); // [Kage] - - -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); - -struct -{ - int (*func) (struct script_state *); - char *name; - char *arg; -} buildin_func[] = -{ - { - buildin_mes, "mes", "s"}, - { - buildin_next, "next", ""}, - { - buildin_close, "close", ""}, - { - buildin_close2, "close2", ""}, - { - buildin_menu, "menu", "sL*"}, - { - buildin_goto, "goto", "L"}, - { - buildin_callsub, "callsub", "L*"}, - { - buildin_callfunc, "callfunc", "F*"}, - { - buildin_return, "return", "*"}, - { - buildin_getarg, "getarg", "i"}, - { - buildin_jobchange, "jobchange", "i*"}, - { - buildin_input, "input", "N"}, - { - buildin_warp, "warp", "Mxy"}, - { - buildin_isat, "isat", "Mxy"}, - { - buildin_areawarp, "areawarp", "MxyxyMxy"}, - { - buildin_setlook, "setlook", "ii"}, - { - buildin_set, "set", "Ne"}, - { - buildin_setarray, "setarray", "Ne*"}, - { - buildin_cleararray, "cleararray", "Nei"}, - { - buildin_copyarray, "copyarray", "NNi"}, - { - buildin_getarraysize, "getarraysize", "N"}, - { - buildin_deletearray, "deletearray", "N*"}, - { - buildin_getelementofarray, "getelementofarray", "Ni"}, - { - buildin_if, "if", "iF*"}, - { - buildin_getitem, "getitem", "Ii**"}, - { - buildin_getitem2, "getitem2", "iiiiiiiii*"}, - { - buildin_makeitem, "makeitem", "IiMxy"}, - { - buildin_delitem, "delitem", "Ii"}, - { - 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_pow, "pow", "ii"}, - { - buildin_countitem, "countitem", "I"}, - { - 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_skill, "skill", "ii*"}, - { - buildin_setskill, "setskill", "ii"}, // [Fate] - { - buildin_guildskill, "guildskill", "ii"}, - { - buildin_getskilllv, "getskilllv", "i"}, - { - buildin_getgdskilllv, "getgdskilllv", "ii"}, - { - buildin_basicskillcheck, "basicskillcheck", "*"}, - { - buildin_getgmlevel, "getgmlevel", ""}, - { - buildin_end, "end", ""}, - { - buildin_getopt2, "getopt2", ""}, - { - buildin_setopt2, "setopt2", "i"}, - { - buildin_end, "break", ""}, - { - 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", "Mxy"}, - { - buildin_gettimetick, "gettimetick", "i"}, - { - buildin_gettime, "gettime", "i"}, - { - buildin_gettimestr, "gettimestr", "si"}, - { - buildin_openstorage, "openstorage", "*"}, - { - buildin_guildopenstorage, "guildopenstorage", "*"}, - { - buildin_itemskill, "itemskill", "iis"}, - { - buildin_monster, "monster", "Mxysmi*"}, - { - buildin_areamonster, "areamonster", "Mxyxysmi*"}, - { - buildin_killmonster, "killmonster", "ME"}, - { - buildin_killmonsterall, "killmonsterall", "M"}, - { - buildin_doevent, "doevent", "E"}, - { - buildin_donpcevent, "donpcevent", "E"}, - { - buildin_addtimer, "addtimer", "tE"}, - { - buildin_deltimer, "deltimer", "E"}, - { - buildin_addtimercount, "addtimercount", "si"}, - { - buildin_initnpctimer, "initnpctimer", ""}, - { - buildin_stopnpctimer, "stopnpctimer", ""}, - { - buildin_startnpctimer, "startnpctimer", "*"}, - { - buildin_setnpctimer, "setnpctimer", "i"}, - { - buildin_getnpctimer, "getnpctimer", "i"}, - { - buildin_announce, "announce", "si"}, - { - buildin_mapannounce, "mapannounce", "Msi"}, - { - buildin_areaannounce, "areaannounce", "Mxyxysi"}, - { - buildin_getusers, "getusers", "i"}, - { - buildin_getmapusers, "getmapusers", "M"}, - { - buildin_getareausers, "getareausers", "Mxyxy*"}, - { - buildin_getareadropitem, "getareadropitem", "Mxyxyi*"}, - { - 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", "iTi*"}, - { - buildin_sc_start2, "sc_start2", "iTii*"}, - { - buildin_sc_end, "sc_end", "i"}, - { - buildin_sc_check, "sc_check", "i"}, - { - buildin_getscrate, "getscrate", "ii*"}, - { - buildin_debugmes, "debugmes", "s"}, - { - 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", "MMxy"}, - { - buildin_setmapflag, "setmapflag", "Mi"}, - { - buildin_removemapflag, "removemapflag", "Mi"}, - { - buildin_pvpon, "pvpon", "M"}, - { - buildin_pvpoff, "pvpoff", "M"}, - { - 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", "P"}, - { - buildin_wedding_effect, "wedding", ""}, - { - buildin_divorce, "divorce", ""}, - { - buildin_getitemname, "getitemname", "I"}, - { - buildin_getspellinvocation, "getspellinvocation", "s"}, - { - buildin_getanchorinvocation, "getanchorinvocation", "s"}, - { - buildin_getpartnerid, "getpartnerid2", ""}, - { - buildin_getexp, "getexp", "ii"}, - { - buildin_getinventorylist, "getinventorylist", ""}, - { - buildin_getskilllist, "getskilllist", ""}, - { - buildin_get_pool_skills, "getpoolskilllist", ""}, - { - buildin_get_activated_pool_skills, "getactivatedpoolskilllist", ""}, - { - buildin_get_unactivated_pool_skills, "getunactivatedpoolskilllist", ""}, - { - buildin_activate_pool_skill, "poolskill", "i"}, - { - buildin_deactivate_pool_skill, "unpoolskill", "i"}, - { - buildin_check_pool_skill, "checkpoolskill", "i"}, - { - buildin_clearitem, "clearitem", ""}, - { - buildin_classchange, "classchange", "ii"}, - { - buildin_misceffect, "misceffect", "i*"}, - { - buildin_soundeffect, "soundeffect", "si"}, - { - buildin_strmobinfo, "strmobinfo", "im"}, // display mob data [Valaris] - { - buildin_guardian, "guardian", "siisii*i"}, // summon guardians - { - buildin_guardianinfo, "guardianinfo", "i"}, // display guardian data [Valaris] - { - 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", "MMxy"}, // Added by RoVeRT - { - buildin_inittimer, "inittimer", ""}, - { - buildin_stoptimer, "stoptimer", ""}, - { - buildin_cmdothernpc, "cmdothernpc", "ss"}, - { - buildin_gmcommand, "gmcommand", "s"}, // [MouseJstr] -// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr] - { - buildin_npcwarp, "npcwarp", "xys"}, // [remoitnane] - { - buildin_message, "message", "Ps"}, // [MouseJstr] - { - buildin_npctalk, "npctalk", "s"}, // [Valaris] - { - buildin_hasitems, "hasitems", ""}, // [Valaris] - { - buildin_mobcount, "mobcount", "ME"}, - { - buildin_getlook, "getlook", "i"}, - { - buildin_getsavepoint, "getsavepoint", "i"}, - { - buildin_areatimer, "areatimer", "MxyxytE"}, - { - buildin_isin, "isin", "Mxyxy"}, - { - buildin_shop, "shop", "s"}, - { - buildin_isdead, "isdead", ""}, - { - buildin_fakenpcname, "fakenpcname", "ssi"}, - { - buildin_unequip_by_id, "unequipbyid", "i"}, // [Freeyorp] - { - buildin_getx, "getx", ""}, // [Kage] - { - buildin_gety, "gety", ""}, // [Kage] - // End Additions - { -NULL, NULL, NULL},}; - -int buildin_message (struct script_state *st); // [MouseJstr] - -enum -{ - C_NOP, C_POS, C_INT, C_PARAM, C_FUNC, C_STR, C_CONSTSTR, C_ARG, - C_NAME, C_EOL, C_RETINFO, - - 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 -}; - -/*========================================== - * 文字列のハッシュを計算 - *------------------------------------------ - */ -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, 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 = strdup (p); - for (i = 0; lowcase[i]; i++) - lowcase[i] = tolower (lowcase[i]); - if ((i = search_str (lowcase)) >= 0) - { - free (lowcase); - return i; - } - free (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, 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; - RECREATE (str_data, struct str_data_t, str_data_size); - memset (str_data + (str_data_size - 128), '\0', 128); - } - while (str_pos + strlen (p) + 1 >= str_size) - { - str_size += 256; - str_buf = (char *) realloc (str_buf, str_size); - memset (str_buf + (str_size - 256), '\0', 256); - } - strcpy (str_buf + str_pos, 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 += strlen (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 = (char *) realloc (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 (unsigned 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: - 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: - // ラベルの可能性があるので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 = 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] = 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変数用 - if (*p == 'l') - p++; // 一時的変数用(like weiss) - - 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 = strchr (p, '\n'); - if (lineend) - { - c = *lineend; - *lineend = 0; - } - if (lineend == NULL || pos < lineend) - { - printf ("%s line %d : ", 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) - printf ("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 (p, &np, 0); - add_scripti (i); - p = 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) - { - disp_error_message ("unexpected character", p); - exit (1); - } - p2 = skip_word (p); - c = *p2; - *p2 = 0; // 名前をadd_strする - l = add_str (p); - - parse_cmd = l; // warn_*_mismatch_paramnumのために必要 - if (l == search_str ("if")) // warn_cmd_no_commaのために必要 - parse_cmd_if++; -/* - // 廃止予定のl14/l15,およびプレフィックスlの警告 - if( strcmp(str_buf+str_data[l].str,"l14")==0 || - strcmp(str_buf+str_data[l].str,"l15")==0 ){ - disp_error_message("l14 and l15 is DEPRECATED. use @menu instead of l15.",p); - }else if(str_buf[str_data[l].str]=='l'){ - disp_error_message("prefix 'l' is DEPRECATED. use prefix '@' instead.",p2); - } -*/ - *p2 = c; - p = p2; - - if (str_data[l].type != C_FUNC && c == '[') - { - // array(name[i] => getelementofarray(name,i) ) - add_scriptl (search_str ("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 - add_scriptl (l); - - } - -#ifdef DEBUG_FUNCIN - if (battle_config.etc_log) - printf ("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) - printf ("parse_subexpr %s\n", p); -#endif - p = skip_space (p); - - if (*p == '-') - { - tmpp = skip_space (p + 1); - if (*tmpp == ';' || *tmpp == ',') - { - add_scriptl (LABEL_NEXTLINE); - p++; - return p; - } - } - tmpp = 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[func].type != C_FUNC) - { - disp_error_message ("expect function", tmpp); - exit (0); - } - - add_scriptc (C_ARG); - while (*p && *p != ')' && i < 128) - { - plist[i] = 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++; - } - plist[i] = 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 (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", - plist[(i < j) ? i : j]); - } - } - } - else // not op == C_FUNC - { - p = parse_subexpr (p, opl); - } - add_scriptc (op); - p = skip_space (p); - } -#ifdef DEBUG_FUNCIN - if (battle_config.etc_log) - printf ("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) - printf ("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) - printf ("parse_expr end %s\n", p); -#endif - return p; -} - -/*========================================== - * 行の解析 - *------------------------------------------ - */ -unsigned char *parse_line (unsigned char *p) -{ - int i = 0, cmd; - const char *plist[128]; - char *p2; - - p = skip_space (p); - if (*p == ';') - return p; - - parse_cmd_if = 0; // warn_cmd_no_commaのために必要 - - // 最初は関数名 - p2 = p; - p = parse_simpleexpr (p); - p = skip_space (p); - - cmd = parse_cmd; - if (str_data[cmd].type != C_FUNC) - { - disp_error_message ("expect command", p2); -// exit(0); - } - - add_scriptc (C_ARG); - while (p && *p && *p != ';' && i < 128) - { - plist[i] = p; - - p = parse_expr (p); - p = skip_space (p); - // 引数区切りの,処理 - if (*p == ',') - p++; - else if (*p != ';' && script_config.warn_cmd_no_comma - && parse_cmd_if * 2 <= i) - { - disp_error_message ("expect ',' or ';' at cmd params", p); - } - p = skip_space (p); - i++; - } - plist[i] = p; - if (!p || *(p++) != ';') - { - disp_error_message ("need ';'", p); - exit (1); - } - add_scriptc (C_FUNC); - - 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", - plist[(i < j) ? i : j]); - } - } - - return p; -} - -/*========================================== - * 組み込み関数の追加 - *------------------------------------------ - */ -static void add_buildin_func (void) -{ - int i, n; - for (i = 0; buildin_func[i].func; i++) - { - n = add_str (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; - - fp = fopen_ ("db/const.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/const.txt\n"); - 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 (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; - script_buf = (unsigned char *) calloc (SCRIPT_BLOCK_SIZE, 1); - 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_NOP; - str_data[i].backpatch = -1; - str_data[i].label = -1; - } - } - - // 外部用label dbの初期化 - if (scriptlabel_db != NULL) - strdb_final (scriptlabel_db, NULL); - scriptlabel_db = strdb_init (50); - - // for error message - startptr = src; - startline = line; - - p = src; - p = skip_space (p); - if (*p != '{') - { - disp_error_message ("not found '{'", p); - return NULL; - } - for (p++; p && *p && *p != '}';) - { - p = skip_space (p); - // labelだけ特殊処理 - tmpp = skip_space (skip_word (p)); - if (*tmpp == ':') - { - 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_insert (scriptlabel_db, (const char*)p, 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 = (char *) realloc (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 - - return script_buf; -} - -// -// 実行系 -// -enum -{ STOP = 1, 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) - { - printf ("script_rid2sd: fatal error ! player not attached!\n"); - } - 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) - printf ("get_val error name?:%s\n", name); - } - if (postfix == '$') - { - - data->type = C_CONSTSTR; - if (prefix == '@' || prefix == 'l') - { - if (sd) - data->u.str = pc_readregstr (sd, data->u.num); - } - else if (prefix == '$') - { - data->u.str = - (char *) numdb_search (mapregstr_db, data->u.num); - } - else - { - printf ("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 == '@' || prefix == 'l') - { - if (sd) - data->u.num = pc_readreg (sd, data->u.num); - } - else if (prefix == '$') - { - data->u.num = (int) numdb_search (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 == '@' || prefix == 'l') - { - pc_setregstr (sd, num, str); - } - else if (prefix == '$') - { - mapreg_setregstr (num, str); - } - else - { - printf ("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 == '@' || prefix == 'l') - { - 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; -} - -/*========================================== - * 文字列への変換 - *------------------------------------------ - */ -char *conv_str (struct script_state *st, struct script_data *data) -{ - get_val (st, data); - if (data->type == C_INT) - { - char *buf; - buf = (char *) calloc (16, 1); - sprintf (buf, "%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) - free (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 *) - realloc (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 *) - realloc (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 = 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, stack->stack_data[pos].u.str); - break; - case C_STR: - push_str (stack, C_STR, strdup (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) - { - free (stack->stack_data[i].u.str); - } - } - 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; -} - -// -// 埋め込み関数 -// -/*========================================== - * - *------------------------------------------ - */ -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) - { - printf ("script: goto: not label !\n"); - st->state = END; - return 0; - } - - 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_search (script_get_userfunc_db (), 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->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->defsp = st->start + 4 + j; - st->state = GOTO; - } - else - { - printf ("script:callfunc: function not found! [%s]\n", str); - st->state = END; - } - return 0; -} - -/*========================================== - * サブルーティンの呼び出し - *------------------------------------------ - */ -int buildin_callsub (struct script_state *st) -{ - int pos = conv_num (st, &(st->stack->stack_data[st->start + 2])); - 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->defsp); // 現在の基準スタックポインタをプッシュ - push_val (st->stack, C_INT, (int) st->script); // 現在のスクリプトをプッシュ - push_val (st->stack, C_RETINFO, st->pos); // 現在のスクリプト位置をプッシュ - - st->pos = pos; - st->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->defsp < 4 - || st->stack->stack_data[st->defsp - 1].type != C_RETINFO) - { - printf ("script:getarg without callfunc or callsub!\n"); - st->state = END; - return 0; - } - max = conv_num (st, &(st->stack->stack_data[st->defsp - 4])); - stsp = st->defsp - max - 4; - if (num >= max) - { - printf ("script:getarg arg1(%d) out of range(%d) !\n", num, max); - st->state = END; - return 0; - } - 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 i, len = 0; // [fate] len is the total # of bytes we need to transmit the string choices - int menu_choices = 0; - int finished_menu_items = 0; // [fate] set to 1 after we hit the first empty string - - struct map_session_data *sd; - - sd = script_rid2sd (st); - - // We don't need to do this iteration if the player cancels, strictly speaking. - for (i = st->start + 2; i < st->end; i += 2) - { - int choice_len; - conv_str (st, &(st->stack->stack_data[i])); - choice_len = strlen (st->stack->stack_data[i].u.str); - len += choice_len + 1; // count # of bytes we'll need for packet. Only used if menu_or_input = 0. - - if (choice_len && !finished_menu_items) - ++menu_choices; - else - finished_menu_items = 1; - } - - if (sd->state.menu_or_input == 0) - { - st->state = RERUNLINE; - sd->state.menu_or_input = 1; - - buf = (char *) calloc (len + 1, 1); - buf[0] = 0; - for (i = st->start + 2; menu_choices > 0; i += 2, --menu_choices) - { - strcat (buf, st->stack->stack_data[i].u.str); - strcat (buf, ":"); - } - clif_scriptmenu (script_rid2sd (st), st->oid, buf); - free (buf); - } - else if (sd->npc_menu == 0xff) - { // cansel - sd->state.menu_or_input = 0; - st->state = END; - } - else - { // goto動作 - // ragemu互換のため - pc_setreg (sd, add_str ("l15"), sd->npc_menu); - pc_setreg (sd, add_str ("@menu"), sd->npc_menu); - sd->state.menu_or_input = 0; - if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices) - { - int pos; - if (st->stack-> - stack_data[st->start + sd->npc_menu * 2 + 1].type != C_POS) - { - st->state = END; - return 0; - } - pos = - conv_num (st, - &(st-> - stack->stack_data[st->start + sd->npc_menu * 2 + - 1])); - st->pos = pos; - st->state = GOTO; - } - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_rand (struct script_state *st) -{ - int range, min, max; - - if (st->end > st->start + 3) - { - 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) - { - int tmp; - tmp = min; - min = max; - max = tmp; - } - range = max - min + 1; - push_val (st->stack, C_INT, (range <= 0 ? 0 : MRAND (range)) + min); - } - else - { - range = conv_num (st, &(st->stack->stack_data[st->start + 2])); - push_val (st->stack, C_INT, range <= 0 ? 0 : MRAND (range)); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_pow (struct script_state *st) -{ - int a, b; - - a = conv_num (st, &(st->stack->stack_data[st->start + 2])); - b = conv_num (st, &(st->stack->stack_data[st->start + 3])); - - push_val (st->stack, C_INT, (int) pow (a * 0.001, b)); - - return 0; -} - -/*========================================== - * Check whether the PC is at the specified location - *------------------------------------------ - */ -int buildin_isat (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 (!sd) - return 1; - - push_val (st->stack, C_INT, - (x == sd->bl.x) - && (y == sd->bl.y) && (!strcmp (str, map[sd->bl.m].name))); - - 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, str, x, y, 0); - return 0; -} - -/*========================================== - * エリア指定ワープ - *------------------------------------------ - */ -int buildin_areawarp_sub (struct block_list *bl, va_list ap) -{ - int x, y; - char *map; - map = va_arg (ap, char *); - x = va_arg (ap, int); - y = va_arg (ap, int); - if (strcmp (map, "Random") == 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; - 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; - - map_foreachinarea (buildin_areawarp_sub, - m, x0, y0, x1, y1, BL_PC, str, x, y); - 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])); - 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])); - 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 = - (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 - { - printf ("buildin_input: string discarded !!\n"); - } - } - else - { - - //commented by Lupus (check Value Number Input fix in clif.c) - //** 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 - } - - // 数値 - if (st->end > st->start + 2) - { // 引数1個 - set_reg (sd, num, name, (void *) sd->npc_amount); - } - else - { - // ragemu互換のため - pc_setreg (sd, add_str ("l14"), sd->npc_amount); - } - } - } - else - { - 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_if (struct script_state *st) -{ - int sel, i; - - sel = conv_num (st, &(st->stack->stack_data[st->start + 2])); - if (!sel) - return 0; - - // 関数名をコピー - push_copy (st->stack, st->start + 3); - // 間に引数マーカを入れて - push_val (st->stack, C_ARG, 0); - // 残りの引数をコピー - for (i = st->start + 4; i < st->end; i++) - { - push_copy (st->stack, i); - } - run_func (st); - - 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) - { - printf ("script: buildin_set: not name\n"); - return 0; - } - - 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 != '@') - { - printf ("buildin_setarray: illegal scope !\n"); - return 0; - } - 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 != '@') - { - printf ("buildin_cleararray: illegal scope !\n"); - return 0; - } - 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 != '@') - { - printf ("buildin_copyarray: illegal scope !\n"); - return 0; - } - if ((postfix == '$' || postfix2 == '$') && postfix != postfix2) - { - printf ("buildin_copyarray: type mismatch !\n"); - return 0; - } - if (prefix != '$' || prefix2 != '$') - sd = script_rid2sd (st); - - 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 = i; - for (; i < 128; i++) - { - void *v = get_val2 (st, num + (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 != '@') - { - printf ("buildin_copyarray: illegal scope !\n"); - return 0; - } - - 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 != '@') - { - printf ("buildin_deletearray: illegal scope !\n"); - return 0; - } - 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, ""); - } - 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) - { - printf - ("script: getelementofarray (operator[]): param2 illegal number %d\n", - i); - push_val (st->stack, C_INT, 0); - } - else - { - push_val (st->stack, C_NAME, - (i << 24) | st->stack->stack_data[st->start + 2].u.num); - } - } - else - { - printf - ("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; - - itemid = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - clif_cutin (script_rid2sd (st), itemdb_search (itemid)->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) - printf ("wrong item ID : countitem(%i)\n", nameid); - } - push_val (st->stack, C_INT, count); - - return 0; -} - -/*========================================== - * 重量チェック - *------------------------------------------ - */ -int buildin_checkweight (struct script_state *st) -{ - int nameid = 0, amount; - 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); - } - - sd = script_rid2sd (st); - if (itemdb_weight (nameid) * amount + sd->weight > sd->max_weight) - { - push_val (st->stack, C_INT, 0); - } - else - { - push_val (st->stack, C_INT, 1); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_getitem (struct script_state *st) -{ - int nameid, 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 = 727; //Default to iten - 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 (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); - 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); - map_addflooritem (&item_tmp, amount, sd->bl.m, sd->bl.x, sd->bl.y, - NULL, NULL, NULL, 0); - } - } - - 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_search (nameid); - 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); - } - } - - 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 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])); - 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 (sd && strcmp (mapname, "this") == 0) - 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); - -// clif_additem(sd,0,0,flag); - map_addflooritem (&item_tmp, amount, m, x, y, NULL, NULL, NULL, 0); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_delitem (struct script_state *st) -{ - int nameid = 0, amount, 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 = 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 - //printf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); - return 0; - } - sd = script_rid2sd (st); - - for (i = 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; - } - for (i = 0; i < MAX_INVENTORY; i++) - { - if (sd->status.inventory[i].nameid == nameid) - { - if (sd->status.inventory[i].amount >= amount) - { - pc_delitem (sd, i, amount, 0); - break; - } - else - { - amount -= sd->status.inventory[i].amount; - if (amount == 0) - amount = sd->status.inventory[i].amount; - pc_delitem (sd, i, amount, 0); - break; - } - } - } - - 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 *) calloc (24, 1); - strcpy (buf, p->name); - 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 != 0) - push_str (st->stack, C_STR, name); - else - push_str (st->stack, C_CONSTSTR, "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 ("$@partymembername$") + (i << 24), - p->member[i].name); - j++; - } - } - } - mapreg_setreg (add_str ("$@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 *) calloc (24, 1); - strcpy (buf, g->name); - return buf; - } - return 0; -} - -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 != 0) - push_str (st->stack, C_STR, name); - else - push_str (st->stack, C_CONSTSTR, "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 *) calloc (24, 1); - strncpy (buf, g->master, 23); - 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, master); - else - push_str (st->stack, C_CONSTSTR, "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 *) calloc (24, 1); - strncpy (buf, sd->status.name, 23); - push_str (st->stack, C_STR, buf); - } - if (num == 1) - { - char *buf; - buf = buildin_getpartyname_sub (sd->status.party_id); - if (buf != 0) - push_str (st->stack, C_STR, buf); - else - push_str (st->stack, C_CONSTSTR, ""); - } - if (num == 2) - { - char *buf; - buf = buildin_getguildname_sub (sd->status.guild_id); - if (buf != 0) - push_str (st->stack, C_STR, buf); - else - push_str (st->stack, C_CONSTSTR, ""); - } - - 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) - { - printf ("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 *) calloc (64, 1); - 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, 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].broken == 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].broken == 1) - { - repaircounter++; - if (num == repaircounter) - { - sd->status.inventory[i].broken = 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); - 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] - && (num != 1 || sd->inventory_data[i]->def > 1 - || (sd->inventory_data[i]->def == 1 - && sd->inventory_data[i]->equip_script == NULL) - || (sd->inventory_data[i]->def <= 0 - && sd->inventory_data[i]->equip_script != NULL))) - { - 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) - push_val (st->stack, C_INT, - pc_percentrefinery (sd, &sd->status.inventory[i])); - 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; - - sd->status.inventory[i].refine++; - pc_unequipitem (sd, i, 0); - clif_refine (sd->fd, sd, 0, i, sd->status.inventory[i].refine); - clif_delitem (sd, i, 1); - clif_additem (sd, i, 1, 0); - pc_equipitem (sd, i, ep); - clif_misceffect (&sd->bl, 3); - } - - 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) - { - sd->status.inventory[i].refine = 0; - pc_unequipitem (sd, i, 0); - // 精錬失敗エフェクトのパケット - 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_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); - clif_skillinfoblock (sd); - - return 0; -} - -/*========================================== - * [Fate] Sets the skill level permanently - *------------------------------------------ - */ -int buildin_setskill (struct script_state *st) -{ - int id, level; - 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])); - sd = script_rid2sd (st); - - sd->status.skill[id].id = level ? id : 0; - sd->status.skill[id].lv = level; - clif_skillinfoblock (sd); - return 0; -} - -/*========================================== - * ギルドスキル取得 - *------------------------------------------ - */ -int buildin_guildskill (struct script_state *st) -{ - int id, level; - 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); - - 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_KAFRACONTACT - * 10002 : GD_GUARDIANRESEARCH - * 10003 : GD_CHARISMA - * 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; -} - -/*========================================== - * [Freeyorp] Return the current opt2 - *------------------------------------------ - */ - -int buildin_getopt2 (struct script_state *st) -{ - struct map_session_data *sd; - - sd = script_rid2sd (st); - - push_val (st->stack, C_INT, sd->opt2); - - return 0; -} - -/*========================================== - * [Freeyorp] Sets opt2 - *------------------------------------------ - */ - -int buildin_setopt2 (struct script_state *st) -{ - int new_opt2; - struct map_session_data *sd; - - new_opt2 = conv_num (st, &(st->stack->stack_data[st->start + 2])); - sd = script_rid2sd (st); - if (new_opt2 == sd->opt2) - return 0; - sd->opt2 = new_opt2; - clif_changeoption (&sd->bl); - pc_calcstatus (sd, 0); - - 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_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; - 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])); - pc_setsavepoint (script_rid2sd (st), str, x, y); - return 0; -} - -/*========================================== - * gettimetick(type) - * - * type The type of time measurement. - * Specify 0 for the system tick, 1 for - * seconds elapsed today, or 2 for seconds - * since Unix epoch. Defaults to 0 for any - * other value. - *------------------------------------------ - */ -int buildin_gettimetick (struct script_state *st) /* Asgard Version */ -{ - int type; - type = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - switch (type) - { - /* Number of seconds elapsed today (0-86399, 00:00:00-23:59:59). */ - case 1: - { - time_t timer; - struct tm *t; - - time (&timer); - t = gmtime (&timer); - push_val (st->stack, C_INT, - ((t->tm_hour) * 3600 + (t->tm_min) * 60 + t->tm_sec)); - break; - } - /* Seconds since Unix epoch. */ - case 2: - push_val (st->stack, C_INT, (int) time (NULL)); - break; - /* System tick (unsigned int, and yes, it will wrap). */ - case 0: - default: - 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 = gmtime (&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 *) calloc (maxlen + 1, 1); - strftime (tmpstr, maxlen, fmtstr, gmtime (&now)); - tmpstr[maxlen] = '\0'; - - push_str (st->stack, C_STR, tmpstr); - return 0; -} - -/*========================================== - * カプラ倉庫を開く - *------------------------------------------ - */ -int buildin_openstorage (struct script_state *st) -{ -// int sync = 0; -// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2])); - struct map_session_data *sd = script_rid2sd (st); - -// if (sync) { - st->state = STOP; - sd->npc_flags.storage = 1; -// } else st->state = END; - - storage_storageopen (sd); - return 0; -} - -int buildin_guildopenstorage (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int ret; - st->state = STOP; - 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; -} - -/*========================================== - * 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_reason (sd, base, job, PC_GAINEXP_REASON_SCRIPT); - - return 0; -} - -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -int buildin_monster (struct script_state *st) -{ - int mob_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])); - mob_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])); - - mob_once_spawn (map_id2sd (st->rid), map, x, y, str, mob_class, amount, - event); - return 0; -} - -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -int buildin_areamonster (struct script_state *st) -{ - int mob_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])); - mob_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, mob_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_foreachinarea (buildin_killmonster_sub, - m, 0, 0, map[m].xs, map[m].ys, 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_foreachinarea (buildin_killmonsterall_sub, - m, 0, 0, map[m].xs, map[m].ys, BL_MOB); - 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); - 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); - 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; -} - -/*========================================== - * 天の声アナウンス - *------------------------------------------ - */ -int buildin_announce (struct script_state *st) -{ - char *str; - 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 (flag & 0x0f) - { - struct block_list *bl = (flag & 0x08) ? map_id2bl (st->oid) : - (struct block_list *) script_rid2sd (st); - clif_GMmessage (bl, str, strlen (str) + 1, flag); - } - else - intif_GMmessage (str, strlen (str) + 1, flag); - return 0; -} - -/*========================================== - * 天の声アナウンス(特定マップ) - *------------------------------------------ - */ -int buildin_mapannounce_sub (struct block_list *bl, va_list ap) -{ - char *str; - int len, flag; - str = va_arg (ap, char *); - len = va_arg (ap, int); - flag = va_arg (ap, int); - clif_GMmessage (bl, str, len, flag | 3); - return 0; -} - -int buildin_mapannounce (struct script_state *st) -{ - char *mapname, *str; - 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 ((m = map_mapname2mapid (mapname)) < 0) - return 0; - map_foreachinarea (buildin_mapannounce_sub, - m, 0, 0, map[m].xs, map[m].ys, BL_PC, str, - strlen (str) + 1, flag & 0x10); - return 0; -} - -/*========================================== - * 天の声アナウンス(特定エリア) - *------------------------------------------ - */ -int buildin_areaannounce (struct script_state *st) -{ - char *map, *str; - 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 ((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); - 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; -} - -/*========================================== - * マップ指定ユーザー数所得 - *------------------------------------------ - */ -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_living_sub (struct block_list *bl, va_list ap) -{ - int *users = va_arg (ap, int *); - if (!pc_isdead((struct map_session_data *)bl)) - (*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])); - - int living = 0; - if (st->end > st->start + 7) - { - living = conv_num (st, &(st->stack->stack_data[st->start + 7])); - } - if ((m = map_mapname2mapid (str)) < 0) - { - push_val (st->stack, C_INT, -1); - return 0; - } - map_foreachinarea (living ? buildin_getareausers_living_sub: 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_sub_anddelete (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; - clif_clearflooritem(drop, 0); - map_delobject(drop->bl.id, drop->bl.type); - } - return 0; -} - -int buildin_getareadropitem (struct script_state *st) -{ - char *str; - int m, x0, y0, x1, y1, item, amount = 0, delitems = 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 (st->end > st->start + 8) - delitems = conv_num (st, &(st->stack->stack_data[st->start + 8])); - - if ((m = map_mapname2mapid (str)) < 0) - { - push_val (st->stack, C_INT, -1); - return 0; - } - if (delitems) - map_foreachinarea (buildin_getareadropitem_sub_anddelete, - m, x0, y0, x1, y1, BL_ITEM, item, &amount); - else - 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; - 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 (bl->type == BL_PC - && ((struct map_session_data *) bl)->state.potionpitcher_flag) - bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); - skill_status_change_start (bl, type, val1, 0, 0, 0, tick, 0); - return 0; -} - -/*========================================== - * 状態異常にかかる(確率指定) - *------------------------------------------ - */ -int buildin_sc_start2 (struct script_state *st) -{ - struct block_list *bl; - int type, tick, val1, 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 (bl->type == BL_PC - && ((struct map_session_data *) bl)->state.potionpitcher_flag) - bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); - if (MRAND (10000) < per) - skill_status_change_start (bl, type, val1, 0, 0, 0, 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 (bl->type == BL_PC - && ((struct map_session_data *) bl)->state.potionpitcher_flag) - bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); - skill_status_change_end (bl, type, -1); -// if(battle_config.etc_log) -// printf("sc_end : %d %d\n",st->rid,type); - return 0; -} - -int buildin_sc_check (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 (bl->type == BL_PC - && ((struct map_session_data *) bl)->state.potionpitcher_flag) - bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); - - push_val (st->stack, C_INT, skill_status_change_active (bl, type)); - - return 0; -} - -/*========================================== - * 状態異常耐性を計算した確率を返す - *------------------------------------------ - */ -int buildin_getscrate (struct script_state *st) -{ - struct block_list *bl; - int sc_def = 100, sc_def_mdef2, sc_def_vit2, sc_def_int2, sc_def_luk2; - int type, rate, luk; - - 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); - - luk = battle_get_luk (bl); - sc_def_mdef2 = 100 - (3 + battle_get_mdef (bl) + luk / 3); - sc_def_vit2 = 100 - (3 + battle_get_vit (bl) + luk / 3); - sc_def_int2 = 100 - (3 + battle_get_int (bl) + luk / 3); - sc_def_luk2 = 100 - (3 + luk); - - if (type == SC_STONE || type == SC_FREEZE) - sc_def = sc_def_mdef2; - else if (type == SC_STAN || type == SC_POISON || type == SC_SILENCE) - sc_def = sc_def_vit2; - else if (type == SC_SLEEP || type == SC_CONFUSION || type == SC_BLIND) - sc_def = sc_def_int2; - else if (type == SC_CURSE) - sc_def = sc_def_luk2; - - 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])); - printf ("script debug : %d %d : %s\n", st->rid, st->oid, - st->stack->stack_data[st->start + 2].u.str); - 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; -} - -/*========================================== - * スキルリセット - *------------------------------------------ - */ -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 == 22 && !battle_config.wedding_modifydisplay) - 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->status.pc_class == 20 || sd->status.pc_class == 4021) - sd->status.pc_class -= 1; - } - else if (sd->status.sex == 1) - { - sd->status.sex = 0; - sd->sex = 0; - if (sd->status.pc_class == 19 || sd->status.pc_class == 4020) - sd->status.pc_class += 1; - } - chrif_char_ask_name (-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex - chrif_save (sd); - 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, strlen (name) + 1, ev); - 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, cd->title); - return 0; - case 5: - push_str (st->stack, C_CONSTSTR, cd->pass); - return 0; - case 16: - push_str (st->stack, C_CONSTSTR, 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 ("$@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, str, x, y, 0); - } - mapreg_setreg (add_str ("$@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 -}; - -int buildin_setmapflagnosave (struct script_state *st) -{ - int m, x, y; - 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); - if (m >= 0) - { - map[m].flag.nosave = 1; - memcpy (map[m].save.map, str2, 16); - 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_PVP_NOPARTY: - map[m].flag.pvp_noparty = 1; - break; - case MF_PVP_NOGUILD: - map[m].flag.pvp_noguild = 1; - break; - case MF_GVG_NOPARTY: - map[m].flag.gvg_noparty = 1; - break; - case MF_NOZENYPENALTY: - map[m].flag.nozenypenalty = 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_FOG: // [Valaris] - map[m].flag.fog = 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; - } - } - - 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_NOPARTY: - map[m].flag.pvp_noparty = 0; - break; - case MF_PVP_NOGUILD: - map[m].flag.pvp_noguild = 0; - break; - case MF_GVG_NOPARTY: - map[m].flag.gvg_noparty = 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_FOG: // [Valaris] - map[m].flag.fog = 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; - - } - } - - return 0; -} - -int buildin_pvpon (struct script_state *st) -{ - int m, i; - char *str; - struct map_session_data *pl_sd = NULL; - - 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; - - 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 (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; - } - } - } - } - - return 0; -} - -int buildin_pvpoff (struct script_state *st) -{ - int m, i; - char *str; - struct map_session_data *pl_sd = NULL; - - 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 = 0; - clif_send0199 (m, 0); - - if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] - return 0; - - 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 (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; -} - -/*========================================== - * NPCエモーション - *------------------------------------------ - */ - -int buildin_emotion (struct script_state *st) -{ - int type; - type = conv_num (st, &(st->stack->stack_data[st->start + 2])); - if (type < 0 || type > 100) - return 0; - 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->mob_class < 1285 || md->mob_class > 1288) - 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_foreachinarea (buildin_maprespawnguildid_sub, m, 0, 0, - map[m].xs - 1, map[m].ys - 1, BL_NUL, g_id, flag); - return 0; -} - -int buildin_agitstart (struct script_state *st) -{ - if (agit_flag == 1) - return 1; // Agit already Start. - agit_flag = 1; - guild_agit_start (); - return 0; -} - -int buildin_agitend (struct script_state *st) -{ - if (agit_flag == 0) - return 1; // 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; - - sd = script_rid2sd (st); - 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 - { - if (agit_flag == 1) - pc_setreg (sd, add_str ("@agit_flag"), 1); - if (agit_flag == 0) - pc_setreg (sd, add_str ("@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 1; -} - -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 *) calloc (24, 1); - strncpy (buf, gc->castle_name, 23); - break; - } - } - } - if (buf) - push_str (st->stack, C_STR, buf); - else - push_str (st->stack, C_CONSTSTR, ""); - 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: - push_val (st->stack, C_INT, gc->visibleG0); - break; - case 11: - push_val (st->stack, C_INT, gc->visibleG1); - break; - case 12: - push_val (st->stack, C_INT, gc->visibleG2); - break; - case 13: - push_val (st->stack, C_INT, gc->visibleG3); - break; - case 14: - push_val (st->stack, C_INT, gc->visibleG4); - break; - case 15: - push_val (st->stack, C_INT, gc->visibleG5); - break; - case 16: - push_val (st->stack, C_INT, gc->visibleG6); - break; - case 17: - push_val (st->stack, C_INT, gc->visibleG7); - break; - case 18: - push_val (st->stack, C_INT, gc->Ghp0); - break; - case 19: - push_val (st->stack, C_INT, gc->Ghp1); - break; - case 20: - push_val (st->stack, C_INT, gc->Ghp2); - break; - case 21: - push_val (st->stack, C_INT, gc->Ghp3); - break; - case 22: - push_val (st->stack, C_INT, gc->Ghp4); - break; - case 23: - push_val (st->stack, C_INT, gc->Ghp5); - break; - case 24: - push_val (st->stack, C_INT, gc->Ghp6); - break; - case 25: - push_val (st->stack, C_INT, gc->Ghp7); - 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: - gc->visibleG0 = value; - break; - case 11: - gc->visibleG1 = value; - break; - case 12: - gc->visibleG2 = value; - break; - case 13: - gc->visibleG3 = value; - break; - case 14: - gc->visibleG4 = value; - break; - case 15: - gc->visibleG5 = value; - break; - case 16: - gc->visibleG6 = value; - break; - case 17: - gc->visibleG7 = value; - break; - case 18: - gc->Ghp0 = value; - break; - case 19: - gc->Ghp1 = value; - break; - case 20: - gc->Ghp2 = value; - break; - case 21: - gc->Ghp3 = value; - break; - case 22: - gc->Ghp4 = value; - break; - case 23: - gc->Ghp5 = value; - break; - case 24: - gc->Ghp6 = value; - break; - case 25: - gc->Ghp7 = 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 = 4; - - 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)) - { - - 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, num, cardflag = 0, flag; - struct map_session_data *sd; - struct item item_tmp; - int c = 4; - - 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)) - { - - 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; - item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = - 0, item_tmp.card[3] = 0; - - 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; - item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = - 0, item_tmp.card[3] = 0; - pc_delitem (sd, i, 1, 0); - 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, num, cardflag = 0, flag, typefail; - struct map_session_data *sd; - struct item item_tmp; - int c = 4; - - 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)) - { - - 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; - item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = - 0, item_tmp.card[3] = 0; - 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) - { // 武具損失 - 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; - item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = - 0, item_tmp.card[3] = 0; - pc_delitem (sd, i, 1, 0); - 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, map_id2sd (st->rid), 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, map_id2sd (st->rid), 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_foreachinarea (buildin_mobcount_sub, - m, 0, 0, map[m].xs, map[m].ys, BL_MOB, event, &c); - - push_val (st->stack, C_INT, (c - 1)); - - 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); - - if (sd == NULL) - return 0; - clif_wedding_effect (&sd->bl); - return 0; -} - -int buildin_divorce (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - - st->state = STOP; // rely on pc_divorce to restart - - sd->npc_flags.divorce = 1; - - 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; -} - -/*================================================ - * 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 mob_class = conv_num (st, &(st->stack->stack_data[st->start + 3])); - - if (num <= 0 || num >= 8 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) - return 0; - - if (num == 1) - { - char *buf; - buf = mob_db[mob_class].name; - push_str (st->stack, C_STR, buf); - return 0; - } - else if (num == 2) - { - char *buf; - buf = mob_db[mob_class].jname; - push_str (st->stack, C_STR, buf); - return 0; - } - else if (num == 3) - push_val (st->stack, C_INT, mob_db[mob_class].lv); - else if (num == 4) - push_val (st->stack, C_INT, mob_db[mob_class].max_hp); - else if (num == 5) - push_val (st->stack, C_INT, mob_db[mob_class].max_sp); - else if (num == 6) - push_val (st->stack, C_INT, mob_db[mob_class].base_exp); - else if (num == 7) - push_val (st->stack, C_INT, mob_db[mob_class].job_exp); - return 0; -} - -/*========================================== - * Summon guardians [Valaris] - *------------------------------------------ - */ -int buildin_guardian (struct script_state *st) -{ - int mob_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])); - mob_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, mob_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 && gc->visibleG0 == 1) - push_val (st->stack, C_INT, gc->Ghp0); - if (guardian == 1 && gc->visibleG1 == 1) - push_val (st->stack, C_INT, gc->Ghp1); - if (guardian == 2 && gc->visibleG2 == 1) - push_val (st->stack, C_INT, gc->Ghp2); - if (guardian == 3 && gc->visibleG3 == 1) - push_val (st->stack, C_INT, gc->Ghp3); - if (guardian == 4 && gc->visibleG4 == 1) - push_val (st->stack, C_INT, gc->Ghp4); - if (guardian == 5 && gc->visibleG5 == 1) - push_val (st->stack, C_INT, gc->Ghp5); - if (guardian == 6 && gc->visibleG6 == 1) - push_val (st->stack, C_INT, gc->Ghp6); - if (guardian == 7 && gc->visibleG7 == 1) - push_val (st->stack, C_INT, gc->Ghp7); - else - push_val (st->stack, C_INT, -1); - - return 0; -} - -/*========================================== - * IDからItem名 - *------------------------------------------ - */ -int buildin_getitemname (struct script_state *st) -{ - 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); - i_data = itemdb_searchname (name); - } - else - { - int item_id = conv_num (st, data); - i_data = itemdb_search (item_id); - } - - item_name = (char *) calloc (24, 1); - if (i_data) - strncpy (item_name, i_data->jname, 23); - else - strncpy (item_name, "Unknown Item", 23); - - push_str (st->stack, C_STR, item_name); - - return 0; -} - -int buildin_getspellinvocation (struct script_state *st) -{ - char *name; - char *invocation; - - name = conv_str (st, &(st->stack->stack_data[st->start + 2])); - - invocation = magic_find_invocation (name); - if (!invocation) - invocation = "..."; - - push_str (st->stack, C_STR, strdup (invocation)); - return 0; -} - -int buildin_getanchorinvocation (struct script_state *st) -{ - char *name; - char *invocation; - - name = conv_str (st, &(st->stack->stack_data[st->start + 2])); - - invocation = magic_find_anchor_invocation (name); - if (!invocation) - invocation = "..."; - - push_str (st->stack, C_STR, strdup (invocation)); - return 0; -} - -int buildin_getpartnerid (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - - push_val (st->stack, C_INT, sd->status.partner_id); - return 0; -} - -/*========================================== - * PCの所持品情報読み取り - *------------------------------------------ - */ -int buildin_getinventorylist (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_INVENTORY; i++) - { - if (sd->status.inventory[i].nameid > 0 - && sd->status.inventory[i].amount > 0) - { - pc_setreg (sd, add_str ("@inventorylist_id") + (j << 24), - sd->status.inventory[i].nameid); - pc_setreg (sd, add_str ("@inventorylist_amount") + (j << 24), - sd->status.inventory[i].amount); - pc_setreg (sd, add_str ("@inventorylist_equip") + (j << 24), - sd->status.inventory[i].equip); - pc_setreg (sd, add_str ("@inventorylist_refine") + (j << 24), - sd->status.inventory[i].refine); - pc_setreg (sd, add_str ("@inventorylist_identify") + (j << 24), - sd->status.inventory[i].identify); - pc_setreg (sd, add_str ("@inventorylist_attribute") + (j << 24), - sd->status.inventory[i].attribute); - pc_setreg (sd, add_str ("@inventorylist_card1") + (j << 24), - sd->status.inventory[i].card[0]); - pc_setreg (sd, add_str ("@inventorylist_card2") + (j << 24), - sd->status.inventory[i].card[1]); - pc_setreg (sd, add_str ("@inventorylist_card3") + (j << 24), - sd->status.inventory[i].card[2]); - pc_setreg (sd, add_str ("@inventorylist_card4") + (j << 24), - sd->status.inventory[i].card[3]); - j++; - } - } - pc_setreg (sd, add_str ("@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 ("@skilllist_id") + (j << 24), - sd->status.skill[i].id); - pc_setreg (sd, add_str ("@skilllist_lv") + (j << 24), - sd->status.skill[i].lv); - pc_setreg (sd, add_str ("@skilllist_flag") + (j << 24), - sd->status.skill[i].flags); - j++; - } - } - pc_setreg (sd, add_str ("@skilllist_count"), j); - return 0; -} - -int buildin_get_activated_pool_skills (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int pool_skills[MAX_SKILL_POOL]; - int skill_pool_size = skill_pool (sd, pool_skills); - int i, count = 0; - - if (!sd) - return 0; - - for (i = 0; i < skill_pool_size; i++) - { - int skill_id = pool_skills[i]; - - if (sd->status.skill[skill_id].id == skill_id) - { - pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), - sd->status.skill[skill_id].id); - pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), - sd->status.skill[skill_id].lv); - pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), - sd->status.skill[skill_id].flags); - pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), - skill_name (skill_id)); - ++count; - } - } - pc_setreg (sd, add_str ("@skilllist_count"), count); - - return 0; -} - -extern int skill_pool_skills[]; -extern int skill_pool_skills_size; - -int buildin_get_unactivated_pool_skills (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int i, count = 0; - - if (!sd) - return 0; - - for (i = 0; i < skill_pool_skills_size; i++) - { - int skill_id = skill_pool_skills[i]; - - if (sd->status.skill[skill_id].id == skill_id && !(sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED)) - { - pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), - sd->status.skill[skill_id].id); - pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), - sd->status.skill[skill_id].lv); - pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), - sd->status.skill[skill_id].flags); - pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), - skill_name (skill_id)); - ++count; - } - } - pc_setreg (sd, add_str ("@skilllist_count"), count); - - return 0; -} - -int buildin_get_pool_skills (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int i, count = 0; - - if (!sd) - return 0; - - for (i = 0; i < skill_pool_skills_size; i++) - { - int skill_id = skill_pool_skills[i]; - - if (sd->status.skill[skill_id].id == skill_id) - { - pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), - sd->status.skill[skill_id].id); - pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), - sd->status.skill[skill_id].lv); - pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), - sd->status.skill[skill_id].flags); - pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), - skill_name (skill_id)); - ++count; - } - } - pc_setreg (sd, add_str ("@skilllist_count"), count); - - return 0; -} - -int buildin_activate_pool_skill (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - skill_pool_activate (sd, skill_id); - clif_skillinfoblock (sd); - - return 0; -} - -int buildin_deactivate_pool_skill (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - skill_pool_deactivate (sd, skill_id); - clif_skillinfoblock (sd); - - return 0; -} - -int buildin_check_pool_skill (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - push_val (st->stack, C_INT, skill_pool_is_activated (sd, skill_id)); - - 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) - pc_delitem (sd, i, sd->status.inventory[i].amount, 0); - } - return 0; -} - -/*========================================== - * NPCクラスチェンジ - * classは変わりたいclass - * typeは通常0なのかな? - *------------------------------------------ - */ -int buildin_classchange (struct script_state *st) -{ - int npc_class, type; - struct block_list *bl = map_id2bl (st->oid); - - if (bl == NULL) - return 0; - - npc_class = conv_num (st, &(st->stack->stack_data[st->start + 2])); - type = conv_num (st, &(st->stack->stack_data[st->start + 3])); - clif_npc_class_change (bl, npc_class, type); - return 0; -} - -/*========================================== - * NPCから発生するエフェクト - * misceffect(effect, [target]) - * - * effect The effect type/ID. - * target The player name or being ID on - * which to display the effect. If not - * specified, it attempts to default to - * the current NPC or invoking PC. - *------------------------------------------ - */ -int buildin_misceffect (struct script_state *st) -{ - int type; - int id = 0; - char *name = NULL; - struct block_list *bl = NULL; - - type = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - if (st->end > st->start + 3) - { - struct script_data *sdata = &(st->stack->stack_data[st->start + 3]); - - get_val (st, sdata); - - if (sdata->type == C_STR || sdata->type == C_CONSTSTR) - name = conv_str (st, sdata); - else - id = conv_num (st, sdata); - } - - if (name) - { - struct map_session_data *sd = map_nick2sd (name); - if (sd) - bl = &sd->bl; - } - else if (id) - bl = map_id2bl (id); - else if (st->oid) - bl = map_id2bl (st->oid); - else - { - struct map_session_data *sd = script_rid2sd (st); - if (sd) - bl = &sd->bl; - } - - if (bl) - clif_misceffect (bl, type); - - return 0; -} - -/*========================================== - * サウンドエフェクト - *------------------------------------------ - */ -int buildin_soundeffect (struct script_state *st) -{ - 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; -} - -/*========================================== - * 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; - - if (sd == NULL) - return 0; - - for (i = 0; i < 11; i++) - if (sd->equip_index[i] >= 0) - pc_unequipitem (sd, sd->equip_index[i], i); - pc_calcstatus (sd, 0); - - return 0; -} - -/*========================================== - * UnequipById [Freeyorp] - *------------------------------------------ - */ - -int buildin_unequip_by_id (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - if (sd == NULL) - return 0; - - int slot_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); - - if (slot_id >= 0 && slot_id < 11 && sd->equip_index[slot_id] >= 0) - pc_unequipitem (sd, sd->equip_index[slot_id], slot_id); - - pc_calcstatus (sd, 0); - - return 0; -} - -/*========================================== - * gmcommand [MouseJstr] - * - * suggested on the forums... - *------------------------------------------ - */ - -int buildin_gmcommand (struct script_state *st) -{ - struct map_session_data *sd; - char *cmd; - - sd = script_rid2sd (st); - cmd = conv_str (st, &(st->stack->stack_data[st->start + 2])); - - is_atcommand (sd->fd, sd, cmd, 99); - - 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; -} - -/*========================================== - * npcwarp [remoitnane] - * Move NPC to a new position on the same map. - *------------------------------------------ - */ -int buildin_npcwarp (struct script_state *st) -{ - int x, y; - char *npc; - struct npc_data *nd = NULL; - - x = conv_num (st, &(st->stack->stack_data[st->start + 2])); - y = conv_num (st, &(st->stack->stack_data[st->start + 3])); - npc = conv_str (st, &(st->stack->stack_data[st->start + 4])); - nd = npc_name2id (npc); - - if (!nd) - return -1; - - short m = nd->bl.m; - - /* Crude sanity checks. */ - if (m < 0 || !nd->bl.prev - || x < 0 || x > map[m].xs -1 - || y < 0 || y > map[m].ys - 1) - return -1; - - npc_enable (npc, 0); - map_delblock(&nd->bl); /* [Freeyorp] */ - nd->bl.x = x; - nd->bl.y = y; - map_addblock(&nd->bl); - npc_enable (npc, 1); - - return 0; -} - -/*========================================== - * message [MouseJstr] - *------------------------------------------ - */ - -int buildin_message (struct script_state *st) -{ - char *msg, *player; - struct map_session_data *pl_sd = NULL; - - 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 1; - 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, 24); - strcat (message, " : "); - strcat (message, str); - 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) - { - push_val (st->stack, C_INT, 1); - return 0; - } - } - - push_val (st->stack, C_INT, 0); - - 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*)calloc (24, 1); - strncpy (mapname, sd->status.save_point.map, 23); - push_str (st->stack, C_STR, mapname); - break; - case 1: - push_val (st->stack, C_INT, x); - break; - case 2: - push_val (st->stack, C_INT, y); - break; - } - return 0; -} - -/*========================================== - * areatimer - *------------------------------------------ - */ -int buildin_areatimer_sub (struct block_list *bl, va_list ap) -{ - int tick; - char *event; - tick = va_arg (ap, int); - event = va_arg (ap, char *); - pc_addeventtimer ((struct map_session_data *) bl, tick, event); - return 0; -} - -int buildin_areatimer (struct script_state *st) -{ - int tick, m; - char *event; - 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])); - tick = conv_num (st, &(st->stack->stack_data[st->start + 7])); - event = conv_str (st, &(st->stack->stack_data[st->start + 8])); - - if ((m = map_mapname2mapid (mapname)) < 0) - return 0; - - map_foreachinarea (buildin_areatimer_sub, - m, x0, y0, x1, y1, BL_PC, tick, event); - return 0; -} - -/*========================================== - * Check whether the PC is in the specified rectangle - *------------------------------------------ - */ -int buildin_isin (struct script_state *st) -{ - int x1, y1, x2, y2; - char *str; - struct map_session_data *sd = script_rid2sd (st); - - str = conv_str (st, &(st->stack->stack_data[st->start + 2])); - x1 = conv_num (st, &(st->stack->stack_data[st->start + 3])); - y1 = conv_num (st, &(st->stack->stack_data[st->start + 4])); - x2 = conv_num (st, &(st->stack->stack_data[st->start + 5])); - y2 = conv_num (st, &(st->stack->stack_data[st->start + 6])); - - if (!sd) - return 1; - - push_val (st->stack, C_INT, - (sd->bl.x >= x1 && sd->bl.x <= x2) - && (sd->bl.y >= y1 && sd->bl.y <= y2) - && (!strcmp (str, map[sd->bl.m].name))); - - return 0; -} - -// Trigger the shop on a (hopefully) nearby shop NPC -int buildin_shop (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - struct npc_data *nd; - - if (!sd) - return 1; - - nd = npc_name2id (conv_str (st, &(st->stack->stack_data[st->start + 2]))); - if (!nd) - return 1; - - buildin_close (st); - clif_npcbuysell (sd, nd->bl.id); - return 0; -} - -/*========================================== - * Check whether the PC is dead - *------------------------------------------ - */ -int buildin_isdead (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - - push_val (st->stack, C_INT, pc_isdead (sd)); - return 0; -} - -/*======================================== - * Changes a NPC name, and sprite - *---------------------------------------- - */ -int buildin_fakenpcname (struct script_state *st) -{ - char *name, *newname; - int newsprite; - struct npc_data *nd; - - name = conv_str (st, &(st->stack->stack_data[st->start + 2])); - newname = conv_str (st, &(st->stack->stack_data[st->start + 3])); - newsprite = conv_num (st, &(st->stack->stack_data[st->start + 4])); - nd = npc_name2id (name); - if (!nd) - return 1; - strncpy (nd->name, newname, sizeof(nd->name)-1); - nd->name[sizeof(nd->name)-1] = '\0'; - nd->npc_class = newsprite; - - // Refresh this npc - npc_enable (name, 0); - npc_enable (name, 1); - - return 0; -} - -/*============================ - * Gets the PC's x pos - *---------------------------- - */ - -int buildin_getx (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - - push_val (st->stack, C_INT, sd->bl.x); - return 0; -} - -/*============================ - * Gets the PC's y pos - *---------------------------- - */ -int buildin_gety (struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd (st); - - push_val (st->stack, C_INT, sd->bl.y); - return 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) - printf ("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 *) - calloc (strlen (st->stack->stack_data[st->stack->sp - 1].u.str) + - strlen (st->stack->stack_data[st->stack->sp].u.str) + 1, - 1); - 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) - free (st->stack->stack_data[st->stack->sp - 1].u.str); - if (st->stack->stack_data[st->stack->sp].type == C_STR) - free (st->stack->stack_data[st->stack->sp].u.str); - 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: - printf ("illegal string operater\n"); - break; - } - - push_val (st->stack, C_INT, a); - - if (st->stack->stack_data[sp1].type == C_STR) - free (s1); - if (st->stack->stack_data[sp2].type == C_STR) - free (s2); -} - -/*========================================== - * 二項演算子(数値) - *------------------------------------------ - */ -void op_2num (struct script_state *st, int op, int i1, int i2) -{ - switch (op) - { - case C_SUB: - i1 -= i2; - break; - case C_MUL: - i1 *= i2; - break; - case C_DIV: - i1 /= i2; - break; - case C_MOD: - i1 %= i2; - 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 - printf ("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) - printf ("function not found\n"); -// st->stack->sp=0; - st->state = END; - return 0; - } - 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) - { - printf ("run_func: not function and command! \n"); -// st->stack->sp=0; - st->state = END; - return 0; - } -#ifdef DEBUG_RUN - if (battle_config.etc_log) - { - printf ("run_func : %s? (%d(%d))\n", str_buf + str_data[func].str, - func, str_data[func].type); - printf ("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; - 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) - { - str_data[func].func (st); - } - else - { - if (battle_config.error_log) - printf ("run_func : %s? (%d(%d))\n", str_buf + str_data[func].str, - func, str_data[func].type); - push_val (st->stack, C_INT, 0); - } - - pop_stack (st->stack, start_sp, end_sp); - - if (st->state == RETFUNC) - { - // ユーザー定義関数からの復帰 - int olddefsp = st->defsp; - int i; - - pop_stack (st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除 - if (st->defsp < 4 - || st->stack->stack_data[st->defsp - 1].type != C_RETINFO) - { - printf - ("script:run_func(return) return without callfunc or callsub!\n"); - st->state = END; - return 0; - } - i = conv_num (st, &(st->stack->stack_data[st->defsp - 4])); // 引数の数所得 - st->pos = conv_num (st, &(st->stack->stack_data[st->defsp - 1])); // スクリプト位置の復元 - st->script = (char *) conv_num (st, &(st->stack->stack_data[st->defsp - 2])); // スクリプトを復元 - st->defsp = conv_num (st, &(st->stack->stack_data[st->defsp - 3])); // 基準スタックポインタを復元 - - pop_stack (st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 - - st->state = GOTO; - } - - return 0; -} - -/*========================================== - * スクリプトの実行メイン部分 - *------------------------------------------ - */ -int run_script_main (unsigned char *script, int pos, int rid, int oid, - struct script_state *st, unsigned char *rootscript) -{ - int c, rerun_pos; - int cmdcount = script_config.check_cmdcount; - int gotocount = script_config.check_gotocount; - struct script_stack *stack = st->stack; - - st->defsp = stack->sp; - st->script = script; - - rerun_pos = st->pos; - for (st->state = 0; st->state == 0;) - { - switch (c = get_com (script, &st->pos)) - { - case C_EOL: - if (stack->sp != st->defsp) - { - if (battle_config.error_log) - printf ("stack.sp(%d) != default(%d)\n", stack->sp, - st->defsp); - stack->sp = st->defsp; - } - rerun_pos = st->pos; - break; - case C_INT: - push_val (stack, C_INT, get_num (script, &st->pos)); - break; - case C_POS: - case C_NAME: - push_val (stack, c, (*(int *) (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, script + st->pos); - while (script[st->pos++]); - break; - case C_FUNC: - run_func (st); - if (st->state == GOTO) - { - rerun_pos = st->pos; - script = st->script; - st->state = 0; - if (gotocount > 0 && (--gotocount) <= 0) - { - printf ("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) - printf ("unknown command : %d @ %d\n", c, pos); - st->state = END; - break; - } - if (cmdcount > 0 && (--cmdcount) <= 0) - { - printf ("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: - { - st->pos = rerun_pos; - } - break; - } - - if (st->state != END) - { - // 再開するためにスタック情報を保存 - struct map_session_data *sd = map_id2sd (st->rid); - if (sd /* && sd->npc_stackbuf==NULL */ ) - { - if (sd->npc_stackbuf) - free (sd->npc_stackbuf); - sd->npc_stackbuf = (char *) - calloc (sizeof (stack->stack_data[0]) * stack->sp_max, 1); - memcpy (sd->npc_stackbuf, stack->stack_data, - sizeof (stack->stack_data[0]) * stack->sp_max); - sd->npc_stack = stack->sp; - sd->npc_stackmax = stack->sp_max; - sd->npc_script = script; - sd->npc_scriptroot = rootscript; - } - } - - return 0; -} - -/*========================================== - * スクリプトの実行 - *------------------------------------------ - */ -int run_script (unsigned char *script, int pos, int rid, int oid) -{ - return run_script_l (script, pos, rid, oid, 0, NULL); -} - -int run_script_l (unsigned char *script, int pos, int rid, int oid, - int args_nr, argrec_t * args) -{ - struct script_stack stack; - struct script_state st; - struct map_session_data *sd = map_id2sd (rid); - unsigned char *rootscript = script; - int i; - if (script == NULL || pos < 0) - return -1; - - if (sd && sd->npc_stackbuf && sd->npc_scriptroot == (char *) rootscript) - { - // 前回のスタックを復帰 - script = sd->npc_script; - stack.sp = sd->npc_stack; - stack.sp_max = sd->npc_stackmax; - stack.stack_data = (struct script_data *) - calloc (stack.sp_max, sizeof (stack.stack_data[0])); - memcpy (stack.stack_data, sd->npc_stackbuf, - sizeof (stack.stack_data[0]) * stack.sp_max); - free (sd->npc_stackbuf); - sd->npc_stackbuf = NULL; - } - else - { - // スタック初期化 - stack.sp = 0; - stack.sp_max = 64; - stack.stack_data = (struct script_data *) - calloc (stack.sp_max, sizeof (stack.stack_data[0])); - } - st.stack = &stack; - st.pos = pos; - st.rid = rid; - st.oid = oid; - for (i = 0; i < args_nr; i++) - { - if (args[i].name[strlen (args[i].name) - 1] == '$') - pc_setregstr (sd, add_str (args[i].name), args[i].v.s); - else - pc_setreg (sd, add_str (args[i].name), args[i].v.i); - } - run_script_main (script, pos, rid, oid, &st, rootscript); - - free (stack.stack_data); - stack.stack_data = NULL; - return st.pos; -} - -/*========================================== - * マップ変数の変更 - *------------------------------------------ - */ -int mapreg_setreg (int num, int val) -{ - if (val != 0) - numdb_insert (mapreg_db, num, val); - else - numdb_erase (mapreg_db, num); - - mapreg_dirty = 1; - return 0; -} - -/*========================================== - * 文字列型マップ変数の変更 - *------------------------------------------ - */ -int mapreg_setregstr (int num, const char *str) -{ - char *p; - - if ((p = (char *)numdb_search (mapregstr_db, num)) != NULL) - free (p); - - if (str == NULL || *str == 0) - { - numdb_erase (mapregstr_db, num); - mapreg_dirty = 1; - return 0; - } - p = (char *) calloc (strlen (str) + 1, 1); - strcpy (p, str); - numdb_insert (mapregstr_db, num, p); - mapreg_dirty = 1; - return 0; -} - -/*========================================== - * 永続的マップ変数の読み込み - *------------------------------------------ - */ -static int script_load_mapreg (void) -{ - 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) - { - printf ("%s: %s broken data !\n", mapreg_txt, buf1); - continue; - } - p = (char *) calloc (strlen (buf2) + 1, 1); - strcpy (p, buf2); - s = add_str (buf1); - numdb_insert (mapregstr_db, (i << 24) | s, p); - } - else - { - if (sscanf (line + n, "%d", &v) != 1) - { - printf ("%s: %s broken data !\n", mapreg_txt, buf1); - continue; - } - s = add_str (buf1); - numdb_insert (mapreg_db, (i << 24) | s, v); - } - } - fclose_ (fp); - mapreg_dirty = 0; - return 0; -} - -/*========================================== - * 永続的マップ変数の書き込み - *------------------------------------------ - */ -static void script_save_mapreg_intsub (db_key_t key, db_val_t data, va_list ap) -{ - 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); - } -} - -static void script_save_mapreg_strsub (db_key_t key, db_val_t data, va_list ap) -{ - 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); - } -} - -static int script_save_mapreg (void) -{ - FILE *fp; - int lock; - - if ((fp = lock_fopen (mapreg_txt, &lock)) == NULL) - return -1; - numdb_foreach (mapreg_db, script_save_mapreg_intsub, fp); - numdb_foreach (mapregstr_db, script_save_mapreg_strsub, fp); - lock_fclose (fp, mapreg_txt, &lock); - mapreg_dirty = 0; - return 0; -} - -static void script_autosave_mapreg (timer_id tid, tick_t tick, custom_id_t id, - custom_data_t data) -{ - if (mapreg_dirty) - script_save_mapreg (); -} - -/*========================================== - * - *------------------------------------------ - */ -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 (char *cfgName) -{ - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - 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 = 8192; - script_config.check_gotocount = 512; - - fp = fopen_ (cfgName, "r"); - if (fp == NULL) - { - printf ("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 (strcasecmp (w1, "refine_posword") == 0) - { - set_posword (w2); - } - if (strcasecmp (w1, "import") == 0) - { - script_config_read (w2); - } - } - fclose_ (fp); - - return 0; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ - -static void mapregstr_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free (data); -} - -static void userfunc_db_final (db_key_t key, db_val_t data, va_list ap) -{ - free ((char*)key.s); - free (data); -} - -int do_final_script (void) -{ - if (mapreg_dirty >= 0) - script_save_mapreg (); - if (script_buf) - free (script_buf); - - if (mapreg_db) - numdb_final (mapreg_db, NULL); - if (mapregstr_db) - strdb_final (mapregstr_db, mapregstr_db_final); - if (scriptlabel_db) - strdb_final (scriptlabel_db, NULL); - if (userfunc_db) - strdb_final (userfunc_db, userfunc_db_final); - - if (str_data) - free (str_data); - if (str_buf) - free (str_buf); - - return 0; -} - -/*========================================== - * 初期化 - *------------------------------------------ - */ -int do_init_script (void) -{ - mapreg_db = numdb_init (); - mapregstr_db = numdb_init (); - script_load_mapreg (); - - add_timer_interval (gettick () + MAPREG_AUTOSAVE_INTERVAL, - script_autosave_mapreg, 0, 0, - MAPREG_AUTOSAVE_INTERVAL); - - scriptlabel_db = strdb_init (50); - return 0; -} diff --git a/src/map/script.cpp b/src/map/script.cpp new file mode 100644 index 0000000..9e851bb --- /dev/null +++ b/src/map/script.cpp @@ -0,0 +1,8145 @@ +// $Id: script.c 148 2004-09-30 14:05:37Z MouseJstr $ +//#define DEBUG_FUNCIN +//#define DEBUG_DISP +//#define DEBUG_RUN + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#ifndef LCCWIN32 +#include <sys/time.h> +#endif + +#include <time.h> +#include <math.h> + +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/lock.hpp" +#include "../common/mt_rand.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "chat.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "../common/db.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "../common/lock.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "storage.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define SCRIPT_BLOCK_SIZE 256 +enum +{ LABEL_NEXTLINE = 1, LABEL_START }; +static unsigned char *script_buf; +static int script_pos, script_size; + +char *str_buf; +int str_pos, str_size; +static struct str_data_t +{ + int type; + int str; + int backpatch; + int label; + int (*func) (struct script_state *); + int val; + int next; +} *str_data; +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 (void) +{ + return scriptlabel_db; +} + +struct dbt *script_get_userfunc_db (void) +{ + if (!userfunc_db) + userfunc_db = strdb_init (50); + return userfunc_db; +} + +static char pos[11][100] = + { "頭", "体", "左手", "右手", "ローブ", "靴", "アクセサリー1", + "アクセサリー2", "頭2", "頭3", "装着していない" +}; + +static struct Script_Config +{ + int warn_func_no_comma; + int warn_cmd_no_comma; + int warn_func_mismatch_paramnum; + int warn_cmd_mismatch_paramnum; + int check_cmdcount; + int check_gotocount; +} script_config; +static int parse_cmd_if = 0; +static int parse_cmd; + +/*========================================== + * ローカルプロトタイプ宣言 (必要な物のみ) + *------------------------------------------ + */ +unsigned char *parse_subexpr (unsigned char *, int); +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_pow (struct script_state *st); +int buildin_warp (struct script_state *st); +int buildin_isat (struct script_state *st); +int buildin_areawarp (struct script_state *st); +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_if (struct script_state *st); +int buildin_getitem (struct script_state *st); +int buildin_getitem2 (struct script_state *st); +int buildin_makeitem (struct script_state *st); +int buildin_delitem (struct script_state *st); +int buildin_viewpoint (struct script_state *st); +int buildin_countitem (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_skill (struct script_state *st); +int buildin_setskill (struct script_state *st); +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_getopt2 (struct script_state *st); +int buildin_setopt2 (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_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_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_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_end (struct script_state *st); +int buildin_sc_check (struct script_state *st); // [Fate] +int buildin_getscrate (struct script_state *st); +int buildin_debugmes (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_getitemname (struct script_state *st); +int buildin_getspellinvocation (struct script_state *st); // [Fate] +int buildin_getanchorinvocation (struct script_state *st); // [Fate] +int buildin_getexp (struct script_state *st); +int buildin_getinventorylist (struct script_state *st); +int buildin_getskilllist (struct script_state *st); +int buildin_get_pool_skills (struct script_state *st); // [fate] +int buildin_get_activated_pool_skills (struct script_state *st); // [fate] +int buildin_get_unactivated_pool_skills (struct script_state *st); // [PO] +int buildin_activate_pool_skill (struct script_state *st); // [fate] +int buildin_deactivate_pool_skill (struct script_state *st); // [fate] +int buildin_check_pool_skill (struct script_state *st); // [fate] +int buildin_getskilllist (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_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_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_gmcommand (struct script_state *st); // [MouseJstr] +int buildin_movenpc (struct script_state *st); // [MouseJstr] +int buildin_npcwarp (struct script_state *st); // [remoitnane] +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_getpartnerid (struct script_state *st); // [Fate] +int buildin_areatimer (struct script_state *st); // [Jaxad0127] +int buildin_isin (struct script_state *st); // [Jaxad0127] +int buildin_shop (struct script_state *st); // [MadCamel] +int buildin_isdead (struct script_state *st); // [Jaxad0127] +int buildin_fakenpcname (struct script_state *st); //[Kage] +int buildin_unequip_by_id (struct script_state *st); // [Freeyorp] +int buildin_getx (struct script_state *st); // [Kage] +int buildin_gety (struct script_state *st); // [Kage] + + +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); + +struct +{ + int (*func) (struct script_state *); + char *name; + char *arg; +} buildin_func[] = +{ + { + buildin_mes, "mes", "s"}, + { + buildin_next, "next", ""}, + { + buildin_close, "close", ""}, + { + buildin_close2, "close2", ""}, + { + buildin_menu, "menu", "sL*"}, + { + buildin_goto, "goto", "L"}, + { + buildin_callsub, "callsub", "L*"}, + { + buildin_callfunc, "callfunc", "F*"}, + { + buildin_return, "return", "*"}, + { + buildin_getarg, "getarg", "i"}, + { + buildin_jobchange, "jobchange", "i*"}, + { + buildin_input, "input", "N"}, + { + buildin_warp, "warp", "Mxy"}, + { + buildin_isat, "isat", "Mxy"}, + { + buildin_areawarp, "areawarp", "MxyxyMxy"}, + { + buildin_setlook, "setlook", "ii"}, + { + buildin_set, "set", "Ne"}, + { + buildin_setarray, "setarray", "Ne*"}, + { + buildin_cleararray, "cleararray", "Nei"}, + { + buildin_copyarray, "copyarray", "NNi"}, + { + buildin_getarraysize, "getarraysize", "N"}, + { + buildin_deletearray, "deletearray", "N*"}, + { + buildin_getelementofarray, "getelementofarray", "Ni"}, + { + buildin_if, "if", "iF*"}, + { + buildin_getitem, "getitem", "Ii**"}, + { + buildin_getitem2, "getitem2", "iiiiiiiii*"}, + { + buildin_makeitem, "makeitem", "IiMxy"}, + { + buildin_delitem, "delitem", "Ii"}, + { + 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_pow, "pow", "ii"}, + { + buildin_countitem, "countitem", "I"}, + { + 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_skill, "skill", "ii*"}, + { + buildin_setskill, "setskill", "ii"}, // [Fate] + { + buildin_guildskill, "guildskill", "ii"}, + { + buildin_getskilllv, "getskilllv", "i"}, + { + buildin_getgdskilllv, "getgdskilllv", "ii"}, + { + buildin_basicskillcheck, "basicskillcheck", "*"}, + { + buildin_getgmlevel, "getgmlevel", ""}, + { + buildin_end, "end", ""}, + { + buildin_getopt2, "getopt2", ""}, + { + buildin_setopt2, "setopt2", "i"}, + { + buildin_end, "break", ""}, + { + 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", "Mxy"}, + { + buildin_gettimetick, "gettimetick", "i"}, + { + buildin_gettime, "gettime", "i"}, + { + buildin_gettimestr, "gettimestr", "si"}, + { + buildin_openstorage, "openstorage", "*"}, + { + buildin_guildopenstorage, "guildopenstorage", "*"}, + { + buildin_itemskill, "itemskill", "iis"}, + { + buildin_monster, "monster", "Mxysmi*"}, + { + buildin_areamonster, "areamonster", "Mxyxysmi*"}, + { + buildin_killmonster, "killmonster", "ME"}, + { + buildin_killmonsterall, "killmonsterall", "M"}, + { + buildin_doevent, "doevent", "E"}, + { + buildin_donpcevent, "donpcevent", "E"}, + { + buildin_addtimer, "addtimer", "tE"}, + { + buildin_deltimer, "deltimer", "E"}, + { + buildin_addtimercount, "addtimercount", "si"}, + { + buildin_initnpctimer, "initnpctimer", ""}, + { + buildin_stopnpctimer, "stopnpctimer", ""}, + { + buildin_startnpctimer, "startnpctimer", "*"}, + { + buildin_setnpctimer, "setnpctimer", "i"}, + { + buildin_getnpctimer, "getnpctimer", "i"}, + { + buildin_announce, "announce", "si"}, + { + buildin_mapannounce, "mapannounce", "Msi"}, + { + buildin_areaannounce, "areaannounce", "Mxyxysi"}, + { + buildin_getusers, "getusers", "i"}, + { + buildin_getmapusers, "getmapusers", "M"}, + { + buildin_getareausers, "getareausers", "Mxyxy*"}, + { + buildin_getareadropitem, "getareadropitem", "Mxyxyi*"}, + { + 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", "iTi*"}, + { + buildin_sc_start2, "sc_start2", "iTii*"}, + { + buildin_sc_end, "sc_end", "i"}, + { + buildin_sc_check, "sc_check", "i"}, + { + buildin_getscrate, "getscrate", "ii*"}, + { + buildin_debugmes, "debugmes", "s"}, + { + 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", "MMxy"}, + { + buildin_setmapflag, "setmapflag", "Mi"}, + { + buildin_removemapflag, "removemapflag", "Mi"}, + { + buildin_pvpon, "pvpon", "M"}, + { + buildin_pvpoff, "pvpoff", "M"}, + { + 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", "P"}, + { + buildin_wedding_effect, "wedding", ""}, + { + buildin_divorce, "divorce", ""}, + { + buildin_getitemname, "getitemname", "I"}, + { + buildin_getspellinvocation, "getspellinvocation", "s"}, + { + buildin_getanchorinvocation, "getanchorinvocation", "s"}, + { + buildin_getpartnerid, "getpartnerid2", ""}, + { + buildin_getexp, "getexp", "ii"}, + { + buildin_getinventorylist, "getinventorylist", ""}, + { + buildin_getskilllist, "getskilllist", ""}, + { + buildin_get_pool_skills, "getpoolskilllist", ""}, + { + buildin_get_activated_pool_skills, "getactivatedpoolskilllist", ""}, + { + buildin_get_unactivated_pool_skills, "getunactivatedpoolskilllist", ""}, + { + buildin_activate_pool_skill, "poolskill", "i"}, + { + buildin_deactivate_pool_skill, "unpoolskill", "i"}, + { + buildin_check_pool_skill, "checkpoolskill", "i"}, + { + buildin_clearitem, "clearitem", ""}, + { + buildin_classchange, "classchange", "ii"}, + { + buildin_misceffect, "misceffect", "i*"}, + { + buildin_soundeffect, "soundeffect", "si"}, + { + buildin_strmobinfo, "strmobinfo", "im"}, // display mob data [Valaris] + { + buildin_guardian, "guardian", "siisii*i"}, // summon guardians + { + buildin_guardianinfo, "guardianinfo", "i"}, // display guardian data [Valaris] + { + 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", "MMxy"}, // Added by RoVeRT + { + buildin_inittimer, "inittimer", ""}, + { + buildin_stoptimer, "stoptimer", ""}, + { + buildin_cmdothernpc, "cmdothernpc", "ss"}, + { + buildin_gmcommand, "gmcommand", "s"}, // [MouseJstr] +// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr] + { + buildin_npcwarp, "npcwarp", "xys"}, // [remoitnane] + { + buildin_message, "message", "Ps"}, // [MouseJstr] + { + buildin_npctalk, "npctalk", "s"}, // [Valaris] + { + buildin_hasitems, "hasitems", ""}, // [Valaris] + { + buildin_mobcount, "mobcount", "ME"}, + { + buildin_getlook, "getlook", "i"}, + { + buildin_getsavepoint, "getsavepoint", "i"}, + { + buildin_areatimer, "areatimer", "MxyxytE"}, + { + buildin_isin, "isin", "Mxyxy"}, + { + buildin_shop, "shop", "s"}, + { + buildin_isdead, "isdead", ""}, + { + buildin_fakenpcname, "fakenpcname", "ssi"}, + { + buildin_unequip_by_id, "unequipbyid", "i"}, // [Freeyorp] + { + buildin_getx, "getx", ""}, // [Kage] + { + buildin_gety, "gety", ""}, // [Kage] + // End Additions + { +NULL, NULL, NULL},}; + +int buildin_message (struct script_state *st); // [MouseJstr] + +enum +{ + C_NOP, C_POS, C_INT, C_PARAM, C_FUNC, C_STR, C_CONSTSTR, C_ARG, + C_NAME, C_EOL, C_RETINFO, + + 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 +}; + +/*========================================== + * 文字列のハッシュを計算 + *------------------------------------------ + */ +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, 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 = strdup (p); + for (i = 0; lowcase[i]; i++) + lowcase[i] = tolower (lowcase[i]); + if ((i = search_str (lowcase)) >= 0) + { + free (lowcase); + return i; + } + free (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, 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; + RECREATE (str_data, struct str_data_t, str_data_size); + memset (str_data + (str_data_size - 128), '\0', 128); + } + while (str_pos + strlen (p) + 1 >= str_size) + { + str_size += 256; + str_buf = (char *) realloc (str_buf, str_size); + memset (str_buf + (str_size - 256), '\0', 256); + } + strcpy (str_buf + str_pos, 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 += strlen (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 = (char *) realloc (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 (unsigned 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: + 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: + // ラベルの可能性があるので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 = 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] = 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変数用 + if (*p == 'l') + p++; // 一時的変数用(like weiss) + + 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 = strchr (p, '\n'); + if (lineend) + { + c = *lineend; + *lineend = 0; + } + if (lineend == NULL || pos < lineend) + { + printf ("%s line %d : ", 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) + printf ("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 (p, &np, 0); + add_scripti (i); + p = 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) + { + disp_error_message ("unexpected character", p); + exit (1); + } + p2 = skip_word (p); + c = *p2; + *p2 = 0; // 名前をadd_strする + l = add_str (p); + + parse_cmd = l; // warn_*_mismatch_paramnumのために必要 + if (l == search_str ("if")) // warn_cmd_no_commaのために必要 + parse_cmd_if++; +/* + // 廃止予定のl14/l15,およびプレフィックスlの警告 + if( strcmp(str_buf+str_data[l].str,"l14")==0 || + strcmp(str_buf+str_data[l].str,"l15")==0 ){ + disp_error_message("l14 and l15 is DEPRECATED. use @menu instead of l15.",p); + }else if(str_buf[str_data[l].str]=='l'){ + disp_error_message("prefix 'l' is DEPRECATED. use prefix '@' instead.",p2); + } +*/ + *p2 = c; + p = p2; + + if (str_data[l].type != C_FUNC && c == '[') + { + // array(name[i] => getelementofarray(name,i) ) + add_scriptl (search_str ("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 + add_scriptl (l); + + } + +#ifdef DEBUG_FUNCIN + if (battle_config.etc_log) + printf ("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) + printf ("parse_subexpr %s\n", p); +#endif + p = skip_space (p); + + if (*p == '-') + { + tmpp = skip_space (p + 1); + if (*tmpp == ';' || *tmpp == ',') + { + add_scriptl (LABEL_NEXTLINE); + p++; + return p; + } + } + tmpp = 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[func].type != C_FUNC) + { + disp_error_message ("expect function", tmpp); + exit (0); + } + + add_scriptc (C_ARG); + while (*p && *p != ')' && i < 128) + { + plist[i] = 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++; + } + plist[i] = 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 (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", + plist[(i < j) ? i : j]); + } + } + } + else // not op == C_FUNC + { + p = parse_subexpr (p, opl); + } + add_scriptc (op); + p = skip_space (p); + } +#ifdef DEBUG_FUNCIN + if (battle_config.etc_log) + printf ("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) + printf ("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) + printf ("parse_expr end %s\n", p); +#endif + return p; +} + +/*========================================== + * 行の解析 + *------------------------------------------ + */ +unsigned char *parse_line (unsigned char *p) +{ + int i = 0, cmd; + const char *plist[128]; + char *p2; + + p = skip_space (p); + if (*p == ';') + return p; + + parse_cmd_if = 0; // warn_cmd_no_commaのために必要 + + // 最初は関数名 + p2 = p; + p = parse_simpleexpr (p); + p = skip_space (p); + + cmd = parse_cmd; + if (str_data[cmd].type != C_FUNC) + { + disp_error_message ("expect command", p2); +// exit(0); + } + + add_scriptc (C_ARG); + while (p && *p && *p != ';' && i < 128) + { + plist[i] = p; + + p = parse_expr (p); + p = skip_space (p); + // 引数区切りの,処理 + if (*p == ',') + p++; + else if (*p != ';' && script_config.warn_cmd_no_comma + && parse_cmd_if * 2 <= i) + { + disp_error_message ("expect ',' or ';' at cmd params", p); + } + p = skip_space (p); + i++; + } + plist[i] = p; + if (!p || *(p++) != ';') + { + disp_error_message ("need ';'", p); + exit (1); + } + add_scriptc (C_FUNC); + + 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", + plist[(i < j) ? i : j]); + } + } + + return p; +} + +/*========================================== + * 組み込み関数の追加 + *------------------------------------------ + */ +static void add_buildin_func (void) +{ + int i, n; + for (i = 0; buildin_func[i].func; i++) + { + n = add_str (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; + + fp = fopen_ ("db/const.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/const.txt\n"); + 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 (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; + script_buf = (unsigned char *) calloc (SCRIPT_BLOCK_SIZE, 1); + 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_NOP; + str_data[i].backpatch = -1; + str_data[i].label = -1; + } + } + + // 外部用label dbの初期化 + if (scriptlabel_db != NULL) + strdb_final (scriptlabel_db, NULL); + scriptlabel_db = strdb_init (50); + + // for error message + startptr = src; + startline = line; + + p = src; + p = skip_space (p); + if (*p != '{') + { + disp_error_message ("not found '{'", p); + return NULL; + } + for (p++; p && *p && *p != '}';) + { + p = skip_space (p); + // labelだけ特殊処理 + tmpp = skip_space (skip_word (p)); + if (*tmpp == ':') + { + 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_insert (scriptlabel_db, (const char*)p, 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 = (char *) realloc (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 + + return script_buf; +} + +// +// 実行系 +// +enum +{ STOP = 1, 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) + { + printf ("script_rid2sd: fatal error ! player not attached!\n"); + } + 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) + printf ("get_val error name?:%s\n", name); + } + if (postfix == '$') + { + + data->type = C_CONSTSTR; + if (prefix == '@' || prefix == 'l') + { + if (sd) + data->u.str = pc_readregstr (sd, data->u.num); + } + else if (prefix == '$') + { + data->u.str = + (char *) numdb_search (mapregstr_db, data->u.num); + } + else + { + printf ("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 == '@' || prefix == 'l') + { + if (sd) + data->u.num = pc_readreg (sd, data->u.num); + } + else if (prefix == '$') + { + data->u.num = (int) numdb_search (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 == '@' || prefix == 'l') + { + pc_setregstr (sd, num, str); + } + else if (prefix == '$') + { + mapreg_setregstr (num, str); + } + else + { + printf ("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 == '@' || prefix == 'l') + { + 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; +} + +/*========================================== + * 文字列への変換 + *------------------------------------------ + */ +char *conv_str (struct script_state *st, struct script_data *data) +{ + get_val (st, data); + if (data->type == C_INT) + { + char *buf; + buf = (char *) calloc (16, 1); + sprintf (buf, "%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) + free (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 *) + realloc (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 *) + realloc (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 = 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, stack->stack_data[pos].u.str); + break; + case C_STR: + push_str (stack, C_STR, strdup (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) + { + free (stack->stack_data[i].u.str); + } + } + 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; +} + +// +// 埋め込み関数 +// +/*========================================== + * + *------------------------------------------ + */ +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) + { + printf ("script: goto: not label !\n"); + st->state = END; + return 0; + } + + 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_search (script_get_userfunc_db (), 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->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->defsp = st->start + 4 + j; + st->state = GOTO; + } + else + { + printf ("script:callfunc: function not found! [%s]\n", str); + st->state = END; + } + return 0; +} + +/*========================================== + * サブルーティンの呼び出し + *------------------------------------------ + */ +int buildin_callsub (struct script_state *st) +{ + int pos = conv_num (st, &(st->stack->stack_data[st->start + 2])); + 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->defsp); // 現在の基準スタックポインタをプッシュ + push_val (st->stack, C_INT, (int) st->script); // 現在のスクリプトをプッシュ + push_val (st->stack, C_RETINFO, st->pos); // 現在のスクリプト位置をプッシュ + + st->pos = pos; + st->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->defsp < 4 + || st->stack->stack_data[st->defsp - 1].type != C_RETINFO) + { + printf ("script:getarg without callfunc or callsub!\n"); + st->state = END; + return 0; + } + max = conv_num (st, &(st->stack->stack_data[st->defsp - 4])); + stsp = st->defsp - max - 4; + if (num >= max) + { + printf ("script:getarg arg1(%d) out of range(%d) !\n", num, max); + st->state = END; + return 0; + } + 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 i, len = 0; // [fate] len is the total # of bytes we need to transmit the string choices + int menu_choices = 0; + int finished_menu_items = 0; // [fate] set to 1 after we hit the first empty string + + struct map_session_data *sd; + + sd = script_rid2sd (st); + + // We don't need to do this iteration if the player cancels, strictly speaking. + for (i = st->start + 2; i < st->end; i += 2) + { + int choice_len; + conv_str (st, &(st->stack->stack_data[i])); + choice_len = strlen (st->stack->stack_data[i].u.str); + len += choice_len + 1; // count # of bytes we'll need for packet. Only used if menu_or_input = 0. + + if (choice_len && !finished_menu_items) + ++menu_choices; + else + finished_menu_items = 1; + } + + if (sd->state.menu_or_input == 0) + { + st->state = RERUNLINE; + sd->state.menu_or_input = 1; + + buf = (char *) calloc (len + 1, 1); + buf[0] = 0; + for (i = st->start + 2; menu_choices > 0; i += 2, --menu_choices) + { + strcat (buf, st->stack->stack_data[i].u.str); + strcat (buf, ":"); + } + clif_scriptmenu (script_rid2sd (st), st->oid, buf); + free (buf); + } + else if (sd->npc_menu == 0xff) + { // cansel + sd->state.menu_or_input = 0; + st->state = END; + } + else + { // goto動作 + // ragemu互換のため + pc_setreg (sd, add_str ("l15"), sd->npc_menu); + pc_setreg (sd, add_str ("@menu"), sd->npc_menu); + sd->state.menu_or_input = 0; + if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices) + { + int pos; + if (st->stack-> + stack_data[st->start + sd->npc_menu * 2 + 1].type != C_POS) + { + st->state = END; + return 0; + } + pos = + conv_num (st, + &(st-> + stack->stack_data[st->start + sd->npc_menu * 2 + + 1])); + st->pos = pos; + st->state = GOTO; + } + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_rand (struct script_state *st) +{ + int range, min, max; + + if (st->end > st->start + 3) + { + 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) + { + int tmp; + tmp = min; + min = max; + max = tmp; + } + range = max - min + 1; + push_val (st->stack, C_INT, (range <= 0 ? 0 : MRAND (range)) + min); + } + else + { + range = conv_num (st, &(st->stack->stack_data[st->start + 2])); + push_val (st->stack, C_INT, range <= 0 ? 0 : MRAND (range)); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_pow (struct script_state *st) +{ + int a, b; + + a = conv_num (st, &(st->stack->stack_data[st->start + 2])); + b = conv_num (st, &(st->stack->stack_data[st->start + 3])); + + push_val (st->stack, C_INT, (int) pow (a * 0.001, b)); + + return 0; +} + +/*========================================== + * Check whether the PC is at the specified location + *------------------------------------------ + */ +int buildin_isat (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 (!sd) + return 1; + + push_val (st->stack, C_INT, + (x == sd->bl.x) + && (y == sd->bl.y) && (!strcmp (str, map[sd->bl.m].name))); + + 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, str, x, y, 0); + return 0; +} + +/*========================================== + * エリア指定ワープ + *------------------------------------------ + */ +int buildin_areawarp_sub (struct block_list *bl, va_list ap) +{ + int x, y; + char *map; + map = va_arg (ap, char *); + x = va_arg (ap, int); + y = va_arg (ap, int); + if (strcmp (map, "Random") == 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; + 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; + + map_foreachinarea (buildin_areawarp_sub, + m, x0, y0, x1, y1, BL_PC, str, x, y); + 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])); + 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])); + 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 = + (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 + { + printf ("buildin_input: string discarded !!\n"); + } + } + else + { + + //commented by Lupus (check Value Number Input fix in clif.c) + //** 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 + } + + // 数値 + if (st->end > st->start + 2) + { // 引数1個 + set_reg (sd, num, name, (void *) sd->npc_amount); + } + else + { + // ragemu互換のため + pc_setreg (sd, add_str ("l14"), sd->npc_amount); + } + } + } + else + { + 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_if (struct script_state *st) +{ + int sel, i; + + sel = conv_num (st, &(st->stack->stack_data[st->start + 2])); + if (!sel) + return 0; + + // 関数名をコピー + push_copy (st->stack, st->start + 3); + // 間に引数マーカを入れて + push_val (st->stack, C_ARG, 0); + // 残りの引数をコピー + for (i = st->start + 4; i < st->end; i++) + { + push_copy (st->stack, i); + } + run_func (st); + + 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) + { + printf ("script: buildin_set: not name\n"); + return 0; + } + + 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 != '@') + { + printf ("buildin_setarray: illegal scope !\n"); + return 0; + } + 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 != '@') + { + printf ("buildin_cleararray: illegal scope !\n"); + return 0; + } + 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 != '@') + { + printf ("buildin_copyarray: illegal scope !\n"); + return 0; + } + if ((postfix == '$' || postfix2 == '$') && postfix != postfix2) + { + printf ("buildin_copyarray: type mismatch !\n"); + return 0; + } + if (prefix != '$' || prefix2 != '$') + sd = script_rid2sd (st); + + 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 = i; + for (; i < 128; i++) + { + void *v = get_val2 (st, num + (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 != '@') + { + printf ("buildin_copyarray: illegal scope !\n"); + return 0; + } + + 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 != '@') + { + printf ("buildin_deletearray: illegal scope !\n"); + return 0; + } + 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, ""); + } + 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) + { + printf + ("script: getelementofarray (operator[]): param2 illegal number %d\n", + i); + push_val (st->stack, C_INT, 0); + } + else + { + push_val (st->stack, C_NAME, + (i << 24) | st->stack->stack_data[st->start + 2].u.num); + } + } + else + { + printf + ("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; + + itemid = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + clif_cutin (script_rid2sd (st), itemdb_search (itemid)->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) + printf ("wrong item ID : countitem(%i)\n", nameid); + } + push_val (st->stack, C_INT, count); + + return 0; +} + +/*========================================== + * 重量チェック + *------------------------------------------ + */ +int buildin_checkweight (struct script_state *st) +{ + int nameid = 0, amount; + 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); + } + + sd = script_rid2sd (st); + if (itemdb_weight (nameid) * amount + sd->weight > sd->max_weight) + { + push_val (st->stack, C_INT, 0); + } + else + { + push_val (st->stack, C_INT, 1); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_getitem (struct script_state *st) +{ + int nameid, 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 = 727; //Default to iten + 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 (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); + 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); + map_addflooritem (&item_tmp, amount, sd->bl.m, sd->bl.x, sd->bl.y, + NULL, NULL, NULL, 0); + } + } + + 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_search (nameid); + 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); + } + } + + 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 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])); + 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 (sd && strcmp (mapname, "this") == 0) + 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); + +// clif_additem(sd,0,0,flag); + map_addflooritem (&item_tmp, amount, m, x, y, NULL, NULL, NULL, 0); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_delitem (struct script_state *st) +{ + int nameid = 0, amount, 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 = 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 + //printf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); + return 0; + } + sd = script_rid2sd (st); + + for (i = 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; + } + for (i = 0; i < MAX_INVENTORY; i++) + { + if (sd->status.inventory[i].nameid == nameid) + { + if (sd->status.inventory[i].amount >= amount) + { + pc_delitem (sd, i, amount, 0); + break; + } + else + { + amount -= sd->status.inventory[i].amount; + if (amount == 0) + amount = sd->status.inventory[i].amount; + pc_delitem (sd, i, amount, 0); + break; + } + } + } + + 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 *) calloc (24, 1); + strcpy (buf, p->name); + 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 != 0) + push_str (st->stack, C_STR, name); + else + push_str (st->stack, C_CONSTSTR, "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 ("$@partymembername$") + (i << 24), + p->member[i].name); + j++; + } + } + } + mapreg_setreg (add_str ("$@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 *) calloc (24, 1); + strcpy (buf, g->name); + return buf; + } + return 0; +} + +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 != 0) + push_str (st->stack, C_STR, name); + else + push_str (st->stack, C_CONSTSTR, "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 *) calloc (24, 1); + strncpy (buf, g->master, 23); + 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, master); + else + push_str (st->stack, C_CONSTSTR, "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 *) calloc (24, 1); + strncpy (buf, sd->status.name, 23); + push_str (st->stack, C_STR, buf); + } + if (num == 1) + { + char *buf; + buf = buildin_getpartyname_sub (sd->status.party_id); + if (buf != 0) + push_str (st->stack, C_STR, buf); + else + push_str (st->stack, C_CONSTSTR, ""); + } + if (num == 2) + { + char *buf; + buf = buildin_getguildname_sub (sd->status.guild_id); + if (buf != 0) + push_str (st->stack, C_STR, buf); + else + push_str (st->stack, C_CONSTSTR, ""); + } + + 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) + { + printf ("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 *) calloc (64, 1); + 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, 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].broken == 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].broken == 1) + { + repaircounter++; + if (num == repaircounter) + { + sd->status.inventory[i].broken = 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); + 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] + && (num != 1 || sd->inventory_data[i]->def > 1 + || (sd->inventory_data[i]->def == 1 + && sd->inventory_data[i]->equip_script == NULL) + || (sd->inventory_data[i]->def <= 0 + && sd->inventory_data[i]->equip_script != NULL))) + { + 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) + push_val (st->stack, C_INT, + pc_percentrefinery (sd, &sd->status.inventory[i])); + 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; + + sd->status.inventory[i].refine++; + pc_unequipitem (sd, i, 0); + clif_refine (sd->fd, sd, 0, i, sd->status.inventory[i].refine); + clif_delitem (sd, i, 1); + clif_additem (sd, i, 1, 0); + pc_equipitem (sd, i, ep); + clif_misceffect (&sd->bl, 3); + } + + 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) + { + sd->status.inventory[i].refine = 0; + pc_unequipitem (sd, i, 0); + // 精錬失敗エフェクトのパケット + 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_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); + clif_skillinfoblock (sd); + + return 0; +} + +/*========================================== + * [Fate] Sets the skill level permanently + *------------------------------------------ + */ +int buildin_setskill (struct script_state *st) +{ + int id, level; + 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])); + sd = script_rid2sd (st); + + sd->status.skill[id].id = level ? id : 0; + sd->status.skill[id].lv = level; + clif_skillinfoblock (sd); + return 0; +} + +/*========================================== + * ギルドスキル取得 + *------------------------------------------ + */ +int buildin_guildskill (struct script_state *st) +{ + int id, level; + 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); + + 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_KAFRACONTACT + * 10002 : GD_GUARDIANRESEARCH + * 10003 : GD_CHARISMA + * 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; +} + +/*========================================== + * [Freeyorp] Return the current opt2 + *------------------------------------------ + */ + +int buildin_getopt2 (struct script_state *st) +{ + struct map_session_data *sd; + + sd = script_rid2sd (st); + + push_val (st->stack, C_INT, sd->opt2); + + return 0; +} + +/*========================================== + * [Freeyorp] Sets opt2 + *------------------------------------------ + */ + +int buildin_setopt2 (struct script_state *st) +{ + int new_opt2; + struct map_session_data *sd; + + new_opt2 = conv_num (st, &(st->stack->stack_data[st->start + 2])); + sd = script_rid2sd (st); + if (new_opt2 == sd->opt2) + return 0; + sd->opt2 = new_opt2; + clif_changeoption (&sd->bl); + pc_calcstatus (sd, 0); + + 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_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; + 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])); + pc_setsavepoint (script_rid2sd (st), str, x, y); + return 0; +} + +/*========================================== + * gettimetick(type) + * + * type The type of time measurement. + * Specify 0 for the system tick, 1 for + * seconds elapsed today, or 2 for seconds + * since Unix epoch. Defaults to 0 for any + * other value. + *------------------------------------------ + */ +int buildin_gettimetick (struct script_state *st) /* Asgard Version */ +{ + int type; + type = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + switch (type) + { + /* Number of seconds elapsed today (0-86399, 00:00:00-23:59:59). */ + case 1: + { + time_t timer; + struct tm *t; + + time (&timer); + t = gmtime (&timer); + push_val (st->stack, C_INT, + ((t->tm_hour) * 3600 + (t->tm_min) * 60 + t->tm_sec)); + break; + } + /* Seconds since Unix epoch. */ + case 2: + push_val (st->stack, C_INT, (int) time (NULL)); + break; + /* System tick (unsigned int, and yes, it will wrap). */ + case 0: + default: + 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 = gmtime (&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 *) calloc (maxlen + 1, 1); + strftime (tmpstr, maxlen, fmtstr, gmtime (&now)); + tmpstr[maxlen] = '\0'; + + push_str (st->stack, C_STR, tmpstr); + return 0; +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +int buildin_openstorage (struct script_state *st) +{ +// int sync = 0; +// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2])); + struct map_session_data *sd = script_rid2sd (st); + +// if (sync) { + st->state = STOP; + sd->npc_flags.storage = 1; +// } else st->state = END; + + storage_storageopen (sd); + return 0; +} + +int buildin_guildopenstorage (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int ret; + st->state = STOP; + 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; +} + +/*========================================== + * 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_reason (sd, base, job, PC_GAINEXP_REASON_SCRIPT); + + return 0; +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +int buildin_monster (struct script_state *st) +{ + int mob_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])); + mob_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])); + + mob_once_spawn (map_id2sd (st->rid), map, x, y, str, mob_class, amount, + event); + return 0; +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +int buildin_areamonster (struct script_state *st) +{ + int mob_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])); + mob_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, mob_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_foreachinarea (buildin_killmonster_sub, + m, 0, 0, map[m].xs, map[m].ys, 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_foreachinarea (buildin_killmonsterall_sub, + m, 0, 0, map[m].xs, map[m].ys, BL_MOB); + 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); + 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); + 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; +} + +/*========================================== + * 天の声アナウンス + *------------------------------------------ + */ +int buildin_announce (struct script_state *st) +{ + char *str; + 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 (flag & 0x0f) + { + struct block_list *bl = (flag & 0x08) ? map_id2bl (st->oid) : + (struct block_list *) script_rid2sd (st); + clif_GMmessage (bl, str, strlen (str) + 1, flag); + } + else + intif_GMmessage (str, strlen (str) + 1, flag); + return 0; +} + +/*========================================== + * 天の声アナウンス(特定マップ) + *------------------------------------------ + */ +int buildin_mapannounce_sub (struct block_list *bl, va_list ap) +{ + char *str; + int len, flag; + str = va_arg (ap, char *); + len = va_arg (ap, int); + flag = va_arg (ap, int); + clif_GMmessage (bl, str, len, flag | 3); + return 0; +} + +int buildin_mapannounce (struct script_state *st) +{ + char *mapname, *str; + 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 ((m = map_mapname2mapid (mapname)) < 0) + return 0; + map_foreachinarea (buildin_mapannounce_sub, + m, 0, 0, map[m].xs, map[m].ys, BL_PC, str, + strlen (str) + 1, flag & 0x10); + return 0; +} + +/*========================================== + * 天の声アナウンス(特定エリア) + *------------------------------------------ + */ +int buildin_areaannounce (struct script_state *st) +{ + char *map, *str; + 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 ((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); + 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; +} + +/*========================================== + * マップ指定ユーザー数所得 + *------------------------------------------ + */ +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_living_sub (struct block_list *bl, va_list ap) +{ + int *users = va_arg (ap, int *); + if (!pc_isdead((struct map_session_data *)bl)) + (*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])); + + int living = 0; + if (st->end > st->start + 7) + { + living = conv_num (st, &(st->stack->stack_data[st->start + 7])); + } + if ((m = map_mapname2mapid (str)) < 0) + { + push_val (st->stack, C_INT, -1); + return 0; + } + map_foreachinarea (living ? buildin_getareausers_living_sub: 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_sub_anddelete (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; + clif_clearflooritem(drop, 0); + map_delobject(drop->bl.id, drop->bl.type); + } + return 0; +} + +int buildin_getareadropitem (struct script_state *st) +{ + char *str; + int m, x0, y0, x1, y1, item, amount = 0, delitems = 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 (st->end > st->start + 8) + delitems = conv_num (st, &(st->stack->stack_data[st->start + 8])); + + if ((m = map_mapname2mapid (str)) < 0) + { + push_val (st->stack, C_INT, -1); + return 0; + } + if (delitems) + map_foreachinarea (buildin_getareadropitem_sub_anddelete, + m, x0, y0, x1, y1, BL_ITEM, item, &amount); + else + 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; + 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 (bl->type == BL_PC + && ((struct map_session_data *) bl)->state.potionpitcher_flag) + bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); + skill_status_change_start (bl, type, val1, 0, 0, 0, tick, 0); + return 0; +} + +/*========================================== + * 状態異常にかかる(確率指定) + *------------------------------------------ + */ +int buildin_sc_start2 (struct script_state *st) +{ + struct block_list *bl; + int type, tick, val1, 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 (bl->type == BL_PC + && ((struct map_session_data *) bl)->state.potionpitcher_flag) + bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); + if (MRAND (10000) < per) + skill_status_change_start (bl, type, val1, 0, 0, 0, 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 (bl->type == BL_PC + && ((struct map_session_data *) bl)->state.potionpitcher_flag) + bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); + skill_status_change_end (bl, type, -1); +// if(battle_config.etc_log) +// printf("sc_end : %d %d\n",st->rid,type); + return 0; +} + +int buildin_sc_check (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 (bl->type == BL_PC + && ((struct map_session_data *) bl)->state.potionpitcher_flag) + bl = map_id2bl (((struct map_session_data *) bl)->skilltarget); + + push_val (st->stack, C_INT, skill_status_change_active (bl, type)); + + return 0; +} + +/*========================================== + * 状態異常耐性を計算した確率を返す + *------------------------------------------ + */ +int buildin_getscrate (struct script_state *st) +{ + struct block_list *bl; + int sc_def = 100, sc_def_mdef2, sc_def_vit2, sc_def_int2, sc_def_luk2; + int type, rate, luk; + + 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); + + luk = battle_get_luk (bl); + sc_def_mdef2 = 100 - (3 + battle_get_mdef (bl) + luk / 3); + sc_def_vit2 = 100 - (3 + battle_get_vit (bl) + luk / 3); + sc_def_int2 = 100 - (3 + battle_get_int (bl) + luk / 3); + sc_def_luk2 = 100 - (3 + luk); + + if (type == SC_STONE || type == SC_FREEZE) + sc_def = sc_def_mdef2; + else if (type == SC_STAN || type == SC_POISON || type == SC_SILENCE) + sc_def = sc_def_vit2; + else if (type == SC_SLEEP || type == SC_CONFUSION || type == SC_BLIND) + sc_def = sc_def_int2; + else if (type == SC_CURSE) + sc_def = sc_def_luk2; + + 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])); + printf ("script debug : %d %d : %s\n", st->rid, st->oid, + st->stack->stack_data[st->start + 2].u.str); + 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; +} + +/*========================================== + * スキルリセット + *------------------------------------------ + */ +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 == 22 && !battle_config.wedding_modifydisplay) + 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->status.pc_class == 20 || sd->status.pc_class == 4021) + sd->status.pc_class -= 1; + } + else if (sd->status.sex == 1) + { + sd->status.sex = 0; + sd->sex = 0; + if (sd->status.pc_class == 19 || sd->status.pc_class == 4020) + sd->status.pc_class += 1; + } + chrif_char_ask_name (-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex + chrif_save (sd); + 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, strlen (name) + 1, ev); + 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, cd->title); + return 0; + case 5: + push_str (st->stack, C_CONSTSTR, cd->pass); + return 0; + case 16: + push_str (st->stack, C_CONSTSTR, 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 ("$@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, str, x, y, 0); + } + mapreg_setreg (add_str ("$@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 +}; + +int buildin_setmapflagnosave (struct script_state *st) +{ + int m, x, y; + 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); + if (m >= 0) + { + map[m].flag.nosave = 1; + memcpy (map[m].save.map, str2, 16); + 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_PVP_NOPARTY: + map[m].flag.pvp_noparty = 1; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild = 1; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty = 1; + break; + case MF_NOZENYPENALTY: + map[m].flag.nozenypenalty = 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_FOG: // [Valaris] + map[m].flag.fog = 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; + } + } + + 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_NOPARTY: + map[m].flag.pvp_noparty = 0; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild = 0; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty = 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_FOG: // [Valaris] + map[m].flag.fog = 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; + + } + } + + return 0; +} + +int buildin_pvpon (struct script_state *st) +{ + int m, i; + char *str; + struct map_session_data *pl_sd = NULL; + + 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; + + 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 (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; + } + } + } + } + + return 0; +} + +int buildin_pvpoff (struct script_state *st) +{ + int m, i; + char *str; + struct map_session_data *pl_sd = NULL; + + 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 = 0; + clif_send0199 (m, 0); + + if (battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] + return 0; + + 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 (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; +} + +/*========================================== + * NPCエモーション + *------------------------------------------ + */ + +int buildin_emotion (struct script_state *st) +{ + int type; + type = conv_num (st, &(st->stack->stack_data[st->start + 2])); + if (type < 0 || type > 100) + return 0; + 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->mob_class < 1285 || md->mob_class > 1288) + 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_foreachinarea (buildin_maprespawnguildid_sub, m, 0, 0, + map[m].xs - 1, map[m].ys - 1, BL_NUL, g_id, flag); + return 0; +} + +int buildin_agitstart (struct script_state *st) +{ + if (agit_flag == 1) + return 1; // Agit already Start. + agit_flag = 1; + guild_agit_start (); + return 0; +} + +int buildin_agitend (struct script_state *st) +{ + if (agit_flag == 0) + return 1; // 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; + + sd = script_rid2sd (st); + 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 + { + if (agit_flag == 1) + pc_setreg (sd, add_str ("@agit_flag"), 1); + if (agit_flag == 0) + pc_setreg (sd, add_str ("@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 1; +} + +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 *) calloc (24, 1); + strncpy (buf, gc->castle_name, 23); + break; + } + } + } + if (buf) + push_str (st->stack, C_STR, buf); + else + push_str (st->stack, C_CONSTSTR, ""); + 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: + push_val (st->stack, C_INT, gc->visibleG0); + break; + case 11: + push_val (st->stack, C_INT, gc->visibleG1); + break; + case 12: + push_val (st->stack, C_INT, gc->visibleG2); + break; + case 13: + push_val (st->stack, C_INT, gc->visibleG3); + break; + case 14: + push_val (st->stack, C_INT, gc->visibleG4); + break; + case 15: + push_val (st->stack, C_INT, gc->visibleG5); + break; + case 16: + push_val (st->stack, C_INT, gc->visibleG6); + break; + case 17: + push_val (st->stack, C_INT, gc->visibleG7); + break; + case 18: + push_val (st->stack, C_INT, gc->Ghp0); + break; + case 19: + push_val (st->stack, C_INT, gc->Ghp1); + break; + case 20: + push_val (st->stack, C_INT, gc->Ghp2); + break; + case 21: + push_val (st->stack, C_INT, gc->Ghp3); + break; + case 22: + push_val (st->stack, C_INT, gc->Ghp4); + break; + case 23: + push_val (st->stack, C_INT, gc->Ghp5); + break; + case 24: + push_val (st->stack, C_INT, gc->Ghp6); + break; + case 25: + push_val (st->stack, C_INT, gc->Ghp7); + 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: + gc->visibleG0 = value; + break; + case 11: + gc->visibleG1 = value; + break; + case 12: + gc->visibleG2 = value; + break; + case 13: + gc->visibleG3 = value; + break; + case 14: + gc->visibleG4 = value; + break; + case 15: + gc->visibleG5 = value; + break; + case 16: + gc->visibleG6 = value; + break; + case 17: + gc->visibleG7 = value; + break; + case 18: + gc->Ghp0 = value; + break; + case 19: + gc->Ghp1 = value; + break; + case 20: + gc->Ghp2 = value; + break; + case 21: + gc->Ghp3 = value; + break; + case 22: + gc->Ghp4 = value; + break; + case 23: + gc->Ghp5 = value; + break; + case 24: + gc->Ghp6 = value; + break; + case 25: + gc->Ghp7 = 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 = 4; + + 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)) + { + + 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, num, cardflag = 0, flag; + struct map_session_data *sd; + struct item item_tmp; + int c = 4; + + 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)) + { + + 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; + item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = + 0, item_tmp.card[3] = 0; + + 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; + item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = + 0, item_tmp.card[3] = 0; + pc_delitem (sd, i, 1, 0); + 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, num, cardflag = 0, flag, typefail; + struct map_session_data *sd; + struct item item_tmp; + int c = 4; + + 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)) + { + + 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; + item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = + 0, item_tmp.card[3] = 0; + 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) + { // 武具損失 + 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; + item_tmp.card[0] = 0, item_tmp.card[1] = 0, item_tmp.card[2] = + 0, item_tmp.card[3] = 0; + pc_delitem (sd, i, 1, 0); + 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, map_id2sd (st->rid), 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, map_id2sd (st->rid), 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_foreachinarea (buildin_mobcount_sub, + m, 0, 0, map[m].xs, map[m].ys, BL_MOB, event, &c); + + push_val (st->stack, C_INT, (c - 1)); + + 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); + + if (sd == NULL) + return 0; + clif_wedding_effect (&sd->bl); + return 0; +} + +int buildin_divorce (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + + st->state = STOP; // rely on pc_divorce to restart + + sd->npc_flags.divorce = 1; + + 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; +} + +/*================================================ + * 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 mob_class = conv_num (st, &(st->stack->stack_data[st->start + 3])); + + if (num <= 0 || num >= 8 || (mob_class >= 0 && mob_class <= 1000) || mob_class > 2000) + return 0; + + if (num == 1) + { + char *buf; + buf = mob_db[mob_class].name; + push_str (st->stack, C_STR, buf); + return 0; + } + else if (num == 2) + { + char *buf; + buf = mob_db[mob_class].jname; + push_str (st->stack, C_STR, buf); + return 0; + } + else if (num == 3) + push_val (st->stack, C_INT, mob_db[mob_class].lv); + else if (num == 4) + push_val (st->stack, C_INT, mob_db[mob_class].max_hp); + else if (num == 5) + push_val (st->stack, C_INT, mob_db[mob_class].max_sp); + else if (num == 6) + push_val (st->stack, C_INT, mob_db[mob_class].base_exp); + else if (num == 7) + push_val (st->stack, C_INT, mob_db[mob_class].job_exp); + return 0; +} + +/*========================================== + * Summon guardians [Valaris] + *------------------------------------------ + */ +int buildin_guardian (struct script_state *st) +{ + int mob_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])); + mob_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, mob_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 && gc->visibleG0 == 1) + push_val (st->stack, C_INT, gc->Ghp0); + if (guardian == 1 && gc->visibleG1 == 1) + push_val (st->stack, C_INT, gc->Ghp1); + if (guardian == 2 && gc->visibleG2 == 1) + push_val (st->stack, C_INT, gc->Ghp2); + if (guardian == 3 && gc->visibleG3 == 1) + push_val (st->stack, C_INT, gc->Ghp3); + if (guardian == 4 && gc->visibleG4 == 1) + push_val (st->stack, C_INT, gc->Ghp4); + if (guardian == 5 && gc->visibleG5 == 1) + push_val (st->stack, C_INT, gc->Ghp5); + if (guardian == 6 && gc->visibleG6 == 1) + push_val (st->stack, C_INT, gc->Ghp6); + if (guardian == 7 && gc->visibleG7 == 1) + push_val (st->stack, C_INT, gc->Ghp7); + else + push_val (st->stack, C_INT, -1); + + return 0; +} + +/*========================================== + * IDからItem名 + *------------------------------------------ + */ +int buildin_getitemname (struct script_state *st) +{ + 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); + i_data = itemdb_searchname (name); + } + else + { + int item_id = conv_num (st, data); + i_data = itemdb_search (item_id); + } + + item_name = (char *) calloc (24, 1); + if (i_data) + strncpy (item_name, i_data->jname, 23); + else + strncpy (item_name, "Unknown Item", 23); + + push_str (st->stack, C_STR, item_name); + + return 0; +} + +int buildin_getspellinvocation (struct script_state *st) +{ + char *name; + char *invocation; + + name = conv_str (st, &(st->stack->stack_data[st->start + 2])); + + invocation = magic_find_invocation (name); + if (!invocation) + invocation = "..."; + + push_str (st->stack, C_STR, strdup (invocation)); + return 0; +} + +int buildin_getanchorinvocation (struct script_state *st) +{ + char *name; + char *invocation; + + name = conv_str (st, &(st->stack->stack_data[st->start + 2])); + + invocation = magic_find_anchor_invocation (name); + if (!invocation) + invocation = "..."; + + push_str (st->stack, C_STR, strdup (invocation)); + return 0; +} + +int buildin_getpartnerid (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + + push_val (st->stack, C_INT, sd->status.partner_id); + return 0; +} + +/*========================================== + * PCの所持品情報読み取り + *------------------------------------------ + */ +int buildin_getinventorylist (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_INVENTORY; i++) + { + if (sd->status.inventory[i].nameid > 0 + && sd->status.inventory[i].amount > 0) + { + pc_setreg (sd, add_str ("@inventorylist_id") + (j << 24), + sd->status.inventory[i].nameid); + pc_setreg (sd, add_str ("@inventorylist_amount") + (j << 24), + sd->status.inventory[i].amount); + pc_setreg (sd, add_str ("@inventorylist_equip") + (j << 24), + sd->status.inventory[i].equip); + pc_setreg (sd, add_str ("@inventorylist_refine") + (j << 24), + sd->status.inventory[i].refine); + pc_setreg (sd, add_str ("@inventorylist_identify") + (j << 24), + sd->status.inventory[i].identify); + pc_setreg (sd, add_str ("@inventorylist_attribute") + (j << 24), + sd->status.inventory[i].attribute); + pc_setreg (sd, add_str ("@inventorylist_card1") + (j << 24), + sd->status.inventory[i].card[0]); + pc_setreg (sd, add_str ("@inventorylist_card2") + (j << 24), + sd->status.inventory[i].card[1]); + pc_setreg (sd, add_str ("@inventorylist_card3") + (j << 24), + sd->status.inventory[i].card[2]); + pc_setreg (sd, add_str ("@inventorylist_card4") + (j << 24), + sd->status.inventory[i].card[3]); + j++; + } + } + pc_setreg (sd, add_str ("@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 ("@skilllist_id") + (j << 24), + sd->status.skill[i].id); + pc_setreg (sd, add_str ("@skilllist_lv") + (j << 24), + sd->status.skill[i].lv); + pc_setreg (sd, add_str ("@skilllist_flag") + (j << 24), + sd->status.skill[i].flags); + j++; + } + } + pc_setreg (sd, add_str ("@skilllist_count"), j); + return 0; +} + +int buildin_get_activated_pool_skills (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int pool_skills[MAX_SKILL_POOL]; + int skill_pool_size = skill_pool (sd, pool_skills); + int i, count = 0; + + if (!sd) + return 0; + + for (i = 0; i < skill_pool_size; i++) + { + int skill_id = pool_skills[i]; + + if (sd->status.skill[skill_id].id == skill_id) + { + pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), + sd->status.skill[skill_id].id); + pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), + sd->status.skill[skill_id].lv); + pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), + sd->status.skill[skill_id].flags); + pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), + skill_name (skill_id)); + ++count; + } + } + pc_setreg (sd, add_str ("@skilllist_count"), count); + + return 0; +} + +extern int skill_pool_skills[]; +extern int skill_pool_skills_size; + +int buildin_get_unactivated_pool_skills (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int i, count = 0; + + if (!sd) + return 0; + + for (i = 0; i < skill_pool_skills_size; i++) + { + int skill_id = skill_pool_skills[i]; + + if (sd->status.skill[skill_id].id == skill_id && !(sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED)) + { + pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), + sd->status.skill[skill_id].id); + pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), + sd->status.skill[skill_id].lv); + pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), + sd->status.skill[skill_id].flags); + pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), + skill_name (skill_id)); + ++count; + } + } + pc_setreg (sd, add_str ("@skilllist_count"), count); + + return 0; +} + +int buildin_get_pool_skills (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int i, count = 0; + + if (!sd) + return 0; + + for (i = 0; i < skill_pool_skills_size; i++) + { + int skill_id = skill_pool_skills[i]; + + if (sd->status.skill[skill_id].id == skill_id) + { + pc_setreg (sd, add_str ("@skilllist_id") + (count << 24), + sd->status.skill[skill_id].id); + pc_setreg (sd, add_str ("@skilllist_lv") + (count << 24), + sd->status.skill[skill_id].lv); + pc_setreg (sd, add_str ("@skilllist_flag") + (count << 24), + sd->status.skill[skill_id].flags); + pc_setregstr (sd, add_str ("@skilllist_name$") + (count << 24), + skill_name (skill_id)); + ++count; + } + } + pc_setreg (sd, add_str ("@skilllist_count"), count); + + return 0; +} + +int buildin_activate_pool_skill (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + skill_pool_activate (sd, skill_id); + clif_skillinfoblock (sd); + + return 0; +} + +int buildin_deactivate_pool_skill (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + skill_pool_deactivate (sd, skill_id); + clif_skillinfoblock (sd); + + return 0; +} + +int buildin_check_pool_skill (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + int skill_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + push_val (st->stack, C_INT, skill_pool_is_activated (sd, skill_id)); + + 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) + pc_delitem (sd, i, sd->status.inventory[i].amount, 0); + } + return 0; +} + +/*========================================== + * NPCクラスチェンジ + * classは変わりたいclass + * typeは通常0なのかな? + *------------------------------------------ + */ +int buildin_classchange (struct script_state *st) +{ + int npc_class, type; + struct block_list *bl = map_id2bl (st->oid); + + if (bl == NULL) + return 0; + + npc_class = conv_num (st, &(st->stack->stack_data[st->start + 2])); + type = conv_num (st, &(st->stack->stack_data[st->start + 3])); + clif_npc_class_change (bl, npc_class, type); + return 0; +} + +/*========================================== + * NPCから発生するエフェクト + * misceffect(effect, [target]) + * + * effect The effect type/ID. + * target The player name or being ID on + * which to display the effect. If not + * specified, it attempts to default to + * the current NPC or invoking PC. + *------------------------------------------ + */ +int buildin_misceffect (struct script_state *st) +{ + int type; + int id = 0; + char *name = NULL; + struct block_list *bl = NULL; + + type = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + if (st->end > st->start + 3) + { + struct script_data *sdata = &(st->stack->stack_data[st->start + 3]); + + get_val (st, sdata); + + if (sdata->type == C_STR || sdata->type == C_CONSTSTR) + name = conv_str (st, sdata); + else + id = conv_num (st, sdata); + } + + if (name) + { + struct map_session_data *sd = map_nick2sd (name); + if (sd) + bl = &sd->bl; + } + else if (id) + bl = map_id2bl (id); + else if (st->oid) + bl = map_id2bl (st->oid); + else + { + struct map_session_data *sd = script_rid2sd (st); + if (sd) + bl = &sd->bl; + } + + if (bl) + clif_misceffect (bl, type); + + return 0; +} + +/*========================================== + * サウンドエフェクト + *------------------------------------------ + */ +int buildin_soundeffect (struct script_state *st) +{ + 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; +} + +/*========================================== + * 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; + + if (sd == NULL) + return 0; + + for (i = 0; i < 11; i++) + if (sd->equip_index[i] >= 0) + pc_unequipitem (sd, sd->equip_index[i], i); + pc_calcstatus (sd, 0); + + return 0; +} + +/*========================================== + * UnequipById [Freeyorp] + *------------------------------------------ + */ + +int buildin_unequip_by_id (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + if (sd == NULL) + return 0; + + int slot_id = conv_num (st, &(st->stack->stack_data[st->start + 2])); + + if (slot_id >= 0 && slot_id < 11 && sd->equip_index[slot_id] >= 0) + pc_unequipitem (sd, sd->equip_index[slot_id], slot_id); + + pc_calcstatus (sd, 0); + + return 0; +} + +/*========================================== + * gmcommand [MouseJstr] + * + * suggested on the forums... + *------------------------------------------ + */ + +int buildin_gmcommand (struct script_state *st) +{ + struct map_session_data *sd; + char *cmd; + + sd = script_rid2sd (st); + cmd = conv_str (st, &(st->stack->stack_data[st->start + 2])); + + is_atcommand (sd->fd, sd, cmd, 99); + + 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; +} + +/*========================================== + * npcwarp [remoitnane] + * Move NPC to a new position on the same map. + *------------------------------------------ + */ +int buildin_npcwarp (struct script_state *st) +{ + int x, y; + char *npc; + struct npc_data *nd = NULL; + + x = conv_num (st, &(st->stack->stack_data[st->start + 2])); + y = conv_num (st, &(st->stack->stack_data[st->start + 3])); + npc = conv_str (st, &(st->stack->stack_data[st->start + 4])); + nd = npc_name2id (npc); + + if (!nd) + return -1; + + short m = nd->bl.m; + + /* Crude sanity checks. */ + if (m < 0 || !nd->bl.prev + || x < 0 || x > map[m].xs -1 + || y < 0 || y > map[m].ys - 1) + return -1; + + npc_enable (npc, 0); + map_delblock(&nd->bl); /* [Freeyorp] */ + nd->bl.x = x; + nd->bl.y = y; + map_addblock(&nd->bl); + npc_enable (npc, 1); + + return 0; +} + +/*========================================== + * message [MouseJstr] + *------------------------------------------ + */ + +int buildin_message (struct script_state *st) +{ + char *msg, *player; + struct map_session_data *pl_sd = NULL; + + 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 1; + 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, 24); + strcat (message, " : "); + strcat (message, str); + 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) + { + push_val (st->stack, C_INT, 1); + return 0; + } + } + + push_val (st->stack, C_INT, 0); + + 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*)calloc (24, 1); + strncpy (mapname, sd->status.save_point.map, 23); + push_str (st->stack, C_STR, mapname); + break; + case 1: + push_val (st->stack, C_INT, x); + break; + case 2: + push_val (st->stack, C_INT, y); + break; + } + return 0; +} + +/*========================================== + * areatimer + *------------------------------------------ + */ +int buildin_areatimer_sub (struct block_list *bl, va_list ap) +{ + int tick; + char *event; + tick = va_arg (ap, int); + event = va_arg (ap, char *); + pc_addeventtimer ((struct map_session_data *) bl, tick, event); + return 0; +} + +int buildin_areatimer (struct script_state *st) +{ + int tick, m; + char *event; + 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])); + tick = conv_num (st, &(st->stack->stack_data[st->start + 7])); + event = conv_str (st, &(st->stack->stack_data[st->start + 8])); + + if ((m = map_mapname2mapid (mapname)) < 0) + return 0; + + map_foreachinarea (buildin_areatimer_sub, + m, x0, y0, x1, y1, BL_PC, tick, event); + return 0; +} + +/*========================================== + * Check whether the PC is in the specified rectangle + *------------------------------------------ + */ +int buildin_isin (struct script_state *st) +{ + int x1, y1, x2, y2; + char *str; + struct map_session_data *sd = script_rid2sd (st); + + str = conv_str (st, &(st->stack->stack_data[st->start + 2])); + x1 = conv_num (st, &(st->stack->stack_data[st->start + 3])); + y1 = conv_num (st, &(st->stack->stack_data[st->start + 4])); + x2 = conv_num (st, &(st->stack->stack_data[st->start + 5])); + y2 = conv_num (st, &(st->stack->stack_data[st->start + 6])); + + if (!sd) + return 1; + + push_val (st->stack, C_INT, + (sd->bl.x >= x1 && sd->bl.x <= x2) + && (sd->bl.y >= y1 && sd->bl.y <= y2) + && (!strcmp (str, map[sd->bl.m].name))); + + return 0; +} + +// Trigger the shop on a (hopefully) nearby shop NPC +int buildin_shop (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + struct npc_data *nd; + + if (!sd) + return 1; + + nd = npc_name2id (conv_str (st, &(st->stack->stack_data[st->start + 2]))); + if (!nd) + return 1; + + buildin_close (st); + clif_npcbuysell (sd, nd->bl.id); + return 0; +} + +/*========================================== + * Check whether the PC is dead + *------------------------------------------ + */ +int buildin_isdead (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + + push_val (st->stack, C_INT, pc_isdead (sd)); + return 0; +} + +/*======================================== + * Changes a NPC name, and sprite + *---------------------------------------- + */ +int buildin_fakenpcname (struct script_state *st) +{ + char *name, *newname; + int newsprite; + struct npc_data *nd; + + name = conv_str (st, &(st->stack->stack_data[st->start + 2])); + newname = conv_str (st, &(st->stack->stack_data[st->start + 3])); + newsprite = conv_num (st, &(st->stack->stack_data[st->start + 4])); + nd = npc_name2id (name); + if (!nd) + return 1; + strncpy (nd->name, newname, sizeof(nd->name)-1); + nd->name[sizeof(nd->name)-1] = '\0'; + nd->npc_class = newsprite; + + // Refresh this npc + npc_enable (name, 0); + npc_enable (name, 1); + + return 0; +} + +/*============================ + * Gets the PC's x pos + *---------------------------- + */ + +int buildin_getx (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + + push_val (st->stack, C_INT, sd->bl.x); + return 0; +} + +/*============================ + * Gets the PC's y pos + *---------------------------- + */ +int buildin_gety (struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd (st); + + push_val (st->stack, C_INT, sd->bl.y); + return 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) + printf ("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 *) + calloc (strlen (st->stack->stack_data[st->stack->sp - 1].u.str) + + strlen (st->stack->stack_data[st->stack->sp].u.str) + 1, + 1); + 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) + free (st->stack->stack_data[st->stack->sp - 1].u.str); + if (st->stack->stack_data[st->stack->sp].type == C_STR) + free (st->stack->stack_data[st->stack->sp].u.str); + 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: + printf ("illegal string operater\n"); + break; + } + + push_val (st->stack, C_INT, a); + + if (st->stack->stack_data[sp1].type == C_STR) + free (s1); + if (st->stack->stack_data[sp2].type == C_STR) + free (s2); +} + +/*========================================== + * 二項演算子(数値) + *------------------------------------------ + */ +void op_2num (struct script_state *st, int op, int i1, int i2) +{ + switch (op) + { + case C_SUB: + i1 -= i2; + break; + case C_MUL: + i1 *= i2; + break; + case C_DIV: + i1 /= i2; + break; + case C_MOD: + i1 %= i2; + 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 + printf ("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) + printf ("function not found\n"); +// st->stack->sp=0; + st->state = END; + return 0; + } + 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) + { + printf ("run_func: not function and command! \n"); +// st->stack->sp=0; + st->state = END; + return 0; + } +#ifdef DEBUG_RUN + if (battle_config.etc_log) + { + printf ("run_func : %s? (%d(%d))\n", str_buf + str_data[func].str, + func, str_data[func].type); + printf ("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; + 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) + { + str_data[func].func (st); + } + else + { + if (battle_config.error_log) + printf ("run_func : %s? (%d(%d))\n", str_buf + str_data[func].str, + func, str_data[func].type); + push_val (st->stack, C_INT, 0); + } + + pop_stack (st->stack, start_sp, end_sp); + + if (st->state == RETFUNC) + { + // ユーザー定義関数からの復帰 + int olddefsp = st->defsp; + int i; + + pop_stack (st->stack, st->defsp, start_sp); // 復帰に邪魔なスタック削除 + if (st->defsp < 4 + || st->stack->stack_data[st->defsp - 1].type != C_RETINFO) + { + printf + ("script:run_func(return) return without callfunc or callsub!\n"); + st->state = END; + return 0; + } + i = conv_num (st, &(st->stack->stack_data[st->defsp - 4])); // 引数の数所得 + st->pos = conv_num (st, &(st->stack->stack_data[st->defsp - 1])); // スクリプト位置の復元 + st->script = (char *) conv_num (st, &(st->stack->stack_data[st->defsp - 2])); // スクリプトを復元 + st->defsp = conv_num (st, &(st->stack->stack_data[st->defsp - 3])); // 基準スタックポインタを復元 + + pop_stack (st->stack, olddefsp - 4 - i, olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 + + st->state = GOTO; + } + + return 0; +} + +/*========================================== + * スクリプトの実行メイン部分 + *------------------------------------------ + */ +int run_script_main (unsigned char *script, int pos, int rid, int oid, + struct script_state *st, unsigned char *rootscript) +{ + int c, rerun_pos; + int cmdcount = script_config.check_cmdcount; + int gotocount = script_config.check_gotocount; + struct script_stack *stack = st->stack; + + st->defsp = stack->sp; + st->script = script; + + rerun_pos = st->pos; + for (st->state = 0; st->state == 0;) + { + switch (c = get_com (script, &st->pos)) + { + case C_EOL: + if (stack->sp != st->defsp) + { + if (battle_config.error_log) + printf ("stack.sp(%d) != default(%d)\n", stack->sp, + st->defsp); + stack->sp = st->defsp; + } + rerun_pos = st->pos; + break; + case C_INT: + push_val (stack, C_INT, get_num (script, &st->pos)); + break; + case C_POS: + case C_NAME: + push_val (stack, c, (*(int *) (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, script + st->pos); + while (script[st->pos++]); + break; + case C_FUNC: + run_func (st); + if (st->state == GOTO) + { + rerun_pos = st->pos; + script = st->script; + st->state = 0; + if (gotocount > 0 && (--gotocount) <= 0) + { + printf ("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) + printf ("unknown command : %d @ %d\n", c, pos); + st->state = END; + break; + } + if (cmdcount > 0 && (--cmdcount) <= 0) + { + printf ("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: + { + st->pos = rerun_pos; + } + break; + } + + if (st->state != END) + { + // 再開するためにスタック情報を保存 + struct map_session_data *sd = map_id2sd (st->rid); + if (sd /* && sd->npc_stackbuf==NULL */ ) + { + if (sd->npc_stackbuf) + free (sd->npc_stackbuf); + sd->npc_stackbuf = (char *) + calloc (sizeof (stack->stack_data[0]) * stack->sp_max, 1); + memcpy (sd->npc_stackbuf, stack->stack_data, + sizeof (stack->stack_data[0]) * stack->sp_max); + sd->npc_stack = stack->sp; + sd->npc_stackmax = stack->sp_max; + sd->npc_script = script; + sd->npc_scriptroot = rootscript; + } + } + + return 0; +} + +/*========================================== + * スクリプトの実行 + *------------------------------------------ + */ +int run_script (unsigned char *script, int pos, int rid, int oid) +{ + return run_script_l (script, pos, rid, oid, 0, NULL); +} + +int run_script_l (unsigned char *script, int pos, int rid, int oid, + int args_nr, argrec_t * args) +{ + struct script_stack stack; + struct script_state st; + struct map_session_data *sd = map_id2sd (rid); + unsigned char *rootscript = script; + int i; + if (script == NULL || pos < 0) + return -1; + + if (sd && sd->npc_stackbuf && sd->npc_scriptroot == (char *) rootscript) + { + // 前回のスタックを復帰 + script = sd->npc_script; + stack.sp = sd->npc_stack; + stack.sp_max = sd->npc_stackmax; + stack.stack_data = (struct script_data *) + calloc (stack.sp_max, sizeof (stack.stack_data[0])); + memcpy (stack.stack_data, sd->npc_stackbuf, + sizeof (stack.stack_data[0]) * stack.sp_max); + free (sd->npc_stackbuf); + sd->npc_stackbuf = NULL; + } + else + { + // スタック初期化 + stack.sp = 0; + stack.sp_max = 64; + stack.stack_data = (struct script_data *) + calloc (stack.sp_max, sizeof (stack.stack_data[0])); + } + st.stack = &stack; + st.pos = pos; + st.rid = rid; + st.oid = oid; + for (i = 0; i < args_nr; i++) + { + if (args[i].name[strlen (args[i].name) - 1] == '$') + pc_setregstr (sd, add_str (args[i].name), args[i].v.s); + else + pc_setreg (sd, add_str (args[i].name), args[i].v.i); + } + run_script_main (script, pos, rid, oid, &st, rootscript); + + free (stack.stack_data); + stack.stack_data = NULL; + return st.pos; +} + +/*========================================== + * マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setreg (int num, int val) +{ + if (val != 0) + numdb_insert (mapreg_db, num, val); + else + numdb_erase (mapreg_db, num); + + mapreg_dirty = 1; + return 0; +} + +/*========================================== + * 文字列型マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setregstr (int num, const char *str) +{ + char *p; + + if ((p = (char *)numdb_search (mapregstr_db, num)) != NULL) + free (p); + + if (str == NULL || *str == 0) + { + numdb_erase (mapregstr_db, num); + mapreg_dirty = 1; + return 0; + } + p = (char *) calloc (strlen (str) + 1, 1); + strcpy (p, str); + numdb_insert (mapregstr_db, num, p); + mapreg_dirty = 1; + return 0; +} + +/*========================================== + * 永続的マップ変数の読み込み + *------------------------------------------ + */ +static int script_load_mapreg (void) +{ + 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) + { + printf ("%s: %s broken data !\n", mapreg_txt, buf1); + continue; + } + p = (char *) calloc (strlen (buf2) + 1, 1); + strcpy (p, buf2); + s = add_str (buf1); + numdb_insert (mapregstr_db, (i << 24) | s, p); + } + else + { + if (sscanf (line + n, "%d", &v) != 1) + { + printf ("%s: %s broken data !\n", mapreg_txt, buf1); + continue; + } + s = add_str (buf1); + numdb_insert (mapreg_db, (i << 24) | s, v); + } + } + fclose_ (fp); + mapreg_dirty = 0; + return 0; +} + +/*========================================== + * 永続的マップ変数の書き込み + *------------------------------------------ + */ +static void script_save_mapreg_intsub (db_key_t key, db_val_t data, va_list ap) +{ + 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); + } +} + +static void script_save_mapreg_strsub (db_key_t key, db_val_t data, va_list ap) +{ + 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); + } +} + +static int script_save_mapreg (void) +{ + FILE *fp; + int lock; + + if ((fp = lock_fopen (mapreg_txt, &lock)) == NULL) + return -1; + numdb_foreach (mapreg_db, script_save_mapreg_intsub, fp); + numdb_foreach (mapregstr_db, script_save_mapreg_strsub, fp); + lock_fclose (fp, mapreg_txt, &lock); + mapreg_dirty = 0; + return 0; +} + +static void script_autosave_mapreg (timer_id tid, tick_t tick, custom_id_t id, + custom_data_t data) +{ + if (mapreg_dirty) + script_save_mapreg (); +} + +/*========================================== + * + *------------------------------------------ + */ +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 (char *cfgName) +{ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + 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 = 8192; + script_config.check_gotocount = 512; + + fp = fopen_ (cfgName, "r"); + if (fp == NULL) + { + printf ("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 (strcasecmp (w1, "refine_posword") == 0) + { + set_posword (w2); + } + if (strcasecmp (w1, "import") == 0) + { + script_config_read (w2); + } + } + fclose_ (fp); + + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ + +static void mapregstr_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +static void userfunc_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free ((char*)key.s); + free (data); +} + +int do_final_script (void) +{ + if (mapreg_dirty >= 0) + script_save_mapreg (); + if (script_buf) + free (script_buf); + + if (mapreg_db) + numdb_final (mapreg_db, NULL); + if (mapregstr_db) + strdb_final (mapregstr_db, mapregstr_db_final); + if (scriptlabel_db) + strdb_final (scriptlabel_db, NULL); + if (userfunc_db) + strdb_final (userfunc_db, userfunc_db_final); + + if (str_data) + free (str_data); + if (str_buf) + free (str_buf); + + return 0; +} + +/*========================================== + * 初期化 + *------------------------------------------ + */ +int do_init_script (void) +{ + mapreg_db = numdb_init (); + mapregstr_db = numdb_init (); + script_load_mapreg (); + + add_timer_interval (gettick () + MAPREG_AUTOSAVE_INTERVAL, + script_autosave_mapreg, 0, 0, + MAPREG_AUTOSAVE_INTERVAL); + + scriptlabel_db = strdb_init (50); + return 0; +} diff --git a/src/map/script.h b/src/map/script.h deleted file mode 100644 index 62d3ee1..0000000 --- a/src/map/script.h +++ /dev/null @@ -1,52 +0,0 @@ -// $Id: script.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ -#ifndef _SCRIPT_H_ -#define _SCRIPT_H_ - -struct script_data -{ - int type; - union - { - int num; - char *str; - } u; -}; - -struct script_stack -{ - int sp, sp_max; - struct script_data *stack_data; -}; -struct script_state -{ - struct script_stack *stack; - int start, end; - int pos, state; - int rid, oid; - char *script, *new_script; - int defsp, new_pos, new_defsp; -}; - -unsigned char *parse_script (unsigned char *, int); -typedef struct argrec -{ - char *name; - union - { - int i; - char *s; - } v; -} argrec_t; -int run_script_l (unsigned char *, int, int, int, int, argrec_t * args); -int run_script (unsigned char *, int, int, int); - -struct dbt *script_get_label_db (void); -struct dbt *script_get_userfunc_db (void); - -int script_config_read (char *cfgName); -int do_init_script (void); -int do_final_script (void); - -extern char mapreg_txt[]; - -#endif diff --git a/src/map/script.hpp b/src/map/script.hpp new file mode 100644 index 0000000..a96bc5a --- /dev/null +++ b/src/map/script.hpp @@ -0,0 +1,52 @@ +// $Id: script.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef SCRIPT_HPP +#define SCRIPT_HPP + +struct script_data +{ + int type; + union + { + int num; + char *str; + } u; +}; + +struct script_stack +{ + int sp, sp_max; + struct script_data *stack_data; +}; +struct script_state +{ + struct script_stack *stack; + int start, end; + int pos, state; + int rid, oid; + char *script, *new_script; + int defsp, new_pos, new_defsp; +}; + +unsigned char *parse_script (unsigned char *, int); +typedef struct argrec +{ + char *name; + union + { + int i; + char *s; + } v; +} argrec_t; +int run_script_l (unsigned char *, int, int, int, int, argrec_t * args); +int run_script (unsigned char *, int, int, int); + +struct dbt *script_get_label_db (void); +struct dbt *script_get_userfunc_db (void); + +int script_config_read (char *cfgName); +int do_init_script (void); +int do_final_script (void); + +extern char mapreg_txt[]; + +#endif diff --git a/src/map/skill-pools.c b/src/map/skill-pools.c deleted file mode 100644 index 0c91360..0000000 --- a/src/map/skill-pools.c +++ /dev/null @@ -1,158 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/mt_rand.h" -#include "magic.h" - -#include "battle.h" -#include "clif.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "../common/socket.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -int skill_pool_skills[MAX_POOL_SKILLS]; -int skill_pool_skills_size = 0; - -extern void skill_pool_register (int id) -{ - if (skill_pool_skills_size + 1 >= MAX_POOL_SKILLS) - { - fprintf (stderr, - "Too many pool skills! Increase MAX_POOL_SKILLS and recompile."); - return; - } - - skill_pool_skills[skill_pool_skills_size++] = id; -} - -char *skill_name (int skill) -{ - if (skill > 0 && skill < MAX_SKILL_DB) - return skill_names[skill].desc; - else - return NULL; -} - -int skill_pool (struct map_session_data *sd, int *skills) -{ - int i, count = 0; - - for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills_size; i++) - { - int skill_id = skill_pool_skills[i]; - if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) - { - if (skills) - skills[count] = skill_id; - ++count; - } - } - - return count; -} - -void skill_pool_empty (struct map_session_data *sd) -{ - int i; - - for (i = 0; i < skill_pool_skills_size; i++) - { - int skill_id = skill_pool_skills[i]; - sd->status.skill[skill_id].flags = 0; - } -} - -int skill_pool_size (struct map_session_data *sd) -{ - return skill_pool (sd, NULL); -} - -int skill_pool_max (struct map_session_data *sd) -{ - return sd->status.skill[TMW_SKILLPOOL].lv; -} - -int skill_pool_activate (struct map_session_data *sd, int skill_id) -{ - if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) - return 0; // Already there - else if (sd->status.skill[skill_id].id == skill_id // knows the skill - && (skill_pool_size (sd) < skill_pool_max (sd))) - { - sd->status.skill[skill_id].flags |= SKILL_POOL_ACTIVATED; - pc_calcstatus (sd, 0); - MAP_LOG_PC (sd, "SKILL-ACTIVATE %d %d %d", skill_id, - sd->status.skill[skill_id].lv, skill_power (sd, - skill_id)); - return 0; - } - - return 1; // failed -} - -int skill_pool_is_activated (struct map_session_data *sd, int skill_id) -{ - return sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED; -} - -int skill_pool_deactivate (struct map_session_data *sd, int skill_id) -{ - if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) - { - sd->status.skill[skill_id].flags &= ~SKILL_POOL_ACTIVATED; - MAP_LOG_PC (sd, "SKILL-DEACTIVATE %d", skill_id); - pc_calcstatus (sd, 0); - return 0; - } - - return 1; -} - -int skill_stat (int skill_id) -{ - return skill_db[skill_id].stat; -} - -int skill_power (struct map_session_data *sd, int skill_id) -{ - int stat = skill_stat (skill_id); - int stat_value, skill_value; - int result; - - if (stat == 0 || !skill_pool_is_activated (sd, skill_id)) - return 0; - - stat_value = battle_get_stat (stat, &(sd->bl)); - skill_value = sd->status.skill[skill_id].lv; - - if ((skill_value * 10) - 1 > stat_value) - skill_value += (stat_value / 10); - else - skill_value *= 2; - - result = (skill_value * stat_value) / 10; - - return result; -} - -int skill_power_bl (struct block_list *bl, int skill) -{ - if (bl->type == BL_PC) - return skill_power ((struct map_session_data *) bl, skill); - else - return 0; -} diff --git a/src/map/skill-pools.cpp b/src/map/skill-pools.cpp new file mode 100644 index 0000000..6ab6ea5 --- /dev/null +++ b/src/map/skill-pools.cpp @@ -0,0 +1,158 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "../common/timer.hpp" +#include "../common/nullpo.hpp" +#include "../common/mt_rand.hpp" +#include "magic.hpp" + +#include "battle.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "../common/socket.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +int skill_pool_skills[MAX_POOL_SKILLS]; +int skill_pool_skills_size = 0; + +extern void skill_pool_register (int id) +{ + if (skill_pool_skills_size + 1 >= MAX_POOL_SKILLS) + { + fprintf (stderr, + "Too many pool skills! Increase MAX_POOL_SKILLS and recompile."); + return; + } + + skill_pool_skills[skill_pool_skills_size++] = id; +} + +char *skill_name (int skill) +{ + if (skill > 0 && skill < MAX_SKILL_DB) + return skill_names[skill].desc; + else + return NULL; +} + +int skill_pool (struct map_session_data *sd, int *skills) +{ + int i, count = 0; + + for (i = 0; count < MAX_SKILL_POOL && i < skill_pool_skills_size; i++) + { + int skill_id = skill_pool_skills[i]; + if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) + { + if (skills) + skills[count] = skill_id; + ++count; + } + } + + return count; +} + +void skill_pool_empty (struct map_session_data *sd) +{ + int i; + + for (i = 0; i < skill_pool_skills_size; i++) + { + int skill_id = skill_pool_skills[i]; + sd->status.skill[skill_id].flags = 0; + } +} + +int skill_pool_size (struct map_session_data *sd) +{ + return skill_pool (sd, NULL); +} + +int skill_pool_max (struct map_session_data *sd) +{ + return sd->status.skill[TMW_SKILLPOOL].lv; +} + +int skill_pool_activate (struct map_session_data *sd, int skill_id) +{ + if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) + return 0; // Already there + else if (sd->status.skill[skill_id].id == skill_id // knows the skill + && (skill_pool_size (sd) < skill_pool_max (sd))) + { + sd->status.skill[skill_id].flags |= SKILL_POOL_ACTIVATED; + pc_calcstatus (sd, 0); + MAP_LOG_PC (sd, "SKILL-ACTIVATE %d %d %d", skill_id, + sd->status.skill[skill_id].lv, skill_power (sd, + skill_id)); + return 0; + } + + return 1; // failed +} + +int skill_pool_is_activated (struct map_session_data *sd, int skill_id) +{ + return sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED; +} + +int skill_pool_deactivate (struct map_session_data *sd, int skill_id) +{ + if (sd->status.skill[skill_id].flags & SKILL_POOL_ACTIVATED) + { + sd->status.skill[skill_id].flags &= ~SKILL_POOL_ACTIVATED; + MAP_LOG_PC (sd, "SKILL-DEACTIVATE %d", skill_id); + pc_calcstatus (sd, 0); + return 0; + } + + return 1; +} + +int skill_stat (int skill_id) +{ + return skill_db[skill_id].stat; +} + +int skill_power (struct map_session_data *sd, int skill_id) +{ + int stat = skill_stat (skill_id); + int stat_value, skill_value; + int result; + + if (stat == 0 || !skill_pool_is_activated (sd, skill_id)) + return 0; + + stat_value = battle_get_stat (stat, &(sd->bl)); + skill_value = sd->status.skill[skill_id].lv; + + if ((skill_value * 10) - 1 > stat_value) + skill_value += (stat_value / 10); + else + skill_value *= 2; + + result = (skill_value * stat_value) / 10; + + return result; +} + +int skill_power_bl (struct block_list *bl, int skill) +{ + if (bl->type == BL_PC) + return skill_power ((struct map_session_data *) bl, skill); + else + return 0; +} diff --git a/src/map/skill.c b/src/map/skill.c deleted file mode 100644 index e232820..0000000 --- a/src/map/skill.c +++ /dev/null @@ -1,12279 +0,0 @@ -// $Id: skill.c,v 1.8 2004/09/25 05:32:19 MouseJstr Exp $ -/* スキル関係 */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/mt_rand.h" -#include "magic.h" - -#include "battle.h" -#include "clif.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "mob.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "../common/socket.h" - -#ifdef MEMWATCH -#include "memwatch.h" -#endif - -#define SKILLUNITTIMER_INVERVAL 100 - -#define STATE_BLIND 0x10 - -/* スキル番号=>ステータス異常番号変換テーブル */ -int SkillStatusChangeTable[] = { /* skill.hのenumのSC_***とあわせること */ -/* 0- */ - -1, -1, -1, -1, -1, -1, - SC_PROVOKE, /* プロボック */ - -1, 1, -1, -/* 10- */ - SC_SIGHT, /* サイト */ - -1, -1, -1, -1, - SC_FREEZE, /* フロストダイバー */ - SC_STONE, /* ストーンカース */ - -1, -1, -1, -/* 20- */ - -1, -1, -1, -1, - SC_RUWACH, /* ルアフ */ - -1, -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_DIVINA, /* レックスディビーナ */ - -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, -1, -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, - SC_SELFDESTRUCTION, - -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, - SC_KEEPING, - -1, -1, - SC_BARRIER, - -1, -1, - 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_FLAMELAUNCHER, - SC_FROSTWEAPON, - SC_LIGHTNINGLOADER, - SC_SEISMICWEAPON, - -1, - SC_VOLCANO, - SC_DELUGE, - SC_VIOLENTGALE, - SC_LANDPROTECTOR, - -1, -/* 290- */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 300- */ - -1, -1, -1, -1, -1, -1, - SC_LULLABY, - SC_RICHMANKIM, - SC_ETERNALCHAOS, - SC_DRUMBATTLE, -/* 310- */ - SC_NIBELUNGEN, - SC_ROKISWEIL, - SC_INTOABYSS, - SC_SIEGFRIED, - -1, -1, -1, - SC_DISSONANCE, - -1, - SC_WHISTLE, -/* 320- */ - SC_ASSNCROS, - SC_POEMBRAGI, - SC_APPLEIDUN, - -1, -1, - SC_UGLYDANCE, - -1, - SC_HUMMING, - SC_DONTFORGETME, - SC_FORTUNE, -/* 330- */ - SC_SERVICE4U, - SC_SELFDESTRUCTION, - -1, -1, -1, -1, -1, -1, -1, -1, -/* 340- */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 350- */ - -1, -1, -1, -1, -1, - SC_AURABLADE, - SC_PARRYING, - SC_CONCENTRATION, - SC_TENSIONRELAX, - SC_BERSERK, -/* 360- */ - SC_BERSERK, - SC_ASSUMPTIO, - SC_BASILICA, - -1, -1, -1, - SC_MAGICPOWER, - -1, -1, - SC_GOSPEL, -/* 370- */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 380- */ - SC_TRUESIGHT, - -1, -1, - SC_WINDWALK, - SC_MELTDOWN, - -1, -1, - SC_CARTBOOST, - -1, - SC_CHASEWALK, -/* 390- */ - SC_REJECTSWORD, - -1, -1, -1, -1, -1, - SC_MARIONETTE, - -1, - SC_HEADCRUSH, - SC_JOINTBEAT, -/* 400 */ - -1, -1, - SC_MINDBREAKER, - SC_MEMORIZE, - SC_FOGWALL, - SC_SPIDERWEB, - -1, -1, -1, -1, -/* 410- */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; - -struct skill_name_db skill_names[] = { - {AC_CHARGEARROW, "CHARGEARROW", "Charge_Arrow"}, - {AC_CONCENTRATION, "CONCENTRATION", "Improve_Concentration"}, - {AC_DOUBLE, "DOUBLE", "Double_Strafe"}, - {AC_MAKINGARROW, "MAKINGARROW", "Arrow_Creation"}, - {AC_OWL, "OWL", "Owl's_Eye"}, - {AC_SHOWER, "SHOWER", "Arrow_Shower"}, - {AC_VULTURE, "VULTURE", "Vulture's_Eye"}, - {ALL_RESURRECTION, "RESURRECTION", "Resurrection"}, - {AL_ANGELUS, "ANGELUS", "Angelus"}, - {AL_BLESSING, "BLESSING", "Blessing"}, - {AL_CRUCIS, "CRUCIS", "Signum_Crusis"}, - {AL_CURE, "CURE", "Cure"}, - {AL_DECAGI, "DECAGI", "Decrease_AGI"}, - {AL_DEMONBANE, "DEMONBANE", "Demon_Bane"}, - {AL_DP, "DP", "Divine_Protection"}, - {AL_HEAL, "HEAL", "Heal"}, - {AL_HOLYLIGHT, "HOLYLIGHT", "Holy_Light"}, - {AL_HOLYWATER, "HOLYWATER", "Aqua_Benedicta"}, - {AL_INCAGI, "INCAGI", "Increase_AGI"}, - {AL_PNEUMA, "PNEUMA", "Pneuma"}, - {AL_RUWACH, "RUWACH", "Ruwach"}, - {AL_TELEPORT, "TELEPORT", "Teleport"}, - {AL_WARP, "WARP", "Warp_Portal"}, - {AM_ACIDTERROR, "ACIDTERROR", "Acid_Terror"}, - {AM_AXEMASTERY, "AXEMASTERY", "Axe_Mastery"}, - {AM_BERSERKPITCHER, "BERSERKPITCHER", "Berserk Pitcher"}, - {AM_BIOETHICS, "BIOETHICS", "Bioethics"}, - {AM_BIOTECHNOLOGY, "BIOTECHNOLOGY", "Biotechnology"}, - {AM_CALLHOMUN, "CALLHOMUN", "Call_Homunculus"}, - {AM_CANNIBALIZE, "CANNIBALIZE", "Bio_Cannibalize"}, - {AM_CP_ARMOR, "ARMOR", "Chemical_Protection_Armor"}, - {AM_CP_HELM, "HELM", "Chemical_Protection_Helm"}, - {AM_CP_SHIELD, "SHIELD", "Chemical_Protection_Shield"}, - {AM_CP_WEAPON, "WEAPON", "Chemical_Protection_Weapon"}, - {AM_CREATECREATURE, "CREATECREATURE", "Life_Creation"}, - {AM_CULTIVATION, "CULTIVATION", "Cultivation"}, - {AM_DEMONSTRATION, "DEMONSTRATION", "Demonstration"}, - {AM_DRILLMASTER, "DRILLMASTER", "Drillmaster"}, - {AM_FLAMECONTROL, "FLAMECONTROL", "Flame_Control"}, - {AM_HEALHOMUN, "HEALHOMUN", "Heal_Homunculus"}, - {AM_LEARNINGPOTION, "LEARNINGPOTION", "AM_LEARNINGPOTION"}, - {AM_PHARMACY, "PHARMACY", "Pharmacy"}, - {AM_POTIONPITCHER, "POTIONPITCHER", "Potion_Pitcher"}, - {AM_REST, "REST", "Sabbath"}, - {AM_RESURRECTHOMUN, "RESURRECTHOMUN", "Ressurect_Homunculus"}, - {AM_SPHEREMINE, "SPHEREMINE", "Sphere_Mine"}, - {ASC_BREAKER, "BREAKER", "Breaker"}, - {ASC_CDP, "CDP", "Create_Deadly_Poison"}, - {ASC_EDP, "EDP", "Deadly_Poison_Enchantment"}, - {ASC_HALLUCINATION, "HALLUCINATION", "Hallucination_Walk"}, - {ASC_KATAR, "KATAR", "Advanced_Katar_Mastery"}, - {ASC_METEORASSAULT, "METEORASSAULT", "Meteor_Assault"}, - {AS_CLOAKING, "CLOAKING", "Cloaking"}, - {AS_ENCHANTPOISON, "ENCHANTPOISON", "Enchant_Poison"}, - {AS_GRIMTOOTH, "GRIMTOOTH", "Grimtooth"}, - {AS_KATAR, "KATAR", "Katar_Mastery"}, - {AS_LEFT, "LEFT", "Lefthand_Mastery"}, - {AS_POISONREACT, "POISONREACT", "Poison_React"}, - {AS_RIGHT, "RIGHT", "Righthand_Mastery"}, - {AS_SONICBLOW, "SONICBLOW", "Sonic_Blow"}, - {AS_SPLASHER, "SPLASHER", "Venom_Splasher"}, - {AS_VENOMDUST, "VENOMDUST", "Venom_Dust"}, - {BA_APPLEIDUN, "APPLEIDUN", "Apple_of_Idun"}, - {BA_ASSASSINCROSS, "ASSASSINCROSS", "Assassin_Cross"}, - {BA_DISSONANCE, "DISSONANCE", "Dissonance"}, - {BA_FROSTJOKE, "FROSTJOKE", "Dumb_Joke"}, - {BA_MUSICALLESSON, "MUSICALLESSON", "Musical_Lesson"}, - {BA_MUSICALSTRIKE, "MUSICALSTRIKE", "Musical_Strike"}, - {BA_POEMBRAGI, "POEMBRAGI", "Poem_of_Bragi"}, - {BA_WHISTLE, "WHISTLE", "Whistle"}, - {BD_ADAPTATION, "ADAPTATION", "Adaption"}, - {BD_DRUMBATTLEFIELD, "DRUMBATTLEFIELD", "Drumb_BattleField"}, - {BD_ENCORE, "ENCORE", "Encore"}, - {BD_ETERNALCHAOS, "ETERNALCHAOS", "Eternal_Chaos"}, - {BD_INTOABYSS, "INTOABYSS", "Into_the_Abyss"}, - {BD_LULLABY, "LULLABY", "Lullaby"}, - {BD_RAGNAROK, "RAGNAROK", "Ragnarok"}, - {BD_RICHMANKIM, "RICHMANKIM", "Rich_Mankim"}, - {BD_RINGNIBELUNGEN, "RINGNIBELUNGEN", "Ring_of_Nibelugen"}, - {BD_ROKISWEIL, "ROKISWEIL", "Loki's_Wail"}, - {BD_SIEGFRIED, "SIEGFRIED", "Invulnerable_Siegfried"}, - {BS_ADRENALINE, "ADRENALINE", "Adrenaline_Rush"}, - {BS_ADRENALINE2, "ADRENALINE2", "Adrenaline Rush 2"}, - {BS_AXE, "AXE", "Smith_Axe"}, - {BS_DAGGER, "DAGGER", "Smith_Dagger"}, - {BS_ENCHANTEDSTONE, "ENCHANTEDSTONE", "Enchantedstone_Craft"}, - {BS_FINDINGORE, "FINDINGORE", "Ore_Discovery"}, - {BS_HAMMERFALL, "HAMMERFALL", "Hammer_Fall"}, - {BS_HILTBINDING, "HILTBINDING", "Hilt_Binding"}, - {BS_IRON, "IRON", "Iron_Tempering"}, - {BS_KNUCKLE, "KNUCKLE", "Smith_Knucklebrace"}, - {BS_MACE, "MACE", "Smith_Mace"}, - {BS_MAXIMIZE, "MAXIMIZE", "Power_Maximize"}, - {BS_ORIDEOCON, "ORIDEOCON", "Orideocon_Research"}, - {BS_OVERTHRUST, "OVERTHRUST", "Power-Thrust"}, - {BS_REPAIRWEAPON, "REPAIRWEAPON", "Weapon_Repair"}, - {BS_SKINTEMPER, "SKINTEMPER", "Skin_Tempering"}, - {BS_SPEAR, "SPEAR", "Smith_Spear"}, - {BS_STEEL, "STEEL", "Steel_Tempering"}, - {BS_SWORD, "SWORD", "Smith_Sword"}, - {BS_TWOHANDSWORD, "TWOHANDSWORD", "Smith_Two-handed_Sword"}, - {BS_WEAPONPERFECT, "WEAPONPERFECT", "Weapon_Perfection"}, - {BS_WEAPONRESEARCH, "WEAPONRESEARCH", "Weaponry_Research"}, - {CG_ARROWVULCAN, "ARROWVULCAN", "Vulcan_Arrow"}, - {CG_MARIONETTE, "MARIONETTE", "Marionette_Control"}, - {CG_MOONLIT, "MOONLIT", "Moonlight_Petals"}, - {CH_CHAINCRUSH, "CHAINCRUSH", "Chain_Crush_Combo"}, - {CH_PALMSTRIKE, "PALMSTRIKE", "Palm_Push_Strike"}, - {CH_SOULCOLLECT, "SOULCOLLECT", "Collect_Soul"}, - {CH_TIGERFIST, "TIGERFIST", "Tiger_Knuckle_Fist"}, - {CR_ALCHEMY, "ALCHEMY", "Alchemy"}, - {CR_AUTOGUARD, "AUTOGUARD", "Guard"}, - {CR_DEFENDER, "DEFENDER", "Defender"}, - {CR_DEVOTION, "DEVOTION", "Sacrifice"}, - {CR_GRANDCROSS, "GRANDCROSS", "Grand_Cross"}, - {CR_HOLYCROSS, "HOLYCROSS", "Holy_Cross"}, - {CR_PROVIDENCE, "PROVIDENCE", "Providence"}, - {CR_REFLECTSHIELD, "REFLECTSHIELD", "Shield_Reflect"}, - {CR_SHIELDBOOMERANG, "SHIELDBOOMERANG", "Shield_Boomerang"}, - {CR_SHIELDCHARGE, "SHIELDCHARGE", "Shield_Charge"}, - {CR_SPEARQUICKEN, "SPEARQUICKEN", "Spear_Quicken"}, - {CR_SYNTHESISPOTION, "SYNTHESISPOTION", "Potion_Synthesis"}, - {CR_TRUST, "TRUST", "Faith"}, - {DC_DANCINGLESSON, "DANCINGLESSON", "Dancing_Lesson"}, - {DC_DONTFORGETME, "DONTFORGETME", "Don't_Forget_Me"}, - {DC_FORTUNEKISS, "FORTUNEKISS", "Fortune_Kiss"}, - {DC_HUMMING, "HUMMING", "Humming"}, - {DC_SCREAM, "SCREAM", "Scream"}, - {DC_SERVICEFORYOU, "SERVICEFORYOU", "Prostitute"}, - {DC_THROWARROW, "THROWARROW", "Throw_Arrow"}, - {DC_UGLYDANCE, "UGLYDANCE", "Ugly_Dance"}, - {HP_ASSUMPTIO, "ASSUMPTIO", "Assumptio"}, - {HP_BASILICA, "BASILICA", "Basilica"}, - {HP_MEDITATIO, "MEDITATIO", "Meditation"}, - {HT_ANKLESNARE, "ANKLESNARE", "Ankle_Snare"}, - {HT_BEASTBANE, "BEASTBANE", "Beast_Bane"}, - {HT_BLASTMINE, "BLASTMINE", "Blast_Mine"}, - {HT_BLITZBEAT, "BLITZBEAT", "Blitz_Beat"}, - {HT_CLAYMORETRAP, "CLAYMORETRAP", "Claymore_Trap"}, - {HT_DETECTING, "DETECTING", "Detect"}, - {HT_FALCON, "FALCON", "Falconry_Mastery"}, - {HT_FLASHER, "FLASHER", "Flasher"}, - {HT_FREEZINGTRAP, "FREEZINGTRAP", "Freezing_Trap"}, - {HT_LANDMINE, "LANDMINE", "Land_Mine"}, - {HT_REMOVETRAP, "REMOVETRAP", "Remove_Trap"}, - {HT_SANDMAN, "SANDMAN", "Sandman"}, - {HT_SHOCKWAVE, "SHOCKWAVE", "Shockwave_Trap"}, - {HT_SKIDTRAP, "SKIDTRAP", "Skid_Trap"}, - {HT_SPRINGTRAP, "SPRINGTRAP", "Spring_Trap"}, - {HT_STEELCROW, "STEELCROW", "Steel_Crow"}, - {HT_TALKIEBOX, "TALKIEBOX", "Talkie_Box"}, - {HW_MAGICCRASHER, "MAGICCRASHER", "Magic_Crasher"}, - {HW_MAGICPOWER, "MAGICPOWER", "Magic_Power"}, - {HW_NAPALMVULCAN, "NAPALMVULCAN", "Napalm_Vulcan"}, - {HW_SOULDRAIN, "SOULDRAIN", "Soul_Drain"}, - {KN_AUTOCOUNTER, "AUTOCOUNTER", "Counter_Attack"}, - {KN_BOWLINGBASH, "BOWLINGBASH", "Bowling_Bash"}, - {KN_BRANDISHSPEAR, "BRANDISHSPEAR", "Brandish_Spear"}, - {KN_CAVALIERMASTERY, "CAVALIERMASTERY", "Cavalier_Mastery"}, - {KN_PIERCE, "PIERCE", "Pierce"}, - {KN_RIDING, "RIDING", "Peco_Peco_Ride"}, - {KN_SPEARBOOMERANG, "SPEARBOOMERANG", "Spear_Boomerang"}, - {KN_SPEARMASTERY, "SPEARMASTERY", "Spear_Mastery"}, - {KN_SPEARSTAB, "SPEARSTAB", "Spear_Stab"}, - {KN_TWOHANDQUICKEN, "TWOHANDQUICKEN", "Twohand_Quicken"}, - {LK_AURABLADE, "AURABLADE", "Aura_Blade"}, - {LK_BERSERK, "BERSERK", "Berserk"}, - {LK_CONCENTRATION, "CONCENTRATION", "Concentration"}, - {LK_FURY, "FURY", "LK_FURY"}, - {LK_HEADCRUSH, "HEADCRUSH", "Head_Crusher"}, - {LK_JOINTBEAT, "JOINTBEAT", "Joint_Beat"}, - {LK_PARRYING, "PARRYING", "Parrying"}, - {LK_SPIRALPIERCE, "SPIRALPIERCE", "Spiral_Pierce"}, - {LK_TENSIONRELAX, "TENSIONRELAX", "Tension_Relax"}, - {MC_CARTREVOLUTION, "CARTREVOLUTION", "Cart_Revolution"}, - {MC_CHANGECART, "CHANGECART", "Change_Cart"}, - {MC_DISCOUNT, "DISCOUNT", "Discount"}, - {MC_IDENTIFY, "IDENTIFY", "Item_Appraisal"}, - {MC_INCCARRY, "INCCARRY", "Enlarge_Weight_Limit"}, - {MC_LOUD, "LOUD", "Lord_Exclamation"}, - {MC_MAMMONITE, "MAMMONITE", "Mammonite"}, - {MC_OVERCHARGE, "OVERCHARGE", "Overcharge"}, - {MC_PUSHCART, "PUSHCART", "Pushcart"}, - {MG_COLDBOLT, "COLDBOLT", "Cold_Bolt"}, - {MG_ENERGYCOAT, "ENERGYCOAT", "Energy_Coat"}, - {MG_FIREBALL, "FIREBALL", "Fire_Ball"}, - {MG_FIREBOLT, "FIREBOLT", "Fire_Bolt"}, - {MG_FIREWALL, "FIREWALL", "Fire_Wall"}, - {MG_FROSTDIVER, "FROSTDIVER", "Frost_Diver"}, - {MG_LIGHTNINGBOLT, "LIGHTNINGBOLT", "Lightening_Bolt"}, - {MG_NAPALMBEAT, "NAPALMBEAT", "Napalm_Beat"}, - {MG_SAFETYWALL, "SAFETYWALL", "Safety_Wall"}, - {MG_SIGHT, "SIGHT", "Sight"}, - {MG_SOULSTRIKE, "SOULSTRIKE", "Soul_Strike"}, - {MG_SRECOVERY, "SRECOVERY", "Increase_SP_Recovery"}, - {MG_STONECURSE, "STONECURSE", "Stone_Curse"}, - {MG_THUNDERSTORM, "THUNDERSTORM", "Thunderstorm"}, - {MO_ABSORBSPIRITS, "ABSORBSPIRITS", "Absorb_Spirits"}, - {MO_BLADESTOP, "BLADESTOP", "Blade_Stop"}, - {MO_BODYRELOCATION, "BODYRELOCATION", "Body_Relocation"}, - {MO_CALLSPIRITS, "CALLSPIRITS", "Call_Spirits"}, - {MO_CHAINCOMBO, "CHAINCOMBO", "Chain_Combo"}, - {MO_COMBOFINISH, "COMBOFINISH", "Combo_Finish"}, - {MO_DODGE, "DODGE", "Dodge"}, - {MO_EXPLOSIONSPIRITS, "EXPLOSIONSPIRITS", "Explosion_Spirits"}, - {MO_EXTREMITYFIST, "EXTREMITYFIST", "Extremity_Fist"}, - {MO_FINGEROFFENSIVE, "FINGEROFFENSIVE", "Finger_Offensive"}, - {MO_INVESTIGATE, "INVESTIGATE", "Investigate"}, - {MO_IRONHAND, "IRONHAND", "Iron_Hand"}, - {MO_SPIRITSRECOVERY, "SPIRITSRECOVERY", "Spirit_Recovery"}, - {MO_STEELBODY, "STEELBODY", "Steel_Body"}, - {MO_TRIPLEATTACK, "TRIPLEATTACK", "Triple_Blows"}, - {NPC_ATTRICHANGE, "ATTRICHANGE", "NPC_ATTRICHANGE"}, - {NPC_BARRIER, "BARRIER", "NPC_BARRIER"}, - {NPC_BLINDATTACK, "BLINDATTACK", "NPC_BLINDATTACK"}, - {NPC_BLOODDRAIN, "BLOODDRAIN", "NPC_BLOODDRAIN"}, - {NPC_CHANGEDARKNESS, "CHANGEDARKNESS", "NPC_CHANGEDARKNESS"}, - {NPC_CHANGEFIRE, "CHANGEFIRE", "NPC_CHANGEFIRE"}, - {NPC_CHANGEGROUND, "CHANGEGROUND", "NPC_CHANGEGROUND"}, - {NPC_CHANGEHOLY, "CHANGEHOLY", "NPC_CHANGEHOLY"}, - {NPC_CHANGEPOISON, "CHANGEPOISON", "NPC_CHANGEPOISON"}, - {NPC_CHANGETELEKINESIS, "CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS"}, - {NPC_CHANGEWATER, "CHANGEWATER", "NPC_CHANGEWATER"}, - {NPC_CHANGEWIND, "CHANGEWIND", "NPC_CHANGEWIND"}, - {NPC_COMBOATTACK, "COMBOATTACK", "NPC_COMBOATTACK"}, - {NPC_CRITICALSLASH, "CRITICALSLASH", "NPC_CRITICALSLASH"}, - {NPC_CURSEATTACK, "CURSEATTACK", "NPC_CURSEATTACK"}, - {NPC_DARKBLESSING, "DARKBLESSING", "NPC_DARKBLESSING"}, - {NPC_DARKBREATH, "DARKBREATH", "NPC_DARKBREATH"}, - {NPC_DARKCROSS, "DARKCROSS", "NPC_DARKCROSS"}, - {NPC_DARKNESSATTACK, "DARKNESSATTACK", "NPC_DARKNESSATTACK"}, - {NPC_DEFENDER, "DEFENDER", "NPC_DEFENDER"}, - {NPC_EMOTION, "EMOTION", "NPC_EMOTION"}, - {NPC_ENERGYDRAIN, "ENERGYDRAIN", "NPC_ENERGYDRAIN"}, - {NPC_FIREATTACK, "FIREATTACK", "NPC_FIREATTACK"}, - {NPC_GROUNDATTACK, "GROUNDATTACK", "NPC_GROUNDATTACK"}, - {NPC_GUIDEDATTACK, "GUIDEDATTACK", "NPC_GUIDEDATTACK"}, - {NPC_HALLUCINATION, "HALLUCINATION", "NPC_HALLUCINATION"}, - {NPC_HOLYATTACK, "HOLYATTACK", "NPC_HOLYATTACK"}, - {NPC_KEEPING, "KEEPING", "NPC_KEEPING"}, - {NPC_LICK, "LICK", "NPC_LICK"}, - {NPC_MAGICALATTACK, "MAGICALATTACK", "NPC_MAGICALATTACK"}, - {NPC_MENTALBREAKER, "MENTALBREAKER", "NPC_MENTALBREAKER"}, - {NPC_METAMORPHOSIS, "METAMORPHOSIS", "NPC_METAMORPHOSIS"}, - {NPC_PETRIFYATTACK, "PETRIFYATTACK", "NPC_PETRIFYATTACK"}, - {NPC_PIERCINGATT, "PIERCINGATT", "NPC_PIERCINGATT"}, - {NPC_POISON, "POISON", "NPC_POISON"}, - {NPC_POISONATTACK, "POISONATTACK", "NPC_POISONATTACK"}, - {NPC_RANDOMATTACK, "RANDOMATTACK", "NPC_RANDOMATTACK"}, - {NPC_RANGEATTACK, "RANGEATTACK", "NPC_RANGEATTACK"}, - {NPC_REBIRTH, "REBIRTH", "NPC_REBIRTH"}, - {NPC_SELFDESTRUCTION, "SELFDESTRUCTION", "Kabooooom!"}, - {NPC_SELFDESTRUCTION2, "SELFDESTRUCTION2", "NPC_SELFDESTRUCTION2"}, - {NPC_SILENCEATTACK, "SILENCEATTACK", "NPC_SILENCEATTACK"}, - {NPC_SLEEPATTACK, "SLEEPATTACK", "NPC_SLEEPATTACK"}, - {NPC_SMOKING, "SMOKING", "NPC_SMOKING"}, - {NPC_SPLASHATTACK, "SPLASHATTACK", "NPC_SPLASHATTACK"}, - {NPC_STUNATTACK, "STUNATTACK", "NPC_STUNATTACK"}, - {NPC_SUICIDE, "SUICIDE", "NPC_SUICIDE"}, - {NPC_SUMMONMONSTER, "SUMMONMONSTER", "NPC_SUMMONMONSTER"}, - {NPC_SUMMONSLAVE, "SUMMONSLAVE", "NPC_SUMMONSLAVE"}, - {NPC_TELEKINESISATTACK, "TELEKINESISATTACK", "NPC_TELEKINESISATTACK"}, - {NPC_TRANSFORMATION, "TRANSFORMATION", "NPC_TRANSFORMATION"}, - {NPC_WATERATTACK, "WATERATTACK", "NPC_WATERATTACK"}, - {NPC_WINDATTACK, "WINDATTACK", "NPC_WINDATTACK"}, - {NV_EMOTE, "EMOTE", "Emote_Skill"}, - {NV_TRADE, "TRADE", "Trade_Skill"}, - {NV_PARTY, "PARTY", "Party_Skill"}, - {NV_FIRSTAID, "FIRSTAID", "First Aid"}, - {NV_TRICKDEAD, "TRICKDEAD", "Play_Dead"}, - {PA_GOSPEL, "GOSPEL", "Gospel"}, - {PA_PRESSURE, "PRESSURE", "Pressure"}, - {PA_SACRIFICE, "SACRIFICE", "Sacrificial_Ritual"}, - {PF_FOGWALL, "FOGWALL", "Wall_of_Fog"}, - {PF_HPCONVERSION, "HPCONVERSION", "Health_Conversion"}, - {PF_MEMORIZE, "MEMORIZE", "Memorize"}, - {PF_MINDBREAKER, "MINDBREAKER", "Mind_Breaker"}, - {PF_SOULBURN, "SOULBURN", "Soul_Burn"}, - {PF_SOULCHANGE, "SOULCHANGE", "Soul_Change"}, - {PF_SPIDERWEB, "SPIDERWEB", "Spider_Web"}, - {PR_ASPERSIO, "ASPERSIO", "Aspersio"}, - {PR_BENEDICTIO, "BENEDICTIO", "B.S_Sacramenti"}, - {PR_GLORIA, "GLORIA", "Gloria"}, - {PR_IMPOSITIO, "IMPOSITIO", "Impositio_Manus"}, - {PR_KYRIE, "KYRIE", "Kyrie_Eleison"}, - {PR_LEXAETERNA, "LEXAETERNA", "Lex_Aeterna"}, - {PR_LEXDIVINA, "LEXDIVINA", "Lex_Divina"}, - {PR_MACEMASTERY, "MACEMASTERY", "Mace_Mastery"}, - {PR_MAGNIFICAT, "MAGNIFICAT", "Magnificat"}, - {PR_MAGNUS, "MAGNUS", "Magnus_Exorcismus"}, - {PR_SANCTUARY, "SANCTUARY", "Santuary"}, - {PR_SLOWPOISON, "SLOWPOISON", "Slow_Poison"}, - {PR_STRECOVERY, "STRECOVERY", "Status_Recovery"}, - {PR_SUFFRAGIUM, "SUFFRAGIUM", "Suffragium"}, - {PR_TURNUNDEAD, "TURNUNDEAD", "Turn_Undead"}, - {RG_BACKSTAP, "BACKSTAP", "Back_Stab"}, - {RG_CLEANER, "CLEANER", "Remover"}, - {RG_COMPULSION, "COMPULSION", "Compulsion_Discount"}, - {RG_FLAGGRAFFITI, "FLAGGRAFFITI", "Flag_Graffity"}, - {RG_GANGSTER, "GANGSTER", "Gangster's_Paradise"}, - {RG_GRAFFITI, "GRAFFITI", "Graffiti"}, - {RG_INTIMIDATE, "INTIMIDATE", "Intimidate"}, - {RG_PLAGIARISM, "PLAGIARISM", "Plagiarism"}, - {RG_RAID, "RAID", "Raid"}, - {RG_SNATCHER, "SNATCHER", "Snatcher"}, - {RG_STEALCOIN, "STEALCOIN", "Steal_Coin"}, - {RG_STRIPARMOR, "STRIPARMOR", "Strip_Armor"}, - {RG_STRIPHELM, "STRIPHELM", "Strip_Helm"}, - {RG_STRIPSHIELD, "STRIPSHIELD", "Strip_Shield"}, - {RG_STRIPWEAPON, "STRIPWEAPON", "Strip_Weapon"}, - {RG_TUNNELDRIVE, "TUNNELDRIVE", "Tunnel_Drive"}, - {SA_ABRACADABRA, "ABRACADABRA", "Hocus-pocus"}, - {SA_ADVANCEDBOOK, "ADVANCEDBOOK", "Advanced_Book"}, - {SA_AUTOSPELL, "AUTOSPELL", "Auto_Cast"}, - {SA_CASTCANCEL, "CASTCANCEL", "Cast_Cancel"}, - {SA_CLASSCHANGE, "CLASSCHANGE", "Class_Change"}, - {SA_COMA, "COMA", "Coma"}, - {SA_DEATH, "DEATH", "Death"}, - {SA_DELUGE, "DELUGE", "Deluge"}, - {SA_DISPELL, "DISPELL", "Dispel"}, - {SA_DRAGONOLOGY, "DRAGONOLOGY", "Dragonology"}, - {SA_FLAMELAUNCHER, "FLAMELAUNCHER", "Flame_Launcher"}, - {SA_FORTUNE, "FORTUNE", "Fortune"}, - {SA_FREECAST, "FREECAST", "Cast_Freedom"}, - {SA_FROSTWEAPON, "FROSTWEAPON", "Frost_Weapon"}, - {SA_FULLRECOVERY, "FULLRECOVERY", "Full_Recovery"}, - {SA_GRAVITY, "GRAVITY", "Gravity"}, - {SA_INSTANTDEATH, "INSTANTDEATH", "Instant_Death"}, - {SA_LANDPROTECTOR, "LANDPROTECTOR", "Land_Protector"}, - {SA_LEVELUP, "LEVELUP", "Level_Up"}, - {SA_LIGHTNINGLOADER, "LIGHTNINGLOADER", "Lightning_Loader"}, - {SA_MAGICROD, "MAGICROD", "Magic_Rod"}, - {SA_MONOCELL, "MONOCELL", "Monocell"}, - {SA_QUESTION, "QUESTION", "Question?"}, - {SA_REVERSEORCISH, "REVERSEORCISH", "Reverse_Orcish"}, - {SA_SEISMICWEAPON, "SEISMICWEAPON", "Seismic_Weapon"}, - {SA_SPELLBREAKER, "SPELLBREAKER", "Break_Spell"}, - {SA_SUMMONMONSTER, "SUMMONMONSTER", "Summon_Monster"}, - {SA_VIOLENTGALE, "VIOLENTGALE", "Violent_Gale"}, - {SA_VOLCANO, "VOLCANO", "Volcano"}, - {SG_DEVIL, "DEVIL", "Devil"}, - {SG_FEEL, "FEEL", "Feel"}, - {SG_FRIEND, "FRIEND", "Friend"}, - {SG_FUSION, "FUSION", "Fusion"}, - {SG_HATE, "HATE", "Hate"}, - {SG_KNOWLEDGE, "KNOWLEDGE", "Knowledge"}, - {SG_MOON_ANGER, "ANGER", "Moon Anger"}, - {SG_MOON_BLESS, "BLESS", "Moon Bless"}, - {SG_MOON_COMFORT, "COMFORT", "Moon Comfort"}, - {SG_MOON_WARM, "WARM", "Moon Warm"}, - {SG_STAR_ANGER, "ANGER", "Star Anger"}, - {SG_STAR_BLESS, "BLESS", "Star Bless"}, - {SG_STAR_COMFORT, "COMFORT", "Star Comfort"}, - {SG_STAR_WARM, "WARM", "Star Warm"}, - {SG_SUN_ANGER, "ANGER", "Sun Anger"}, - {SG_SUN_BLESS, "BLESS", "Sun Bless"}, - {SG_SUN_COMFORT, "COMFORT", "Sun Comfort"}, - {SG_SUN_WARM, "WARM", "Sun Warm"}, - {SL_ALCHEMIST, "ALCHEMIST", "Alchemist"}, - {SL_ASSASIN, "ASSASIN", "Assasin"}, - {SL_BARDDANCER, "BARDDANCER", "Bard Dancer"}, - {SL_BLACKSMITH, "BLACKSMITH", "Black Smith"}, - {SL_CRUSADER, "CRUSADER", "Crusader"}, - {SL_HUNTER, "HUNTER", "Hunter"}, - {SL_KAAHI, "KAAHI", "Kaahi"}, - {SL_KAINA, "KAINA", "Kaina"}, - {SL_KAITE, "KAITE", "Kaite"}, - {SL_KAIZEL, "KAIZEL", "Kaizel"}, - {SL_KAUPE, "KAUPE", "Kaupe"}, - {SL_KNIGHT, "KNIGHT", "Knight"}, - {SL_MONK, "MONK", "Monk"}, - {SL_PRIEST, "PRIEST", "Priest"}, - {SL_ROGUE, "ROGUE", "Rogue"}, - {SL_SAGE, "SAGE", "Sage"}, - {SL_SKA, "SKA", "SKA"}, - {SL_SKE, "SKE", "SKE"}, - {SL_SMA, "SMA", "SMA"}, - {SL_SOULLINKER, "SOULLINKER", "Soul Linker"}, - {SL_STAR, "STAR", "Star"}, - {SL_STIN, "STIN", "Stin"}, - {SL_STUN, "STUN", "Stun"}, - {SL_SUPERNOVICE, "SUPERNOVICE", "Super Novice"}, - {SL_SWOO, "SWOO", "Swoo"}, - {SL_WIZARD, "WIZARD", "Wizard"}, - {SM_AUTOBERSERK, "AUTOBERSERK", "Auto_Berserk"}, - {SM_BASH, "BASH", "Bash"}, - {SM_ENDURE, "ENDURE", "Endure"}, - {SM_FATALBLOW, "FATALBLOW", "Attack_Weak_Point"}, - {SM_MAGNUM, "MAGNUM", "Magnum_Break"}, - {SM_MOVINGRECOVERY, "MOVINGRECOVERY", "Moving_HP_Recovery"}, - {SM_PROVOKE, "PROVOKE", "Provoke"}, - {SM_RECOVERY, "RECOVERY", "Increase_HP_Recovery"}, - {SM_SWORD, "SWORD", "Sword_Mastery"}, - {SM_TWOHAND, "TWOHAND", "Two-Handed_Sword_Mastery"}, - {SN_FALCONASSAULT, "FALCONASSAULT", "Falcon_Assault"}, - {SN_SHARPSHOOTING, "SHARPSHOOTING", "Sharpshooting"}, - {SN_SIGHT, "SIGHT", "True_Sight"}, - {SN_WINDWALK, "WINDWALK", "Wind_Walk"}, - {ST_CHASEWALK, "CHASEWALK", "Chase_Walk"}, - {ST_REJECTSWORD, "REJECTSWORD", "Reject_Sword"}, - {ST_STEALBACKPACK, "STEALBACKPACK", "Steal_Backpack"}, - {TF_BACKSLIDING, "BACKSLIDING", "Back_Sliding"}, - {TF_DETOXIFY, "DETOXIFY", "Detoxify"}, - {TF_DOUBLE, "DOUBLE", "Double_Attack"}, - {TF_HIDING, "HIDING", "Hiding"}, - {TF_MISS, "MISS", "Improve_Dodge"}, - {TF_PICKSTONE, "PICKSTONE", "Take_Stone"}, - {TF_POISON, "POISON", "Envenom"}, - {TF_SPRINKLESAND, "SPRINKLESAND", "Throw_Sand"}, - {TF_STEAL, "STEAL", "Steal"}, - {TF_THROWSTONE, "THROWSTONE", "Throw_Stone"}, - {TK_COUNTER, "COUNTER", "Counter"}, - {TK_DODGE, "DODGE", "Dodge"}, - {TK_DOWNKICK, "DOWNKICK", "Down Kick"}, - {TK_HIGHJUMP, "HIGHJUMP", "High Jump"}, - {TK_HPTIME, "HPTIME", "HP Time"}, - {TK_JUMPKICK, "JUMPKICK", "Jump Kick"}, - {TK_POWER, "POWER", "Power"}, - {TK_READYCOUNTER, "READYCOUNTER", "Ready Counter"}, - {TK_READYDOWN, "READYDOWN", "Ready Down"}, - {TK_READYSTORM, "READYSTORM", "Ready Storm"}, - {TK_READYTURN, "READYTURN", "Ready Turn"}, - {TK_RUN, "RUN", "TK_RUN"}, - {TK_SEVENWIND, "SEVENWIND", "Seven Wind"}, - {TK_SPTIME, "SPTIME", "SP Time"}, - {TK_STORMKICK, "STORMKICK", "Storm Kick"}, - {TK_TURNKICK, "TURNKICK", "Turn Kick"}, - {WE_BABY, "BABY", "Adopt_Baby"}, - {WE_CALLBABY, "CALLBABY", "Call_Baby"}, - {WE_CALLPARENT, "CALLPARENT", "Call_Parent"}, - {WE_CALLPARTNER, "CALLPARTNER", "I Want to See You"}, - {WE_FEMALE, "FEMALE", "I Only Look Up to You"}, - {WE_MALE, "MALE", "I Will Protect You"}, - {WS_CARTBOOST, "CARTBOOST", "Cart_Boost"}, - {WS_CREATECOIN, "CREATECOIN", "Create_Coins"}, - {WS_CREATENUGGET, "CREATENUGGET", "Create_Nuggets"}, - {WS_MELTDOWN, "MELTDOWN", "Meltdown"}, - {WS_SYSTEMCREATE, "SYSTEMCREATE", "Create_System_tower"}, - {WZ_EARTHSPIKE, "EARTHSPIKE", "Earth_Spike"}, - {WZ_ESTIMATION, "ESTIMATION", "Sense"}, - {WZ_FIREIVY, "FIREIVY", "Fire_Ivy"}, - {WZ_FIREPILLAR, "FIREPILLAR", "Fire_Pillar"}, - {WZ_FROSTNOVA, "FROSTNOVA", "Frost_Nova"}, - {WZ_HEAVENDRIVE, "HEAVENDRIVE", "Heaven's_Drive"}, - {WZ_ICEWALL, "ICEWALL", "Ice_Wall"}, - {WZ_JUPITEL, "JUPITEL", "Jupitel_Thunder"}, - {WZ_METEOR, "METEOR", "Meteor_Storm"}, - {WZ_QUAGMIRE, "QUAGMIRE", "Quagmire"}, - {WZ_SIGHTRASHER, "SIGHTRASHER", "Sightrasher"}, - {WZ_STORMGUST, "STORMGUST", "Storm_Gust"}, - {WZ_VERMILION, "VERMILION", "Lord_of_Vermilion"}, - {WZ_WATERBALL, "WATERBALL", "Water_Ball"}, - {0, 0, 0} -}; - -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]; - -#define UNARMED_PLAYER_DAMAGE_MIN(bl) (skill_power_bl((bl), TMW_BRAWLING) >> 4) // +50 for 200 -#define UNARMED_PLAYER_DAMAGE_MAX(bl) (skill_power_bl((bl), TMW_BRAWLING)) // +200 for 200 - -int skill_get_hit (int id) -{ - return skill_db[id].hit; -} - -int skill_get_inf (int id) -{ - return skill_db[id].inf; -} - -int skill_get_pl (int id) -{ - return skill_db[id].pl; -} - -int skill_get_nk (int id) -{ - return skill_db[id].nk; -} - -int skill_get_max (int id) -{ - return skill_db[id].max; -} - -int skill_get_max_raise (int id) -{ - return skill_db[id].max_raise; -} - -int skill_get_range (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].range[lv - 1]; -} - -int skill_get_hp (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].hp[lv - 1]; -} - -int skill_get_sp (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].sp[lv - 1]; -} - -int skill_get_zeny (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].zeny[lv - 1]; -} - -int skill_get_num (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].num[lv - 1]; -} - -int skill_get_cast (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].cast[lv - 1]; -} - -int skill_get_delay (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].delay[lv - 1]; -} - -int skill_get_time (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].upkeep_time[lv - 1]; -} - -int skill_get_time2 (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].upkeep_time2[lv - 1]; -} - -int skill_get_castdef (int id) -{ - return skill_db[id].cast_def_rate; -} - -int skill_get_weapontype (int id) -{ - return skill_db[id].weapon; -} - -int skill_get_inf2 (int id) -{ - return skill_db[id].inf2; -} - -int skill_get_maxcount (int id) -{ - return skill_db[id].maxcount; -} - -int skill_get_blewcount (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].blewcount[lv - 1]; -} - -int skill_get_mhp (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].mhp[lv - 1]; -} - -int skill_get_castnodex (int id, int lv) -{ - return (lv <= 0) ? 0 : skill_db[id].castnodex[lv - 1]; -} - -/* プロトタイプ */ -struct skill_unit_group *skill_unitsetting (struct block_list *src, - int skillid, int skilllv, int x, - int y, int flag); -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 skill_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); -int skill_landprotector (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); - -// [MouseJstr] - skill ok to cast? and when? -static int skillnotok (int skillid, struct map_session_data *sd) -{ - if (sd == 0) - return 0; - if (pc_isGM (sd) >= 20) - return 0; // gm's can do anything damn thing they want - switch (skillid) - { - case AL_WARP: - case AL_TELEPORT: - case MC_IDENTIFY: - return 0; // always allowed - default: - return (map[sd->bl.m].flag.noskill); - } -} - -static int distance (int x0, int y0, int x1, int y1) -{ - int dx, dy; - - dx = abs (x0 - x1); - dy = abs (y0 - y1); - return dx > dy ? dx : dy; -} - -/* スキルユニットIDを返す(これもデータベースに入れたいな) */ -int skill_get_unit_id (int id, int flag) -{ - - switch (id) - { - case MG_SAFETYWALL: - return 0x7e; /* セイフティウォール */ - case MG_FIREWALL: - return 0x7f; /* ファイアーウォール */ - case AL_WARP: - return (flag == 0) ? 0x81 : 0x80; /* ワープポータル */ - case PR_BENEDICTIO: - return 0x82; /* 聖体降福 */ - case PR_SANCTUARY: - return 0x83; /* サンクチュアリ */ - case PR_MAGNUS: - return 0x84; /* マグヌスエクソシズム */ - case AL_PNEUMA: - return 0x85; /* ニューマ */ - case MG_THUNDERSTORM: - return 0x86; /* サンダーストーム */ - case WZ_HEAVENDRIVE: - return 0x86; /* ヘヴンズドライブ */ - case WZ_SIGHTRASHER: - return 0x86; /* サイトラッシャー */ - case WZ_METEOR: - return 0x86; /* メテオストーム */ - case WZ_VERMILION: - return 0x86; /* ロードオブヴァーミリオン */ - case WZ_FROSTNOVA: - return 0x86; /* フロストノヴァ */ - case WZ_STORMGUST: - return 0x86; /* ストームガスト(とりあえずLoVと同じで処理) */ - case CR_GRANDCROSS: - return 0x86; /* グランドクロス */ - case WZ_FIREPILLAR: - return (flag == 0) ? 0x87 : 0x88; /* ファイアーピラー */ - case HT_TALKIEBOX: - return 0x99; /* トーキーボックス */ - case WZ_ICEWALL: - return 0x8d; /* アイスウォール */ - case WZ_QUAGMIRE: - return 0x8e; /* クァグマイア */ - case HT_BLASTMINE: - return 0x8f; /* ブラストマイン */ - case HT_SKIDTRAP: - return 0x90; /* スキッドトラップ */ - case HT_ANKLESNARE: - return 0x91; /* アンクルスネア */ - case AS_VENOMDUST: - return 0x92; /* ベノムダスト */ - case HT_LANDMINE: - return 0x93; /* ランドマイン */ - case HT_SHOCKWAVE: - return 0x94; /* ショックウェーブトラップ */ - case HT_SANDMAN: - return 0x95; /* サンドマン */ - case HT_FLASHER: - return 0x96; /* フラッシャー */ - case HT_FREEZINGTRAP: - return 0x97; /* フリージングトラップ */ - case HT_CLAYMORETRAP: - return 0x98; /* クレイモアートラップ */ - case SA_VOLCANO: - return 0x9a; /* ボルケーノ */ - case SA_DELUGE: - return 0x9b; /* デリュージ */ - case SA_VIOLENTGALE: - return 0x9c; /* バイオレントゲイル */ - case SA_LANDPROTECTOR: - return 0x9d; /* ランドプロテクター */ - case BD_LULLABY: - return 0x9e; /* 子守歌 */ - case BD_RICHMANKIM: - return 0x9f; /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: - return 0xa0; /* 永遠の混沌 */ - case BD_DRUMBATTLEFIELD: - return 0xa1; /* 戦太鼓の響き */ - case BD_RINGNIBELUNGEN: - return 0xa2; /* ニーベルングの指輪 */ - case BD_ROKISWEIL: - return 0xa3; /* ロキの叫び */ - case BD_INTOABYSS: - return 0xa4; /* 深淵の中に */ - case BD_SIEGFRIED: - return 0xa5; /* 不死身のジークフリード */ - case BA_DISSONANCE: - return 0xa6; /* 不協和音 */ - case BA_WHISTLE: - return 0xa7; /* 口笛 */ - case BA_ASSASSINCROSS: - return 0xa8; /* 夕陽のアサシンクロス */ - case BA_POEMBRAGI: - return 0xa9; /* ブラギの詩 */ - case BA_APPLEIDUN: - return 0xaa; /* イドゥンの林檎 */ - case DC_UGLYDANCE: - return 0xab; /* 自分勝手なダンス */ - case DC_HUMMING: - return 0xac; /* ハミング */ - case DC_DONTFORGETME: - return 0xad; /* 私を忘れないで… */ - case DC_FORTUNEKISS: - return 0xae; /* 幸運のキス */ - case DC_SERVICEFORYOU: - return 0xaf; /* サービスフォーユー */ - case RG_GRAFFITI: - return 0xb0; /* グラフィティ */ - case AM_DEMONSTRATION: - return 0xb1; /* デモンストレーション */ - case WE_CALLPARTNER: - return 0xb2; /* あなたに逢いたい */ - case PA_GOSPEL: - return 0xb3; /* ゴスペル */ - case HP_BASILICA: - return 0xb4; /* バジリカ */ - case PF_FOGWALL: - return 0xb6; /* フォグウォール */ - case PF_SPIDERWEB: - return 0xb7; /* スパイダーウェッブ */ - } - return 0; - /* - * 0x89,0x8a,0x8b 表示無し - * 0x9a 炎属性の詠唱みたいなエフェクト - * 0x9b 水属性の詠唱みたいなエフェクト - * 0x9c 風属性の詠唱みたいなエフェクト - * 0x9d 白い小さなエフェクト - * 0xb1 Alchemist Demonstration - * 0xb2 = Pink Warp Portal - * 0xb3 = Gospel For Paladin - * 0xb4 = Basilica - * 0xb5 = Empty - * 0xb6 = Fog Wall for Professor - * 0xb7 = Spider Web for Professor - * 0xb8 = Empty - * 0xb9 = - */ -} - -/*========================================== - * スキル追加効果 - *------------------------------------------ - */ -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[] = { - MG_STONECURSE, MG_FROSTDIVER, NPC_STUNATTACK, - NPC_SLEEPATTACK, TF_POISON, NPC_CURSEATTACK, - NPC_SILENCEATTACK, 0, NPC_BLINDATTACK - }; - - struct map_session_data *sd = NULL; - struct map_session_data *dstsd = NULL; - struct mob_data *md = NULL; - struct mob_data *dstmd = NULL; - - int skill, skill2; - int rate, luk; - - int sc_def_mdef, sc_def_vit, sc_def_int, sc_def_luk; - int sc_def_mdef2, sc_def_vit2, sc_def_int2, sc_def_luk2; - int sc_def_phys_shield_spell; - - nullpo_retr (0, src); - nullpo_retr (0, bl); - - if (skilllv < 0) - return 0; - - if (src->type == BL_PC) - { - nullpo_retr (0, sd = (struct map_session_data *) src); - } - else if (src->type == BL_MOB) - { - nullpo_retr (0, md = (struct mob_data *) src); //未使用? - } - - sc_def_phys_shield_spell = 0; - if (battle_get_sc_data (bl)[SC_PHYS_SHIELD].timer != -1) - sc_def_phys_shield_spell = - battle_get_sc_data (bl)[SC_PHYS_SHIELD].val1; - - //対象の耐性 - luk = battle_get_luk (bl); - sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + luk / 3); - sc_def_vit = 100 - (3 + battle_get_vit (bl) + luk / 3); - sc_def_int = 100 - (3 + battle_get_int (bl) + luk / 3); - sc_def_luk = 100 - (3 + luk); - //自分の耐性 - luk = battle_get_luk (src); - sc_def_mdef2 = 100 - (3 + battle_get_mdef (src) + luk / 3); - sc_def_vit2 = 100 - (3 + battle_get_vit (src) + luk / 3); - sc_def_int2 = 100 - (3 + battle_get_int (src) + luk / 3); - sc_def_luk2 = 100 - (3 + luk); - if (bl->type == BL_PC) - dstsd = (struct map_session_data *) bl; - else if (bl->type == BL_MOB) - { - dstmd = (struct mob_data *) bl; //未使用? - if (sc_def_mdef > 50) - sc_def_mdef = 50; - if (sc_def_vit > 50) - sc_def_vit = 50; - if (sc_def_int > 50) - sc_def_int = 50; - if (sc_def_luk > 50) - sc_def_luk = 50; - } - if (sc_def_mdef < 0) - sc_def_mdef = 0; - if (sc_def_vit < 0) - sc_def_vit = 0; - if (sc_def_int < 0) - sc_def_int = 0; - - switch (skillid) - { - case 0: /* 通常攻撃 */ - /* 自動鷹 */ - if (sd && pc_isfalcon (sd) && sd->status.weapon == 11 - && (skill = pc_checkskill (sd, HT_BLITZBEAT)) > 0 - && MRAND (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); - } - // スナッチャー - if (sd && sd->status.weapon != 11 - && (skill = pc_checkskill (sd, RG_SNATCHER)) > 0) - if ((skill * 15 + 55) + - (skill2 = - pc_checkskill (sd, TF_STEAL)) * 10 > MRAND (1000)) - { - if (pc_steal_item (sd, bl)) - clif_skill_nodamage (src, bl, TF_STEAL, skill2, 1); - else - clif_skill_fail (sd, skillid, 0, 0); - } - break; - - case SM_BASH: /* バッシュ(急所攻撃) */ - if (sd && (skill = pc_checkskill (sd, SM_FATALBLOW)) > 0) - { - if (MRAND (100) < 6 * (skilllv - 5) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (SM_FATALBLOW, - skilllv), 0); - } - break; - - case TF_POISON: /* インベナム */ - case AS_SPLASHER: /* ベナムスプラッシャー */ - if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) - skill_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 (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case HT_FREEZINGTRAP: /* フリージングトラップ */ - rate = skilllv * 3 + 35; - if (MRAND (100) < rate * sc_def_mdef / 100) - skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case MG_FROSTDIVER: /* フロストダイバー */ - case WZ_FROSTNOVA: /* フロストノヴァ */ - rate = - (skilllv * 3 + 35) * sc_def_mdef / 100 - - (battle_get_int (bl) + battle_get_luk (bl)) / 15; - rate = rate <= 5 ? 5 : rate; - if (MRAND (100) < rate) - skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - else if (sd) - clif_skill_fail (sd, skillid, 0, 0); - break; - - case WZ_STORMGUST: /* ストームガスト */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - if (sc_data) - { - sc_data[SC_FREEZE].val3++; - if (sc_data[SC_FREEZE].val3 >= 3) - skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, - 0, skill_get_time2 (skillid, - skilllv), - 0); - } - } - break; - - case HT_LANDMINE: /* ランドマイン */ - if (MRAND (100) < (5 * skilllv + 30) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case HT_SHOCKWAVE: /* ショックウェーブトラップ */ - if (map[bl->m].flag.pvp && dstsd) - { - dstsd->status.sp -= - dstsd->status.sp * (5 + 15 * skilllv) / 100; - pc_calcstatus (dstsd, 0); - } - break; - case HT_SANDMAN: /* サンドマン */ - if (MRAND (100) < (5 * skilllv + 30) * sc_def_int / 100) - skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case TF_SPRINKLESAND: /* 砂まき */ - if (MRAND (100) < 15 * sc_def_int / 100) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case TF_THROWSTONE: /* 石投げ */ - if (MRAND (100) < 5 * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case CR_HOLYCROSS: /* ホーリークロス */ - if (MRAND (100) < 3 * skilllv * sc_def_int / 100) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case CR_GRANDCROSS: /* グランドクロス */ - { - int race = battle_get_race (bl); - if ((battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) && MRAND (100) < 100000 * sc_def_int / 100) //強制付与だが完全耐性には無効 - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - } - break; - - case CR_SHIELDCHARGE: /* シールドチャージ */ - if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case RG_RAID: /* サプライズアタック */ - if (MRAND (100) < (10 + 3 * skilllv) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case BA_FROSTJOKE: - if (MRAND (100) < (15 + 5 * skilllv) * sc_def_mdef / 100) - skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case DC_SCREAM: - if (MRAND (100) < (25 + 5 * skilllv) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case BD_LULLABY: /* 子守唄 */ - if (MRAND (100) < 15 * sc_def_int / 100) - skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - /* MOBの追加効果付きスキル */ - - case NPC_PETRIFYATTACK: - if (MRAND (100) < sc_def_mdef) - skill_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 (MRAND (100) < - 50 - (sc_def_vit >> 2) - (sc_def_phys_shield_spell) + - (skilllv >> 2)) - skill_status_change_start (bl, sc[skillid - NPC_POISON], - skilllv, 0, 0, 0, skilllv, 0); - break; - case NPC_CURSEATTACK: - if (MRAND (100) < sc_def_luk) - skill_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 (MRAND (100) < sc_def_int) - skill_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; - -// -- moonsoul (adding status effect chance given to wizard aoe skills meteor and vermillion) -// - case WZ_METEOR: - if (MRAND (100) < sc_def_vit) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case WZ_VERMILION: - if (MRAND (100) < sc_def_int) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - -// -- moonsoul (stun ability of new champion skill tigerfist) -// - case CH_TIGERFIST: - if (MRAND (100) < (5 + skilllv * 5) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case LK_SPIRALPIERCE: - if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case ST_REJECTSWORD: /* フリージングトラップ */ - if (MRAND (100) < (10 + skilllv * 5)) - skill_status_change_start (bl, SC_AUTOCOUNTER, skilllv, 0, 0, - 0, skill_get_time2 (skillid, - skilllv), 0); - break; - case PF_FOGWALL: /* ホーリークロス */ - if (MRAND (100) < 3 * skilllv * sc_def_int / 100) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - { //条件が良く分からないので適当に - int race = battle_get_race (bl); - if (! - (battle_check_undead (race, battle_get_elem_type (bl)) - || race == 6) - && MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) - skill_status_change_start (bl, SC_HEADCRUSH, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - } - break; - case LK_JOINTBEAT: /* ジョイントビート */ - //条件が良く分からないので適当に - if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) - skill_status_change_start (bl, SC_JOINTBEAT, 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では拘束時間半減? - sec = sec / 2; - battle_stopwalking (bl, 1); - skill_status_change_start (bl, SC_SPIDERWEB, skilllv, 0, 0, 0, - sec, 0); - } - break; - case ASC_METEORASSAULT: /* メテオアサルト */ - if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) //状態異常は詳細が分からないので適当に - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) - skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - case MO_EXTREMITYFIST: /* 阿修羅覇凰拳 */ - //阿修羅を使うと5分間自然回復しないようになる - skill_status_change_start (src, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), 0); - break; - } - - if (sd && skillid != MC_CARTREVOLUTION && attack_type & BF_WEAPON) - { /* カードによる追加効果 */ - int i; - int sc_def_card = 100; - - for (i = SC_STONE; i <= SC_BLIND; i++) - { - //対象に状態異常 - if (i == SC_STONE || i == SC_FREEZE) - sc_def_card = sc_def_mdef; - else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) - sc_def_card = sc_def_vit; - else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) - sc_def_card = sc_def_int; - else if (i == SC_CURSE) - sc_def_card = sc_def_luk; - - if (!sd->state.arrow_atk) - { - if (MRAND (10000) < - (sd->addeff[i - SC_STONE]) * sc_def_card / 100) - { - if (battle_config.battle_log) - printf - ("PC %d skill_addeff: cardによる異常発動 %d %d\n", - sd->bl.id, i, sd->addeff[i - SC_STONE]); - skill_status_change_start (bl, i, 7, 0, 0, 0, - (i == - SC_CONFUSION) ? 10000 + - 7000 : - skill_get_time2 (sc2 - [i - - SC_STONE], - 7), 0); - } - } - else - { - if (MRAND (10000) < - (sd->addeff[i - SC_STONE] + - sd->arrow_addeff[i - SC_STONE]) * sc_def_card / 100) - { - if (battle_config.battle_log) - printf - ("PC %d skill_addeff: cardによる異常発動 %d %d\n", - sd->bl.id, i, sd->addeff[i - SC_STONE]); - skill_status_change_start (bl, i, 7, 0, 0, 0, - (i == - SC_CONFUSION) ? 10000 + - 7000 : - skill_get_time2 (sc2 - [i - - SC_STONE], - 7), 0); - } - } - //自分に状態異常 - if (i == SC_STONE || i == SC_FREEZE) - sc_def_card = sc_def_mdef2; - else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) - sc_def_card = sc_def_vit2; - else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) - sc_def_card = sc_def_int2; - else if (i == SC_CURSE) - sc_def_card = sc_def_luk2; - - if (!sd->state.arrow_atk) - { - if (MRAND (10000) < - (sd->addeff2[i - SC_STONE]) * sc_def_card / 100) - { - if (battle_config.battle_log) - printf - ("PC %d skill_addeff: cardによる異常発動 %d %d\n", - src->id, i, sd->addeff2[i - SC_STONE]); - skill_status_change_start (src, i, 7, 0, 0, 0, - (i == - SC_CONFUSION) ? 10000 + - 7000 : - skill_get_time2 (sc2 - [i - - SC_STONE], - 7), 0); - } - } - else - { - if (MRAND (10000) < - (sd->addeff2[i - SC_STONE] + - sd->arrow_addeff2[i - SC_STONE]) * sc_def_card / 100) - { - if (battle_config.battle_log) - printf - ("PC %d skill_addeff: cardによる異常発動 %d %d\n", - src->id, i, sd->addeff2[i - SC_STONE]); - skill_status_change_start (src, i, 7, 0, 0, 0, - (i == - SC_CONFUSION) ? 10000 + - 7000 : - skill_get_time2 (sc2 - [i - - SC_STONE], - 7), 0); - } - } - } - } - return 0; -} - -/*========================================================================= - スキル攻撃吹き飛ばし処理 --------------------------------------------------------------------------*/ -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 ret, prev_state = MS_IDLE; - int moveblock; - struct map_session_data *sd = NULL; - struct mob_data *md = NULL; - struct skill_unit *su = NULL; - - nullpo_retr (0, src); - nullpo_retr (0, target); - - if (target->type == BL_PC) - { - nullpo_retr (0, sd = (struct map_session_data *) target); - } - else if (target->type == BL_MOB) - { - nullpo_retr (0, md = (struct mob_data *) target); - } - else if (target->type == BL_SKILL) - { - nullpo_retr (0, su = (struct skill_unit *) target); - } - else - return 0; - - if (!(count & 0x10000 && (sd || md || su))) - { /* 指定なしなら位置関係から方向を求める */ - dx = target->x - src->x; - dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); - dy = target->y - src->y; - dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); - } - if (dx == 0 && dy == 0) - { - int dir = battle_get_dir (target); - 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; - moveblock = (x / BLOCK_SIZE != nx / BLOCK_SIZE - || y / BLOCK_SIZE != ny / BLOCK_SIZE); - - if (count & 0x20000) - { - battle_stopwalking (target, 1); - if (sd) - { - sd->to_x = nx; - sd->to_y = ny; - sd->walktimer = 1; - clif_walkok (sd); - clif_movechar (sd); - } - else if (md) - { - md->to_x = nx; - md->to_y = ny; - prev_state = md->state.state; - md->state.state = MS_WALK; - clif_fixmobpos (md); - } - } - else - battle_stopwalking (target, 2); - - dx = nx - x; - dy = ny - y; - - if (sd) /* 画面外に出たので消去 */ - map_foreachinmovearea (clif_pcoutsight, target->m, x - AREA_SIZE, - y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, - dx, dy, 0, 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); - - if (su) - { - skill_unit_move_unit_group (su->group, target->m, dx, dy); - } - else - { -// struct status_change *sc_data=battle_get_sc_data(target); - if (moveblock) - map_delblock (target); - target->x = nx; - target->y = ny; - if (moveblock) - map_addblock (target); -/*ダンス中にエフェクトは移動しないらしい - if(sc_data && sc_data[SC_DANCING].timer!=-1){ //対象がダンス中なのでエフェクトも移動 - struct skill_unit_group *sg=(struct skill_unit_group *)sc_data[SC_DANCING].val2; - if(sg) - skill_unit_move_unit_group(sg,target->m,dx,dy); - } -*/ - } - - if (sd) - { /* 画面内に入ってきたので表示 */ - map_foreachinmovearea (clif_pcinsight, target->m, nx - AREA_SIZE, - ny - AREA_SIZE, nx + AREA_SIZE, ny + AREA_SIZE, - -dx, -dy, 0, sd); - if (count & 0x20000) - sd->walktimer = -1; - } - 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); - if (count & 0x20000) - md->state.state = prev_state; - } - - skill_unit_move (target, gettick (), (count & 0xffff) + 7); /* スキルユニットの判定 */ - - return 0; -} - -/* - * ========================================================================= - * スキル攻撃効果処理まとめ - * flagの説明。16進図 - * 00XRTTff - * ff = magicで計算に渡される) - * TT = パケットのtype部分(0でデフォルト) - * X = パケットのスキルLv - * R = 予約(skill_area_subで使用する) - *------------------------------------------------------------------------- - */ - -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; - int type, lv, damage; - - rdamage = 0; - nullpo_retr (0, src); - nullpo_retr (0, dsrc); - nullpo_retr (0, bl); - - sc_data = battle_get_sc_data (bl); - -//何もしない判定ここから - if (dsrc->m != bl->m) //対象が同じマップにいなければ何もしない - return 0; - if (src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL) //prevよくわからない※ - return 0; - if (src->type == BL_PC && pc_isdead ((struct map_session_data *) src)) //術者?がPCですでに死んでいたら何もしない - return 0; - if (dsrc->type == BL_PC && pc_isdead ((struct map_session_data *) dsrc)) //術者?がPCですでに死んでいたら何もしない - return 0; - if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) //対象がPCですでに死んでいたら何もしない - return 0; - if (skillnotok (skillid, (struct map_session_data *) bl)) - return 0; // [MouseJstr] - if (sc_data && sc_data[SC_HIDING].timer != -1) - { //ハイディング状態で - if (skill_get_pl (skillid) != 2) //スキルの属性が地属性でなければ何もしない - return 0; - } - if (sc_data && sc_data[SC_TRICKDEAD].timer != -1) //死んだふり中は何もしない - return 0; - if (skillid == WZ_STORMGUST) - { //使用スキルがストームガストで - if (sc_data && sc_data[SC_FREEZE].timer != -1) //凍結状態なら何もしない - return 0; - } - if (skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフロストノヴァで、dsrcとblが同じ場所なら何もしない - return 0; - if (src->type == BL_PC && ((struct map_session_data *) src)->chatID) //術者がPCでチャット中なら何もしない - return 0; - if (dsrc->type == BL_PC && ((struct map_session_data *) dsrc)->chatID) //術者がPCでチャット中なら何もしない - return 0; - if (src->type == BL_PC && bl - && mob_gvmobcheck (((struct map_session_data *) src), bl) == 0) - return 0; - -//何もしない判定ここまで - - type = -1; - lv = (flag >> 20) & 0xf; - dmg = battle_calc_attack (attack_type, src, bl, skillid, skilllv, flag & 0xff); //ダメージ計算 - -//マジックロッド処理ここから - if (attack_type & BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 - && src == dsrc) - { //魔法攻撃でマジックロッド状態でsrc=dsrcなら - dmg.damage = dmg.damage2 = 0; //ダメージ0 - if (bl->type == BL_PC) - { //対象がPCの場合 - 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 (((struct map_session_data *) bl)->status.sp + sp > - ((struct map_session_data *) bl)->status.max_sp) - { //回復SP+現在のSPがMSPより大きい場合 - sp = ((struct map_session_data *) bl)->status.max_sp - ((struct map_session_data *) bl)->status.sp; //SPをMSP-現在SPにする - ((struct map_session_data *) bl)->status.sp = ((struct map_session_data *) bl)->status.max_sp; //現在のSPにMSPを代入 - } - else //回復SP+現在のSPがMSPより小さい場合は回復SPを加算 - ((struct map_session_data *) bl)->status.sp += sp; - clif_heal (((struct map_session_data *) bl)->fd, SP_SP, sp); //SP回復エフェクトの表示 - ((struct map_session_data *) bl)->canact_tick = tick + skill_delayfix (bl, skill_get_delay (SA_MAGICROD, sc_data[SC_MAGICROD].val1)); // - } - 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_) //吹き飛ばし判定?※ - dmg.blewcount = 0; - - if (skillid == CR_GRANDCROSS) - { //グランドクロス - if (battle_config.gx_disptype) - dsrc = src; // 敵ダメージ白文字表示 - if (src == bl) - type = 4; // 反動はダメージモーションなし - } - -//使用者がPCの場合の処理ここから - if (src->type == BL_PC) - { - struct map_session_data *sd = (struct map_session_data *) src; - nullpo_retr (0, sd); -//連打掌(MO_CHAINCOMBO)ここから - if (skillid == MO_CHAINCOMBO) - { - int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); //基本ディレイの計算 - if (damage < battle_get_hp (bl)) - { //ダメージが対象のHPより小さい場合 - if (pc_checkskill (sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0) //猛龍拳(MO_COMBOFINISH)取得&気球保持時は+300ms - delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 - - skill_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); //コンボディレイパケットの送信 - } -//連打掌(MO_CHAINCOMBO)ここまで -//猛龍拳(MO_COMBOFINISH)ここから - else if (skillid == MO_COMBOFINISH) - { - int delay = - 700 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); - if (damage < battle_get_hp (bl)) - { - //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms - //伏虎拳(CH_TIGERFIST)取得時も+300ms - if ((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; //追加ディレイをconfにより調整 - - skill_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); //コンボディレイパケットの送信 - } -//猛龍拳(MO_COMBOFINISH)ここまで -//伏虎拳(CH_TIGERFIST)ここから - else if (skillid == CH_TIGERFIST) - { - int delay = - 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); - if (damage < battle_get_hp (bl)) - { - if (pc_checkskill (sd, CH_CHAINCRUSH) > 0) //連柱崩撃(CH_CHAINCRUSH)取得時は+300ms - delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 - - skill_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); //コンボディレイパケットの送信 - } -//伏虎拳(CH_TIGERFIST)ここまで -//連柱崩撃(CH_CHAINCRUSH)ここから - else if (skillid == CH_CHAINCRUSH) - { - int delay = - 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); - if (damage < battle_get_hp (bl)) - { - //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms - if (pc_checkskill (sd, MO_EXTREMITYFIST) > 0 - && sd->spiritball >= 4 - && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) - delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 - - skill_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); //コンボディレイパケットの送信 - } -//連柱崩撃(CH_CHAINCRUSH)ここまで - } -//使用者がPCの場合の処理ここまで -//武器スキル?ここから - //AppleGirl Was Here - if (attack_type & BF_MAGIC && damage > 0 && src != bl && src == dsrc) - { //Blah Blah - if (bl->type == BL_PC) - { //Blah Blah - struct map_session_data *tsd = (struct map_session_data *) bl; - if (tsd->magic_damage_return > 0) - { //More Blah - rdamage += damage * tsd->magic_damage_return / 100; - if (rdamage < 1) - rdamage = 1; - } - } - } - //Stop Here - if (attack_type & BF_WEAPON && damage > 0 && src != bl && src == dsrc) - { //武器スキル&ダメージあり&使用者と対象者が違う&src=dsrc - if (dmg.flag & BF_SHORT) - { //近距離攻撃時?※ - if (bl->type == BL_PC) - { //対象がPCの時 - struct map_session_data *tsd = (struct map_session_data *) bl; - nullpo_retr (0, tsd); - if (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 (bl->type == BL_PC) - { //対象がPCの時 - struct map_session_data *tsd = (struct map_session_data *) bl; - nullpo_retr (0, tsd); - if (tsd->long_weapon_damage_return > 0) - { //遠距離攻撃跳ね返し?※ - rdamage += damage * tsd->long_weapon_damage_return / 100; - if (rdamage < 1) - rdamage = 1; - } - } - } - if (rdamage > 0) - clif_damage (src, src, tick, dmg.amotion, 0, rdamage, 1, 4, 0); - } -//武器スキル?ここまで - - switch (skillid) - { - case WZ_SIGHTRASHER: - clif_skill_damage (src, bl, tick, dmg.amotion, dmg.dmotion, - damage, dmg.div_, skillid, - (lv != 0) ? lv : skilllv, 5); - break; - case AS_SPLASHER: - clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, - damage, dmg.div_, skillid, -1, 5); - break; - case NPC_SELFDESTRUCTION: - case NPC_SELFDESTRUCTION2: - break; - default: - clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, - damage, dmg.div_, skillid, - (lv != 0) ? lv : skilllv, - (skillid == 0) ? 5 : type); - } - if (dmg.blewcount > 0 && !map[src->m].flag.gvg) - { /* 吹き飛ばし処理とそのパケット */ - if (skillid == WZ_SIGHTRASHER) - skill_blown (src, bl, dmg.blewcount); - else - skill_blown (dsrc, bl, dmg.blewcount); - if (bl->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) bl); - else - clif_fixpos (bl); - } - - map_freeblock_lock (); - /* 実際にダメージ処理を行う */ - if (skillid != KN_BOWLINGBASH || flag) - battle_damage (src, bl, damage, 0); - if (skillid == RG_INTIMIDATE && damage > 0 - && !(battle_get_mode (bl) & 0x20) && !map[src->m].flag.gvg) - { - int s_lv = battle_get_lv (src), t_lv = battle_get_lv (bl); - int rate = 50 + skilllv * 5; - rate = rate + (s_lv - t_lv); - if (MRAND (100) < rate) - skill_addtimerskill (src, tick + 800, bl->id, 0, 0, skillid, - skilllv, 0, flag); - } -/* - if(damage > 0 && dmg.flag&BF_SKILL && bl->type==BL_PC && pc_checkskill((struct map_session_data *)bl,RG_PLAGIARISM)){ - struct map_session_data *tsd = (struct map_session_data *)bl; - nullpo_retr(0, tsd); - if(!tsd->status.skill[skillid].id && !tsd->status.skill[skillid].id - && !(skillid > NPC_PIERCINGATT && skillid < NPC_SUMMONMONSTER) ){ - //既に盗んでいるスキルがあれば該当スキルを消す - if (tsd->cloneskill_id && tsd->cloneskill_lv && 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->cloneskill_lv=skilllv; - tsd->status.skill[skillid].id=skillid; - tsd->status.skill[skillid].lv=(pc_checkskill(tsd,RG_PLAGIARISM) > skill_get_max(skillid))? - skill_get_max(skillid):pc_checkskill(tsd,RG_PLAGIARISM); - tsd->status.skill[skillid].flag=13;//cloneskill flag - clif_skillinfoblock(tsd); - } - } -*/ - /* ダメージがあるなら追加効果判定 */ - if (bl->prev != NULL) - { - struct map_session_data *sd = (struct map_session_data *) bl; - nullpo_retr (0, sd); - if (bl->type != BL_PC || (sd && !pc_isdead (sd))) - { - if (damage > 0) - skill_additional_effect (src, bl, skillid, skilllv, - attack_type, tick); - if (bl->type == BL_MOB && src != bl) /* スキル使用条件のMOBスキル */ - { - struct mob_data *md = (struct mob_data *) bl; - nullpo_retr (0, md); - if (battle_config.mob_changetarget_byskill == 1) - { - int target; - target = md->target_id; - if (src->type == BL_PC) - 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 (src->type == BL_PC && dmg.flag & BF_WEAPON && src != bl && src == dsrc - && damage > 0) - { - struct map_session_data *sd = (struct map_session_data *) src; - int hp = 0, sp = 0; - nullpo_retr (0, sd); - if (sd->hp_drain_rate && sd->hp_drain_per > 0 && dmg.damage > 0 - && MRAND (100) < sd->hp_drain_rate) - { - hp += (dmg.damage * sd->hp_drain_per) / 100; - if (sd->hp_drain_rate > 0 && hp < 1) - hp = 1; - else if (sd->hp_drain_rate < 0 && hp > -1) - hp = -1; - } - if (sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 && dmg.damage2 > 0 - && MRAND (100) < sd->hp_drain_rate_) - { - hp += (dmg.damage2 * sd->hp_drain_per_) / 100; - if (sd->hp_drain_rate_ > 0 && hp < 1) - hp = 1; - else if (sd->hp_drain_rate_ < 0 && hp > -1) - hp = -1; - } - if (sd->sp_drain_rate > 0 && sd->sp_drain_per > 0 && dmg.damage > 0 - && MRAND (100) < sd->sp_drain_rate) - { - sp += (dmg.damage * sd->sp_drain_per) / 100; - if (sd->sp_drain_rate > 0 && sp < 1) - sp = 1; - else if (sd->sp_drain_rate < 0 && sp > -1) - sp = -1; - } - if (sd->sp_drain_rate_ > 0 && sd->sp_drain_per_ > 0 && dmg.damage2 > 0 - && MRAND (100) < sd->sp_drain_rate_) - { - sp += (dmg.damage2 * sd->sp_drain_per_) / 100; - if (sd->sp_drain_rate_ > 0 && sp < 1) - sp = 1; - else if (sd->sp_drain_rate_ < 0 && sp > -1) - sp = -1; - } - if (hp || sp) - pc_heal (sd, hp, sp); - } - - if ((skillid != KN_BOWLINGBASH || flag) && rdamage > 0) - battle_damage (bl, src, rdamage, 0); - - if (attack_type & BF_WEAPON && sc_data - && sc_data[SC_AUTOCOUNTER].timer != -1 - && sc_data[SC_AUTOCOUNTER].val4 > 0) - { - if (sc_data[SC_AUTOCOUNTER].val3 == dsrc->id) - battle_weapon_attack (bl, dsrc, tick, - 0x8000 | sc_data[SC_AUTOCOUNTER].val1); - skill_status_change_end (bl, SC_AUTOCOUNTER, -1); - } - - map_freeblock_unlock (); - - return (dmg.damage + dmg.damage2); /* 与ダメを返す */ -} - -/*========================================== - * スキル範囲攻撃用(map_foreachinareaから呼ばれる) - * flagについて:16進図を確認 - * MSB <- 00fTffff ->LSB - * T =ターゲット選択用(BCT_*) - * ffff=自由に使用可能 - * 0 =予約。0に固定 - *------------------------------------------ - */ -static int skill_area_temp[8]; /* 一時変数。必要なら使う。 */ -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, x, y, range, sx[4], sy[4]; - int t_range, tx[4], ty[4]; - int i, r_flag, 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; - - x = va_arg (ap, int); - y = va_arg (ap, int); - range = va_arg (ap, int); - skillid = va_arg (ap, int); - - if (skillid == MG_SAFETYWALL || skillid == AL_PNEUMA) - { - if (unit->group->unit_id != 0x7e && unit->group->unit_id != 0x85) - return 0; - } - else if (skillid == AL_WARP) - { - if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) - && unit->group->unit_id != 0x92) - return 0; - } - else if ((skillid >= HT_SKIDTRAP && skillid <= HT_CLAYMORETRAP) - || skillid == HT_TALKIEBOX) - { - if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) - && unit->group->unit_id != 0x92) - return 0; - } - else if (skillid == WZ_FIREPILLAR) - { - if (unit->group->unit_id != 0x87) - return 0; - } - else - return 0; - t_range = (unit->range != 0) ? unit->range : unit->group->range; - tx[0] = tx[3] = unit->bl.x - t_range; - tx[1] = tx[2] = unit->bl.x + t_range; - ty[0] = ty[1] = unit->bl.y - t_range; - ty[2] = ty[3] = unit->bl.y + t_range; - sx[0] = sx[3] = x - range; - sx[1] = sx[2] = x + range; - sy[0] = sy[1] = y - range; - sy[2] = sy[3] = y + range; - for (i = r_flag = 0; i < 4; i++) - { - if (sx[i] >= tx[0] && sx[i] <= tx[1] && sy[i] >= ty[0] - && sy[i] <= ty[2]) - { - r_flag = 1; - break; - } - if (tx[i] >= sx[0] && tx[i] <= sx[1] && ty[i] >= sy[0] - && ty[i] <= sy[2]) - { - r_flag = 1; - break; - } - } - if (r_flag) - (*c)++; - - return 0; -} - -int skill_check_unit_range (int m, int x, int y, int range, int skillid) -{ - int c = 0; - - map_foreachinarea (skill_check_unit_range_sub, m, x - 10, y - 10, x + 10, - y + 10, BL_SKILL, &c, x, y, range, skillid); - - return c; -} - -static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap) -{ - int *c; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, c = va_arg (ap, int *)); - - if (bl->prev == NULL || (bl->type != BL_PC && bl->type != BL_MOB)) - return 0; - - if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) - return 0; - - (*c)++; - - return 0; -} - -int skill_check_unit_range2 (int m, int x, int y, int range) -{ - int c = 0; - - map_foreachinarea (skill_check_unit_range2_sub, m, x - range, y - range, - x + range, y + range, 0, &c); - - return c; -} - -/*========================================================================= - * 範囲スキル使用処理小分けここから - */ -/* 対象の数をカウントする。(skill_area_temp[0]を初期化しておくこと) */ -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 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static void skill_timerskill (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = NULL; - struct mob_data *md = NULL; - struct block_list *src = map_id2bl (id), *target; - struct skill_timerskill *skl = NULL; - int range; - - nullpo_retv (src); - - if (src->prev == NULL) - return; - - if (src->type == BL_PC) - { - nullpo_retv (sd = (struct map_session_data *) src); - skl = &sd->skilltimerskill[data]; - } - else if (src->type == BL_MOB) - { - nullpo_retv (md = (struct mob_data *) src); - skl = &md->skilltimerskill[data]; - } - - else - return; - - nullpo_retv (skl); - - skl->timer = -1; - 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; //初期化してないのにアドレス突っ込んでいいのかな? - target->type = BL_NUL; - target->m = src->m; - target->prev = target->next = NULL; - } - } - if (target == NULL) - return; - if (target->prev == NULL && skl->skill_id != RG_INTIMIDATE) - return; - if (src->m != target->m) - return; - if (sd && pc_isdead (sd)) - return; - if (target->type == BL_PC - && pc_isdead ((struct map_session_data *) target) - && skl->skill_id != RG_INTIMIDATE) - return; - - switch (skl->skill_id) - { - case TF_BACKSLIDING: - clif_skill_nodamage (src, src, skl->skill_id, skl->skill_lv, - 1); - break; - case RG_INTIMIDATE: - if (sd && !map[src->m].flag.noteleport) - { - int x, y, i, j, c; - pc_randomwarp (sd, 3); - for (i = 0; i < 16; i++) - { - j = MRAND (8); - x = sd->bl.x + dirx[j]; - y = sd->bl.y + diry[j]; - if ((c = map_getcell (sd->bl.m, x, y)) != 1 && c != 5) - 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].name, 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, c; - mob_warp (md, -1, -1, -1, 3); - for (i = 0; i < 16; i++) - { - j = MRAND (8); - x = md->bl.x + dirx[j]; - y = md->bl.y + diry[j]; - if ((c = map_getcell (md->bl.m, x, y)) != 1 && c != 5) - 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].name, 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 = 15; //視界全体 - map_foreachinarea (skill_frostjoke_scream, src->m, - src->x - range, src->y - range, - src->x + range, src->y + range, 0, src, - skl->skill_id, skl->skill_lv, tick); - 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; - 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, - 0); - 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, 0); - break; - } - } -} - -/*========================================== - * - *------------------------------------------ - */ -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; - - 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; - } - - 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); - for (i = 0; i < MAX_SKILLTIMERSKILL; i++) - { - if (sd->skilltimerskill[i].timer != -1) - { - delete_timer (sd->skilltimerskill[i].timer, skill_timerskill); - sd->skilltimerskill[i].timer = -1; - } - } - } - 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; - } - } - } - - return 0; -} - -/* 範囲スキル使用処理小分けここまで - * ------------------------------------------------------------------------- - */ - -/*========================================== - * スキル使用(詠唱完了、ID指定攻撃系) - * (スパゲッティに向けて1歩前進!(ダメポ)) - *------------------------------------------ - */ -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; - int i; - - nullpo_retr (1, src); - nullpo_retr (1, bl); - - if (src->type == BL_PC) - sd = (struct map_session_data *) src; - if (sd && pc_isdead (sd)) - return 1; - - if ((skillid == WZ_SIGHTRASHER || skillid == CR_GRANDCROSS) && src != bl) - bl = src; - if (bl->prev == NULL) - return 1; - if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) - return 1; - map_freeblock_lock (); - switch (skillid) - { - /* 武器攻撃系スキル */ - case SM_BASH: /* バッシュ */ - case MC_MAMMONITE: /* メマーナイト */ - case AC_DOUBLE: /* ダブルストレイフィング */ - case AS_SONICBLOW: /* ソニックブロー */ - case KN_PIERCE: /* ピアース */ - case KN_SPEARBOOMERANG: /* スピアブーメラン */ - case TF_POISON: /* インベナム */ - case TF_SPRINKLESAND: /* 砂まき */ - case AC_CHARGEARROW: /* チャージアロー */ - case KN_SPEARSTAB: /* スピアスタブ */ - case RG_RAID: /* サプライズアタック */ - case RG_INTIMIDATE: /* インティミデイト */ - case BA_MUSICALSTRIKE: /* ミュージカルストライク */ - case DC_THROWARROW: /* 矢撃ち */ - case BA_DISSONANCE: /* 不協和音 */ - case CR_HOLYCROSS: /* ホーリークロス */ - case CR_SHIELDCHARGE: - case CR_SHIELDBOOMERANG: - - /* 以下MOB専用 */ - /* 単体攻撃、SP減少攻撃、遠距離攻撃、防御無視攻撃、多段攻撃 */ - case NPC_PIERCINGATT: - case NPC_MENTALBREAKER: - case NPC_RANGEATTACK: - case NPC_CRITICALSLASH: - case NPC_COMBOATTACK: - /* 必中攻撃、毒攻撃、暗黒攻撃、沈黙攻撃、スタン攻撃 */ - case NPC_GUIDEDATTACK: - case NPC_POISON: - case NPC_BLINDATTACK: - case NPC_SILENCEATTACK: - case NPC_STUNATTACK: - /* 石化攻撃、呪い攻撃、睡眠攻撃、ランダムATK攻撃 */ - case NPC_PETRIFYATTACK: - case NPC_CURSEATTACK: - case NPC_SLEEPATTACK: - case NPC_RANDOMATTACK: - /* 水属性攻撃、地属性攻撃、火属性攻撃、風属性攻撃 */ - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - /* 毒属性攻撃、聖属性攻撃、闇属性攻撃、念属性攻撃、SP減少攻撃 */ - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_TELEKINESISATTACK: - case LK_AURABLADE: /* オーラブレード */ - case LK_SPIRALPIERCE: /* スパイラルピアース */ - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - case LK_JOINTBEAT: /* ジョイントビート */ - case PA_PRESSURE: /* プレッシャー */ - case PA_SACRIFICE: /* サクリファイス */ - case SN_SHARPSHOOTING: /* シャープシューティング */ - case CG_ARROWVULCAN: /* アローバルカン */ - case ASC_BREAKER: /* ソウルブレーカー */ - case HW_MAGICCRASHER: /* マジッククラッシャー */ - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - break; - case NPC_DARKBREATH: - clif_emotion (src, 7); - skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, - flag); - break; - case MO_INVESTIGATE: /* 発勁 */ - { - struct status_change *sc_data = battle_get_sc_data (src); - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - if (sc_data[SC_BLADESTOP].timer != -1) - skill_status_change_end (src, SC_BLADESTOP, -1); - } - break; - case SN_FALCONASSAULT: /* ファルコンアサルト */ - skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, - flag); - break; - case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ - { - struct mob_data *md = (struct mob_data *) bl; - nullpo_retr (1, md); - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - if (md->hp > 0) - { - skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); - if (bl->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) bl); - else - clif_fixpos (bl); - } - } - break; - case RG_BACKSTAP: /* バックスタブ */ - { - int dir = map_calc_dir (src, bl->x, bl->y), t_dir = - battle_get_dir (bl); - int dist = distance (src->x, src->y, bl->x, bl->y); - if ((dist > 0 && !map_check_dir (dir, t_dir)) - || bl->type == BL_SKILL) - { - struct status_change *sc_data = battle_get_sc_data (src); - if (sc_data && sc_data[SC_HIDING].timer != -1) - skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); - } - else if (src->type == BL_PC) - clif_skill_fail (sd, sd->skillid, 0, 0); - } - break; - - case AM_ACIDTERROR: /* アシッドテラー */ - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - if (bl->type == BL_PC - && MRAND (100) < skill_get_time (skillid, skilllv) - && battle_config.equipment_breaking) - pc_breakarmor ((struct map_session_data *) bl); - break; - case MO_FINGEROFFENSIVE: /* 指弾 */ - { - struct status_change *sc_data = battle_get_sc_data (src); - - if (!battle_config.finger_offensive_type) - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - else - { - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - if (sd) - { - 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) - skill_status_change_end (src, SC_BLADESTOP, -1); - } - break; - case MO_CHAINCOMBO: /* 連打掌 */ - { - struct status_change *sc_data = battle_get_sc_data (src); - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - if (sc_data && sc_data[SC_BLADESTOP].timer != -1) - skill_status_change_end (src, SC_BLADESTOP, -1); - } - break; - case MO_COMBOFINISH: /* 猛龍拳 */ - case CH_TIGERFIST: /* 伏虎拳 */ - case CH_CHAINCRUSH: /* 連柱崩撃 */ - case CH_PALMSTRIKE: /* 猛虎硬派山 */ - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - break; - case MO_EXTREMITYFIST: /* 阿修羅覇鳳拳 */ - { - struct status_change *sc_data = battle_get_sc_data (src); - - if (sd) - { - struct walkpath_data wpd; - int dx, dy; - - 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, sd->skillid, 0, 0); - break; - } - } - sd->to_x = sd->bl.x + dx; - sd->to_y = sd->bl.y + dy; - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - 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; - pc_movepos (sd, sd->to_x, sd->to_y); - skill_status_change_end (&sd->bl, SC_COMBO, -1); - } - else - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - flag); - skill_status_change_end (src, SC_EXPLOSIONSPIRITS, -1); - if (sc_data && sc_data[SC_BLADESTOP].timer != -1) - skill_status_change_end (src, SC_BLADESTOP, -1); - } - break; - /* 武器系範囲攻撃スキル */ - case AC_SHOWER: /* アローシャワー */ - case SM_MAGNUM: /* マグナムブレイク */ - case AS_GRIMTOOTH: /* グリムトゥース */ - case MC_CARTREVOLUTION: /* カートレヴォリューション */ - case NPC_SPLASHATTACK: /* スプラッシュアタック */ - case ASC_METEORASSAULT: /* メテオアサルト */ - case AS_SPLASHER: /* [Valaris] */ - if (flag & 1) - { - /* 個別にダメージを与える */ - if (bl->id != skill_area_temp[1]) - { - int dist = 0; - if (skillid == SM_MAGNUM) - { /* マグナムブレイクなら中心からの距離を計算 */ - int dx = abs (bl->x - skill_area_temp[2]); - int dy = abs (bl->y - skill_area_temp[3]); - dist = ((dx > dy) ? dx : dy); - } - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, - tick, 0x0500 | dist); - } - } - else - { - int ar = 1; - int x = bl->x, y = bl->y; - if (skillid == SM_MAGNUM) - { - x = src->x; - y = src->y; - } - else if (skillid == AC_SHOWER || skillid == ASC_METEORASSAULT) /* アローシャワー、メテオアサルト範囲5*5 */ - ar = 2; - else if (skillid == AS_SPLASHER) /* ベナムスプラッシャー範囲3*3 */ - ar = 1; - else if (skillid == NPC_SPLASHATTACK) /* スプラッシュアタックは範囲7*7 */ - ar = 3; - skill_area_temp[1] = bl->id; - skill_area_temp[2] = x; - skill_area_temp[3] = y; - /* まずターゲットに攻撃を加える */ - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, - 0); - /* その後ターゲット以外の範囲内の敵全体に処理を行う */ - map_foreachinarea (skill_area_sub, - bl->m, x - ar, y - ar, x + ar, y + ar, 0, - src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - 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 damage; - map_freeblock_lock (); - damage = - skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, - tick, 0); - if (damage > 0) - { - int i, c; /* 他人から聞いた動きなので間違ってる可能性大&効率が悪いっす>< */ - c = skill_get_blewcount (skillid, skilllv); - if (map[bl->m].flag.gvg) - c = 0; - for (i = 0; i < c; i++) - { - skill_blown (src, bl, 1); - if (bl->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) bl); - else - clif_fixpos (bl); - skill_area_temp[0] = 0; - map_foreachinarea (skill_area_sub, - bl->m, bl->x - 1, bl->y - 1, - bl->x + 1, bl->y + 1, 0, src, - skillid, skilllv, tick, - flag | BCT_ENEMY, - skill_area_sub_count); - if (skill_area_temp[0] > 1) - break; - } - skill_area_temp[1] = bl->id; - skill_area_temp[2] = bl->x; - skill_area_temp[3] = bl->y; - /* その後ターゲット以外の範囲内の敵全体に処理を行う */ - map_foreachinarea (skill_area_sub, - bl->m, bl->x - 1, bl->y - 1, bl->x + 1, - bl->y + 1, 0, src, skillid, skilllv, - tick, flag | BCT_ENEMY | 1, - skill_castend_damage_id); - battle_damage (src, bl, damage, 1); - if (rdamage > 0) - battle_damage (bl, src, rdamage, 0); - } - map_freeblock_unlock (); - } - break; - - case ALL_RESURRECTION: /* リザレクション */ - case PR_TURNUNDEAD: /* ターンアンデッド */ - if (bl->type != BL_PC - && battle_check_undead (battle_get_race (bl), - battle_get_elem_type (bl))) - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, - flag); - else - { - map_freeblock_unlock (); - return 1; - } - break; - - /* 魔法系スキル */ - case MG_SOULSTRIKE: /* ソウルストライク */ - case MG_COLDBOLT: /* コールドボルト */ - case MG_FIREBOLT: /* ファイアーボルト */ - case MG_LIGHTNINGBOLT: /* ライトニングボルト */ - case WZ_EARTHSPIKE: /* アーススパイク */ - case AL_HEAL: /* ヒール */ - case AL_HOLYLIGHT: /* ホーリーライト */ - case MG_FROSTDIVER: /* フロストダイバー */ - case WZ_JUPITEL: /* ユピテルサンダー */ - case NPC_MAGICALATTACK: /* MOB:魔法打撃攻撃 */ - case PR_ASPERSIO: /* アスペルシオ */ - 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) - skill_status_change_start (src, SC_WATERBALL, skilllv, bl->id, - 0, 0, 0, 0); - break; - - case PR_BENEDICTIO: /* 聖体降福 */ - if (battle_get_race (bl) == 1 || battle_get_race (bl) == 6) - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, - flag); - break; - - /* 魔法系範囲攻撃スキル */ - case MG_NAPALMBEAT: /* ナパームビート */ - case MG_FIREBALL: /* ファイヤーボール */ - if (flag & 1) - { - /* 個別にダメージを与える */ - if (bl->id != skill_area_temp[1]) - { - if (skillid == MG_FIREBALL) - { /* ファイヤーボールなら中心からの距離を計算 */ - int dx = abs (bl->x - skill_area_temp[2]); - int dy = abs (bl->y - skill_area_temp[3]); - skill_area_temp[0] = ((dx > dy) ? dx : dy); - } - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, - tick, skill_area_temp[0] | 0x0500); - } - } - else - { - int ar = (skillid == MG_NAPALMBEAT) ? 1 : 2; - skill_area_temp[1] = bl->id; - if (skillid == MG_NAPALMBEAT) - { /* ナパームでは先に数える */ - skill_area_temp[0] = 0; - map_foreachinarea (skill_area_sub, - bl->m, bl->x - 1, bl->y - 1, bl->x + 1, - bl->y + 1, 0, src, skillid, skilllv, - tick, flag | BCT_ENEMY, - skill_area_sub_count); - } - else - { - skill_area_temp[0] = 0; - skill_area_temp[2] = bl->x; - skill_area_temp[3] = bl->y; - } - /* まずターゲットに攻撃を加える */ - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, - skill_area_temp[0]); - /* その後ターゲット以外の範囲内の敵全体に処理を行う */ - map_foreachinarea (skill_area_sub, - bl->m, bl->x - ar, bl->y - ar, bl->x + ar, - bl->y + ar, 0, 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 - { - int ar = (skillid == HW_NAPALMVULCAN) ? 1 : 2; - skill_area_temp[1] = bl->id; - if (skillid == HW_NAPALMVULCAN) - { - skill_area_temp[0] = 0; - map_foreachinarea (skill_area_sub, - bl->m, bl->x - 1, bl->y - 1, bl->x + 1, - bl->y + 1, 0, src, skillid, skilllv, - tick, flag | BCT_ENEMY, - skill_area_sub_count); - } - else - { - skill_area_temp[0] = 0; - skill_area_temp[2] = bl->x; - skill_area_temp[3] = bl->y; - } - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, - skill_area_temp[0]); - map_foreachinarea (skill_area_sub, - bl->m, bl->x - ar, bl->y - ar, bl->x + ar, - bl->y + ar, 0, src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - break; - - case WZ_FROSTNOVA: /* フロストノヴァ */ - skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); - skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, - flag); - break; - - case WZ_SIGHTRASHER: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); - skill_status_change_end (src, SC_SIGHT, -1); - break; - - /* その他 */ - case HT_BLITZBEAT: /* ブリッツビート */ - if (flag & 1) - { - /* 個別にダメージを与える */ - if (bl->id != skill_area_temp[1]) - skill_attack (BF_MISC, src, src, bl, skillid, skilllv, - tick, - skill_area_temp[0] | (flag & 0xf00000)); - } - else - { - skill_area_temp[0] = 0; - skill_area_temp[1] = bl->id; - if (flag & 0xf00000) - map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, - bl->y - 1, bl->x + 1, bl->y + 1, 0, - src, skillid, skilllv, tick, - flag | BCT_ENEMY, - skill_area_sub_count); - /* まずターゲットに攻撃を加える */ - skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, - skill_area_temp[0] | (flag & 0xf00000)); - /* その後ターゲット以外の範囲内の敵全体に処理を行う */ - map_foreachinarea (skill_area_sub, - bl->m, bl->x - 1, bl->y - 1, bl->x + 1, - bl->y + 1, 0, src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - break; - - case CR_GRANDCROSS: /* グランドクロス */ - /* スキルユニット配置 */ - skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); - if (sd) - sd->canmove_tick = tick + 1000; - else if (src->type == BL_MOB) - mob_changestate ((struct mob_data *) src, MS_DELAY, 1000); - break; - - case TF_THROWSTONE: /* 石投げ */ - case NPC_SMOKING: /* スモーキング */ - skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, 0); - break; - - case NPC_SELFDESTRUCTION: /* 自爆 */ - case NPC_SELFDESTRUCTION2: /* 自爆2 */ - if (flag & 1) - { - /* 個別にダメージを与える */ - if (src->type == BL_MOB) - { - struct mob_data *mb = (struct mob_data *) src; - nullpo_retr (1, mb); - mb->hp = skill_area_temp[2]; - if (bl->id != skill_area_temp[1]) - skill_attack (BF_MISC, src, src, bl, - NPC_SELFDESTRUCTION, skilllv, tick, - flag); - mb->hp = 1; - } - } - else - { - struct mob_data *md; - if ((md = (struct mob_data *) src)) - { - skill_area_temp[1] = bl->id; - skill_area_temp[2] = battle_get_hp (src); - clif_skill_nodamage (src, src, NPC_SELFDESTRUCTION, -1, - 1); - map_foreachinarea (skill_area_sub, bl->m, bl->x - 5, - bl->y - 5, bl->x + 5, bl->y + 5, 0, - src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - battle_damage (src, src, md->hp, 0); - } - } - break; - - /* HP吸収/HP吸収魔法 */ - case NPC_BLOODDRAIN: - case NPC_ENERGYDRAIN: - { - int heal; - 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.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, 0, src, - skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - } - break; - - default: - map_freeblock_unlock (); - return 1; - } - map_freeblock_unlock (); - - return 0; -} - -/*========================================== - * スキル使用(詠唱完了、ID指定支援系) - *------------------------------------------ - */ -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, strip_fix, strip_time, strip_per; - int sc_dex, sc_luk; - //クラスチェンジ用ボスモンスターID - int changeclass[] = - { 1038, 1039, 1046, 1059, 1086, 1087, 1112, 1115, 1157, 1159, 1190, - 1272, 1312, 1373, 1492 - }; - int poringclass[] = { 1002 }; - - nullpo_retr (1, src); - nullpo_retr (1, bl); - - if (src->type == BL_PC) - sd = (struct map_session_data *) src; - else if (src->type == BL_MOB) - md = (struct mob_data *) src; - - sc_dex = battle_get_mdef (bl); - sc_luk = battle_get_luk (bl); - sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); - sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); - sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3); - strip_fix = battle_get_dex (src) - battle_get_dex (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 (sc_def_vit > 50) - sc_def_vit = 50; - if (sc_def_mdef > 50) - sc_def_mdef = 50; - } - if (sc_def_vit < 0) - sc_def_vit = 0; - if (sc_def_mdef < 0) - sc_def_mdef = 0; - if (strip_fix < 0) - strip_fix = 0; - - if (bl == NULL || bl->prev == NULL) - return 1; - if (sd && pc_isdead (sd)) - return 1; - if (dstsd && pc_isdead (dstsd) && skillid != ALL_RESURRECTION) - return 1; - if (battle_get_class (bl) == 1288) - return 1; - if (skillnotok (skillid, (struct map_session_data *) bl)) // [MouseJstr] - return 0; - - map_freeblock_lock (); - switch (skillid) - { - case AL_HEAL: /* ヒール */ - { - int heal = skill_calc_heal (src, skilllv); - int heal_get_jobexp; - int skill; - struct pc_base_job s_class; - - if (dstsd && dstsd->special_state.no_magic_damage) - heal = 0; /* 黄金蟲カード(ヒール量0) */ - if (sd) - { - s_class = pc_calc_base_job (sd->status.pc_class); - if ((skill = pc_checkskill (sd, HP_MEDITATIO)) > 0) // メディテイティオ - heal += heal * (skill * 2 / 100); - if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && s_class.job == 23 && sd->status.sex == 0) //自分も対象もPC、対象が自分のパートナー、自分がスパノビ、自分が♀なら - heal = heal * 2; //スパノビの嫁が旦那にヒールすると2倍になる - } - - clif_skill_nodamage (src, bl, skillid, heal, 1); - heal_get_jobexp = battle_heal (NULL, bl, heal, 0, 0); - - // JOB経験値獲得 - if (src->type == BL_PC && bl->type == BL_PC && heal > 0 - && src != bl && 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; - pc_gainexp ((struct map_session_data *) src, 0, - heal_get_jobexp); - } - } - break; - - case ALL_RESURRECTION: /* リザレクション */ - if (bl->type == BL_PC) - { - int per = 0; - struct map_session_data *tsd = (struct map_session_data *) bl; - nullpo_retr (1, tsd); - if ((map[bl->m].flag.pvp) && tsd->pvp_point < 0) - break; /* PVPで復活不可能状態 */ - - if (pc_isdead (tsd)) - { /* 死亡判定 */ - 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; - } - tsd->status.hp = tsd->status.max_hp * per / 100; - if (tsd->status.hp <= 0) - tsd->status.hp = 1; - if (tsd->special_state.restart_full_recover) - { /* オシリスカード */ - tsd->status.hp = tsd->status.max_hp; - tsd->status.sp = tsd->status.max_sp; - } - pc_setstand (tsd); - if (battle_config.pc_invincible_time > 0) - pc_setinvincibletimer (tsd, - battle_config.pc_invincible_time); - clif_updatestatus (tsd, SP_HP); - clif_resurrection (&tsd->bl, 1); - if (src != bl && sd && battle_config.resurrection_exp > 0) - { - int exp = 0, jexp = 0; - int lv = - tsd->status.base_level - sd->status.base_level, - jlv = - tsd->status.job_level - sd->status.job_level; - if (lv > 0) - { - exp = - (int) ((double) tsd->status.base_exp * - (double) lv * - (double) battle_config.resurrection_exp - / 1000000.); - if (exp < 1) - exp = 1; - } - if (jlv > 0) - { - jexp = - (int) ((double) tsd->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 (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (MRAND (100) < - (50 + skilllv * 3 + - (battle_get_lv (src) + battle_get_int (src) / 5) - - sc_def_mdef)) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), - 0); - } - break; - - case AL_CRUCIS: - if (flag & 1) - { - int race = battle_get_race (bl), ele = - battle_get_elem_type (bl); - if (battle_check_target (src, bl, BCT_ENEMY) - && (race == 6 || battle_check_undead (race, ele))) - { - int slv = battle_get_lv (src), tlv = - battle_get_lv (bl), rate; - rate = 25 + skilllv * 2 + slv - tlv; - if (MRAND (100) < rate) - skill_status_change_start (bl, - SkillStatusChangeTable - [skillid], skilllv, 0, 0, - 0, 0, 0); - } - } - else - { - int range = 15; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - map_foreachinarea (skill_area_sub, - src->m, src->x - range, src->y - range, - src->x + range, src->y + range, 0, src, - skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_nodamage_id); - } - break; - - case PR_LEXDIVINA: /* レックスディビーナ */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (sc_data && sc_data[SC_DIVINA].timer != -1) - skill_status_change_end (bl, SC_DIVINA, -1); - else if (MRAND (100) < sc_def_vit) - { - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), - 0); - } - } - break; - case SA_ABRACADABRA: - break; - case SA_COMA: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (dstsd) - { - dstsd->status.hp = 1; - dstsd->status.sp = 1; - clif_updatestatus (dstsd, SP_HP); - clif_updatestatus (dstsd, SP_SP); - } - if (dstmd) - dstmd->hp = 1; - break; - case SA_FULLRECOVERY: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (dstsd) - pc_heal (dstsd, dstsd->status.max_hp, dstsd->status.max_sp); - if (dstmd) - dstmd->hp = battle_get_max_hp (&dstmd->bl); - break; - case SA_SUMMONMONSTER: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (sd) - mob_once_spawn (sd, map[sd->bl.m].name, sd->bl.x, sd->bl.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); - if (sd) - pc_damage (NULL, sd, sd->status.max_hp); - break; - - case SA_QUESTION: - case SA_GRAVITY: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - break; - case SA_CLASSCHANGE: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (dstmd) - mob_class_change (dstmd, changeclass); - break; - case SA_MONOCELL: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (dstmd) - mob_class_change (dstmd, poringclass); - break; - case SA_DEATH: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (dstsd) - pc_damage (NULL, dstsd, dstsd->status.max_hp); - if (dstmd) - mob_damage (NULL, dstmd, dstmd->hp, 1); - break; - case SA_REVERSEORCISH: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (dstsd) - pc_setoption (dstsd, dstsd->status.option | 0x0800); - break; - case SA_FORTUNE: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (sd) - pc_getzeny (sd, battle_get_lv (bl) * 100); - break; - case AL_INCAGI: /* 速度増加 */ - case AL_BLESSING: /* ブレッシング */ - case PR_SLOWPOISON: - case PR_IMPOSITIO: /* イムポシティオマヌス */ - case PR_LEXAETERNA: /* レックスエーテルナ */ - case PR_SUFFRAGIUM: /* サフラギウム */ - case PR_BENEDICTIO: /* 聖体降福 */ - case CR_PROVIDENCE: /* プロヴィデンス */ - case CG_MARIONETTE: /* マリオネットコントロール */ - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - } - else - { - skill_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_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] - case SA_FROSTWEAPON: - case SA_LIGHTNINGLOADER: - case SA_SEISMICWEAPON: - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - break; - } - if (bl->type == BL_PC) - { - struct map_session_data *sd2 = (struct map_session_data *) bl; - if (sd2->status.weapon == 0 - || sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 - || sd2->sc_data[SC_FROSTWEAPON].timer != -1 - || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 - || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 - || sd2->sc_data[SC_ENCPOISON].timer != -1) - { - clif_skill_fail (sd, skillid, 0, 0); - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - break; - } - } - if (MRAND (100) > (75 + skilllv * 1) && (skilllv != 5)) - { - clif_skill_fail (sd, skillid, 0, 0); - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - if (bl->type == BL_PC && battle_config.equipment_breaking) - { - struct map_session_data *sd2 = - (struct map_session_data *) bl; - if (sd != sd2) - clif_displaymessage (sd->fd, - "You broke target's weapon"); - pc_breakweapon (sd2); - } - break; - } - else - { - skill_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: /* アスペルシオ */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (bl->type == BL_MOB) - break; - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case PR_KYRIE: /* キリエエレイソン */ - clif_skill_nodamage (bl, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case KN_AUTOCOUNTER: /* オートカウンター */ - case KN_TWOHANDQUICKEN: /* ツーハンドクイッケン */ - case CR_SPEARQUICKEN: /* スピアクイッケン */ - case CR_REFLECTSHIELD: - case AS_POISONREACT: /* ポイズンリアクト */ - case MC_LOUD: /* ラウドボイス */ - case MG_ENERGYCOAT: /* エナジーコート */ - case SM_ENDURE: /* インデュア */ - case MG_SIGHT: /* サイト */ - case AL_RUWACH: /* ルアフ */ - case MO_EXPLOSIONSPIRITS: // 爆裂波動 - case MO_STEELBODY: // 金剛 - case LK_AURABLADE: /* オーラブレード */ - case LK_PARRYING: /* パリイング */ - case LK_CONCENTRATION: /* コンセントレーション */ - case LK_BERSERK: /* バーサーク */ - case HP_ASSUMPTIO: /* */ - case WS_CARTBOOST: /* カートブースト */ - case SN_SIGHT: /* トゥルーサイト */ - case WS_MELTDOWN: /* メルトダウン */ - case ST_REJECTSWORD: /* リジェクトソード */ - case HW_MAGICPOWER: /* 魔法力増幅 */ - case PF_MEMORIZE: /* メモライズ */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case AS_ENCHANTPOISON: // Prevent spamming [Valaris] - if (bl->type == BL_PC) - { - struct map_session_data *sd2 = (struct map_session_data *) bl; - if (sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 - || sd2->sc_data[SC_FROSTWEAPON].timer != -1 - || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 - || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 - || sd2->sc_data[SC_ENCPOISON].timer != -1) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - clif_skill_fail (sd, skillid, 0, 0); - break; - } - } - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case LK_TENSIONRELAX: /* テンションリラックス */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - pc_setsit (sd); - clif_sitting (sd->fd, sd); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case MC_CHANGECART: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - break; - case AC_CONCENTRATION: /* 集中力向上 */ - { - int range = 1; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - map_foreachinarea (skill_status_change_timer_sub, src->m, - src->x - range, src->y - range, src->x + range, - src->y + range, 0, src, - SkillStatusChangeTable[skillid], tick); - } - break; - case SM_PROVOKE: /* プロボック */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - - /* MVPmobと不死には効かない */ - if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない - { - map_freeblock_unlock (); - return 1; - } - - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - - if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 - skill_castcancel (bl, 0); - if (dstsd && dstsd->skilltimer != -1 - && (!dstsd->special_state.no_castcancel - || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel - && !dstsd->special_state.no_castcancel2) - skill_castcancel (bl, 0); - - if (sc_data) - { - if (sc_data[SC_FREEZE].timer != -1) - skill_status_change_end (bl, SC_FREEZE, -1); - if (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0) - skill_status_change_end (bl, SC_STONE, -1); - if (sc_data[SC_SLEEP].timer != -1) - skill_status_change_end (bl, SC_SLEEP, -1); - } - - if (bl->type == BL_MOB) - { - int range = skill_get_range (skillid, skilllv); - if (range < 0) - range = battle_get_range (src) - (range + 1); - mob_target ((struct mob_data *) bl, src, range); - } - } - break; - - case CR_DEVOTION: /* ディボーション */ - if (sd && dstsd) - { - //転生や養子の場合の元の職業を算出する - - int lv = sd->status.base_level - dstsd->status.base_level; - lv = (lv < 0) ? -lv : lv; - if ((dstsd->bl.type != BL_PC) // 相手はPCじゃないとだめ - || (sd->bl.id == dstsd->bl.id) // 相手が自分はだめ - || (lv > 10) // レベル差±10まで - || (!sd->status.party_id && !sd->status.guild_id) // PTにもギルドにも所属無しはだめ - || ((sd->status.party_id != dstsd->status.party_id) // 同じパーティーか、 - || (sd->status.guild_id != dstsd->status.guild_id)) // 同じギルドじゃないとだめ - || (dstsd->status.pc_class == 14 || dstsd->status.pc_class == 21 - || dstsd->status.pc_class == 4015 - || dstsd->status.pc_class == 4022)) - { // クルセだめ - clif_skill_fail (sd, skillid, 0, 0); - map_freeblock_unlock (); - return 1; - } - for (i = 0; i < skilllv; i++) - { - if (!sd->dev.val1[i]) - { // 空きがあったら入れる - sd->dev.val1[i] = bl->id; - sd->dev.val2[i] = bl->id; - break; - } - else if (i == skilllv - 1) - { // 空きがなかった - clif_skill_fail (sd, skillid, 0, 0); - map_freeblock_unlock (); - return 1; - } - } - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - clif_devotion (sd, bl->id); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - src->id, 1, 0, 0, - 1000 * (15 + 15 * skilllv), 0); - } - else - 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_BLADESTOP: // 白刃取り - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (src, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case MO_ABSORBSPIRITS: // 気奪 - i = 0; - if (sd && dstsd) - { - if (sd == dstsd || map[sd->bl.m].flag.pvp - || map[sd->bl.m].flag.gvg) - { - if (dstsd->spiritball > 0) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - i = dstsd->spiritball * 7; - pc_delspiritball (dstsd, dstsd->spiritball, 0); - if (i > 0x7FFF) - i = 0x7FFF; - if (sd->status.sp + i > sd->status.max_sp) - i = sd->status.max_sp - sd->status.sp; - } - } - } - else if (sd && dstmd) - { //対象がモンスターの場合 - //20%の確率で対象のLv*2のSPを回復する。成功したときはターゲット(σ゚Д゚)σゲッツ!! - if (MRAND (100) < 20) - { - i = 2 * mob_db[dstmd->mob_class].lv; - mob_target (dstmd, src, 0); - } - } - if (i) - { - sd->status.sp += i; - clif_heal (sd->fd, SP_SP, i); - } - else - 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,32); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - }*/ - break; - case WS_CREATECOIN: /* クリエイトコイン */ -/* if(sd) { - clif_skill_produce_mix_list(sd,64); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - }*/ - break; - case WS_CREATENUGGET: /* 塊製造 */ -/* if(sd) { - clif_skill_produce_mix_list(sd,128); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - }*/ - break; - case BS_HAMMERFALL: /* ハンマーフォール */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_weapon_damage) - break; - if (MRAND (100) < (20 + 10 * skilllv) * sc_def_vit / 100) - { - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - } - break; - - case RG_RAID: /* サプライズアタック */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - { - int x = bl->x, y = bl->y; - skill_area_temp[1] = bl->id; - skill_area_temp[2] = x; - skill_area_temp[3] = y; - map_foreachinarea (skill_area_sub, - bl->m, x - 1, y - 1, x + 1, y + 1, 0, - src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 - 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], 0, 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], 0, 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], 0, 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)) - { - /* 個別の処理 */ - clif_skill_nodamage (bl, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), - 0); - } - else - { - /* パーティ全体への処理 */ - 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_WEAPONPERFECT: /* ウェポンパーフェクション */ - case BS_OVERTHRUST: /* オーバートラスト */ - if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) - { - /* 個別の処理 */ - clif_skill_nodamage (bl, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, (src == bl) ? 1 : 0, 0, 0, - skill_get_time (skillid, skilllv), - 0); - } - else - { - /* パーティ全体への処理 */ - 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: /* オートガード */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - int sc = SkillStatusChangeTable[skillid]; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (tsc_data) - { - if (tsc_data[sc].timer == -1) - /* 付加する */ - skill_status_change_start (bl, sc, skilllv, 0, 0, 0, - skill_get_time (skillid, - skilllv), 0); - else - /* 解除する */ - skill_status_change_end (bl, sc, -1); - } - } - break; - - case TF_HIDING: /* ハイディング */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - int sc = SkillStatusChangeTable[skillid]; - clif_skill_nodamage (src, bl, skillid, -1, 1); - if (tsc_data) - { - if (tsc_data[sc].timer == -1) - /* 付加する */ - skill_status_change_start (bl, sc, skilllv, 0, 0, 0, - skill_get_time (skillid, - skilllv), 0); - else - /* 解除する */ - skill_status_change_end (bl, sc, -1); - } - } - break; - - case AS_CLOAKING: /* クローキング */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - int sc = SkillStatusChangeTable[skillid]; - clif_skill_nodamage (src, bl, skillid, -1, 1); - if (tsc_data) - { - if (tsc_data[sc].timer == -1) - /* 付加する */ - skill_status_change_start (bl, sc, skilllv, 0, 0, 0, - skill_get_time (skillid, - skilllv), 0); - else - /* 解除する */ - skill_status_change_end (bl, sc, -1); - } - - skill_check_cloaking (bl); - } - break; - - case ST_CHASEWALK: /* ハイディング */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - int sc = SkillStatusChangeTable[skillid]; - clif_skill_nodamage (src, bl, skillid, -1, 1); - if (tsc_data) - { - if (tsc_data[sc].timer == -1) - /* 付加する */ - skill_status_change_start (bl, sc, skilllv, 0, 0, 0, - skill_get_time (skillid, - skilllv), 0); - else - /* 解除する */ - skill_status_change_end (bl, sc, -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: /* 不死身のジークフリード */ - case BA_DISSONANCE: /* 不協和音 */ - case BA_POEMBRAGI: /* ブラギの詩 */ - case BA_WHISTLE: /* 口笛 */ - case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ - case BA_APPLEIDUN: /* イドゥンの林檎 */ - case DC_UGLYDANCE: /* 自分勝手なダンス */ - case DC_HUMMING: /* ハミング */ - case DC_DONTFORGETME: /* 私を忘れないで… */ - case DC_FORTUNEKISS: /* 幸運のキス */ - case DC_SERVICEFORYOU: /* サービスフォーユー */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); - break; - - case HP_BASILICA: /* バジリカ */ - case PA_GOSPEL: /* ゴスペル */ - skill_clear_unitgroup (src); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); - break; - - case BD_ADAPTATION: /* アドリブ */ - { - struct status_change *sc_data = battle_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, 0); - } - } - break; - - case BA_FROSTJOKE: /* 寒いジョーク */ - case DC_SCREAM: /* スクリーム */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_addtimerskill (src, tick + 3000, bl->id, 0, 0, skillid, - skilllv, 0, flag); - break; - - case TF_STEAL: // スティール - if (sd) - { - if (pc_steal_item (sd, bl)) - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - else - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - } - break; - - case RG_STEALCOIN: // スティールコイン - if (sd) - { - if (pc_steal_coin (sd, bl)) - { - int range = skill_get_range (skillid, skilllv); - if (range < 0) - range = battle_get_range (src) - (range + 1); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - mob_target ((struct mob_data *) bl, src, range); - } - else - clif_skill_nodamage (src, bl, skillid, skilllv, 0); - } - break; - - case MG_STONECURSE: /* ストーンカース */ - if (bl->type == BL_MOB && battle_get_mode (bl) & 0x20) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - break; - } - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (MRAND (100) < skilllv * 4 + 20 - && !battle_check_undead (battle_get_race (bl), - battle_get_elem_type (bl))) - skill_status_change_start (bl, SC_STONE, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - else if (sd) - clif_skill_fail (sd, skillid, 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: /* キュアー */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_status_change_end (bl, SC_SILENCE, -1); - skill_status_change_end (bl, SC_BLIND, -1); - skill_status_change_end (bl, SC_CONFUSION, -1); - if (battle_check_undead - (battle_get_race (bl), battle_get_elem_type (bl))) - { //アンデッドなら暗闇効果 - skill_status_change_start (bl, SC_CONFUSION, 1, 0, 0, 0, 6000, - 0); - } - break; - - case TF_DETOXIFY: /* 解毒 */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_end (bl, SC_POISON, -1); - break; - - case PR_STRECOVERY: /* リカバリー */ - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_status_change_end (bl, SC_FREEZE, -1); - skill_status_change_end (bl, SC_STONE, -1); - skill_status_change_end (bl, SC_SLEEP, -1); - skill_status_change_end (bl, SC_STAN, -1); - if (battle_check_undead - (battle_get_race (bl), battle_get_elem_type (bl))) - { //アンデッドなら暗闇効果 - int blind_time; - //blind_time=30-battle_get_vit(bl)/10-battle_get_int/15; - blind_time = - 30 * (100 - - (battle_get_int (bl) + - battle_get_vit (bl)) / 2) / 100; - if (MRAND (100) < - (100 - - (battle_get_int (bl) / 2 + battle_get_vit (bl) / 3 + - battle_get_luk (bl) / 10))) - skill_status_change_start (bl, SC_BLIND, 1, 0, 0, 0, - blind_time, 0); - } - if (dstmd) - { - dstmd->attacked_id = 0; - dstmd->target_id = 0; - dstmd->state.targettype = NONE_ATTACKABLE; - dstmd->state.skillstate = MSS_IDLE; - dstmd->next_walktime = tick + MRAND (3000) + 3000; - } - } - break; - - case WZ_ESTIMATION: /* モンスター情報 */ - if (src->type == BL_PC) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - clif_skill_estimation ((struct map_session_data *) src, bl); - } - break; - - case MC_IDENTIFY: /* アイテム鑑定 */ - if (sd) - clif_item_identify_list (sd); - break; - - case BS_REPAIRWEAPON: /* 武器修理 */ - if (sd) -//動作しないのでとりあえずコメントアウト -// clif_item_repair_list(sd); - break; - - case AL_TELEPORT: /* テレポート */ - if (sd) - { - if (map[sd->bl.m].flag.noteleport) - { /* テレポ禁止 */ - clif_skill_teleportmessage (sd, 0); - break; - } - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (sd->skilllv == 1) - clif_skill_warppoint (sd, sd->skillid, "Random", "", "", - ""); - else - { - clif_skill_warppoint (sd, sd->skillid, "Random", - sd->status.save_point.map, "", ""); - } - } - else if (bl->type == BL_MOB) - mob_warp ((struct mob_data *) bl, -1, -1, -1, 3); - break; - - case AL_HOLYWATER: /* アクアベネディクタ */ - if (sd) - { - int eflag; - struct item item_tmp; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - memset (&item_tmp, 0, sizeof (item_tmp)); - item_tmp.nameid = 523; - item_tmp.identify = 1; - if (battle_config.holywater_name_input) - { - item_tmp.card[0] = 0xfe; - item_tmp.card[1] = 0; - *((unsigned long *) (&item_tmp.card[2])) = sd->char_id; /* キャラID */ - } - 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 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 RG_STRIPWEAPON: /* ストリップウェポン */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - - if (tsc_data && tsc_data[SC_CP_WEAPON].timer != -1) - break; - strip_per = 5 + 2 * skilllv + strip_fix / 5; - strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; - if (MRAND (100) < strip_per) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, strip_time, 0); - if (dstsd) - { - for (i = 0; i < MAX_INVENTORY; i++) - { - if (dstsd->status.inventory[i].equip - && dstsd->status.inventory[i].equip & 0x0002) - { - pc_unequipitem (dstsd, i, 0); - break; - } - } - } - } - } - break; - - case RG_STRIPSHIELD: /* ストリップシールド */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - - if (tsc_data && tsc_data[SC_CP_SHIELD].timer != -1) - break; - strip_per = 5 + 2 * skilllv + strip_fix / 5; - strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; - if (MRAND (100) < strip_per) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, strip_time, 0); - if (dstsd) - { - for (i = 0; i < MAX_INVENTORY; i++) - { - if (dstsd->status.inventory[i].equip - && dstsd->status.inventory[i].equip & 0x0020) - { - pc_unequipitem (dstsd, i, 0); - break; - } - } - } - } - } - break; - - case RG_STRIPARMOR: /* ストリップアーマー */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - - if (tsc_data && tsc_data[SC_CP_ARMOR].timer != -1) - break; - strip_per = 5 + 2 * skilllv + strip_fix / 5; - strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; - if (MRAND (100) < strip_per) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, strip_time, 0); - if (dstsd) - { - for (i = 0; i < MAX_INVENTORY; i++) - { - if (dstsd->status.inventory[i].equip - && dstsd->status.inventory[i].equip & 0x0010) - { - pc_unequipitem (dstsd, i, 0); - break; - } - } - } - } - } - break; - case RG_STRIPHELM: /* ストリップヘルム */ - { - struct status_change *tsc_data = battle_get_sc_data (bl); - - if (tsc_data && tsc_data[SC_CP_HELM].timer != -1) - break; - strip_per = 5 + 2 * skilllv + strip_fix / 5; - strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; - if (MRAND (100) < strip_per) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, - SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, strip_time, 0); - if (dstsd) - { - for (i = 0; i < MAX_INVENTORY; i++) - { - if (dstsd->status.inventory[i].equip - && dstsd->status.inventory[i].equip & 0x0100) - { - pc_unequipitem (dstsd, i, 0); - break; - } - } - } - } - } - break; - /* PotionPitcher */ - case AM_POTIONPITCHER: /* ポーションピッチャー */ - { - struct block_list tbl; - int i, x, hp = 0, sp = 0; - if (sd) - { - if (sd == dstsd) - { // cancel use on oneself - map_freeblock_unlock (); - return 1; - } - 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; - } - sd->state.potionpitcher_flag = 1; - sd->potion_hp = sd->potion_sp = sd->potion_per_hp = - sd->potion_per_sp = 0; - sd->skilltarget = bl->id; - run_script (sd->inventory_data[i]->use_script, 0, sd->bl.id, - 0); - pc_delitem (sd, i, skill_db[skillid].amount[x], 0); - sd->state.potionpitcher_flag = 0; - if (sd->potion_per_hp > 0 || sd->potion_per_sp > 0) - { - hp = battle_get_max_hp (bl) * sd->potion_per_hp / 100; - hp = hp * (100 + - pc_checkskill (sd, - AM_POTIONPITCHER) * 10 + - pc_checkskill (sd, - AM_LEARNINGPOTION) * 5) / 100; - if (dstsd) - { - sp = dstsd->status.max_sp * sd->potion_per_sp / 100; - sp = sp * (100 + - pc_checkskill (sd, - AM_POTIONPITCHER) + - pc_checkskill (sd, - AM_LEARNINGPOTION) * 5) / - 100; - } - } - else - { - if (sd->potion_hp > 0) - { - hp = sd->potion_hp * (100 + - pc_checkskill (sd, - AM_POTIONPITCHER) - * 10 + pc_checkskill (sd, - AM_LEARNINGPOTION) - * 5) / 100; - hp = hp * (100 + (battle_get_vit (bl) << 1)) / 100; - if (dstsd) - hp = hp * (100 + - pc_checkskill (dstsd, - SM_RECOVERY) * 10) / - 100; - } - if (sd->potion_sp > 0) - { - sp = sd->potion_sp * (100 + - pc_checkskill (sd, - AM_POTIONPITCHER) - + pc_checkskill (sd, - AM_LEARNINGPOTION) - * 5) / 100; - sp = sp * (100 + (battle_get_int (bl) << 1)) / 100; - if (dstsd) - sp = sp * (100 + - pc_checkskill (dstsd, - MG_SRECOVERY) * 10) / - 100; - } - } - } - else - { - hp = (1 + MRAND (400)) * (100 + skilllv * 10) / 100; - hp = hp * (100 + (battle_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 || (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: - { - struct status_change *tsc_data = battle_get_sc_data (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (tsc_data && tsc_data[SC_STRIPWEAPON].timer != -1) - skill_status_change_end (bl, SC_STRIPWEAPON, -1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - } - break; - case AM_CP_SHIELD: - { - struct status_change *tsc_data = battle_get_sc_data (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (tsc_data && tsc_data[SC_STRIPSHIELD].timer != -1) - skill_status_change_end (bl, SC_STRIPSHIELD, -1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - } - break; - case AM_CP_ARMOR: - { - struct status_change *tsc_data = battle_get_sc_data (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (tsc_data && tsc_data[SC_STRIPARMOR].timer != -1) - skill_status_change_end (bl, SC_STRIPARMOR, -1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - } - break; - case AM_CP_HELM: - { - struct status_change *tsc_data = battle_get_sc_data (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (tsc_data && tsc_data[SC_STRIPHELM].timer != -1) - skill_status_change_end (bl, SC_STRIPHELM, -1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - } - break; - case SA_DISPELL: /* ディスペル */ - { - int i; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - for (i = 0; i < 136; i++) - { - if (i == SC_RIDING || i == SC_FALCON || 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) - continue; - skill_status_change_end (bl, i, -1); - } - } - break; - - case TF_BACKSLIDING: /* バックステップ */ - battle_stopwalking (src, 1); - skill_blown (src, bl, - skill_get_blewcount (skillid, skilllv) | 0x10000); - if (src->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) src); - else if (src->type == BL_PC) - clif_fixpos (src); - skill_addtimerskill (src, tick + 200, src->id, 0, 0, skillid, - skilllv, 0, flag); - 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 = battle_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; - if (bl->type == BL_PC) - { - if (dstsd && dstsd->skilltimer != -1) - { - bl_skillid = dstsd->skillid; - bl_skilllv = dstsd->skilllv; - } - } - else if (bl->type == BL_MOB) - { - if (dstmd && dstmd->skilltimer != -1) - { - bl_skillid = dstmd->skillid; - bl_skilllv = dstmd->skilllv; - } - } - if (bl_skillid > 0 - && skill_db[bl_skillid].skill_type == BF_MAGIC) - { - 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, 0, -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; - clif_heal (sd->fd, SP_SP, sp); - } - } - else if (sd) - clif_skill_fail (sd, skillid, 0, 0); - } - } - break; - case SA_MAGICROD: - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_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; - 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 = MRAND (3); - spellid = spellarray[i]; - maxlv = skilllv - 1; - } - else if (skilllv > 0) - { - spellid = MG_NAPALMBEAT; - maxlv = 3; - } - if (spellid > 0) - skill_status_change_start (src, SC_AUTOSPELL, skilllv, - spellid, maxlv, 0, - skill_get_time (SA_AUTOSPELL, - skilllv), 0); - } - break; - - /* ランダム属性変化、水属性変化、地、火、風 */ - case NPC_ATTRICHANGE: - case NPC_CHANGEWATER: - case NPC_CHANGEGROUND: - case NPC_CHANGEFIRE: - case NPC_CHANGEWIND: - /* 毒、聖、念、闇 */ - case NPC_CHANGEPOISON: - case NPC_CHANGEHOLY: - case NPC_CHANGEDARKNESS: - case NPC_CHANGETELEKINESIS: - if (md) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - md->def_ele = skill_get_pl (skillid); - if (md->def_ele == 0) /* ランダム変化、ただし、 */ - md->def_ele = MRAND (10); /* 不死属性は除く */ - md->def_ele += (1 + MRAND (4)) * 20; /* 属性レベルはランダム */ - } - break; - - case NPC_HALLUCINATION: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - - case NPC_KEEPING: - case NPC_BARRIER: - { - int skill_time = skill_get_time (skillid, skilllv); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, skill_time, 0); - mob_changestate ((struct mob_data *) src, MS_DELAY, skill_time); - } - break; - - case NPC_DARKBLESSING: - { - int sc_def = 100 - battle_get_mdef (bl); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (battle_get_elem_type (bl) == 7 || battle_get_race (bl) == 6) - break; - if (MRAND (100) < sc_def * (50 + skilllv * 5) / 100) - { - if (dstsd) - { - int hp = battle_get_hp (bl) - 1; - pc_heal (dstsd, -hp, 0); - } - else if (dstmd) - dstmd->hp = 1; - } - } - break; - - case NPC_SELFDESTRUCTION: /* 自爆 */ - case NPC_SELFDESTRUCTION2: /* 自爆2 */ - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, skillid, 0, 0, - skill_get_time (skillid, skilllv), 0); - break; - case NPC_LICK: - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_weapon_damage) - break; - if (dstsd) - pc_heal (dstsd, 0, -100); - if (MRAND (100) < (skilllv * 5) * sc_def_vit / 100) - skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, - skill_get_time2 (skillid, skilllv), - 0); - break; - - case NPC_SUICIDE: /* 自決 */ - if (src && bl && md) - { - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - mob_damage (NULL, md, md->hp, 0); - } - break; - - case NPC_SUMMONSLAVE: /* 手下召喚 */ - case NPC_SUMMONMONSTER: /* MOB召喚 */ - if (md && !md->master_id) - { - mob_summonslave (md, - mob_db[md->mob_class].skill[md->skillidx].val, - skilllv, - (skillid == NPC_SUMMONSLAVE) ? 1 : 0); - } - break; - - case NPC_TRANSFORMATION: - case NPC_METAMORPHOSIS: - if (md) - mob_class_change (md, - mob_db[md->mob_class].skill[md->skillidx].val); - break; - - case NPC_EMOTION: /* エモーション */ - if (md) - clif_emotion (&md->bl, - mob_db[md->mob_class].skill[md->skillidx].val[0]); - break; - - case NPC_DEFENDER: - 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 = sd->status.max_hp * abs (hp_rate) / 100; // 15% - clif_skill_nodamage (src, bl, skillid, gain_hp, 1); - battle_heal (NULL, bl, gain_hp, 0, 0); - } - break; - case WE_FEMALE: /* あなたの為に犠牲になります */ - if (sd && dstsd) - { - int sp_rate = - (skilllv <= - 0) ? 0 : skill_db[skillid].sp_rate[skilllv - 1]; - int gain_sp = sd->status.max_sp * abs (sp_rate) / 100; // 15% - clif_skill_nodamage (src, bl, skillid, gain_sp, 1); - battle_heal (NULL, bl, 0, gain_sp, 0); - } - break; - - case WE_CALLPARTNER: /* あなたに会いたい */ - if (sd && dstsd) - { - if (map[sd->bl.m].flag.nomemo) - { - clif_skill_teleportmessage (sd, 1); - return 0; - } - if ((dstsd = pc_get_partner (sd)) == NULL) - { - clif_skill_fail (sd, skillid, 0, 0); - return 0; - } - skill_unitsetting (src, skillid, skilllv, sd->bl.x, sd->bl.y, - 0); - } - break; - - case PF_HPCONVERSION: /* ライフ置き換え */ - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - if (sd) - { - int conv_hp = 0, conv_sp = 0; - conv_hp = sd->status.hp / 10; //基本はHPの10% - sd->status.hp -= conv_hp; //HPを減らす - conv_sp = conv_hp * 20 * skilllv / 100; - conv_sp = - (sd->status.sp + conv_sp > - sd->status.max_sp) ? sd->status.max_sp - - sd->status.sp : conv_sp; - sd->status.sp += conv_sp; //SPを増やす - pc_heal (sd, -conv_hp, conv_sp); - clif_heal (sd->fd, SP_SP, conv_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[bl->m].flag.pvp - || map[bl->m].flag.gvg) && (su->group->unit_id >= 0x8f - && su->group->unit_id <= - 0x99) - && (su->group->unit_id != 0x92)) - { //罠を取り返す - if (sd) - { - if (battle_config.skill_removetrap_type == 1) - { - 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 == 0x91 && su->group->val2) - { - struct block_list *target = - map_id2bl (su->group->val2); - if (target - && (target->type == BL_PC - || target->type == BL_MOB)) - skill_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 0x8f: /* ブラストマイン */ - case 0x90: /* スキッドトラップ */ - case 0x93: /* ランドマイン */ - case 0x94: /* ショックウェーブトラップ */ - case 0x95: /* サンドマン */ - case 0x96: /* フラッシャー */ - case 0x97: /* フリージングトラップ */ - case 0x98: /* クレイモアートラップ */ - case 0x99: /* トーキーボックス */ - su->group->unit_id = 0x8c; - 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 ((double) battle_get_max_hp (bl) * 2 / 3 < battle_get_hp (bl)) //HPが2/3以上残っていたら失敗 - return 1; - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, skillid, src->id, 0, - skill_get_time (skillid, skilllv), 0); - break; - case PF_MINDBREAKER: /* プロボック */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - - /* MVPmobと不死には効かない */ - if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない - { - map_freeblock_unlock (); - return 1; - } - - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - skill_status_change_start (bl, SkillStatusChangeTable[skillid], - skilllv, 0, 0, 0, - skill_get_time (skillid, skilllv), 0); - - if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 - skill_castcancel (bl, 0); - if (dstsd && dstsd->skilltimer != -1 - && (!dstsd->special_state.no_castcancel - || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel - && !dstsd->special_state.no_castcancel2) - skill_castcancel (bl, 0); - - if (sc_data) - { - if (sc_data[SC_FREEZE].timer != -1) - skill_status_change_end (bl, SC_FREEZE, -1); - if (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0) - skill_status_change_end (bl, SC_STONE, -1); - if (sc_data[SC_SLEEP].timer != -1) - skill_status_change_end (bl, SC_SLEEP, -1); - } - - if (bl->type == BL_MOB) - { - int range = skill_get_range (skillid, skilllv); - if (range < 0) - range = battle_get_range (src) - (range + 1); - mob_target ((struct mob_data *) bl, src, range); - } - } - break; - - case RG_CLEANER: //AppleGirl - 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->src_id == src->id || map[bl->m].flag.pvp - || map[bl->m].flag.gvg) && (su->group->unit_id == 0xb0)) - { //罠を取り返す - if (sd) - skill_delunit (su); - } - } - break; - default: - printf ("Unknown skill used:%d\n", skillid); - map_freeblock_unlock (); - return 1; - } - - map_freeblock_unlock (); - return 0; -} - -/*========================================== - * スキル使用(詠唱完了、ID指定) - *------------------------------------------ - */ -void skill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; - struct block_list *bl; - int range, inf2; - - nullpo_retv ( sd); - - if (sd->bl.prev == NULL) //prevが無いのはありなの? - return; - - if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid) /* タイマIDの確認 */ - return; - if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 - && pc_checkskill (sd, SA_FREECAST) > 0) - { - sd->speed = sd->prev_speed; - clif_updatestatus (sd, SP_SPEED); - } - if (sd->skillid != SA_CASTCANCEL) - sd->skilltimer = -1; - - if ((bl = map_id2bl (sd->skilltarget)) == NULL || bl->prev == NULL) - { - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - if (sd->bl.m != bl->m || pc_isdead (sd)) - { //マップが違うか自分が死んでいる - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - - if (sd->skillid == PR_LEXAETERNA) - { - struct status_change *sc_data = battle_get_sc_data (bl); - if (sc_data - && (sc_data[SC_FREEZE].timer != -1 - || (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0))) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - else if (sd->skillid == RG_BACKSTAP) - { - int dir = map_calc_dir (&sd->bl, bl->x, bl->y), t_dir = - battle_get_dir (bl); - int dist = distance (sd->bl.x, sd->bl.y, bl->x, bl->y); - if (bl->type != BL_SKILL && (dist == 0 || map_check_dir (dir, t_dir))) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - - inf2 = skill_get_inf2 (sd->skillid); - if (((skill_get_inf (sd->skillid) & 1) || inf2 & 4) && // 彼我敵対関係チェック - battle_check_target (&sd->bl, bl, BCT_ENEMY) <= 0) - { - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - if (inf2 & 0xC00 && sd->bl.id != bl->id) - { - int fail_flag = 1; - if (inf2 & 0x400 && battle_check_target (&sd->bl, bl, BCT_PARTY) > 0) - fail_flag = 0; - if (inf2 & 0x800 && sd->status.guild_id > 0 - && sd->status.guild_id == battle_get_guild_id (bl)) - fail_flag = 0; - if (fail_flag) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - - range = skill_get_range (sd->skillid, sd->skilllv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - range += battle_config.pc_skill_add_range; - if ((sd->skillid == MO_EXTREMITYFIST && sd->sc_data[SC_COMBO].timer != -1 - && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) - || (sd->skillid == CH_TIGERFIST && sd->sc_data[SC_COMBO].timer != -1 - && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) - || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 - && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) - || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 - && sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)) - range += - skill_get_blewcount (MO_COMBOFINISH, sd->sc_data[SC_COMBO].val2); - if (battle_config.skill_out_range_consume) - { // changed to allow casting when target walks out of range [Valaris] - if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - if (!skill_check_condition (sd, 1)) - { /* 使用条件チェック */ - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - sd->skillitem = sd->skillitemlv = -1; - if (battle_config.skill_out_range_consume) - { - if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - return; - } - } - - if (battle_config.pc_skill_log) - printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); - pc_stop_walking (sd, 0); - - switch (skill_get_nk (sd->skillid)) - { - /* 攻撃系/吹き飛ばし系 */ - case 0: - case 2: - skill_castend_damage_id (&sd->bl, bl, sd->skillid, sd->skilllv, - tick, 0); - break; - case 1: /* 支援系 */ - if ((sd->skillid == AL_HEAL - || (sd->skillid == ALL_RESURRECTION && bl->type != BL_PC) - || sd->skillid == PR_ASPERSIO) - && battle_check_undead (battle_get_race (bl), - battle_get_elem_type (bl))) - skill_castend_damage_id (&sd->bl, bl, sd->skillid, - sd->skilllv, tick, 0); - else - skill_castend_nodamage_id (&sd->bl, bl, sd->skillid, - sd->skilllv, tick, 0); - break; - } -} - -/*========================================== - * スキル使用(詠唱完了、場所指定の実際の処理) - *------------------------------------------ - */ -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; - int i, tmpx = 0, tmpy = 0, x1 = 0, y1 = 0; - - nullpo_retr (0, src); - - if (src->type == BL_PC) - { - nullpo_retr (0, sd = (struct map_session_data *) src); - } - if (skillid != WZ_METEOR && - skillid != WZ_SIGHTRASHER && - skillid != AM_CANNIBALIZE && skillid != AM_SPHEREMINE) - clif_skill_poseffect (src, skillid, skilllv, x, y, tick); - - if (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, 0, - src, skillid, skilllv, tick, - flag | BCT_NOENEMY | 1, - skill_castend_nodamage_id); - map_foreachinarea (skill_area_sub, src->m, x - 1, y - 1, x + 1, - y + 1, 0, src, skillid, skilllv, tick, - flag | BCT_ENEMY | 1, skill_castend_damage_id); - break; - - case BS_HAMMERFALL: /* ハンマーフォール */ - skill_area_temp[1] = src->id; - skill_area_temp[2] = x; - skill_area_temp[3] = y; - map_foreachinarea (skill_area_sub, - src->m, x - 2, y - 2, x + 2, y + 2, 0, - src, skillid, skilllv, tick, - flag | BCT_ENEMY | 2, - skill_castend_nodamage_id); - break; - - case HT_DETECTING: /* ディテクティング */ - { - const int range = 7; - map_foreachinarea (skill_status_change_timer_sub, - src->m, src->x - range, src->y - range, - src->x + range, src->y + range, 0, 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_SIGHTRASHER: - case WZ_QUAGMIRE: /* クァグマイア */ - case WZ_VERMILION: /* ロードオブヴァーミリオン */ - case WZ_FROSTNOVA: /* フロストノヴァ */ - case WZ_STORMGUST: /* ストームガスト */ - case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ - case PR_SANCTUARY: /* サンクチュアリ */ - case PR_MAGNUS: /* マグヌスエクソシズム */ - case CR_GRANDCROSS: /* グランドクロス */ - 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_SPIDERWEB: /* スパイダーウェッブ */ - case PF_FOGWALL: /* フォグウォール */ - 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 SA_VOLCANO: /* ボルケーノ */ - case SA_DELUGE: /* デリュージ */ - case SA_VIOLENTGALE: /* バイオレントゲイル */ - case SA_LANDPROTECTOR: /* ランドプロテクター */ - skill_clear_element_field (src); //既に自分が発動している属性場をクリア - skill_unitsetting (src, skillid, skilllv, x, y, 0); - break; - - case WZ_METEOR: //メテオストーム - { - int flag = 0; - for (i = 0; i < 2 + (skilllv >> 1); i++) - { - int j = 0, c; - do - { - tmpx = x + (MRAND (7) - 3); - tmpy = y + (MRAND (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 (((c = map_getcell (src->m, tmpx, tmpy)) == 1 || c == 5) - && j < 100); - if (j >= 100) - continue; - if (flag == 0) - { - clif_skill_poseffect (src, skillid, skilllv, tmpx, tmpy, - tick); - flag = 1; - } - if (i > 0) - skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, - skillid, skilllv, (x1 << 16) | y1, - flag); - x1 = tmpx; - y1 = tmpy; - } - skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, skillid, - skilllv, -1, flag); - } - break; - - case AL_WARP: /* ワープポータル */ - if (sd) - { - if (map[sd->bl.m].flag.noteleport) /* テレポ禁止 */ - break; - clif_skill_warppoint (sd, sd->skillid, - sd->status.save_point.map, - (sd->skilllv > - 1) ? sd->status.memo_point[0].map : "", - (sd->skilllv > - 2) ? sd->status.memo_point[1].map : "", - (sd->skilllv > - 3) ? sd->status. - memo_point[2].map : ""); - } - break; - case MO_BODYRELOCATION: - if (sd) - { - pc_movepos (sd, x, y); - } - else if (src->type == BL_MOB) - mob_warp ((struct mob_data *) src, -1, x, y, 0); - break; - case AM_CANNIBALIZE: // バイオプラント - if (sd) - { - int mx, my, id = 0; - struct mob_data *md; - - mx = x; // + (rand()%10 - 5); - my = y; // + (rand()%10 - 5); - id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1118, 1, - ""); - if ((md = (struct mob_data *) map_id2bl (id)) != NULL) - { - md->master_id = sd->bl.id; - md->hp = 2210 + skilllv * 200; - md->state.special_mob_ai = 1; - 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; - case AM_SPHEREMINE: // スフィアーマイン - if (sd) - { - int mx, my, id = 0; - struct mob_data *md; - - mx = x; // + (rand()%10 - 5); - my = y; // + (rand()%10 - 5); - id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1142, 1, - ""); - if ((md = (struct mob_data *) map_id2bl (id)) != NULL) - { - md->master_id = sd->bl.id; - md->hp = 1000 + skilllv * 200; - md->state.special_mob_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; - } - - return 0; -} - -/*========================================== - * スキル使用(詠唱完了、map指定) - *------------------------------------------ - */ -int skill_castend_map (struct map_session_data *sd, int skill_num, - const char *map) -{ - int x = 0, y = 0; - - nullpo_retr (0, sd); - if (sd->bl.prev == NULL || pc_isdead (sd)) - return 0; - - if (sd->opt1 > 0 || sd->status.option & 2) - return 0; - //スキルが使えない状態異常中 - if (sd->sc_data) - { - if (sd->sc_data[SC_DIVINA].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) - return 0; - } - - if (skill_num != sd->skillid) /* 不正パケットらしい */ - return 0; - - pc_stopattack (sd); - - if (battle_config.pc_skill_log) - printf ("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) - 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[] = { - &sd->status.save_point, &sd->status.memo_point[0], - &sd->status.memo_point[1], &sd->status.memo_point[2], - }; - struct skill_unit_group *group; - int i; - int maxcount = 0; - - if ((maxcount = skill_get_maxcount (sd->skillid)) > 0) - { - int 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); - sd->canact_tick = gettick (); - sd->canmove_tick = gettick (); - sd->skillitem = sd->skillitemlv = -1; - return 0; - } - } - - for (i = 0; i < sd->skilllv; i++) - { - if (strcmp (map, p[i]->map) == 0) - { - x = p[i]->x; - y = p[i]->y; - break; - } - } - if (x == 0 || y == 0) /* 不正パケット? */ - return 0; - - if (!skill_check_condition (sd, 3)) - return 0; - if ((group = - skill_unitsetting (&sd->bl, sd->skillid, sd->skilllv, - sd->skillx, sd->skilly, 0)) == NULL) - return 0; - CREATE (group->valstr, char, 24); - memcpy (group->valstr, map, 24); - group->val2 = (x << 16) | y; - } - break; - } - - return 0; -} - -/*========================================== - * スキルユニット設定処理 - *------------------------------------------ - */ -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, count = 1, limit = 10000, val1 = 0, val2 = 0; - int target = BCT_ENEMY, interval = 1000, range = 0; - int dir = 0, aoe_diameter = 0; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills - - nullpo_retr (0, src); - - switch (skillid) - { /* 設定 */ - - case MG_SAFETYWALL: /* セイフティウォール */ - limit = skill_get_time (skillid, skilllv); - val2 = skilllv + 1; - interval = -1; - target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; - break; - - case MG_FIREWALL: /* ファイヤーウォール */ - if (src->x == x && src->y == y) - dir = 2; - else - dir = map_calc_dir (src, x, y); - if (dir & 1) - count = 5; - else - count = 3; - limit = skill_get_time (skillid, skilllv); - val2 = 4 + skilllv; - interval = 1; - break; - - case AL_PNEUMA: /* ニューマ */ - limit = skill_get_time (skillid, skilllv); - interval = -1; - target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; - count = 9; - break; - - case AL_WARP: /* ワープポータル */ - target = BCT_ALL; - val1 = skilllv + 6; - if (flag == 0) - limit = 2000; - else - limit = skill_get_time (skillid, skilllv); - break; - - case PR_SANCTUARY: /* サンクチュアリ */ - count = 21; - limit = skill_get_time (skillid, skilllv); - val1 = skilllv + 3; - val2 = (skilllv > 6) ? 777 : skilllv * 100; - target = BCT_ALL; - range = 1; - break; - - case PR_MAGNUS: /* マグヌスエクソシズム */ - count = 33; - limit = skill_get_time (skillid, skilllv); - interval = 3000; - break; - - case WZ_FIREPILLAR: /* ファイアーピラー */ - if (flag == 0) - limit = skill_get_time (skillid, skilllv); - else - limit = 1000; - interval = 2000; - val1 = skilllv + 2; - range = 1; - break; - - case MG_THUNDERSTORM: /* サンダーストーム */ - limit = 500; - range = 1; - break; - - case WZ_FROSTNOVA: /* フロストノヴァ */ - limit = 500; - range = 5; - break; - case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ - limit = 500; - range = 2; - break; - - case WZ_METEOR: /* メテオストーム */ - limit = 500; - range = 3; - break; - - case WZ_SIGHTRASHER: - limit = 500; - count = 41; - break; - - case WZ_VERMILION: /* ロードオブヴァーミリオン */ - limit = 4100; - interval = 1000; - range = 6; - break; - - case WZ_ICEWALL: /* アイスウォール */ - limit = skill_get_time (skillid, skilllv); - count = 5; - break; - - case WZ_STORMGUST: /* ストームガスト */ - limit = 4600; - interval = 450; - range = 5; - break; - - case WZ_QUAGMIRE: /* クァグマイア */ - limit = skill_get_time (skillid, skilllv); - interval = 200; - count = 25; - break; - - case HT_SKIDTRAP: /* スキッドトラップ */ - case HT_LANDMINE: /* ランドマイン */ - case HT_ANKLESNARE: /* アンクルスネア */ - case HT_SANDMAN: /* サンドマン */ - case PF_SPIDERWEB: /* スパイダーウェッブ */ - case HT_FLASHER: /* フラッシャー */ - case HT_FREEZINGTRAP: /* フリージングトラップ */ - case HT_BLASTMINE: /* ブラストマイン */ - case HT_CLAYMORETRAP: /* クレイモアートラップ */ - limit = skill_get_time (skillid, skilllv); - range = 1; - break; - - case HT_TALKIEBOX: /* トーキーボックス */ - limit = skill_get_time (skillid, skilllv); - range = 1; - target = BCT_ALL; - break; - - case HT_SHOCKWAVE: /* ショックウェーブトラップ */ - limit = skill_get_time (skillid, skilllv); - range = 1; - val1 = skilllv * 15 + 10; - break; - - case AS_VENOMDUST: /* ベノムダスト */ - limit = skill_get_time (skillid, skilllv); - interval = 1000; - count = 5; - break; - - case CR_GRANDCROSS: /* グランドクロス */ - count = 29; - limit = 1000; - interval = 300; - break; - - case SA_VOLCANO: /* ボルケーノ */ - case SA_DELUGE: /* デリュージ */ - case SA_VIOLENTGALE: /* バイオレントゲイル */ - limit = skill_get_time (skillid, skilllv); - count = skilllv <= 2 ? 25 : (skilllv <= 4 ? 49 : 81); - target = BCT_ALL; - break; - - case SA_LANDPROTECTOR: /* グランドクロス */ - limit = skill_get_time (skillid, skilllv); // changed to get duration from cast_db (moonsoul) - val1 = skilllv * 15 + 10; - aoe_diameter = skilllv + skilllv % 2 + 5; - target = BCT_ALL; - count = aoe_diameter * aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) - break; - - case BD_LULLABY: /* 子守唄 */ - case BD_ETERNALCHAOS: /* エターナルカオス */ - case BD_ROKISWEIL: /* ロキの叫び */ - count = 81; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_ALL; - break; - case BD_RICHMANKIM: - case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ - case BD_INTOABYSS: /* 深淵の中に */ - case BD_SIEGFRIED: /* 不死身のジークフリード */ - count = 81; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_PARTY; - break; - - case BA_WHISTLE: /* 口笛 */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - BA_MUSICALLESSON) + 1) >> 1; - val2 = ((battle_get_agi (src) / 10) & 0xffff) << 16; - val2 |= (battle_get_luk (src) / 10) & 0xffff; - break; - case DC_HUMMING: /* ハミング */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - DC_DANCINGLESSON) + 1) >> 1; - val2 = battle_get_dex (src) / 10; - break; - - case BA_DISSONANCE: /* 不協和音 */ - case DC_UGLYDANCE: /* 自分勝手なダンス */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_ENEMY; - break; - - case DC_DONTFORGETME: /* 私を忘れないで… */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_ENEMY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - DC_DANCINGLESSON) + 1) >> 1; - val2 = ((battle_get_str (src) / 20) & 0xffff) << 16; - val2 |= (battle_get_agi (src) / 10) & 0xffff; - break; - case BA_POEMBRAGI: /* ブラギの詩 */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - pc_checkskill ((struct map_session_data *) src, - BA_MUSICALLESSON); - val2 = ((battle_get_dex (src) / 10) & 0xffff) << 16; - val2 |= (battle_get_int (src) / 5) & 0xffff; - break; - case BA_APPLEIDUN: /* イドゥンの林檎 */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - ((pc_checkskill - ((struct map_session_data *) src, - BA_MUSICALLESSON)) & 0xffff) << 16; - else - val1 = 0; - val1 |= (battle_get_vit (src)) & 0xffff; - val2 = 0; //回復用タイムカウンタ(6秒毎に1増加) - break; - case DC_SERVICEFORYOU: /* サービスフォーユー */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_PARTY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - DC_DANCINGLESSON) + 1) >> 1; - val2 = battle_get_int (src) / 10; - break; - case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - BA_MUSICALLESSON) + 1) >> 1; - val2 = battle_get_agi (src) / 20; - break; - case DC_FORTUNEKISS: /* 幸運のキス */ - count = 49; - limit = skill_get_time (skillid, skilllv); - range = 5; - target = BCT_NOENEMY; - if (src->type == BL_PC) - val1 = - (pc_checkskill - ((struct map_session_data *) src, - DC_DANCINGLESSON) + 1) >> 1; - val2 = battle_get_luk (src) / 10; - break; - case AM_DEMONSTRATION: /* デモンストレーション */ - limit = skill_get_time (skillid, skilllv); - interval = 1000; - range = 1; - target = BCT_ENEMY; - break; - case WE_CALLPARTNER: /* あなたに逢いたい */ - limit = skill_get_time (skillid, skilllv); - range = -1; - break; - - case HP_BASILICA: /* バジリカ */ - limit = skill_get_time (skillid, skilllv); - target = BCT_ALL; - range = 3; - //Fix to prevent the priest from walking while Basilica is up. - battle_stopwalking (src, 1); - skill_status_change_start (src, SC_ANKLE, skilllv, 0, 0, 0, limit, - 0); - break; - case PA_GOSPEL: /* ゴスペル */ - count = 49; - target = BCT_PARTY; - limit = skill_get_time (skillid, skilllv); - break; - case PF_FOGWALL: /* フォグウォール */ - count = 15; - limit = skill_get_time (skillid, skilllv); - break; - case RG_GRAFFITI: /* Graffiti */ - count = 1; // Leave this at 1 [Valaris] - limit = 600000; // Time length [Valaris] - break; - }; - - nullpo_retr (NULL, group = - skill_initunitgroup (src, count, skillid, skilllv, - skill_get_unit_id (skillid, flag & 1))); - group->limit = limit; - group->val1 = val1; - group->val2 = val2; - group->target_flag = target; - group->interval = interval; - group->range = range; - if (skillid == HT_TALKIEBOX || skillid == RG_GRAFFITI) - { - CREATE (group->valstr, char, 80); - memcpy (group->valstr, talkie_mes, 80); - } - for (i = 0; i < count; i++) - { - struct skill_unit *unit; - int ux = x, uy = y, val1 = skilllv, val2 = 0, limit = - group->limit, alive = 1; - int range = group->range; - switch (skillid) - { /* 設定 */ - case AL_PNEUMA: /* ニューマ */ - { - static const int dx[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 }; - static const int dy[9] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 }; - ux += dx[i]; - uy += dy[i]; - } - break; - case MG_FIREWALL: /* ファイヤーウォール */ - { - if (dir & 1) - { /* 斜め配置 */ - static const int dx[][5] = { - {1, 1, 0, 0, -1}, {-1, -1, 0, 0, 1}, - }, dy[][5] = - { - { - 1, 0, 0, -1, -1}, - { - 1, 0, 0, -1, -1},}; - ux += dx[(dir >> 1) & 1][i]; - uy += dy[(dir >> 1) & 1][i]; - } - else - { /* 上下配置 */ - if (dir % 4 == 0) /* 上下 */ - ux += i - 1; - else /* 左右 */ - uy += i - 1; - } - val2 = group->val2; - } - 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, - }; - ux += dx[i]; - uy += dy[i]; - } - 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 - }; - ux += dx[i]; - uy += dy[i]; - } - break; - - case WZ_SIGHTRASHER: - { - static const int dx[] = { - -5, 0, 5, -4, 0, 4, -3, 0, 3, -2, 0, 2, -1, 0, 1, -5, -4, - -3, -2, -1, 0, 1, 2, 3, 4, 5, -1, 0, 1, -2, 0, 2, -3, - 0, 3, -4, 0, 4, -5, 0, 5 - }; - static const int dy[] = { - -5, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, - -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, - 3, 3, 3, 4, 4, 4, 5, 5, 5 - }; - ux += dx[i]; - uy += dy[i]; - } - break; - - case WZ_ICEWALL: /* アイスウォール */ - { - 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 }; - if (skilllv <= 1) - val1 = 500; - else - val1 = 200 + 200 * skilllv; - if (src->x == x && src->y == y) - dir = 2; - else - dir = map_calc_dir (src, x, y); - ux += (2 - i) * diry[dir]; - uy += (i - 2) * dirx[dir]; - } - break; - - case WZ_QUAGMIRE: /* クァグマイア */ - ux += (i % 5 - 2); - uy += (i / 5 - 2); - if (i == 12) - range = 2; - else - range = -1; - - break; - - case AS_VENOMDUST: /* ベノムダスト */ - { - static const int dx[] = { -1, 0, 0, 0, 1 }; - static const int dy[] = { 0, -1, 0, 1, 0 }; - ux += dx[i]; - uy += dy[i]; - } - break; - - case CR_GRANDCROSS: /* グランドクロス */ - { - 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, - }; - ux += dx[i]; - uy += dy[i]; - } - break; - case SA_VOLCANO: /* ボルケーノ */ - case SA_DELUGE: /* デリュージ */ - case SA_VIOLENTGALE: /* バイオレントゲイル */ - { - int u_range = 0, central = 0; - if (skilllv <= 2) - { - u_range = 2; - central = 12; - } - else if (skilllv <= 4) - { - u_range = 3; - central = 24; - } - else if (skilllv >= 5) - { - u_range = 4; - central = 40; - } - ux += (i % (u_range * 2 + 1) - u_range); - uy += (i / (u_range * 2 + 1) - u_range); - - if (i == central) - range = u_range; //中央のユニットの効果範囲は全範囲 - else - range = -1; //中央以外のユニットは飾り - } - break; - case SA_LANDPROTECTOR: /* ランドプロテクター */ - { - int u_range = 0; - - if (skilllv <= 2) - u_range = 3; - else if (skilllv <= 4) - u_range = 4; - else if (skilllv >= 5) - u_range = 5; - - ux += (i % (u_range * 2 + 1) - u_range); - uy += (i / (u_range * 2 + 1) - u_range); - - range = 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: /* 不死身のジークフリード */ - ux += (i % 9 - 4); - uy += (i / 9 - 4); - if (i == 40) - range = 4; /* 中心の場合は範囲を4にオーバーライド */ - else - range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ - break; - case BA_DISSONANCE: /* 不協和音 */ - case BA_WHISTLE: /* 口笛 */ - case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ - case BA_POEMBRAGI: /* ブラギの詩 */ - case BA_APPLEIDUN: /* イドゥンの林檎 */ - case DC_UGLYDANCE: /* 自分勝手なダンス */ - case DC_HUMMING: /* ハミング */ - case DC_DONTFORGETME: /* 私を忘れないで… */ - case DC_FORTUNEKISS: /* 幸運のキス */ - case DC_SERVICEFORYOU: /* サービスフォーユー */ - ux += (i % 7 - 3); - uy += (i / 7 - 3); - if (i == 40) - range = 4; /* 中心の場合は範囲を4にオーバーライド */ - else - range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ - break; - case PA_GOSPEL: /* ゴスペル */ - ux += (i % 7 - 3); - uy += (i / 7 - 3); - break; - case PF_FOGWALL: /* フォグウォール */ - ux += (i % 5 - 2); - uy += (i / 5 - 1); - break; - case RG_GRAFFITI: /* Graffiti [Valaris] */ - ux += (i % 5 - 2); - uy += (i / 5 - 2); - break; - } - //直上スキルの場合設置座標上にランドプロテクターがないかチェック - if (range <= 0) - map_foreachinarea (skill_landprotector, src->m, ux, uy, ux, uy, - BL_SKILL, skillid, &alive); - - if (skillid == WZ_ICEWALL && alive) - { - val2 = map_getcell (src->m, ux, uy); - if (val2 == 5 || val2 == 1) - alive = 0; - else - { - map_setcell (src->m, ux, uy, 5); - 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; - } - } - 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 skill_unit_group_tickset *ts; - struct map_session_data *srcsd = NULL; - int diff, goflag, splash_count = 0; - - nullpo_retr (0, src); - nullpo_retr (0, bl); - - if (bl->prev == NULL || !src->alive - || (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl))) - return 0; - - nullpo_retr (0, sg = src->group); - nullpo_retr (0, ss = map_id2bl (sg->src_id)); - - if (ss->type == BL_PC) - nullpo_retr (0, srcsd = (struct map_session_data *) ss); - if (srcsd && srcsd->chatID) - return 0; - - if (bl->type != BL_PC && bl->type != BL_MOB) - return 0; - nullpo_retr (0, ts = skill_unitgrouptickset_search (bl, sg->group_id)); - diff = DIFF_TICK (tick, ts->tick); - goflag = (diff > sg->interval || diff < 0); - if (sg->skill_id == CR_GRANDCROSS && !battle_config.gx_allhit) // 重なっていたら3HITしない - goflag = (diff > sg->interval * map_count_oncell (bl->m, bl->x, bl->y) - || diff < 0); - - //対象がLP上に居る場合は無効 - map_foreachinarea (skill_landprotector, bl->m, bl->x, bl->y, bl->x, bl->y, - BL_SKILL, 0, &goflag); - - if (!goflag) - return 0; - ts->tick = tick; - ts->group_id = sg->group_id; - - switch (sg->unit_id) - { - case 0x83: /* サンクチュアリ */ - { - int race = battle_get_race (bl); - int damage_flag = - (battle_check_undead (race, battle_get_elem_type (bl)) - || race == 6) ? 1 : 0; - - if (battle_get_hp (bl) >= battle_get_max_hp (bl) && !damage_flag) - break; - - if ((sg->val1--) <= 0) - { - skill_delunitgroup (sg); - return 0; - } - if (!damage_flag) - { - int heal = sg->val2; - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - heal = 0; /* 黄金蟲カード(ヒール量0) */ - clif_skill_nodamage (&src->bl, bl, AL_HEAL, heal, 1); - battle_heal (NULL, bl, heal, 0, 0); - } - else - skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - } - break; - - case 0x84: /* マグヌスエクソシズム */ - { - int race = battle_get_race (bl); - int damage_flag = - (battle_check_undead (race, battle_get_elem_type (bl)) - || race == 6) ? 1 : 0; - - if (!damage_flag) - return 0; - skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - } - break; - - case 0x85: /* ニューマ */ - { - struct skill_unit *unit2; - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SC_PNEUMA; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, (int) src, - 0, 0, 0, 0); - else if ((unit2 = (struct skill_unit *) sc_data[type].val2) - && unit2 != src) - { - if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) - skill_status_change_start (bl, type, sg->skill_lv, - (int) src, 0, 0, 0, 0); - ts->tick -= sg->interval; - } - } - break; - case 0x7e: /* セイフティウォール */ - { - struct skill_unit *unit2; - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SC_SAFETYWALL; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, (int) src, - 0, 0, 0, 0); - else if ((unit2 = (struct skill_unit *) sc_data[type].val2) - && unit2 != src) - { - if (sg->val1 < unit2->group->val1) - skill_status_change_start (bl, type, sg->skill_lv, - (int) src, 0, 0, 0, 0); - ts->tick -= sg->interval; - } - } - break; - - case 0x86: /* ロードオブヴァーミリオン(&ストームガスト &グランドクロス) */ - skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - break; - - case 0x7f: /* ファイヤーウォール */ - if ((src->val2--) > 0) - skill_attack (BF_MAGIC, ss, &src->bl, bl, - sg->skill_id, sg->skill_lv, tick, 0); - if (src->val2 <= 0) - skill_delunit (src); - break; - - case 0x87: /* ファイアーピラー(発動前) */ - skill_delunit (src); - skill_unitsetting (ss, sg->skill_id, sg->skill_lv, src->bl.x, - src->bl.y, 1); - break; - - case 0x88: /* ファイアーピラー(発動後) */ - if (DIFF_TICK (tick, sg->tick) < 150) - skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - break; - - case 0x90: /* スキッドトラップ */ - { - int i, c = skill_get_blewcount (sg->skill_id, sg->skill_lv); - if (map[bl->m].flag.gvg) - c = 0; - for (i = 0; i < c; i++) - skill_blown (&src->bl, bl, 1 | 0x30000); - sg->unit_id = 0x8c; - clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); - sg->limit = DIFF_TICK (tick, sg->tick) + 1500; - } - break; - - case 0x93: /* ランドマイン */ - skill_attack (BF_MISC, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - sg->unit_id = 0x8c; - clif_changelook (&src->bl, LOOK_BASE, 0x88); - sg->limit = DIFF_TICK (tick, sg->tick) + 1500; - break; - - case 0x8f: /* ブラストマイン */ - case 0x94: /* ショックウェーブトラップ */ - case 0x95: /* サンドマン */ - case 0x96: /* フラッシャー */ - case 0x97: /* フリージングトラップ */ - case 0x98: /* クレイモアートラップ */ - 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, - 0, &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, - 0, &src->bl, tick, splash_count); - sg->unit_id = 0x8c; - clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); - sg->limit = DIFF_TICK (tick, sg->tick) + 1500; - break; - - case 0x91: /* アンクルスネア */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - if (sg->val2 == 0 && sc_data && sc_data[SC_ANKLE].timer == -1) - { - int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE - || bl->y / BLOCK_SIZE != - src->bl.y / BLOCK_SIZE); - int sec = skill_get_time2 (sg->skill_id, - sg->skill_lv) - - (double) battle_get_agi (bl) * 0.1; - if (battle_get_mode (bl) & 0x20) - sec = sec / 5; - battle_stopwalking (bl, 1); - skill_status_change_start (bl, SC_ANKLE, sg->skill_lv, 0, 0, - 0, sec, 0); - - if (moveblock) - map_delblock (bl); - bl->x = src->bl.x; - bl->y = src->bl.y; - if (moveblock) - map_addblock (bl); - if (bl->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) bl); - else - clif_fixpos (bl); - clif_01ac (&src->bl); - sg->limit = DIFF_TICK (tick, sg->tick) + sec; - sg->val2 = bl->id; - } - } - break; - - case 0x80: /* ワープポータル(発動後) */ - if (bl->type == BL_PC) - { - struct map_session_data *sd = (struct map_session_data *) bl; - if (sd && src->bl.m == bl->m && src->bl.x == bl->x - && src->bl.y == bl->y && src->bl.x == sd->to_x - && src->bl.y == sd->to_y) - { - if (battle_config.chat_warpportal || !sd->chatID) - { - if ((sg->val1--) > 0) - { - pc_setpos (sd, sg->valstr, sg->val2 >> 16, - sg->val2 & 0xffff, 3); - if (sg->src_id == bl->id - || (strcmp (map[src->bl.m].name, sg->valstr) - == 0 && src->bl.x == (sg->val2 >> 16) - && src->bl.y == (sg->val2 & 0xffff))) - skill_delunitgroup (sg); - } - else - skill_delunitgroup (sg); - } - } - } - else if (bl->type == BL_MOB && battle_config.mob_warpportal) - { - int m = map_mapname2mapid (sg->valstr); - struct mob_data *md; - md = (struct mob_data *) bl; - mob_warp ((struct mob_data *) bl, m, sg->val2 >> 16, - sg->val2 & 0xffff, 3); - } - break; - - case 0x8e: /* クァグマイア */ - { - int type = SkillStatusChangeTable[sg->skill_id]; - if (bl->type == BL_PC - && ((struct map_session_data *) bl)-> - special_state.no_magic_damage) - break; - if (battle_get_sc_data (bl)[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, (int) src, - 0, 0, - skill_get_time2 (sg->skill_id, - sg->skill_lv), 0); - } - break; - case 0x92: /* ベノムダスト */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SkillStatusChangeTable[sg->skill_id]; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, (int) src, - 0, 0, - skill_get_time2 (sg->skill_id, - sg->skill_lv), 0); - } - break; - case 0x9a: /* ボルケーノ */ - case 0x9b: /* デリュージ */ - case 0x9c: /* バイオレントゲイル */ - { - struct skill_unit *unit2; - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SkillStatusChangeTable[sg->skill_id]; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, (int) src, - 0, 0, - skill_get_time2 (sg->skill_id, - sg->skill_lv), 0); - else if ((unit2 = (struct skill_unit *) sc_data[type].val2) - && unit2 != src) - { - if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) - skill_status_change_start (bl, type, sg->skill_lv, - (int) src, 0, 0, - skill_get_time2 (sg->skill_id, - sg->skill_lv), - 0); - ts->tick -= sg->interval; - } - } break; - - case 0x9e: /* 子守唄 */ - case 0x9f: /* ニヨルドの宴 */ - case 0xa0: /* 永遠の混沌 */ - case 0xa1: /* 戦太鼓の響き */ - case 0xa2: /* ニーベルングの指輪 */ - case 0xa3: /* ロキの叫び */ - case 0xa4: /* 深淵の中に */ - case 0xa5: /* 不死身のジークフリード */ - case 0xa6: /* 不協和音 */ - case 0xa7: /* 口笛 */ - case 0xa8: /* 夕陽のアサシンクロス */ - case 0xa9: /* ブラギの詩 */ - case 0xab: /* 自分勝手なダンス */ - case 0xac: /* ハミング */ - case 0xad: /* 私を忘れないで… */ - case 0xae: /* 幸運のキス */ - case 0xaf: /* サービスフォーユー */ - case 0xb4: - { - struct skill_unit *unit2; - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SkillStatusChangeTable[sg->skill_id]; - if (sg->src_id == bl->id) - break; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, sg->val1, - sg->val2, (int) src, - skill_get_time2 (sg->skill_id, - sg->skill_lv), 0); - else if ((unit2 = (struct skill_unit *) sc_data[type].val4) - && unit2 != src) - { - if (unit2->group - && DIFF_TICK (sg->tick, unit2->group->tick) > 0) - skill_status_change_start (bl, type, sg->skill_lv, - sg->val1, sg->val2, (int) src, - skill_get_time2 (sg->skill_id, - sg->skill_lv), - 0); - ts->tick -= sg->interval; - } - } break; - - case 0xaa: /* イドゥンの林檎 */ - { - struct skill_unit *unit2; - struct status_change *sc_data = battle_get_sc_data (bl); - int type = SkillStatusChangeTable[sg->skill_id]; - if (sg->src_id == bl->id) - break; - if (sc_data && sc_data[type].timer == -1) - skill_status_change_start (bl, type, sg->skill_lv, - (sg->val1) >> 16, - (sg->val1) & 0xffff, (int) src, - skill_get_time2 (sg->skill_id, - sg->skill_lv), 0); - else if ((unit2 = (struct skill_unit *) sc_data[type].val4) - && unit2 != src) - { - if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) - skill_status_change_start (bl, type, sg->skill_lv, - (sg->val1) >> 16, - (sg->val1) & 0xffff, (int) src, - skill_get_time2 (sg->skill_id, - sg->skill_lv), - 0); - ts->tick -= sg->interval; - } - } break; - - case 0xb1: /* デモンストレーション */ - skill_attack (BF_WEAPON, ss, &src->bl, bl, sg->skill_id, - sg->skill_lv, tick, 0); - if (bl->type == BL_PC && MRAND (100) < sg->skill_lv - && battle_config.equipment_breaking) - pc_breakweapon ((struct map_session_data *) bl); - break; - case 0x99: /* トーキーボックス */ - if (sg->src_id == bl->id) //自分が踏んでも発動しない - break; - if (sg->val2 == 0) - { - clif_talkiebox (&src->bl, sg->valstr); - sg->unit_id = 0x8c; - clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); - sg->limit = DIFF_TICK (tick, sg->tick) + 5000; - sg->val2 = -1; //踏んだ - } - break; - case 0xb2: /* あなたを_会いたいです */ - case 0xb3: /* ゴスペル */ - case 0xb6: /* フォグウォール */ - //とりあえず何もしない - break; - - case 0xb7: /* スパイダーウェッブ */ - if (sg->val2 == 0) - { - int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE - || bl->y / BLOCK_SIZE != - src->bl.y / BLOCK_SIZE); - skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, - BF_MISC, tick); - if (moveblock) - map_delblock (bl); - bl->x = (&src->bl)->x; - bl->y = (&src->bl)->y; - if (moveblock) - map_addblock (bl); - if (bl->type == BL_MOB) - clif_fixmobpos ((struct mob_data *) bl); - else - clif_fixpos (bl); - clif_01ac (&src->bl); - sg->limit = - DIFF_TICK (tick, - sg->tick) + skill_get_time2 (sg->skill_id, - sg->skill_lv); - sg->val2 = bl->id; - } - break; - -/* default: - if(battle_config.error_log) - printf("skill_unit_onplace: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); - break;*/ - } - if (bl->type == BL_MOB && ss != bl) /* スキル使用条件のMOBスキル */ - { - if (battle_config.mob_changetarget_byskill == 1) - { - int target = ((struct mob_data *) bl)->target_id; - if (ss->type == BL_PC) - ((struct mob_data *) bl)->target_id = ss->id; - mobskill_use ((struct mob_data *) bl, tick, - MSC_SKILLUSED | (sg->skill_id << 16)); - ((struct mob_data *) bl)->target_id = target; - } - else - mobskill_use ((struct mob_data *) bl, tick, - MSC_SKILLUSED | (sg->skill_id << 16)); - } - - return 0; -} - -/*========================================== - * スキルユニットから離脱する(もしくはしている)場合 - *------------------------------------------ - */ -int skill_unit_onout (struct skill_unit *src, struct block_list *bl, - unsigned int tick) -{ - struct skill_unit_group *sg; - - nullpo_retr (0, src); - nullpo_retr (0, bl); - nullpo_retr (0, sg = src->group); - - if (bl->prev == NULL || !src->alive) - return 0; - - if (bl->type != BL_PC && bl->type != BL_MOB) - return 0; - - switch (sg->unit_id) - { - case 0x7e: /* セイフティウォール */ - case 0x85: /* ニューマ */ - case 0x8e: /* クァグマイア */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - int type = - (sg->unit_id == 0x85) ? SC_PNEUMA : - ((sg->unit_id == 0x7e) ? SC_SAFETYWALL : SC_QUAGMIRE); - if ((type != SC_QUAGMIRE || bl->type != BL_MOB) && - sc_data && sc_data[type].timer != -1 - && ((struct skill_unit *) sc_data[type].val2) == src) - { - skill_status_change_end (bl, type, -1); - } - } break; - - case 0x91: /* アンクルスネア */ - { - struct block_list *target = map_id2bl (sg->val2); - if (target && target == bl) - { - skill_status_change_end (bl, SC_ANKLE, -1); - sg->limit = DIFF_TICK (tick, sg->tick) + 1000; - } - } - break; - case 0xb5: - case 0xb8: - { - struct block_list *target = map_id2bl (sg->val2); - if (target == bl) - skill_status_change_end (bl, SC_SPIDERWEB, -1); - sg->limit = DIFF_TICK (tick, sg->tick) + 1000; - } - break; - case 0xb6: - { - struct block_list *target = map_id2bl (sg->val2); - if (target == bl) - skill_status_change_end (bl, SC_FOGWALL, -1); - sg->limit = DIFF_TICK (tick, sg->tick) + 1000; - } - break; - case 0x9a: /* ボルケーノ */ - case 0x9b: /* デリュージ */ - case 0x9c: /* バイオレントゲイル */ - { - struct status_change *sc_data = battle_get_sc_data (bl); - struct skill_unit *su; - int type = SkillStatusChangeTable[sg->skill_id]; - if (sc_data && sc_data[type].timer != -1 - && (su = ((struct skill_unit *) sc_data[type].val2)) - && su == src) - { - skill_status_change_end (bl, type, -1); - } - } - break; - - case 0x9e: /* 子守唄 */ - case 0x9f: /* ニヨルドの宴 */ - case 0xa0: /* 永遠の混沌 */ - case 0xa1: /* 戦太鼓の響き */ - case 0xa2: /* ニーベルングの指輪 */ - case 0xa3: /* ロキの叫び */ - case 0xa4: /* 深淵の中に */ - case 0xa5: /* 不死身のジークフリード */ - case 0xa6: /* 不協和音 */ - case 0xa7: /* 口笛 */ - case 0xa8: /* 夕陽のアサシンクロス */ - case 0xa9: /* ブラギの詩 */ - case 0xaa: /* イドゥンの林檎 */ - case 0xab: /* 自分勝手なダンス */ - case 0xac: /* ハミング */ - case 0xad: /* 私を忘れないで… */ - case 0xae: /* 幸運のキス */ - case 0xaf: /* サービスフォーユー */ - case 0xb4: - { - struct status_change *sc_data = battle_get_sc_data (bl); - struct skill_unit *su; - int type = SkillStatusChangeTable[sg->skill_id]; - if (sc_data && sc_data[type].timer != -1 - && (su = ((struct skill_unit *) sc_data[type].val4)) - && su == src) - { - skill_status_change_end (bl, type, -1); - } - } - break; - case 0xb7: /* スパイダーウェッブ */ - { - struct block_list *target = map_id2bl (sg->val2); - if (target && target == bl) - skill_status_change_end (bl, SC_SPIDERWEB, -1); - sg->limit = DIFF_TICK (tick, sg->tick) + 1000; - } - break; - -/* default: - if(battle_config.error_log) - printf("skill_unit_onout: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); - break;*/ - } - skill_unitgrouptickset_delete (bl, sg->group_id); - return 0; -} - -/*========================================== - * スキルユニットの削除イベント - *------------------------------------------ - */ -int skill_unit_ondelete (struct skill_unit *src, struct block_list *bl, - unsigned int tick) -{ - struct skill_unit_group *sg; - - nullpo_retr (0, src); - nullpo_retr (0, bl); - nullpo_retr (0, sg = src->group); - - if (bl->prev == NULL || !src->alive) - return 0; - - if (bl->type != BL_PC && bl->type != BL_MOB) - return 0; - - switch (sg->unit_id) - { - case 0x85: /* ニューマ */ - case 0x7e: /* セイフティウォール */ - case 0x8e: /* クァグマイヤ */ - case 0x9a: /* ボルケーノ */ - case 0x9b: /* デリュージ */ - case 0x9c: /* バイオレントゲイル */ - case 0x9e: /* 子守唄 */ - case 0x9f: /* ニヨルドの宴 */ - case 0xa0: /* 永遠の混沌 */ - case 0xa1: /* 戦太鼓の響き */ - case 0xa2: /* ニーベルングの指輪 */ - case 0xa3: /* ロキの叫び */ - case 0xa4: /* 深淵の中に */ - case 0xa5: /* 不死身のジークフリード */ - case 0xa6: /* 不協和音 */ - case 0xa7: /* 口笛 */ - case 0xa8: /* 夕陽のアサシンクロス */ - case 0xa9: /* ブラギの詩 */ - case 0xaa: /* イドゥンの林檎 */ - case 0xab: /* 自分勝手なダンス */ - case 0xac: /* ハミング */ - case 0xad: /* 私を忘れないで… */ - case 0xae: /* 幸運のキス */ - case 0xaf: /* サービスフォーユー */ - case 0xb4: - return skill_unit_onout (src, bl, tick); - -/* default: - if(battle_config.error_log) - printf("skill_unit_ondelete: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); - break;*/ - } - skill_unitgrouptickset_delete (bl, sg->group_id); - 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 0x81: /* ワープポータル(発動前) */ - { - 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; - CREATE (group->valstr, char, 24); - memcpy (group->valstr, sg->valstr, 24); - group->val2 = sg->val2; - } - break; - - case 0x8d: /* アイスウォール */ - map_setcell (src->bl.m, src->bl.x, src->bl.y, src->val2); - clif_changemapcell (src->bl.m, src->bl.x, src->bl.y, src->val2, - 1); - break; - case 0xb2: /* あなたに会いたい */ - { - struct map_session_data *sd = NULL; - struct map_session_data *p_sd = NULL; - if ((sd = - (struct map_session_data *) (map_id2bl (sg->src_id))) == - NULL) - return 0; - if ((p_sd = pc_get_partner (sd)) == NULL) - return 0; - - pc_setpos (p_sd, map[src->bl.m].name, 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); - - switch (sg->unit_id) - { - case 0x8d: /* アイスウォール */ - src->val1 -= damage; - break; - case 0x8f: /* ブラストマイン */ - case 0x98: /* クレイモアートラップ */ - skill_blown (bl, &src->bl, 2); //吹き飛ばしてみる - break; - default: - damage = 0; - break; - } - return damage; -} - -/*---------------------------------------------------------------------------- */ - -/*========================================== - * スキル使用(詠唱完了、場所指定) - *------------------------------------------ - */ -void skill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; - int range, maxcount; - - nullpo_retv (sd); - - if (sd->bl.prev == NULL) - return; - if (sd->skilltimer != tid) /* タイマIDの確認 */ - return; - if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) > 0) - { - sd->speed = sd->prev_speed; - clif_updatestatus (sd, SP_SPEED); - } - sd->skilltimer = -1; - if (pc_isdead (sd)) - { - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - - if (battle_config.pc_skill_reiteration == 0) - { - range = -1; - switch (sd->skillid) - { - case MG_SAFETYWALL: - case WZ_FIREPILLAR: - 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 AL_WARP: - case PF_SPIDERWEB: /* スパイダーウェッブ */ - case RG_GRAFFITI: /* グラフィティ */ - range = 0; - break; - case AL_PNEUMA: - range = 1; - break; - } - if (range >= 0) - { - if (skill_check_unit_range - (sd->bl.m, sd->skillx, sd->skilly, range, sd->skillid) > 0) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - } - if (battle_config.pc_skill_nofootset) - { - range = -1; - switch (sd->skillid) - { - case WZ_FIREPILLAR: - 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 PF_SPIDERWEB: /* スパイダーウェッブ */ - case WZ_ICEWALL: - range = 1; - break; - case AL_WARP: - range = 0; - break; - } - if (range >= 0) - { - if (skill_check_unit_range2 - (sd->bl.m, sd->skillx, sd->skilly, range) > 0) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - } - - 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); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - } - - range = skill_get_range (sd->skillid, sd->skilllv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - range += battle_config.pc_skill_add_range; - if (battle_config.skill_out_range_consume) - { // changed to allow casting when target walks out of range [Valaris] - if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - } - if (!skill_check_condition (sd, 1)) - { /* 使用条件チェック */ - sd->canact_tick = tick; - sd->canmove_tick = tick; - sd->skillitem = sd->skillitemlv = -1; - return; - } - sd->skillitem = sd->skillitemlv = -1; - if (battle_config.skill_out_range_consume) - { - if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - sd->canact_tick = tick; - sd->canmove_tick = tick; - return; - } - } - - if (battle_config.pc_skill_log) - printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); - pc_stop_walking (sd, 0); - - skill_castend_pos2 (&sd->bl, sd->skillx, sd->skilly, sd->skillid, - sd->skilllv, tick, 0); -} - -/*========================================== - * 範囲内キャラ存在確認判定処理(foreachinarea) - *------------------------------------------ - */ - -static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) -{ - int *c; - struct block_list *src; - struct map_session_data *sd; - struct map_session_data *ssd; - struct pc_base_job s_class; - struct pc_base_job ss_class; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, sd = (struct map_session_data *) bl); - nullpo_retr (0, src = va_arg (ap, struct block_list *)); - nullpo_retr (0, c = va_arg (ap, int *)); - nullpo_retr (0, ssd = (struct map_session_data *) src); - - s_class = pc_calc_base_job (sd->status.pc_class); - //チェックしない設定ならcにありえない大きな数字を返して終了 - if (!battle_config.player_skill_partner_check) - { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ - (*c) = 99; - return 0; - } - - ; - ss_class = pc_calc_base_job (ssd->status.pc_class); - - switch (ssd->skillid) - { - case PR_BENEDICTIO: /* 聖体降福 */ - if (sd != ssd - && (sd->status.pc_class == 4 || sd->status.pc_class == 8 - || sd->status.pc_class == 15 || sd->status.pc_class == 4005 - || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) - && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) - && sd->status.sp >= 10) - (*c)++; - 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: /* 不死身のジークフリード */ - case BD_RAGNAROK: /* 神々の黄昏 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - if (sd != ssd && - ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || - (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || - (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || - (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || - (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || - (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && - pc_checkskill (sd, ssd->skillid) > 0 && - (*c) == 0 && - sd->status.party_id == ssd->status.party_id && - !pc_issit (sd) && sd->sc_data[SC_DANCING].timer == -1) - (*c) = pc_checkskill (sd, ssd->skillid); - break; - } - return 0; -} - -/*========================================== - * 範囲内キャラ存在確認判定後スキル使用処理(foreachinarea) - *------------------------------------------ - */ - -static int skill_check_condition_use_sub (struct block_list *bl, va_list ap) -{ - int *c; - struct block_list *src; - struct map_session_data *sd; - struct map_session_data *ssd; - struct pc_base_job s_class; - struct pc_base_job ss_class; - int skillid, skilllv; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, sd = (struct map_session_data *) bl); - nullpo_retr (0, src = va_arg (ap, struct block_list *)); - nullpo_retr (0, c = va_arg (ap, int *)); - nullpo_retr (0, ssd = (struct map_session_data *) src); - - s_class = pc_calc_base_job (sd->status.pc_class); - - //チェックしない設定ならcにありえない大きな数字を返して終了 - if (!battle_config.player_skill_partner_check) - { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ - (*c) = 99; - return 0; - } - - ss_class = pc_calc_base_job (ssd->status.pc_class); - skillid = ssd->skillid; - skilllv = ssd->skilllv; - switch (skillid) - { - case PR_BENEDICTIO: /* 聖体降福 */ - if (sd != ssd - && (sd->status.pc_class == 4 || sd->status.pc_class == 8 - || sd->status.pc_class == 15 || sd->status.pc_class == 4005 - || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) - && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) - && sd->status.sp >= 10) - { - sd->status.sp -= 10; - pc_calcstatus (sd, 0); - (*c)++; - } - 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: /* 不死身のジークフリード */ - case BD_RAGNAROK: /* 神々の黄昏 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - if (sd != ssd && //本人以外で - ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || - (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || - (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || - (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || - (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || - (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && //自分がダンサーならバードで - pc_checkskill (sd, skillid) > 0 && //スキルを持っていて - (*c) == 0 && //最初の一人で - sd->status.party_id == ssd->status.party_id && //パーティーが同じで - !pc_issit (sd) && //座ってない - sd->sc_data[SC_DANCING].timer == -1 //ダンス中じゃない - ) - { - ssd->sc_data[SC_DANCING].val4 = bl->id; - clif_skill_nodamage (bl, src, skillid, skilllv, 1); - skill_status_change_start (bl, SC_DANCING, skillid, - ssd->sc_data[SC_DANCING].val2, 0, - src->id, skill_get_time (skillid, - skilllv) + - 1000, 0); - sd->skillid_dance = sd->skillid = skillid; - sd->skilllv_dance = sd->skilllv = skilllv; - (*c)++; - } - break; - } - return 0; -} - -/*========================================== - * 範囲内バイオプラント、スフィアマイン用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); - - if (!(src_id = va_arg (ap, int))) - return 0; - if (!(mob_class = va_arg (ap, int))) - return 0; - nullpo_retr (0, c = va_arg (ap, int *)); - - if (md->mob_class == mob_class && md->master_id == src_id) - (*c)++; - return 0; -} - -/*========================================== - * スキル使用条件(偽で使用失敗) - *------------------------------------------ - */ -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]; - - nullpo_retr (0, sd); - - if (battle_config.gm_skilluncond > 0 - && pc_isGM (sd) >= battle_config.gm_skilluncond) - { - 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->skillid == AC_MAKINGARROW && sd->state.make_arrow_flag == 1) - { - sd->skillitem = sd->skillitemlv = -1; - return 0; - } - /*if(sd->skillid == AM_PHARMACY && sd->state.produce_flag == 1) { - * sd->skillitem = sd->skillitemlv = -1; - * return 0; - * } */ - - if (sd->skillitem == sd->skillid) - { /* アイテムの場合無条件成功 */ - if (type & 1) - sd->skillitem = sd->skillitemlv = -1; - return 1; - } - if (sd->opt1 > 0) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - return 0; - } - if (sd->sc_data) - { - if (sd->sc_data[SC_DIVINA].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) - { - clif_skill_fail (sd, sd->skillid, 0, 0); - return 0; /* 状態異常や沈黙など */ - } - } - skill = sd->skillid; - lv = sd->skilllv; - 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; - if (sd->dsprate != 100) - sp = sp * sd->dsprate / 100; /* 消費SP修正 */ - - 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: - if (sd->sc_data[SkillStatusChangeTable[skill]].timer != -1) - return 1; /* 解除する場合はSP消費しない */ - break; - case AL_TELEPORT: - case AL_WARP: - 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_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: // 阿修羅覇鳳拳 - if ((sd->sc_data[SC_COMBO].timer != -1 - && (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH - || sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) - || sd->sc_data[SC_BLADESTOP].timer != -1) - spiritball--; - break; - 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秒以上のみ? - clif_skill_fail (sd, skill, 0, 0); - return 0; - } - } - break; - case PR_BENEDICTIO: /* 聖体降福 */ - { - int range = 1; - int c = 0; - if (!(type & 1)) - { - 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); - if (c < 2) - { - clif_skill_fail (sd, skill, 0, 0); - return 0; - } - } - else - { - map_foreachinarea (skill_check_condition_use_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); - } - } - 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 maxcount = skill_get_maxcount (skill); - int mob_class = (skill == AM_CANNIBALIZE) ? 1118 : 1142; - if (battle_config.pc_land_skill_limit && maxcount > 0) - { - map_foreachinarea (skill_check_condition_mob_master_sub, - sd->bl.m, 0, 0, map[sd->bl.m].xs, - map[sd->bl.m].ys, BL_MOB, sd->bl.id, - mob_class, &c); - if (c >= maxcount) - { - clif_skill_fail (sd, skill, 0, 0); - return 0; - } - } - } - break; - case MG_FIREWALL: /* ファイアーウォール */ - /* 数制限 */ - 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; - } - - if (!(type & 2)) - { - if (hp > 0 && sd->status.hp < hp) - { /* HPチェック */ - clif_skill_fail (sd, skill, 2, 0); /* HP不足:失敗通知 */ - return 0; - } - if (sp > 0 && sd->status.sp < sp) - { /* SPチェック */ - clif_skill_fail (sd, skill, 1, 0); /* SP不足:失敗通知 */ - 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 & 2)) - { - clif_skill_fail (sd, skill, 0, 0); - return 0; - } - break; - case ST_CLOAKING: - if (!(sd->status.option & 4)) - { - 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_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: - if (map_getcell (sd->bl.m, sd->bl.x, sd->bl.y) != 3 - && (sd->sc_data[SC_DELUGE].timer == -1)) - { //水場判定 - clif_skill_fail (sd, skill, 0, 0); - return 0; - } - break; - } - - 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) - continue; - if (((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) - && sd->sc_data[SC_INTOABYSS].timer != -1) - continue; - if (skill == AM_POTIONPITCHER && 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 (!(type & 1)) - return 1; - - if (skill != AM_POTIONPITCHER) - { - if (skill == AL_WARP && !(type & 2)) - return 1; - for (i = 0; i < 10; i++) - { - if (index[i] >= 0) - pc_delitem (sd, index[i], amount[i], 0); // アイテム消費 - } - } - - if (type & 2) - return 1; - - pc_heal (sd, -sp, -hp); // [Fate] This might suppress some dupe messages - -/* 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 time) -{ - struct map_session_data *sd; - struct mob_data *md; // [Valaris] - struct status_change *sc_data; - int dex; - int castrate = 100; - int skill, lv, castnodex; - - nullpo_retr (0, bl); - - if (bl->type == BL_MOB) - { // Crash fix [Valaris] - md = (struct mob_data *) bl; - skill = md->skillid; - lv = md->skilllv; - } - - else - { - sd = (struct map_session_data *) bl; - skill = sd->skillid; - lv = sd->skilllv; - } - - sc_data = battle_get_sc_data (bl); - dex = battle_get_dex (bl); - - if (skill > MAX_SKILL_DB || skill < 0) - return 0; - - castnodex = skill_get_castnodex (skill, lv); - - if (time == 0) - return 0; - if (castnodex > 0 && bl->type == BL_PC) - castrate = ((struct map_session_data *) bl)->castrate; - else if (castnodex <= 0 && bl->type == BL_PC) - { - castrate = ((struct map_session_data *) bl)->castrate; - time = - time * castrate * (battle_config.castrate_dex_scale - - dex) / (battle_config.castrate_dex_scale * - 100); - time = time * battle_config.cast_rate / 100; - } - - /* サフラギウム */ - if (sc_data && sc_data[SC_SUFFRAGIUM].timer != -1) - { - time = time * (100 - sc_data[SC_SUFFRAGIUM].val1 * 15) / 100; - skill_status_change_end (bl, SC_SUFFRAGIUM, -1); - } - /* ブラギの詩 */ - if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) - time = - time * (100 - - (sc_data[SC_POEMBRAGI].val1 * 3 + - sc_data[SC_POEMBRAGI].val2 + - (sc_data[SC_POEMBRAGI].val3 >> 16))) / 100; - - return (time > 0) ? time : 0; -} - -/*========================================== - * ディレイ計算 - *------------------------------------------ - */ -int skill_delayfix (struct block_list *bl, int time) -{ - struct status_change *sc_data; - - nullpo_retr (0, bl); - - sc_data = battle_get_sc_data (bl); - if (time <= 0) - return 0; - - if (bl->type == BL_PC) - { - if (battle_config.delay_dependon_dex) /* dexの影響を計算する */ - time = - time * (battle_config.castrate_dex_scale - - battle_get_dex (bl)) / - battle_config.castrate_dex_scale; - time = time * battle_config.delay_rate / 100; - } - - /* ブラギの詩 */ - if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) - time = - time * (100 - - (sc_data[SC_POEMBRAGI].val1 * 3 + - sc_data[SC_POEMBRAGI].val2 + - (sc_data[SC_POEMBRAGI].val3 & 0xffff))) / 100; - - return (time > 0) ? time : 0; -} - -/*========================================== - * スキル使用(ID指定) - *------------------------------------------ - */ -int skill_use_id (struct map_session_data *sd, int target_id, - int skill_num, int skill_lv) -{ - unsigned int tick; - int casttime = 0, delay = 0, skill, range; - struct map_session_data *target_sd = NULL; - int forcecast = 0; - struct block_list *bl; - struct status_change *sc_data; - tick = gettick (); - - nullpo_retr (0, sd); - - if ((bl = map_id2bl (target_id)) == NULL) - { -/* if(battle_config.error_log) - printf("skill target not found %d\n",target_id); */ - return 0; - } - if (sd->bl.m != bl->m || pc_isdead (sd)) - return 0; - - if (skillnotok (skill_num, sd)) // [MouseJstr] - return 0; - - if (sd->skillid == WZ_ICEWALL && map[sd->bl.m].flag.noicewall - && !map[sd->bl.m].flag.pvp) - { // noicewall flag [Valaris] - clif_skill_fail (sd, sd->skillid, 0, 0); - return 0; - } - sc_data = sd->sc_data; - - /* 沈黙や異常(ただし、グリムなどの判定をする) */ - if (sd->opt1 > 0) - return 0; - if (sd->sc_data) - { - if (sc_data[SC_CHASEWALK].timer != -1) - return 0; - if (sc_data[SC_VOLCANO].timer != -1) - { - if (skill_num == WZ_ICEWALL) - return 0; - } - if (sc_data[SC_ROKISWEIL].timer != -1) - { - if (skill_num == BD_ADAPTATION) - return 0; - } - if (sd->sc_data[SC_DIVINA].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) - { - return 0; /* 状態異常や沈黙など */ - } - - if (sc_data[SC_BLADESTOP].timer != -1) - { - int lv = sc_data[SC_BLADESTOP].val1; - if (sc_data[SC_BLADESTOP].val2 == 1) - return 0; //白羽された側なのでダメ - if (lv == 1) - return 0; - if (lv == 2 && skill_num != MO_FINGEROFFENSIVE) - return 0; - if (lv == 3 && skill_num != MO_FINGEROFFENSIVE - && skill_num != MO_INVESTIGATE) - return 0; - if (lv == 4 && skill_num != MO_FINGEROFFENSIVE - && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) - return 0; - if (lv == 5 && skill_num != MO_FINGEROFFENSIVE - && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO - && skill_num != MO_EXTREMITYFIST) - return 0; - } - } - - if (sd->status.option & 4 && skill_num == TF_HIDING) - return 0; - if (sd->status.option & 2 && skill_num != TF_HIDING - && skill_num != AS_GRIMTOOTH && skill_num != RG_BACKSTAP - && skill_num != RG_RAID) - return 0; - - if (map[sd->bl.m].flag.gvg) - { //GvGで使用できないスキル - switch (skill_num) - { - case SM_ENDURE: - case AL_TELEPORT: - case AL_WARP: - case WZ_ICEWALL: - case TF_BACKSLIDING: - case LK_BERSERK: - case HP_BASILICA: - case ST_CHASEWALK: - return 0; - } - } - - /* 演奏/ダンス中 */ - if (sc_data && sc_data[SC_DANCING].timer != -1) - { -// if(battle_config.pc_skill_log) -// printf("dancing! %d\n",skill_num); - if (sc_data[SC_DANCING].val4 && skill_num != BD_ADAPTATION) //合奏中はアドリブ以外不可 - return 0; - if (skill_num != BD_ADAPTATION && skill_num != BA_MUSICALSTRIKE - && skill_num != DC_THROWARROW) - { - return 0; - } - } - - if (skill_get_inf2 (skill_num) & 0x200 && sd->bl.id == target_id) - 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; - } - - sd->skillid = skill_num; - sd->skilllv = skill_lv; - - switch (skill_num) - { //事前にレベルが変わったりするスキル - case BD_LULLABY: /* 子守歌 */ - case BD_RICHMANKIM: /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: /* 永遠の混沌 */ - case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ - case BD_ROKISWEIL: /* ロキの叫び */ - case BD_INTOABYSS: /* 深淵の中に */ - case BD_SIEGFRIED: /* 不死身のジークフリード */ - case BD_RAGNAROK: /* 神々の黄昏 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - { - int range = 1; - int c = 0; - 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); - if (c < 1) - { - clif_skill_fail (sd, skill_num, 0, 0); - return 0; - } - else if (c == 99) - { //相方不要設定だった - ; - } - else - { - sd->skilllv = (c + skill_lv) / 2; - } - } - break; - } - - if (!skill_check_condition (sd, 0)) - return 0; - - /* 射程と障害物チェック */ - range = skill_get_range (skill_num, skill_lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - if (!battle_check_range (&sd->bl, bl, range)) - return 0; - - if (bl->type == BL_PC) - { - target_sd = (struct map_session_data *) bl; - if (target_sd && skill_num == ALL_RESURRECTION - && !pc_isdead (target_sd)) - 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 == MO_EXTREMITYFIST && sd->state.skill_flag)) - pc_stopattack (sd); - - casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); - if (skill_num != SA_MAGICROD) - delay = - skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); - sd->state.skillcastcancel = skill_db[skill_num].castcancel; - - switch (skill_num) - { /* 何か特殊な処理が必要 */ -// case AL_HEAL: /* ヒール */ -// if(battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) -// forcecast=1; /* ヒールアタックなら詠唱エフェクト有り */ -// break; - case ALL_RESURRECTION: /* リザレクション */ - if (bl->type != BL_PC - && battle_check_undead (battle_get_race (bl), - battle_get_elem_type (bl))) - { /* 敵がアンデッドなら */ - forcecast = 1; /* ターンアンデットと同じ詠唱時間 */ - casttime = - skill_castfix (&sd->bl, - skill_get_cast (PR_TURNUNDEAD, skill_lv)); - } - break; - case MO_FINGEROFFENSIVE: /* 指弾 */ - casttime += - casttime * - ((skill_lv > sd->spiritball) ? sd->spiritball : skill_lv); - break; - case MO_CHAINCOMBO: /*連打掌 */ - target_id = sd->attacktarget; - if (sc_data && sc_data[SC_BLADESTOP].timer != -1) - { - struct block_list *tbl; - if ((tbl = (struct block_list *) sc_data[SC_BLADESTOP].val4) == NULL) //ターゲットがいない? - return 0; - target_id = tbl->id; - } - break; - case MO_COMBOFINISH: /*猛龍拳 */ - case CH_TIGERFIST: /* 伏虎拳 */ - case CH_CHAINCRUSH: /* 連柱崩撃 */ - target_id = sd->attacktarget; - break; - -// -- moonsoul (altered to allow proper usage of extremity from new champion combos) -// - case MO_EXTREMITYFIST: /*阿修羅覇鳳拳 */ - if (sc_data && sc_data[SC_COMBO].timer != -1 - && (sc_data[SC_COMBO].val1 == MO_COMBOFINISH - || sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) - { - casttime = 0; - target_id = sd->attacktarget; - } - forcecast = 1; - break; - case SA_MAGICROD: - case SA_SPELLBREAKER: - forcecast = 1; - break; - case WE_MALE: - case WE_FEMALE: - { - struct map_session_data *p_sd = NULL; - if ((p_sd = pc_get_partner (sd)) == NULL) - return 0; - target_id = p_sd->bl.id; - //rangeをもう1回検査 - range = skill_get_range (skill_num, skill_lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - if (!battle_check_range (&sd->bl, &p_sd->bl, range)) - { - return 0; - } - } - break; - case AS_SPLASHER: /* ベナムスプラッシャー */ - { - struct status_change *t_sc_data = battle_get_sc_data (bl); - if (t_sc_data && t_sc_data[SC_POISON].timer == -1) - { - clif_skill_fail (sd, skill_num, 0, 10); - return 0; - } - } - break; - case PF_MEMORIZE: /* メモライズ */ - casttime = 12000; - break; - - } - - //メモライズ状態ならキャストタイムが1/3 - if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) - { - casttime = casttime / 3; - if ((--sc_data[SC_MEMORIZE].val2) <= 0) - skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); - } - - if (battle_config.pc_skill_log) - printf ("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(sd->skillitem == skill_num) -// casttime = delay = 0; - - if (casttime > 0 || forcecast) - { /* 詠唱が必要 */ - struct mob_data *md; - clif_skillcasting (&sd->bl, - sd->bl.id, target_id, 0, 0, skill_num, casttime); - - /* 詠唱反応モンスター */ - if (bl->type == BL_MOB && (md = (struct mob_data *) bl) - && mob_db[md->mob_class].mode & 0x10 && md->state.state != MS_ATTACK - && sd->invincible_timer == -1) - { - md->target_id = sd->bl.id; - md->state.targettype = ATTACKABLE; - md->min_chase = 13; - } - } - - if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ - sd->state.skillcastcancel = 0; - - sd->skilltarget = target_id; -/* sd->cast_target_bl = bl; */ - sd->skillx = 0; - sd->skilly = 0; - sd->canact_tick = tick + casttime + delay; - sd->canmove_tick = tick; - if (!(battle_config.pc_cloak_check_type & 2) && sc_data - && sc_data[SC_CLOAKING].timer != -1 && sd->skillid != AS_CLOAKING) - skill_status_change_end (&sd->bl, SC_CLOAKING, -1); - if (casttime > 0) - { - sd->skilltimer = - add_timer (tick + casttime, skill_castend_id, sd->bl.id, 0); - if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) - { - sd->prev_speed = sd->speed; - sd->speed = sd->speed * (175 - skill * 5) / 100; - clif_updatestatus (sd, SP_SPEED); - } - else - pc_stop_walking (sd, 0); - } - else - { - if (skill_num != SA_CASTCANCEL) - sd->skilltimer = -1; - skill_castend_id (sd->skilltimer, tick, sd->bl.id, 0); - } - - //マジックパワーの効果終了 - if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 - && skill_num != HW_MAGICPOWER) - skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); - - return 0; -} - -/*========================================== - * スキル使用(場所指定) - *------------------------------------------ - */ -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; - unsigned int tick; - int casttime = 0, delay = 0, skill, range; - - nullpo_retr (0, sd); - - if (pc_isdead (sd)) - return 0; - - if (skillnotok (skill_num, sd)) // [MoueJstr] - return 0; - - sc_data = sd->sc_data; - - if (sd->opt1 > 0) - return 0; - if (sc_data) - { - if (sc_data[SC_DIVINA].timer != -1 || - sc_data[SC_ROKISWEIL].timer != -1 || - sc_data[SC_AUTOCOUNTER].timer != -1 || - sc_data[SC_STEELBODY].timer != -1 || - sc_data[SC_DANCING].timer != -1 || - sc_data[SC_BERSERK].timer != -1) - return 0; /* 状態異常や沈黙など */ - } - - if (sd->status.option & 2) - return 0; - - if (map[sd->bl.m].flag.gvg - && (skill_num == SM_ENDURE || skill_num == AL_TELEPORT - || skill_num == AL_WARP || skill_num == WZ_ICEWALL - || skill_num == TF_BACKSLIDING)) - 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; - - /* 射程と障害物チェック */ - bl.type = BL_NUL; - bl.m = sd->bl.m; - bl.x = skill_x; - bl.y = skill_y; - range = skill_get_range (skill_num, skill_lv); - if (range < 0) - range = battle_get_range (&sd->bl) - (range + 1); - if (!battle_check_range (&sd->bl, &bl, range)) - return 0; - - pc_stopattack (sd); - - casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); - delay = skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); - sd->state.skillcastcancel = skill_db[skill_num].castcancel; - - if (battle_config.pc_skill_log) - printf ("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); - -// if(sd->skillitem == skill_num) -// casttime = delay = 0; - //メモライズ状態ならキャストタイムが1/3 - if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) - { - casttime = casttime / 3; - if ((--sc_data[SC_MEMORIZE].val2) <= 0) - skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); - } - - if (casttime > 0) /* 詠唱が必要 */ - clif_skillcasting (&sd->bl, - sd->bl.id, 0, skill_x, skill_y, skill_num, - casttime); - - if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ - sd->state.skillcastcancel = 0; - - sd->skilltarget = 0; -/* sd->cast_target_bl = NULL; */ - tick = gettick (); - sd->canact_tick = tick + casttime + delay; - sd->canmove_tick = tick; - if (!(battle_config.pc_cloak_check_type & 2) && sc_data - && sc_data[SC_CLOAKING].timer != -1) - skill_status_change_end (&sd->bl, SC_CLOAKING, -1); - if (casttime > 0) - { - sd->skilltimer = - add_timer (tick + casttime, skill_castend_pos, sd->bl.id, 0); - if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) - { - sd->prev_speed = sd->speed; - sd->speed = sd->speed * (175 - skill * 5) / 100; - clif_updatestatus (sd, SP_SPEED); - } - else - pc_stop_walking (sd, 0); - } - else - { - sd->skilltimer = -1; - skill_castend_pos (sd->skilltimer, tick, sd->bl.id, 0); - } - //マジックパワーの効果終了 - if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 - && skill_num != HW_MAGICPOWER) - skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); - - return 0; -} - -/*========================================== - * スキル詠唱キャンセル - *------------------------------------------ - */ -int skill_castcancel (struct block_list *bl, int type) -{ - int inf; - - 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 (pc_checkskill (sd, SA_FREECAST) > 0) - { - sd->speed = sd->prev_speed; - clif_updatestatus (sd, SP_SPEED); - } - if (!type) - { - if ((inf = skill_get_inf (sd->skillid)) == 2 || inf == 32) - delete_timer (sd->skilltimer, skill_castend_pos); - else - delete_timer (sd->skilltimer, skill_castend_id); - } - else - { - if ((inf = skill_get_inf (sd->skillid_old)) == 2 || inf == 32) - delete_timer (sd->skilltimer, skill_castend_pos); - else - delete_timer (sd->skilltimer, skill_castend_id); - } - 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 ((inf = skill_get_inf (md->skillid)) == 2 || inf == 32) - delete_timer (md->skilltimer, mobskill_castend_pos); - else - delete_timer (md->skilltimer, mobskill_castend_id); - md->skilltimer = -1; - clif_skillcastcancel (bl); - } - 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; - } - } -} - -/*========================================== - * ディボーション 有効確認 - *------------------------------------------ - */ -void skill_devotion (struct map_session_data *md, int target) -{ - // 総確認 - int n; - - nullpo_retv (md); - - for (n = 0; n < 5; n++) - { - if (md->dev.val1[n]) - { - struct map_session_data *sd = map_id2sd (md->dev.val1[n]); - // 相手が見つからない // 相手をディボしてるのが自分じゃない // 距離が離れてる - if (sd == NULL - || (sd->sc_data - && (md->bl.id != sd->sc_data[SC_DEVOTION].val1)) - || skill_devotion3 (&md->bl, md->dev.val1[n])) - { - skill_devotion_end (md, sd, n); - } - } - } -} - -void skill_devotion2 (struct block_list *bl, int crusader) -{ - // 被ディボーションが歩いた時の距離チェック - struct map_session_data *sd = map_id2sd (crusader); - - nullpo_retv (bl); - - if (sd) - skill_devotion3 (&sd->bl, bl->id); -} - -int skill_devotion3 (struct block_list *bl, int target) -{ - // クルセが歩いた時の距離チェック - struct map_session_data *md; - struct map_session_data *sd; - int n, r = 0; - - nullpo_retr (1, bl); - - if ((md = (struct map_session_data *) bl) == NULL - || (sd = map_id2sd (target)) == NULL) - return 1; - else - r = distance (bl->x, bl->y, sd->bl.x, sd->bl.y); - - if (pc_checkskill (sd, CR_DEVOTION) + 6 < r) - { // 許容範囲を超えてた - for (n = 0; n < 5; n++) - if (md->dev.val1[n] == target) - md->dev.val2[n] = 0; // 離れた時は、糸を切るだけ - clif_devotion (md, sd->bl.id); - return 1; - } - return 0; -} - -void skill_devotion_end (struct map_session_data *md, - struct map_session_data *sd, int target) -{ - // クルセと被ディボキャラのリセット - nullpo_retv (md); - nullpo_retv (sd); - - md->dev.val1[target] = md->dev.val2[target] = 0; - if (sd && sd->sc_data) - { - // skill_status_change_end(sd->bl,SC_DEVOTION,-1); - sd->sc_data[SC_DEVOTION].val1 = 0; - sd->sc_data[SC_DEVOTION].val2 = 0; - clif_status_change (&sd->bl, SC_DEVOTION, 0); - clif_devotion (md, sd->bl.id); - } -} - -/*========================================== - * オートスペル - *------------------------------------------ - */ -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 (skillid == MG_NAPALMBEAT) - maxlv = 3; - else if (skillid == MG_COLDBOLT || skillid == MG_FIREBOLT - || skillid == MG_LIGHTNINGBOLT) - { - 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; - - skill_status_change_start (&sd->bl, SC_AUTOSPELL, skilllv, skillid, maxlv, 0, // val1:スキルID val2:使用最大Lv - skill_get_time (SA_AUTOSPELL, skilllv), 0); // にしてみたけどbscriptが書き易い・・・? - 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 > 0) - { /*ギャングスター成功したら自分にもギャングスター属性付与 */ - 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 < 1) - 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; -} - -/*========================================== - * 寒いジョーク・スクリーム判定処理(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); - tick = va_arg (ap, unsigned int); - - if (src == bl) //自分には効かない - return 0; - - 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) - { - if (MRAND (100) < 10) //PTメンバにも低確率でかかる(とりあえず10%) - skill_additional_effect (src, bl, skillnum, skilllv, BF_MISC, - tick); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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); - 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, skillid; - - nullpo_retr (0, bl); - - if (bl->type == BL_MOB) - md = (struct mob_data *) bl; - if (bl->type == BL_PC) - sd = (struct map_session_data *) bl; - - for (i = 0; i < MAX_MOBSKILLUNITGROUP; 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; -} - -/*========================================== - * ランドプロテクターチェック(foreachinarea) - *------------------------------------------ - */ -int skill_landprotector (struct block_list *bl, va_list ap) -{ - int skillid; - int *alive; - struct skill_unit *unit; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - - skillid = va_arg (ap, int); - alive = va_arg (ap, int *); - if ((unit = (struct skill_unit *) bl) == NULL) - return 0; - - if (skillid == SA_LANDPROTECTOR) - { - skill_delunit (unit); - } - else - { - if (alive && unit->group->skill_id == SA_LANDPROTECTOR) - (*alive) = 0; - } - return 0; -} - -/*========================================== - * イドゥンの林檎の回復処理(foreachinarea) - *------------------------------------------ - */ -int skill_idun_heal (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit; - struct skill_unit_group *sg; - int heal; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, unit = va_arg (ap, struct skill_unit *)); - nullpo_retr (0, sg = unit->group); - - heal = - 30 + sg->skill_lv * 5 + ((sg->val1) >> 16) * 5 + - ((sg->val1) & 0xfff) / 2; - - if (bl->type == BL_SKILL || bl->id == sg->src_id) - return 0; - - if (bl->type == BL_PC || bl->type == BL_MOB) - { - clif_skill_nodamage (&unit->bl, bl, AL_HEAL, heal, 1); - battle_heal (NULL, bl, heal, 0, 0); - } - 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)); - - 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 0x95: /* サンドマン */ - case 0x96: /* フラッシャー */ - case 0x94: /* ショックウェーブトラップ */ - skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, - BF_MISC, tick); - break; - case 0x8f: /* ブラストマイン */ - case 0x98: /* クレイモアートラップ */ - 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); - } - case 0x97: /* フリージングトラップ */ - skill_attack (BF_WEAPON, ss, src, bl, sg->skill_id, - sg->skill_lv, tick, (sg->val2) ? 0x0500 : 0); - break; - default: - break; - } - } - - return 0; -} - -/*---------------------------------------------------------------------------- - * ステータス異常 - *---------------------------------------------------------------------------- - */ - -/*========================================== - * ステータス異常タイマー範囲処理 - *------------------------------------------ - */ -int skill_status_change_timer_sub (struct block_list *bl, va_list ap) -{ - struct block_list *src; - int type; - unsigned int tick; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, src = va_arg (ap, struct block_list *)); - type = va_arg (ap, int); - tick = va_arg (ap, unsigned int); - - if (bl->type != BL_PC && bl->type != BL_MOB) - return 0; - - switch (type) - { - case SC_SIGHT: /* サイト */ - case SC_CONCENTRATE: - if ((*battle_get_option (bl)) & 6) - { - skill_status_change_end (bl, SC_HIDING, -1); - skill_status_change_end (bl, SC_CLOAKING, -1); - } - break; - case SC_RUWACH: /* ルアフ */ - if ((*battle_get_option (bl)) & 6) - { - skill_status_change_end (bl, SC_HIDING, -1); - skill_status_change_end (bl, SC_CLOAKING, -1); - if (battle_check_target (src, bl, BCT_ENEMY) > 0) - { - struct status_change *sc_data = battle_get_sc_data (bl); - skill_attack (BF_MAGIC, src, src, bl, AL_RUWACH, - sc_data[type].val1, tick, 0); - } - } - break; - } - return 0; -} - -/*========================================== - * ステータス異常終了 - *------------------------------------------ - */ -int skill_status_change_active (struct block_list *bl, int type) -{ - struct status_change *sc_data; - - nullpo_retr (0, bl); - if (bl->type != BL_PC && bl->type != BL_MOB) - { - if (battle_config.error_log) - printf ("skill_status_change_active: neither MOB nor PC !\n"); - return 0; - } - - nullpo_retr (0, sc_data = battle_get_sc_data (bl)); - - return sc_data[type].timer != -1; -} - -int skill_status_change_end (struct block_list *bl, int type, int tid) -{ - 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) - printf ("skill_status_change_end: neither MOB nor PC !\n"); - return 0; - } - nullpo_retr (0, sc_data = battle_get_sc_data (bl)); - nullpo_retr (0, sc_count = battle_get_sc_count (bl)); - nullpo_retr (0, option = battle_get_option (bl)); - nullpo_retr (0, opt1 = battle_get_opt1 (bl)); - nullpo_retr (0, opt2 = battle_get_opt2 (bl)); - nullpo_retr (0, opt3 = battle_get_opt3 (bl)); - - if ((*sc_count) > 0 && sc_data[type].timer != -1 - && (sc_data[type].timer == tid || tid == -1)) - { - - if (tid == -1) // タイマから呼ばれていないならタイマ削除をする - delete_timer (sc_data[type].timer, skill_status_change_timer); - - /* 該当の異常を正常に戻す */ - sc_data[type].timer = -1; - (*sc_count)--; - - switch (type) - { /* 異常の種類ごとの処理 */ - case SC_PROVOKE: /* プロボック */ - case SC_CONCENTRATE: /* 集中力向上 */ - case SC_BLESSING: /* ブレッシング */ - case SC_ANGELUS: /* アンゼルス */ - case SC_INCREASEAGI: /* 速度上昇 */ - case SC_DECREASEAGI: /* 速度減少 */ - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - case SC_HIDING: - case SC_TWOHANDQUICKEN: /* 2HQ */ - 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_DEFENDER: - case SC_SPEEDPOTION0: /* 増速ポーション */ - case SC_SPEEDPOTION1: - case SC_SPEEDPOTION2: - case SC_APPLEIDUN: /* イドゥンの林檎 */ - case SC_RIDING: - case SC_BLADESTOP_WAIT: - case SC_AURABLADE: /* オーラブレード */ - case SC_PARRYING: /* パリイング */ - case SC_CONCENTRATION: /* コンセントレーション */ - case SC_TENSIONRELAX: /* テンションリラックス */ - case SC_ASSUMPTIO: /* アシャンプティオ */ - case SC_WINDWALK: /* ウインドウォーク */ - case SC_TRUESIGHT: /* トゥルーサイト */ - case SC_SPIDERWEB: /* スパイダーウェッブ */ - case SC_MAGICPOWER: /* 魔法力増幅 */ - case SC_CHASEWALK: - case SC_ATKPOT: /* attack potion [Valaris] */ - case SC_MATKPOT: /* magic attack potion [Valaris] */ - case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) - case SC_MELTDOWN: /* メルトダウン */ - case SC_PHYS_SHIELD: - case SC_HASTE: - calc_flag = 1; - break; - case SC_BERSERK: /* バーサーク */ - calc_flag = 1; - clif_status_change (bl, SC_INCREASEAGI, 0); /* アイコン消去 */ - break; - case SC_DEVOTION: /* ディボーション */ - { - struct map_session_data *md = map_id2sd (sc_data[type].val1); - sc_data[type].val1 = sc_data[type].val2 = 0; - skill_devotion (md, bl->id); - calc_flag = 1; - } - break; - case SC_BLADESTOP: - { - struct status_change *t_sc_data = - battle_get_sc_data ((struct block_list *) - sc_data[type].val4); - //片方が切れたので相手の白刃状態が切れてないのなら解除 - if (t_sc_data && t_sc_data[SC_BLADESTOP].timer != -1) - skill_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].val4 - && (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].val4 = 0; - } - } - calc_flag = 1; - break; - case SC_GRAFFITI: - { - struct skill_unit_group *sg = (struct skill_unit_group *) sc_data[type].val4; //val4がグラフィティのgroup_id - if (sg) - skill_delunitgroup (sg); - } - break; - case SC_NOCHAT: //チャット禁止状態 - { - struct map_session_data *sd = NULL; - if (bl->type == BL_PC - && (sd = (struct map_session_data *) bl)) - { - 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_SELFDESTRUCTION: /* 自爆 */ - { - //自分のダメージは0にして - struct mob_data *md = NULL; - if (bl->type == BL_MOB && (md = (struct mob_data *) bl)) - skill_castend_damage_id (bl, bl, sc_data[type].val2, - sc_data[type].val1, gettick (), - 0); - } - 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; - } - - if (bl->type == BL_PC && type < SC_SENDMAX) - clif_status_change (bl, 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_SLOWPOISON: - if (sc_data[SC_POISON].timer != -1) - *opt2 |= 0x1; - *opt2 &= ~0x200; - opt_flag = 1; - break; - - case SC_SIGNUMCRUCIS: - *opt2 &= ~0x40; - opt_flag = 1; - break; - - case SC_SPEEDPOTION0: - *opt2 &= ~0x20; - opt_flag = 1; - break; - - case SC_ATKPOT: - *opt2 &= ~0x80; - opt_flag = 1; - break; - - case SC_HIDING: - case SC_CLOAKING: - *option &= ~((type == SC_HIDING) ? 2 : 4); - opt_flag = 1; - break; - - case SC_CHASEWALK: - *option &= ~16388; - opt_flag = 1; - break; - - case SC_SIGHT: - *option &= ~1; - opt_flag = 1; - break; - case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) - *option &= ~4096; - opt_flag = 1; - break; - case SC_RUWACH: - *option &= ~8192; - opt_flag = 1; - break; - - //opt3 - case SC_TWOHANDQUICKEN: /* 2HQ */ - 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: // 金剛 - *opt3 &= ~16; - break; - case SC_BLADESTOP: /* 白刃取り */ - *opt3 &= ~32; - break; - case SC_BERSERK: /* バーサーク */ - *opt3 &= ~128; - break; - case SC_MARIONETTE: /* マリオネットコントロール */ - *opt3 &= ~1024; - break; - case SC_ASSUMPTIO: /* アスムプティオ */ - *opt3 &= ~2048; - break; - } - - if (night_flag == 1 && (*opt2 & STATE_BLIND) == 0 - && bl->type == BL_PC) - { // by [Yor] - *opt2 |= STATE_BLIND; - opt_flag = 1; - } - - if (opt_flag) /* optionの変更を伝える */ - clif_changeoption (bl); - - if (bl->type == BL_PC && calc_flag) - pc_calcstatus ((struct map_session_data *) bl, 0); /* ステータス再計算 */ - } - - return 0; -} - -int skill_update_heal_animation (struct map_session_data *sd) -{ - const int mask = 0x100; - int was_active; - int is_active; - - nullpo_retr (0, sd); - was_active = sd->opt2 & mask; - is_active = sd->quick_regeneration_hp.amount > 0; - - if ((was_active && is_active) || (!was_active && !is_active)) - return 0; // no update - - if (is_active) - sd->opt2 |= mask; - else - sd->opt2 &= ~mask; - - return clif_changeoption (&sd->bl); -} - -/*========================================== - * ステータス異常終了タイマー - *------------------------------------------ - */ -void skill_status_change_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - int type = data; - struct block_list *bl; - struct map_session_data *sd = NULL; - struct status_change *sc_data; - //short *sc_count; //使ってない? - - if ((bl = map_id2bl (id)) == NULL) - return; //該当IDがすでに消滅しているというのはいかにもありそうなのでスルーしてみる - nullpo_retv (sc_data = battle_get_sc_data (bl)); - - if (bl->type == BL_PC) - sd = (struct map_session_data *) bl; - - //sc_count=battle_get_sc_count(bl); //使ってない? - - if (sc_data[type].timer != tid) - { - if (battle_config.error_log) - printf ("skill_status_change_timer %d != %d\n", tid, - sc_data[type].timer); - } - - if (sc_data[type].spell_invocation) - { // Must report termination - spell_effect_report_termination (sc_data[type].spell_invocation, - bl->id, type, 0); - sc_data[type].spell_invocation = 0; - } - - switch (type) - { /* 特殊な処理になる場合 */ - case SC_MAXIMIZEPOWER: /* マキシマイズパワー */ - case SC_CLOAKING: /* クローキング */ - case SC_CHASEWALK: - if (sd) - { - if (sd->status.sp > 0) - { /* SP切れるまで持続 */ - sd->status.sp--; - clif_updatestatus (sd, SP_SP); - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - sc_data[type].val2 + - tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - 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, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - - case SC_SIGHT: /* サイト */ - { - const int range = 7; - map_foreachinarea (skill_status_change_timer_sub, - bl->m, bl->x - range, bl->y - range, - bl->x + range, bl->y + range, 0, bl, type, - tick); - - if ((--sc_data[type].val2) > 0) - { - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 250 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - case SC_RUWACH: /* ルアフ */ - { - const int range = 5; - map_foreachinarea (skill_status_change_timer_sub, - bl->m, bl->x - range, bl->y - range, - bl->x + range, bl->y + range, 0, bl, type, - tick); - - if ((--sc_data[type].val2) > 0) - { - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 250 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - { - int race = battle_get_race (bl); - if (race == 6 - || battle_check_undead (race, battle_get_elem_type (bl))) - { - sc_data[type].timer = - add_timer (1000 * 600 + tick, skill_status_change_timer, - bl->id, data); - return; - } - } - 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, skill_status_change_timer, bl->id, - data); - return; - } - break; - - case SC_WATERBALL: /* ウォーターボール */ - { - struct block_list *target = map_id2bl (sc_data[type].val2); - if (target == NULL || target->prev == NULL) - break; - skill_attack (BF_MAGIC, bl, bl, target, WZ_WATERBALL, - sc_data[type].val1, tick, 0); - if ((--sc_data[type].val3) > 0) - { - sc_data[type].timer = - add_timer (150 + tick, skill_status_change_timer, bl->id, - data); - return; - } - } - break; - - case SC_ENDURE: /* インデュア */ - if (sd && sd->special_state.infinite_endure) - { - sc_data[type].timer = - add_timer (1000 * 600 + tick, skill_status_change_timer, - bl->id, data); - sc_data[type].val2 = 1; - return; - } - break; - - case SC_DISSONANCE: /* 不協和音 */ - if ((--sc_data[type].val2) > 0) - { - struct skill_unit *unit = - (struct skill_unit *) sc_data[type].val4; - struct block_list *src; - - if (!unit || !unit->group) - break; - src = map_id2bl (unit->group->src_id); - if (!src) - break; - skill_attack (BF_MISC, src, &unit->bl, bl, - unit->group->skill_id, sc_data[type].val1, tick, - 0); - sc_data[type].timer = - add_timer (skill_get_time2 - (unit->group->skill_id, - unit->group->skill_lv) + tick, - skill_status_change_timer, bl->id, data); - return; - } - break; - - case SC_LULLABY: /* 子守唄 */ - if ((--sc_data[type].val2) > 0) - { - struct skill_unit *unit = - (struct skill_unit *) sc_data[type].val4; - if (!unit || !unit->group || unit->group->src_id == bl->id) - break; - skill_additional_effect (bl, bl, unit->group->skill_id, - sc_data[type].val1, - BF_LONG | BF_SKILL | BF_MISC, tick); - sc_data[type].timer = - add_timer (skill_get_time - (unit->group->skill_id, - unit->group->skill_lv) / 10 + tick, - skill_status_change_timer, bl->id, data); - return; - } - break; - - case SC_STONE: - if (sc_data[type].val2 != 0) - { - short *opt1 = battle_get_opt1 (bl); - sc_data[type].val2 = 0; - sc_data[type].val4 = 0; - battle_stopwalking (bl, 1); - if (opt1) - { - *opt1 = 1; - clif_changeoption (bl); - } - sc_data[type].timer = - add_timer (1000 + tick, skill_status_change_timer, bl->id, - data); - return; - } - else if ((--sc_data[type].val3) > 0) - { - int hp = battle_get_max_hp (bl); - if ((++sc_data[type].val4) % 5 == 0 - && battle_get_hp (bl) > hp >> 2) - { - hp = hp / 100; - if (hp < 1) - hp = 1; - if (bl->type == BL_PC) - pc_heal ((struct map_session_data *) bl, -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, skill_status_change_timer, bl->id, - data); - return; - } - break; - case SC_POISON: - if (sc_data[SC_SLOWPOISON].timer == -1) - { - const int resist_poison = - skill_power_bl (bl, TMW_RESIST_POISON) >> 3; - if (resist_poison) - sc_data[type].val1 -= MRAND (resist_poison + 1); - - if ((--sc_data[type].val1) > 0) - { - - int hp = battle_get_max_hp (bl); - if (battle_get_hp (bl) > hp >> 4) - { - if (bl->type == BL_PC) - { - hp = 3 + hp * 3 / 200; - pc_heal ((struct map_session_data *) bl, -hp, 0); - } - else if (bl->type == BL_MOB) - { - struct mob_data *md; - if ((md = ((struct mob_data *) bl)) == NULL) - break; - hp = 3 + hp / 200; - md->hp -= hp; - } - } - sc_data[type].timer = - add_timer (1000 + tick, skill_status_change_timer, - bl->id, data); - } - } - else - sc_data[type].timer = - add_timer (2000 + tick, skill_status_change_timer, bl->id, - data); - break; - - case SC_TENSIONRELAX: /* テンションリラックス */ - if (sd) - { /* SPがあって、HPが満タンでなければ継続 */ - if (sd->status.sp > 12 && sd->status.max_hp > sd->status.hp) - { - if (sc_data[type].val2 % (sc_data[type].val1 + 3) == 0) - { - sd->status.sp -= 12; - clif_updatestatus (sd, SP_SP); - } - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 10000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - if (sd->status.max_hp <= sd->status.hp) - skill_status_change_end (&sd->bl, SC_TENSIONRELAX, -1); - } - break; - - /* 時間切れ無し?? */ - case SC_AETERNA: - case SC_TRICKDEAD: - case SC_RIDING: - case SC_FALCON: - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_MAGICPOWER: /* 魔法力増幅 */ - case SC_REJECTSWORD: /* リジェクトソード */ - case SC_MEMORIZE: /* メモライズ */ - case SC_BROKNWEAPON: - case SC_BROKNARMOR: - if (sc_data[type].timer == tid) - sc_data[type].timer = - add_timer (1000 * 600 + tick, skill_status_change_timer, - bl->id, data); - return; - - case SC_DANCING: //ダンススキルの時間SP消費 - { - int s = 0; - if (sd) - { - if (sd->status.sp > 0 && (--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 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 DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */ - s = 10; - break; - } - if (s && ((sc_data[type].val3 % s) == 0)) - { - sd->status.sp--; - clif_updatestatus (sd, SP_SP); - } - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 1000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - } - break; - case SC_BERSERK: /* バーサーク */ - if (sd) - { /* HPが100以上なら継続 */ - if ((sd->status.hp - sd->status.hp / 100) > 100) - { - sd->status.hp -= sd->status.hp / 100; - clif_updatestatus (sd, SP_HP); - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 15000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) - if (sd) - { - time_t timer; - if (time (&timer) < ((sc_data[type].val2) + 3600)) - { //1時間たっていないので継続 - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 10000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - case SC_NOCHAT: //チャット禁止状態 - if (sd && battle_config.muting_players) - { - time_t timer; - if ((++sd->status.manner) - && time (&timer) < - ((sc_data[type].val2) + 60 * (0 - sd->status.manner))) - { //開始からstatus.manner分経ってないので継続 - clif_updatestatus (sd, SP_MANNER); - sc_data[type].timer = add_timer ( /* タイマー再設定(60秒) */ - 60000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - } - break; - case SC_SELFDESTRUCTION: /* 自爆 */ - if (--sc_data[type].val3 > 0) - { - struct mob_data *md; - if (bl->type == BL_MOB && (md = (struct mob_data *) bl) - && md->stats[MOB_SPEED] > 250) - { - md->stats[MOB_SPEED] -= 250; - md->next_walktime = tick; - } - sc_data[type].timer = add_timer ( /* タイマー再設定 */ - 1000 + tick, - skill_status_change_timer, - bl->id, data); - return; - } - break; - - case SC_FLYING_BACKPACK: - clif_updatestatus (sd, SP_WEIGHT); - break; - - } - - skill_status_change_end (bl, type, tid); -} - -/*========================================== - * ステータス異常終了 - *------------------------------------------ - */ -int skill_encchant_eremental_end (struct block_list *bl, int type) -{ - struct status_change *sc_data; - - nullpo_retr (0, bl); - nullpo_retr (0, sc_data = battle_get_sc_data (bl)); - - if (type != SC_ENCPOISON && sc_data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解除 */ - skill_status_change_end (bl, SC_ENCPOISON, -1); - if (type != SC_ASPERSIO && sc_data[SC_ASPERSIO].timer != -1) /* アスペルシオ解除 */ - skill_status_change_end (bl, SC_ASPERSIO, -1); - if (type != SC_FLAMELAUNCHER && sc_data[SC_FLAMELAUNCHER].timer != -1) /* フレイムランチャ解除 */ - skill_status_change_end (bl, SC_FLAMELAUNCHER, -1); - if (type != SC_FROSTWEAPON && sc_data[SC_FROSTWEAPON].timer != -1) /* フロストウェポン解除 */ - skill_status_change_end (bl, SC_FROSTWEAPON, -1); - if (type != SC_LIGHTNINGLOADER && sc_data[SC_LIGHTNINGLOADER].timer != -1) /* ライトニングローダー解除 */ - skill_status_change_end (bl, SC_LIGHTNINGLOADER, -1); - if (type != SC_SEISMICWEAPON && sc_data[SC_SEISMICWEAPON].timer != -1) /* サイスミックウェポン解除 */ - skill_status_change_end (bl, SC_SEISMICWEAPON, -1); - - return 0; -} - -/*========================================== - * ステータス異常開始 - *------------------------------------------ - */ -int skill_status_change_start (struct block_list *bl, int type, int val1, - int val2, int val3, int val4, int tick, - int flag) -{ - return skill_status_effect (bl, type, val1, val2, val3, val4, tick, flag, - 0); -} - -int skill_status_effect (struct block_list *bl, int type, int val1, int val2, - int val3, int val4, int tick, int flag, - int spell_invocation) -{ - 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, race, mode, elem, undead_flag; - int scdef = 0; - - nullpo_retr (0, bl); - if (bl->type == BL_SKILL) - return 0; - nullpo_retr (0, sc_data = battle_get_sc_data (bl)); - nullpo_retr (0, sc_count = battle_get_sc_count (bl)); - nullpo_retr (0, option = battle_get_option (bl)); - nullpo_retr (0, opt1 = battle_get_opt1 (bl)); - nullpo_retr (0, opt2 = battle_get_opt2 (bl)); - nullpo_retr (0, opt3 = battle_get_opt3 (bl)); - - race = battle_get_race (bl); - mode = battle_get_mode (bl); - elem = battle_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; - - switch (type) - { - case SC_STONE: - case SC_FREEZE: - scdef = 3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3; - break; - case SC_STAN: - case SC_SILENCE: - case SC_POISON: - scdef = 3 + battle_get_vit (bl) + battle_get_luk (bl) / 3; - break; - case SC_SLEEP: - case SC_BLIND: - scdef = 3 + battle_get_int (bl) + battle_get_luk (bl) / 3; - break; - case SC_CURSE: - scdef = 3 + battle_get_luk (bl); - break; - -// case SC_CONFUSION: - default: - scdef = 0; - } - if (scdef >= 100) - return 0; - if (bl->type == BL_PC) - { - sd = (struct map_session_data *) bl; - if (sd && type == SC_ADRENALINE - && !(skill_get_weapontype (BS_ADRENALINE) & - (1 << sd->status.weapon))) - return 0; - - if (SC_STONE <= type && type <= SC_BLIND) - { /* カードによる耐性 */ - if (sd && sd->reseff[type - SC_STONE] > 0 - && MRAND (10000) < sd->reseff[type - SC_STONE]) - { - if (battle_config.battle_log) - printf ("PC %d skill_sc_start: cardによる異常耐性発動\n", - sd->bl.id); - return 0; - } - } - } - else if (bl->type == BL_MOB) - { - } - else - { - if (battle_config.error_log) - printf ("skill_status_change_start: neither MOB nor PC !\n"); - return 0; - } - - if (type == SC_FREEZE && undead_flag && !(flag & 1)) - return 0; - - if ((type == SC_ADRENALINE || type == SC_WEAPONPERFECTION - || type == SC_OVERTHRUST) && sc_data[type].timer != -1 - && sc_data[type].val2 && !val2) - return 0; - - if (mode & 0x20 && (type == SC_STONE || type == SC_FREEZE || - type == SC_STAN || type == SC_SLEEP - || type == SC_SILENCE || type == SC_QUAGMIRE - || type == SC_DECREASEAGI || type == SC_SIGNUMCRUCIS - || type == SC_PROVOKE || (type == SC_BLESSING - && (undead_flag - || race == 6))) - && !(flag & 1)) - { - /* ボスには効かない(ただしカードによる効果は適用される) */ - return 0; - } - if (type == SC_FREEZE || type == SC_STAN || type == SC_SLEEP) - battle_stopwalking (bl, 1); - - if (sc_data[type].timer != -1) - { /* すでに同じ異常になっている場合タイマ解除 */ - if (sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION && type != SC_SPEEDPOTION0 && type != SC_SPEEDPOTION1 && type != SC_SPEEDPOTION2 && type != SC_ATKPOT && type != SC_MATKPOT) // added atk and matk potions [Valaris] - return 0; - if (type >= SC_STAN && type <= SC_BLIND) - return 0; /* 継ぎ足しができない状態異常である時は状態異常を行わない */ - if (type == SC_GRAFFITI) - { //異常中にもう一度状態異常になった時に解除してから再度かかる - skill_status_change_end (bl, type, -1); - } - else - { - (*sc_count)--; - delete_timer (sc_data[type].timer, skill_status_change_timer); - sc_data[type].timer = -1; - } - } - - switch (type) - { /* 異常の種類ごとの処理 */ - case SC_PROVOKE: /* プロボック */ - calc_flag = 1; - if (tick <= 0) - tick = 1000; /* (オートバーサーク) */ - break; - case SC_ENDURE: /* インデュア */ - if (tick <= 0) - tick = 1000 * 60; - break; - case SC_CONCENTRATE: /* 集中力向上 */ - calc_flag = 1; - break; - case SC_BLESSING: /* ブレッシング */ - { - if (bl->type == BL_PC || (!undead_flag && race != 6)) - { - if (sc_data[SC_CURSE].timer != -1) - skill_status_change_end (bl, SC_CURSE, -1); - if (sc_data[SC_STONE].timer != -1 - && sc_data[SC_STONE].val2 == 0) - skill_status_change_end (bl, SC_STONE, -1); - } - calc_flag = 1; - } - break; - case SC_ANGELUS: /* アンゼルス */ - calc_flag = 1; - break; - case SC_INCREASEAGI: /* 速度上昇 */ - calc_flag = 1; - if (sc_data[SC_DECREASEAGI].timer != -1) - skill_status_change_end (bl, SC_DECREASEAGI, -1); - if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ - skill_status_change_end (bl, SC_WINDWALK, -1); - break; - case SC_DECREASEAGI: /* 速度減少 */ - calc_flag = 1; - if (sc_data[SC_INCREASEAGI].timer != -1) - skill_status_change_end (bl, SC_INCREASEAGI, -1); - break; - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - calc_flag = 1; -// val2 = 14 + val1; - val2 = 10 + val1 * 2; - tick = 600 * 1000; - clif_emotion (bl, 4); - break; - case SC_SLOWPOISON: - if (sc_data[SC_POISON].timer == -1) - return 0; - break; - case SC_TWOHANDQUICKEN: /* 2HQ */ - *opt3 |= 1; - calc_flag = 1; - break; - case SC_ADRENALINE: /* アドレナリンラッシュ */ - calc_flag = 1; - break; - case SC_WEAPONPERFECTION: /* ウェポンパーフェクション */ - if (battle_config.party_skill_penaly && !val2) - tick /= 5; - break; - case SC_OVERTHRUST: /* オーバースラスト */ - *opt3 |= 2; - if (battle_config.party_skill_penaly && !val2) - tick /= 10; - break; - case SC_MAXIMIZEPOWER: /* マキシマイズパワー(SPが1減る時間,val2にも) */ - if (bl->type == BL_PC) - val2 = tick; - else - tick = 5000 * val1; - break; - case SC_ENCPOISON: /* エンチャントポイズン */ - calc_flag = 1; - val2 = (((val1 - 1) / 2) + 3) * 100; /* 毒付与確率 */ - skill_encchant_eremental_end (bl, SC_ENCPOISON); - break; - case SC_POISONREACT: /* ポイズンリアクト */ - break; - case SC_IMPOSITIO: /* インポシティオマヌス */ - calc_flag = 1; - break; - case SC_ASPERSIO: /* アスペルシオ */ - skill_encchant_eremental_end (bl, SC_ASPERSIO); - break; - case SC_SUFFRAGIUM: /* サフラギム */ - case SC_BENEDICTIO: /* 聖体 */ - case SC_MAGNIFICAT: /* マグニフィカート */ - case SC_AETERNA: /* エーテルナ */ - break; - case SC_ENERGYCOAT: /* エナジーコート */ - *opt3 |= 4; - break; - case SC_MAGICROD: - val2 = val1 * 20; - break; - case SC_KYRIE: /* キリエエレイソン */ - val2 = battle_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) - skill_status_change_end (bl, SC_ASSUMPTIO, -1); - break; - case SC_MINDBREAKER: - calc_flag = 1; - if (tick <= 0) - tick = 1000; /* (オートバーサーク) */ - case SC_GLORIA: /* グロリア */ - calc_flag = 1; - break; - case SC_LOUD: /* ラウドボイス */ - calc_flag = 1; - break; - case SC_TRICKDEAD: /* 死んだふり */ - break; - case SC_QUAGMIRE: /* クァグマイア */ - calc_flag = 1; - if (sc_data[SC_CONCENTRATE].timer != -1) /* 集中力向上解除 */ - skill_status_change_end (bl, SC_CONCENTRATE, -1); - if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ - skill_status_change_end (bl, SC_INCREASEAGI, -1); - if (sc_data[SC_TWOHANDQUICKEN].timer != -1) - skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); - if (sc_data[SC_SPEARSQUICKEN].timer != -1) - skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); - if (sc_data[SC_ADRENALINE].timer != -1) - skill_status_change_end (bl, SC_ADRENALINE, -1); - if (sc_data[SC_LOUD].timer != -1) - skill_status_change_end (bl, SC_LOUD, -1); - if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ - skill_status_change_end (bl, SC_TRUESIGHT, -1); - if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ - skill_status_change_end (bl, SC_WINDWALK, -1); - if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ - skill_status_change_end (bl, SC_CARTBOOST, -1); - break; - case SC_FLAMELAUNCHER: /* フレームランチャー */ - skill_encchant_eremental_end (bl, SC_FLAMELAUNCHER); - break; - case SC_FROSTWEAPON: /* フロストウェポン */ - skill_encchant_eremental_end (bl, SC_FROSTWEAPON); - break; - case SC_LIGHTNINGLOADER: /* ライトニングローダー */ - skill_encchant_eremental_end (bl, SC_LIGHTNINGLOADER); - break; - case SC_SEISMICWEAPON: /* サイズミックウェポン */ - skill_encchant_eremental_end (bl, SC_SEISMICWEAPON); - break; - case SC_DEVOTION: /* ディボーション */ - calc_flag = 1; - break; - case SC_PROVIDENCE: /* プロヴィデンス */ - calc_flag = 1; - val2 = val1 * 5; - break; - case SC_REFLECTSHIELD: - val2 = 10 + val1 * 3; - break; - case SC_STRIPWEAPON: - case SC_STRIPSHIELD: - case SC_STRIPARMOR: - case SC_STRIPHELM: - case SC_CP_WEAPON: - case SC_CP_SHIELD: - case SC_CP_ARMOR: - case SC_CP_HELM: - break; - - case SC_AUTOSPELL: /* オートスペル */ - val4 = 5 + val1 * 2; - break; - - case SC_VOLCANO: - calc_flag = 1; - val3 = val1 * 10; - val4 = - val1 >= 5 ? 20 : (val1 == - 4 ? 19 : (val1 == - 3 ? 17 : (val1 == 2 ? 14 : 10))); - break; - case SC_DELUGE: - calc_flag = 1; - val3 = - val1 >= 5 ? 15 : (val1 == - 4 ? 14 : (val1 == - 3 ? 12 : (val1 == 2 ? 9 : 5))); - val4 = - val1 >= 5 ? 20 : (val1 == - 4 ? 19 : (val1 == - 3 ? 17 : (val1 == 2 ? 14 : 10))); - break; - case SC_VIOLENTGALE: - calc_flag = 1; - val3 = val1 * 3; - val4 = - val1 >= 5 ? 20 : (val1 == - 4 ? 19 : (val1 == - 3 ? 17 : (val1 == 2 ? 14 : 10))); - break; - - case SC_SPEARSQUICKEN: /* スピアクイッケン */ - calc_flag = 1; - val2 = 20 + val1; - *opt3 |= 1; - break; - case SC_COMBO: - break; - case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */ - break; - case SC_BLADESTOP: /* 白刃取り */ - if (val2 == 2) - clif_bladestop ((struct block_list *) val3, - (struct block_list *) val4, 1); - *opt3 |= 32; - break; - - case SC_LULLABY: /* 子守唄 */ - val2 = 11; - break; - case SC_RICHMANKIM: - break; - case SC_ETERNALCHAOS: /* エターナルカオス */ - calc_flag = 1; - break; - case SC_DRUMBATTLE: /* 戦太鼓の響き */ - calc_flag = 1; - val2 = (val1 + 1) * 25; - val3 = (val1 + 1) * 2; - break; - case SC_NIBELUNGEN: /* ニーベルングの指輪 */ - calc_flag = 1; - val2 = (val1 + 2) * 50; - val3 = (val1 + 2) * 25; - break; - case SC_ROKISWEIL: /* ロキの叫び */ - break; - case SC_INTOABYSS: /* 深淵の中に */ - break; - case SC_SIEGFRIED: /* 不死身のジークフリード */ - calc_flag = 1; - val2 = 40 + val1 * 5; - val3 = val1 * 10; - break; - case SC_DISSONANCE: /* 不協和音 */ - val2 = 10; - break; - case SC_WHISTLE: /* 口笛 */ - calc_flag = 1; - break; - case SC_ASSNCROS: /* 夕陽のアサシンクロス */ - calc_flag = 1; - break; - case SC_POEMBRAGI: /* ブラギの詩 */ - break; - case SC_APPLEIDUN: /* イドゥンの林檎 */ - calc_flag = 1; - break; - case SC_UGLYDANCE: /* 自分勝手なダンス */ - val2 = 10; - break; - case SC_HUMMING: /* ハミング */ - calc_flag = 1; - break; - case SC_DONTFORGETME: /* 私を忘れないで */ - calc_flag = 1; - if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ - skill_status_change_end (bl, SC_INCREASEAGI, -1); - if (sc_data[SC_TWOHANDQUICKEN].timer != -1) - skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); - if (sc_data[SC_SPEARSQUICKEN].timer != -1) - skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); - if (sc_data[SC_ADRENALINE].timer != -1) - skill_status_change_end (bl, SC_ADRENALINE, -1); - if (sc_data[SC_ASSNCROS].timer != -1) - skill_status_change_end (bl, SC_ASSNCROS, -1); - if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ - skill_status_change_end (bl, SC_TRUESIGHT, -1); - if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ - skill_status_change_end (bl, SC_WINDWALK, -1); - if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ - skill_status_change_end (bl, SC_CARTBOOST, -1); - break; - case SC_FORTUNE: /* 幸運のキス */ - calc_flag = 1; - break; - case SC_SERVICE4U: /* サービスフォーユー */ - calc_flag = 1; - break; - case SC_DANCING: /* ダンス/演奏中 */ - calc_flag = 1; - val3 = tick / 1000; - tick = 1000; - break; - - case SC_EXPLOSIONSPIRITS: // 爆裂波動 - calc_flag = 1; - val2 = 75 + 25 * val1; - *opt3 |= 8; - break; - case SC_STEELBODY: // 金剛 - calc_flag = 1; - *opt3 |= 16; - break; - case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */ - break; - case SC_AUTOCOUNTER: - val3 = val4 = 0; - break; - - case SC_SPEEDPOTION0: /* 増速ポーション */ - *opt2 |= 0x20; - case SC_SPEEDPOTION1: - case SC_SPEEDPOTION2: - calc_flag = 1; - tick = 1000 * tick; -// val2 = 5*(2+type-SC_SPEEDPOTION0); - break; - - /* atk & matk potions [Valaris] */ - case SC_ATKPOT: - *opt2 |= 0x80; - case SC_MATKPOT: - calc_flag = 1; - tick = 1000 * tick; - break; - case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) - { - time_t timer; - - calc_flag = 1; - tick = 10000; - if (!val2) - val2 = time (&timer); - } - break; - case SC_NOCHAT: //チャット禁止状態 - { - time_t timer; - - if (!battle_config.muting_players) - break; - - tick = 60000; - if (!val2) - val2 = time (&timer); - updateflag = SP_MANNER; - } - break; - case SC_SELFDESTRUCTION: //自爆 - clif_skillcasting (bl, bl->id, bl->id, 0, 0, 331, - skill_get_time (val2, val1)); - val3 = tick / 1000; - tick = 1000; - break; - - /* option1 */ - case SC_STONE: /* 石化 */ - if (!(flag & 2)) - { - int sc_def = battle_get_mdef (bl) * 200; - tick = tick - sc_def; - } - val3 = tick / 1000; - if (val3 < 1) - val3 = 1; - tick = 5000; - val2 = 1; - break; - case SC_SLEEP: /* 睡眠 */ - if (!(flag & 2)) - { -// int sc_def = 100 - (battle_get_int(bl) + battle_get_luk(bl)/3); -// tick = tick * sc_def / 100; -// if(tick < 1000) tick = 1000; - tick = 30000; //睡眠はステータス耐性に関わらず30秒 - } - break; - case SC_FREEZE: /* 凍結 */ - if (!(flag & 2)) - { - int sc_def = 100 - battle_get_mdef (bl); - tick = tick * sc_def / 100; - } - break; - case SC_STAN: /* スタン(val2にミリ秒セット) */ - if (!(flag & 2)) - { - int sc_def = - 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 3); - tick = tick * sc_def / 100; - } - break; - - /* option2 */ - case SC_POISON: /* 毒 */ - calc_flag = 1; - if (!(flag & 2)) - { - int sc_def = - 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 5); - tick = tick * sc_def / 100; - } - val3 = tick / 1000; - if (val3 < 1) - val3 = 1; - tick = 1000; - break; - case SC_SILENCE: /* 沈黙(レックスデビーナ) */ - if (!(flag & 2)) - { - int sc_def = 100 - battle_get_vit (bl); - tick = tick * sc_def / 100; - } - break; - case SC_BLIND: /* 暗黒 */ - calc_flag = 1; - if (!(flag & 2)) - { - int sc_def = - battle_get_lv (bl) / 10 + battle_get_int (bl) / 15; - tick = 30000 - sc_def; - } - break; - case SC_CURSE: - calc_flag = 1; - if (!(flag & 2)) - { - int sc_def = 100 - battle_get_vit (bl); - tick = tick * sc_def / 100; - } - break; - - /* option */ - case SC_HIDING: /* ハイディング */ - calc_flag = 1; - if (bl->type == BL_PC) - { - val2 = tick / 1000; /* 持続時間 */ - tick = 1000; - } - break; - case SC_CHASEWALK: - case SC_CLOAKING: /* クローキング */ - if (bl->type == BL_PC) - val2 = tick; - else - tick = 5000 * val1; - break; - case SC_SIGHT: /* サイト/ルアフ */ - case SC_RUWACH: - val2 = tick / 250; - tick = 10; - break; - - /* セーフティウォール、ニューマ */ - case SC_SAFETYWALL: - case SC_PNEUMA: - tick = ((struct skill_unit *) val2)->group->limit; - break; - - /* アンクル */ - case SC_ANKLE: - break; - - /* ウォーターボール */ - case SC_WATERBALL: - tick = 150; - if (val1 > 5) //レベルが5以上の場合は25発に制限(1発目はすでに打ってるので-1) - val3 = 5 * 5 - 1; - else - val3 = (val1 | 1) * (val1 | 1) - 1; - break; - - /* スキルじゃない/時間に関係しない */ - case SC_RIDING: - calc_flag = 1; - tick = 600 * 1000; - break; - case SC_FALCON: - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_BROKNWEAPON: - case SC_BROKNARMOR: - tick = 600 * 1000; - break; - - case SC_AUTOGUARD: - { - int i, t; - for (i = val2 = 0; i < val1; i++) - { - t = 5 - (i >> 1); - val2 += (t < 0) ? 1 : t; - } - } - break; - - case SC_DEFENDER: - calc_flag = 1; - val2 = 5 + val1 * 15; - break; - - case SC_KEEPING: - case SC_BARRIER: - case SC_HALLUCINATION: - break; - case SC_CONCENTRATION: /* コンセントレーション */ - *opt3 |= 1; - calc_flag = 1; - break; - case SC_TENSIONRELAX: /* テンションリラックス */ - calc_flag = 1; - if (bl->type == BL_PC) - { - tick = 10000; - } - break; - case SC_AURABLADE: /* オーラブレード */ - case SC_PARRYING: /* パリイング */ -// case SC_ASSUMPTIO: /* */ - case SC_HEADCRUSH: /* ヘッドクラッシュ */ - case SC_JOINTBEAT: /* ジョイントビート */ -// case SC_MARIONETTE: /* マリオネットコントロール */ - - //とりあえず手抜き - break; - -// -- moonsoul (for new upper class related skill status effects) -/* - case SC_AURABLADE: - val2 = val1*10; - break; - case SC_PARRYING: - val2=val1*3; - break; - case SC_CONCENTRATION: - calc_flag=1; - val2=val1*10; - val3=val1*5; - break; - case SC_TENSIONRELAX: -// val2 = 10; -// val3 = 15; - break; - case SC_BERSERK: - calc_flag=1; - break; - case SC_ASSUMPTIO: - if(sc_data[SC_KYRIE].timer!=-1 ) - skill_status_change_end(bl,SC_KYRIE,-1); - break; -*/ - case SC_WINDWALK: /* ウインドウォーク */ - calc_flag = 1; - val2 = (val1 / 2); //Flee上昇率 - break; - case SC_BERSERK: /* バーサーク */ - if (sd) - { - sd->status.sp = 0; - clif_updatestatus (sd, SP_SP); - clif_status_change (bl, SC_INCREASEAGI, 1); /* アイコン表示 */ - } - *opt3 |= 128; - tick = 1000; - calc_flag = 1; - break; - case SC_ASSUMPTIO: /* アスムプティオ */ - *opt3 |= 2048; - break; - case SC_MARIONETTE: /* マリオネットコントロール */ - *opt3 |= 1024; - break; - case SC_MELTDOWN: /* メルトダウン */ - case SC_CARTBOOST: /* カートブースト */ - case SC_TRUESIGHT: /* トゥルーサイト */ - case SC_SPIDERWEB: /* スパイダーウェッブ */ - case SC_MAGICPOWER: /* 魔法力増幅 */ - calc_flag = 1; - break; - case SC_REJECTSWORD: /* リジェクトソード */ - val2 = 3; //3回攻撃を跳ね返す - break; - case SC_MEMORIZE: /* メモライズ */ - val2 = 3; //3回詠唱を1/3にする - break; - case SC_GRAFFITI: /* グラフィティ */ - { - struct skill_unit_group *sg = - skill_unitsetting (bl, RG_GRAFFITI, val1, val2, val3, 0); - if (sg) - val4 = (int) sg; - } - break; - case SC_HASTE: - calc_flag = 1; - case SC_SPLASHER: /* ベナムスプラッシャー */ - case SC_PHYS_SHIELD: - case SC_MBARRIER: - case SC_HALT_REGENERATE: - case SC_HIDE: - break; - case SC_FLYING_BACKPACK: - updateflag = SP_WEIGHT; - break; - default: - if (battle_config.error_log) - printf ("UnknownStatusChange [%d]\n", type); - return 0; - } - - if (bl->type == BL_PC && type < SC_SENDMAX) - clif_status_change (bl, type, 1); /* アイコン表示 */ - - /* optionの変更 */ - switch (type) - { - case SC_STONE: - case SC_FREEZE: - case SC_STAN: - case SC_SLEEP: - battle_stopattack (bl); /* 攻撃停止 */ - skill_stop_dancing (bl, 0); /* 演奏/ダンスの中断 */ - { /* 同時に掛からないステータス異常を解除 */ - int i; - for (i = SC_STONE; i <= SC_SLEEP; i++) - { - if (sc_data[i].timer != -1) - { - (*sc_count)--; - delete_timer (sc_data[i].timer, - skill_status_change_timer); - sc_data[i].timer = -1; - } - } - } - if (type == SC_STONE) - *opt1 = 6; - else - *opt1 = type - SC_STONE + 1; - opt_flag = 1; - break; - case SC_POISON: - if (sc_data[SC_SLOWPOISON].timer == -1) - { - *opt2 |= 0x1; - opt_flag = 1; - } - break; - - case SC_CURSE: - case SC_SILENCE: - case SC_BLIND: - *opt2 |= 1 << (type - SC_POISON); - opt_flag = 1; - break; - case SC_SLOWPOISON: - *opt2 &= ~0x1; - *opt2 |= 0x200; - opt_flag = 1; - break; - case SC_SIGNUMCRUCIS: - *opt2 |= 0x40; - opt_flag = 1; - break; - case SC_HIDING: - case SC_CLOAKING: - battle_stopattack (bl); /* 攻撃停止 */ - *option |= ((type == SC_HIDING) ? 2 : 4); - opt_flag = 1; - break; - case SC_CHASEWALK: - battle_stopattack (bl); /* 攻撃停止 */ - *option |= 16388; - opt_flag = 1; - break; - case SC_SIGHT: - *option |= 1; - opt_flag = 1; - break; - case SC_RUWACH: - *option |= 8192; - opt_flag = 1; - break; - case SC_WEDDING: - *option |= 4096; - opt_flag = 1; - } - - 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; - if (sc_data[type].spell_invocation) // Supplant by newer spell - spell_effect_report_termination (sc_data[type].spell_invocation, - bl->id, type, 1); - - sc_data[type].spell_invocation = spell_invocation; - - /* タイマー設定 */ - sc_data[type].timer = - add_timer (gettick () + tick, skill_status_change_timer, bl->id, - type); - - if (bl->type == BL_PC && calc_flag) - pc_calcstatus (sd, 0); /* ステータス再計算 */ - - if (bl->type == BL_PC && updateflag) - clif_updatestatus (sd, updateflag); /* ステータスをクライアントに送る */ - - return 0; -} - -/*========================================== - * ステータス異常全解除 - *------------------------------------------ - */ -int skill_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 = battle_get_sc_data (bl)); - nullpo_retr (0, sc_count = battle_get_sc_count (bl)); - nullpo_retr (0, option = battle_get_option (bl)); - nullpo_retr (0, opt1 = battle_get_opt1 (bl)); - nullpo_retr (0, opt2 = battle_get_opt2 (bl)); - nullpo_retr (0, opt3 = battle_get_opt3 (bl)); - - if (*sc_count == 0) - return 0; - for (i = 0; i < MAX_STATUSCHANGE; i++) - { - if (sc_data[i].timer != -1) - { /* 異常があるならタイマーを削除する */ -/* - delete_timer(sc_data[i].timer, skill_status_change_timer); - sc_data[i].timer = -1; - - if (!type && i < SC_SENDMAX) - clif_status_change(bl, i, 0); -*/ - - skill_status_change_end (bl, i, -1); - } - } - *sc_count = 0; - *opt1 = 0; - *opt2 = 0; - *opt3 = 0; - *option &= OPTION_MASK; - - if (night_flag == 1 && type == BL_PC) // by [Yor] - *opt2 |= STATE_BLIND; - - if (!type || type & 2) - clif_changeoption (bl); - - return 0; -} - -/* クローキング検査(周りに移動不可能地帯があるか) */ -int skill_check_cloaking (struct block_list *bl) -{ - struct map_session_data *sd = NULL; - static int dx[] = { -1, 0, 1, -1, 1, -1, 0, 1 }; - static int dy[] = { -1, -1, -1, 0, 0, 1, 1, 1 }; - int end = 1, i; - - nullpo_retr (0, bl); - - if (pc_checkskill (sd, AS_CLOAKING) > 2) - return 0; - if (bl->type == BL_PC && battle_config.pc_cloak_check_type & 1) - return 0; - if (bl->type == BL_MOB && battle_config.monster_cloak_check_type & 1) - return 0; - for (i = 0; i < sizeof (dx) / sizeof (dx[0]); i++) - { - int c = map_getcell (bl->m, bl->x + dx[i], bl->y + dy[i]); - if (c == 1 || c == 5) - end = 0; - } - if (end) - { - skill_status_change_end (bl, SC_CLOAKING, -1); - *battle_get_option (bl) &= ~4; /* 念のための処理 */ - } - return end; -} - -/* - *---------------------------------------------------------------------------- - * スキルユニット - *---------------------------------------------------------------------------- - */ - -/*========================================== - * 演奏/ダンススキルかどうか判定 - * 引数 スキルID - * 戻り ダンスじゃない=0 合奏=2 それ以外のダンス=1 - *------------------------------------------ - */ -int skill_is_danceskill (int id) -{ - int i; - switch (id) - { - case BD_LULLABY: /* 子守歌 */ - case BD_RICHMANKIM: /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: /* 永遠の混沌 */ - case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ - case BD_ROKISWEIL: /* ロキの叫び */ - case BD_INTOABYSS: /* 深淵の中に */ - case BD_SIEGFRIED: /* 不死身のジークフリード */ - case BD_RAGNAROK: /* 神々の黄昏 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - i = 2; - break; - case BA_DISSONANCE: /* 不協和音 */ - case BA_FROSTJOKE: /* 寒いジョーク */ - case BA_WHISTLE: /* 口笛 */ - case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ - case BA_POEMBRAGI: /* ブラギの詩 */ - case BA_APPLEIDUN: /* イドゥンの林檎 */ - case DC_UGLYDANCE: /* 自分勝手なダンス */ - case DC_SCREAM: /* スクリーム */ - case DC_HUMMING: /* ハミング */ - case DC_DONTFORGETME: /* 私を忘れないで… */ - case DC_FORTUNEKISS: /* 幸運のキス */ - case DC_SERVICEFORYOU: /* サービスフォーユー */ - i = 1; - break; - default: - i = 0; - } - return i; -} - -/*========================================== - * 演奏/ダンスをやめる - * flag 1で合奏中なら相方にユニットを任せる - * - *------------------------------------------ - */ -void skill_stop_dancing (struct block_list *src, int flag) -{ - struct status_change *sc_data; - struct skill_unit_group *group; - - nullpo_retv (src); - - sc_data = battle_get_sc_data (src); - if (sc_data && sc_data[SC_DANCING].timer == -1) - return; - group = (struct skill_unit_group *) sc_data[SC_DANCING].val2; //ダンスのスキルユニットIDはval2に入ってる - if (group && src->type == BL_PC && sc_data && sc_data[SC_DANCING].val4) - { //合奏中断 - struct map_session_data *dsd = map_id2sd (sc_data[SC_DANCING].val4); //相方のsd取得 - if (flag) - { //ログアウトなど片方が落ちても演奏が継続される - if (dsd && src->id == group->src_id) - { //グループを持ってるPCが落ちる - group->src_id = sc_data[SC_DANCING].val4; //相方にグループを任せる - if (flag & 1) //ログアウト - dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 - if (flag & 2) //ハエ飛びなど - return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり - } - else if (dsd && dsd->bl.id == group->src_id) - { //相方がグループを持っているPCが落ちる(自分はグループを持っていない) - if (flag & 1) //ログアウト - dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 - if (flag & 2) //ハエ飛びなど - return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり - } - skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる - //そしてグループは消さない&消さないのでステータス計算もいらない? - return; - } - else - { - if (dsd && src->id == group->src_id) - { //グループを持ってるPCが止める - skill_status_change_end ((struct block_list *) dsd, SC_DANCING, -1); //相手のステータスを終了させる - } - if (dsd && dsd->bl.id == group->src_id) - { //相方がグループを持っているPCが止める(自分はグループを持っていない) - skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる - } - } - } - if (flag & 2 && group && src->type == BL_PC) - { //ハエで飛んだときとかはユニットも飛ぶ - struct map_session_data *sd = (struct map_session_data *) src; - skill_unit_move_unit_group (group, sd->bl.m, (sd->to_x - sd->bl.x), - (sd->to_y - sd->bl.y)); - return; - } - skill_delunitgroup (group); - if (src->type == BL_PC) - pc_calcstatus ((struct map_session_data *) src, 0); -} - -/*========================================== - * スキルユニット初期化 - *------------------------------------------ - */ -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); - return unit; -} - -int skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap); -/*========================================== - * スキルユニット削除 - *------------------------------------------ - */ -int skill_delunit (struct skill_unit *unit) -{ - struct skill_unit_group *group; - int range; - - nullpo_retr (0, unit); - if (!unit->alive) - return 0; - nullpo_retr (0, group = unit->group); - - /* onlimitイベント呼び出し */ - skill_unit_onlimit (unit, gettick ()); - - /* ondeleteイベント呼び出し */ - range = group->range; - map_foreachinarea (skill_unit_timer_sub_ondelete, unit->bl.m, - unit->bl.x - range, unit->bl.y - range, - unit->bl.x + range, unit->bl.y + range, 0, &unit->bl, - gettick ()); - - clif_skill_delunit (unit); - - unit->group = NULL; - unit->alive = 0; - map_delobjectnofree (unit->bl.id, BL_SKILL); - if (group->alive_count > 0 && (--group->alive_count) <= 0) - skill_delunitgroup (group); - - return 0; -} - -/*========================================== - * スキルユニットグループ初期化 - *------------------------------------------ - */ -static int skill_unit_group_newid = 10; -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; - - 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; - } - 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) - { - printf ("skill_initunitgroup: error unit group !\n"); - exit (1); - } - - group->src_id = src->id; - group->party_id = battle_get_party_id (src); - group->guild_id = battle_get_guild_id (src); - group->group_id = skill_unit_group_newid++; - if (skill_unit_group_newid <= 0) - skill_unit_group_newid = 10; - CREATE (group->unit, struct skill_unit, count); - 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->range = 0; - group->limit = 10000; - group->interval = 1000; - group->tick = gettick (); - group->valstr = NULL; - - if (skill_is_danceskill (skillid)) - { - 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; - } - skill_status_change_start (src, SC_DANCING, skillid, (int) group, 0, - 0, skill_get_time (skillid, - skilllv) + 1000, 0); - switch (skillid) - { //合奏スキルは相方をダンス状態にする - case BD_LULLABY: /* 子守歌 */ - case BD_RICHMANKIM: /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: /* 永遠の混沌 */ - case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ - case BD_ROKISWEIL: /* ロキの叫び */ - case BD_INTOABYSS: /* 深淵の中に */ - case BD_SIEGFRIED: /* 不死身のジークフリード */ - case BD_RAGNAROK: /* 神々の黄昏 */ - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - { - int range = 1; - int c = 0; - if (sd) - { - map_foreachinarea (skill_check_condition_use_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); - } - } - } - } - 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 (skill_is_danceskill (group->skill_id)) - { //ダンススキルはダンス状態を解除する - if (src) - skill_status_change_end (src, SC_DANCING, -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) - { - map_freeblock (group->valstr); - group->valstr = NULL; - } - - map_freeblock (group->unit); /* free()の替わり */ - 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; - } - 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, - int group_id) -{ - int i, j = 0, k, s = group_id % MAX_SKILLUNITGROUPTICKSET; - struct skill_unit_group_tickset *set = NULL; - - nullpo_retr (0, bl); - - if (bl->type == BL_PC) - { - set = ((struct map_session_data *) bl)->skillunittick; - } - else - { - set = ((struct mob_data *) bl)->skillunittick; - } - if (set == NULL) - return 0; - for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) - if (set[(k = (i + s) % MAX_SKILLUNITGROUPTICKSET)].group_id == - group_id) - return &set[k]; - else if (set[k].group_id == 0) - j = k; - - return &set[j]; -} - -/*========================================== - * スキルユニットグループの被影響tick削除 - *------------------------------------------ - */ -int skill_unitgrouptickset_delete (struct block_list *bl, int group_id) -{ - int i, s = group_id % MAX_SKILLUNITGROUPTICKSET; - struct skill_unit_group_tickset *set = NULL, *ts; - - nullpo_retr (0, bl); - - if (bl->type == BL_PC) - { - set = ((struct map_session_data *) bl)->skillunittick; - } - else - { - set = ((struct mob_data *) bl)->skillunittick; - } - - if (set != NULL) - { - - for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) - if ((ts = - &set[(i + s) % MAX_SKILLUNITGROUPTICKSET])->group_id == - group_id) - ts->group_id = 0; - - } - return 0; -} - -/*========================================== - * スキルユニットタイマー発動処理用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_timer_sub_onplace (struct block_list *bl, va_list ap) -{ - struct block_list *src; - struct skill_unit *su; - unsigned int tick; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - src = va_arg (ap, struct block_list *); - - tick = va_arg (ap, unsigned int); - su = (struct skill_unit *) src; - - if (su && su->alive) - { - struct skill_unit_group *sg; - sg = su->group; - if (sg && battle_check_target (src, bl, sg->target_flag) > 0) - skill_unit_onplace (su, bl, tick); - } - return 0; -} - -/*========================================== - * スキルユニットタイマー削除処理用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap) -{ - struct block_list *src; - struct skill_unit *su; - unsigned int tick; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - src = va_arg (ap, struct block_list *); - - tick = va_arg (ap, unsigned int); - su = (struct skill_unit *) src; - - if (su && su->alive) - { - struct skill_unit_group *sg; - sg = su->group; - if (sg && battle_check_target (src, bl, sg->target_flag) > 0) - skill_unit_ondelete (su, 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); - nullpo_retr (0, group = unit->group); - tick = va_arg (ap, unsigned int); - - if (!unit->alive) - return 0; - - range = (unit->range != 0) ? unit->range : group->range; - - /* onplaceイベント呼び出し */ - if (unit->alive && unit->range >= 0) - { - map_foreachinarea (skill_unit_timer_sub_onplace, bl->m, - bl->x - range, bl->y - range, bl->x + range, - bl->y + range, 0, bl, tick); - if (group->unit_id == 0xaa - && DIFF_TICK (tick, group->tick) >= 6000 * group->val2) - { - map_foreachinarea (skill_idun_heal, bl->m, - bl->x - range, bl->y - range, bl->x + range, - bl->y + range, 0, unit); - group->val2++; - } - } - /* 時間切れ削除 */ - if (unit->alive && - (DIFF_TICK (tick, group->tick) >= group->limit - || DIFF_TICK (tick, group->tick) >= unit->limit)) - { - switch (group->unit_id) - { - - case 0x8f: /* ブラストマイン */ - group->unit_id = 0x8c; - 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 0x90: /* スキッドトラップ */ - case 0x91: /* アンクルスネア */ - case 0x93: /* ランドマイン */ - case 0x94: /* ショックウェーブトラップ */ - case 0x95: /* サンドマン */ - case 0x96: /* フラッシャー */ - case 0x97: /* フリージングトラップ */ - case 0x98: /* クレイモアートラップ */ - case 0x99: /* トーキーボックス */ - { - struct block_list *src = map_id2bl (group->src_id); - if (group->unit_id == 0x91 && group->val2); - else - { - if (src && src->type == BL_PC) - { - 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); // 罠返還 - } - } - } - default: - skill_delunit (unit); - } - } - - if (group->unit_id == 0x8d) - { - unit->val1 -= 5; - if (unit->val1 <= 0 && unit->limit + group->tick > tick + 700) - unit->limit = DIFF_TICK (tick + 700, group->tick); - } - - return 0; -} - -/*========================================== - * スキルユニットタイマー処理 - *------------------------------------------ - */ -void skill_unit_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) -{ - map_freeblock_lock (); - - map_foreachobject (skill_unit_timer_sub, BL_SKILL, tick); - - map_freeblock_unlock (); -} - -/*========================================== - * スキルユニット移動時処理用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_out_all_sub (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - struct block_list *src; - int range; - unsigned int tick; - - 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 *) bl); - nullpo_retr (0, group = unit->group); - - tick = va_arg (ap, unsigned int); - - if (!unit->alive || src->prev == NULL) - return 0; - - range = (unit->range != 0) ? unit->range : group->range; - - if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) - return 0; - - if (src->x >= bl->x - range && src->x <= bl->x + range && - src->y >= bl->y - range && src->y <= bl->y + range) - skill_unit_onout (unit, src, tick); - - return 0; -} - -/*========================================== - * スキルユニット移動時処理 - *------------------------------------------ - */ -int skill_unit_out_all (struct block_list *bl, unsigned int tick, int range) -{ - nullpo_retr (0, bl); - - if (bl->prev == NULL) - return 0; - - if (range < 7) - range = 7; - map_foreachinarea (skill_unit_out_all_sub, - bl->m, bl->x - range, bl->y - range, bl->x + range, - bl->y + range, BL_SKILL, bl, tick); - - return 0; -} - -/*========================================== - * スキルユニット移動時処理用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_move_sub (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - struct block_list *src; - int range; - unsigned int tick; - - nullpo_retr (0, bl); - nullpo_retr (0, ap); - nullpo_retr (0, unit = (struct skill_unit *) bl); - nullpo_retr (0, src = va_arg (ap, struct block_list *)); - - tick = va_arg (ap, unsigned int); - - if (!unit->alive || src->prev == NULL) - return 0; - - if ((group = unit->group) == NULL) - return 0; - range = (unit->range != 0) ? unit->range : group->range; - - if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) - return 0; - - if (src->x >= bl->x - range && src->x <= bl->x + range && - src->y >= bl->y - range && src->y <= bl->y + range) - skill_unit_onplace (unit, src, tick); - else - skill_unit_onout (unit, src, tick); - - return 0; -} - -/*========================================== - * スキルユニット移動時処理 - *------------------------------------------ - */ -int skill_unit_move (struct block_list *bl, unsigned int tick, int range) -{ - nullpo_retr (0, bl); - - if (bl->prev == NULL) - return 0; - - if (range < 7) - range = 7; - map_foreachinarea (skill_unit_move_sub, - bl->m, bl->x - range, bl->y - range, bl->x + range, - bl->y + range, BL_SKILL, bl, tick); - - return 0; -} - -/*========================================== - * スキルユニット自体の移動時処理(foreachinarea) - *------------------------------------------ - */ -int skill_unit_move_unit_group_sub (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - struct block_list *src; - int range; - unsigned int tick; - - 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, group = unit->group); - - tick = va_arg (ap, unsigned int); - - if (!unit->alive || bl->prev == NULL) - return 0; - - range = (unit->range != 0) ? unit->range : group->range; - - if (range < 0 || battle_check_target (src, bl, group->target_flag) <= 0) - return 0; - if (bl->x >= src->x - range && bl->x <= src->x + range && - bl->y >= src->y - range && bl->y <= src->y + range) - skill_unit_onplace (unit, bl, tick); - else - skill_unit_onout (unit, bl, tick); - return 0; -} - -/*========================================== - * スキルユニット自体の移動時処理 - * 引数はグループと移動量 - *------------------------------------------ - */ -int skill_unit_move_unit_group (struct skill_unit_group *group, int m, int dx, - int dy) -{ - nullpo_retr (0, group); - - if (group->unit_count <= 0) - return 0; - - if (group->unit != NULL) - { - if (!battle_config.unit_movement_type) - { - int i; - for (i = 0; i < group->unit_count; i++) - { - struct skill_unit *unit = &group->unit[i]; - if (unit->alive && !(m == unit->bl.m && dx == 0 && dy == 0)) - { - int range = unit->range; - map_delblock (&unit->bl); - unit->bl.m = m; - unit->bl.x += dx; - unit->bl.y += dy; - map_addblock (&unit->bl); - clif_skill_setunit (unit); - if (range > 0) - { - if (range < 7) - range = 7; - map_foreachinarea (skill_unit_move_unit_group_sub, - unit->bl.m, unit->bl.x - range, - unit->bl.y - range, - unit->bl.x + range, - unit->bl.y + range, 0, &unit->bl, - gettick ()); - } - } - } - } - else - { - int i, j, *r_flag, *s_flag, *m_flag; - struct skill_unit *unit1; - struct skill_unit *unit2; - r_flag = (int *) malloc (sizeof (int) * group->unit_count); - s_flag = (int *) malloc (sizeof (int) * group->unit_count); - m_flag = (int *) malloc (sizeof (int) * group->unit_count); - memset (r_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ - memset (s_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ - memset (m_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ - - //先にフラグを全部決める - for (i = 0; i < group->unit_count; i++) - { - int move_check = 0; // かぶりフラグ - unit1 = &group->unit[i]; - for (j = 0; j < group->unit_count; j++) - { - unit2 = &group->unit[j]; - if (unit1->bl.m == m && unit1->bl.x + dx == unit2->bl.x - && unit1->bl.y + dy == unit2->bl.y) - { - //移動先にユニットがかぶってたら - s_flag[i] = 1; // 移動前のユニットナンバーの継承フラグon - r_flag[j] = 1; // かぶるユニットナンバーの残留フラグon - move_check = 1; //ユニットがかぶった。 - break; - } - } - if (!move_check) // ユニットがかぶってなかったら - m_flag[i] = 1; // 移動前ユニットナンバーの移動フラグon - } - - //フラグに基づいてユニット移動 - for (i = 0; i < group->unit_count; i++) - { - unit1 = &group->unit[i]; - if (m_flag[i]) - { // 移動フラグがonで - if (!r_flag[i]) - { // 残留フラグがoffなら - //単純移動(rangeも継承の必要無し) - int range = unit1->range; - map_delblock (&unit1->bl); - unit1->bl.m = m; - unit1->bl.x += dx; - unit1->bl.y += dy; - map_addblock (&unit1->bl); - clif_skill_setunit (unit1); - if (range > 0) - { - if (range < 7) - range = 7; - map_foreachinarea (skill_unit_move_unit_group_sub, - unit1->bl.m, - unit1->bl.x - range, - unit1->bl.y - range, - unit1->bl.x + range, - unit1->bl.y + range, 0, - &unit1->bl, gettick ()); - } - } - else - { // 残留フラグがonなら - //空ユニットになるので、継承可能なユニットを探す - for (j = 0; j < group->unit_count; j++) - { - unit2 = &group->unit[j]; - if (s_flag[j] && !r_flag[j]) - { - // 継承移動(range継承付き) - int range = unit1->range; - map_delblock (&unit2->bl); - unit2->bl.m = m; - unit2->bl.x = unit1->bl.x + dx; - unit2->bl.y = unit1->bl.y + dy; - unit2->range = unit1->range; - map_addblock (&unit2->bl); - clif_skill_setunit (unit2); - if (range > 0) - { - if (range < 7) - range = 7; - map_foreachinarea - (skill_unit_move_unit_group_sub, - unit2->bl.m, unit2->bl.x - range, - unit2->bl.y - range, - unit2->bl.x + range, - unit2->bl.y + range, 0, &unit2->bl, - gettick ()); - } - s_flag[j] = 0; // 継承完了したのでoff - break; - } - } - } - } - } - free (r_flag); - free (s_flag); - free (m_flag); - } - } - return 0; -} - -/*---------------------------------------------------------------------------- - * アイテム合成 - *---------------------------------------------------------------------------- - */ - -/*---------------------------------------------------------------------------- - * 初期化系 - */ - -static int scan_stat (char *statname) -{ - if (!strcasecmp (statname, "str")) - return SP_STR; - if (!strcasecmp (statname, "dex")) - return SP_DEX; - if (!strcasecmp (statname, "agi")) - return SP_AGI; - if (!strcasecmp (statname, "vit")) - return SP_VIT; - if (!strcasecmp (statname, "int")) - return SP_INT; - if (!strcasecmp (statname, "luk")) - return SP_LUK; - if (!strcasecmp (statname, "none")) - return 0; - - else - fprintf (stderr, "Unknown stat `%s'\n", statname); - return 0; -} - -extern void skill_pool_register (int id); // [Fate] Remember that a certain skill ID belongs to a pool skill - -/*========================================== - * スキル関係ファイル読み込み - * skill_db.txt スキルデータ - * skill_cast_db.txt スキルの詠唱時間とディレイデータ - *------------------------------------------ - */ -int skill_readdb (void) -{ - int i, j, k, l; - FILE *fp; - char line[1024], *p; - - /* The main skill database */ - memset (skill_db, 0, sizeof (skill_db)); - fp = fopen_ ("db/skill_db.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/skill_db.txt\n"); - return 1; - } - while (fgets (line, 1020, fp)) - { - char *split[50], *split2[MAX_SKILL_LEVEL]; - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 18 && p; j++) - { - while (*p == '\t' || *p == ' ') - p++; - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - if (split[17] == NULL || j < 18) - { - fprintf (stderr, "Incomplete skill db data online (%d entries)\n", - j); - continue; - } - - i = atoi (split[0]); - if (i < 0 || i > MAX_SKILL_DB) - continue; - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].range[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - 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_raise = atoi (split[6]); - skill_db[i].max = atoi (split[7]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[8]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].num[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - if (strcasecmp (split[9], "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 (strcasecmp (split[13], "weapon") == 0) - skill_db[i].skill_type = BF_WEAPON; - else if (strcasecmp (split[12], "magic") == 0) - skill_db[i].skill_type = BF_MAGIC; - else if (strcasecmp (split[12], "misc") == 0) - skill_db[i].skill_type = BF_MISC; - else - skill_db[i].skill_type = 0; - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[14]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].blewcount[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - if (!strcasecmp (split[15], "passive")) - { - skill_pool_register (i); - skill_db[i].poolflags = SKILL_POOL_FLAG; - } - else if (!strcasecmp (split[15], "active")) - { - skill_pool_register (i); - skill_db[i].poolflags = SKILL_POOL_FLAG | SKILL_POOL_ACTIVE; - } - else - skill_db[i].poolflags = 0; - - skill_db[i].stat = scan_stat (split[16]); - - skill_names[i].desc = strdup (split[17]); - { // replace "_" by " " - char *s = skill_names[i].desc; - while ((s = strchr (s, '_'))) - *s = ' '; - if ((s = strchr (skill_names[i].desc, '\t')) - || (s = strchr (skill_names[i].desc, ' ')) - || (s = strchr (skill_names[i].desc, '\n'))) - *s = '\000'; - } - } - fclose_ (fp); - printf ("read db/skill_db.txt done\n"); - - fp = fopen_ ("db/skill_require_db.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/skill_require_db.txt\n"); - return 1; - } - while (fgets (line, 1020, fp)) - { - char *split[51], *split2[MAX_SKILL_LEVEL]; - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 30 && p; j++) - { - while (*p == '\t' || *p == ' ') - p++; - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - if (split[29] == NULL || j < 30) - continue; - - i = atoi (split[0]); - if (i < 0 || i > MAX_SKILL_DB) - continue; - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].hp[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].mhp[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].sp[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].hp_rate[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[5]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].sp_rate[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[6]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].zeny[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[7]; j < 32 && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < 32 && split2[k]; k++) - { - l = atoi (split2[k]); - if (l == 99) - { - skill_db[i].weapon = 0xffffffff; - break; - } - else - skill_db[i].weapon |= 1 << l; - } - - if (strcasecmp (split[8], "hiding") == 0) - skill_db[i].state = ST_HIDING; - else if (strcasecmp (split[8], "cloaking") == 0) - skill_db[i].state = ST_CLOAKING; - else if (strcasecmp (split[8], "hidden") == 0) - skill_db[i].state = ST_HIDDEN; - else if (strcasecmp (split[8], "riding") == 0) - skill_db[i].state = ST_RIDING; - else if (strcasecmp (split[8], "falcon") == 0) - skill_db[i].state = ST_FALCON; - else if (strcasecmp (split[8], "cart") == 0) - skill_db[i].state = ST_CART; - else if (strcasecmp (split[8], "shield") == 0) - skill_db[i].state = ST_SHIELD; - else if (strcasecmp (split[8], "sight") == 0) - skill_db[i].state = ST_SIGHT; - else if (strcasecmp (split[8], "explosionspirits") == 0) - skill_db[i].state = ST_EXPLOSIONSPIRITS; - else if (strcasecmp (split[8], "recover_weight_rate") == 0) - skill_db[i].state = ST_RECOV_WEIGHT_RATE; - else if (strcasecmp (split[8], "move_enable") == 0) - skill_db[i].state = ST_MOVE_ENABLE; - else if (strcasecmp (split[8], "water") == 0) - skill_db[i].state = ST_WATER; - else - skill_db[i].state = ST_NONE; - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[9]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].spiritball[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - 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); - printf ("read db/skill_require_db.txt done\n"); - - /* ? */ - fp = fopen_ ("db/skill_cast_db.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/skill_cast_db.txt\n"); - return 1; - } - while (fgets (line, 1020, fp)) - { - char *split[50], *split2[MAX_SKILL_LEVEL]; - memset (split, 0, sizeof (split)); // [Valaris] thanks to fov - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 5 && p; j++) - { - while (*p == '\t' || *p == ' ') - p++; - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - if (split[4] == NULL || j < 5) - continue; - - i = atoi (split[0]); - if (i < 0 || i > MAX_SKILL_DB) - continue; - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].cast[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].delay[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].upkeep_time[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].upkeep_time2[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - } - fclose_ (fp); - printf ("read db/skill_cast_db.txt done\n"); - - fp = fopen_ ("db/skill_castnodex_db.txt", "r"); - if (fp == NULL) - { - printf ("can't read db/skill_castnodex_db.txt\n"); - return 1; - } - while (fgets (line, 1020, fp)) - { - char *split[50], *split2[MAX_SKILL_LEVEL]; - memset (split, 0, sizeof (split)); - if (line[0] == '/' && line[1] == '/') - continue; - for (j = 0, p = line; j < 2 && p; j++) - { - while (*p == '\t' || *p == ' ') - p++; - split[j] = p; - p = strchr (p, ','); - if (p) - *p++ = 0; - } - - i = atoi (split[0]); - if (i < 0 || i > MAX_SKILL_DB) - continue; - - memset (split2, 0, sizeof (split2)); - for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) - { - split2[j] = p; - p = strchr (p, ':'); - if (p) - *p++ = 0; - } - for (k = 0; k < MAX_SKILL_LEVEL; k++) - skill_db[i].castnodex[k] = - (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); - } - fclose_ (fp); - printf ("read db/skill_castnodex_db.txt done\n"); - - return 0; -} - -void skill_reload (void) -{ - /* - * - * <empty skill database> - * <?> - * - */ - - do_init_skill (); -} - -/*========================================== - * スキル関係初期化処理 - *------------------------------------------ - */ -int do_init_skill (void) -{ - skill_readdb (); - - add_timer_interval (gettick () + SKILLUNITTIMER_INVERVAL, - skill_unit_timer, 0, 0, SKILLUNITTIMER_INVERVAL); - - return 0; -} diff --git a/src/map/skill.cpp b/src/map/skill.cpp new file mode 100644 index 0000000..779af98 --- /dev/null +++ b/src/map/skill.cpp @@ -0,0 +1,12279 @@ +// $Id: skill.c,v 1.8 2004/09/25 05:32:19 MouseJstr Exp $ +/* スキル関係 */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "../common/timer.hpp" +#include "../common/nullpo.hpp" +#include "../common/mt_rand.hpp" +#include "magic.hpp" + +#include "battle.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "../common/socket.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define SKILLUNITTIMER_INVERVAL 100 + +#define STATE_BLIND 0x10 + +/* スキル番号=>ステータス異常番号変換テーブル */ +int SkillStatusChangeTable[] = { /* skill.hのenumのSC_***とあわせること */ +/* 0- */ + -1, -1, -1, -1, -1, -1, + SC_PROVOKE, /* プロボック */ + -1, 1, -1, +/* 10- */ + SC_SIGHT, /* サイト */ + -1, -1, -1, -1, + SC_FREEZE, /* フロストダイバー */ + SC_STONE, /* ストーンカース */ + -1, -1, -1, +/* 20- */ + -1, -1, -1, -1, + SC_RUWACH, /* ルアフ */ + -1, -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_DIVINA, /* レックスディビーナ */ + -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, -1, -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, + SC_SELFDESTRUCTION, + -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, + SC_KEEPING, + -1, -1, + SC_BARRIER, + -1, -1, + 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_FLAMELAUNCHER, + SC_FROSTWEAPON, + SC_LIGHTNINGLOADER, + SC_SEISMICWEAPON, + -1, + SC_VOLCANO, + SC_DELUGE, + SC_VIOLENTGALE, + SC_LANDPROTECTOR, + -1, +/* 290- */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* 300- */ + -1, -1, -1, -1, -1, -1, + SC_LULLABY, + SC_RICHMANKIM, + SC_ETERNALCHAOS, + SC_DRUMBATTLE, +/* 310- */ + SC_NIBELUNGEN, + SC_ROKISWEIL, + SC_INTOABYSS, + SC_SIEGFRIED, + -1, -1, -1, + SC_DISSONANCE, + -1, + SC_WHISTLE, +/* 320- */ + SC_ASSNCROS, + SC_POEMBRAGI, + SC_APPLEIDUN, + -1, -1, + SC_UGLYDANCE, + -1, + SC_HUMMING, + SC_DONTFORGETME, + SC_FORTUNE, +/* 330- */ + SC_SERVICE4U, + SC_SELFDESTRUCTION, + -1, -1, -1, -1, -1, -1, -1, -1, +/* 340- */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* 350- */ + -1, -1, -1, -1, -1, + SC_AURABLADE, + SC_PARRYING, + SC_CONCENTRATION, + SC_TENSIONRELAX, + SC_BERSERK, +/* 360- */ + SC_BERSERK, + SC_ASSUMPTIO, + SC_BASILICA, + -1, -1, -1, + SC_MAGICPOWER, + -1, -1, + SC_GOSPEL, +/* 370- */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* 380- */ + SC_TRUESIGHT, + -1, -1, + SC_WINDWALK, + SC_MELTDOWN, + -1, -1, + SC_CARTBOOST, + -1, + SC_CHASEWALK, +/* 390- */ + SC_REJECTSWORD, + -1, -1, -1, -1, -1, + SC_MARIONETTE, + -1, + SC_HEADCRUSH, + SC_JOINTBEAT, +/* 400 */ + -1, -1, + SC_MINDBREAKER, + SC_MEMORIZE, + SC_FOGWALL, + SC_SPIDERWEB, + -1, -1, -1, -1, +/* 410- */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +struct skill_name_db skill_names[] = { + {AC_CHARGEARROW, "CHARGEARROW", "Charge_Arrow"}, + {AC_CONCENTRATION, "CONCENTRATION", "Improve_Concentration"}, + {AC_DOUBLE, "DOUBLE", "Double_Strafe"}, + {AC_MAKINGARROW, "MAKINGARROW", "Arrow_Creation"}, + {AC_OWL, "OWL", "Owl's_Eye"}, + {AC_SHOWER, "SHOWER", "Arrow_Shower"}, + {AC_VULTURE, "VULTURE", "Vulture's_Eye"}, + {ALL_RESURRECTION, "RESURRECTION", "Resurrection"}, + {AL_ANGELUS, "ANGELUS", "Angelus"}, + {AL_BLESSING, "BLESSING", "Blessing"}, + {AL_CRUCIS, "CRUCIS", "Signum_Crusis"}, + {AL_CURE, "CURE", "Cure"}, + {AL_DECAGI, "DECAGI", "Decrease_AGI"}, + {AL_DEMONBANE, "DEMONBANE", "Demon_Bane"}, + {AL_DP, "DP", "Divine_Protection"}, + {AL_HEAL, "HEAL", "Heal"}, + {AL_HOLYLIGHT, "HOLYLIGHT", "Holy_Light"}, + {AL_HOLYWATER, "HOLYWATER", "Aqua_Benedicta"}, + {AL_INCAGI, "INCAGI", "Increase_AGI"}, + {AL_PNEUMA, "PNEUMA", "Pneuma"}, + {AL_RUWACH, "RUWACH", "Ruwach"}, + {AL_TELEPORT, "TELEPORT", "Teleport"}, + {AL_WARP, "WARP", "Warp_Portal"}, + {AM_ACIDTERROR, "ACIDTERROR", "Acid_Terror"}, + {AM_AXEMASTERY, "AXEMASTERY", "Axe_Mastery"}, + {AM_BERSERKPITCHER, "BERSERKPITCHER", "Berserk Pitcher"}, + {AM_BIOETHICS, "BIOETHICS", "Bioethics"}, + {AM_BIOTECHNOLOGY, "BIOTECHNOLOGY", "Biotechnology"}, + {AM_CALLHOMUN, "CALLHOMUN", "Call_Homunculus"}, + {AM_CANNIBALIZE, "CANNIBALIZE", "Bio_Cannibalize"}, + {AM_CP_ARMOR, "ARMOR", "Chemical_Protection_Armor"}, + {AM_CP_HELM, "HELM", "Chemical_Protection_Helm"}, + {AM_CP_SHIELD, "SHIELD", "Chemical_Protection_Shield"}, + {AM_CP_WEAPON, "WEAPON", "Chemical_Protection_Weapon"}, + {AM_CREATECREATURE, "CREATECREATURE", "Life_Creation"}, + {AM_CULTIVATION, "CULTIVATION", "Cultivation"}, + {AM_DEMONSTRATION, "DEMONSTRATION", "Demonstration"}, + {AM_DRILLMASTER, "DRILLMASTER", "Drillmaster"}, + {AM_FLAMECONTROL, "FLAMECONTROL", "Flame_Control"}, + {AM_HEALHOMUN, "HEALHOMUN", "Heal_Homunculus"}, + {AM_LEARNINGPOTION, "LEARNINGPOTION", "AM_LEARNINGPOTION"}, + {AM_PHARMACY, "PHARMACY", "Pharmacy"}, + {AM_POTIONPITCHER, "POTIONPITCHER", "Potion_Pitcher"}, + {AM_REST, "REST", "Sabbath"}, + {AM_RESURRECTHOMUN, "RESURRECTHOMUN", "Ressurect_Homunculus"}, + {AM_SPHEREMINE, "SPHEREMINE", "Sphere_Mine"}, + {ASC_BREAKER, "BREAKER", "Breaker"}, + {ASC_CDP, "CDP", "Create_Deadly_Poison"}, + {ASC_EDP, "EDP", "Deadly_Poison_Enchantment"}, + {ASC_HALLUCINATION, "HALLUCINATION", "Hallucination_Walk"}, + {ASC_KATAR, "KATAR", "Advanced_Katar_Mastery"}, + {ASC_METEORASSAULT, "METEORASSAULT", "Meteor_Assault"}, + {AS_CLOAKING, "CLOAKING", "Cloaking"}, + {AS_ENCHANTPOISON, "ENCHANTPOISON", "Enchant_Poison"}, + {AS_GRIMTOOTH, "GRIMTOOTH", "Grimtooth"}, + {AS_KATAR, "KATAR", "Katar_Mastery"}, + {AS_LEFT, "LEFT", "Lefthand_Mastery"}, + {AS_POISONREACT, "POISONREACT", "Poison_React"}, + {AS_RIGHT, "RIGHT", "Righthand_Mastery"}, + {AS_SONICBLOW, "SONICBLOW", "Sonic_Blow"}, + {AS_SPLASHER, "SPLASHER", "Venom_Splasher"}, + {AS_VENOMDUST, "VENOMDUST", "Venom_Dust"}, + {BA_APPLEIDUN, "APPLEIDUN", "Apple_of_Idun"}, + {BA_ASSASSINCROSS, "ASSASSINCROSS", "Assassin_Cross"}, + {BA_DISSONANCE, "DISSONANCE", "Dissonance"}, + {BA_FROSTJOKE, "FROSTJOKE", "Dumb_Joke"}, + {BA_MUSICALLESSON, "MUSICALLESSON", "Musical_Lesson"}, + {BA_MUSICALSTRIKE, "MUSICALSTRIKE", "Musical_Strike"}, + {BA_POEMBRAGI, "POEMBRAGI", "Poem_of_Bragi"}, + {BA_WHISTLE, "WHISTLE", "Whistle"}, + {BD_ADAPTATION, "ADAPTATION", "Adaption"}, + {BD_DRUMBATTLEFIELD, "DRUMBATTLEFIELD", "Drumb_BattleField"}, + {BD_ENCORE, "ENCORE", "Encore"}, + {BD_ETERNALCHAOS, "ETERNALCHAOS", "Eternal_Chaos"}, + {BD_INTOABYSS, "INTOABYSS", "Into_the_Abyss"}, + {BD_LULLABY, "LULLABY", "Lullaby"}, + {BD_RAGNAROK, "RAGNAROK", "Ragnarok"}, + {BD_RICHMANKIM, "RICHMANKIM", "Rich_Mankim"}, + {BD_RINGNIBELUNGEN, "RINGNIBELUNGEN", "Ring_of_Nibelugen"}, + {BD_ROKISWEIL, "ROKISWEIL", "Loki's_Wail"}, + {BD_SIEGFRIED, "SIEGFRIED", "Invulnerable_Siegfried"}, + {BS_ADRENALINE, "ADRENALINE", "Adrenaline_Rush"}, + {BS_ADRENALINE2, "ADRENALINE2", "Adrenaline Rush 2"}, + {BS_AXE, "AXE", "Smith_Axe"}, + {BS_DAGGER, "DAGGER", "Smith_Dagger"}, + {BS_ENCHANTEDSTONE, "ENCHANTEDSTONE", "Enchantedstone_Craft"}, + {BS_FINDINGORE, "FINDINGORE", "Ore_Discovery"}, + {BS_HAMMERFALL, "HAMMERFALL", "Hammer_Fall"}, + {BS_HILTBINDING, "HILTBINDING", "Hilt_Binding"}, + {BS_IRON, "IRON", "Iron_Tempering"}, + {BS_KNUCKLE, "KNUCKLE", "Smith_Knucklebrace"}, + {BS_MACE, "MACE", "Smith_Mace"}, + {BS_MAXIMIZE, "MAXIMIZE", "Power_Maximize"}, + {BS_ORIDEOCON, "ORIDEOCON", "Orideocon_Research"}, + {BS_OVERTHRUST, "OVERTHRUST", "Power-Thrust"}, + {BS_REPAIRWEAPON, "REPAIRWEAPON", "Weapon_Repair"}, + {BS_SKINTEMPER, "SKINTEMPER", "Skin_Tempering"}, + {BS_SPEAR, "SPEAR", "Smith_Spear"}, + {BS_STEEL, "STEEL", "Steel_Tempering"}, + {BS_SWORD, "SWORD", "Smith_Sword"}, + {BS_TWOHANDSWORD, "TWOHANDSWORD", "Smith_Two-handed_Sword"}, + {BS_WEAPONPERFECT, "WEAPONPERFECT", "Weapon_Perfection"}, + {BS_WEAPONRESEARCH, "WEAPONRESEARCH", "Weaponry_Research"}, + {CG_ARROWVULCAN, "ARROWVULCAN", "Vulcan_Arrow"}, + {CG_MARIONETTE, "MARIONETTE", "Marionette_Control"}, + {CG_MOONLIT, "MOONLIT", "Moonlight_Petals"}, + {CH_CHAINCRUSH, "CHAINCRUSH", "Chain_Crush_Combo"}, + {CH_PALMSTRIKE, "PALMSTRIKE", "Palm_Push_Strike"}, + {CH_SOULCOLLECT, "SOULCOLLECT", "Collect_Soul"}, + {CH_TIGERFIST, "TIGERFIST", "Tiger_Knuckle_Fist"}, + {CR_ALCHEMY, "ALCHEMY", "Alchemy"}, + {CR_AUTOGUARD, "AUTOGUARD", "Guard"}, + {CR_DEFENDER, "DEFENDER", "Defender"}, + {CR_DEVOTION, "DEVOTION", "Sacrifice"}, + {CR_GRANDCROSS, "GRANDCROSS", "Grand_Cross"}, + {CR_HOLYCROSS, "HOLYCROSS", "Holy_Cross"}, + {CR_PROVIDENCE, "PROVIDENCE", "Providence"}, + {CR_REFLECTSHIELD, "REFLECTSHIELD", "Shield_Reflect"}, + {CR_SHIELDBOOMERANG, "SHIELDBOOMERANG", "Shield_Boomerang"}, + {CR_SHIELDCHARGE, "SHIELDCHARGE", "Shield_Charge"}, + {CR_SPEARQUICKEN, "SPEARQUICKEN", "Spear_Quicken"}, + {CR_SYNTHESISPOTION, "SYNTHESISPOTION", "Potion_Synthesis"}, + {CR_TRUST, "TRUST", "Faith"}, + {DC_DANCINGLESSON, "DANCINGLESSON", "Dancing_Lesson"}, + {DC_DONTFORGETME, "DONTFORGETME", "Don't_Forget_Me"}, + {DC_FORTUNEKISS, "FORTUNEKISS", "Fortune_Kiss"}, + {DC_HUMMING, "HUMMING", "Humming"}, + {DC_SCREAM, "SCREAM", "Scream"}, + {DC_SERVICEFORYOU, "SERVICEFORYOU", "Prostitute"}, + {DC_THROWARROW, "THROWARROW", "Throw_Arrow"}, + {DC_UGLYDANCE, "UGLYDANCE", "Ugly_Dance"}, + {HP_ASSUMPTIO, "ASSUMPTIO", "Assumptio"}, + {HP_BASILICA, "BASILICA", "Basilica"}, + {HP_MEDITATIO, "MEDITATIO", "Meditation"}, + {HT_ANKLESNARE, "ANKLESNARE", "Ankle_Snare"}, + {HT_BEASTBANE, "BEASTBANE", "Beast_Bane"}, + {HT_BLASTMINE, "BLASTMINE", "Blast_Mine"}, + {HT_BLITZBEAT, "BLITZBEAT", "Blitz_Beat"}, + {HT_CLAYMORETRAP, "CLAYMORETRAP", "Claymore_Trap"}, + {HT_DETECTING, "DETECTING", "Detect"}, + {HT_FALCON, "FALCON", "Falconry_Mastery"}, + {HT_FLASHER, "FLASHER", "Flasher"}, + {HT_FREEZINGTRAP, "FREEZINGTRAP", "Freezing_Trap"}, + {HT_LANDMINE, "LANDMINE", "Land_Mine"}, + {HT_REMOVETRAP, "REMOVETRAP", "Remove_Trap"}, + {HT_SANDMAN, "SANDMAN", "Sandman"}, + {HT_SHOCKWAVE, "SHOCKWAVE", "Shockwave_Trap"}, + {HT_SKIDTRAP, "SKIDTRAP", "Skid_Trap"}, + {HT_SPRINGTRAP, "SPRINGTRAP", "Spring_Trap"}, + {HT_STEELCROW, "STEELCROW", "Steel_Crow"}, + {HT_TALKIEBOX, "TALKIEBOX", "Talkie_Box"}, + {HW_MAGICCRASHER, "MAGICCRASHER", "Magic_Crasher"}, + {HW_MAGICPOWER, "MAGICPOWER", "Magic_Power"}, + {HW_NAPALMVULCAN, "NAPALMVULCAN", "Napalm_Vulcan"}, + {HW_SOULDRAIN, "SOULDRAIN", "Soul_Drain"}, + {KN_AUTOCOUNTER, "AUTOCOUNTER", "Counter_Attack"}, + {KN_BOWLINGBASH, "BOWLINGBASH", "Bowling_Bash"}, + {KN_BRANDISHSPEAR, "BRANDISHSPEAR", "Brandish_Spear"}, + {KN_CAVALIERMASTERY, "CAVALIERMASTERY", "Cavalier_Mastery"}, + {KN_PIERCE, "PIERCE", "Pierce"}, + {KN_RIDING, "RIDING", "Peco_Peco_Ride"}, + {KN_SPEARBOOMERANG, "SPEARBOOMERANG", "Spear_Boomerang"}, + {KN_SPEARMASTERY, "SPEARMASTERY", "Spear_Mastery"}, + {KN_SPEARSTAB, "SPEARSTAB", "Spear_Stab"}, + {KN_TWOHANDQUICKEN, "TWOHANDQUICKEN", "Twohand_Quicken"}, + {LK_AURABLADE, "AURABLADE", "Aura_Blade"}, + {LK_BERSERK, "BERSERK", "Berserk"}, + {LK_CONCENTRATION, "CONCENTRATION", "Concentration"}, + {LK_FURY, "FURY", "LK_FURY"}, + {LK_HEADCRUSH, "HEADCRUSH", "Head_Crusher"}, + {LK_JOINTBEAT, "JOINTBEAT", "Joint_Beat"}, + {LK_PARRYING, "PARRYING", "Parrying"}, + {LK_SPIRALPIERCE, "SPIRALPIERCE", "Spiral_Pierce"}, + {LK_TENSIONRELAX, "TENSIONRELAX", "Tension_Relax"}, + {MC_CARTREVOLUTION, "CARTREVOLUTION", "Cart_Revolution"}, + {MC_CHANGECART, "CHANGECART", "Change_Cart"}, + {MC_DISCOUNT, "DISCOUNT", "Discount"}, + {MC_IDENTIFY, "IDENTIFY", "Item_Appraisal"}, + {MC_INCCARRY, "INCCARRY", "Enlarge_Weight_Limit"}, + {MC_LOUD, "LOUD", "Lord_Exclamation"}, + {MC_MAMMONITE, "MAMMONITE", "Mammonite"}, + {MC_OVERCHARGE, "OVERCHARGE", "Overcharge"}, + {MC_PUSHCART, "PUSHCART", "Pushcart"}, + {MG_COLDBOLT, "COLDBOLT", "Cold_Bolt"}, + {MG_ENERGYCOAT, "ENERGYCOAT", "Energy_Coat"}, + {MG_FIREBALL, "FIREBALL", "Fire_Ball"}, + {MG_FIREBOLT, "FIREBOLT", "Fire_Bolt"}, + {MG_FIREWALL, "FIREWALL", "Fire_Wall"}, + {MG_FROSTDIVER, "FROSTDIVER", "Frost_Diver"}, + {MG_LIGHTNINGBOLT, "LIGHTNINGBOLT", "Lightening_Bolt"}, + {MG_NAPALMBEAT, "NAPALMBEAT", "Napalm_Beat"}, + {MG_SAFETYWALL, "SAFETYWALL", "Safety_Wall"}, + {MG_SIGHT, "SIGHT", "Sight"}, + {MG_SOULSTRIKE, "SOULSTRIKE", "Soul_Strike"}, + {MG_SRECOVERY, "SRECOVERY", "Increase_SP_Recovery"}, + {MG_STONECURSE, "STONECURSE", "Stone_Curse"}, + {MG_THUNDERSTORM, "THUNDERSTORM", "Thunderstorm"}, + {MO_ABSORBSPIRITS, "ABSORBSPIRITS", "Absorb_Spirits"}, + {MO_BLADESTOP, "BLADESTOP", "Blade_Stop"}, + {MO_BODYRELOCATION, "BODYRELOCATION", "Body_Relocation"}, + {MO_CALLSPIRITS, "CALLSPIRITS", "Call_Spirits"}, + {MO_CHAINCOMBO, "CHAINCOMBO", "Chain_Combo"}, + {MO_COMBOFINISH, "COMBOFINISH", "Combo_Finish"}, + {MO_DODGE, "DODGE", "Dodge"}, + {MO_EXPLOSIONSPIRITS, "EXPLOSIONSPIRITS", "Explosion_Spirits"}, + {MO_EXTREMITYFIST, "EXTREMITYFIST", "Extremity_Fist"}, + {MO_FINGEROFFENSIVE, "FINGEROFFENSIVE", "Finger_Offensive"}, + {MO_INVESTIGATE, "INVESTIGATE", "Investigate"}, + {MO_IRONHAND, "IRONHAND", "Iron_Hand"}, + {MO_SPIRITSRECOVERY, "SPIRITSRECOVERY", "Spirit_Recovery"}, + {MO_STEELBODY, "STEELBODY", "Steel_Body"}, + {MO_TRIPLEATTACK, "TRIPLEATTACK", "Triple_Blows"}, + {NPC_ATTRICHANGE, "ATTRICHANGE", "NPC_ATTRICHANGE"}, + {NPC_BARRIER, "BARRIER", "NPC_BARRIER"}, + {NPC_BLINDATTACK, "BLINDATTACK", "NPC_BLINDATTACK"}, + {NPC_BLOODDRAIN, "BLOODDRAIN", "NPC_BLOODDRAIN"}, + {NPC_CHANGEDARKNESS, "CHANGEDARKNESS", "NPC_CHANGEDARKNESS"}, + {NPC_CHANGEFIRE, "CHANGEFIRE", "NPC_CHANGEFIRE"}, + {NPC_CHANGEGROUND, "CHANGEGROUND", "NPC_CHANGEGROUND"}, + {NPC_CHANGEHOLY, "CHANGEHOLY", "NPC_CHANGEHOLY"}, + {NPC_CHANGEPOISON, "CHANGEPOISON", "NPC_CHANGEPOISON"}, + {NPC_CHANGETELEKINESIS, "CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS"}, + {NPC_CHANGEWATER, "CHANGEWATER", "NPC_CHANGEWATER"}, + {NPC_CHANGEWIND, "CHANGEWIND", "NPC_CHANGEWIND"}, + {NPC_COMBOATTACK, "COMBOATTACK", "NPC_COMBOATTACK"}, + {NPC_CRITICALSLASH, "CRITICALSLASH", "NPC_CRITICALSLASH"}, + {NPC_CURSEATTACK, "CURSEATTACK", "NPC_CURSEATTACK"}, + {NPC_DARKBLESSING, "DARKBLESSING", "NPC_DARKBLESSING"}, + {NPC_DARKBREATH, "DARKBREATH", "NPC_DARKBREATH"}, + {NPC_DARKCROSS, "DARKCROSS", "NPC_DARKCROSS"}, + {NPC_DARKNESSATTACK, "DARKNESSATTACK", "NPC_DARKNESSATTACK"}, + {NPC_DEFENDER, "DEFENDER", "NPC_DEFENDER"}, + {NPC_EMOTION, "EMOTION", "NPC_EMOTION"}, + {NPC_ENERGYDRAIN, "ENERGYDRAIN", "NPC_ENERGYDRAIN"}, + {NPC_FIREATTACK, "FIREATTACK", "NPC_FIREATTACK"}, + {NPC_GROUNDATTACK, "GROUNDATTACK", "NPC_GROUNDATTACK"}, + {NPC_GUIDEDATTACK, "GUIDEDATTACK", "NPC_GUIDEDATTACK"}, + {NPC_HALLUCINATION, "HALLUCINATION", "NPC_HALLUCINATION"}, + {NPC_HOLYATTACK, "HOLYATTACK", "NPC_HOLYATTACK"}, + {NPC_KEEPING, "KEEPING", "NPC_KEEPING"}, + {NPC_LICK, "LICK", "NPC_LICK"}, + {NPC_MAGICALATTACK, "MAGICALATTACK", "NPC_MAGICALATTACK"}, + {NPC_MENTALBREAKER, "MENTALBREAKER", "NPC_MENTALBREAKER"}, + {NPC_METAMORPHOSIS, "METAMORPHOSIS", "NPC_METAMORPHOSIS"}, + {NPC_PETRIFYATTACK, "PETRIFYATTACK", "NPC_PETRIFYATTACK"}, + {NPC_PIERCINGATT, "PIERCINGATT", "NPC_PIERCINGATT"}, + {NPC_POISON, "POISON", "NPC_POISON"}, + {NPC_POISONATTACK, "POISONATTACK", "NPC_POISONATTACK"}, + {NPC_RANDOMATTACK, "RANDOMATTACK", "NPC_RANDOMATTACK"}, + {NPC_RANGEATTACK, "RANGEATTACK", "NPC_RANGEATTACK"}, + {NPC_REBIRTH, "REBIRTH", "NPC_REBIRTH"}, + {NPC_SELFDESTRUCTION, "SELFDESTRUCTION", "Kabooooom!"}, + {NPC_SELFDESTRUCTION2, "SELFDESTRUCTION2", "NPC_SELFDESTRUCTION2"}, + {NPC_SILENCEATTACK, "SILENCEATTACK", "NPC_SILENCEATTACK"}, + {NPC_SLEEPATTACK, "SLEEPATTACK", "NPC_SLEEPATTACK"}, + {NPC_SMOKING, "SMOKING", "NPC_SMOKING"}, + {NPC_SPLASHATTACK, "SPLASHATTACK", "NPC_SPLASHATTACK"}, + {NPC_STUNATTACK, "STUNATTACK", "NPC_STUNATTACK"}, + {NPC_SUICIDE, "SUICIDE", "NPC_SUICIDE"}, + {NPC_SUMMONMONSTER, "SUMMONMONSTER", "NPC_SUMMONMONSTER"}, + {NPC_SUMMONSLAVE, "SUMMONSLAVE", "NPC_SUMMONSLAVE"}, + {NPC_TELEKINESISATTACK, "TELEKINESISATTACK", "NPC_TELEKINESISATTACK"}, + {NPC_TRANSFORMATION, "TRANSFORMATION", "NPC_TRANSFORMATION"}, + {NPC_WATERATTACK, "WATERATTACK", "NPC_WATERATTACK"}, + {NPC_WINDATTACK, "WINDATTACK", "NPC_WINDATTACK"}, + {NV_EMOTE, "EMOTE", "Emote_Skill"}, + {NV_TRADE, "TRADE", "Trade_Skill"}, + {NV_PARTY, "PARTY", "Party_Skill"}, + {NV_FIRSTAID, "FIRSTAID", "First Aid"}, + {NV_TRICKDEAD, "TRICKDEAD", "Play_Dead"}, + {PA_GOSPEL, "GOSPEL", "Gospel"}, + {PA_PRESSURE, "PRESSURE", "Pressure"}, + {PA_SACRIFICE, "SACRIFICE", "Sacrificial_Ritual"}, + {PF_FOGWALL, "FOGWALL", "Wall_of_Fog"}, + {PF_HPCONVERSION, "HPCONVERSION", "Health_Conversion"}, + {PF_MEMORIZE, "MEMORIZE", "Memorize"}, + {PF_MINDBREAKER, "MINDBREAKER", "Mind_Breaker"}, + {PF_SOULBURN, "SOULBURN", "Soul_Burn"}, + {PF_SOULCHANGE, "SOULCHANGE", "Soul_Change"}, + {PF_SPIDERWEB, "SPIDERWEB", "Spider_Web"}, + {PR_ASPERSIO, "ASPERSIO", "Aspersio"}, + {PR_BENEDICTIO, "BENEDICTIO", "B.S_Sacramenti"}, + {PR_GLORIA, "GLORIA", "Gloria"}, + {PR_IMPOSITIO, "IMPOSITIO", "Impositio_Manus"}, + {PR_KYRIE, "KYRIE", "Kyrie_Eleison"}, + {PR_LEXAETERNA, "LEXAETERNA", "Lex_Aeterna"}, + {PR_LEXDIVINA, "LEXDIVINA", "Lex_Divina"}, + {PR_MACEMASTERY, "MACEMASTERY", "Mace_Mastery"}, + {PR_MAGNIFICAT, "MAGNIFICAT", "Magnificat"}, + {PR_MAGNUS, "MAGNUS", "Magnus_Exorcismus"}, + {PR_SANCTUARY, "SANCTUARY", "Santuary"}, + {PR_SLOWPOISON, "SLOWPOISON", "Slow_Poison"}, + {PR_STRECOVERY, "STRECOVERY", "Status_Recovery"}, + {PR_SUFFRAGIUM, "SUFFRAGIUM", "Suffragium"}, + {PR_TURNUNDEAD, "TURNUNDEAD", "Turn_Undead"}, + {RG_BACKSTAP, "BACKSTAP", "Back_Stab"}, + {RG_CLEANER, "CLEANER", "Remover"}, + {RG_COMPULSION, "COMPULSION", "Compulsion_Discount"}, + {RG_FLAGGRAFFITI, "FLAGGRAFFITI", "Flag_Graffity"}, + {RG_GANGSTER, "GANGSTER", "Gangster's_Paradise"}, + {RG_GRAFFITI, "GRAFFITI", "Graffiti"}, + {RG_INTIMIDATE, "INTIMIDATE", "Intimidate"}, + {RG_PLAGIARISM, "PLAGIARISM", "Plagiarism"}, + {RG_RAID, "RAID", "Raid"}, + {RG_SNATCHER, "SNATCHER", "Snatcher"}, + {RG_STEALCOIN, "STEALCOIN", "Steal_Coin"}, + {RG_STRIPARMOR, "STRIPARMOR", "Strip_Armor"}, + {RG_STRIPHELM, "STRIPHELM", "Strip_Helm"}, + {RG_STRIPSHIELD, "STRIPSHIELD", "Strip_Shield"}, + {RG_STRIPWEAPON, "STRIPWEAPON", "Strip_Weapon"}, + {RG_TUNNELDRIVE, "TUNNELDRIVE", "Tunnel_Drive"}, + {SA_ABRACADABRA, "ABRACADABRA", "Hocus-pocus"}, + {SA_ADVANCEDBOOK, "ADVANCEDBOOK", "Advanced_Book"}, + {SA_AUTOSPELL, "AUTOSPELL", "Auto_Cast"}, + {SA_CASTCANCEL, "CASTCANCEL", "Cast_Cancel"}, + {SA_CLASSCHANGE, "CLASSCHANGE", "Class_Change"}, + {SA_COMA, "COMA", "Coma"}, + {SA_DEATH, "DEATH", "Death"}, + {SA_DELUGE, "DELUGE", "Deluge"}, + {SA_DISPELL, "DISPELL", "Dispel"}, + {SA_DRAGONOLOGY, "DRAGONOLOGY", "Dragonology"}, + {SA_FLAMELAUNCHER, "FLAMELAUNCHER", "Flame_Launcher"}, + {SA_FORTUNE, "FORTUNE", "Fortune"}, + {SA_FREECAST, "FREECAST", "Cast_Freedom"}, + {SA_FROSTWEAPON, "FROSTWEAPON", "Frost_Weapon"}, + {SA_FULLRECOVERY, "FULLRECOVERY", "Full_Recovery"}, + {SA_GRAVITY, "GRAVITY", "Gravity"}, + {SA_INSTANTDEATH, "INSTANTDEATH", "Instant_Death"}, + {SA_LANDPROTECTOR, "LANDPROTECTOR", "Land_Protector"}, + {SA_LEVELUP, "LEVELUP", "Level_Up"}, + {SA_LIGHTNINGLOADER, "LIGHTNINGLOADER", "Lightning_Loader"}, + {SA_MAGICROD, "MAGICROD", "Magic_Rod"}, + {SA_MONOCELL, "MONOCELL", "Monocell"}, + {SA_QUESTION, "QUESTION", "Question?"}, + {SA_REVERSEORCISH, "REVERSEORCISH", "Reverse_Orcish"}, + {SA_SEISMICWEAPON, "SEISMICWEAPON", "Seismic_Weapon"}, + {SA_SPELLBREAKER, "SPELLBREAKER", "Break_Spell"}, + {SA_SUMMONMONSTER, "SUMMONMONSTER", "Summon_Monster"}, + {SA_VIOLENTGALE, "VIOLENTGALE", "Violent_Gale"}, + {SA_VOLCANO, "VOLCANO", "Volcano"}, + {SG_DEVIL, "DEVIL", "Devil"}, + {SG_FEEL, "FEEL", "Feel"}, + {SG_FRIEND, "FRIEND", "Friend"}, + {SG_FUSION, "FUSION", "Fusion"}, + {SG_HATE, "HATE", "Hate"}, + {SG_KNOWLEDGE, "KNOWLEDGE", "Knowledge"}, + {SG_MOON_ANGER, "ANGER", "Moon Anger"}, + {SG_MOON_BLESS, "BLESS", "Moon Bless"}, + {SG_MOON_COMFORT, "COMFORT", "Moon Comfort"}, + {SG_MOON_WARM, "WARM", "Moon Warm"}, + {SG_STAR_ANGER, "ANGER", "Star Anger"}, + {SG_STAR_BLESS, "BLESS", "Star Bless"}, + {SG_STAR_COMFORT, "COMFORT", "Star Comfort"}, + {SG_STAR_WARM, "WARM", "Star Warm"}, + {SG_SUN_ANGER, "ANGER", "Sun Anger"}, + {SG_SUN_BLESS, "BLESS", "Sun Bless"}, + {SG_SUN_COMFORT, "COMFORT", "Sun Comfort"}, + {SG_SUN_WARM, "WARM", "Sun Warm"}, + {SL_ALCHEMIST, "ALCHEMIST", "Alchemist"}, + {SL_ASSASIN, "ASSASIN", "Assasin"}, + {SL_BARDDANCER, "BARDDANCER", "Bard Dancer"}, + {SL_BLACKSMITH, "BLACKSMITH", "Black Smith"}, + {SL_CRUSADER, "CRUSADER", "Crusader"}, + {SL_HUNTER, "HUNTER", "Hunter"}, + {SL_KAAHI, "KAAHI", "Kaahi"}, + {SL_KAINA, "KAINA", "Kaina"}, + {SL_KAITE, "KAITE", "Kaite"}, + {SL_KAIZEL, "KAIZEL", "Kaizel"}, + {SL_KAUPE, "KAUPE", "Kaupe"}, + {SL_KNIGHT, "KNIGHT", "Knight"}, + {SL_MONK, "MONK", "Monk"}, + {SL_PRIEST, "PRIEST", "Priest"}, + {SL_ROGUE, "ROGUE", "Rogue"}, + {SL_SAGE, "SAGE", "Sage"}, + {SL_SKA, "SKA", "SKA"}, + {SL_SKE, "SKE", "SKE"}, + {SL_SMA, "SMA", "SMA"}, + {SL_SOULLINKER, "SOULLINKER", "Soul Linker"}, + {SL_STAR, "STAR", "Star"}, + {SL_STIN, "STIN", "Stin"}, + {SL_STUN, "STUN", "Stun"}, + {SL_SUPERNOVICE, "SUPERNOVICE", "Super Novice"}, + {SL_SWOO, "SWOO", "Swoo"}, + {SL_WIZARD, "WIZARD", "Wizard"}, + {SM_AUTOBERSERK, "AUTOBERSERK", "Auto_Berserk"}, + {SM_BASH, "BASH", "Bash"}, + {SM_ENDURE, "ENDURE", "Endure"}, + {SM_FATALBLOW, "FATALBLOW", "Attack_Weak_Point"}, + {SM_MAGNUM, "MAGNUM", "Magnum_Break"}, + {SM_MOVINGRECOVERY, "MOVINGRECOVERY", "Moving_HP_Recovery"}, + {SM_PROVOKE, "PROVOKE", "Provoke"}, + {SM_RECOVERY, "RECOVERY", "Increase_HP_Recovery"}, + {SM_SWORD, "SWORD", "Sword_Mastery"}, + {SM_TWOHAND, "TWOHAND", "Two-Handed_Sword_Mastery"}, + {SN_FALCONASSAULT, "FALCONASSAULT", "Falcon_Assault"}, + {SN_SHARPSHOOTING, "SHARPSHOOTING", "Sharpshooting"}, + {SN_SIGHT, "SIGHT", "True_Sight"}, + {SN_WINDWALK, "WINDWALK", "Wind_Walk"}, + {ST_CHASEWALK, "CHASEWALK", "Chase_Walk"}, + {ST_REJECTSWORD, "REJECTSWORD", "Reject_Sword"}, + {ST_STEALBACKPACK, "STEALBACKPACK", "Steal_Backpack"}, + {TF_BACKSLIDING, "BACKSLIDING", "Back_Sliding"}, + {TF_DETOXIFY, "DETOXIFY", "Detoxify"}, + {TF_DOUBLE, "DOUBLE", "Double_Attack"}, + {TF_HIDING, "HIDING", "Hiding"}, + {TF_MISS, "MISS", "Improve_Dodge"}, + {TF_PICKSTONE, "PICKSTONE", "Take_Stone"}, + {TF_POISON, "POISON", "Envenom"}, + {TF_SPRINKLESAND, "SPRINKLESAND", "Throw_Sand"}, + {TF_STEAL, "STEAL", "Steal"}, + {TF_THROWSTONE, "THROWSTONE", "Throw_Stone"}, + {TK_COUNTER, "COUNTER", "Counter"}, + {TK_DODGE, "DODGE", "Dodge"}, + {TK_DOWNKICK, "DOWNKICK", "Down Kick"}, + {TK_HIGHJUMP, "HIGHJUMP", "High Jump"}, + {TK_HPTIME, "HPTIME", "HP Time"}, + {TK_JUMPKICK, "JUMPKICK", "Jump Kick"}, + {TK_POWER, "POWER", "Power"}, + {TK_READYCOUNTER, "READYCOUNTER", "Ready Counter"}, + {TK_READYDOWN, "READYDOWN", "Ready Down"}, + {TK_READYSTORM, "READYSTORM", "Ready Storm"}, + {TK_READYTURN, "READYTURN", "Ready Turn"}, + {TK_RUN, "RUN", "TK_RUN"}, + {TK_SEVENWIND, "SEVENWIND", "Seven Wind"}, + {TK_SPTIME, "SPTIME", "SP Time"}, + {TK_STORMKICK, "STORMKICK", "Storm Kick"}, + {TK_TURNKICK, "TURNKICK", "Turn Kick"}, + {WE_BABY, "BABY", "Adopt_Baby"}, + {WE_CALLBABY, "CALLBABY", "Call_Baby"}, + {WE_CALLPARENT, "CALLPARENT", "Call_Parent"}, + {WE_CALLPARTNER, "CALLPARTNER", "I Want to See You"}, + {WE_FEMALE, "FEMALE", "I Only Look Up to You"}, + {WE_MALE, "MALE", "I Will Protect You"}, + {WS_CARTBOOST, "CARTBOOST", "Cart_Boost"}, + {WS_CREATECOIN, "CREATECOIN", "Create_Coins"}, + {WS_CREATENUGGET, "CREATENUGGET", "Create_Nuggets"}, + {WS_MELTDOWN, "MELTDOWN", "Meltdown"}, + {WS_SYSTEMCREATE, "SYSTEMCREATE", "Create_System_tower"}, + {WZ_EARTHSPIKE, "EARTHSPIKE", "Earth_Spike"}, + {WZ_ESTIMATION, "ESTIMATION", "Sense"}, + {WZ_FIREIVY, "FIREIVY", "Fire_Ivy"}, + {WZ_FIREPILLAR, "FIREPILLAR", "Fire_Pillar"}, + {WZ_FROSTNOVA, "FROSTNOVA", "Frost_Nova"}, + {WZ_HEAVENDRIVE, "HEAVENDRIVE", "Heaven's_Drive"}, + {WZ_ICEWALL, "ICEWALL", "Ice_Wall"}, + {WZ_JUPITEL, "JUPITEL", "Jupitel_Thunder"}, + {WZ_METEOR, "METEOR", "Meteor_Storm"}, + {WZ_QUAGMIRE, "QUAGMIRE", "Quagmire"}, + {WZ_SIGHTRASHER, "SIGHTRASHER", "Sightrasher"}, + {WZ_STORMGUST, "STORMGUST", "Storm_Gust"}, + {WZ_VERMILION, "VERMILION", "Lord_of_Vermilion"}, + {WZ_WATERBALL, "WATERBALL", "Water_Ball"}, + {0, 0, 0} +}; + +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]; + +#define UNARMED_PLAYER_DAMAGE_MIN(bl) (skill_power_bl((bl), TMW_BRAWLING) >> 4) // +50 for 200 +#define UNARMED_PLAYER_DAMAGE_MAX(bl) (skill_power_bl((bl), TMW_BRAWLING)) // +200 for 200 + +int skill_get_hit (int id) +{ + return skill_db[id].hit; +} + +int skill_get_inf (int id) +{ + return skill_db[id].inf; +} + +int skill_get_pl (int id) +{ + return skill_db[id].pl; +} + +int skill_get_nk (int id) +{ + return skill_db[id].nk; +} + +int skill_get_max (int id) +{ + return skill_db[id].max; +} + +int skill_get_max_raise (int id) +{ + return skill_db[id].max_raise; +} + +int skill_get_range (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].range[lv - 1]; +} + +int skill_get_hp (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].hp[lv - 1]; +} + +int skill_get_sp (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].sp[lv - 1]; +} + +int skill_get_zeny (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].zeny[lv - 1]; +} + +int skill_get_num (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].num[lv - 1]; +} + +int skill_get_cast (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].cast[lv - 1]; +} + +int skill_get_delay (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].delay[lv - 1]; +} + +int skill_get_time (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].upkeep_time[lv - 1]; +} + +int skill_get_time2 (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].upkeep_time2[lv - 1]; +} + +int skill_get_castdef (int id) +{ + return skill_db[id].cast_def_rate; +} + +int skill_get_weapontype (int id) +{ + return skill_db[id].weapon; +} + +int skill_get_inf2 (int id) +{ + return skill_db[id].inf2; +} + +int skill_get_maxcount (int id) +{ + return skill_db[id].maxcount; +} + +int skill_get_blewcount (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].blewcount[lv - 1]; +} + +int skill_get_mhp (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].mhp[lv - 1]; +} + +int skill_get_castnodex (int id, int lv) +{ + return (lv <= 0) ? 0 : skill_db[id].castnodex[lv - 1]; +} + +/* プロトタイプ */ +struct skill_unit_group *skill_unitsetting (struct block_list *src, + int skillid, int skilllv, int x, + int y, int flag); +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 skill_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); +int skill_landprotector (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); + +// [MouseJstr] - skill ok to cast? and when? +static int skillnotok (int skillid, struct map_session_data *sd) +{ + if (sd == 0) + return 0; + if (pc_isGM (sd) >= 20) + return 0; // gm's can do anything damn thing they want + switch (skillid) + { + case AL_WARP: + case AL_TELEPORT: + case MC_IDENTIFY: + return 0; // always allowed + default: + return (map[sd->bl.m].flag.noskill); + } +} + +static int distance (int x0, int y0, int x1, int y1) +{ + int dx, dy; + + dx = abs (x0 - x1); + dy = abs (y0 - y1); + return dx > dy ? dx : dy; +} + +/* スキルユニットIDを返す(これもデータベースに入れたいな) */ +int skill_get_unit_id (int id, int flag) +{ + + switch (id) + { + case MG_SAFETYWALL: + return 0x7e; /* セイフティウォール */ + case MG_FIREWALL: + return 0x7f; /* ファイアーウォール */ + case AL_WARP: + return (flag == 0) ? 0x81 : 0x80; /* ワープポータル */ + case PR_BENEDICTIO: + return 0x82; /* 聖体降福 */ + case PR_SANCTUARY: + return 0x83; /* サンクチュアリ */ + case PR_MAGNUS: + return 0x84; /* マグヌスエクソシズム */ + case AL_PNEUMA: + return 0x85; /* ニューマ */ + case MG_THUNDERSTORM: + return 0x86; /* サンダーストーム */ + case WZ_HEAVENDRIVE: + return 0x86; /* ヘヴンズドライブ */ + case WZ_SIGHTRASHER: + return 0x86; /* サイトラッシャー */ + case WZ_METEOR: + return 0x86; /* メテオストーム */ + case WZ_VERMILION: + return 0x86; /* ロードオブヴァーミリオン */ + case WZ_FROSTNOVA: + return 0x86; /* フロストノヴァ */ + case WZ_STORMGUST: + return 0x86; /* ストームガスト(とりあえずLoVと同じで処理) */ + case CR_GRANDCROSS: + return 0x86; /* グランドクロス */ + case WZ_FIREPILLAR: + return (flag == 0) ? 0x87 : 0x88; /* ファイアーピラー */ + case HT_TALKIEBOX: + return 0x99; /* トーキーボックス */ + case WZ_ICEWALL: + return 0x8d; /* アイスウォール */ + case WZ_QUAGMIRE: + return 0x8e; /* クァグマイア */ + case HT_BLASTMINE: + return 0x8f; /* ブラストマイン */ + case HT_SKIDTRAP: + return 0x90; /* スキッドトラップ */ + case HT_ANKLESNARE: + return 0x91; /* アンクルスネア */ + case AS_VENOMDUST: + return 0x92; /* ベノムダスト */ + case HT_LANDMINE: + return 0x93; /* ランドマイン */ + case HT_SHOCKWAVE: + return 0x94; /* ショックウェーブトラップ */ + case HT_SANDMAN: + return 0x95; /* サンドマン */ + case HT_FLASHER: + return 0x96; /* フラッシャー */ + case HT_FREEZINGTRAP: + return 0x97; /* フリージングトラップ */ + case HT_CLAYMORETRAP: + return 0x98; /* クレイモアートラップ */ + case SA_VOLCANO: + return 0x9a; /* ボルケーノ */ + case SA_DELUGE: + return 0x9b; /* デリュージ */ + case SA_VIOLENTGALE: + return 0x9c; /* バイオレントゲイル */ + case SA_LANDPROTECTOR: + return 0x9d; /* ランドプロテクター */ + case BD_LULLABY: + return 0x9e; /* 子守歌 */ + case BD_RICHMANKIM: + return 0x9f; /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: + return 0xa0; /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: + return 0xa1; /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: + return 0xa2; /* ニーベルングの指輪 */ + case BD_ROKISWEIL: + return 0xa3; /* ロキの叫び */ + case BD_INTOABYSS: + return 0xa4; /* 深淵の中に */ + case BD_SIEGFRIED: + return 0xa5; /* 不死身のジークフリード */ + case BA_DISSONANCE: + return 0xa6; /* 不協和音 */ + case BA_WHISTLE: + return 0xa7; /* 口笛 */ + case BA_ASSASSINCROSS: + return 0xa8; /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: + return 0xa9; /* ブラギの詩 */ + case BA_APPLEIDUN: + return 0xaa; /* イドゥンの林檎 */ + case DC_UGLYDANCE: + return 0xab; /* 自分勝手なダンス */ + case DC_HUMMING: + return 0xac; /* ハミング */ + case DC_DONTFORGETME: + return 0xad; /* 私を忘れないで… */ + case DC_FORTUNEKISS: + return 0xae; /* 幸運のキス */ + case DC_SERVICEFORYOU: + return 0xaf; /* サービスフォーユー */ + case RG_GRAFFITI: + return 0xb0; /* グラフィティ */ + case AM_DEMONSTRATION: + return 0xb1; /* デモンストレーション */ + case WE_CALLPARTNER: + return 0xb2; /* あなたに逢いたい */ + case PA_GOSPEL: + return 0xb3; /* ゴスペル */ + case HP_BASILICA: + return 0xb4; /* バジリカ */ + case PF_FOGWALL: + return 0xb6; /* フォグウォール */ + case PF_SPIDERWEB: + return 0xb7; /* スパイダーウェッブ */ + } + return 0; + /* + * 0x89,0x8a,0x8b 表示無し + * 0x9a 炎属性の詠唱みたいなエフェクト + * 0x9b 水属性の詠唱みたいなエフェクト + * 0x9c 風属性の詠唱みたいなエフェクト + * 0x9d 白い小さなエフェクト + * 0xb1 Alchemist Demonstration + * 0xb2 = Pink Warp Portal + * 0xb3 = Gospel For Paladin + * 0xb4 = Basilica + * 0xb5 = Empty + * 0xb6 = Fog Wall for Professor + * 0xb7 = Spider Web for Professor + * 0xb8 = Empty + * 0xb9 = + */ +} + +/*========================================== + * スキル追加効果 + *------------------------------------------ + */ +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[] = { + MG_STONECURSE, MG_FROSTDIVER, NPC_STUNATTACK, + NPC_SLEEPATTACK, TF_POISON, NPC_CURSEATTACK, + NPC_SILENCEATTACK, 0, NPC_BLINDATTACK + }; + + struct map_session_data *sd = NULL; + struct map_session_data *dstsd = NULL; + struct mob_data *md = NULL; + struct mob_data *dstmd = NULL; + + int skill, skill2; + int rate, luk; + + int sc_def_mdef, sc_def_vit, sc_def_int, sc_def_luk; + int sc_def_mdef2, sc_def_vit2, sc_def_int2, sc_def_luk2; + int sc_def_phys_shield_spell; + + nullpo_retr (0, src); + nullpo_retr (0, bl); + + if (skilllv < 0) + return 0; + + if (src->type == BL_PC) + { + nullpo_retr (0, sd = (struct map_session_data *) src); + } + else if (src->type == BL_MOB) + { + nullpo_retr (0, md = (struct mob_data *) src); //未使用? + } + + sc_def_phys_shield_spell = 0; + if (battle_get_sc_data (bl)[SC_PHYS_SHIELD].timer != -1) + sc_def_phys_shield_spell = + battle_get_sc_data (bl)[SC_PHYS_SHIELD].val1; + + //対象の耐性 + luk = battle_get_luk (bl); + sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + luk / 3); + sc_def_vit = 100 - (3 + battle_get_vit (bl) + luk / 3); + sc_def_int = 100 - (3 + battle_get_int (bl) + luk / 3); + sc_def_luk = 100 - (3 + luk); + //自分の耐性 + luk = battle_get_luk (src); + sc_def_mdef2 = 100 - (3 + battle_get_mdef (src) + luk / 3); + sc_def_vit2 = 100 - (3 + battle_get_vit (src) + luk / 3); + sc_def_int2 = 100 - (3 + battle_get_int (src) + luk / 3); + sc_def_luk2 = 100 - (3 + luk); + if (bl->type == BL_PC) + dstsd = (struct map_session_data *) bl; + else if (bl->type == BL_MOB) + { + dstmd = (struct mob_data *) bl; //未使用? + if (sc_def_mdef > 50) + sc_def_mdef = 50; + if (sc_def_vit > 50) + sc_def_vit = 50; + if (sc_def_int > 50) + sc_def_int = 50; + if (sc_def_luk > 50) + sc_def_luk = 50; + } + if (sc_def_mdef < 0) + sc_def_mdef = 0; + if (sc_def_vit < 0) + sc_def_vit = 0; + if (sc_def_int < 0) + sc_def_int = 0; + + switch (skillid) + { + case 0: /* 通常攻撃 */ + /* 自動鷹 */ + if (sd && pc_isfalcon (sd) && sd->status.weapon == 11 + && (skill = pc_checkskill (sd, HT_BLITZBEAT)) > 0 + && MRAND (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); + } + // スナッチャー + if (sd && sd->status.weapon != 11 + && (skill = pc_checkskill (sd, RG_SNATCHER)) > 0) + if ((skill * 15 + 55) + + (skill2 = + pc_checkskill (sd, TF_STEAL)) * 10 > MRAND (1000)) + { + if (pc_steal_item (sd, bl)) + clif_skill_nodamage (src, bl, TF_STEAL, skill2, 1); + else + clif_skill_fail (sd, skillid, 0, 0); + } + break; + + case SM_BASH: /* バッシュ(急所攻撃) */ + if (sd && (skill = pc_checkskill (sd, SM_FATALBLOW)) > 0) + { + if (MRAND (100) < 6 * (skilllv - 5) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (SM_FATALBLOW, + skilllv), 0); + } + break; + + case TF_POISON: /* インベナム */ + case AS_SPLASHER: /* ベナムスプラッシャー */ + if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) + skill_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 (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case HT_FREEZINGTRAP: /* フリージングトラップ */ + rate = skilllv * 3 + 35; + if (MRAND (100) < rate * sc_def_mdef / 100) + skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case MG_FROSTDIVER: /* フロストダイバー */ + case WZ_FROSTNOVA: /* フロストノヴァ */ + rate = + (skilllv * 3 + 35) * sc_def_mdef / 100 - + (battle_get_int (bl) + battle_get_luk (bl)) / 15; + rate = rate <= 5 ? 5 : rate; + if (MRAND (100) < rate) + skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + else if (sd) + clif_skill_fail (sd, skillid, 0, 0); + break; + + case WZ_STORMGUST: /* ストームガスト */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + if (sc_data) + { + sc_data[SC_FREEZE].val3++; + if (sc_data[SC_FREEZE].val3 >= 3) + skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, + 0, skill_get_time2 (skillid, + skilllv), + 0); + } + } + break; + + case HT_LANDMINE: /* ランドマイン */ + if (MRAND (100) < (5 * skilllv + 30) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case HT_SHOCKWAVE: /* ショックウェーブトラップ */ + if (map[bl->m].flag.pvp && dstsd) + { + dstsd->status.sp -= + dstsd->status.sp * (5 + 15 * skilllv) / 100; + pc_calcstatus (dstsd, 0); + } + break; + case HT_SANDMAN: /* サンドマン */ + if (MRAND (100) < (5 * skilllv + 30) * sc_def_int / 100) + skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case TF_SPRINKLESAND: /* 砂まき */ + if (MRAND (100) < 15 * sc_def_int / 100) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case TF_THROWSTONE: /* 石投げ */ + if (MRAND (100) < 5 * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case CR_HOLYCROSS: /* ホーリークロス */ + if (MRAND (100) < 3 * skilllv * sc_def_int / 100) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case CR_GRANDCROSS: /* グランドクロス */ + { + int race = battle_get_race (bl); + if ((battle_check_undead (race, battle_get_elem_type (bl)) || race == 6) && MRAND (100) < 100000 * sc_def_int / 100) //強制付与だが完全耐性には無効 + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + } + break; + + case CR_SHIELDCHARGE: /* シールドチャージ */ + if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case RG_RAID: /* サプライズアタック */ + if (MRAND (100) < (10 + 3 * skilllv) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case BA_FROSTJOKE: + if (MRAND (100) < (15 + 5 * skilllv) * sc_def_mdef / 100) + skill_status_change_start (bl, SC_FREEZE, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case DC_SCREAM: + if (MRAND (100) < (25 + 5 * skilllv) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case BD_LULLABY: /* 子守唄 */ + if (MRAND (100) < 15 * sc_def_int / 100) + skill_status_change_start (bl, SC_SLEEP, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + /* MOBの追加効果付きスキル */ + + case NPC_PETRIFYATTACK: + if (MRAND (100) < sc_def_mdef) + skill_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 (MRAND (100) < + 50 - (sc_def_vit >> 2) - (sc_def_phys_shield_spell) + + (skilllv >> 2)) + skill_status_change_start (bl, sc[skillid - NPC_POISON], + skilllv, 0, 0, 0, skilllv, 0); + break; + case NPC_CURSEATTACK: + if (MRAND (100) < sc_def_luk) + skill_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 (MRAND (100) < sc_def_int) + skill_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; + +// -- moonsoul (adding status effect chance given to wizard aoe skills meteor and vermillion) +// + case WZ_METEOR: + if (MRAND (100) < sc_def_vit) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case WZ_VERMILION: + if (MRAND (100) < sc_def_int) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + +// -- moonsoul (stun ability of new champion skill tigerfist) +// + case CH_TIGERFIST: + if (MRAND (100) < (5 + skilllv * 5) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case LK_SPIRALPIERCE: + if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case ST_REJECTSWORD: /* フリージングトラップ */ + if (MRAND (100) < (10 + skilllv * 5)) + skill_status_change_start (bl, SC_AUTOCOUNTER, skilllv, 0, 0, + 0, skill_get_time2 (skillid, + skilllv), 0); + break; + case PF_FOGWALL: /* ホーリークロス */ + if (MRAND (100) < 3 * skilllv * sc_def_int / 100) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + { //条件が良く分からないので適当に + int race = battle_get_race (bl); + if (! + (battle_check_undead (race, battle_get_elem_type (bl)) + || race == 6) + && MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) + skill_status_change_start (bl, SC_HEADCRUSH, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + } + break; + case LK_JOINTBEAT: /* ジョイントビート */ + //条件が良く分からないので適当に + if (MRAND (100) < (2 * skilllv + 10) * sc_def_vit / 100) + skill_status_change_start (bl, SC_JOINTBEAT, 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では拘束時間半減? + sec = sec / 2; + battle_stopwalking (bl, 1); + skill_status_change_start (bl, SC_SPIDERWEB, skilllv, 0, 0, 0, + sec, 0); + } + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + if (MRAND (100) < (15 + skilllv * 5) * sc_def_vit / 100) //状態異常は詳細が分からないので適当に + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + if (MRAND (100) < (10 + 3 * skilllv) * sc_def_int / 100) + skill_status_change_start (bl, SC_BLIND, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + case MO_EXTREMITYFIST: /* 阿修羅覇凰拳 */ + //阿修羅を使うと5分間自然回復しないようになる + skill_status_change_start (src, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), 0); + break; + } + + if (sd && skillid != MC_CARTREVOLUTION && attack_type & BF_WEAPON) + { /* カードによる追加効果 */ + int i; + int sc_def_card = 100; + + for (i = SC_STONE; i <= SC_BLIND; i++) + { + //対象に状態異常 + if (i == SC_STONE || i == SC_FREEZE) + sc_def_card = sc_def_mdef; + else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) + sc_def_card = sc_def_vit; + else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) + sc_def_card = sc_def_int; + else if (i == SC_CURSE) + sc_def_card = sc_def_luk; + + if (!sd->state.arrow_atk) + { + if (MRAND (10000) < + (sd->addeff[i - SC_STONE]) * sc_def_card / 100) + { + if (battle_config.battle_log) + printf + ("PC %d skill_addeff: cardによる異常発動 %d %d\n", + sd->bl.id, i, sd->addeff[i - SC_STONE]); + skill_status_change_start (bl, i, 7, 0, 0, 0, + (i == + SC_CONFUSION) ? 10000 + + 7000 : + skill_get_time2 (sc2 + [i - + SC_STONE], + 7), 0); + } + } + else + { + if (MRAND (10000) < + (sd->addeff[i - SC_STONE] + + sd->arrow_addeff[i - SC_STONE]) * sc_def_card / 100) + { + if (battle_config.battle_log) + printf + ("PC %d skill_addeff: cardによる異常発動 %d %d\n", + sd->bl.id, i, sd->addeff[i - SC_STONE]); + skill_status_change_start (bl, i, 7, 0, 0, 0, + (i == + SC_CONFUSION) ? 10000 + + 7000 : + skill_get_time2 (sc2 + [i - + SC_STONE], + 7), 0); + } + } + //自分に状態異常 + if (i == SC_STONE || i == SC_FREEZE) + sc_def_card = sc_def_mdef2; + else if (i == SC_STAN || i == SC_POISON || i == SC_SILENCE) + sc_def_card = sc_def_vit2; + else if (i == SC_SLEEP || i == SC_CONFUSION || i == SC_BLIND) + sc_def_card = sc_def_int2; + else if (i == SC_CURSE) + sc_def_card = sc_def_luk2; + + if (!sd->state.arrow_atk) + { + if (MRAND (10000) < + (sd->addeff2[i - SC_STONE]) * sc_def_card / 100) + { + if (battle_config.battle_log) + printf + ("PC %d skill_addeff: cardによる異常発動 %d %d\n", + src->id, i, sd->addeff2[i - SC_STONE]); + skill_status_change_start (src, i, 7, 0, 0, 0, + (i == + SC_CONFUSION) ? 10000 + + 7000 : + skill_get_time2 (sc2 + [i - + SC_STONE], + 7), 0); + } + } + else + { + if (MRAND (10000) < + (sd->addeff2[i - SC_STONE] + + sd->arrow_addeff2[i - SC_STONE]) * sc_def_card / 100) + { + if (battle_config.battle_log) + printf + ("PC %d skill_addeff: cardによる異常発動 %d %d\n", + src->id, i, sd->addeff2[i - SC_STONE]); + skill_status_change_start (src, i, 7, 0, 0, 0, + (i == + SC_CONFUSION) ? 10000 + + 7000 : + skill_get_time2 (sc2 + [i - + SC_STONE], + 7), 0); + } + } + } + } + return 0; +} + +/*========================================================================= + スキル攻撃吹き飛ばし処理 +-------------------------------------------------------------------------*/ +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 ret, prev_state = MS_IDLE; + int moveblock; + struct map_session_data *sd = NULL; + struct mob_data *md = NULL; + struct skill_unit *su = NULL; + + nullpo_retr (0, src); + nullpo_retr (0, target); + + if (target->type == BL_PC) + { + nullpo_retr (0, sd = (struct map_session_data *) target); + } + else if (target->type == BL_MOB) + { + nullpo_retr (0, md = (struct mob_data *) target); + } + else if (target->type == BL_SKILL) + { + nullpo_retr (0, su = (struct skill_unit *) target); + } + else + return 0; + + if (!(count & 0x10000 && (sd || md || su))) + { /* 指定なしなら位置関係から方向を求める */ + dx = target->x - src->x; + dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); + dy = target->y - src->y; + dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); + } + if (dx == 0 && dy == 0) + { + int dir = battle_get_dir (target); + 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; + moveblock = (x / BLOCK_SIZE != nx / BLOCK_SIZE + || y / BLOCK_SIZE != ny / BLOCK_SIZE); + + if (count & 0x20000) + { + battle_stopwalking (target, 1); + if (sd) + { + sd->to_x = nx; + sd->to_y = ny; + sd->walktimer = 1; + clif_walkok (sd); + clif_movechar (sd); + } + else if (md) + { + md->to_x = nx; + md->to_y = ny; + prev_state = md->state.state; + md->state.state = MS_WALK; + clif_fixmobpos (md); + } + } + else + battle_stopwalking (target, 2); + + dx = nx - x; + dy = ny - y; + + if (sd) /* 画面外に出たので消去 */ + map_foreachinmovearea (clif_pcoutsight, target->m, x - AREA_SIZE, + y - AREA_SIZE, x + AREA_SIZE, y + AREA_SIZE, + dx, dy, 0, 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); + + if (su) + { + skill_unit_move_unit_group (su->group, target->m, dx, dy); + } + else + { +// struct status_change *sc_data=battle_get_sc_data(target); + if (moveblock) + map_delblock (target); + target->x = nx; + target->y = ny; + if (moveblock) + map_addblock (target); +/*ダンス中にエフェクトは移動しないらしい + if(sc_data && sc_data[SC_DANCING].timer!=-1){ //対象がダンス中なのでエフェクトも移動 + struct skill_unit_group *sg=(struct skill_unit_group *)sc_data[SC_DANCING].val2; + if(sg) + skill_unit_move_unit_group(sg,target->m,dx,dy); + } +*/ + } + + if (sd) + { /* 画面内に入ってきたので表示 */ + map_foreachinmovearea (clif_pcinsight, target->m, nx - AREA_SIZE, + ny - AREA_SIZE, nx + AREA_SIZE, ny + AREA_SIZE, + -dx, -dy, 0, sd); + if (count & 0x20000) + sd->walktimer = -1; + } + 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); + if (count & 0x20000) + md->state.state = prev_state; + } + + skill_unit_move (target, gettick (), (count & 0xffff) + 7); /* スキルユニットの判定 */ + + return 0; +} + +/* + * ========================================================================= + * スキル攻撃効果処理まとめ + * flagの説明。16進図 + * 00XRTTff + * ff = magicで計算に渡される) + * TT = パケットのtype部分(0でデフォルト) + * X = パケットのスキルLv + * R = 予約(skill_area_subで使用する) + *------------------------------------------------------------------------- + */ + +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; + int type, lv, damage; + + rdamage = 0; + nullpo_retr (0, src); + nullpo_retr (0, dsrc); + nullpo_retr (0, bl); + + sc_data = battle_get_sc_data (bl); + +//何もしない判定ここから + if (dsrc->m != bl->m) //対象が同じマップにいなければ何もしない + return 0; + if (src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL) //prevよくわからない※ + return 0; + if (src->type == BL_PC && pc_isdead ((struct map_session_data *) src)) //術者?がPCですでに死んでいたら何もしない + return 0; + if (dsrc->type == BL_PC && pc_isdead ((struct map_session_data *) dsrc)) //術者?がPCですでに死んでいたら何もしない + return 0; + if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) //対象がPCですでに死んでいたら何もしない + return 0; + if (skillnotok (skillid, (struct map_session_data *) bl)) + return 0; // [MouseJstr] + if (sc_data && sc_data[SC_HIDING].timer != -1) + { //ハイディング状態で + if (skill_get_pl (skillid) != 2) //スキルの属性が地属性でなければ何もしない + return 0; + } + if (sc_data && sc_data[SC_TRICKDEAD].timer != -1) //死んだふり中は何もしない + return 0; + if (skillid == WZ_STORMGUST) + { //使用スキルがストームガストで + if (sc_data && sc_data[SC_FREEZE].timer != -1) //凍結状態なら何もしない + return 0; + } + if (skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフロストノヴァで、dsrcとblが同じ場所なら何もしない + return 0; + if (src->type == BL_PC && ((struct map_session_data *) src)->chatID) //術者がPCでチャット中なら何もしない + return 0; + if (dsrc->type == BL_PC && ((struct map_session_data *) dsrc)->chatID) //術者がPCでチャット中なら何もしない + return 0; + if (src->type == BL_PC && bl + && mob_gvmobcheck (((struct map_session_data *) src), bl) == 0) + return 0; + +//何もしない判定ここまで + + type = -1; + lv = (flag >> 20) & 0xf; + dmg = battle_calc_attack (attack_type, src, bl, skillid, skilllv, flag & 0xff); //ダメージ計算 + +//マジックロッド処理ここから + if (attack_type & BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 + && src == dsrc) + { //魔法攻撃でマジックロッド状態でsrc=dsrcなら + dmg.damage = dmg.damage2 = 0; //ダメージ0 + if (bl->type == BL_PC) + { //対象がPCの場合 + 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 (((struct map_session_data *) bl)->status.sp + sp > + ((struct map_session_data *) bl)->status.max_sp) + { //回復SP+現在のSPがMSPより大きい場合 + sp = ((struct map_session_data *) bl)->status.max_sp - ((struct map_session_data *) bl)->status.sp; //SPをMSP-現在SPにする + ((struct map_session_data *) bl)->status.sp = ((struct map_session_data *) bl)->status.max_sp; //現在のSPにMSPを代入 + } + else //回復SP+現在のSPがMSPより小さい場合は回復SPを加算 + ((struct map_session_data *) bl)->status.sp += sp; + clif_heal (((struct map_session_data *) bl)->fd, SP_SP, sp); //SP回復エフェクトの表示 + ((struct map_session_data *) bl)->canact_tick = tick + skill_delayfix (bl, skill_get_delay (SA_MAGICROD, sc_data[SC_MAGICROD].val1)); // + } + 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_) //吹き飛ばし判定?※ + dmg.blewcount = 0; + + if (skillid == CR_GRANDCROSS) + { //グランドクロス + if (battle_config.gx_disptype) + dsrc = src; // 敵ダメージ白文字表示 + if (src == bl) + type = 4; // 反動はダメージモーションなし + } + +//使用者がPCの場合の処理ここから + if (src->type == BL_PC) + { + struct map_session_data *sd = (struct map_session_data *) src; + nullpo_retr (0, sd); +//連打掌(MO_CHAINCOMBO)ここから + if (skillid == MO_CHAINCOMBO) + { + int delay = 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); //基本ディレイの計算 + if (damage < battle_get_hp (bl)) + { //ダメージが対象のHPより小さい場合 + if (pc_checkskill (sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0) //猛龍拳(MO_COMBOFINISH)取得&気球保持時は+300ms + delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 + + skill_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); //コンボディレイパケットの送信 + } +//連打掌(MO_CHAINCOMBO)ここまで +//猛龍拳(MO_COMBOFINISH)ここから + else if (skillid == MO_COMBOFINISH) + { + int delay = + 700 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); + if (damage < battle_get_hp (bl)) + { + //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms + //伏虎拳(CH_TIGERFIST)取得時も+300ms + if ((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; //追加ディレイをconfにより調整 + + skill_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); //コンボディレイパケットの送信 + } +//猛龍拳(MO_COMBOFINISH)ここまで +//伏虎拳(CH_TIGERFIST)ここから + else if (skillid == CH_TIGERFIST) + { + int delay = + 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); + if (damage < battle_get_hp (bl)) + { + if (pc_checkskill (sd, CH_CHAINCRUSH) > 0) //連柱崩撃(CH_CHAINCRUSH)取得時は+300ms + delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 + + skill_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); //コンボディレイパケットの送信 + } +//伏虎拳(CH_TIGERFIST)ここまで +//連柱崩撃(CH_CHAINCRUSH)ここから + else if (skillid == CH_CHAINCRUSH) + { + int delay = + 1000 - 4 * battle_get_agi (src) - 2 * battle_get_dex (src); + if (damage < battle_get_hp (bl)) + { + //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms + if (pc_checkskill (sd, MO_EXTREMITYFIST) > 0 + && sd->spiritball >= 4 + && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) + delay += 300 * battle_config.combo_delay_rate / 100; //追加ディレイをconfにより調整 + + skill_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); //コンボディレイパケットの送信 + } +//連柱崩撃(CH_CHAINCRUSH)ここまで + } +//使用者がPCの場合の処理ここまで +//武器スキル?ここから + //AppleGirl Was Here + if (attack_type & BF_MAGIC && damage > 0 && src != bl && src == dsrc) + { //Blah Blah + if (bl->type == BL_PC) + { //Blah Blah + struct map_session_data *tsd = (struct map_session_data *) bl; + if (tsd->magic_damage_return > 0) + { //More Blah + rdamage += damage * tsd->magic_damage_return / 100; + if (rdamage < 1) + rdamage = 1; + } + } + } + //Stop Here + if (attack_type & BF_WEAPON && damage > 0 && src != bl && src == dsrc) + { //武器スキル&ダメージあり&使用者と対象者が違う&src=dsrc + if (dmg.flag & BF_SHORT) + { //近距離攻撃時?※ + if (bl->type == BL_PC) + { //対象がPCの時 + struct map_session_data *tsd = (struct map_session_data *) bl; + nullpo_retr (0, tsd); + if (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 (bl->type == BL_PC) + { //対象がPCの時 + struct map_session_data *tsd = (struct map_session_data *) bl; + nullpo_retr (0, tsd); + if (tsd->long_weapon_damage_return > 0) + { //遠距離攻撃跳ね返し?※ + rdamage += damage * tsd->long_weapon_damage_return / 100; + if (rdamage < 1) + rdamage = 1; + } + } + } + if (rdamage > 0) + clif_damage (src, src, tick, dmg.amotion, 0, rdamage, 1, 4, 0); + } +//武器スキル?ここまで + + switch (skillid) + { + case WZ_SIGHTRASHER: + clif_skill_damage (src, bl, tick, dmg.amotion, dmg.dmotion, + damage, dmg.div_, skillid, + (lv != 0) ? lv : skilllv, 5); + break; + case AS_SPLASHER: + clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, + damage, dmg.div_, skillid, -1, 5); + break; + case NPC_SELFDESTRUCTION: + case NPC_SELFDESTRUCTION2: + break; + default: + clif_skill_damage (dsrc, bl, tick, dmg.amotion, dmg.dmotion, + damage, dmg.div_, skillid, + (lv != 0) ? lv : skilllv, + (skillid == 0) ? 5 : type); + } + if (dmg.blewcount > 0 && !map[src->m].flag.gvg) + { /* 吹き飛ばし処理とそのパケット */ + if (skillid == WZ_SIGHTRASHER) + skill_blown (src, bl, dmg.blewcount); + else + skill_blown (dsrc, bl, dmg.blewcount); + if (bl->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) bl); + else + clif_fixpos (bl); + } + + map_freeblock_lock (); + /* 実際にダメージ処理を行う */ + if (skillid != KN_BOWLINGBASH || flag) + battle_damage (src, bl, damage, 0); + if (skillid == RG_INTIMIDATE && damage > 0 + && !(battle_get_mode (bl) & 0x20) && !map[src->m].flag.gvg) + { + int s_lv = battle_get_lv (src), t_lv = battle_get_lv (bl); + int rate = 50 + skilllv * 5; + rate = rate + (s_lv - t_lv); + if (MRAND (100) < rate) + skill_addtimerskill (src, tick + 800, bl->id, 0, 0, skillid, + skilllv, 0, flag); + } +/* + if(damage > 0 && dmg.flag&BF_SKILL && bl->type==BL_PC && pc_checkskill((struct map_session_data *)bl,RG_PLAGIARISM)){ + struct map_session_data *tsd = (struct map_session_data *)bl; + nullpo_retr(0, tsd); + if(!tsd->status.skill[skillid].id && !tsd->status.skill[skillid].id + && !(skillid > NPC_PIERCINGATT && skillid < NPC_SUMMONMONSTER) ){ + //既に盗んでいるスキルがあれば該当スキルを消す + if (tsd->cloneskill_id && tsd->cloneskill_lv && 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->cloneskill_lv=skilllv; + tsd->status.skill[skillid].id=skillid; + tsd->status.skill[skillid].lv=(pc_checkskill(tsd,RG_PLAGIARISM) > skill_get_max(skillid))? + skill_get_max(skillid):pc_checkskill(tsd,RG_PLAGIARISM); + tsd->status.skill[skillid].flag=13;//cloneskill flag + clif_skillinfoblock(tsd); + } + } +*/ + /* ダメージがあるなら追加効果判定 */ + if (bl->prev != NULL) + { + struct map_session_data *sd = (struct map_session_data *) bl; + nullpo_retr (0, sd); + if (bl->type != BL_PC || (sd && !pc_isdead (sd))) + { + if (damage > 0) + skill_additional_effect (src, bl, skillid, skilllv, + attack_type, tick); + if (bl->type == BL_MOB && src != bl) /* スキル使用条件のMOBスキル */ + { + struct mob_data *md = (struct mob_data *) bl; + nullpo_retr (0, md); + if (battle_config.mob_changetarget_byskill == 1) + { + int target; + target = md->target_id; + if (src->type == BL_PC) + 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 (src->type == BL_PC && dmg.flag & BF_WEAPON && src != bl && src == dsrc + && damage > 0) + { + struct map_session_data *sd = (struct map_session_data *) src; + int hp = 0, sp = 0; + nullpo_retr (0, sd); + if (sd->hp_drain_rate && sd->hp_drain_per > 0 && dmg.damage > 0 + && MRAND (100) < sd->hp_drain_rate) + { + hp += (dmg.damage * sd->hp_drain_per) / 100; + if (sd->hp_drain_rate > 0 && hp < 1) + hp = 1; + else if (sd->hp_drain_rate < 0 && hp > -1) + hp = -1; + } + if (sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 && dmg.damage2 > 0 + && MRAND (100) < sd->hp_drain_rate_) + { + hp += (dmg.damage2 * sd->hp_drain_per_) / 100; + if (sd->hp_drain_rate_ > 0 && hp < 1) + hp = 1; + else if (sd->hp_drain_rate_ < 0 && hp > -1) + hp = -1; + } + if (sd->sp_drain_rate > 0 && sd->sp_drain_per > 0 && dmg.damage > 0 + && MRAND (100) < sd->sp_drain_rate) + { + sp += (dmg.damage * sd->sp_drain_per) / 100; + if (sd->sp_drain_rate > 0 && sp < 1) + sp = 1; + else if (sd->sp_drain_rate < 0 && sp > -1) + sp = -1; + } + if (sd->sp_drain_rate_ > 0 && sd->sp_drain_per_ > 0 && dmg.damage2 > 0 + && MRAND (100) < sd->sp_drain_rate_) + { + sp += (dmg.damage2 * sd->sp_drain_per_) / 100; + if (sd->sp_drain_rate_ > 0 && sp < 1) + sp = 1; + else if (sd->sp_drain_rate_ < 0 && sp > -1) + sp = -1; + } + if (hp || sp) + pc_heal (sd, hp, sp); + } + + if ((skillid != KN_BOWLINGBASH || flag) && rdamage > 0) + battle_damage (bl, src, rdamage, 0); + + if (attack_type & BF_WEAPON && sc_data + && sc_data[SC_AUTOCOUNTER].timer != -1 + && sc_data[SC_AUTOCOUNTER].val4 > 0) + { + if (sc_data[SC_AUTOCOUNTER].val3 == dsrc->id) + battle_weapon_attack (bl, dsrc, tick, + 0x8000 | sc_data[SC_AUTOCOUNTER].val1); + skill_status_change_end (bl, SC_AUTOCOUNTER, -1); + } + + map_freeblock_unlock (); + + return (dmg.damage + dmg.damage2); /* 与ダメを返す */ +} + +/*========================================== + * スキル範囲攻撃用(map_foreachinareaから呼ばれる) + * flagについて:16進図を確認 + * MSB <- 00fTffff ->LSB + * T =ターゲット選択用(BCT_*) + * ffff=自由に使用可能 + * 0 =予約。0に固定 + *------------------------------------------ + */ +static int skill_area_temp[8]; /* 一時変数。必要なら使う。 */ +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, x, y, range, sx[4], sy[4]; + int t_range, tx[4], ty[4]; + int i, r_flag, 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; + + x = va_arg (ap, int); + y = va_arg (ap, int); + range = va_arg (ap, int); + skillid = va_arg (ap, int); + + if (skillid == MG_SAFETYWALL || skillid == AL_PNEUMA) + { + if (unit->group->unit_id != 0x7e && unit->group->unit_id != 0x85) + return 0; + } + else if (skillid == AL_WARP) + { + if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) + && unit->group->unit_id != 0x92) + return 0; + } + else if ((skillid >= HT_SKIDTRAP && skillid <= HT_CLAYMORETRAP) + || skillid == HT_TALKIEBOX) + { + if ((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) + && unit->group->unit_id != 0x92) + return 0; + } + else if (skillid == WZ_FIREPILLAR) + { + if (unit->group->unit_id != 0x87) + return 0; + } + else + return 0; + t_range = (unit->range != 0) ? unit->range : unit->group->range; + tx[0] = tx[3] = unit->bl.x - t_range; + tx[1] = tx[2] = unit->bl.x + t_range; + ty[0] = ty[1] = unit->bl.y - t_range; + ty[2] = ty[3] = unit->bl.y + t_range; + sx[0] = sx[3] = x - range; + sx[1] = sx[2] = x + range; + sy[0] = sy[1] = y - range; + sy[2] = sy[3] = y + range; + for (i = r_flag = 0; i < 4; i++) + { + if (sx[i] >= tx[0] && sx[i] <= tx[1] && sy[i] >= ty[0] + && sy[i] <= ty[2]) + { + r_flag = 1; + break; + } + if (tx[i] >= sx[0] && tx[i] <= sx[1] && ty[i] >= sy[0] + && ty[i] <= sy[2]) + { + r_flag = 1; + break; + } + } + if (r_flag) + (*c)++; + + return 0; +} + +int skill_check_unit_range (int m, int x, int y, int range, int skillid) +{ + int c = 0; + + map_foreachinarea (skill_check_unit_range_sub, m, x - 10, y - 10, x + 10, + y + 10, BL_SKILL, &c, x, y, range, skillid); + + return c; +} + +static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap) +{ + int *c; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, c = va_arg (ap, int *)); + + if (bl->prev == NULL || (bl->type != BL_PC && bl->type != BL_MOB)) + return 0; + + if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) + return 0; + + (*c)++; + + return 0; +} + +int skill_check_unit_range2 (int m, int x, int y, int range) +{ + int c = 0; + + map_foreachinarea (skill_check_unit_range2_sub, m, x - range, y - range, + x + range, y + range, 0, &c); + + return c; +} + +/*========================================================================= + * 範囲スキル使用処理小分けここから + */ +/* 対象の数をカウントする。(skill_area_temp[0]を初期化しておくこと) */ +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 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static void skill_timerskill (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = NULL; + struct mob_data *md = NULL; + struct block_list *src = map_id2bl (id), *target; + struct skill_timerskill *skl = NULL; + int range; + + nullpo_retv (src); + + if (src->prev == NULL) + return; + + if (src->type == BL_PC) + { + nullpo_retv (sd = (struct map_session_data *) src); + skl = &sd->skilltimerskill[data]; + } + else if (src->type == BL_MOB) + { + nullpo_retv (md = (struct mob_data *) src); + skl = &md->skilltimerskill[data]; + } + + else + return; + + nullpo_retv (skl); + + skl->timer = -1; + 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; //初期化してないのにアドレス突っ込んでいいのかな? + target->type = BL_NUL; + target->m = src->m; + target->prev = target->next = NULL; + } + } + if (target == NULL) + return; + if (target->prev == NULL && skl->skill_id != RG_INTIMIDATE) + return; + if (src->m != target->m) + return; + if (sd && pc_isdead (sd)) + return; + if (target->type == BL_PC + && pc_isdead ((struct map_session_data *) target) + && skl->skill_id != RG_INTIMIDATE) + return; + + switch (skl->skill_id) + { + case TF_BACKSLIDING: + clif_skill_nodamage (src, src, skl->skill_id, skl->skill_lv, + 1); + break; + case RG_INTIMIDATE: + if (sd && !map[src->m].flag.noteleport) + { + int x, y, i, j, c; + pc_randomwarp (sd, 3); + for (i = 0; i < 16; i++) + { + j = MRAND (8); + x = sd->bl.x + dirx[j]; + y = sd->bl.y + diry[j]; + if ((c = map_getcell (sd->bl.m, x, y)) != 1 && c != 5) + 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].name, 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, c; + mob_warp (md, -1, -1, -1, 3); + for (i = 0; i < 16; i++) + { + j = MRAND (8); + x = md->bl.x + dirx[j]; + y = md->bl.y + diry[j]; + if ((c = map_getcell (md->bl.m, x, y)) != 1 && c != 5) + 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].name, 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 = 15; //視界全体 + map_foreachinarea (skill_frostjoke_scream, src->m, + src->x - range, src->y - range, + src->x + range, src->y + range, 0, src, + skl->skill_id, skl->skill_lv, tick); + 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; + 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, + 0); + 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, 0); + break; + } + } +} + +/*========================================== + * + *------------------------------------------ + */ +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; + + 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; + } + + 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); + for (i = 0; i < MAX_SKILLTIMERSKILL; i++) + { + if (sd->skilltimerskill[i].timer != -1) + { + delete_timer (sd->skilltimerskill[i].timer, skill_timerskill); + sd->skilltimerskill[i].timer = -1; + } + } + } + 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; + } + } + } + + return 0; +} + +/* 範囲スキル使用処理小分けここまで + * ------------------------------------------------------------------------- + */ + +/*========================================== + * スキル使用(詠唱完了、ID指定攻撃系) + * (スパゲッティに向けて1歩前進!(ダメポ)) + *------------------------------------------ + */ +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; + int i; + + nullpo_retr (1, src); + nullpo_retr (1, bl); + + if (src->type == BL_PC) + sd = (struct map_session_data *) src; + if (sd && pc_isdead (sd)) + return 1; + + if ((skillid == WZ_SIGHTRASHER || skillid == CR_GRANDCROSS) && src != bl) + bl = src; + if (bl->prev == NULL) + return 1; + if (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl)) + return 1; + map_freeblock_lock (); + switch (skillid) + { + /* 武器攻撃系スキル */ + case SM_BASH: /* バッシュ */ + case MC_MAMMONITE: /* メマーナイト */ + case AC_DOUBLE: /* ダブルストレイフィング */ + case AS_SONICBLOW: /* ソニックブロー */ + case KN_PIERCE: /* ピアース */ + case KN_SPEARBOOMERANG: /* スピアブーメラン */ + case TF_POISON: /* インベナム */ + case TF_SPRINKLESAND: /* 砂まき */ + case AC_CHARGEARROW: /* チャージアロー */ + case KN_SPEARSTAB: /* スピアスタブ */ + case RG_RAID: /* サプライズアタック */ + case RG_INTIMIDATE: /* インティミデイト */ + case BA_MUSICALSTRIKE: /* ミュージカルストライク */ + case DC_THROWARROW: /* 矢撃ち */ + case BA_DISSONANCE: /* 不協和音 */ + case CR_HOLYCROSS: /* ホーリークロス */ + case CR_SHIELDCHARGE: + case CR_SHIELDBOOMERANG: + + /* 以下MOB専用 */ + /* 単体攻撃、SP減少攻撃、遠距離攻撃、防御無視攻撃、多段攻撃 */ + case NPC_PIERCINGATT: + case NPC_MENTALBREAKER: + case NPC_RANGEATTACK: + case NPC_CRITICALSLASH: + case NPC_COMBOATTACK: + /* 必中攻撃、毒攻撃、暗黒攻撃、沈黙攻撃、スタン攻撃 */ + case NPC_GUIDEDATTACK: + case NPC_POISON: + case NPC_BLINDATTACK: + case NPC_SILENCEATTACK: + case NPC_STUNATTACK: + /* 石化攻撃、呪い攻撃、睡眠攻撃、ランダムATK攻撃 */ + case NPC_PETRIFYATTACK: + case NPC_CURSEATTACK: + case NPC_SLEEPATTACK: + case NPC_RANDOMATTACK: + /* 水属性攻撃、地属性攻撃、火属性攻撃、風属性攻撃 */ + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + /* 毒属性攻撃、聖属性攻撃、闇属性攻撃、念属性攻撃、SP減少攻撃 */ + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + case LK_AURABLADE: /* オーラブレード */ + case LK_SPIRALPIERCE: /* スパイラルピアース */ + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + case LK_JOINTBEAT: /* ジョイントビート */ + case PA_PRESSURE: /* プレッシャー */ + case PA_SACRIFICE: /* サクリファイス */ + case SN_SHARPSHOOTING: /* シャープシューティング */ + case CG_ARROWVULCAN: /* アローバルカン */ + case ASC_BREAKER: /* ソウルブレーカー */ + case HW_MAGICCRASHER: /* マジッククラッシャー */ + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + break; + case NPC_DARKBREATH: + clif_emotion (src, 7); + skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, + flag); + break; + case MO_INVESTIGATE: /* 発勁 */ + { + struct status_change *sc_data = battle_get_sc_data (src); + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + if (sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end (src, SC_BLADESTOP, -1); + } + break; + case SN_FALCONASSAULT: /* ファルコンアサルト */ + skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, + flag); + break; + case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ + { + struct mob_data *md = (struct mob_data *) bl; + nullpo_retr (1, md); + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + if (md->hp > 0) + { + skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); + if (bl->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) bl); + else + clif_fixpos (bl); + } + } + break; + case RG_BACKSTAP: /* バックスタブ */ + { + int dir = map_calc_dir (src, bl->x, bl->y), t_dir = + battle_get_dir (bl); + int dist = distance (src->x, src->y, bl->x, bl->y); + if ((dist > 0 && !map_check_dir (dir, t_dir)) + || bl->type == BL_SKILL) + { + struct status_change *sc_data = battle_get_sc_data (src); + if (sc_data && sc_data[SC_HIDING].timer != -1) + skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + skill_blown (src, bl, skill_get_blewcount (skillid, skilllv)); + } + else if (src->type == BL_PC) + clif_skill_fail (sd, sd->skillid, 0, 0); + } + break; + + case AM_ACIDTERROR: /* アシッドテラー */ + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + if (bl->type == BL_PC + && MRAND (100) < skill_get_time (skillid, skilllv) + && battle_config.equipment_breaking) + pc_breakarmor ((struct map_session_data *) bl); + break; + case MO_FINGEROFFENSIVE: /* 指弾 */ + { + struct status_change *sc_data = battle_get_sc_data (src); + + if (!battle_config.finger_offensive_type) + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + else + { + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + if (sd) + { + 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) + skill_status_change_end (src, SC_BLADESTOP, -1); + } + break; + case MO_CHAINCOMBO: /* 連打掌 */ + { + struct status_change *sc_data = battle_get_sc_data (src); + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + if (sc_data && sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end (src, SC_BLADESTOP, -1); + } + break; + case MO_COMBOFINISH: /* 猛龍拳 */ + case CH_TIGERFIST: /* 伏虎拳 */ + case CH_CHAINCRUSH: /* 連柱崩撃 */ + case CH_PALMSTRIKE: /* 猛虎硬派山 */ + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + break; + case MO_EXTREMITYFIST: /* 阿修羅覇鳳拳 */ + { + struct status_change *sc_data = battle_get_sc_data (src); + + if (sd) + { + struct walkpath_data wpd; + int dx, dy; + + 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, sd->skillid, 0, 0); + break; + } + } + sd->to_x = sd->bl.x + dx; + sd->to_y = sd->bl.y + dy; + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + 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; + pc_movepos (sd, sd->to_x, sd->to_y); + skill_status_change_end (&sd->bl, SC_COMBO, -1); + } + else + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + flag); + skill_status_change_end (src, SC_EXPLOSIONSPIRITS, -1); + if (sc_data && sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end (src, SC_BLADESTOP, -1); + } + break; + /* 武器系範囲攻撃スキル */ + case AC_SHOWER: /* アローシャワー */ + case SM_MAGNUM: /* マグナムブレイク */ + case AS_GRIMTOOTH: /* グリムトゥース */ + case MC_CARTREVOLUTION: /* カートレヴォリューション */ + case NPC_SPLASHATTACK: /* スプラッシュアタック */ + case ASC_METEORASSAULT: /* メテオアサルト */ + case AS_SPLASHER: /* [Valaris] */ + if (flag & 1) + { + /* 個別にダメージを与える */ + if (bl->id != skill_area_temp[1]) + { + int dist = 0; + if (skillid == SM_MAGNUM) + { /* マグナムブレイクなら中心からの距離を計算 */ + int dx = abs (bl->x - skill_area_temp[2]); + int dy = abs (bl->y - skill_area_temp[3]); + dist = ((dx > dy) ? dx : dy); + } + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, + tick, 0x0500 | dist); + } + } + else + { + int ar = 1; + int x = bl->x, y = bl->y; + if (skillid == SM_MAGNUM) + { + x = src->x; + y = src->y; + } + else if (skillid == AC_SHOWER || skillid == ASC_METEORASSAULT) /* アローシャワー、メテオアサルト範囲5*5 */ + ar = 2; + else if (skillid == AS_SPLASHER) /* ベナムスプラッシャー範囲3*3 */ + ar = 1; + else if (skillid == NPC_SPLASHATTACK) /* スプラッシュアタックは範囲7*7 */ + ar = 3; + skill_area_temp[1] = bl->id; + skill_area_temp[2] = x; + skill_area_temp[3] = y; + /* まずターゲットに攻撃を加える */ + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, tick, + 0); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea (skill_area_sub, + bl->m, x - ar, y - ar, x + ar, y + ar, 0, + src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + 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 damage; + map_freeblock_lock (); + damage = + skill_attack (BF_WEAPON, src, src, bl, skillid, skilllv, + tick, 0); + if (damage > 0) + { + int i, c; /* 他人から聞いた動きなので間違ってる可能性大&効率が悪いっす>< */ + c = skill_get_blewcount (skillid, skilllv); + if (map[bl->m].flag.gvg) + c = 0; + for (i = 0; i < c; i++) + { + skill_blown (src, bl, 1); + if (bl->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) bl); + else + clif_fixpos (bl); + skill_area_temp[0] = 0; + map_foreachinarea (skill_area_sub, + bl->m, bl->x - 1, bl->y - 1, + bl->x + 1, bl->y + 1, 0, src, + skillid, skilllv, tick, + flag | BCT_ENEMY, + skill_area_sub_count); + if (skill_area_temp[0] > 1) + break; + } + skill_area_temp[1] = bl->id; + skill_area_temp[2] = bl->x; + skill_area_temp[3] = bl->y; + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea (skill_area_sub, + bl->m, bl->x - 1, bl->y - 1, bl->x + 1, + bl->y + 1, 0, src, skillid, skilllv, + tick, flag | BCT_ENEMY | 1, + skill_castend_damage_id); + battle_damage (src, bl, damage, 1); + if (rdamage > 0) + battle_damage (bl, src, rdamage, 0); + } + map_freeblock_unlock (); + } + break; + + case ALL_RESURRECTION: /* リザレクション */ + case PR_TURNUNDEAD: /* ターンアンデッド */ + if (bl->type != BL_PC + && battle_check_undead (battle_get_race (bl), + battle_get_elem_type (bl))) + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, + flag); + else + { + map_freeblock_unlock (); + return 1; + } + break; + + /* 魔法系スキル */ + case MG_SOULSTRIKE: /* ソウルストライク */ + case MG_COLDBOLT: /* コールドボルト */ + case MG_FIREBOLT: /* ファイアーボルト */ + case MG_LIGHTNINGBOLT: /* ライトニングボルト */ + case WZ_EARTHSPIKE: /* アーススパイク */ + case AL_HEAL: /* ヒール */ + case AL_HOLYLIGHT: /* ホーリーライト */ + case MG_FROSTDIVER: /* フロストダイバー */ + case WZ_JUPITEL: /* ユピテルサンダー */ + case NPC_MAGICALATTACK: /* MOB:魔法打撃攻撃 */ + case PR_ASPERSIO: /* アスペルシオ */ + 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) + skill_status_change_start (src, SC_WATERBALL, skilllv, bl->id, + 0, 0, 0, 0); + break; + + case PR_BENEDICTIO: /* 聖体降福 */ + if (battle_get_race (bl) == 1 || battle_get_race (bl) == 6) + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, + flag); + break; + + /* 魔法系範囲攻撃スキル */ + case MG_NAPALMBEAT: /* ナパームビート */ + case MG_FIREBALL: /* ファイヤーボール */ + if (flag & 1) + { + /* 個別にダメージを与える */ + if (bl->id != skill_area_temp[1]) + { + if (skillid == MG_FIREBALL) + { /* ファイヤーボールなら中心からの距離を計算 */ + int dx = abs (bl->x - skill_area_temp[2]); + int dy = abs (bl->y - skill_area_temp[3]); + skill_area_temp[0] = ((dx > dy) ? dx : dy); + } + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, + tick, skill_area_temp[0] | 0x0500); + } + } + else + { + int ar = (skillid == MG_NAPALMBEAT) ? 1 : 2; + skill_area_temp[1] = bl->id; + if (skillid == MG_NAPALMBEAT) + { /* ナパームでは先に数える */ + skill_area_temp[0] = 0; + map_foreachinarea (skill_area_sub, + bl->m, bl->x - 1, bl->y - 1, bl->x + 1, + bl->y + 1, 0, src, skillid, skilllv, + tick, flag | BCT_ENEMY, + skill_area_sub_count); + } + else + { + skill_area_temp[0] = 0; + skill_area_temp[2] = bl->x; + skill_area_temp[3] = bl->y; + } + /* まずターゲットに攻撃を加える */ + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, + skill_area_temp[0]); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea (skill_area_sub, + bl->m, bl->x - ar, bl->y - ar, bl->x + ar, + bl->y + ar, 0, 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 + { + int ar = (skillid == HW_NAPALMVULCAN) ? 1 : 2; + skill_area_temp[1] = bl->id; + if (skillid == HW_NAPALMVULCAN) + { + skill_area_temp[0] = 0; + map_foreachinarea (skill_area_sub, + bl->m, bl->x - 1, bl->y - 1, bl->x + 1, + bl->y + 1, 0, src, skillid, skilllv, + tick, flag | BCT_ENEMY, + skill_area_sub_count); + } + else + { + skill_area_temp[0] = 0; + skill_area_temp[2] = bl->x; + skill_area_temp[3] = bl->y; + } + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, + skill_area_temp[0]); + map_foreachinarea (skill_area_sub, + bl->m, bl->x - ar, bl->y - ar, bl->x + ar, + bl->y + ar, 0, src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + break; + + case WZ_FROSTNOVA: /* フロストノヴァ */ + skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); + skill_attack (BF_MAGIC, src, src, bl, skillid, skilllv, tick, + flag); + break; + + case WZ_SIGHTRASHER: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); + skill_status_change_end (src, SC_SIGHT, -1); + break; + + /* その他 */ + case HT_BLITZBEAT: /* ブリッツビート */ + if (flag & 1) + { + /* 個別にダメージを与える */ + if (bl->id != skill_area_temp[1]) + skill_attack (BF_MISC, src, src, bl, skillid, skilllv, + tick, + skill_area_temp[0] | (flag & 0xf00000)); + } + else + { + skill_area_temp[0] = 0; + skill_area_temp[1] = bl->id; + if (flag & 0xf00000) + map_foreachinarea (skill_area_sub, bl->m, bl->x - 1, + bl->y - 1, bl->x + 1, bl->y + 1, 0, + src, skillid, skilllv, tick, + flag | BCT_ENEMY, + skill_area_sub_count); + /* まずターゲットに攻撃を加える */ + skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, + skill_area_temp[0] | (flag & 0xf00000)); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea (skill_area_sub, + bl->m, bl->x - 1, bl->y - 1, bl->x + 1, + bl->y + 1, 0, src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + break; + + case CR_GRANDCROSS: /* グランドクロス */ + /* スキルユニット配置 */ + skill_castend_pos2 (src, bl->x, bl->y, skillid, skilllv, tick, 0); + if (sd) + sd->canmove_tick = tick + 1000; + else if (src->type == BL_MOB) + mob_changestate ((struct mob_data *) src, MS_DELAY, 1000); + break; + + case TF_THROWSTONE: /* 石投げ */ + case NPC_SMOKING: /* スモーキング */ + skill_attack (BF_MISC, src, src, bl, skillid, skilllv, tick, 0); + break; + + case NPC_SELFDESTRUCTION: /* 自爆 */ + case NPC_SELFDESTRUCTION2: /* 自爆2 */ + if (flag & 1) + { + /* 個別にダメージを与える */ + if (src->type == BL_MOB) + { + struct mob_data *mb = (struct mob_data *) src; + nullpo_retr (1, mb); + mb->hp = skill_area_temp[2]; + if (bl->id != skill_area_temp[1]) + skill_attack (BF_MISC, src, src, bl, + NPC_SELFDESTRUCTION, skilllv, tick, + flag); + mb->hp = 1; + } + } + else + { + struct mob_data *md; + if ((md = (struct mob_data *) src)) + { + skill_area_temp[1] = bl->id; + skill_area_temp[2] = battle_get_hp (src); + clif_skill_nodamage (src, src, NPC_SELFDESTRUCTION, -1, + 1); + map_foreachinarea (skill_area_sub, bl->m, bl->x - 5, + bl->y - 5, bl->x + 5, bl->y + 5, 0, + src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + battle_damage (src, src, md->hp, 0); + } + } + break; + + /* HP吸収/HP吸収魔法 */ + case NPC_BLOODDRAIN: + case NPC_ENERGYDRAIN: + { + int heal; + 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.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, 0, src, + skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + } + break; + + default: + map_freeblock_unlock (); + return 1; + } + map_freeblock_unlock (); + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、ID指定支援系) + *------------------------------------------ + */ +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, strip_fix, strip_time, strip_per; + int sc_dex, sc_luk; + //クラスチェンジ用ボスモンスターID + int changeclass[] = + { 1038, 1039, 1046, 1059, 1086, 1087, 1112, 1115, 1157, 1159, 1190, + 1272, 1312, 1373, 1492 + }; + int poringclass[] = { 1002 }; + + nullpo_retr (1, src); + nullpo_retr (1, bl); + + if (src->type == BL_PC) + sd = (struct map_session_data *) src; + else if (src->type == BL_MOB) + md = (struct mob_data *) src; + + sc_dex = battle_get_mdef (bl); + sc_luk = battle_get_luk (bl); + sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); + sc_def_vit = 100 - (3 + battle_get_vit (bl) + battle_get_luk (bl) / 3); + sc_def_mdef = 100 - (3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3); + strip_fix = battle_get_dex (src) - battle_get_dex (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 (sc_def_vit > 50) + sc_def_vit = 50; + if (sc_def_mdef > 50) + sc_def_mdef = 50; + } + if (sc_def_vit < 0) + sc_def_vit = 0; + if (sc_def_mdef < 0) + sc_def_mdef = 0; + if (strip_fix < 0) + strip_fix = 0; + + if (bl == NULL || bl->prev == NULL) + return 1; + if (sd && pc_isdead (sd)) + return 1; + if (dstsd && pc_isdead (dstsd) && skillid != ALL_RESURRECTION) + return 1; + if (battle_get_class (bl) == 1288) + return 1; + if (skillnotok (skillid, (struct map_session_data *) bl)) // [MouseJstr] + return 0; + + map_freeblock_lock (); + switch (skillid) + { + case AL_HEAL: /* ヒール */ + { + int heal = skill_calc_heal (src, skilllv); + int heal_get_jobexp; + int skill; + struct pc_base_job s_class; + + if (dstsd && dstsd->special_state.no_magic_damage) + heal = 0; /* 黄金蟲カード(ヒール量0) */ + if (sd) + { + s_class = pc_calc_base_job (sd->status.pc_class); + if ((skill = pc_checkskill (sd, HP_MEDITATIO)) > 0) // メディテイティオ + heal += heal * (skill * 2 / 100); + if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && s_class.job == 23 && sd->status.sex == 0) //自分も対象もPC、対象が自分のパートナー、自分がスパノビ、自分が♀なら + heal = heal * 2; //スパノビの嫁が旦那にヒールすると2倍になる + } + + clif_skill_nodamage (src, bl, skillid, heal, 1); + heal_get_jobexp = battle_heal (NULL, bl, heal, 0, 0); + + // JOB経験値獲得 + if (src->type == BL_PC && bl->type == BL_PC && heal > 0 + && src != bl && 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; + pc_gainexp ((struct map_session_data *) src, 0, + heal_get_jobexp); + } + } + break; + + case ALL_RESURRECTION: /* リザレクション */ + if (bl->type == BL_PC) + { + int per = 0; + struct map_session_data *tsd = (struct map_session_data *) bl; + nullpo_retr (1, tsd); + if ((map[bl->m].flag.pvp) && tsd->pvp_point < 0) + break; /* PVPで復活不可能状態 */ + + if (pc_isdead (tsd)) + { /* 死亡判定 */ + 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; + } + tsd->status.hp = tsd->status.max_hp * per / 100; + if (tsd->status.hp <= 0) + tsd->status.hp = 1; + if (tsd->special_state.restart_full_recover) + { /* オシリスカード */ + tsd->status.hp = tsd->status.max_hp; + tsd->status.sp = tsd->status.max_sp; + } + pc_setstand (tsd); + if (battle_config.pc_invincible_time > 0) + pc_setinvincibletimer (tsd, + battle_config.pc_invincible_time); + clif_updatestatus (tsd, SP_HP); + clif_resurrection (&tsd->bl, 1); + if (src != bl && sd && battle_config.resurrection_exp > 0) + { + int exp = 0, jexp = 0; + int lv = + tsd->status.base_level - sd->status.base_level, + jlv = + tsd->status.job_level - sd->status.job_level; + if (lv > 0) + { + exp = + (int) ((double) tsd->status.base_exp * + (double) lv * + (double) battle_config.resurrection_exp + / 1000000.); + if (exp < 1) + exp = 1; + } + if (jlv > 0) + { + jexp = + (int) ((double) tsd->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 (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (MRAND (100) < + (50 + skilllv * 3 + + (battle_get_lv (src) + battle_get_int (src) / 5) - + sc_def_mdef)) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), + 0); + } + break; + + case AL_CRUCIS: + if (flag & 1) + { + int race = battle_get_race (bl), ele = + battle_get_elem_type (bl); + if (battle_check_target (src, bl, BCT_ENEMY) + && (race == 6 || battle_check_undead (race, ele))) + { + int slv = battle_get_lv (src), tlv = + battle_get_lv (bl), rate; + rate = 25 + skilllv * 2 + slv - tlv; + if (MRAND (100) < rate) + skill_status_change_start (bl, + SkillStatusChangeTable + [skillid], skilllv, 0, 0, + 0, 0, 0); + } + } + else + { + int range = 15; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + map_foreachinarea (skill_area_sub, + src->m, src->x - range, src->y - range, + src->x + range, src->y + range, 0, src, + skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_nodamage_id); + } + break; + + case PR_LEXDIVINA: /* レックスディビーナ */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (sc_data && sc_data[SC_DIVINA].timer != -1) + skill_status_change_end (bl, SC_DIVINA, -1); + else if (MRAND (100) < sc_def_vit) + { + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), + 0); + } + } + break; + case SA_ABRACADABRA: + break; + case SA_COMA: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (dstsd) + { + dstsd->status.hp = 1; + dstsd->status.sp = 1; + clif_updatestatus (dstsd, SP_HP); + clif_updatestatus (dstsd, SP_SP); + } + if (dstmd) + dstmd->hp = 1; + break; + case SA_FULLRECOVERY: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (dstsd) + pc_heal (dstsd, dstsd->status.max_hp, dstsd->status.max_sp); + if (dstmd) + dstmd->hp = battle_get_max_hp (&dstmd->bl); + break; + case SA_SUMMONMONSTER: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (sd) + mob_once_spawn (sd, map[sd->bl.m].name, sd->bl.x, sd->bl.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); + if (sd) + pc_damage (NULL, sd, sd->status.max_hp); + break; + + case SA_QUESTION: + case SA_GRAVITY: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + break; + case SA_CLASSCHANGE: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (dstmd) + mob_class_change (dstmd, changeclass); + break; + case SA_MONOCELL: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (dstmd) + mob_class_change (dstmd, poringclass); + break; + case SA_DEATH: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (dstsd) + pc_damage (NULL, dstsd, dstsd->status.max_hp); + if (dstmd) + mob_damage (NULL, dstmd, dstmd->hp, 1); + break; + case SA_REVERSEORCISH: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (dstsd) + pc_setoption (dstsd, dstsd->status.option | 0x0800); + break; + case SA_FORTUNE: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (sd) + pc_getzeny (sd, battle_get_lv (bl) * 100); + break; + case AL_INCAGI: /* 速度増加 */ + case AL_BLESSING: /* ブレッシング */ + case PR_SLOWPOISON: + case PR_IMPOSITIO: /* イムポシティオマヌス */ + case PR_LEXAETERNA: /* レックスエーテルナ */ + case PR_SUFFRAGIUM: /* サフラギウム */ + case PR_BENEDICTIO: /* 聖体降福 */ + case CR_PROVIDENCE: /* プロヴィデンス */ + case CG_MARIONETTE: /* マリオネットコントロール */ + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + } + else + { + skill_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_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] + case SA_FROSTWEAPON: + case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + break; + } + if (bl->type == BL_PC) + { + struct map_session_data *sd2 = (struct map_session_data *) bl; + if (sd2->status.weapon == 0 + || sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 + || sd2->sc_data[SC_FROSTWEAPON].timer != -1 + || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 + || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 + || sd2->sc_data[SC_ENCPOISON].timer != -1) + { + clif_skill_fail (sd, skillid, 0, 0); + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + break; + } + } + if (MRAND (100) > (75 + skilllv * 1) && (skilllv != 5)) + { + clif_skill_fail (sd, skillid, 0, 0); + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + if (bl->type == BL_PC && battle_config.equipment_breaking) + { + struct map_session_data *sd2 = + (struct map_session_data *) bl; + if (sd != sd2) + clif_displaymessage (sd->fd, + "You broke target's weapon"); + pc_breakweapon (sd2); + } + break; + } + else + { + skill_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: /* アスペルシオ */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (bl->type == BL_MOB) + break; + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case PR_KYRIE: /* キリエエレイソン */ + clif_skill_nodamage (bl, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case KN_AUTOCOUNTER: /* オートカウンター */ + case KN_TWOHANDQUICKEN: /* ツーハンドクイッケン */ + case CR_SPEARQUICKEN: /* スピアクイッケン */ + case CR_REFLECTSHIELD: + case AS_POISONREACT: /* ポイズンリアクト */ + case MC_LOUD: /* ラウドボイス */ + case MG_ENERGYCOAT: /* エナジーコート */ + case SM_ENDURE: /* インデュア */ + case MG_SIGHT: /* サイト */ + case AL_RUWACH: /* ルアフ */ + case MO_EXPLOSIONSPIRITS: // 爆裂波動 + case MO_STEELBODY: // 金剛 + case LK_AURABLADE: /* オーラブレード */ + case LK_PARRYING: /* パリイング */ + case LK_CONCENTRATION: /* コンセントレーション */ + case LK_BERSERK: /* バーサーク */ + case HP_ASSUMPTIO: /* */ + case WS_CARTBOOST: /* カートブースト */ + case SN_SIGHT: /* トゥルーサイト */ + case WS_MELTDOWN: /* メルトダウン */ + case ST_REJECTSWORD: /* リジェクトソード */ + case HW_MAGICPOWER: /* 魔法力増幅 */ + case PF_MEMORIZE: /* メモライズ */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case AS_ENCHANTPOISON: // Prevent spamming [Valaris] + if (bl->type == BL_PC) + { + struct map_session_data *sd2 = (struct map_session_data *) bl; + if (sd2->sc_data[SC_FLAMELAUNCHER].timer != -1 + || sd2->sc_data[SC_FROSTWEAPON].timer != -1 + || sd2->sc_data[SC_LIGHTNINGLOADER].timer != -1 + || sd2->sc_data[SC_SEISMICWEAPON].timer != -1 + || sd2->sc_data[SC_ENCPOISON].timer != -1) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + clif_skill_fail (sd, skillid, 0, 0); + break; + } + } + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case LK_TENSIONRELAX: /* テンションリラックス */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + pc_setsit (sd); + clif_sitting (sd->fd, sd); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case MC_CHANGECART: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + break; + case AC_CONCENTRATION: /* 集中力向上 */ + { + int range = 1; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + map_foreachinarea (skill_status_change_timer_sub, src->m, + src->x - range, src->y - range, src->x + range, + src->y + range, 0, src, + SkillStatusChangeTable[skillid], tick); + } + break; + case SM_PROVOKE: /* プロボック */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + + /* MVPmobと不死には効かない */ + if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない + { + map_freeblock_unlock (); + return 1; + } + + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + + if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 + skill_castcancel (bl, 0); + if (dstsd && dstsd->skilltimer != -1 + && (!dstsd->special_state.no_castcancel + || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel + && !dstsd->special_state.no_castcancel2) + skill_castcancel (bl, 0); + + if (sc_data) + { + if (sc_data[SC_FREEZE].timer != -1) + skill_status_change_end (bl, SC_FREEZE, -1); + if (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0) + skill_status_change_end (bl, SC_STONE, -1); + if (sc_data[SC_SLEEP].timer != -1) + skill_status_change_end (bl, SC_SLEEP, -1); + } + + if (bl->type == BL_MOB) + { + int range = skill_get_range (skillid, skilllv); + if (range < 0) + range = battle_get_range (src) - (range + 1); + mob_target ((struct mob_data *) bl, src, range); + } + } + break; + + case CR_DEVOTION: /* ディボーション */ + if (sd && dstsd) + { + //転生や養子の場合の元の職業を算出する + + int lv = sd->status.base_level - dstsd->status.base_level; + lv = (lv < 0) ? -lv : lv; + if ((dstsd->bl.type != BL_PC) // 相手はPCじゃないとだめ + || (sd->bl.id == dstsd->bl.id) // 相手が自分はだめ + || (lv > 10) // レベル差±10まで + || (!sd->status.party_id && !sd->status.guild_id) // PTにもギルドにも所属無しはだめ + || ((sd->status.party_id != dstsd->status.party_id) // 同じパーティーか、 + || (sd->status.guild_id != dstsd->status.guild_id)) // 同じギルドじゃないとだめ + || (dstsd->status.pc_class == 14 || dstsd->status.pc_class == 21 + || dstsd->status.pc_class == 4015 + || dstsd->status.pc_class == 4022)) + { // クルセだめ + clif_skill_fail (sd, skillid, 0, 0); + map_freeblock_unlock (); + return 1; + } + for (i = 0; i < skilllv; i++) + { + if (!sd->dev.val1[i]) + { // 空きがあったら入れる + sd->dev.val1[i] = bl->id; + sd->dev.val2[i] = bl->id; + break; + } + else if (i == skilllv - 1) + { // 空きがなかった + clif_skill_fail (sd, skillid, 0, 0); + map_freeblock_unlock (); + return 1; + } + } + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + clif_devotion (sd, bl->id); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + src->id, 1, 0, 0, + 1000 * (15 + 15 * skilllv), 0); + } + else + 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_BLADESTOP: // 白刃取り + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (src, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case MO_ABSORBSPIRITS: // 気奪 + i = 0; + if (sd && dstsd) + { + if (sd == dstsd || map[sd->bl.m].flag.pvp + || map[sd->bl.m].flag.gvg) + { + if (dstsd->spiritball > 0) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + i = dstsd->spiritball * 7; + pc_delspiritball (dstsd, dstsd->spiritball, 0); + if (i > 0x7FFF) + i = 0x7FFF; + if (sd->status.sp + i > sd->status.max_sp) + i = sd->status.max_sp - sd->status.sp; + } + } + } + else if (sd && dstmd) + { //対象がモンスターの場合 + //20%の確率で対象のLv*2のSPを回復する。成功したときはターゲット(σ゚Д゚)σゲッツ!! + if (MRAND (100) < 20) + { + i = 2 * mob_db[dstmd->mob_class].lv; + mob_target (dstmd, src, 0); + } + } + if (i) + { + sd->status.sp += i; + clif_heal (sd->fd, SP_SP, i); + } + else + 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,32); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + }*/ + break; + case WS_CREATECOIN: /* クリエイトコイン */ +/* if(sd) { + clif_skill_produce_mix_list(sd,64); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + }*/ + break; + case WS_CREATENUGGET: /* 塊製造 */ +/* if(sd) { + clif_skill_produce_mix_list(sd,128); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + }*/ + break; + case BS_HAMMERFALL: /* ハンマーフォール */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_weapon_damage) + break; + if (MRAND (100) < (20 + 10 * skilllv) * sc_def_vit / 100) + { + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + } + break; + + case RG_RAID: /* サプライズアタック */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + { + int x = bl->x, y = bl->y; + skill_area_temp[1] = bl->id; + skill_area_temp[2] = x; + skill_area_temp[3] = y; + map_foreachinarea (skill_area_sub, + bl->m, x - 1, y - 1, x + 1, y + 1, 0, + src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + skill_status_change_end (src, SC_HIDING, -1); // ハイディング解除 + 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], 0, 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], 0, 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], 0, 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)) + { + /* 個別の処理 */ + clif_skill_nodamage (bl, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), + 0); + } + else + { + /* パーティ全体への処理 */ + 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_WEAPONPERFECT: /* ウェポンパーフェクション */ + case BS_OVERTHRUST: /* オーバートラスト */ + if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) + { + /* 個別の処理 */ + clif_skill_nodamage (bl, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, (src == bl) ? 1 : 0, 0, 0, + skill_get_time (skillid, skilllv), + 0); + } + else + { + /* パーティ全体への処理 */ + 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: /* オートガード */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + int sc = SkillStatusChangeTable[skillid]; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (tsc_data) + { + if (tsc_data[sc].timer == -1) + /* 付加する */ + skill_status_change_start (bl, sc, skilllv, 0, 0, 0, + skill_get_time (skillid, + skilllv), 0); + else + /* 解除する */ + skill_status_change_end (bl, sc, -1); + } + } + break; + + case TF_HIDING: /* ハイディング */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + int sc = SkillStatusChangeTable[skillid]; + clif_skill_nodamage (src, bl, skillid, -1, 1); + if (tsc_data) + { + if (tsc_data[sc].timer == -1) + /* 付加する */ + skill_status_change_start (bl, sc, skilllv, 0, 0, 0, + skill_get_time (skillid, + skilllv), 0); + else + /* 解除する */ + skill_status_change_end (bl, sc, -1); + } + } + break; + + case AS_CLOAKING: /* クローキング */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + int sc = SkillStatusChangeTable[skillid]; + clif_skill_nodamage (src, bl, skillid, -1, 1); + if (tsc_data) + { + if (tsc_data[sc].timer == -1) + /* 付加する */ + skill_status_change_start (bl, sc, skilllv, 0, 0, 0, + skill_get_time (skillid, + skilllv), 0); + else + /* 解除する */ + skill_status_change_end (bl, sc, -1); + } + + skill_check_cloaking (bl); + } + break; + + case ST_CHASEWALK: /* ハイディング */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + int sc = SkillStatusChangeTable[skillid]; + clif_skill_nodamage (src, bl, skillid, -1, 1); + if (tsc_data) + { + if (tsc_data[sc].timer == -1) + /* 付加する */ + skill_status_change_start (bl, sc, skilllv, 0, 0, 0, + skill_get_time (skillid, + skilllv), 0); + else + /* 解除する */ + skill_status_change_end (bl, sc, -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: /* 不死身のジークフリード */ + case BA_DISSONANCE: /* 不協和音 */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); + break; + + case HP_BASILICA: /* バジリカ */ + case PA_GOSPEL: /* ゴスペル */ + skill_clear_unitgroup (src); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_unitsetting (src, skillid, skilllv, src->x, src->y, 0); + break; + + case BD_ADAPTATION: /* アドリブ */ + { + struct status_change *sc_data = battle_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, 0); + } + } + break; + + case BA_FROSTJOKE: /* 寒いジョーク */ + case DC_SCREAM: /* スクリーム */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_addtimerskill (src, tick + 3000, bl->id, 0, 0, skillid, + skilllv, 0, flag); + break; + + case TF_STEAL: // スティール + if (sd) + { + if (pc_steal_item (sd, bl)) + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + else + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + } + break; + + case RG_STEALCOIN: // スティールコイン + if (sd) + { + if (pc_steal_coin (sd, bl)) + { + int range = skill_get_range (skillid, skilllv); + if (range < 0) + range = battle_get_range (src) - (range + 1); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + mob_target ((struct mob_data *) bl, src, range); + } + else + clif_skill_nodamage (src, bl, skillid, skilllv, 0); + } + break; + + case MG_STONECURSE: /* ストーンカース */ + if (bl->type == BL_MOB && battle_get_mode (bl) & 0x20) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + break; + } + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (MRAND (100) < skilllv * 4 + 20 + && !battle_check_undead (battle_get_race (bl), + battle_get_elem_type (bl))) + skill_status_change_start (bl, SC_STONE, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + else if (sd) + clif_skill_fail (sd, skillid, 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: /* キュアー */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_status_change_end (bl, SC_SILENCE, -1); + skill_status_change_end (bl, SC_BLIND, -1); + skill_status_change_end (bl, SC_CONFUSION, -1); + if (battle_check_undead + (battle_get_race (bl), battle_get_elem_type (bl))) + { //アンデッドなら暗闇効果 + skill_status_change_start (bl, SC_CONFUSION, 1, 0, 0, 0, 6000, + 0); + } + break; + + case TF_DETOXIFY: /* 解毒 */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_end (bl, SC_POISON, -1); + break; + + case PR_STRECOVERY: /* リカバリー */ + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_status_change_end (bl, SC_FREEZE, -1); + skill_status_change_end (bl, SC_STONE, -1); + skill_status_change_end (bl, SC_SLEEP, -1); + skill_status_change_end (bl, SC_STAN, -1); + if (battle_check_undead + (battle_get_race (bl), battle_get_elem_type (bl))) + { //アンデッドなら暗闇効果 + int blind_time; + //blind_time=30-battle_get_vit(bl)/10-battle_get_int/15; + blind_time = + 30 * (100 - + (battle_get_int (bl) + + battle_get_vit (bl)) / 2) / 100; + if (MRAND (100) < + (100 - + (battle_get_int (bl) / 2 + battle_get_vit (bl) / 3 + + battle_get_luk (bl) / 10))) + skill_status_change_start (bl, SC_BLIND, 1, 0, 0, 0, + blind_time, 0); + } + if (dstmd) + { + dstmd->attacked_id = 0; + dstmd->target_id = 0; + dstmd->state.targettype = NONE_ATTACKABLE; + dstmd->state.skillstate = MSS_IDLE; + dstmd->next_walktime = tick + MRAND (3000) + 3000; + } + } + break; + + case WZ_ESTIMATION: /* モンスター情報 */ + if (src->type == BL_PC) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + clif_skill_estimation ((struct map_session_data *) src, bl); + } + break; + + case MC_IDENTIFY: /* アイテム鑑定 */ + if (sd) + clif_item_identify_list (sd); + break; + + case BS_REPAIRWEAPON: /* 武器修理 */ + if (sd) +//動作しないのでとりあえずコメントアウト +// clif_item_repair_list(sd); + break; + + case AL_TELEPORT: /* テレポート */ + if (sd) + { + if (map[sd->bl.m].flag.noteleport) + { /* テレポ禁止 */ + clif_skill_teleportmessage (sd, 0); + break; + } + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (sd->skilllv == 1) + clif_skill_warppoint (sd, sd->skillid, "Random", "", "", + ""); + else + { + clif_skill_warppoint (sd, sd->skillid, "Random", + sd->status.save_point.map, "", ""); + } + } + else if (bl->type == BL_MOB) + mob_warp ((struct mob_data *) bl, -1, -1, -1, 3); + break; + + case AL_HOLYWATER: /* アクアベネディクタ */ + if (sd) + { + int eflag; + struct item item_tmp; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + memset (&item_tmp, 0, sizeof (item_tmp)); + item_tmp.nameid = 523; + item_tmp.identify = 1; + if (battle_config.holywater_name_input) + { + item_tmp.card[0] = 0xfe; + item_tmp.card[1] = 0; + *((unsigned long *) (&item_tmp.card[2])) = sd->char_id; /* キャラID */ + } + 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 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 RG_STRIPWEAPON: /* ストリップウェポン */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + + if (tsc_data && tsc_data[SC_CP_WEAPON].timer != -1) + break; + strip_per = 5 + 2 * skilllv + strip_fix / 5; + strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; + if (MRAND (100) < strip_per) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, strip_time, 0); + if (dstsd) + { + for (i = 0; i < MAX_INVENTORY; i++) + { + if (dstsd->status.inventory[i].equip + && dstsd->status.inventory[i].equip & 0x0002) + { + pc_unequipitem (dstsd, i, 0); + break; + } + } + } + } + } + break; + + case RG_STRIPSHIELD: /* ストリップシールド */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + + if (tsc_data && tsc_data[SC_CP_SHIELD].timer != -1) + break; + strip_per = 5 + 2 * skilllv + strip_fix / 5; + strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; + if (MRAND (100) < strip_per) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, strip_time, 0); + if (dstsd) + { + for (i = 0; i < MAX_INVENTORY; i++) + { + if (dstsd->status.inventory[i].equip + && dstsd->status.inventory[i].equip & 0x0020) + { + pc_unequipitem (dstsd, i, 0); + break; + } + } + } + } + } + break; + + case RG_STRIPARMOR: /* ストリップアーマー */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + + if (tsc_data && tsc_data[SC_CP_ARMOR].timer != -1) + break; + strip_per = 5 + 2 * skilllv + strip_fix / 5; + strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; + if (MRAND (100) < strip_per) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, strip_time, 0); + if (dstsd) + { + for (i = 0; i < MAX_INVENTORY; i++) + { + if (dstsd->status.inventory[i].equip + && dstsd->status.inventory[i].equip & 0x0010) + { + pc_unequipitem (dstsd, i, 0); + break; + } + } + } + } + } + break; + case RG_STRIPHELM: /* ストリップヘルム */ + { + struct status_change *tsc_data = battle_get_sc_data (bl); + + if (tsc_data && tsc_data[SC_CP_HELM].timer != -1) + break; + strip_per = 5 + 2 * skilllv + strip_fix / 5; + strip_time = skill_get_time (skillid, skilllv) + strip_fix / 2; + if (MRAND (100) < strip_per) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, + SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, strip_time, 0); + if (dstsd) + { + for (i = 0; i < MAX_INVENTORY; i++) + { + if (dstsd->status.inventory[i].equip + && dstsd->status.inventory[i].equip & 0x0100) + { + pc_unequipitem (dstsd, i, 0); + break; + } + } + } + } + } + break; + /* PotionPitcher */ + case AM_POTIONPITCHER: /* ポーションピッチャー */ + { + struct block_list tbl; + int i, x, hp = 0, sp = 0; + if (sd) + { + if (sd == dstsd) + { // cancel use on oneself + map_freeblock_unlock (); + return 1; + } + 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; + } + sd->state.potionpitcher_flag = 1; + sd->potion_hp = sd->potion_sp = sd->potion_per_hp = + sd->potion_per_sp = 0; + sd->skilltarget = bl->id; + run_script (sd->inventory_data[i]->use_script, 0, sd->bl.id, + 0); + pc_delitem (sd, i, skill_db[skillid].amount[x], 0); + sd->state.potionpitcher_flag = 0; + if (sd->potion_per_hp > 0 || sd->potion_per_sp > 0) + { + hp = battle_get_max_hp (bl) * sd->potion_per_hp / 100; + hp = hp * (100 + + pc_checkskill (sd, + AM_POTIONPITCHER) * 10 + + pc_checkskill (sd, + AM_LEARNINGPOTION) * 5) / 100; + if (dstsd) + { + sp = dstsd->status.max_sp * sd->potion_per_sp / 100; + sp = sp * (100 + + pc_checkskill (sd, + AM_POTIONPITCHER) + + pc_checkskill (sd, + AM_LEARNINGPOTION) * 5) / + 100; + } + } + else + { + if (sd->potion_hp > 0) + { + hp = sd->potion_hp * (100 + + pc_checkskill (sd, + AM_POTIONPITCHER) + * 10 + pc_checkskill (sd, + AM_LEARNINGPOTION) + * 5) / 100; + hp = hp * (100 + (battle_get_vit (bl) << 1)) / 100; + if (dstsd) + hp = hp * (100 + + pc_checkskill (dstsd, + SM_RECOVERY) * 10) / + 100; + } + if (sd->potion_sp > 0) + { + sp = sd->potion_sp * (100 + + pc_checkskill (sd, + AM_POTIONPITCHER) + + pc_checkskill (sd, + AM_LEARNINGPOTION) + * 5) / 100; + sp = sp * (100 + (battle_get_int (bl) << 1)) / 100; + if (dstsd) + sp = sp * (100 + + pc_checkskill (dstsd, + MG_SRECOVERY) * 10) / + 100; + } + } + } + else + { + hp = (1 + MRAND (400)) * (100 + skilllv * 10) / 100; + hp = hp * (100 + (battle_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 || (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: + { + struct status_change *tsc_data = battle_get_sc_data (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (tsc_data && tsc_data[SC_STRIPWEAPON].timer != -1) + skill_status_change_end (bl, SC_STRIPWEAPON, -1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + } + break; + case AM_CP_SHIELD: + { + struct status_change *tsc_data = battle_get_sc_data (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (tsc_data && tsc_data[SC_STRIPSHIELD].timer != -1) + skill_status_change_end (bl, SC_STRIPSHIELD, -1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + } + break; + case AM_CP_ARMOR: + { + struct status_change *tsc_data = battle_get_sc_data (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (tsc_data && tsc_data[SC_STRIPARMOR].timer != -1) + skill_status_change_end (bl, SC_STRIPARMOR, -1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + } + break; + case AM_CP_HELM: + { + struct status_change *tsc_data = battle_get_sc_data (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (tsc_data && tsc_data[SC_STRIPHELM].timer != -1) + skill_status_change_end (bl, SC_STRIPHELM, -1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + } + break; + case SA_DISPELL: /* ディスペル */ + { + int i; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + for (i = 0; i < 136; i++) + { + if (i == SC_RIDING || i == SC_FALCON || 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) + continue; + skill_status_change_end (bl, i, -1); + } + } + break; + + case TF_BACKSLIDING: /* バックステップ */ + battle_stopwalking (src, 1); + skill_blown (src, bl, + skill_get_blewcount (skillid, skilllv) | 0x10000); + if (src->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) src); + else if (src->type == BL_PC) + clif_fixpos (src); + skill_addtimerskill (src, tick + 200, src->id, 0, 0, skillid, + skilllv, 0, flag); + 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 = battle_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; + if (bl->type == BL_PC) + { + if (dstsd && dstsd->skilltimer != -1) + { + bl_skillid = dstsd->skillid; + bl_skilllv = dstsd->skilllv; + } + } + else if (bl->type == BL_MOB) + { + if (dstmd && dstmd->skilltimer != -1) + { + bl_skillid = dstmd->skillid; + bl_skilllv = dstmd->skilllv; + } + } + if (bl_skillid > 0 + && skill_db[bl_skillid].skill_type == BF_MAGIC) + { + 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, 0, -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; + clif_heal (sd->fd, SP_SP, sp); + } + } + else if (sd) + clif_skill_fail (sd, skillid, 0, 0); + } + } + break; + case SA_MAGICROD: + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_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; + 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 = MRAND (3); + spellid = spellarray[i]; + maxlv = skilllv - 1; + } + else if (skilllv > 0) + { + spellid = MG_NAPALMBEAT; + maxlv = 3; + } + if (spellid > 0) + skill_status_change_start (src, SC_AUTOSPELL, skilllv, + spellid, maxlv, 0, + skill_get_time (SA_AUTOSPELL, + skilllv), 0); + } + break; + + /* ランダム属性変化、水属性変化、地、火、風 */ + case NPC_ATTRICHANGE: + case NPC_CHANGEWATER: + case NPC_CHANGEGROUND: + case NPC_CHANGEFIRE: + case NPC_CHANGEWIND: + /* 毒、聖、念、闇 */ + case NPC_CHANGEPOISON: + case NPC_CHANGEHOLY: + case NPC_CHANGEDARKNESS: + case NPC_CHANGETELEKINESIS: + if (md) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + md->def_ele = skill_get_pl (skillid); + if (md->def_ele == 0) /* ランダム変化、ただし、 */ + md->def_ele = MRAND (10); /* 不死属性は除く */ + md->def_ele += (1 + MRAND (4)) * 20; /* 属性レベルはランダム */ + } + break; + + case NPC_HALLUCINATION: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + + case NPC_KEEPING: + case NPC_BARRIER: + { + int skill_time = skill_get_time (skillid, skilllv); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, skill_time, 0); + mob_changestate ((struct mob_data *) src, MS_DELAY, skill_time); + } + break; + + case NPC_DARKBLESSING: + { + int sc_def = 100 - battle_get_mdef (bl); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (battle_get_elem_type (bl) == 7 || battle_get_race (bl) == 6) + break; + if (MRAND (100) < sc_def * (50 + skilllv * 5) / 100) + { + if (dstsd) + { + int hp = battle_get_hp (bl) - 1; + pc_heal (dstsd, -hp, 0); + } + else if (dstmd) + dstmd->hp = 1; + } + } + break; + + case NPC_SELFDESTRUCTION: /* 自爆 */ + case NPC_SELFDESTRUCTION2: /* 自爆2 */ + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, skillid, 0, 0, + skill_get_time (skillid, skilllv), 0); + break; + case NPC_LICK: + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_weapon_damage) + break; + if (dstsd) + pc_heal (dstsd, 0, -100); + if (MRAND (100) < (skilllv * 5) * sc_def_vit / 100) + skill_status_change_start (bl, SC_STAN, skilllv, 0, 0, 0, + skill_get_time2 (skillid, skilllv), + 0); + break; + + case NPC_SUICIDE: /* 自決 */ + if (src && bl && md) + { + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + mob_damage (NULL, md, md->hp, 0); + } + break; + + case NPC_SUMMONSLAVE: /* 手下召喚 */ + case NPC_SUMMONMONSTER: /* MOB召喚 */ + if (md && !md->master_id) + { + mob_summonslave (md, + mob_db[md->mob_class].skill[md->skillidx].val, + skilllv, + (skillid == NPC_SUMMONSLAVE) ? 1 : 0); + } + break; + + case NPC_TRANSFORMATION: + case NPC_METAMORPHOSIS: + if (md) + mob_class_change (md, + mob_db[md->mob_class].skill[md->skillidx].val); + break; + + case NPC_EMOTION: /* エモーション */ + if (md) + clif_emotion (&md->bl, + mob_db[md->mob_class].skill[md->skillidx].val[0]); + break; + + case NPC_DEFENDER: + 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 = sd->status.max_hp * abs (hp_rate) / 100; // 15% + clif_skill_nodamage (src, bl, skillid, gain_hp, 1); + battle_heal (NULL, bl, gain_hp, 0, 0); + } + break; + case WE_FEMALE: /* あなたの為に犠牲になります */ + if (sd && dstsd) + { + int sp_rate = + (skilllv <= + 0) ? 0 : skill_db[skillid].sp_rate[skilllv - 1]; + int gain_sp = sd->status.max_sp * abs (sp_rate) / 100; // 15% + clif_skill_nodamage (src, bl, skillid, gain_sp, 1); + battle_heal (NULL, bl, 0, gain_sp, 0); + } + break; + + case WE_CALLPARTNER: /* あなたに会いたい */ + if (sd && dstsd) + { + if (map[sd->bl.m].flag.nomemo) + { + clif_skill_teleportmessage (sd, 1); + return 0; + } + if ((dstsd = pc_get_partner (sd)) == NULL) + { + clif_skill_fail (sd, skillid, 0, 0); + return 0; + } + skill_unitsetting (src, skillid, skilllv, sd->bl.x, sd->bl.y, + 0); + } + break; + + case PF_HPCONVERSION: /* ライフ置き換え */ + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + if (sd) + { + int conv_hp = 0, conv_sp = 0; + conv_hp = sd->status.hp / 10; //基本はHPの10% + sd->status.hp -= conv_hp; //HPを減らす + conv_sp = conv_hp * 20 * skilllv / 100; + conv_sp = + (sd->status.sp + conv_sp > + sd->status.max_sp) ? sd->status.max_sp - + sd->status.sp : conv_sp; + sd->status.sp += conv_sp; //SPを増やす + pc_heal (sd, -conv_hp, conv_sp); + clif_heal (sd->fd, SP_SP, conv_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[bl->m].flag.pvp + || map[bl->m].flag.gvg) && (su->group->unit_id >= 0x8f + && su->group->unit_id <= + 0x99) + && (su->group->unit_id != 0x92)) + { //罠を取り返す + if (sd) + { + if (battle_config.skill_removetrap_type == 1) + { + 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 == 0x91 && su->group->val2) + { + struct block_list *target = + map_id2bl (su->group->val2); + if (target + && (target->type == BL_PC + || target->type == BL_MOB)) + skill_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 0x8f: /* ブラストマイン */ + case 0x90: /* スキッドトラップ */ + case 0x93: /* ランドマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + case 0x99: /* トーキーボックス */ + su->group->unit_id = 0x8c; + 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 ((double) battle_get_max_hp (bl) * 2 / 3 < battle_get_hp (bl)) //HPが2/3以上残っていたら失敗 + return 1; + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, skillid, src->id, 0, + skill_get_time (skillid, skilllv), 0); + break; + case PF_MINDBREAKER: /* プロボック */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + + /* MVPmobと不死には効かない */ + if ((bl->type == BL_MOB && battle_get_mode (bl) & 0x20) || battle_check_undead (battle_get_race (bl), battle_get_elem_type (bl))) //不死には効かない + { + map_freeblock_unlock (); + return 1; + } + + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + skill_status_change_start (bl, SkillStatusChangeTable[skillid], + skilllv, 0, 0, 0, + skill_get_time (skillid, skilllv), 0); + + if (dstmd && dstmd->skilltimer != -1 && dstmd->state.skillcastcancel) // 詠唱妨害 + skill_castcancel (bl, 0); + if (dstsd && dstsd->skilltimer != -1 + && (!dstsd->special_state.no_castcancel + || map[bl->m].flag.gvg) && dstsd->state.skillcastcancel + && !dstsd->special_state.no_castcancel2) + skill_castcancel (bl, 0); + + if (sc_data) + { + if (sc_data[SC_FREEZE].timer != -1) + skill_status_change_end (bl, SC_FREEZE, -1); + if (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0) + skill_status_change_end (bl, SC_STONE, -1); + if (sc_data[SC_SLEEP].timer != -1) + skill_status_change_end (bl, SC_SLEEP, -1); + } + + if (bl->type == BL_MOB) + { + int range = skill_get_range (skillid, skilllv); + if (range < 0) + range = battle_get_range (src) - (range + 1); + mob_target ((struct mob_data *) bl, src, range); + } + } + break; + + case RG_CLEANER: //AppleGirl + 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->src_id == src->id || map[bl->m].flag.pvp + || map[bl->m].flag.gvg) && (su->group->unit_id == 0xb0)) + { //罠を取り返す + if (sd) + skill_delunit (su); + } + } + break; + default: + printf ("Unknown skill used:%d\n", skillid); + map_freeblock_unlock (); + return 1; + } + + map_freeblock_unlock (); + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、ID指定) + *------------------------------------------ + */ +void skill_castend_id (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; + struct block_list *bl; + int range, inf2; + + nullpo_retv ( sd); + + if (sd->bl.prev == NULL) //prevが無いのはありなの? + return; + + if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid) /* タイマIDの確認 */ + return; + if (sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 + && pc_checkskill (sd, SA_FREECAST) > 0) + { + sd->speed = sd->prev_speed; + clif_updatestatus (sd, SP_SPEED); + } + if (sd->skillid != SA_CASTCANCEL) + sd->skilltimer = -1; + + if ((bl = map_id2bl (sd->skilltarget)) == NULL || bl->prev == NULL) + { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + if (sd->bl.m != bl->m || pc_isdead (sd)) + { //マップが違うか自分が死んでいる + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + + if (sd->skillid == PR_LEXAETERNA) + { + struct status_change *sc_data = battle_get_sc_data (bl); + if (sc_data + && (sc_data[SC_FREEZE].timer != -1 + || (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0))) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + else if (sd->skillid == RG_BACKSTAP) + { + int dir = map_calc_dir (&sd->bl, bl->x, bl->y), t_dir = + battle_get_dir (bl); + int dist = distance (sd->bl.x, sd->bl.y, bl->x, bl->y); + if (bl->type != BL_SKILL && (dist == 0 || map_check_dir (dir, t_dir))) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + + inf2 = skill_get_inf2 (sd->skillid); + if (((skill_get_inf (sd->skillid) & 1) || inf2 & 4) && // 彼我敵対関係チェック + battle_check_target (&sd->bl, bl, BCT_ENEMY) <= 0) + { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + if (inf2 & 0xC00 && sd->bl.id != bl->id) + { + int fail_flag = 1; + if (inf2 & 0x400 && battle_check_target (&sd->bl, bl, BCT_PARTY) > 0) + fail_flag = 0; + if (inf2 & 0x800 && sd->status.guild_id > 0 + && sd->status.guild_id == battle_get_guild_id (bl)) + fail_flag = 0; + if (fail_flag) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + + range = skill_get_range (sd->skillid, sd->skilllv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + range += battle_config.pc_skill_add_range; + if ((sd->skillid == MO_EXTREMITYFIST && sd->sc_data[SC_COMBO].timer != -1 + && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) + || (sd->skillid == CH_TIGERFIST && sd->sc_data[SC_COMBO].timer != -1 + && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) + || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 + && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) + || (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 + && sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)) + range += + skill_get_blewcount (MO_COMBOFINISH, sd->sc_data[SC_COMBO].val2); + if (battle_config.skill_out_range_consume) + { // changed to allow casting when target walks out of range [Valaris] + if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + if (!skill_check_condition (sd, 1)) + { /* 使用条件チェック */ + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + sd->skillitem = sd->skillitemlv = -1; + if (battle_config.skill_out_range_consume) + { + if (range < distance (sd->bl.x, sd->bl.y, bl->x, bl->y)) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + return; + } + } + + if (battle_config.pc_skill_log) + printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); + pc_stop_walking (sd, 0); + + switch (skill_get_nk (sd->skillid)) + { + /* 攻撃系/吹き飛ばし系 */ + case 0: + case 2: + skill_castend_damage_id (&sd->bl, bl, sd->skillid, sd->skilllv, + tick, 0); + break; + case 1: /* 支援系 */ + if ((sd->skillid == AL_HEAL + || (sd->skillid == ALL_RESURRECTION && bl->type != BL_PC) + || sd->skillid == PR_ASPERSIO) + && battle_check_undead (battle_get_race (bl), + battle_get_elem_type (bl))) + skill_castend_damage_id (&sd->bl, bl, sd->skillid, + sd->skilllv, tick, 0); + else + skill_castend_nodamage_id (&sd->bl, bl, sd->skillid, + sd->skilllv, tick, 0); + break; + } +} + +/*========================================== + * スキル使用(詠唱完了、場所指定の実際の処理) + *------------------------------------------ + */ +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; + int i, tmpx = 0, tmpy = 0, x1 = 0, y1 = 0; + + nullpo_retr (0, src); + + if (src->type == BL_PC) + { + nullpo_retr (0, sd = (struct map_session_data *) src); + } + if (skillid != WZ_METEOR && + skillid != WZ_SIGHTRASHER && + skillid != AM_CANNIBALIZE && skillid != AM_SPHEREMINE) + clif_skill_poseffect (src, skillid, skilllv, x, y, tick); + + if (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, 0, + src, skillid, skilllv, tick, + flag | BCT_NOENEMY | 1, + skill_castend_nodamage_id); + map_foreachinarea (skill_area_sub, src->m, x - 1, y - 1, x + 1, + y + 1, 0, src, skillid, skilllv, tick, + flag | BCT_ENEMY | 1, skill_castend_damage_id); + break; + + case BS_HAMMERFALL: /* ハンマーフォール */ + skill_area_temp[1] = src->id; + skill_area_temp[2] = x; + skill_area_temp[3] = y; + map_foreachinarea (skill_area_sub, + src->m, x - 2, y - 2, x + 2, y + 2, 0, + src, skillid, skilllv, tick, + flag | BCT_ENEMY | 2, + skill_castend_nodamage_id); + break; + + case HT_DETECTING: /* ディテクティング */ + { + const int range = 7; + map_foreachinarea (skill_status_change_timer_sub, + src->m, src->x - range, src->y - range, + src->x + range, src->y + range, 0, 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_SIGHTRASHER: + case WZ_QUAGMIRE: /* クァグマイア */ + case WZ_VERMILION: /* ロードオブヴァーミリオン */ + case WZ_FROSTNOVA: /* フロストノヴァ */ + case WZ_STORMGUST: /* ストームガスト */ + case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ + case PR_SANCTUARY: /* サンクチュアリ */ + case PR_MAGNUS: /* マグヌスエクソシズム */ + case CR_GRANDCROSS: /* グランドクロス */ + 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_SPIDERWEB: /* スパイダーウェッブ */ + case PF_FOGWALL: /* フォグウォール */ + 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 SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + case SA_LANDPROTECTOR: /* ランドプロテクター */ + skill_clear_element_field (src); //既に自分が発動している属性場をクリア + skill_unitsetting (src, skillid, skilllv, x, y, 0); + break; + + case WZ_METEOR: //メテオストーム + { + int flag = 0; + for (i = 0; i < 2 + (skilllv >> 1); i++) + { + int j = 0, c; + do + { + tmpx = x + (MRAND (7) - 3); + tmpy = y + (MRAND (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 (((c = map_getcell (src->m, tmpx, tmpy)) == 1 || c == 5) + && j < 100); + if (j >= 100) + continue; + if (flag == 0) + { + clif_skill_poseffect (src, skillid, skilllv, tmpx, tmpy, + tick); + flag = 1; + } + if (i > 0) + skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, + skillid, skilllv, (x1 << 16) | y1, + flag); + x1 = tmpx; + y1 = tmpy; + } + skill_addtimerskill (src, tick + i * 1000, 0, tmpx, tmpy, skillid, + skilllv, -1, flag); + } + break; + + case AL_WARP: /* ワープポータル */ + if (sd) + { + if (map[sd->bl.m].flag.noteleport) /* テレポ禁止 */ + break; + clif_skill_warppoint (sd, sd->skillid, + sd->status.save_point.map, + (sd->skilllv > + 1) ? sd->status.memo_point[0].map : "", + (sd->skilllv > + 2) ? sd->status.memo_point[1].map : "", + (sd->skilllv > + 3) ? sd->status. + memo_point[2].map : ""); + } + break; + case MO_BODYRELOCATION: + if (sd) + { + pc_movepos (sd, x, y); + } + else if (src->type == BL_MOB) + mob_warp ((struct mob_data *) src, -1, x, y, 0); + break; + case AM_CANNIBALIZE: // バイオプラント + if (sd) + { + int mx, my, id = 0; + struct mob_data *md; + + mx = x; // + (rand()%10 - 5); + my = y; // + (rand()%10 - 5); + id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1118, 1, + ""); + if ((md = (struct mob_data *) map_id2bl (id)) != NULL) + { + md->master_id = sd->bl.id; + md->hp = 2210 + skilllv * 200; + md->state.special_mob_ai = 1; + 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; + case AM_SPHEREMINE: // スフィアーマイン + if (sd) + { + int mx, my, id = 0; + struct mob_data *md; + + mx = x; // + (rand()%10 - 5); + my = y; // + (rand()%10 - 5); + id = mob_once_spawn (sd, "this", mx, my, "--ja--", 1142, 1, + ""); + if ((md = (struct mob_data *) map_id2bl (id)) != NULL) + { + md->master_id = sd->bl.id; + md->hp = 1000 + skilllv * 200; + md->state.special_mob_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; + } + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、map指定) + *------------------------------------------ + */ +int skill_castend_map (struct map_session_data *sd, int skill_num, + const char *map) +{ + int x = 0, y = 0; + + nullpo_retr (0, sd); + if (sd->bl.prev == NULL || pc_isdead (sd)) + return 0; + + if (sd->opt1 > 0 || sd->status.option & 2) + return 0; + //スキルが使えない状態異常中 + if (sd->sc_data) + { + if (sd->sc_data[SC_DIVINA].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) + return 0; + } + + if (skill_num != sd->skillid) /* 不正パケットらしい */ + return 0; + + pc_stopattack (sd); + + if (battle_config.pc_skill_log) + printf ("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) + 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[] = { + &sd->status.save_point, &sd->status.memo_point[0], + &sd->status.memo_point[1], &sd->status.memo_point[2], + }; + struct skill_unit_group *group; + int i; + int maxcount = 0; + + if ((maxcount = skill_get_maxcount (sd->skillid)) > 0) + { + int 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); + sd->canact_tick = gettick (); + sd->canmove_tick = gettick (); + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + + for (i = 0; i < sd->skilllv; i++) + { + if (strcmp (map, p[i]->map) == 0) + { + x = p[i]->x; + y = p[i]->y; + break; + } + } + if (x == 0 || y == 0) /* 不正パケット? */ + return 0; + + if (!skill_check_condition (sd, 3)) + return 0; + if ((group = + skill_unitsetting (&sd->bl, sd->skillid, sd->skilllv, + sd->skillx, sd->skilly, 0)) == NULL) + return 0; + CREATE (group->valstr, char, 24); + memcpy (group->valstr, map, 24); + group->val2 = (x << 16) | y; + } + break; + } + + return 0; +} + +/*========================================== + * スキルユニット設定処理 + *------------------------------------------ + */ +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, count = 1, limit = 10000, val1 = 0, val2 = 0; + int target = BCT_ENEMY, interval = 1000, range = 0; + int dir = 0, aoe_diameter = 0; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills + + nullpo_retr (0, src); + + switch (skillid) + { /* 設定 */ + + case MG_SAFETYWALL: /* セイフティウォール */ + limit = skill_get_time (skillid, skilllv); + val2 = skilllv + 1; + interval = -1; + target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; + break; + + case MG_FIREWALL: /* ファイヤーウォール */ + if (src->x == x && src->y == y) + dir = 2; + else + dir = map_calc_dir (src, x, y); + if (dir & 1) + count = 5; + else + count = 3; + limit = skill_get_time (skillid, skilllv); + val2 = 4 + skilllv; + interval = 1; + break; + + case AL_PNEUMA: /* ニューマ */ + limit = skill_get_time (skillid, skilllv); + interval = -1; + target = (battle_config.defnotenemy) ? BCT_NOENEMY : BCT_ALL; + count = 9; + break; + + case AL_WARP: /* ワープポータル */ + target = BCT_ALL; + val1 = skilllv + 6; + if (flag == 0) + limit = 2000; + else + limit = skill_get_time (skillid, skilllv); + break; + + case PR_SANCTUARY: /* サンクチュアリ */ + count = 21; + limit = skill_get_time (skillid, skilllv); + val1 = skilllv + 3; + val2 = (skilllv > 6) ? 777 : skilllv * 100; + target = BCT_ALL; + range = 1; + break; + + case PR_MAGNUS: /* マグヌスエクソシズム */ + count = 33; + limit = skill_get_time (skillid, skilllv); + interval = 3000; + break; + + case WZ_FIREPILLAR: /* ファイアーピラー */ + if (flag == 0) + limit = skill_get_time (skillid, skilllv); + else + limit = 1000; + interval = 2000; + val1 = skilllv + 2; + range = 1; + break; + + case MG_THUNDERSTORM: /* サンダーストーム */ + limit = 500; + range = 1; + break; + + case WZ_FROSTNOVA: /* フロストノヴァ */ + limit = 500; + range = 5; + break; + case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ + limit = 500; + range = 2; + break; + + case WZ_METEOR: /* メテオストーム */ + limit = 500; + range = 3; + break; + + case WZ_SIGHTRASHER: + limit = 500; + count = 41; + break; + + case WZ_VERMILION: /* ロードオブヴァーミリオン */ + limit = 4100; + interval = 1000; + range = 6; + break; + + case WZ_ICEWALL: /* アイスウォール */ + limit = skill_get_time (skillid, skilllv); + count = 5; + break; + + case WZ_STORMGUST: /* ストームガスト */ + limit = 4600; + interval = 450; + range = 5; + break; + + case WZ_QUAGMIRE: /* クァグマイア */ + limit = skill_get_time (skillid, skilllv); + interval = 200; + count = 25; + break; + + case HT_SKIDTRAP: /* スキッドトラップ */ + case HT_LANDMINE: /* ランドマイン */ + case HT_ANKLESNARE: /* アンクルスネア */ + case HT_SANDMAN: /* サンドマン */ + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case HT_FLASHER: /* フラッシャー */ + case HT_FREEZINGTRAP: /* フリージングトラップ */ + case HT_BLASTMINE: /* ブラストマイン */ + case HT_CLAYMORETRAP: /* クレイモアートラップ */ + limit = skill_get_time (skillid, skilllv); + range = 1; + break; + + case HT_TALKIEBOX: /* トーキーボックス */ + limit = skill_get_time (skillid, skilllv); + range = 1; + target = BCT_ALL; + break; + + case HT_SHOCKWAVE: /* ショックウェーブトラップ */ + limit = skill_get_time (skillid, skilllv); + range = 1; + val1 = skilllv * 15 + 10; + break; + + case AS_VENOMDUST: /* ベノムダスト */ + limit = skill_get_time (skillid, skilllv); + interval = 1000; + count = 5; + break; + + case CR_GRANDCROSS: /* グランドクロス */ + count = 29; + limit = 1000; + interval = 300; + break; + + case SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + limit = skill_get_time (skillid, skilllv); + count = skilllv <= 2 ? 25 : (skilllv <= 4 ? 49 : 81); + target = BCT_ALL; + break; + + case SA_LANDPROTECTOR: /* グランドクロス */ + limit = skill_get_time (skillid, skilllv); // changed to get duration from cast_db (moonsoul) + val1 = skilllv * 15 + 10; + aoe_diameter = skilllv + skilllv % 2 + 5; + target = BCT_ALL; + count = aoe_diameter * aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) + break; + + case BD_LULLABY: /* 子守唄 */ + case BD_ETERNALCHAOS: /* エターナルカオス */ + case BD_ROKISWEIL: /* ロキの叫び */ + count = 81; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_ALL; + break; + case BD_RICHMANKIM: + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + count = 81; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_PARTY; + break; + + case BA_WHISTLE: /* 口笛 */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + BA_MUSICALLESSON) + 1) >> 1; + val2 = ((battle_get_agi (src) / 10) & 0xffff) << 16; + val2 |= (battle_get_luk (src) / 10) & 0xffff; + break; + case DC_HUMMING: /* ハミング */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + DC_DANCINGLESSON) + 1) >> 1; + val2 = battle_get_dex (src) / 10; + break; + + case BA_DISSONANCE: /* 不協和音 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_ENEMY; + break; + + case DC_DONTFORGETME: /* 私を忘れないで… */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_ENEMY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + DC_DANCINGLESSON) + 1) >> 1; + val2 = ((battle_get_str (src) / 20) & 0xffff) << 16; + val2 |= (battle_get_agi (src) / 10) & 0xffff; + break; + case BA_POEMBRAGI: /* ブラギの詩 */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + pc_checkskill ((struct map_session_data *) src, + BA_MUSICALLESSON); + val2 = ((battle_get_dex (src) / 10) & 0xffff) << 16; + val2 |= (battle_get_int (src) / 5) & 0xffff; + break; + case BA_APPLEIDUN: /* イドゥンの林檎 */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + ((pc_checkskill + ((struct map_session_data *) src, + BA_MUSICALLESSON)) & 0xffff) << 16; + else + val1 = 0; + val1 |= (battle_get_vit (src)) & 0xffff; + val2 = 0; //回復用タイムカウンタ(6秒毎に1増加) + break; + case DC_SERVICEFORYOU: /* サービスフォーユー */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_PARTY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + DC_DANCINGLESSON) + 1) >> 1; + val2 = battle_get_int (src) / 10; + break; + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + BA_MUSICALLESSON) + 1) >> 1; + val2 = battle_get_agi (src) / 20; + break; + case DC_FORTUNEKISS: /* 幸運のキス */ + count = 49; + limit = skill_get_time (skillid, skilllv); + range = 5; + target = BCT_NOENEMY; + if (src->type == BL_PC) + val1 = + (pc_checkskill + ((struct map_session_data *) src, + DC_DANCINGLESSON) + 1) >> 1; + val2 = battle_get_luk (src) / 10; + break; + case AM_DEMONSTRATION: /* デモンストレーション */ + limit = skill_get_time (skillid, skilllv); + interval = 1000; + range = 1; + target = BCT_ENEMY; + break; + case WE_CALLPARTNER: /* あなたに逢いたい */ + limit = skill_get_time (skillid, skilllv); + range = -1; + break; + + case HP_BASILICA: /* バジリカ */ + limit = skill_get_time (skillid, skilllv); + target = BCT_ALL; + range = 3; + //Fix to prevent the priest from walking while Basilica is up. + battle_stopwalking (src, 1); + skill_status_change_start (src, SC_ANKLE, skilllv, 0, 0, 0, limit, + 0); + break; + case PA_GOSPEL: /* ゴスペル */ + count = 49; + target = BCT_PARTY; + limit = skill_get_time (skillid, skilllv); + break; + case PF_FOGWALL: /* フォグウォール */ + count = 15; + limit = skill_get_time (skillid, skilllv); + break; + case RG_GRAFFITI: /* Graffiti */ + count = 1; // Leave this at 1 [Valaris] + limit = 600000; // Time length [Valaris] + break; + }; + + nullpo_retr (NULL, group = + skill_initunitgroup (src, count, skillid, skilllv, + skill_get_unit_id (skillid, flag & 1))); + group->limit = limit; + group->val1 = val1; + group->val2 = val2; + group->target_flag = target; + group->interval = interval; + group->range = range; + if (skillid == HT_TALKIEBOX || skillid == RG_GRAFFITI) + { + CREATE (group->valstr, char, 80); + memcpy (group->valstr, talkie_mes, 80); + } + for (i = 0; i < count; i++) + { + struct skill_unit *unit; + int ux = x, uy = y, val1 = skilllv, val2 = 0, limit = + group->limit, alive = 1; + int range = group->range; + switch (skillid) + { /* 設定 */ + case AL_PNEUMA: /* ニューマ */ + { + static const int dx[9] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 }; + static const int dy[9] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 }; + ux += dx[i]; + uy += dy[i]; + } + break; + case MG_FIREWALL: /* ファイヤーウォール */ + { + if (dir & 1) + { /* 斜め配置 */ + static const int dx[][5] = { + {1, 1, 0, 0, -1}, {-1, -1, 0, 0, 1}, + }, dy[][5] = + { + { + 1, 0, 0, -1, -1}, + { + 1, 0, 0, -1, -1},}; + ux += dx[(dir >> 1) & 1][i]; + uy += dy[(dir >> 1) & 1][i]; + } + else + { /* 上下配置 */ + if (dir % 4 == 0) /* 上下 */ + ux += i - 1; + else /* 左右 */ + uy += i - 1; + } + val2 = group->val2; + } + 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, + }; + ux += dx[i]; + uy += dy[i]; + } + 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 + }; + ux += dx[i]; + uy += dy[i]; + } + break; + + case WZ_SIGHTRASHER: + { + static const int dx[] = { + -5, 0, 5, -4, 0, 4, -3, 0, 3, -2, 0, 2, -1, 0, 1, -5, -4, + -3, -2, -1, 0, 1, 2, 3, 4, 5, -1, 0, 1, -2, 0, 2, -3, + 0, 3, -4, 0, 4, -5, 0, 5 + }; + static const int dy[] = { + -5, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 3, 3, 3, 4, 4, 4, 5, 5, 5 + }; + ux += dx[i]; + uy += dy[i]; + } + break; + + case WZ_ICEWALL: /* アイスウォール */ + { + 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 }; + if (skilllv <= 1) + val1 = 500; + else + val1 = 200 + 200 * skilllv; + if (src->x == x && src->y == y) + dir = 2; + else + dir = map_calc_dir (src, x, y); + ux += (2 - i) * diry[dir]; + uy += (i - 2) * dirx[dir]; + } + break; + + case WZ_QUAGMIRE: /* クァグマイア */ + ux += (i % 5 - 2); + uy += (i / 5 - 2); + if (i == 12) + range = 2; + else + range = -1; + + break; + + case AS_VENOMDUST: /* ベノムダスト */ + { + static const int dx[] = { -1, 0, 0, 0, 1 }; + static const int dy[] = { 0, -1, 0, 1, 0 }; + ux += dx[i]; + uy += dy[i]; + } + break; + + case CR_GRANDCROSS: /* グランドクロス */ + { + 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, + }; + ux += dx[i]; + uy += dy[i]; + } + break; + case SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + { + int u_range = 0, central = 0; + if (skilllv <= 2) + { + u_range = 2; + central = 12; + } + else if (skilllv <= 4) + { + u_range = 3; + central = 24; + } + else if (skilllv >= 5) + { + u_range = 4; + central = 40; + } + ux += (i % (u_range * 2 + 1) - u_range); + uy += (i / (u_range * 2 + 1) - u_range); + + if (i == central) + range = u_range; //中央のユニットの効果範囲は全範囲 + else + range = -1; //中央以外のユニットは飾り + } + break; + case SA_LANDPROTECTOR: /* ランドプロテクター */ + { + int u_range = 0; + + if (skilllv <= 2) + u_range = 3; + else if (skilllv <= 4) + u_range = 4; + else if (skilllv >= 5) + u_range = 5; + + ux += (i % (u_range * 2 + 1) - u_range); + uy += (i / (u_range * 2 + 1) - u_range); + + range = 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: /* 不死身のジークフリード */ + ux += (i % 9 - 4); + uy += (i / 9 - 4); + if (i == 40) + range = 4; /* 中心の場合は範囲を4にオーバーライド */ + else + range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ + break; + case BA_DISSONANCE: /* 不協和音 */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + ux += (i % 7 - 3); + uy += (i / 7 - 3); + if (i == 40) + range = 4; /* 中心の場合は範囲を4にオーバーライド */ + else + range = -1; /* 中心じゃない場合は範囲を-1にオーバーライド */ + break; + case PA_GOSPEL: /* ゴスペル */ + ux += (i % 7 - 3); + uy += (i / 7 - 3); + break; + case PF_FOGWALL: /* フォグウォール */ + ux += (i % 5 - 2); + uy += (i / 5 - 1); + break; + case RG_GRAFFITI: /* Graffiti [Valaris] */ + ux += (i % 5 - 2); + uy += (i / 5 - 2); + break; + } + //直上スキルの場合設置座標上にランドプロテクターがないかチェック + if (range <= 0) + map_foreachinarea (skill_landprotector, src->m, ux, uy, ux, uy, + BL_SKILL, skillid, &alive); + + if (skillid == WZ_ICEWALL && alive) + { + val2 = map_getcell (src->m, ux, uy); + if (val2 == 5 || val2 == 1) + alive = 0; + else + { + map_setcell (src->m, ux, uy, 5); + 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; + } + } + 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 skill_unit_group_tickset *ts; + struct map_session_data *srcsd = NULL; + int diff, goflag, splash_count = 0; + + nullpo_retr (0, src); + nullpo_retr (0, bl); + + if (bl->prev == NULL || !src->alive + || (bl->type == BL_PC && pc_isdead ((struct map_session_data *) bl))) + return 0; + + nullpo_retr (0, sg = src->group); + nullpo_retr (0, ss = map_id2bl (sg->src_id)); + + if (ss->type == BL_PC) + nullpo_retr (0, srcsd = (struct map_session_data *) ss); + if (srcsd && srcsd->chatID) + return 0; + + if (bl->type != BL_PC && bl->type != BL_MOB) + return 0; + nullpo_retr (0, ts = skill_unitgrouptickset_search (bl, sg->group_id)); + diff = DIFF_TICK (tick, ts->tick); + goflag = (diff > sg->interval || diff < 0); + if (sg->skill_id == CR_GRANDCROSS && !battle_config.gx_allhit) // 重なっていたら3HITしない + goflag = (diff > sg->interval * map_count_oncell (bl->m, bl->x, bl->y) + || diff < 0); + + //対象がLP上に居る場合は無効 + map_foreachinarea (skill_landprotector, bl->m, bl->x, bl->y, bl->x, bl->y, + BL_SKILL, 0, &goflag); + + if (!goflag) + return 0; + ts->tick = tick; + ts->group_id = sg->group_id; + + switch (sg->unit_id) + { + case 0x83: /* サンクチュアリ */ + { + int race = battle_get_race (bl); + int damage_flag = + (battle_check_undead (race, battle_get_elem_type (bl)) + || race == 6) ? 1 : 0; + + if (battle_get_hp (bl) >= battle_get_max_hp (bl) && !damage_flag) + break; + + if ((sg->val1--) <= 0) + { + skill_delunitgroup (sg); + return 0; + } + if (!damage_flag) + { + int heal = sg->val2; + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + heal = 0; /* 黄金蟲カード(ヒール量0) */ + clif_skill_nodamage (&src->bl, bl, AL_HEAL, heal, 1); + battle_heal (NULL, bl, heal, 0, 0); + } + else + skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + } + break; + + case 0x84: /* マグヌスエクソシズム */ + { + int race = battle_get_race (bl); + int damage_flag = + (battle_check_undead (race, battle_get_elem_type (bl)) + || race == 6) ? 1 : 0; + + if (!damage_flag) + return 0; + skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + } + break; + + case 0x85: /* ニューマ */ + { + struct skill_unit *unit2; + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SC_PNEUMA; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, (int) src, + 0, 0, 0, 0); + else if ((unit2 = (struct skill_unit *) sc_data[type].val2) + && unit2 != src) + { + if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) + skill_status_change_start (bl, type, sg->skill_lv, + (int) src, 0, 0, 0, 0); + ts->tick -= sg->interval; + } + } + break; + case 0x7e: /* セイフティウォール */ + { + struct skill_unit *unit2; + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SC_SAFETYWALL; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, (int) src, + 0, 0, 0, 0); + else if ((unit2 = (struct skill_unit *) sc_data[type].val2) + && unit2 != src) + { + if (sg->val1 < unit2->group->val1) + skill_status_change_start (bl, type, sg->skill_lv, + (int) src, 0, 0, 0, 0); + ts->tick -= sg->interval; + } + } + break; + + case 0x86: /* ロードオブヴァーミリオン(&ストームガスト &グランドクロス) */ + skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + break; + + case 0x7f: /* ファイヤーウォール */ + if ((src->val2--) > 0) + skill_attack (BF_MAGIC, ss, &src->bl, bl, + sg->skill_id, sg->skill_lv, tick, 0); + if (src->val2 <= 0) + skill_delunit (src); + break; + + case 0x87: /* ファイアーピラー(発動前) */ + skill_delunit (src); + skill_unitsetting (ss, sg->skill_id, sg->skill_lv, src->bl.x, + src->bl.y, 1); + break; + + case 0x88: /* ファイアーピラー(発動後) */ + if (DIFF_TICK (tick, sg->tick) < 150) + skill_attack (BF_MAGIC, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + break; + + case 0x90: /* スキッドトラップ */ + { + int i, c = skill_get_blewcount (sg->skill_id, sg->skill_lv); + if (map[bl->m].flag.gvg) + c = 0; + for (i = 0; i < c; i++) + skill_blown (&src->bl, bl, 1 | 0x30000); + sg->unit_id = 0x8c; + clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); + sg->limit = DIFF_TICK (tick, sg->tick) + 1500; + } + break; + + case 0x93: /* ランドマイン */ + skill_attack (BF_MISC, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + sg->unit_id = 0x8c; + clif_changelook (&src->bl, LOOK_BASE, 0x88); + sg->limit = DIFF_TICK (tick, sg->tick) + 1500; + break; + + case 0x8f: /* ブラストマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + 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, + 0, &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, + 0, &src->bl, tick, splash_count); + sg->unit_id = 0x8c; + clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); + sg->limit = DIFF_TICK (tick, sg->tick) + 1500; + break; + + case 0x91: /* アンクルスネア */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + if (sg->val2 == 0 && sc_data && sc_data[SC_ANKLE].timer == -1) + { + int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE + || bl->y / BLOCK_SIZE != + src->bl.y / BLOCK_SIZE); + int sec = skill_get_time2 (sg->skill_id, + sg->skill_lv) - + (double) battle_get_agi (bl) * 0.1; + if (battle_get_mode (bl) & 0x20) + sec = sec / 5; + battle_stopwalking (bl, 1); + skill_status_change_start (bl, SC_ANKLE, sg->skill_lv, 0, 0, + 0, sec, 0); + + if (moveblock) + map_delblock (bl); + bl->x = src->bl.x; + bl->y = src->bl.y; + if (moveblock) + map_addblock (bl); + if (bl->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) bl); + else + clif_fixpos (bl); + clif_01ac (&src->bl); + sg->limit = DIFF_TICK (tick, sg->tick) + sec; + sg->val2 = bl->id; + } + } + break; + + case 0x80: /* ワープポータル(発動後) */ + if (bl->type == BL_PC) + { + struct map_session_data *sd = (struct map_session_data *) bl; + if (sd && src->bl.m == bl->m && src->bl.x == bl->x + && src->bl.y == bl->y && src->bl.x == sd->to_x + && src->bl.y == sd->to_y) + { + if (battle_config.chat_warpportal || !sd->chatID) + { + if ((sg->val1--) > 0) + { + pc_setpos (sd, sg->valstr, sg->val2 >> 16, + sg->val2 & 0xffff, 3); + if (sg->src_id == bl->id + || (strcmp (map[src->bl.m].name, sg->valstr) + == 0 && src->bl.x == (sg->val2 >> 16) + && src->bl.y == (sg->val2 & 0xffff))) + skill_delunitgroup (sg); + } + else + skill_delunitgroup (sg); + } + } + } + else if (bl->type == BL_MOB && battle_config.mob_warpportal) + { + int m = map_mapname2mapid (sg->valstr); + struct mob_data *md; + md = (struct mob_data *) bl; + mob_warp ((struct mob_data *) bl, m, sg->val2 >> 16, + sg->val2 & 0xffff, 3); + } + break; + + case 0x8e: /* クァグマイア */ + { + int type = SkillStatusChangeTable[sg->skill_id]; + if (bl->type == BL_PC + && ((struct map_session_data *) bl)-> + special_state.no_magic_damage) + break; + if (battle_get_sc_data (bl)[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, (int) src, + 0, 0, + skill_get_time2 (sg->skill_id, + sg->skill_lv), 0); + } + break; + case 0x92: /* ベノムダスト */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SkillStatusChangeTable[sg->skill_id]; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, (int) src, + 0, 0, + skill_get_time2 (sg->skill_id, + sg->skill_lv), 0); + } + break; + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + { + struct skill_unit *unit2; + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SkillStatusChangeTable[sg->skill_id]; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, (int) src, + 0, 0, + skill_get_time2 (sg->skill_id, + sg->skill_lv), 0); + else if ((unit2 = (struct skill_unit *) sc_data[type].val2) + && unit2 != src) + { + if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) + skill_status_change_start (bl, type, sg->skill_lv, + (int) src, 0, 0, + skill_get_time2 (sg->skill_id, + sg->skill_lv), + 0); + ts->tick -= sg->interval; + } + } break; + + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + { + struct skill_unit *unit2; + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SkillStatusChangeTable[sg->skill_id]; + if (sg->src_id == bl->id) + break; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, sg->val1, + sg->val2, (int) src, + skill_get_time2 (sg->skill_id, + sg->skill_lv), 0); + else if ((unit2 = (struct skill_unit *) sc_data[type].val4) + && unit2 != src) + { + if (unit2->group + && DIFF_TICK (sg->tick, unit2->group->tick) > 0) + skill_status_change_start (bl, type, sg->skill_lv, + sg->val1, sg->val2, (int) src, + skill_get_time2 (sg->skill_id, + sg->skill_lv), + 0); + ts->tick -= sg->interval; + } + } break; + + case 0xaa: /* イドゥンの林檎 */ + { + struct skill_unit *unit2; + struct status_change *sc_data = battle_get_sc_data (bl); + int type = SkillStatusChangeTable[sg->skill_id]; + if (sg->src_id == bl->id) + break; + if (sc_data && sc_data[type].timer == -1) + skill_status_change_start (bl, type, sg->skill_lv, + (sg->val1) >> 16, + (sg->val1) & 0xffff, (int) src, + skill_get_time2 (sg->skill_id, + sg->skill_lv), 0); + else if ((unit2 = (struct skill_unit *) sc_data[type].val4) + && unit2 != src) + { + if (DIFF_TICK (sg->tick, unit2->group->tick) > 0) + skill_status_change_start (bl, type, sg->skill_lv, + (sg->val1) >> 16, + (sg->val1) & 0xffff, (int) src, + skill_get_time2 (sg->skill_id, + sg->skill_lv), + 0); + ts->tick -= sg->interval; + } + } break; + + case 0xb1: /* デモンストレーション */ + skill_attack (BF_WEAPON, ss, &src->bl, bl, sg->skill_id, + sg->skill_lv, tick, 0); + if (bl->type == BL_PC && MRAND (100) < sg->skill_lv + && battle_config.equipment_breaking) + pc_breakweapon ((struct map_session_data *) bl); + break; + case 0x99: /* トーキーボックス */ + if (sg->src_id == bl->id) //自分が踏んでも発動しない + break; + if (sg->val2 == 0) + { + clif_talkiebox (&src->bl, sg->valstr); + sg->unit_id = 0x8c; + clif_changelook (&src->bl, LOOK_BASE, sg->unit_id); + sg->limit = DIFF_TICK (tick, sg->tick) + 5000; + sg->val2 = -1; //踏んだ + } + break; + case 0xb2: /* あなたを_会いたいです */ + case 0xb3: /* ゴスペル */ + case 0xb6: /* フォグウォール */ + //とりあえず何もしない + break; + + case 0xb7: /* スパイダーウェッブ */ + if (sg->val2 == 0) + { + int moveblock = (bl->x / BLOCK_SIZE != src->bl.x / BLOCK_SIZE + || bl->y / BLOCK_SIZE != + src->bl.y / BLOCK_SIZE); + skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, + BF_MISC, tick); + if (moveblock) + map_delblock (bl); + bl->x = (&src->bl)->x; + bl->y = (&src->bl)->y; + if (moveblock) + map_addblock (bl); + if (bl->type == BL_MOB) + clif_fixmobpos ((struct mob_data *) bl); + else + clif_fixpos (bl); + clif_01ac (&src->bl); + sg->limit = + DIFF_TICK (tick, + sg->tick) + skill_get_time2 (sg->skill_id, + sg->skill_lv); + sg->val2 = bl->id; + } + break; + +/* default: + if(battle_config.error_log) + printf("skill_unit_onplace: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + if (bl->type == BL_MOB && ss != bl) /* スキル使用条件のMOBスキル */ + { + if (battle_config.mob_changetarget_byskill == 1) + { + int target = ((struct mob_data *) bl)->target_id; + if (ss->type == BL_PC) + ((struct mob_data *) bl)->target_id = ss->id; + mobskill_use ((struct mob_data *) bl, tick, + MSC_SKILLUSED | (sg->skill_id << 16)); + ((struct mob_data *) bl)->target_id = target; + } + else + mobskill_use ((struct mob_data *) bl, tick, + MSC_SKILLUSED | (sg->skill_id << 16)); + } + + return 0; +} + +/*========================================== + * スキルユニットから離脱する(もしくはしている)場合 + *------------------------------------------ + */ +int skill_unit_onout (struct skill_unit *src, struct block_list *bl, + unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr (0, src); + nullpo_retr (0, bl); + nullpo_retr (0, sg = src->group); + + if (bl->prev == NULL || !src->alive) + return 0; + + if (bl->type != BL_PC && bl->type != BL_MOB) + return 0; + + switch (sg->unit_id) + { + case 0x7e: /* セイフティウォール */ + case 0x85: /* ニューマ */ + case 0x8e: /* クァグマイア */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + int type = + (sg->unit_id == 0x85) ? SC_PNEUMA : + ((sg->unit_id == 0x7e) ? SC_SAFETYWALL : SC_QUAGMIRE); + if ((type != SC_QUAGMIRE || bl->type != BL_MOB) && + sc_data && sc_data[type].timer != -1 + && ((struct skill_unit *) sc_data[type].val2) == src) + { + skill_status_change_end (bl, type, -1); + } + } break; + + case 0x91: /* アンクルスネア */ + { + struct block_list *target = map_id2bl (sg->val2); + if (target && target == bl) + { + skill_status_change_end (bl, SC_ANKLE, -1); + sg->limit = DIFF_TICK (tick, sg->tick) + 1000; + } + } + break; + case 0xb5: + case 0xb8: + { + struct block_list *target = map_id2bl (sg->val2); + if (target == bl) + skill_status_change_end (bl, SC_SPIDERWEB, -1); + sg->limit = DIFF_TICK (tick, sg->tick) + 1000; + } + break; + case 0xb6: + { + struct block_list *target = map_id2bl (sg->val2); + if (target == bl) + skill_status_change_end (bl, SC_FOGWALL, -1); + sg->limit = DIFF_TICK (tick, sg->tick) + 1000; + } + break; + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + { + struct status_change *sc_data = battle_get_sc_data (bl); + struct skill_unit *su; + int type = SkillStatusChangeTable[sg->skill_id]; + if (sc_data && sc_data[type].timer != -1 + && (su = ((struct skill_unit *) sc_data[type].val2)) + && su == src) + { + skill_status_change_end (bl, type, -1); + } + } + break; + + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xaa: /* イドゥンの林檎 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + { + struct status_change *sc_data = battle_get_sc_data (bl); + struct skill_unit *su; + int type = SkillStatusChangeTable[sg->skill_id]; + if (sc_data && sc_data[type].timer != -1 + && (su = ((struct skill_unit *) sc_data[type].val4)) + && su == src) + { + skill_status_change_end (bl, type, -1); + } + } + break; + case 0xb7: /* スパイダーウェッブ */ + { + struct block_list *target = map_id2bl (sg->val2); + if (target && target == bl) + skill_status_change_end (bl, SC_SPIDERWEB, -1); + sg->limit = DIFF_TICK (tick, sg->tick) + 1000; + } + break; + +/* default: + if(battle_config.error_log) + printf("skill_unit_onout: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + skill_unitgrouptickset_delete (bl, sg->group_id); + return 0; +} + +/*========================================== + * スキルユニットの削除イベント + *------------------------------------------ + */ +int skill_unit_ondelete (struct skill_unit *src, struct block_list *bl, + unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr (0, src); + nullpo_retr (0, bl); + nullpo_retr (0, sg = src->group); + + if (bl->prev == NULL || !src->alive) + return 0; + + if (bl->type != BL_PC && bl->type != BL_MOB) + return 0; + + switch (sg->unit_id) + { + case 0x85: /* ニューマ */ + case 0x7e: /* セイフティウォール */ + case 0x8e: /* クァグマイヤ */ + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xaa: /* イドゥンの林檎 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + return skill_unit_onout (src, bl, tick); + +/* default: + if(battle_config.error_log) + printf("skill_unit_ondelete: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + skill_unitgrouptickset_delete (bl, sg->group_id); + 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 0x81: /* ワープポータル(発動前) */ + { + 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; + CREATE (group->valstr, char, 24); + memcpy (group->valstr, sg->valstr, 24); + group->val2 = sg->val2; + } + break; + + case 0x8d: /* アイスウォール */ + map_setcell (src->bl.m, src->bl.x, src->bl.y, src->val2); + clif_changemapcell (src->bl.m, src->bl.x, src->bl.y, src->val2, + 1); + break; + case 0xb2: /* あなたに会いたい */ + { + struct map_session_data *sd = NULL; + struct map_session_data *p_sd = NULL; + if ((sd = + (struct map_session_data *) (map_id2bl (sg->src_id))) == + NULL) + return 0; + if ((p_sd = pc_get_partner (sd)) == NULL) + return 0; + + pc_setpos (p_sd, map[src->bl.m].name, 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); + + switch (sg->unit_id) + { + case 0x8d: /* アイスウォール */ + src->val1 -= damage; + break; + case 0x8f: /* ブラストマイン */ + case 0x98: /* クレイモアートラップ */ + skill_blown (bl, &src->bl, 2); //吹き飛ばしてみる + break; + default: + damage = 0; + break; + } + return damage; +} + +/*---------------------------------------------------------------------------- */ + +/*========================================== + * スキル使用(詠唱完了、場所指定) + *------------------------------------------ + */ +void skill_castend_pos (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + struct map_session_data *sd = map_id2sd (id) /*,*target_sd=NULL */ ; + int range, maxcount; + + nullpo_retv (sd); + + if (sd->bl.prev == NULL) + return; + if (sd->skilltimer != tid) /* タイマIDの確認 */ + return; + if (sd->skilltimer != -1 && pc_checkskill (sd, SA_FREECAST) > 0) + { + sd->speed = sd->prev_speed; + clif_updatestatus (sd, SP_SPEED); + } + sd->skilltimer = -1; + if (pc_isdead (sd)) + { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + + if (battle_config.pc_skill_reiteration == 0) + { + range = -1; + switch (sd->skillid) + { + case MG_SAFETYWALL: + case WZ_FIREPILLAR: + 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 AL_WARP: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case RG_GRAFFITI: /* グラフィティ */ + range = 0; + break; + case AL_PNEUMA: + range = 1; + break; + } + if (range >= 0) + { + if (skill_check_unit_range + (sd->bl.m, sd->skillx, sd->skilly, range, sd->skillid) > 0) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + } + if (battle_config.pc_skill_nofootset) + { + range = -1; + switch (sd->skillid) + { + case WZ_FIREPILLAR: + 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 PF_SPIDERWEB: /* スパイダーウェッブ */ + case WZ_ICEWALL: + range = 1; + break; + case AL_WARP: + range = 0; + break; + } + if (range >= 0) + { + if (skill_check_unit_range2 + (sd->bl.m, sd->skillx, sd->skilly, range) > 0) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + } + + 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); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + } + + range = skill_get_range (sd->skillid, sd->skilllv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + range += battle_config.pc_skill_add_range; + if (battle_config.skill_out_range_consume) + { // changed to allow casting when target walks out of range [Valaris] + if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + } + if (!skill_check_condition (sd, 1)) + { /* 使用条件チェック */ + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return; + } + sd->skillitem = sd->skillitemlv = -1; + if (battle_config.skill_out_range_consume) + { + if (range < distance (sd->bl.x, sd->bl.y, sd->skillx, sd->skilly)) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + return; + } + } + + if (battle_config.pc_skill_log) + printf ("PC %d skill castend skill=%d\n", sd->bl.id, sd->skillid); + pc_stop_walking (sd, 0); + + skill_castend_pos2 (&sd->bl, sd->skillx, sd->skilly, sd->skillid, + sd->skilllv, tick, 0); +} + +/*========================================== + * 範囲内キャラ存在確認判定処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) +{ + int *c; + struct block_list *src; + struct map_session_data *sd; + struct map_session_data *ssd; + struct pc_base_job s_class; + struct pc_base_job ss_class; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, sd = (struct map_session_data *) bl); + nullpo_retr (0, src = va_arg (ap, struct block_list *)); + nullpo_retr (0, c = va_arg (ap, int *)); + nullpo_retr (0, ssd = (struct map_session_data *) src); + + s_class = pc_calc_base_job (sd->status.pc_class); + //チェックしない設定ならcにありえない大きな数字を返して終了 + if (!battle_config.player_skill_partner_check) + { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ + (*c) = 99; + return 0; + } + + ; + ss_class = pc_calc_base_job (ssd->status.pc_class); + + switch (ssd->skillid) + { + case PR_BENEDICTIO: /* 聖体降福 */ + if (sd != ssd + && (sd->status.pc_class == 4 || sd->status.pc_class == 8 + || sd->status.pc_class == 15 || sd->status.pc_class == 4005 + || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) + && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) + && sd->status.sp >= 10) + (*c)++; + 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: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + if (sd != ssd && + ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || + (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || + (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || + (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || + (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || + (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && + pc_checkskill (sd, ssd->skillid) > 0 && + (*c) == 0 && + sd->status.party_id == ssd->status.party_id && + !pc_issit (sd) && sd->sc_data[SC_DANCING].timer == -1) + (*c) = pc_checkskill (sd, ssd->skillid); + break; + } + return 0; +} + +/*========================================== + * 範囲内キャラ存在確認判定後スキル使用処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_use_sub (struct block_list *bl, va_list ap) +{ + int *c; + struct block_list *src; + struct map_session_data *sd; + struct map_session_data *ssd; + struct pc_base_job s_class; + struct pc_base_job ss_class; + int skillid, skilllv; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, sd = (struct map_session_data *) bl); + nullpo_retr (0, src = va_arg (ap, struct block_list *)); + nullpo_retr (0, c = va_arg (ap, int *)); + nullpo_retr (0, ssd = (struct map_session_data *) src); + + s_class = pc_calc_base_job (sd->status.pc_class); + + //チェックしない設定ならcにありえない大きな数字を返して終了 + if (!battle_config.player_skill_partner_check) + { //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ + (*c) = 99; + return 0; + } + + ss_class = pc_calc_base_job (ssd->status.pc_class); + skillid = ssd->skillid; + skilllv = ssd->skilllv; + switch (skillid) + { + case PR_BENEDICTIO: /* 聖体降福 */ + if (sd != ssd + && (sd->status.pc_class == 4 || sd->status.pc_class == 8 + || sd->status.pc_class == 15 || sd->status.pc_class == 4005 + || sd->status.pc_class == 4009 || sd->status.pc_class == 4016) + && (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) + && sd->status.sp >= 10) + { + sd->status.sp -= 10; + pc_calcstatus (sd, 0); + (*c)++; + } + 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: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + if (sd != ssd && //本人以外で + ((ssd->status.pc_class == 19 && sd->status.pc_class == 20) || + (ssd->status.pc_class == 20 && sd->status.pc_class == 19) || + (ssd->status.pc_class == 4020 && sd->status.pc_class == 4021) || + (ssd->status.pc_class == 4021 && sd->status.pc_class == 4020) || + (ssd->status.pc_class == 20 && sd->status.pc_class == 4020) || + (ssd->status.pc_class == 19 && sd->status.pc_class == 4021)) && //自分がダンサーならバードで + pc_checkskill (sd, skillid) > 0 && //スキルを持っていて + (*c) == 0 && //最初の一人で + sd->status.party_id == ssd->status.party_id && //パーティーが同じで + !pc_issit (sd) && //座ってない + sd->sc_data[SC_DANCING].timer == -1 //ダンス中じゃない + ) + { + ssd->sc_data[SC_DANCING].val4 = bl->id; + clif_skill_nodamage (bl, src, skillid, skilllv, 1); + skill_status_change_start (bl, SC_DANCING, skillid, + ssd->sc_data[SC_DANCING].val2, 0, + src->id, skill_get_time (skillid, + skilllv) + + 1000, 0); + sd->skillid_dance = sd->skillid = skillid; + sd->skilllv_dance = sd->skilllv = skilllv; + (*c)++; + } + break; + } + return 0; +} + +/*========================================== + * 範囲内バイオプラント、スフィアマイン用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); + + if (!(src_id = va_arg (ap, int))) + return 0; + if (!(mob_class = va_arg (ap, int))) + return 0; + nullpo_retr (0, c = va_arg (ap, int *)); + + if (md->mob_class == mob_class && md->master_id == src_id) + (*c)++; + return 0; +} + +/*========================================== + * スキル使用条件(偽で使用失敗) + *------------------------------------------ + */ +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]; + + nullpo_retr (0, sd); + + if (battle_config.gm_skilluncond > 0 + && pc_isGM (sd) >= battle_config.gm_skilluncond) + { + 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->skillid == AC_MAKINGARROW && sd->state.make_arrow_flag == 1) + { + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + /*if(sd->skillid == AM_PHARMACY && sd->state.produce_flag == 1) { + * sd->skillitem = sd->skillitemlv = -1; + * return 0; + * } */ + + if (sd->skillitem == sd->skillid) + { /* アイテムの場合無条件成功 */ + if (type & 1) + sd->skillitem = sd->skillitemlv = -1; + return 1; + } + if (sd->opt1 > 0) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + return 0; + } + if (sd->sc_data) + { + if (sd->sc_data[SC_DIVINA].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) + { + clif_skill_fail (sd, sd->skillid, 0, 0); + return 0; /* 状態異常や沈黙など */ + } + } + skill = sd->skillid; + lv = sd->skilllv; + 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; + if (sd->dsprate != 100) + sp = sp * sd->dsprate / 100; /* 消費SP修正 */ + + 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: + if (sd->sc_data[SkillStatusChangeTable[skill]].timer != -1) + return 1; /* 解除する場合はSP消費しない */ + break; + case AL_TELEPORT: + case AL_WARP: + 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_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: // 阿修羅覇鳳拳 + if ((sd->sc_data[SC_COMBO].timer != -1 + && (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH + || sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) + || sd->sc_data[SC_BLADESTOP].timer != -1) + spiritball--; + break; + 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秒以上のみ? + clif_skill_fail (sd, skill, 0, 0); + return 0; + } + } + break; + case PR_BENEDICTIO: /* 聖体降福 */ + { + int range = 1; + int c = 0; + if (!(type & 1)) + { + 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); + if (c < 2) + { + clif_skill_fail (sd, skill, 0, 0); + return 0; + } + } + else + { + map_foreachinarea (skill_check_condition_use_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); + } + } + 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 maxcount = skill_get_maxcount (skill); + int mob_class = (skill == AM_CANNIBALIZE) ? 1118 : 1142; + if (battle_config.pc_land_skill_limit && maxcount > 0) + { + map_foreachinarea (skill_check_condition_mob_master_sub, + sd->bl.m, 0, 0, map[sd->bl.m].xs, + map[sd->bl.m].ys, BL_MOB, sd->bl.id, + mob_class, &c); + if (c >= maxcount) + { + clif_skill_fail (sd, skill, 0, 0); + return 0; + } + } + } + break; + case MG_FIREWALL: /* ファイアーウォール */ + /* 数制限 */ + 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; + } + + if (!(type & 2)) + { + if (hp > 0 && sd->status.hp < hp) + { /* HPチェック */ + clif_skill_fail (sd, skill, 2, 0); /* HP不足:失敗通知 */ + return 0; + } + if (sp > 0 && sd->status.sp < sp) + { /* SPチェック */ + clif_skill_fail (sd, skill, 1, 0); /* SP不足:失敗通知 */ + 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 & 2)) + { + clif_skill_fail (sd, skill, 0, 0); + return 0; + } + break; + case ST_CLOAKING: + if (!(sd->status.option & 4)) + { + 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_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: + if (map_getcell (sd->bl.m, sd->bl.x, sd->bl.y) != 3 + && (sd->sc_data[SC_DELUGE].timer == -1)) + { //水場判定 + clif_skill_fail (sd, skill, 0, 0); + return 0; + } + break; + } + + 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) + continue; + if (((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) + && sd->sc_data[SC_INTOABYSS].timer != -1) + continue; + if (skill == AM_POTIONPITCHER && 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 (!(type & 1)) + return 1; + + if (skill != AM_POTIONPITCHER) + { + if (skill == AL_WARP && !(type & 2)) + return 1; + for (i = 0; i < 10; i++) + { + if (index[i] >= 0) + pc_delitem (sd, index[i], amount[i], 0); // アイテム消費 + } + } + + if (type & 2) + return 1; + + pc_heal (sd, -sp, -hp); // [Fate] This might suppress some dupe messages + +/* 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 time) +{ + struct map_session_data *sd; + struct mob_data *md; // [Valaris] + struct status_change *sc_data; + int dex; + int castrate = 100; + int skill, lv, castnodex; + + nullpo_retr (0, bl); + + if (bl->type == BL_MOB) + { // Crash fix [Valaris] + md = (struct mob_data *) bl; + skill = md->skillid; + lv = md->skilllv; + } + + else + { + sd = (struct map_session_data *) bl; + skill = sd->skillid; + lv = sd->skilllv; + } + + sc_data = battle_get_sc_data (bl); + dex = battle_get_dex (bl); + + if (skill > MAX_SKILL_DB || skill < 0) + return 0; + + castnodex = skill_get_castnodex (skill, lv); + + if (time == 0) + return 0; + if (castnodex > 0 && bl->type == BL_PC) + castrate = ((struct map_session_data *) bl)->castrate; + else if (castnodex <= 0 && bl->type == BL_PC) + { + castrate = ((struct map_session_data *) bl)->castrate; + time = + time * castrate * (battle_config.castrate_dex_scale - + dex) / (battle_config.castrate_dex_scale * + 100); + time = time * battle_config.cast_rate / 100; + } + + /* サフラギウム */ + if (sc_data && sc_data[SC_SUFFRAGIUM].timer != -1) + { + time = time * (100 - sc_data[SC_SUFFRAGIUM].val1 * 15) / 100; + skill_status_change_end (bl, SC_SUFFRAGIUM, -1); + } + /* ブラギの詩 */ + if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) + time = + time * (100 - + (sc_data[SC_POEMBRAGI].val1 * 3 + + sc_data[SC_POEMBRAGI].val2 + + (sc_data[SC_POEMBRAGI].val3 >> 16))) / 100; + + return (time > 0) ? time : 0; +} + +/*========================================== + * ディレイ計算 + *------------------------------------------ + */ +int skill_delayfix (struct block_list *bl, int time) +{ + struct status_change *sc_data; + + nullpo_retr (0, bl); + + sc_data = battle_get_sc_data (bl); + if (time <= 0) + return 0; + + if (bl->type == BL_PC) + { + if (battle_config.delay_dependon_dex) /* dexの影響を計算する */ + time = + time * (battle_config.castrate_dex_scale - + battle_get_dex (bl)) / + battle_config.castrate_dex_scale; + time = time * battle_config.delay_rate / 100; + } + + /* ブラギの詩 */ + if (sc_data && sc_data[SC_POEMBRAGI].timer != -1) + time = + time * (100 - + (sc_data[SC_POEMBRAGI].val1 * 3 + + sc_data[SC_POEMBRAGI].val2 + + (sc_data[SC_POEMBRAGI].val3 & 0xffff))) / 100; + + return (time > 0) ? time : 0; +} + +/*========================================== + * スキル使用(ID指定) + *------------------------------------------ + */ +int skill_use_id (struct map_session_data *sd, int target_id, + int skill_num, int skill_lv) +{ + unsigned int tick; + int casttime = 0, delay = 0, skill, range; + struct map_session_data *target_sd = NULL; + int forcecast = 0; + struct block_list *bl; + struct status_change *sc_data; + tick = gettick (); + + nullpo_retr (0, sd); + + if ((bl = map_id2bl (target_id)) == NULL) + { +/* if(battle_config.error_log) + printf("skill target not found %d\n",target_id); */ + return 0; + } + if (sd->bl.m != bl->m || pc_isdead (sd)) + return 0; + + if (skillnotok (skill_num, sd)) // [MouseJstr] + return 0; + + if (sd->skillid == WZ_ICEWALL && map[sd->bl.m].flag.noicewall + && !map[sd->bl.m].flag.pvp) + { // noicewall flag [Valaris] + clif_skill_fail (sd, sd->skillid, 0, 0); + return 0; + } + sc_data = sd->sc_data; + + /* 沈黙や異常(ただし、グリムなどの判定をする) */ + if (sd->opt1 > 0) + return 0; + if (sd->sc_data) + { + if (sc_data[SC_CHASEWALK].timer != -1) + return 0; + if (sc_data[SC_VOLCANO].timer != -1) + { + if (skill_num == WZ_ICEWALL) + return 0; + } + if (sc_data[SC_ROKISWEIL].timer != -1) + { + if (skill_num == BD_ADAPTATION) + return 0; + } + if (sd->sc_data[SC_DIVINA].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) + { + return 0; /* 状態異常や沈黙など */ + } + + if (sc_data[SC_BLADESTOP].timer != -1) + { + int lv = sc_data[SC_BLADESTOP].val1; + if (sc_data[SC_BLADESTOP].val2 == 1) + return 0; //白羽された側なのでダメ + if (lv == 1) + return 0; + if (lv == 2 && skill_num != MO_FINGEROFFENSIVE) + return 0; + if (lv == 3 && skill_num != MO_FINGEROFFENSIVE + && skill_num != MO_INVESTIGATE) + return 0; + if (lv == 4 && skill_num != MO_FINGEROFFENSIVE + && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) + return 0; + if (lv == 5 && skill_num != MO_FINGEROFFENSIVE + && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO + && skill_num != MO_EXTREMITYFIST) + return 0; + } + } + + if (sd->status.option & 4 && skill_num == TF_HIDING) + return 0; + if (sd->status.option & 2 && skill_num != TF_HIDING + && skill_num != AS_GRIMTOOTH && skill_num != RG_BACKSTAP + && skill_num != RG_RAID) + return 0; + + if (map[sd->bl.m].flag.gvg) + { //GvGで使用できないスキル + switch (skill_num) + { + case SM_ENDURE: + case AL_TELEPORT: + case AL_WARP: + case WZ_ICEWALL: + case TF_BACKSLIDING: + case LK_BERSERK: + case HP_BASILICA: + case ST_CHASEWALK: + return 0; + } + } + + /* 演奏/ダンス中 */ + if (sc_data && sc_data[SC_DANCING].timer != -1) + { +// if(battle_config.pc_skill_log) +// printf("dancing! %d\n",skill_num); + if (sc_data[SC_DANCING].val4 && skill_num != BD_ADAPTATION) //合奏中はアドリブ以外不可 + return 0; + if (skill_num != BD_ADAPTATION && skill_num != BA_MUSICALSTRIKE + && skill_num != DC_THROWARROW) + { + return 0; + } + } + + if (skill_get_inf2 (skill_num) & 0x200 && sd->bl.id == target_id) + 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; + } + + sd->skillid = skill_num; + sd->skilllv = skill_lv; + + switch (skill_num) + { //事前にレベルが変わったりするスキル + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + { + int range = 1; + int c = 0; + 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); + if (c < 1) + { + clif_skill_fail (sd, skill_num, 0, 0); + return 0; + } + else if (c == 99) + { //相方不要設定だった + ; + } + else + { + sd->skilllv = (c + skill_lv) / 2; + } + } + break; + } + + if (!skill_check_condition (sd, 0)) + return 0; + + /* 射程と障害物チェック */ + range = skill_get_range (skill_num, skill_lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + if (!battle_check_range (&sd->bl, bl, range)) + return 0; + + if (bl->type == BL_PC) + { + target_sd = (struct map_session_data *) bl; + if (target_sd && skill_num == ALL_RESURRECTION + && !pc_isdead (target_sd)) + 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 == MO_EXTREMITYFIST && sd->state.skill_flag)) + pc_stopattack (sd); + + casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); + if (skill_num != SA_MAGICROD) + delay = + skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); + sd->state.skillcastcancel = skill_db[skill_num].castcancel; + + switch (skill_num) + { /* 何か特殊な処理が必要 */ +// case AL_HEAL: /* ヒール */ +// if(battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) +// forcecast=1; /* ヒールアタックなら詠唱エフェクト有り */ +// break; + case ALL_RESURRECTION: /* リザレクション */ + if (bl->type != BL_PC + && battle_check_undead (battle_get_race (bl), + battle_get_elem_type (bl))) + { /* 敵がアンデッドなら */ + forcecast = 1; /* ターンアンデットと同じ詠唱時間 */ + casttime = + skill_castfix (&sd->bl, + skill_get_cast (PR_TURNUNDEAD, skill_lv)); + } + break; + case MO_FINGEROFFENSIVE: /* 指弾 */ + casttime += + casttime * + ((skill_lv > sd->spiritball) ? sd->spiritball : skill_lv); + break; + case MO_CHAINCOMBO: /*連打掌 */ + target_id = sd->attacktarget; + if (sc_data && sc_data[SC_BLADESTOP].timer != -1) + { + struct block_list *tbl; + if ((tbl = (struct block_list *) sc_data[SC_BLADESTOP].val4) == NULL) //ターゲットがいない? + return 0; + target_id = tbl->id; + } + break; + case MO_COMBOFINISH: /*猛龍拳 */ + case CH_TIGERFIST: /* 伏虎拳 */ + case CH_CHAINCRUSH: /* 連柱崩撃 */ + target_id = sd->attacktarget; + break; + +// -- moonsoul (altered to allow proper usage of extremity from new champion combos) +// + case MO_EXTREMITYFIST: /*阿修羅覇鳳拳 */ + if (sc_data && sc_data[SC_COMBO].timer != -1 + && (sc_data[SC_COMBO].val1 == MO_COMBOFINISH + || sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) + { + casttime = 0; + target_id = sd->attacktarget; + } + forcecast = 1; + break; + case SA_MAGICROD: + case SA_SPELLBREAKER: + forcecast = 1; + break; + case WE_MALE: + case WE_FEMALE: + { + struct map_session_data *p_sd = NULL; + if ((p_sd = pc_get_partner (sd)) == NULL) + return 0; + target_id = p_sd->bl.id; + //rangeをもう1回検査 + range = skill_get_range (skill_num, skill_lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + if (!battle_check_range (&sd->bl, &p_sd->bl, range)) + { + return 0; + } + } + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + { + struct status_change *t_sc_data = battle_get_sc_data (bl); + if (t_sc_data && t_sc_data[SC_POISON].timer == -1) + { + clif_skill_fail (sd, skill_num, 0, 10); + return 0; + } + } + break; + case PF_MEMORIZE: /* メモライズ */ + casttime = 12000; + break; + + } + + //メモライズ状態ならキャストタイムが1/3 + if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) + { + casttime = casttime / 3; + if ((--sc_data[SC_MEMORIZE].val2) <= 0) + skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); + } + + if (battle_config.pc_skill_log) + printf ("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(sd->skillitem == skill_num) +// casttime = delay = 0; + + if (casttime > 0 || forcecast) + { /* 詠唱が必要 */ + struct mob_data *md; + clif_skillcasting (&sd->bl, + sd->bl.id, target_id, 0, 0, skill_num, casttime); + + /* 詠唱反応モンスター */ + if (bl->type == BL_MOB && (md = (struct mob_data *) bl) + && mob_db[md->mob_class].mode & 0x10 && md->state.state != MS_ATTACK + && sd->invincible_timer == -1) + { + md->target_id = sd->bl.id; + md->state.targettype = ATTACKABLE; + md->min_chase = 13; + } + } + + if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ + sd->state.skillcastcancel = 0; + + sd->skilltarget = target_id; +/* sd->cast_target_bl = bl; */ + sd->skillx = 0; + sd->skilly = 0; + sd->canact_tick = tick + casttime + delay; + sd->canmove_tick = tick; + if (!(battle_config.pc_cloak_check_type & 2) && sc_data + && sc_data[SC_CLOAKING].timer != -1 && sd->skillid != AS_CLOAKING) + skill_status_change_end (&sd->bl, SC_CLOAKING, -1); + if (casttime > 0) + { + sd->skilltimer = + add_timer (tick + casttime, skill_castend_id, sd->bl.id, 0); + if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) + { + sd->prev_speed = sd->speed; + sd->speed = sd->speed * (175 - skill * 5) / 100; + clif_updatestatus (sd, SP_SPEED); + } + else + pc_stop_walking (sd, 0); + } + else + { + if (skill_num != SA_CASTCANCEL) + sd->skilltimer = -1; + skill_castend_id (sd->skilltimer, tick, sd->bl.id, 0); + } + + //マジックパワーの効果終了 + if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 + && skill_num != HW_MAGICPOWER) + skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); + + return 0; +} + +/*========================================== + * スキル使用(場所指定) + *------------------------------------------ + */ +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; + unsigned int tick; + int casttime = 0, delay = 0, skill, range; + + nullpo_retr (0, sd); + + if (pc_isdead (sd)) + return 0; + + if (skillnotok (skill_num, sd)) // [MoueJstr] + return 0; + + sc_data = sd->sc_data; + + if (sd->opt1 > 0) + return 0; + if (sc_data) + { + if (sc_data[SC_DIVINA].timer != -1 || + sc_data[SC_ROKISWEIL].timer != -1 || + sc_data[SC_AUTOCOUNTER].timer != -1 || + sc_data[SC_STEELBODY].timer != -1 || + sc_data[SC_DANCING].timer != -1 || + sc_data[SC_BERSERK].timer != -1) + return 0; /* 状態異常や沈黙など */ + } + + if (sd->status.option & 2) + return 0; + + if (map[sd->bl.m].flag.gvg + && (skill_num == SM_ENDURE || skill_num == AL_TELEPORT + || skill_num == AL_WARP || skill_num == WZ_ICEWALL + || skill_num == TF_BACKSLIDING)) + 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; + + /* 射程と障害物チェック */ + bl.type = BL_NUL; + bl.m = sd->bl.m; + bl.x = skill_x; + bl.y = skill_y; + range = skill_get_range (skill_num, skill_lv); + if (range < 0) + range = battle_get_range (&sd->bl) - (range + 1); + if (!battle_check_range (&sd->bl, &bl, range)) + return 0; + + pc_stopattack (sd); + + casttime = skill_castfix (&sd->bl, skill_get_cast (skill_num, skill_lv)); + delay = skill_delayfix (&sd->bl, skill_get_delay (skill_num, skill_lv)); + sd->state.skillcastcancel = skill_db[skill_num].castcancel; + + if (battle_config.pc_skill_log) + printf ("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); + +// if(sd->skillitem == skill_num) +// casttime = delay = 0; + //メモライズ状態ならキャストタイムが1/3 + if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) + { + casttime = casttime / 3; + if ((--sc_data[SC_MEMORIZE].val2) <= 0) + skill_status_change_end (&sd->bl, SC_MEMORIZE, -1); + } + + if (casttime > 0) /* 詠唱が必要 */ + clif_skillcasting (&sd->bl, + sd->bl.id, 0, skill_x, skill_y, skill_num, + casttime); + + if (casttime <= 0) /* 詠唱の無いものはキャンセルされない */ + sd->state.skillcastcancel = 0; + + sd->skilltarget = 0; +/* sd->cast_target_bl = NULL; */ + tick = gettick (); + sd->canact_tick = tick + casttime + delay; + sd->canmove_tick = tick; + if (!(battle_config.pc_cloak_check_type & 2) && sc_data + && sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end (&sd->bl, SC_CLOAKING, -1); + if (casttime > 0) + { + sd->skilltimer = + add_timer (tick + casttime, skill_castend_pos, sd->bl.id, 0); + if ((skill = pc_checkskill (sd, SA_FREECAST)) > 0) + { + sd->prev_speed = sd->speed; + sd->speed = sd->speed * (175 - skill * 5) / 100; + clif_updatestatus (sd, SP_SPEED); + } + else + pc_stop_walking (sd, 0); + } + else + { + sd->skilltimer = -1; + skill_castend_pos (sd->skilltimer, tick, sd->bl.id, 0); + } + //マジックパワーの効果終了 + if (sc_data && sc_data[SC_MAGICPOWER].timer != -1 + && skill_num != HW_MAGICPOWER) + skill_status_change_end (&sd->bl, SC_MAGICPOWER, -1); + + return 0; +} + +/*========================================== + * スキル詠唱キャンセル + *------------------------------------------ + */ +int skill_castcancel (struct block_list *bl, int type) +{ + int inf; + + 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 (pc_checkskill (sd, SA_FREECAST) > 0) + { + sd->speed = sd->prev_speed; + clif_updatestatus (sd, SP_SPEED); + } + if (!type) + { + if ((inf = skill_get_inf (sd->skillid)) == 2 || inf == 32) + delete_timer (sd->skilltimer, skill_castend_pos); + else + delete_timer (sd->skilltimer, skill_castend_id); + } + else + { + if ((inf = skill_get_inf (sd->skillid_old)) == 2 || inf == 32) + delete_timer (sd->skilltimer, skill_castend_pos); + else + delete_timer (sd->skilltimer, skill_castend_id); + } + 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 ((inf = skill_get_inf (md->skillid)) == 2 || inf == 32) + delete_timer (md->skilltimer, mobskill_castend_pos); + else + delete_timer (md->skilltimer, mobskill_castend_id); + md->skilltimer = -1; + clif_skillcastcancel (bl); + } + 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; + } + } +} + +/*========================================== + * ディボーション 有効確認 + *------------------------------------------ + */ +void skill_devotion (struct map_session_data *md, int target) +{ + // 総確認 + int n; + + nullpo_retv (md); + + for (n = 0; n < 5; n++) + { + if (md->dev.val1[n]) + { + struct map_session_data *sd = map_id2sd (md->dev.val1[n]); + // 相手が見つからない // 相手をディボしてるのが自分じゃない // 距離が離れてる + if (sd == NULL + || (sd->sc_data + && (md->bl.id != sd->sc_data[SC_DEVOTION].val1)) + || skill_devotion3 (&md->bl, md->dev.val1[n])) + { + skill_devotion_end (md, sd, n); + } + } + } +} + +void skill_devotion2 (struct block_list *bl, int crusader) +{ + // 被ディボーションが歩いた時の距離チェック + struct map_session_data *sd = map_id2sd (crusader); + + nullpo_retv (bl); + + if (sd) + skill_devotion3 (&sd->bl, bl->id); +} + +int skill_devotion3 (struct block_list *bl, int target) +{ + // クルセが歩いた時の距離チェック + struct map_session_data *md; + struct map_session_data *sd; + int n, r = 0; + + nullpo_retr (1, bl); + + if ((md = (struct map_session_data *) bl) == NULL + || (sd = map_id2sd (target)) == NULL) + return 1; + else + r = distance (bl->x, bl->y, sd->bl.x, sd->bl.y); + + if (pc_checkskill (sd, CR_DEVOTION) + 6 < r) + { // 許容範囲を超えてた + for (n = 0; n < 5; n++) + if (md->dev.val1[n] == target) + md->dev.val2[n] = 0; // 離れた時は、糸を切るだけ + clif_devotion (md, sd->bl.id); + return 1; + } + return 0; +} + +void skill_devotion_end (struct map_session_data *md, + struct map_session_data *sd, int target) +{ + // クルセと被ディボキャラのリセット + nullpo_retv (md); + nullpo_retv (sd); + + md->dev.val1[target] = md->dev.val2[target] = 0; + if (sd && sd->sc_data) + { + // skill_status_change_end(sd->bl,SC_DEVOTION,-1); + sd->sc_data[SC_DEVOTION].val1 = 0; + sd->sc_data[SC_DEVOTION].val2 = 0; + clif_status_change (&sd->bl, SC_DEVOTION, 0); + clif_devotion (md, sd->bl.id); + } +} + +/*========================================== + * オートスペル + *------------------------------------------ + */ +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 (skillid == MG_NAPALMBEAT) + maxlv = 3; + else if (skillid == MG_COLDBOLT || skillid == MG_FIREBOLT + || skillid == MG_LIGHTNINGBOLT) + { + 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; + + skill_status_change_start (&sd->bl, SC_AUTOSPELL, skilllv, skillid, maxlv, 0, // val1:スキルID val2:使用最大Lv + skill_get_time (SA_AUTOSPELL, skilllv), 0); // にしてみたけどbscriptが書き易い・・・? + 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 > 0) + { /*ギャングスター成功したら自分にもギャングスター属性付与 */ + 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 < 1) + 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; +} + +/*========================================== + * 寒いジョーク・スクリーム判定処理(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); + tick = va_arg (ap, unsigned int); + + if (src == bl) //自分には効かない + return 0; + + 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) + { + if (MRAND (100) < 10) //PTメンバにも低確率でかかる(とりあえず10%) + skill_additional_effect (src, bl, skillnum, skilllv, BF_MISC, + tick); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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); + 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, skillid; + + nullpo_retr (0, bl); + + if (bl->type == BL_MOB) + md = (struct mob_data *) bl; + if (bl->type == BL_PC) + sd = (struct map_session_data *) bl; + + for (i = 0; i < MAX_MOBSKILLUNITGROUP; 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; +} + +/*========================================== + * ランドプロテクターチェック(foreachinarea) + *------------------------------------------ + */ +int skill_landprotector (struct block_list *bl, va_list ap) +{ + int skillid; + int *alive; + struct skill_unit *unit; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + + skillid = va_arg (ap, int); + alive = va_arg (ap, int *); + if ((unit = (struct skill_unit *) bl) == NULL) + return 0; + + if (skillid == SA_LANDPROTECTOR) + { + skill_delunit (unit); + } + else + { + if (alive && unit->group->skill_id == SA_LANDPROTECTOR) + (*alive) = 0; + } + return 0; +} + +/*========================================== + * イドゥンの林檎の回復処理(foreachinarea) + *------------------------------------------ + */ +int skill_idun_heal (struct block_list *bl, va_list ap) +{ + struct skill_unit *unit; + struct skill_unit_group *sg; + int heal; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, unit = va_arg (ap, struct skill_unit *)); + nullpo_retr (0, sg = unit->group); + + heal = + 30 + sg->skill_lv * 5 + ((sg->val1) >> 16) * 5 + + ((sg->val1) & 0xfff) / 2; + + if (bl->type == BL_SKILL || bl->id == sg->src_id) + return 0; + + if (bl->type == BL_PC || bl->type == BL_MOB) + { + clif_skill_nodamage (&unit->bl, bl, AL_HEAL, heal, 1); + battle_heal (NULL, bl, heal, 0, 0); + } + 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)); + + 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 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x94: /* ショックウェーブトラップ */ + skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, + BF_MISC, tick); + break; + case 0x8f: /* ブラストマイン */ + case 0x98: /* クレイモアートラップ */ + 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); + } + case 0x97: /* フリージングトラップ */ + skill_attack (BF_WEAPON, ss, src, bl, sg->skill_id, + sg->skill_lv, tick, (sg->val2) ? 0x0500 : 0); + break; + default: + break; + } + } + + return 0; +} + +/*---------------------------------------------------------------------------- + * ステータス異常 + *---------------------------------------------------------------------------- + */ + +/*========================================== + * ステータス異常タイマー範囲処理 + *------------------------------------------ + */ +int skill_status_change_timer_sub (struct block_list *bl, va_list ap) +{ + struct block_list *src; + int type; + unsigned int tick; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, src = va_arg (ap, struct block_list *)); + type = va_arg (ap, int); + tick = va_arg (ap, unsigned int); + + if (bl->type != BL_PC && bl->type != BL_MOB) + return 0; + + switch (type) + { + case SC_SIGHT: /* サイト */ + case SC_CONCENTRATE: + if ((*battle_get_option (bl)) & 6) + { + skill_status_change_end (bl, SC_HIDING, -1); + skill_status_change_end (bl, SC_CLOAKING, -1); + } + break; + case SC_RUWACH: /* ルアフ */ + if ((*battle_get_option (bl)) & 6) + { + skill_status_change_end (bl, SC_HIDING, -1); + skill_status_change_end (bl, SC_CLOAKING, -1); + if (battle_check_target (src, bl, BCT_ENEMY) > 0) + { + struct status_change *sc_data = battle_get_sc_data (bl); + skill_attack (BF_MAGIC, src, src, bl, AL_RUWACH, + sc_data[type].val1, tick, 0); + } + } + break; + } + return 0; +} + +/*========================================== + * ステータス異常終了 + *------------------------------------------ + */ +int skill_status_change_active (struct block_list *bl, int type) +{ + struct status_change *sc_data; + + nullpo_retr (0, bl); + if (bl->type != BL_PC && bl->type != BL_MOB) + { + if (battle_config.error_log) + printf ("skill_status_change_active: neither MOB nor PC !\n"); + return 0; + } + + nullpo_retr (0, sc_data = battle_get_sc_data (bl)); + + return sc_data[type].timer != -1; +} + +int skill_status_change_end (struct block_list *bl, int type, int tid) +{ + 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) + printf ("skill_status_change_end: neither MOB nor PC !\n"); + return 0; + } + nullpo_retr (0, sc_data = battle_get_sc_data (bl)); + nullpo_retr (0, sc_count = battle_get_sc_count (bl)); + nullpo_retr (0, option = battle_get_option (bl)); + nullpo_retr (0, opt1 = battle_get_opt1 (bl)); + nullpo_retr (0, opt2 = battle_get_opt2 (bl)); + nullpo_retr (0, opt3 = battle_get_opt3 (bl)); + + if ((*sc_count) > 0 && sc_data[type].timer != -1 + && (sc_data[type].timer == tid || tid == -1)) + { + + if (tid == -1) // タイマから呼ばれていないならタイマ削除をする + delete_timer (sc_data[type].timer, skill_status_change_timer); + + /* 該当の異常を正常に戻す */ + sc_data[type].timer = -1; + (*sc_count)--; + + switch (type) + { /* 異常の種類ごとの処理 */ + case SC_PROVOKE: /* プロボック */ + case SC_CONCENTRATE: /* 集中力向上 */ + case SC_BLESSING: /* ブレッシング */ + case SC_ANGELUS: /* アンゼルス */ + case SC_INCREASEAGI: /* 速度上昇 */ + case SC_DECREASEAGI: /* 速度減少 */ + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + case SC_HIDING: + case SC_TWOHANDQUICKEN: /* 2HQ */ + 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_DEFENDER: + case SC_SPEEDPOTION0: /* 増速ポーション */ + case SC_SPEEDPOTION1: + case SC_SPEEDPOTION2: + case SC_APPLEIDUN: /* イドゥンの林檎 */ + case SC_RIDING: + case SC_BLADESTOP_WAIT: + case SC_AURABLADE: /* オーラブレード */ + case SC_PARRYING: /* パリイング */ + case SC_CONCENTRATION: /* コンセントレーション */ + case SC_TENSIONRELAX: /* テンションリラックス */ + case SC_ASSUMPTIO: /* アシャンプティオ */ + case SC_WINDWALK: /* ウインドウォーク */ + case SC_TRUESIGHT: /* トゥルーサイト */ + case SC_SPIDERWEB: /* スパイダーウェッブ */ + case SC_MAGICPOWER: /* 魔法力増幅 */ + case SC_CHASEWALK: + case SC_ATKPOT: /* attack potion [Valaris] */ + case SC_MATKPOT: /* magic attack potion [Valaris] */ + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + case SC_MELTDOWN: /* メルトダウン */ + case SC_PHYS_SHIELD: + case SC_HASTE: + calc_flag = 1; + break; + case SC_BERSERK: /* バーサーク */ + calc_flag = 1; + clif_status_change (bl, SC_INCREASEAGI, 0); /* アイコン消去 */ + break; + case SC_DEVOTION: /* ディボーション */ + { + struct map_session_data *md = map_id2sd (sc_data[type].val1); + sc_data[type].val1 = sc_data[type].val2 = 0; + skill_devotion (md, bl->id); + calc_flag = 1; + } + break; + case SC_BLADESTOP: + { + struct status_change *t_sc_data = + battle_get_sc_data ((struct block_list *) + sc_data[type].val4); + //片方が切れたので相手の白刃状態が切れてないのなら解除 + if (t_sc_data && t_sc_data[SC_BLADESTOP].timer != -1) + skill_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].val4 + && (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].val4 = 0; + } + } + calc_flag = 1; + break; + case SC_GRAFFITI: + { + struct skill_unit_group *sg = (struct skill_unit_group *) sc_data[type].val4; //val4がグラフィティのgroup_id + if (sg) + skill_delunitgroup (sg); + } + break; + case SC_NOCHAT: //チャット禁止状態 + { + struct map_session_data *sd = NULL; + if (bl->type == BL_PC + && (sd = (struct map_session_data *) bl)) + { + 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_SELFDESTRUCTION: /* 自爆 */ + { + //自分のダメージは0にして + struct mob_data *md = NULL; + if (bl->type == BL_MOB && (md = (struct mob_data *) bl)) + skill_castend_damage_id (bl, bl, sc_data[type].val2, + sc_data[type].val1, gettick (), + 0); + } + 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; + } + + if (bl->type == BL_PC && type < SC_SENDMAX) + clif_status_change (bl, 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_SLOWPOISON: + if (sc_data[SC_POISON].timer != -1) + *opt2 |= 0x1; + *opt2 &= ~0x200; + opt_flag = 1; + break; + + case SC_SIGNUMCRUCIS: + *opt2 &= ~0x40; + opt_flag = 1; + break; + + case SC_SPEEDPOTION0: + *opt2 &= ~0x20; + opt_flag = 1; + break; + + case SC_ATKPOT: + *opt2 &= ~0x80; + opt_flag = 1; + break; + + case SC_HIDING: + case SC_CLOAKING: + *option &= ~((type == SC_HIDING) ? 2 : 4); + opt_flag = 1; + break; + + case SC_CHASEWALK: + *option &= ~16388; + opt_flag = 1; + break; + + case SC_SIGHT: + *option &= ~1; + opt_flag = 1; + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + *option &= ~4096; + opt_flag = 1; + break; + case SC_RUWACH: + *option &= ~8192; + opt_flag = 1; + break; + + //opt3 + case SC_TWOHANDQUICKEN: /* 2HQ */ + 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: // 金剛 + *opt3 &= ~16; + break; + case SC_BLADESTOP: /* 白刃取り */ + *opt3 &= ~32; + break; + case SC_BERSERK: /* バーサーク */ + *opt3 &= ~128; + break; + case SC_MARIONETTE: /* マリオネットコントロール */ + *opt3 &= ~1024; + break; + case SC_ASSUMPTIO: /* アスムプティオ */ + *opt3 &= ~2048; + break; + } + + if (night_flag == 1 && (*opt2 & STATE_BLIND) == 0 + && bl->type == BL_PC) + { // by [Yor] + *opt2 |= STATE_BLIND; + opt_flag = 1; + } + + if (opt_flag) /* optionの変更を伝える */ + clif_changeoption (bl); + + if (bl->type == BL_PC && calc_flag) + pc_calcstatus ((struct map_session_data *) bl, 0); /* ステータス再計算 */ + } + + return 0; +} + +int skill_update_heal_animation (struct map_session_data *sd) +{ + const int mask = 0x100; + int was_active; + int is_active; + + nullpo_retr (0, sd); + was_active = sd->opt2 & mask; + is_active = sd->quick_regeneration_hp.amount > 0; + + if ((was_active && is_active) || (!was_active && !is_active)) + return 0; // no update + + if (is_active) + sd->opt2 |= mask; + else + sd->opt2 &= ~mask; + + return clif_changeoption (&sd->bl); +} + +/*========================================== + * ステータス異常終了タイマー + *------------------------------------------ + */ +void skill_status_change_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + int type = data; + struct block_list *bl; + struct map_session_data *sd = NULL; + struct status_change *sc_data; + //short *sc_count; //使ってない? + + if ((bl = map_id2bl (id)) == NULL) + return; //該当IDがすでに消滅しているというのはいかにもありそうなのでスルーしてみる + nullpo_retv (sc_data = battle_get_sc_data (bl)); + + if (bl->type == BL_PC) + sd = (struct map_session_data *) bl; + + //sc_count=battle_get_sc_count(bl); //使ってない? + + if (sc_data[type].timer != tid) + { + if (battle_config.error_log) + printf ("skill_status_change_timer %d != %d\n", tid, + sc_data[type].timer); + } + + if (sc_data[type].spell_invocation) + { // Must report termination + spell_effect_report_termination (sc_data[type].spell_invocation, + bl->id, type, 0); + sc_data[type].spell_invocation = 0; + } + + switch (type) + { /* 特殊な処理になる場合 */ + case SC_MAXIMIZEPOWER: /* マキシマイズパワー */ + case SC_CLOAKING: /* クローキング */ + case SC_CHASEWALK: + if (sd) + { + if (sd->status.sp > 0) + { /* SP切れるまで持続 */ + sd->status.sp--; + clif_updatestatus (sd, SP_SP); + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + sc_data[type].val2 + + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + 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, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + + case SC_SIGHT: /* サイト */ + { + const int range = 7; + map_foreachinarea (skill_status_change_timer_sub, + bl->m, bl->x - range, bl->y - range, + bl->x + range, bl->y + range, 0, bl, type, + tick); + + if ((--sc_data[type].val2) > 0) + { + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 250 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + case SC_RUWACH: /* ルアフ */ + { + const int range = 5; + map_foreachinarea (skill_status_change_timer_sub, + bl->m, bl->x - range, bl->y - range, + bl->x + range, bl->y + range, 0, bl, type, + tick); + + if ((--sc_data[type].val2) > 0) + { + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 250 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + { + int race = battle_get_race (bl); + if (race == 6 + || battle_check_undead (race, battle_get_elem_type (bl))) + { + sc_data[type].timer = + add_timer (1000 * 600 + tick, skill_status_change_timer, + bl->id, data); + return; + } + } + 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, skill_status_change_timer, bl->id, + data); + return; + } + break; + + case SC_WATERBALL: /* ウォーターボール */ + { + struct block_list *target = map_id2bl (sc_data[type].val2); + if (target == NULL || target->prev == NULL) + break; + skill_attack (BF_MAGIC, bl, bl, target, WZ_WATERBALL, + sc_data[type].val1, tick, 0); + if ((--sc_data[type].val3) > 0) + { + sc_data[type].timer = + add_timer (150 + tick, skill_status_change_timer, bl->id, + data); + return; + } + } + break; + + case SC_ENDURE: /* インデュア */ + if (sd && sd->special_state.infinite_endure) + { + sc_data[type].timer = + add_timer (1000 * 600 + tick, skill_status_change_timer, + bl->id, data); + sc_data[type].val2 = 1; + return; + } + break; + + case SC_DISSONANCE: /* 不協和音 */ + if ((--sc_data[type].val2) > 0) + { + struct skill_unit *unit = + (struct skill_unit *) sc_data[type].val4; + struct block_list *src; + + if (!unit || !unit->group) + break; + src = map_id2bl (unit->group->src_id); + if (!src) + break; + skill_attack (BF_MISC, src, &unit->bl, bl, + unit->group->skill_id, sc_data[type].val1, tick, + 0); + sc_data[type].timer = + add_timer (skill_get_time2 + (unit->group->skill_id, + unit->group->skill_lv) + tick, + skill_status_change_timer, bl->id, data); + return; + } + break; + + case SC_LULLABY: /* 子守唄 */ + if ((--sc_data[type].val2) > 0) + { + struct skill_unit *unit = + (struct skill_unit *) sc_data[type].val4; + if (!unit || !unit->group || unit->group->src_id == bl->id) + break; + skill_additional_effect (bl, bl, unit->group->skill_id, + sc_data[type].val1, + BF_LONG | BF_SKILL | BF_MISC, tick); + sc_data[type].timer = + add_timer (skill_get_time + (unit->group->skill_id, + unit->group->skill_lv) / 10 + tick, + skill_status_change_timer, bl->id, data); + return; + } + break; + + case SC_STONE: + if (sc_data[type].val2 != 0) + { + short *opt1 = battle_get_opt1 (bl); + sc_data[type].val2 = 0; + sc_data[type].val4 = 0; + battle_stopwalking (bl, 1); + if (opt1) + { + *opt1 = 1; + clif_changeoption (bl); + } + sc_data[type].timer = + add_timer (1000 + tick, skill_status_change_timer, bl->id, + data); + return; + } + else if ((--sc_data[type].val3) > 0) + { + int hp = battle_get_max_hp (bl); + if ((++sc_data[type].val4) % 5 == 0 + && battle_get_hp (bl) > hp >> 2) + { + hp = hp / 100; + if (hp < 1) + hp = 1; + if (bl->type == BL_PC) + pc_heal ((struct map_session_data *) bl, -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, skill_status_change_timer, bl->id, + data); + return; + } + break; + case SC_POISON: + if (sc_data[SC_SLOWPOISON].timer == -1) + { + const int resist_poison = + skill_power_bl (bl, TMW_RESIST_POISON) >> 3; + if (resist_poison) + sc_data[type].val1 -= MRAND (resist_poison + 1); + + if ((--sc_data[type].val1) > 0) + { + + int hp = battle_get_max_hp (bl); + if (battle_get_hp (bl) > hp >> 4) + { + if (bl->type == BL_PC) + { + hp = 3 + hp * 3 / 200; + pc_heal ((struct map_session_data *) bl, -hp, 0); + } + else if (bl->type == BL_MOB) + { + struct mob_data *md; + if ((md = ((struct mob_data *) bl)) == NULL) + break; + hp = 3 + hp / 200; + md->hp -= hp; + } + } + sc_data[type].timer = + add_timer (1000 + tick, skill_status_change_timer, + bl->id, data); + } + } + else + sc_data[type].timer = + add_timer (2000 + tick, skill_status_change_timer, bl->id, + data); + break; + + case SC_TENSIONRELAX: /* テンションリラックス */ + if (sd) + { /* SPがあって、HPが満タンでなければ継続 */ + if (sd->status.sp > 12 && sd->status.max_hp > sd->status.hp) + { + if (sc_data[type].val2 % (sc_data[type].val1 + 3) == 0) + { + sd->status.sp -= 12; + clif_updatestatus (sd, SP_SP); + } + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 10000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + if (sd->status.max_hp <= sd->status.hp) + skill_status_change_end (&sd->bl, SC_TENSIONRELAX, -1); + } + break; + + /* 時間切れ無し?? */ + case SC_AETERNA: + case SC_TRICKDEAD: + case SC_RIDING: + case SC_FALCON: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_MAGICPOWER: /* 魔法力増幅 */ + case SC_REJECTSWORD: /* リジェクトソード */ + case SC_MEMORIZE: /* メモライズ */ + case SC_BROKNWEAPON: + case SC_BROKNARMOR: + if (sc_data[type].timer == tid) + sc_data[type].timer = + add_timer (1000 * 600 + tick, skill_status_change_timer, + bl->id, data); + return; + + case SC_DANCING: //ダンススキルの時間SP消費 + { + int s = 0; + if (sd) + { + if (sd->status.sp > 0 && (--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 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 DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */ + s = 10; + break; + } + if (s && ((sc_data[type].val3 % s) == 0)) + { + sd->status.sp--; + clif_updatestatus (sd, SP_SP); + } + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 1000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + } + break; + case SC_BERSERK: /* バーサーク */ + if (sd) + { /* HPが100以上なら継続 */ + if ((sd->status.hp - sd->status.hp / 100) > 100) + { + sd->status.hp -= sd->status.hp / 100; + clif_updatestatus (sd, SP_HP); + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 15000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + if (sd) + { + time_t timer; + if (time (&timer) < ((sc_data[type].val2) + 3600)) + { //1時間たっていないので継続 + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 10000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + case SC_NOCHAT: //チャット禁止状態 + if (sd && battle_config.muting_players) + { + time_t timer; + if ((++sd->status.manner) + && time (&timer) < + ((sc_data[type].val2) + 60 * (0 - sd->status.manner))) + { //開始からstatus.manner分経ってないので継続 + clif_updatestatus (sd, SP_MANNER); + sc_data[type].timer = add_timer ( /* タイマー再設定(60秒) */ + 60000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + } + break; + case SC_SELFDESTRUCTION: /* 自爆 */ + if (--sc_data[type].val3 > 0) + { + struct mob_data *md; + if (bl->type == BL_MOB && (md = (struct mob_data *) bl) + && md->stats[MOB_SPEED] > 250) + { + md->stats[MOB_SPEED] -= 250; + md->next_walktime = tick; + } + sc_data[type].timer = add_timer ( /* タイマー再設定 */ + 1000 + tick, + skill_status_change_timer, + bl->id, data); + return; + } + break; + + case SC_FLYING_BACKPACK: + clif_updatestatus (sd, SP_WEIGHT); + break; + + } + + skill_status_change_end (bl, type, tid); +} + +/*========================================== + * ステータス異常終了 + *------------------------------------------ + */ +int skill_encchant_eremental_end (struct block_list *bl, int type) +{ + struct status_change *sc_data; + + nullpo_retr (0, bl); + nullpo_retr (0, sc_data = battle_get_sc_data (bl)); + + if (type != SC_ENCPOISON && sc_data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解除 */ + skill_status_change_end (bl, SC_ENCPOISON, -1); + if (type != SC_ASPERSIO && sc_data[SC_ASPERSIO].timer != -1) /* アスペルシオ解除 */ + skill_status_change_end (bl, SC_ASPERSIO, -1); + if (type != SC_FLAMELAUNCHER && sc_data[SC_FLAMELAUNCHER].timer != -1) /* フレイムランチャ解除 */ + skill_status_change_end (bl, SC_FLAMELAUNCHER, -1); + if (type != SC_FROSTWEAPON && sc_data[SC_FROSTWEAPON].timer != -1) /* フロストウェポン解除 */ + skill_status_change_end (bl, SC_FROSTWEAPON, -1); + if (type != SC_LIGHTNINGLOADER && sc_data[SC_LIGHTNINGLOADER].timer != -1) /* ライトニングローダー解除 */ + skill_status_change_end (bl, SC_LIGHTNINGLOADER, -1); + if (type != SC_SEISMICWEAPON && sc_data[SC_SEISMICWEAPON].timer != -1) /* サイスミックウェポン解除 */ + skill_status_change_end (bl, SC_SEISMICWEAPON, -1); + + return 0; +} + +/*========================================== + * ステータス異常開始 + *------------------------------------------ + */ +int skill_status_change_start (struct block_list *bl, int type, int val1, + int val2, int val3, int val4, int tick, + int flag) +{ + return skill_status_effect (bl, type, val1, val2, val3, val4, tick, flag, + 0); +} + +int skill_status_effect (struct block_list *bl, int type, int val1, int val2, + int val3, int val4, int tick, int flag, + int spell_invocation) +{ + 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, race, mode, elem, undead_flag; + int scdef = 0; + + nullpo_retr (0, bl); + if (bl->type == BL_SKILL) + return 0; + nullpo_retr (0, sc_data = battle_get_sc_data (bl)); + nullpo_retr (0, sc_count = battle_get_sc_count (bl)); + nullpo_retr (0, option = battle_get_option (bl)); + nullpo_retr (0, opt1 = battle_get_opt1 (bl)); + nullpo_retr (0, opt2 = battle_get_opt2 (bl)); + nullpo_retr (0, opt3 = battle_get_opt3 (bl)); + + race = battle_get_race (bl); + mode = battle_get_mode (bl); + elem = battle_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; + + switch (type) + { + case SC_STONE: + case SC_FREEZE: + scdef = 3 + battle_get_mdef (bl) + battle_get_luk (bl) / 3; + break; + case SC_STAN: + case SC_SILENCE: + case SC_POISON: + scdef = 3 + battle_get_vit (bl) + battle_get_luk (bl) / 3; + break; + case SC_SLEEP: + case SC_BLIND: + scdef = 3 + battle_get_int (bl) + battle_get_luk (bl) / 3; + break; + case SC_CURSE: + scdef = 3 + battle_get_luk (bl); + break; + +// case SC_CONFUSION: + default: + scdef = 0; + } + if (scdef >= 100) + return 0; + if (bl->type == BL_PC) + { + sd = (struct map_session_data *) bl; + if (sd && type == SC_ADRENALINE + && !(skill_get_weapontype (BS_ADRENALINE) & + (1 << sd->status.weapon))) + return 0; + + if (SC_STONE <= type && type <= SC_BLIND) + { /* カードによる耐性 */ + if (sd && sd->reseff[type - SC_STONE] > 0 + && MRAND (10000) < sd->reseff[type - SC_STONE]) + { + if (battle_config.battle_log) + printf ("PC %d skill_sc_start: cardによる異常耐性発動\n", + sd->bl.id); + return 0; + } + } + } + else if (bl->type == BL_MOB) + { + } + else + { + if (battle_config.error_log) + printf ("skill_status_change_start: neither MOB nor PC !\n"); + return 0; + } + + if (type == SC_FREEZE && undead_flag && !(flag & 1)) + return 0; + + if ((type == SC_ADRENALINE || type == SC_WEAPONPERFECTION + || type == SC_OVERTHRUST) && sc_data[type].timer != -1 + && sc_data[type].val2 && !val2) + return 0; + + if (mode & 0x20 && (type == SC_STONE || type == SC_FREEZE || + type == SC_STAN || type == SC_SLEEP + || type == SC_SILENCE || type == SC_QUAGMIRE + || type == SC_DECREASEAGI || type == SC_SIGNUMCRUCIS + || type == SC_PROVOKE || (type == SC_BLESSING + && (undead_flag + || race == 6))) + && !(flag & 1)) + { + /* ボスには効かない(ただしカードによる効果は適用される) */ + return 0; + } + if (type == SC_FREEZE || type == SC_STAN || type == SC_SLEEP) + battle_stopwalking (bl, 1); + + if (sc_data[type].timer != -1) + { /* すでに同じ異常になっている場合タイマ解除 */ + if (sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION && type != SC_SPEEDPOTION0 && type != SC_SPEEDPOTION1 && type != SC_SPEEDPOTION2 && type != SC_ATKPOT && type != SC_MATKPOT) // added atk and matk potions [Valaris] + return 0; + if (type >= SC_STAN && type <= SC_BLIND) + return 0; /* 継ぎ足しができない状態異常である時は状態異常を行わない */ + if (type == SC_GRAFFITI) + { //異常中にもう一度状態異常になった時に解除してから再度かかる + skill_status_change_end (bl, type, -1); + } + else + { + (*sc_count)--; + delete_timer (sc_data[type].timer, skill_status_change_timer); + sc_data[type].timer = -1; + } + } + + switch (type) + { /* 異常の種類ごとの処理 */ + case SC_PROVOKE: /* プロボック */ + calc_flag = 1; + if (tick <= 0) + tick = 1000; /* (オートバーサーク) */ + break; + case SC_ENDURE: /* インデュア */ + if (tick <= 0) + tick = 1000 * 60; + break; + case SC_CONCENTRATE: /* 集中力向上 */ + calc_flag = 1; + break; + case SC_BLESSING: /* ブレッシング */ + { + if (bl->type == BL_PC || (!undead_flag && race != 6)) + { + if (sc_data[SC_CURSE].timer != -1) + skill_status_change_end (bl, SC_CURSE, -1); + if (sc_data[SC_STONE].timer != -1 + && sc_data[SC_STONE].val2 == 0) + skill_status_change_end (bl, SC_STONE, -1); + } + calc_flag = 1; + } + break; + case SC_ANGELUS: /* アンゼルス */ + calc_flag = 1; + break; + case SC_INCREASEAGI: /* 速度上昇 */ + calc_flag = 1; + if (sc_data[SC_DECREASEAGI].timer != -1) + skill_status_change_end (bl, SC_DECREASEAGI, -1); + if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ + skill_status_change_end (bl, SC_WINDWALK, -1); + break; + case SC_DECREASEAGI: /* 速度減少 */ + calc_flag = 1; + if (sc_data[SC_INCREASEAGI].timer != -1) + skill_status_change_end (bl, SC_INCREASEAGI, -1); + break; + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + calc_flag = 1; +// val2 = 14 + val1; + val2 = 10 + val1 * 2; + tick = 600 * 1000; + clif_emotion (bl, 4); + break; + case SC_SLOWPOISON: + if (sc_data[SC_POISON].timer == -1) + return 0; + break; + case SC_TWOHANDQUICKEN: /* 2HQ */ + *opt3 |= 1; + calc_flag = 1; + break; + case SC_ADRENALINE: /* アドレナリンラッシュ */ + calc_flag = 1; + break; + case SC_WEAPONPERFECTION: /* ウェポンパーフェクション */ + if (battle_config.party_skill_penaly && !val2) + tick /= 5; + break; + case SC_OVERTHRUST: /* オーバースラスト */ + *opt3 |= 2; + if (battle_config.party_skill_penaly && !val2) + tick /= 10; + break; + case SC_MAXIMIZEPOWER: /* マキシマイズパワー(SPが1減る時間,val2にも) */ + if (bl->type == BL_PC) + val2 = tick; + else + tick = 5000 * val1; + break; + case SC_ENCPOISON: /* エンチャントポイズン */ + calc_flag = 1; + val2 = (((val1 - 1) / 2) + 3) * 100; /* 毒付与確率 */ + skill_encchant_eremental_end (bl, SC_ENCPOISON); + break; + case SC_POISONREACT: /* ポイズンリアクト */ + break; + case SC_IMPOSITIO: /* インポシティオマヌス */ + calc_flag = 1; + break; + case SC_ASPERSIO: /* アスペルシオ */ + skill_encchant_eremental_end (bl, SC_ASPERSIO); + break; + case SC_SUFFRAGIUM: /* サフラギム */ + case SC_BENEDICTIO: /* 聖体 */ + case SC_MAGNIFICAT: /* マグニフィカート */ + case SC_AETERNA: /* エーテルナ */ + break; + case SC_ENERGYCOAT: /* エナジーコート */ + *opt3 |= 4; + break; + case SC_MAGICROD: + val2 = val1 * 20; + break; + case SC_KYRIE: /* キリエエレイソン */ + val2 = battle_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) + skill_status_change_end (bl, SC_ASSUMPTIO, -1); + break; + case SC_MINDBREAKER: + calc_flag = 1; + if (tick <= 0) + tick = 1000; /* (オートバーサーク) */ + case SC_GLORIA: /* グロリア */ + calc_flag = 1; + break; + case SC_LOUD: /* ラウドボイス */ + calc_flag = 1; + break; + case SC_TRICKDEAD: /* 死んだふり */ + break; + case SC_QUAGMIRE: /* クァグマイア */ + calc_flag = 1; + if (sc_data[SC_CONCENTRATE].timer != -1) /* 集中力向上解除 */ + skill_status_change_end (bl, SC_CONCENTRATE, -1); + if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ + skill_status_change_end (bl, SC_INCREASEAGI, -1); + if (sc_data[SC_TWOHANDQUICKEN].timer != -1) + skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); + if (sc_data[SC_SPEARSQUICKEN].timer != -1) + skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); + if (sc_data[SC_ADRENALINE].timer != -1) + skill_status_change_end (bl, SC_ADRENALINE, -1); + if (sc_data[SC_LOUD].timer != -1) + skill_status_change_end (bl, SC_LOUD, -1); + if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ + skill_status_change_end (bl, SC_TRUESIGHT, -1); + if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ + skill_status_change_end (bl, SC_WINDWALK, -1); + if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ + skill_status_change_end (bl, SC_CARTBOOST, -1); + break; + case SC_FLAMELAUNCHER: /* フレームランチャー */ + skill_encchant_eremental_end (bl, SC_FLAMELAUNCHER); + break; + case SC_FROSTWEAPON: /* フロストウェポン */ + skill_encchant_eremental_end (bl, SC_FROSTWEAPON); + break; + case SC_LIGHTNINGLOADER: /* ライトニングローダー */ + skill_encchant_eremental_end (bl, SC_LIGHTNINGLOADER); + break; + case SC_SEISMICWEAPON: /* サイズミックウェポン */ + skill_encchant_eremental_end (bl, SC_SEISMICWEAPON); + break; + case SC_DEVOTION: /* ディボーション */ + calc_flag = 1; + break; + case SC_PROVIDENCE: /* プロヴィデンス */ + calc_flag = 1; + val2 = val1 * 5; + break; + case SC_REFLECTSHIELD: + val2 = 10 + val1 * 3; + break; + case SC_STRIPWEAPON: + case SC_STRIPSHIELD: + case SC_STRIPARMOR: + case SC_STRIPHELM: + case SC_CP_WEAPON: + case SC_CP_SHIELD: + case SC_CP_ARMOR: + case SC_CP_HELM: + break; + + case SC_AUTOSPELL: /* オートスペル */ + val4 = 5 + val1 * 2; + break; + + case SC_VOLCANO: + calc_flag = 1; + val3 = val1 * 10; + val4 = + val1 >= 5 ? 20 : (val1 == + 4 ? 19 : (val1 == + 3 ? 17 : (val1 == 2 ? 14 : 10))); + break; + case SC_DELUGE: + calc_flag = 1; + val3 = + val1 >= 5 ? 15 : (val1 == + 4 ? 14 : (val1 == + 3 ? 12 : (val1 == 2 ? 9 : 5))); + val4 = + val1 >= 5 ? 20 : (val1 == + 4 ? 19 : (val1 == + 3 ? 17 : (val1 == 2 ? 14 : 10))); + break; + case SC_VIOLENTGALE: + calc_flag = 1; + val3 = val1 * 3; + val4 = + val1 >= 5 ? 20 : (val1 == + 4 ? 19 : (val1 == + 3 ? 17 : (val1 == 2 ? 14 : 10))); + break; + + case SC_SPEARSQUICKEN: /* スピアクイッケン */ + calc_flag = 1; + val2 = 20 + val1; + *opt3 |= 1; + break; + case SC_COMBO: + break; + case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */ + break; + case SC_BLADESTOP: /* 白刃取り */ + if (val2 == 2) + clif_bladestop ((struct block_list *) val3, + (struct block_list *) val4, 1); + *opt3 |= 32; + break; + + case SC_LULLABY: /* 子守唄 */ + val2 = 11; + break; + case SC_RICHMANKIM: + break; + case SC_ETERNALCHAOS: /* エターナルカオス */ + calc_flag = 1; + break; + case SC_DRUMBATTLE: /* 戦太鼓の響き */ + calc_flag = 1; + val2 = (val1 + 1) * 25; + val3 = (val1 + 1) * 2; + break; + case SC_NIBELUNGEN: /* ニーベルングの指輪 */ + calc_flag = 1; + val2 = (val1 + 2) * 50; + val3 = (val1 + 2) * 25; + break; + case SC_ROKISWEIL: /* ロキの叫び */ + break; + case SC_INTOABYSS: /* 深淵の中に */ + break; + case SC_SIEGFRIED: /* 不死身のジークフリード */ + calc_flag = 1; + val2 = 40 + val1 * 5; + val3 = val1 * 10; + break; + case SC_DISSONANCE: /* 不協和音 */ + val2 = 10; + break; + case SC_WHISTLE: /* 口笛 */ + calc_flag = 1; + break; + case SC_ASSNCROS: /* 夕陽のアサシンクロス */ + calc_flag = 1; + break; + case SC_POEMBRAGI: /* ブラギの詩 */ + break; + case SC_APPLEIDUN: /* イドゥンの林檎 */ + calc_flag = 1; + break; + case SC_UGLYDANCE: /* 自分勝手なダンス */ + val2 = 10; + break; + case SC_HUMMING: /* ハミング */ + calc_flag = 1; + break; + case SC_DONTFORGETME: /* 私を忘れないで */ + calc_flag = 1; + if (sc_data[SC_INCREASEAGI].timer != -1) /* 速度上昇解除 */ + skill_status_change_end (bl, SC_INCREASEAGI, -1); + if (sc_data[SC_TWOHANDQUICKEN].timer != -1) + skill_status_change_end (bl, SC_TWOHANDQUICKEN, -1); + if (sc_data[SC_SPEARSQUICKEN].timer != -1) + skill_status_change_end (bl, SC_SPEARSQUICKEN, -1); + if (sc_data[SC_ADRENALINE].timer != -1) + skill_status_change_end (bl, SC_ADRENALINE, -1); + if (sc_data[SC_ASSNCROS].timer != -1) + skill_status_change_end (bl, SC_ASSNCROS, -1); + if (sc_data[SC_TRUESIGHT].timer != -1) /* トゥルーサイト */ + skill_status_change_end (bl, SC_TRUESIGHT, -1); + if (sc_data[SC_WINDWALK].timer != -1) /* ウインドウォーク */ + skill_status_change_end (bl, SC_WINDWALK, -1); + if (sc_data[SC_CARTBOOST].timer != -1) /* カートブースト */ + skill_status_change_end (bl, SC_CARTBOOST, -1); + break; + case SC_FORTUNE: /* 幸運のキス */ + calc_flag = 1; + break; + case SC_SERVICE4U: /* サービスフォーユー */ + calc_flag = 1; + break; + case SC_DANCING: /* ダンス/演奏中 */ + calc_flag = 1; + val3 = tick / 1000; + tick = 1000; + break; + + case SC_EXPLOSIONSPIRITS: // 爆裂波動 + calc_flag = 1; + val2 = 75 + 25 * val1; + *opt3 |= 8; + break; + case SC_STEELBODY: // 金剛 + calc_flag = 1; + *opt3 |= 16; + break; + case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */ + break; + case SC_AUTOCOUNTER: + val3 = val4 = 0; + break; + + case SC_SPEEDPOTION0: /* 増速ポーション */ + *opt2 |= 0x20; + case SC_SPEEDPOTION1: + case SC_SPEEDPOTION2: + calc_flag = 1; + tick = 1000 * tick; +// val2 = 5*(2+type-SC_SPEEDPOTION0); + break; + + /* atk & matk potions [Valaris] */ + case SC_ATKPOT: + *opt2 |= 0x80; + case SC_MATKPOT: + calc_flag = 1; + tick = 1000 * tick; + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + { + time_t timer; + + calc_flag = 1; + tick = 10000; + if (!val2) + val2 = time (&timer); + } + break; + case SC_NOCHAT: //チャット禁止状態 + { + time_t timer; + + if (!battle_config.muting_players) + break; + + tick = 60000; + if (!val2) + val2 = time (&timer); + updateflag = SP_MANNER; + } + break; + case SC_SELFDESTRUCTION: //自爆 + clif_skillcasting (bl, bl->id, bl->id, 0, 0, 331, + skill_get_time (val2, val1)); + val3 = tick / 1000; + tick = 1000; + break; + + /* option1 */ + case SC_STONE: /* 石化 */ + if (!(flag & 2)) + { + int sc_def = battle_get_mdef (bl) * 200; + tick = tick - sc_def; + } + val3 = tick / 1000; + if (val3 < 1) + val3 = 1; + tick = 5000; + val2 = 1; + break; + case SC_SLEEP: /* 睡眠 */ + if (!(flag & 2)) + { +// int sc_def = 100 - (battle_get_int(bl) + battle_get_luk(bl)/3); +// tick = tick * sc_def / 100; +// if(tick < 1000) tick = 1000; + tick = 30000; //睡眠はステータス耐性に関わらず30秒 + } + break; + case SC_FREEZE: /* 凍結 */ + if (!(flag & 2)) + { + int sc_def = 100 - battle_get_mdef (bl); + tick = tick * sc_def / 100; + } + break; + case SC_STAN: /* スタン(val2にミリ秒セット) */ + if (!(flag & 2)) + { + int sc_def = + 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 3); + tick = tick * sc_def / 100; + } + break; + + /* option2 */ + case SC_POISON: /* 毒 */ + calc_flag = 1; + if (!(flag & 2)) + { + int sc_def = + 100 - (battle_get_vit (bl) + battle_get_luk (bl) / 5); + tick = tick * sc_def / 100; + } + val3 = tick / 1000; + if (val3 < 1) + val3 = 1; + tick = 1000; + break; + case SC_SILENCE: /* 沈黙(レックスデビーナ) */ + if (!(flag & 2)) + { + int sc_def = 100 - battle_get_vit (bl); + tick = tick * sc_def / 100; + } + break; + case SC_BLIND: /* 暗黒 */ + calc_flag = 1; + if (!(flag & 2)) + { + int sc_def = + battle_get_lv (bl) / 10 + battle_get_int (bl) / 15; + tick = 30000 - sc_def; + } + break; + case SC_CURSE: + calc_flag = 1; + if (!(flag & 2)) + { + int sc_def = 100 - battle_get_vit (bl); + tick = tick * sc_def / 100; + } + break; + + /* option */ + case SC_HIDING: /* ハイディング */ + calc_flag = 1; + if (bl->type == BL_PC) + { + val2 = tick / 1000; /* 持続時間 */ + tick = 1000; + } + break; + case SC_CHASEWALK: + case SC_CLOAKING: /* クローキング */ + if (bl->type == BL_PC) + val2 = tick; + else + tick = 5000 * val1; + break; + case SC_SIGHT: /* サイト/ルアフ */ + case SC_RUWACH: + val2 = tick / 250; + tick = 10; + break; + + /* セーフティウォール、ニューマ */ + case SC_SAFETYWALL: + case SC_PNEUMA: + tick = ((struct skill_unit *) val2)->group->limit; + break; + + /* アンクル */ + case SC_ANKLE: + break; + + /* ウォーターボール */ + case SC_WATERBALL: + tick = 150; + if (val1 > 5) //レベルが5以上の場合は25発に制限(1発目はすでに打ってるので-1) + val3 = 5 * 5 - 1; + else + val3 = (val1 | 1) * (val1 | 1) - 1; + break; + + /* スキルじゃない/時間に関係しない */ + case SC_RIDING: + calc_flag = 1; + tick = 600 * 1000; + break; + case SC_FALCON: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_BROKNWEAPON: + case SC_BROKNARMOR: + tick = 600 * 1000; + break; + + case SC_AUTOGUARD: + { + int i, t; + for (i = val2 = 0; i < val1; i++) + { + t = 5 - (i >> 1); + val2 += (t < 0) ? 1 : t; + } + } + break; + + case SC_DEFENDER: + calc_flag = 1; + val2 = 5 + val1 * 15; + break; + + case SC_KEEPING: + case SC_BARRIER: + case SC_HALLUCINATION: + break; + case SC_CONCENTRATION: /* コンセントレーション */ + *opt3 |= 1; + calc_flag = 1; + break; + case SC_TENSIONRELAX: /* テンションリラックス */ + calc_flag = 1; + if (bl->type == BL_PC) + { + tick = 10000; + } + break; + case SC_AURABLADE: /* オーラブレード */ + case SC_PARRYING: /* パリイング */ +// case SC_ASSUMPTIO: /* */ + case SC_HEADCRUSH: /* ヘッドクラッシュ */ + case SC_JOINTBEAT: /* ジョイントビート */ +// case SC_MARIONETTE: /* マリオネットコントロール */ + + //とりあえず手抜き + break; + +// -- moonsoul (for new upper class related skill status effects) +/* + case SC_AURABLADE: + val2 = val1*10; + break; + case SC_PARRYING: + val2=val1*3; + break; + case SC_CONCENTRATION: + calc_flag=1; + val2=val1*10; + val3=val1*5; + break; + case SC_TENSIONRELAX: +// val2 = 10; +// val3 = 15; + break; + case SC_BERSERK: + calc_flag=1; + break; + case SC_ASSUMPTIO: + if(sc_data[SC_KYRIE].timer!=-1 ) + skill_status_change_end(bl,SC_KYRIE,-1); + break; +*/ + case SC_WINDWALK: /* ウインドウォーク */ + calc_flag = 1; + val2 = (val1 / 2); //Flee上昇率 + break; + case SC_BERSERK: /* バーサーク */ + if (sd) + { + sd->status.sp = 0; + clif_updatestatus (sd, SP_SP); + clif_status_change (bl, SC_INCREASEAGI, 1); /* アイコン表示 */ + } + *opt3 |= 128; + tick = 1000; + calc_flag = 1; + break; + case SC_ASSUMPTIO: /* アスムプティオ */ + *opt3 |= 2048; + break; + case SC_MARIONETTE: /* マリオネットコントロール */ + *opt3 |= 1024; + break; + case SC_MELTDOWN: /* メルトダウン */ + case SC_CARTBOOST: /* カートブースト */ + case SC_TRUESIGHT: /* トゥルーサイト */ + case SC_SPIDERWEB: /* スパイダーウェッブ */ + case SC_MAGICPOWER: /* 魔法力増幅 */ + calc_flag = 1; + break; + case SC_REJECTSWORD: /* リジェクトソード */ + val2 = 3; //3回攻撃を跳ね返す + break; + case SC_MEMORIZE: /* メモライズ */ + val2 = 3; //3回詠唱を1/3にする + break; + case SC_GRAFFITI: /* グラフィティ */ + { + struct skill_unit_group *sg = + skill_unitsetting (bl, RG_GRAFFITI, val1, val2, val3, 0); + if (sg) + val4 = (int) sg; + } + break; + case SC_HASTE: + calc_flag = 1; + case SC_SPLASHER: /* ベナムスプラッシャー */ + case SC_PHYS_SHIELD: + case SC_MBARRIER: + case SC_HALT_REGENERATE: + case SC_HIDE: + break; + case SC_FLYING_BACKPACK: + updateflag = SP_WEIGHT; + break; + default: + if (battle_config.error_log) + printf ("UnknownStatusChange [%d]\n", type); + return 0; + } + + if (bl->type == BL_PC && type < SC_SENDMAX) + clif_status_change (bl, type, 1); /* アイコン表示 */ + + /* optionの変更 */ + switch (type) + { + case SC_STONE: + case SC_FREEZE: + case SC_STAN: + case SC_SLEEP: + battle_stopattack (bl); /* 攻撃停止 */ + skill_stop_dancing (bl, 0); /* 演奏/ダンスの中断 */ + { /* 同時に掛からないステータス異常を解除 */ + int i; + for (i = SC_STONE; i <= SC_SLEEP; i++) + { + if (sc_data[i].timer != -1) + { + (*sc_count)--; + delete_timer (sc_data[i].timer, + skill_status_change_timer); + sc_data[i].timer = -1; + } + } + } + if (type == SC_STONE) + *opt1 = 6; + else + *opt1 = type - SC_STONE + 1; + opt_flag = 1; + break; + case SC_POISON: + if (sc_data[SC_SLOWPOISON].timer == -1) + { + *opt2 |= 0x1; + opt_flag = 1; + } + break; + + case SC_CURSE: + case SC_SILENCE: + case SC_BLIND: + *opt2 |= 1 << (type - SC_POISON); + opt_flag = 1; + break; + case SC_SLOWPOISON: + *opt2 &= ~0x1; + *opt2 |= 0x200; + opt_flag = 1; + break; + case SC_SIGNUMCRUCIS: + *opt2 |= 0x40; + opt_flag = 1; + break; + case SC_HIDING: + case SC_CLOAKING: + battle_stopattack (bl); /* 攻撃停止 */ + *option |= ((type == SC_HIDING) ? 2 : 4); + opt_flag = 1; + break; + case SC_CHASEWALK: + battle_stopattack (bl); /* 攻撃停止 */ + *option |= 16388; + opt_flag = 1; + break; + case SC_SIGHT: + *option |= 1; + opt_flag = 1; + break; + case SC_RUWACH: + *option |= 8192; + opt_flag = 1; + break; + case SC_WEDDING: + *option |= 4096; + opt_flag = 1; + } + + 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; + if (sc_data[type].spell_invocation) // Supplant by newer spell + spell_effect_report_termination (sc_data[type].spell_invocation, + bl->id, type, 1); + + sc_data[type].spell_invocation = spell_invocation; + + /* タイマー設定 */ + sc_data[type].timer = + add_timer (gettick () + tick, skill_status_change_timer, bl->id, + type); + + if (bl->type == BL_PC && calc_flag) + pc_calcstatus (sd, 0); /* ステータス再計算 */ + + if (bl->type == BL_PC && updateflag) + clif_updatestatus (sd, updateflag); /* ステータスをクライアントに送る */ + + return 0; +} + +/*========================================== + * ステータス異常全解除 + *------------------------------------------ + */ +int skill_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 = battle_get_sc_data (bl)); + nullpo_retr (0, sc_count = battle_get_sc_count (bl)); + nullpo_retr (0, option = battle_get_option (bl)); + nullpo_retr (0, opt1 = battle_get_opt1 (bl)); + nullpo_retr (0, opt2 = battle_get_opt2 (bl)); + nullpo_retr (0, opt3 = battle_get_opt3 (bl)); + + if (*sc_count == 0) + return 0; + for (i = 0; i < MAX_STATUSCHANGE; i++) + { + if (sc_data[i].timer != -1) + { /* 異常があるならタイマーを削除する */ +/* + delete_timer(sc_data[i].timer, skill_status_change_timer); + sc_data[i].timer = -1; + + if (!type && i < SC_SENDMAX) + clif_status_change(bl, i, 0); +*/ + + skill_status_change_end (bl, i, -1); + } + } + *sc_count = 0; + *opt1 = 0; + *opt2 = 0; + *opt3 = 0; + *option &= OPTION_MASK; + + if (night_flag == 1 && type == BL_PC) // by [Yor] + *opt2 |= STATE_BLIND; + + if (!type || type & 2) + clif_changeoption (bl); + + return 0; +} + +/* クローキング検査(周りに移動不可能地帯があるか) */ +int skill_check_cloaking (struct block_list *bl) +{ + struct map_session_data *sd = NULL; + static int dx[] = { -1, 0, 1, -1, 1, -1, 0, 1 }; + static int dy[] = { -1, -1, -1, 0, 0, 1, 1, 1 }; + int end = 1, i; + + nullpo_retr (0, bl); + + if (pc_checkskill (sd, AS_CLOAKING) > 2) + return 0; + if (bl->type == BL_PC && battle_config.pc_cloak_check_type & 1) + return 0; + if (bl->type == BL_MOB && battle_config.monster_cloak_check_type & 1) + return 0; + for (i = 0; i < sizeof (dx) / sizeof (dx[0]); i++) + { + int c = map_getcell (bl->m, bl->x + dx[i], bl->y + dy[i]); + if (c == 1 || c == 5) + end = 0; + } + if (end) + { + skill_status_change_end (bl, SC_CLOAKING, -1); + *battle_get_option (bl) &= ~4; /* 念のための処理 */ + } + return end; +} + +/* + *---------------------------------------------------------------------------- + * スキルユニット + *---------------------------------------------------------------------------- + */ + +/*========================================== + * 演奏/ダンススキルかどうか判定 + * 引数 スキルID + * 戻り ダンスじゃない=0 合奏=2 それ以外のダンス=1 + *------------------------------------------ + */ +int skill_is_danceskill (int id) +{ + int i; + switch (id) + { + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + i = 2; + break; + case BA_DISSONANCE: /* 不協和音 */ + case BA_FROSTJOKE: /* 寒いジョーク */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_SCREAM: /* スクリーム */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + i = 1; + break; + default: + i = 0; + } + return i; +} + +/*========================================== + * 演奏/ダンスをやめる + * flag 1で合奏中なら相方にユニットを任せる + * + *------------------------------------------ + */ +void skill_stop_dancing (struct block_list *src, int flag) +{ + struct status_change *sc_data; + struct skill_unit_group *group; + + nullpo_retv (src); + + sc_data = battle_get_sc_data (src); + if (sc_data && sc_data[SC_DANCING].timer == -1) + return; + group = (struct skill_unit_group *) sc_data[SC_DANCING].val2; //ダンスのスキルユニットIDはval2に入ってる + if (group && src->type == BL_PC && sc_data && sc_data[SC_DANCING].val4) + { //合奏中断 + struct map_session_data *dsd = map_id2sd (sc_data[SC_DANCING].val4); //相方のsd取得 + if (flag) + { //ログアウトなど片方が落ちても演奏が継続される + if (dsd && src->id == group->src_id) + { //グループを持ってるPCが落ちる + group->src_id = sc_data[SC_DANCING].val4; //相方にグループを任せる + if (flag & 1) //ログアウト + dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 + if (flag & 2) //ハエ飛びなど + return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり + } + else if (dsd && dsd->bl.id == group->src_id) + { //相方がグループを持っているPCが落ちる(自分はグループを持っていない) + if (flag & 1) //ログアウト + dsd->sc_data[SC_DANCING].val4 = 0; //相方の相方を0にして合奏終了→通常のダンス状態 + if (flag & 2) //ハエ飛びなど + return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり + } + skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる + //そしてグループは消さない&消さないのでステータス計算もいらない? + return; + } + else + { + if (dsd && src->id == group->src_id) + { //グループを持ってるPCが止める + skill_status_change_end ((struct block_list *) dsd, SC_DANCING, -1); //相手のステータスを終了させる + } + if (dsd && dsd->bl.id == group->src_id) + { //相方がグループを持っているPCが止める(自分はグループを持っていない) + skill_status_change_end (src, SC_DANCING, -1); //自分のステータスを終了させる + } + } + } + if (flag & 2 && group && src->type == BL_PC) + { //ハエで飛んだときとかはユニットも飛ぶ + struct map_session_data *sd = (struct map_session_data *) src; + skill_unit_move_unit_group (group, sd->bl.m, (sd->to_x - sd->bl.x), + (sd->to_y - sd->bl.y)); + return; + } + skill_delunitgroup (group); + if (src->type == BL_PC) + pc_calcstatus ((struct map_session_data *) src, 0); +} + +/*========================================== + * スキルユニット初期化 + *------------------------------------------ + */ +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); + return unit; +} + +int skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap); +/*========================================== + * スキルユニット削除 + *------------------------------------------ + */ +int skill_delunit (struct skill_unit *unit) +{ + struct skill_unit_group *group; + int range; + + nullpo_retr (0, unit); + if (!unit->alive) + return 0; + nullpo_retr (0, group = unit->group); + + /* onlimitイベント呼び出し */ + skill_unit_onlimit (unit, gettick ()); + + /* ondeleteイベント呼び出し */ + range = group->range; + map_foreachinarea (skill_unit_timer_sub_ondelete, unit->bl.m, + unit->bl.x - range, unit->bl.y - range, + unit->bl.x + range, unit->bl.y + range, 0, &unit->bl, + gettick ()); + + clif_skill_delunit (unit); + + unit->group = NULL; + unit->alive = 0; + map_delobjectnofree (unit->bl.id, BL_SKILL); + if (group->alive_count > 0 && (--group->alive_count) <= 0) + skill_delunitgroup (group); + + return 0; +} + +/*========================================== + * スキルユニットグループ初期化 + *------------------------------------------ + */ +static int skill_unit_group_newid = 10; +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; + + 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; + } + 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) + { + printf ("skill_initunitgroup: error unit group !\n"); + exit (1); + } + + group->src_id = src->id; + group->party_id = battle_get_party_id (src); + group->guild_id = battle_get_guild_id (src); + group->group_id = skill_unit_group_newid++; + if (skill_unit_group_newid <= 0) + skill_unit_group_newid = 10; + CREATE (group->unit, struct skill_unit, count); + 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->range = 0; + group->limit = 10000; + group->interval = 1000; + group->tick = gettick (); + group->valstr = NULL; + + if (skill_is_danceskill (skillid)) + { + 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; + } + skill_status_change_start (src, SC_DANCING, skillid, (int) group, 0, + 0, skill_get_time (skillid, + skilllv) + 1000, 0); + switch (skillid) + { //合奏スキルは相方をダンス状態にする + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + { + int range = 1; + int c = 0; + if (sd) + { + map_foreachinarea (skill_check_condition_use_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); + } + } + } + } + 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 (skill_is_danceskill (group->skill_id)) + { //ダンススキルはダンス状態を解除する + if (src) + skill_status_change_end (src, SC_DANCING, -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) + { + map_freeblock (group->valstr); + group->valstr = NULL; + } + + map_freeblock (group->unit); /* free()の替わり */ + 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; + } + 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, + int group_id) +{ + int i, j = 0, k, s = group_id % MAX_SKILLUNITGROUPTICKSET; + struct skill_unit_group_tickset *set = NULL; + + nullpo_retr (0, bl); + + if (bl->type == BL_PC) + { + set = ((struct map_session_data *) bl)->skillunittick; + } + else + { + set = ((struct mob_data *) bl)->skillunittick; + } + if (set == NULL) + return 0; + for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) + if (set[(k = (i + s) % MAX_SKILLUNITGROUPTICKSET)].group_id == + group_id) + return &set[k]; + else if (set[k].group_id == 0) + j = k; + + return &set[j]; +} + +/*========================================== + * スキルユニットグループの被影響tick削除 + *------------------------------------------ + */ +int skill_unitgrouptickset_delete (struct block_list *bl, int group_id) +{ + int i, s = group_id % MAX_SKILLUNITGROUPTICKSET; + struct skill_unit_group_tickset *set = NULL, *ts; + + nullpo_retr (0, bl); + + if (bl->type == BL_PC) + { + set = ((struct map_session_data *) bl)->skillunittick; + } + else + { + set = ((struct mob_data *) bl)->skillunittick; + } + + if (set != NULL) + { + + for (i = 0; i < MAX_SKILLUNITGROUPTICKSET; i++) + if ((ts = + &set[(i + s) % MAX_SKILLUNITGROUPTICKSET])->group_id == + group_id) + ts->group_id = 0; + + } + return 0; +} + +/*========================================== + * スキルユニットタイマー発動処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_timer_sub_onplace (struct block_list *bl, va_list ap) +{ + struct block_list *src; + struct skill_unit *su; + unsigned int tick; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + src = va_arg (ap, struct block_list *); + + tick = va_arg (ap, unsigned int); + su = (struct skill_unit *) src; + + if (su && su->alive) + { + struct skill_unit_group *sg; + sg = su->group; + if (sg && battle_check_target (src, bl, sg->target_flag) > 0) + skill_unit_onplace (su, bl, tick); + } + return 0; +} + +/*========================================== + * スキルユニットタイマー削除処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_timer_sub_ondelete (struct block_list *bl, va_list ap) +{ + struct block_list *src; + struct skill_unit *su; + unsigned int tick; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + src = va_arg (ap, struct block_list *); + + tick = va_arg (ap, unsigned int); + su = (struct skill_unit *) src; + + if (su && su->alive) + { + struct skill_unit_group *sg; + sg = su->group; + if (sg && battle_check_target (src, bl, sg->target_flag) > 0) + skill_unit_ondelete (su, 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); + nullpo_retr (0, group = unit->group); + tick = va_arg (ap, unsigned int); + + if (!unit->alive) + return 0; + + range = (unit->range != 0) ? unit->range : group->range; + + /* onplaceイベント呼び出し */ + if (unit->alive && unit->range >= 0) + { + map_foreachinarea (skill_unit_timer_sub_onplace, bl->m, + bl->x - range, bl->y - range, bl->x + range, + bl->y + range, 0, bl, tick); + if (group->unit_id == 0xaa + && DIFF_TICK (tick, group->tick) >= 6000 * group->val2) + { + map_foreachinarea (skill_idun_heal, bl->m, + bl->x - range, bl->y - range, bl->x + range, + bl->y + range, 0, unit); + group->val2++; + } + } + /* 時間切れ削除 */ + if (unit->alive && + (DIFF_TICK (tick, group->tick) >= group->limit + || DIFF_TICK (tick, group->tick) >= unit->limit)) + { + switch (group->unit_id) + { + + case 0x8f: /* ブラストマイン */ + group->unit_id = 0x8c; + 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 0x90: /* スキッドトラップ */ + case 0x91: /* アンクルスネア */ + case 0x93: /* ランドマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + case 0x99: /* トーキーボックス */ + { + struct block_list *src = map_id2bl (group->src_id); + if (group->unit_id == 0x91 && group->val2); + else + { + if (src && src->type == BL_PC) + { + 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); // 罠返還 + } + } + } + default: + skill_delunit (unit); + } + } + + if (group->unit_id == 0x8d) + { + unit->val1 -= 5; + if (unit->val1 <= 0 && unit->limit + group->tick > tick + 700) + unit->limit = DIFF_TICK (tick + 700, group->tick); + } + + return 0; +} + +/*========================================== + * スキルユニットタイマー処理 + *------------------------------------------ + */ +void skill_unit_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + map_freeblock_lock (); + + map_foreachobject (skill_unit_timer_sub, BL_SKILL, tick); + + map_freeblock_unlock (); +} + +/*========================================== + * スキルユニット移動時処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_out_all_sub (struct block_list *bl, va_list ap) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + 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 *) bl); + nullpo_retr (0, group = unit->group); + + tick = va_arg (ap, unsigned int); + + if (!unit->alive || src->prev == NULL) + return 0; + + range = (unit->range != 0) ? unit->range : group->range; + + if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) + return 0; + + if (src->x >= bl->x - range && src->x <= bl->x + range && + src->y >= bl->y - range && src->y <= bl->y + range) + skill_unit_onout (unit, src, tick); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理 + *------------------------------------------ + */ +int skill_unit_out_all (struct block_list *bl, unsigned int tick, int range) +{ + nullpo_retr (0, bl); + + if (bl->prev == NULL) + return 0; + + if (range < 7) + range = 7; + map_foreachinarea (skill_unit_out_all_sub, + bl->m, bl->x - range, bl->y - range, bl->x + range, + bl->y + range, BL_SKILL, bl, tick); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_move_sub (struct block_list *bl, va_list ap) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, unit = (struct skill_unit *) bl); + nullpo_retr (0, src = va_arg (ap, struct block_list *)); + + tick = va_arg (ap, unsigned int); + + if (!unit->alive || src->prev == NULL) + return 0; + + if ((group = unit->group) == NULL) + return 0; + range = (unit->range != 0) ? unit->range : group->range; + + if (range < 0 || battle_check_target (bl, src, group->target_flag) <= 0) + return 0; + + if (src->x >= bl->x - range && src->x <= bl->x + range && + src->y >= bl->y - range && src->y <= bl->y + range) + skill_unit_onplace (unit, src, tick); + else + skill_unit_onout (unit, src, tick); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理 + *------------------------------------------ + */ +int skill_unit_move (struct block_list *bl, unsigned int tick, int range) +{ + nullpo_retr (0, bl); + + if (bl->prev == NULL) + return 0; + + if (range < 7) + range = 7; + map_foreachinarea (skill_unit_move_sub, + bl->m, bl->x - range, bl->y - range, bl->x + range, + bl->y + range, BL_SKILL, bl, tick); + + return 0; +} + +/*========================================== + * スキルユニット自体の移動時処理(foreachinarea) + *------------------------------------------ + */ +int skill_unit_move_unit_group_sub (struct block_list *bl, va_list ap) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + 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, group = unit->group); + + tick = va_arg (ap, unsigned int); + + if (!unit->alive || bl->prev == NULL) + return 0; + + range = (unit->range != 0) ? unit->range : group->range; + + if (range < 0 || battle_check_target (src, bl, group->target_flag) <= 0) + return 0; + if (bl->x >= src->x - range && bl->x <= src->x + range && + bl->y >= src->y - range && bl->y <= src->y + range) + skill_unit_onplace (unit, bl, tick); + else + skill_unit_onout (unit, bl, tick); + return 0; +} + +/*========================================== + * スキルユニット自体の移動時処理 + * 引数はグループと移動量 + *------------------------------------------ + */ +int skill_unit_move_unit_group (struct skill_unit_group *group, int m, int dx, + int dy) +{ + nullpo_retr (0, group); + + if (group->unit_count <= 0) + return 0; + + if (group->unit != NULL) + { + if (!battle_config.unit_movement_type) + { + int i; + for (i = 0; i < group->unit_count; i++) + { + struct skill_unit *unit = &group->unit[i]; + if (unit->alive && !(m == unit->bl.m && dx == 0 && dy == 0)) + { + int range = unit->range; + map_delblock (&unit->bl); + unit->bl.m = m; + unit->bl.x += dx; + unit->bl.y += dy; + map_addblock (&unit->bl); + clif_skill_setunit (unit); + if (range > 0) + { + if (range < 7) + range = 7; + map_foreachinarea (skill_unit_move_unit_group_sub, + unit->bl.m, unit->bl.x - range, + unit->bl.y - range, + unit->bl.x + range, + unit->bl.y + range, 0, &unit->bl, + gettick ()); + } + } + } + } + else + { + int i, j, *r_flag, *s_flag, *m_flag; + struct skill_unit *unit1; + struct skill_unit *unit2; + r_flag = (int *) malloc (sizeof (int) * group->unit_count); + s_flag = (int *) malloc (sizeof (int) * group->unit_count); + m_flag = (int *) malloc (sizeof (int) * group->unit_count); + memset (r_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ + memset (s_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ + memset (m_flag, 0, sizeof (int) * group->unit_count); // 継承フラグ + + //先にフラグを全部決める + for (i = 0; i < group->unit_count; i++) + { + int move_check = 0; // かぶりフラグ + unit1 = &group->unit[i]; + for (j = 0; j < group->unit_count; j++) + { + unit2 = &group->unit[j]; + if (unit1->bl.m == m && unit1->bl.x + dx == unit2->bl.x + && unit1->bl.y + dy == unit2->bl.y) + { + //移動先にユニットがかぶってたら + s_flag[i] = 1; // 移動前のユニットナンバーの継承フラグon + r_flag[j] = 1; // かぶるユニットナンバーの残留フラグon + move_check = 1; //ユニットがかぶった。 + break; + } + } + if (!move_check) // ユニットがかぶってなかったら + m_flag[i] = 1; // 移動前ユニットナンバーの移動フラグon + } + + //フラグに基づいてユニット移動 + for (i = 0; i < group->unit_count; i++) + { + unit1 = &group->unit[i]; + if (m_flag[i]) + { // 移動フラグがonで + if (!r_flag[i]) + { // 残留フラグがoffなら + //単純移動(rangeも継承の必要無し) + int range = unit1->range; + map_delblock (&unit1->bl); + unit1->bl.m = m; + unit1->bl.x += dx; + unit1->bl.y += dy; + map_addblock (&unit1->bl); + clif_skill_setunit (unit1); + if (range > 0) + { + if (range < 7) + range = 7; + map_foreachinarea (skill_unit_move_unit_group_sub, + unit1->bl.m, + unit1->bl.x - range, + unit1->bl.y - range, + unit1->bl.x + range, + unit1->bl.y + range, 0, + &unit1->bl, gettick ()); + } + } + else + { // 残留フラグがonなら + //空ユニットになるので、継承可能なユニットを探す + for (j = 0; j < group->unit_count; j++) + { + unit2 = &group->unit[j]; + if (s_flag[j] && !r_flag[j]) + { + // 継承移動(range継承付き) + int range = unit1->range; + map_delblock (&unit2->bl); + unit2->bl.m = m; + unit2->bl.x = unit1->bl.x + dx; + unit2->bl.y = unit1->bl.y + dy; + unit2->range = unit1->range; + map_addblock (&unit2->bl); + clif_skill_setunit (unit2); + if (range > 0) + { + if (range < 7) + range = 7; + map_foreachinarea + (skill_unit_move_unit_group_sub, + unit2->bl.m, unit2->bl.x - range, + unit2->bl.y - range, + unit2->bl.x + range, + unit2->bl.y + range, 0, &unit2->bl, + gettick ()); + } + s_flag[j] = 0; // 継承完了したのでoff + break; + } + } + } + } + } + free (r_flag); + free (s_flag); + free (m_flag); + } + } + return 0; +} + +/*---------------------------------------------------------------------------- + * アイテム合成 + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * 初期化系 + */ + +static int scan_stat (char *statname) +{ + if (!strcasecmp (statname, "str")) + return SP_STR; + if (!strcasecmp (statname, "dex")) + return SP_DEX; + if (!strcasecmp (statname, "agi")) + return SP_AGI; + if (!strcasecmp (statname, "vit")) + return SP_VIT; + if (!strcasecmp (statname, "int")) + return SP_INT; + if (!strcasecmp (statname, "luk")) + return SP_LUK; + if (!strcasecmp (statname, "none")) + return 0; + + else + fprintf (stderr, "Unknown stat `%s'\n", statname); + return 0; +} + +extern void skill_pool_register (int id); // [Fate] Remember that a certain skill ID belongs to a pool skill + +/*========================================== + * スキル関係ファイル読み込み + * skill_db.txt スキルデータ + * skill_cast_db.txt スキルの詠唱時間とディレイデータ + *------------------------------------------ + */ +int skill_readdb (void) +{ + int i, j, k, l; + FILE *fp; + char line[1024], *p; + + /* The main skill database */ + memset (skill_db, 0, sizeof (skill_db)); + fp = fopen_ ("db/skill_db.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/skill_db.txt\n"); + return 1; + } + while (fgets (line, 1020, fp)) + { + char *split[50], *split2[MAX_SKILL_LEVEL]; + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 18 && p; j++) + { + while (*p == '\t' || *p == ' ') + p++; + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + if (split[17] == NULL || j < 18) + { + fprintf (stderr, "Incomplete skill db data online (%d entries)\n", + j); + continue; + } + + i = atoi (split[0]); + if (i < 0 || i > MAX_SKILL_DB) + continue; + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].range[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + 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_raise = atoi (split[6]); + skill_db[i].max = atoi (split[7]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[8]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].num[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + if (strcasecmp (split[9], "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 (strcasecmp (split[13], "weapon") == 0) + skill_db[i].skill_type = BF_WEAPON; + else if (strcasecmp (split[12], "magic") == 0) + skill_db[i].skill_type = BF_MAGIC; + else if (strcasecmp (split[12], "misc") == 0) + skill_db[i].skill_type = BF_MISC; + else + skill_db[i].skill_type = 0; + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[14]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].blewcount[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + if (!strcasecmp (split[15], "passive")) + { + skill_pool_register (i); + skill_db[i].poolflags = SKILL_POOL_FLAG; + } + else if (!strcasecmp (split[15], "active")) + { + skill_pool_register (i); + skill_db[i].poolflags = SKILL_POOL_FLAG | SKILL_POOL_ACTIVE; + } + else + skill_db[i].poolflags = 0; + + skill_db[i].stat = scan_stat (split[16]); + + skill_names[i].desc = strdup (split[17]); + { // replace "_" by " " + char *s = skill_names[i].desc; + while ((s = strchr (s, '_'))) + *s = ' '; + if ((s = strchr (skill_names[i].desc, '\t')) + || (s = strchr (skill_names[i].desc, ' ')) + || (s = strchr (skill_names[i].desc, '\n'))) + *s = '\000'; + } + } + fclose_ (fp); + printf ("read db/skill_db.txt done\n"); + + fp = fopen_ ("db/skill_require_db.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/skill_require_db.txt\n"); + return 1; + } + while (fgets (line, 1020, fp)) + { + char *split[51], *split2[MAX_SKILL_LEVEL]; + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 30 && p; j++) + { + while (*p == '\t' || *p == ' ') + p++; + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + if (split[29] == NULL || j < 30) + continue; + + i = atoi (split[0]); + if (i < 0 || i > MAX_SKILL_DB) + continue; + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].hp[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].mhp[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].sp[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].hp_rate[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[5]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].sp_rate[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[6]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].zeny[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[7]; j < 32 && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < 32 && split2[k]; k++) + { + l = atoi (split2[k]); + if (l == 99) + { + skill_db[i].weapon = 0xffffffff; + break; + } + else + skill_db[i].weapon |= 1 << l; + } + + if (strcasecmp (split[8], "hiding") == 0) + skill_db[i].state = ST_HIDING; + else if (strcasecmp (split[8], "cloaking") == 0) + skill_db[i].state = ST_CLOAKING; + else if (strcasecmp (split[8], "hidden") == 0) + skill_db[i].state = ST_HIDDEN; + else if (strcasecmp (split[8], "riding") == 0) + skill_db[i].state = ST_RIDING; + else if (strcasecmp (split[8], "falcon") == 0) + skill_db[i].state = ST_FALCON; + else if (strcasecmp (split[8], "cart") == 0) + skill_db[i].state = ST_CART; + else if (strcasecmp (split[8], "shield") == 0) + skill_db[i].state = ST_SHIELD; + else if (strcasecmp (split[8], "sight") == 0) + skill_db[i].state = ST_SIGHT; + else if (strcasecmp (split[8], "explosionspirits") == 0) + skill_db[i].state = ST_EXPLOSIONSPIRITS; + else if (strcasecmp (split[8], "recover_weight_rate") == 0) + skill_db[i].state = ST_RECOV_WEIGHT_RATE; + else if (strcasecmp (split[8], "move_enable") == 0) + skill_db[i].state = ST_MOVE_ENABLE; + else if (strcasecmp (split[8], "water") == 0) + skill_db[i].state = ST_WATER; + else + skill_db[i].state = ST_NONE; + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[9]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].spiritball[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + 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); + printf ("read db/skill_require_db.txt done\n"); + + /* ? */ + fp = fopen_ ("db/skill_cast_db.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/skill_cast_db.txt\n"); + return 1; + } + while (fgets (line, 1020, fp)) + { + char *split[50], *split2[MAX_SKILL_LEVEL]; + memset (split, 0, sizeof (split)); // [Valaris] thanks to fov + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 5 && p; j++) + { + while (*p == '\t' || *p == ' ') + p++; + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + if (split[4] == NULL || j < 5) + continue; + + i = atoi (split[0]); + if (i < 0 || i > MAX_SKILL_DB) + continue; + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].cast[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[2]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].delay[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[3]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].upkeep_time[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[4]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].upkeep_time2[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + } + fclose_ (fp); + printf ("read db/skill_cast_db.txt done\n"); + + fp = fopen_ ("db/skill_castnodex_db.txt", "r"); + if (fp == NULL) + { + printf ("can't read db/skill_castnodex_db.txt\n"); + return 1; + } + while (fgets (line, 1020, fp)) + { + char *split[50], *split2[MAX_SKILL_LEVEL]; + memset (split, 0, sizeof (split)); + if (line[0] == '/' && line[1] == '/') + continue; + for (j = 0, p = line; j < 2 && p; j++) + { + while (*p == '\t' || *p == ' ') + p++; + split[j] = p; + p = strchr (p, ','); + if (p) + *p++ = 0; + } + + i = atoi (split[0]); + if (i < 0 || i > MAX_SKILL_DB) + continue; + + memset (split2, 0, sizeof (split2)); + for (j = 0, p = split[1]; j < MAX_SKILL_LEVEL && p; j++) + { + split2[j] = p; + p = strchr (p, ':'); + if (p) + *p++ = 0; + } + for (k = 0; k < MAX_SKILL_LEVEL; k++) + skill_db[i].castnodex[k] = + (split2[k]) ? atoi (split2[k]) : atoi (split2[0]); + } + fclose_ (fp); + printf ("read db/skill_castnodex_db.txt done\n"); + + return 0; +} + +void skill_reload (void) +{ + /* + * + * <empty skill database> + * <?> + * + */ + + do_init_skill (); +} + +/*========================================== + * スキル関係初期化処理 + *------------------------------------------ + */ +int do_init_skill (void) +{ + skill_readdb (); + + 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 deleted file mode 100644 index 6c8795a..0000000 --- a/src/map/skill.h +++ /dev/null @@ -1,891 +0,0 @@ -// $Id: skill.h,v 1.5 2004/09/25 05:32:19 MouseJstr Exp $ -#ifndef _SKILL_H_ -#define _SKILL_H_ - -#include "../common/timer.h" - -#include "map.h" -#include "magic.h" - -#define MAX_SKILL_DB 450 -#define MAX_SKILL_PRODUCE_DB 150 -#define MAX_SKILL_ARROW_DB 150 -#define MAX_SKILL_ABRA_DB 350 - -#define SKILL_POOL_FLAG 0x1 // is a pool skill -#define SKILL_POOL_ACTIVE 0x2 // is an active pool skill -#define SKILL_POOL_ACTIVATED 0x4 // pool skill has been activated (used for clif) - -// スキルデータベース -struct skill_db -{ - int range[MAX_SKILL_LEVEL], hit, inf, pl, nk, max, stat, poolflags, max_raise; // `max' is the global max, `max_raise' is the maximum attainable via skill-ups - 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]; -}; -extern struct skill_db skill_db[MAX_SKILL_DB]; - -struct skill_name_db -{ - int id; // skill id - const char *name; // search strings - const char *desc; // description that shows up for search's -}; -extern struct skill_name_db skill_names[]; - -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_max_raise (int id); -int skill_get_range (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_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); - -// ユニットスキル -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); -struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct - block_list - *bl, - int group_id); -int skill_unitgrouptickset_delete (struct block_list *bl, int group_id); -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 time); -int skill_delayfix (struct block_list *bl, int time); -int skill_check_unit_range (int m, int x, int y, int range, int skillid); -int skill_check_unit_range2 (int m, int x, int y, int range); -// -- 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 range); -int skill_unit_move_unit_group (struct skill_unit_group *group, int m, - int dx, int dy); - -struct skill_unit_group *skill_check_dancing (struct block_list *src); -void skill_stop_dancing (struct block_list *src, int flag); - -// 詠唱キャンセル -int skill_castcancel (struct block_list *bl, int type); - -int skill_gangsterparadise (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); -int skill_autospell (struct map_session_data *md, int skillid); -void skill_devotion (struct map_session_data *md, int target); -void skill_devotion2 (struct block_list *bl, int crusader); -int skill_devotion3 (struct block_list *bl, int target); -void skill_devotion_end (struct map_session_data *md, - struct map_session_data *sd, int target); - -#define skill_calc_heal(bl,skill_lv) (( battle_get_lv(bl)+battle_get_int(bl) )/8 *(4+ skill_lv*8)) - -// その他 -int skill_check_cloaking (struct block_list *bl); -int skill_is_danceskill (int id); - -// ステータス異常 -int skill_status_effect (struct block_list *bl, int type, int val1, int val2, - int val3, int val4, int tick, int flag, - int spell_invocation); -int skill_status_change_start (struct block_list *bl, int type, int val1, - int val2, int val3, int val4, int tick, - int flag); -void skill_status_change_timer (timer_id, tick_t, custom_id_t, custom_data_t); -int skill_status_change_active (struct block_list *bl, int type); // [fate] -int skill_encchant_eremental_end (struct block_list *bl, int type); -int skill_status_change_end (struct block_list *bl, int type, int tid); -int skill_status_change_clear (struct block_list *bl, int type); - -// 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); - -int skill_update_heal_animation (struct map_session_data *sd); // [Fate] Check whether the healing flag must be updated, do so if needed - -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_RECOV_WEIGHT_RATE, ST_MOVE_ENABLE, ST_WATER, -}; - -enum -{ // struct map_session_data の status_changeの番号テーブル - SC_SENDMAX = 256, - SC_PROVOKE = 0, - SC_ENDURE = 1, - SC_TWOHANDQUICKEN = 2, - SC_CONCENTRATE = 3, - SC_HIDING = 4, - SC_CLOAKING = 5, - SC_ENCPOISON = 6, - SC_POISONREACT = 7, - SC_QUAGMIRE = 8, - SC_ANGELUS = 9, - SC_BLESSING = 10, - SC_SIGNUMCRUCIS = 11, - SC_INCREASEAGI = 12, - SC_DECREASEAGI = 13, - SC_SLOWPOISON = 14, - SC_IMPOSITIO = 15, - SC_SUFFRAGIUM = 16, - SC_ASPERSIO = 17, - SC_BENEDICTIO = 18, - SC_KYRIE = 19, - SC_MAGNIFICAT = 20, - SC_GLORIA = 21, - SC_AETERNA = 22, - SC_ADRENALINE = 23, - SC_WEAPONPERFECTION = 24, - SC_OVERTHRUST = 25, - SC_MAXIMIZEPOWER = 26, - SC_RIDING = 27, - SC_FALCON = 28, - SC_TRICKDEAD = 29, - SC_LOUD = 30, - SC_ENERGYCOAT = 31, - SC_BROKNARMOR = 32, - SC_BROKNWEAPON = 33, - SC_HALLUCINATION = 34, - SC_WEIGHT50 = 35, - SC_WEIGHT90 = 36, - SC_SPEEDPOTION0 = 37, - SC_SPEEDPOTION1 = 38, - SC_SPEEDPOTION2 = 39, - - SC_STRIPWEAPON = 50, - SC_STRIPSHIELD = 51, - SC_STRIPARMOR = 52, - SC_STRIPHELM = 53, - SC_CP_WEAPON = 54, - SC_CP_SHIELD = 55, - SC_CP_ARMOR = 56, - SC_CP_HELM = 57, - SC_AUTOGUARD = 58, - SC_REFLECTSHIELD = 59, - SC_DEVOTION = 60, - SC_PROVIDENCE = 61, - SC_DEFENDER = 62, - SC_AUTOSPELL = 65, - SC_EXPLOSIONSPIRITS = 86, - SC_STEELBODY = 87, - SC_SPEARSQUICKEN = 68, - - SC_HEALING = 70, - - SC_SIGHTTRASHER = 73, - - SC_COMBO = 89, - SC_FLAMELAUNCHER = 90, - SC_FROSTWEAPON = 91, - SC_LIGHTNINGLOADER = 92, - SC_SEISMICWEAPON = 93, - - SC_AURABLADE = 103, - SC_PARRYING = 104, - SC_CONCENTRATION = 105, - SC_TENSIONRELAX = 106, - SC_BERSERK = 107, - - SC_ASSUMPTIO = 110, - - SC_MAGICPOWER = 113, - - SC_TRUESIGHT = 115, - SC_WINDWALK = 116, - SC_MELTDOWN = 117, - SC_CARTBOOST = 118, - - SC_REJECTSWORD = 120, - SC_MARIONETTE = 121, - - SC_HEADCRUSH = 124, - SC_JOINTBEAT = 125, - SC_BASILICA = 125, - - SC_STONE = 128, - SC_FREEZE = 129, - SC_STAN = 130, - SC_SLEEP = 131, - SC_POISON = 132, - SC_CURSE = 133, - SC_SILENCE = 134, - SC_CONFUSION = 135, - SC_BLIND = 136, - - SC_SAFETYWALL = 140, - SC_PNEUMA = 141, - SC_WATERBALL = 142, - SC_ANKLE = 143, - SC_DANCING = 144, - SC_KEEPING = 145, - SC_BARRIER = 146, - - SC_MAGICROD = 149, - SC_SIGHT = 150, - SC_RUWACH = 151, - SC_AUTOCOUNTER = 152, - SC_VOLCANO = 153, - SC_DELUGE = 154, - SC_VIOLENTGALE = 155, - SC_BLADESTOP_WAIT = 156, - SC_BLADESTOP = 157, - SC_EXTREMITYFIST = 158, - SC_GRAFFITI = 159, - SC_ENSEMBLE = 159, - - SC_LULLABY = 160, - SC_RICHMANKIM = 161, - SC_ETERNALCHAOS = 162, - SC_DRUMBATTLE = 163, - SC_NIBELUNGEN = 164, - SC_ROKISWEIL = 165, - SC_INTOABYSS = 166, - SC_SIEGFRIED = 167, - SC_DISSONANCE = 168, - SC_WHISTLE = 169, - SC_ASSNCROS = 170, - SC_POEMBRAGI = 171, - SC_APPLEIDUN = 172, - SC_UGLYDANCE = 173, - SC_HUMMING = 174, - SC_DONTFORGETME = 175, - SC_FORTUNE = 176, - SC_SERVICE4U = 177, - SC_FOGWALL = 178, - SC_GOSPEL = 179, - SC_SPIDERWEB = 180, - SC_MEMORIZE = 181, - SC_LANDPROTECTOR = 182, - SC_ADAPTATION = 183, - SC_CHASEWALK = 184, - SC_ATKPOT = 185, - SC_MATKPOT = 186, - SC_WEDDING = 187, - SC_NOCHAT = 188, - SC_SPLASHER = 189, - SC_SELFDESTRUCTION = 190, - SC_MINDBREAKER = 191, - SC_SPELLBREAKER = 192, - -// Added for Fate's spells - SC_HIDE = 194, // Hide from `detect' magic - SC_HALT_REGENERATE = 195, // Suspend regeneration - SC_FLYING_BACKPACK = 196, // Flying backpack - SC_MBARRIER = 197, // Magical barrier, magic resistance (val1 : power (%)) - SC_HASTE = 198, // `Haste' spell (val1 : power) - SC_PHYS_SHIELD = 199, // `Protect' spell, reduce damage (val1: power) - - SC_DIVINA = SC_SILENCE, -}; -extern int SkillStatusChangeTable[]; - -enum -{ - NV_EMOTE = 1, - NV_TRADE, - NV_PARTY, - - 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 = 45, - 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_SELFDESTRUCTION2 = 333, - - WE_MALE = 334, - WE_FEMALE, - WE_CALLPARTNER, - - NPC_DARKCROSS = 338, - - TMW_SKILLPOOL = 339, // skill pool size - - TMW_MAGIC = 340, - TMW_MAGIC_LIFE = 341, - TMW_MAGIC_WAR = 342, - TMW_MAGIC_TRANSMUTE = 343, - TMW_MAGIC_NATURE = 344, - TMW_MAGIC_ETHER = 345, - TMW_MAGIC_DARK = 346, - TMW_MAGIC_LIGHT = 347, - - TMW_BRAWLING = 350, - TMW_LUCKY_COUNTER = 351, - TMW_SPEED = 352, - TMW_RESIST_POISON = 353, - TMW_ASTRAL_SOUL = 354, - TMW_RAGING = 355, - - LK_AURABLADE = 356, - 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, - - GD_APPROVAL = 10000, - GD_KAFRACONTACT, - GD_GUARDIANRESEARCH, - GD_CHARISMA, - GD_EXTENSION, -}; - -// [Fate] Skill pools API - -// Max. # of active entries in the skill pool -#define MAX_SKILL_POOL 3 -// Max. # of skills that may be classified as pool skills in db/skill_db.txt -#define MAX_POOL_SKILLS 128 - -extern int skill_pool_skills[MAX_POOL_SKILLS]; // All pool skills -extern int skill_pool_skills_size; // Number of entries in skill_pool_skills - -int skill_pool (struct map_session_data *sd, int *skills); // Yields all active skills in the skill pool; no more than MAX_SKILL_POOL. Return is number of skills. -int skill_pool_size (struct map_session_data *sd); -int skill_pool_max (struct map_session_data *sd); // Max. number of pool skills -void skill_pool_empty (struct map_session_data *sd); // Deactivate all pool skills -int skill_pool_activate (struct map_session_data *sd, int skill); // Skill into skill pool. Return is zero iff okay. -int skill_pool_is_activated (struct map_session_data *sd, int skill); // Skill into skill pool. Return is zero when activated. -int skill_pool_deactivate (struct map_session_data *sd, int skill); // Skill out of skill pool. Return is zero iff okay. -char *skill_name (int skill); // Yield configurable skill name -int skill_stat (int skill); // Yields the stat associated with a skill. Returns zero if none, or SP_STR, SP_VIT, ... otherwise -int skill_power (struct map_session_data *sd, int skill); // Yields the power of a skill. This is zero if the skill is unknown or if it's a pool skill that is outside of the skill pool, - // otherwise a value from 0 to 255 (with 200 being the `normal maximum') -int skill_power_bl (struct block_list *bl, int skill); // Yields the power of a skill. This is zero if the skill is unknown or if it's a pool skill that is outside of the skill pool, - // otherwise a value from 0 to 255 (with 200 being the `normal maximum') - -#endif diff --git a/src/map/skill.hpp b/src/map/skill.hpp new file mode 100644 index 0000000..e2aaba5 --- /dev/null +++ b/src/map/skill.hpp @@ -0,0 +1,891 @@ +// $Id: skill.h,v 1.5 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef SKILL_HPP +#define SKILL_HPP + +#include "../common/timer.hpp" + +#include "map.hpp" +#include "magic.hpp" + +#define MAX_SKILL_DB 450 +#define MAX_SKILL_PRODUCE_DB 150 +#define MAX_SKILL_ARROW_DB 150 +#define MAX_SKILL_ABRA_DB 350 + +#define SKILL_POOL_FLAG 0x1 // is a pool skill +#define SKILL_POOL_ACTIVE 0x2 // is an active pool skill +#define SKILL_POOL_ACTIVATED 0x4 // pool skill has been activated (used for clif) + +// スキルデータベース +struct skill_db +{ + int range[MAX_SKILL_LEVEL], hit, inf, pl, nk, max, stat, poolflags, max_raise; // `max' is the global max, `max_raise' is the maximum attainable via skill-ups + 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]; +}; +extern struct skill_db skill_db[MAX_SKILL_DB]; + +struct skill_name_db +{ + int id; // skill id + const char *name; // search strings + const char *desc; // description that shows up for search's +}; +extern struct skill_name_db skill_names[]; + +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_max_raise (int id); +int skill_get_range (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_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); + +// ユニットスキル +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); +struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct + block_list + *bl, + int group_id); +int skill_unitgrouptickset_delete (struct block_list *bl, int group_id); +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 time); +int skill_delayfix (struct block_list *bl, int time); +int skill_check_unit_range (int m, int x, int y, int range, int skillid); +int skill_check_unit_range2 (int m, int x, int y, int range); +// -- 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 range); +int skill_unit_move_unit_group (struct skill_unit_group *group, int m, + int dx, int dy); + +struct skill_unit_group *skill_check_dancing (struct block_list *src); +void skill_stop_dancing (struct block_list *src, int flag); + +// 詠唱キャンセル +int skill_castcancel (struct block_list *bl, int type); + +int skill_gangsterparadise (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); +int skill_autospell (struct map_session_data *md, int skillid); +void skill_devotion (struct map_session_data *md, int target); +void skill_devotion2 (struct block_list *bl, int crusader); +int skill_devotion3 (struct block_list *bl, int target); +void skill_devotion_end (struct map_session_data *md, + struct map_session_data *sd, int target); + +#define skill_calc_heal(bl,skill_lv) (( battle_get_lv(bl)+battle_get_int(bl) )/8 *(4+ skill_lv*8)) + +// その他 +int skill_check_cloaking (struct block_list *bl); +int skill_is_danceskill (int id); + +// ステータス異常 +int skill_status_effect (struct block_list *bl, int type, int val1, int val2, + int val3, int val4, int tick, int flag, + int spell_invocation); +int skill_status_change_start (struct block_list *bl, int type, int val1, + int val2, int val3, int val4, int tick, + int flag); +void skill_status_change_timer (timer_id, tick_t, custom_id_t, custom_data_t); +int skill_status_change_active (struct block_list *bl, int type); // [fate] +int skill_encchant_eremental_end (struct block_list *bl, int type); +int skill_status_change_end (struct block_list *bl, int type, int tid); +int skill_status_change_clear (struct block_list *bl, int type); + +// 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); + +int skill_update_heal_animation (struct map_session_data *sd); // [Fate] Check whether the healing flag must be updated, do so if needed + +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_RECOV_WEIGHT_RATE, ST_MOVE_ENABLE, ST_WATER, +}; + +enum +{ // struct map_session_data の status_changeの番号テーブル + SC_SENDMAX = 256, + SC_PROVOKE = 0, + SC_ENDURE = 1, + SC_TWOHANDQUICKEN = 2, + SC_CONCENTRATE = 3, + SC_HIDING = 4, + SC_CLOAKING = 5, + SC_ENCPOISON = 6, + SC_POISONREACT = 7, + SC_QUAGMIRE = 8, + SC_ANGELUS = 9, + SC_BLESSING = 10, + SC_SIGNUMCRUCIS = 11, + SC_INCREASEAGI = 12, + SC_DECREASEAGI = 13, + SC_SLOWPOISON = 14, + SC_IMPOSITIO = 15, + SC_SUFFRAGIUM = 16, + SC_ASPERSIO = 17, + SC_BENEDICTIO = 18, + SC_KYRIE = 19, + SC_MAGNIFICAT = 20, + SC_GLORIA = 21, + SC_AETERNA = 22, + SC_ADRENALINE = 23, + SC_WEAPONPERFECTION = 24, + SC_OVERTHRUST = 25, + SC_MAXIMIZEPOWER = 26, + SC_RIDING = 27, + SC_FALCON = 28, + SC_TRICKDEAD = 29, + SC_LOUD = 30, + SC_ENERGYCOAT = 31, + SC_BROKNARMOR = 32, + SC_BROKNWEAPON = 33, + SC_HALLUCINATION = 34, + SC_WEIGHT50 = 35, + SC_WEIGHT90 = 36, + SC_SPEEDPOTION0 = 37, + SC_SPEEDPOTION1 = 38, + SC_SPEEDPOTION2 = 39, + + SC_STRIPWEAPON = 50, + SC_STRIPSHIELD = 51, + SC_STRIPARMOR = 52, + SC_STRIPHELM = 53, + SC_CP_WEAPON = 54, + SC_CP_SHIELD = 55, + SC_CP_ARMOR = 56, + SC_CP_HELM = 57, + SC_AUTOGUARD = 58, + SC_REFLECTSHIELD = 59, + SC_DEVOTION = 60, + SC_PROVIDENCE = 61, + SC_DEFENDER = 62, + SC_AUTOSPELL = 65, + SC_EXPLOSIONSPIRITS = 86, + SC_STEELBODY = 87, + SC_SPEARSQUICKEN = 68, + + SC_HEALING = 70, + + SC_SIGHTTRASHER = 73, + + SC_COMBO = 89, + SC_FLAMELAUNCHER = 90, + SC_FROSTWEAPON = 91, + SC_LIGHTNINGLOADER = 92, + SC_SEISMICWEAPON = 93, + + SC_AURABLADE = 103, + SC_PARRYING = 104, + SC_CONCENTRATION = 105, + SC_TENSIONRELAX = 106, + SC_BERSERK = 107, + + SC_ASSUMPTIO = 110, + + SC_MAGICPOWER = 113, + + SC_TRUESIGHT = 115, + SC_WINDWALK = 116, + SC_MELTDOWN = 117, + SC_CARTBOOST = 118, + + SC_REJECTSWORD = 120, + SC_MARIONETTE = 121, + + SC_HEADCRUSH = 124, + SC_JOINTBEAT = 125, + SC_BASILICA = 125, + + SC_STONE = 128, + SC_FREEZE = 129, + SC_STAN = 130, + SC_SLEEP = 131, + SC_POISON = 132, + SC_CURSE = 133, + SC_SILENCE = 134, + SC_CONFUSION = 135, + SC_BLIND = 136, + + SC_SAFETYWALL = 140, + SC_PNEUMA = 141, + SC_WATERBALL = 142, + SC_ANKLE = 143, + SC_DANCING = 144, + SC_KEEPING = 145, + SC_BARRIER = 146, + + SC_MAGICROD = 149, + SC_SIGHT = 150, + SC_RUWACH = 151, + SC_AUTOCOUNTER = 152, + SC_VOLCANO = 153, + SC_DELUGE = 154, + SC_VIOLENTGALE = 155, + SC_BLADESTOP_WAIT = 156, + SC_BLADESTOP = 157, + SC_EXTREMITYFIST = 158, + SC_GRAFFITI = 159, + SC_ENSEMBLE = 159, + + SC_LULLABY = 160, + SC_RICHMANKIM = 161, + SC_ETERNALCHAOS = 162, + SC_DRUMBATTLE = 163, + SC_NIBELUNGEN = 164, + SC_ROKISWEIL = 165, + SC_INTOABYSS = 166, + SC_SIEGFRIED = 167, + SC_DISSONANCE = 168, + SC_WHISTLE = 169, + SC_ASSNCROS = 170, + SC_POEMBRAGI = 171, + SC_APPLEIDUN = 172, + SC_UGLYDANCE = 173, + SC_HUMMING = 174, + SC_DONTFORGETME = 175, + SC_FORTUNE = 176, + SC_SERVICE4U = 177, + SC_FOGWALL = 178, + SC_GOSPEL = 179, + SC_SPIDERWEB = 180, + SC_MEMORIZE = 181, + SC_LANDPROTECTOR = 182, + SC_ADAPTATION = 183, + SC_CHASEWALK = 184, + SC_ATKPOT = 185, + SC_MATKPOT = 186, + SC_WEDDING = 187, + SC_NOCHAT = 188, + SC_SPLASHER = 189, + SC_SELFDESTRUCTION = 190, + SC_MINDBREAKER = 191, + SC_SPELLBREAKER = 192, + +// Added for Fate's spells + SC_HIDE = 194, // Hide from `detect' magic + SC_HALT_REGENERATE = 195, // Suspend regeneration + SC_FLYING_BACKPACK = 196, // Flying backpack + SC_MBARRIER = 197, // Magical barrier, magic resistance (val1 : power (%)) + SC_HASTE = 198, // `Haste' spell (val1 : power) + SC_PHYS_SHIELD = 199, // `Protect' spell, reduce damage (val1: power) + + SC_DIVINA = SC_SILENCE, +}; +extern int SkillStatusChangeTable[]; + +enum +{ + NV_EMOTE = 1, + NV_TRADE, + NV_PARTY, + + 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 = 45, + 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_SELFDESTRUCTION2 = 333, + + WE_MALE = 334, + WE_FEMALE, + WE_CALLPARTNER, + + NPC_DARKCROSS = 338, + + TMW_SKILLPOOL = 339, // skill pool size + + TMW_MAGIC = 340, + TMW_MAGIC_LIFE = 341, + TMW_MAGIC_WAR = 342, + TMW_MAGIC_TRANSMUTE = 343, + TMW_MAGIC_NATURE = 344, + TMW_MAGIC_ETHER = 345, + TMW_MAGIC_DARK = 346, + TMW_MAGIC_LIGHT = 347, + + TMW_BRAWLING = 350, + TMW_LUCKY_COUNTER = 351, + TMW_SPEED = 352, + TMW_RESIST_POISON = 353, + TMW_ASTRAL_SOUL = 354, + TMW_RAGING = 355, + + LK_AURABLADE = 356, + 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, + + GD_APPROVAL = 10000, + GD_KAFRACONTACT, + GD_GUARDIANRESEARCH, + GD_CHARISMA, + GD_EXTENSION, +}; + +// [Fate] Skill pools API + +// Max. # of active entries in the skill pool +#define MAX_SKILL_POOL 3 +// Max. # of skills that may be classified as pool skills in db/skill_db.txt +#define MAX_POOL_SKILLS 128 + +extern int skill_pool_skills[MAX_POOL_SKILLS]; // All pool skills +extern int skill_pool_skills_size; // Number of entries in skill_pool_skills + +int skill_pool (struct map_session_data *sd, int *skills); // Yields all active skills in the skill pool; no more than MAX_SKILL_POOL. Return is number of skills. +int skill_pool_size (struct map_session_data *sd); +int skill_pool_max (struct map_session_data *sd); // Max. number of pool skills +void skill_pool_empty (struct map_session_data *sd); // Deactivate all pool skills +int skill_pool_activate (struct map_session_data *sd, int skill); // Skill into skill pool. Return is zero iff okay. +int skill_pool_is_activated (struct map_session_data *sd, int skill); // Skill into skill pool. Return is zero when activated. +int skill_pool_deactivate (struct map_session_data *sd, int skill); // Skill out of skill pool. Return is zero iff okay. +char *skill_name (int skill); // Yield configurable skill name +int skill_stat (int skill); // Yields the stat associated with a skill. Returns zero if none, or SP_STR, SP_VIT, ... otherwise +int skill_power (struct map_session_data *sd, int skill); // Yields the power of a skill. This is zero if the skill is unknown or if it's a pool skill that is outside of the skill pool, + // otherwise a value from 0 to 255 (with 200 being the `normal maximum') +int skill_power_bl (struct block_list *bl, int skill); // Yields the power of a skill. This is zero if the skill is unknown or if it's a pool skill that is outside of the skill pool, + // otherwise a value from 0 to 255 (with 200 being the `normal maximum') + +#endif diff --git a/src/map/storage.c b/src/map/storage.c deleted file mode 100644 index 4a0d934..0000000 --- a/src/map/storage.c +++ /dev/null @@ -1,787 +0,0 @@ -// 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/db.h" -#include "../common/nullpo.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; -} - -static void guild_storage_db_final (db_key_t key, db_val_t data, va_list ap) -{ - struct guild_storage *gstor = (struct guild_storage *) data; - free (gstor); -} - -static void storage_db_final (db_key_t key, db_val_t data, va_list ap) -{ - struct storage *stor = (struct storage *) data; - free (stor); -} - -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 = numdb_init (); - guild_storage_db = numdb_init (); - return 1; -} - -void do_final_storage (void) // by [MC Cameri] -{ - if (storage_db) - numdb_final (storage_db, storage_db_final); - if (guild_storage_db) - numdb_final (guild_storage_db, guild_storage_db_final); -} - -static void storage_reconnect_sub (db_key_t key, db_val_t 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, 0); - } - 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, stor->dirty == 2 ? 1 : 0); - } -} - -//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex -void do_reconnect_storage (void) -{ - numdb_foreach (storage_db, storage_reconnect_sub, 0); - numdb_foreach (guild_storage_db, storage_reconnect_sub, 1); -} - -struct storage *account2storage (int account_id) -{ - struct storage *stor = - (struct storage *) numdb_search (storage_db, account_id); - if (stor == NULL) - { - CREATE (stor, struct storage, 1); - stor->account_id = account_id; - numdb_insert (storage_db, stor->account_id, stor); - } - return stor; -} - -// Just to ask storage, without creation -struct storage *account2storage2 (int account_id) -{ - return (struct storage *) numdb_search (storage_db, account_id); -} - -int storage_delete (int account_id) -{ - struct storage *stor = - (struct storage *) numdb_search (storage_db, account_id); - if (stor) - { - numdb_erase (storage_db, account_id); - free (stor); - } - return 0; -} - -/*========================================== - * カプラ倉庫を開く - *------------------------------------------ - */ -int storage_storageopen (struct map_session_data *sd) -{ - struct storage *stor; - nullpo_retr (0, sd); - - if (sd->state.storage_flag) - return 1; //Already open? - - if ((stor = - (struct storage *) numdb_search (storage_db, - sd->status.account_id)) == NULL) - { //Request storage. - intif_request_storage (sd->status.account_id); - return 1; - } - - if (stor->storage_status) - return 1; //Already open/player already has it open... - - stor->storage_status = 1; - sd->state.storage_flag = 1; - clif_storageitemlist (sd, stor); - clif_storageequiplist (sd, stor); - clif_updatestorageamount (sd, stor); - return 0; -} - -/*========================================== - * Internal add-item function. - *------------------------------------------ - */ -static int storage_additem (struct map_session_data *sd, struct storage *stor, - struct item *item_data, int amount) -{ - struct item_data *data; - int i; - - if (item_data->nameid <= 0 || amount <= 0) - return 1; - - data = itemdb_search (item_data->nameid); - - if (!itemdb_isequip2 (data)) - { //Stackable - for (i = 0; i < MAX_STORAGE; i++) - { - if (compare_item (&stor->storage_[i], item_data)) - { - if (amount > MAX_AMOUNT - stor->storage_[i].amount) - return 1; - stor->storage_[i].amount += amount; - clif_storageitemadded (sd, stor, i, amount); - stor->dirty = 1; - return 0; - } - } - } - //Add item - for (i = 0; i < MAX_STORAGE && stor->storage_[i].nameid; i++); - - if (i >= MAX_STORAGE) - return 1; - - 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); - stor->dirty = 1; - return 0; -} - -/*========================================== - * Internal del-item function - *------------------------------------------ - */ -static int storage_delitem (struct map_session_data *sd, struct storage *stor, - int n, int amount) -{ - - 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; -} - -/*========================================== - * Add an item to the storage from the inventory. - *------------------------------------------ - */ -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) - return 0; // storage full / storage closed - - if (index < 0 || index >= MAX_INVENTORY) - return 0; - - if (sd->status.inventory[index].nameid <= 0) - return 0; //No item on that spot - - if (amount < 1 || amount > sd->status.inventory[index].amount) - return 0; - -// log_tostorage(sd, index, 0); - if (storage_additem (sd, stor, &sd->status.inventory[index], amount) == 0) - { - // remove item from inventory - pc_unequipinvyitem (sd, index, 0); - pc_delitem (sd, index, amount, 0); - } - - return 1; -} - -/*========================================== - * Retrieve an item from the storage. - *------------------------------------------ - */ -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 (index < 0 || index >= MAX_STORAGE) - return 0; - - if (stor->storage_[index].nameid <= 0) - return 0; //Nothing there - - if (amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if ((flag = pc_additem (sd, &stor->storage_[index], amount)) == 0) - storage_delitem (sd, stor, index, amount); - else - clif_additem (sd, 0, 0, flag); -// log_fromstorage(sd, index, 0); - return 1; -} - -/*========================================== - * Move an item from cart to storage. - *------------------------------------------ - */ -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) - return 0; // storage full / storage closed - - if (index < 0 || index >= MAX_CART) - return 0; - - if (sd->status.cart[index].nameid <= 0) - return 0; //No item there. - - if (amount < 1 || amount > sd->status.cart[index].amount) - return 0; - - if (storage_additem (sd, stor, &sd->status.cart[index], amount) == 0) - pc_cart_delitem (sd, index, amount, 0); - - return 1; -} - -/*========================================== - * Get from Storage to the Cart - *------------------------------------------ - */ -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) - return 0; - - if (index < 0 || index >= MAX_STORAGE) - return 0; - - if (stor->storage_[index].nameid <= 0) - return 0; //Nothing there. - - if (amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if (pc_cart_additem (sd, &stor->storage_[index], amount) == 0) - storage_delitem (sd, stor, index, amount); - - return 1; -} - -/*========================================== - * 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); - if (stor->storage_status) - { - if (save_settings & 4) - chrif_save (sd); //Invokes the storage saving as well. - else - storage_storage_save (sd->status.account_id, 0); - } - stor->storage_status = 0; - sd->state.storage_flag = 0; - - if (sd->npc_flags.storage) - { - sd->npc_flags.storage = 0; - map_scriptcont (sd, sd->npc_id); - } - - return 0; -} - -/*========================================== - * When quitting the game. - *------------------------------------------ - */ -int storage_storage_quit (struct map_session_data *sd) -{ - struct storage *stor; - - nullpo_retr (0, sd); - - stor = account2storage2 (sd->status.account_id); - if (stor) - { - chrif_save (sd); //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, int final) -{ - struct storage *stor; - - stor = account2storage2 (account_id); - if (!stor) - return 0; - - if (stor->dirty) - { - if (final) - { - stor->dirty = 2; - stor->storage_status = 0; //To prevent further manipulation of it. - } - intif_send_storage (stor); - return 1; - } - if (final) - { //Clear storage from memory. Nothing to save. - storage_delete (account_id); - 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; -} - -struct guild_storage *guild2storage (int guild_id) -{ - struct guild_storage *gs = NULL; - if (guild_search (guild_id) != NULL) - { - gs = (struct guild_storage *) numdb_search (guild_storage_db, - guild_id); - if (gs == NULL) - { - CREATE (gs, struct guild_storage, 1); - gs->guild_id = guild_id; - numdb_insert (guild_storage_db, gs->guild_id, gs); - } - } - return gs; -} - -struct guild_storage *guild2storage2 (int guild_id) -{ //For just locating a storage without creating one. [Skotlex] - return (struct guild_storage *) numdb_search (guild_storage_db, guild_id); -} - -int guild_storage_delete (int guild_id) -{ - struct guild_storage *gstor = - (struct guild_storage *) numdb_search (guild_storage_db, guild_id); - if (gstor) - { - numdb_erase (guild_storage_db, guild_id); - free (gstor); - } - 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.storage_flag) - return 1; //Can't open both storages at a time. - - if ((gstor = guild2storage2 (sd->status.guild_id)) == NULL) - { - intif_request_guild_storage (sd->status.account_id, - sd->status.guild_id); - return 0; - } - if (gstor->storage_status) - return 1; - - gstor->storage_status = 1; - sd->state.storage_flag = 2; - clif_guildstorageitemlist (sd, gstor); - clif_guildstorageequiplist (sd, gstor); - clif_updateguildstorageamount (sd, gstor); - 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_isequip2 (data)) - { //Stackable - 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); - stor->dirty = 1; - return 0; - } - } - } - //Add item - for (i = 0; i < MAX_GUILD_STORAGE && stor->storage_[i].nameid; i++); - - if (i >= MAX_GUILD_STORAGE) - return 1; - - 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); - 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); - nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); - - if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) - return 0; - - if (index < 0 || index >= MAX_INVENTORY) - return 0; - - if (sd->status.inventory[index].nameid <= 0) - return 0; - - if (amount < 1 || amount > sd->status.inventory[index].amount) - return 0; - -// log_tostorage(sd, index, 1); - if (guild_storage_additem (sd, stor, &sd->status.inventory[index], amount) - == 0) - pc_delitem (sd, index, amount, 0); - - return 1; -} - -int storage_guild_storageget (struct map_session_data *sd, int index, - int amount) -{ - struct guild_storage *stor; - int flag; - - nullpo_retr (0, sd); - nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); - - if (!stor->storage_status) - return 0; - - if (index < 0 || index >= MAX_GUILD_STORAGE) - return 0; - - if (stor->storage_[index].nameid <= 0) - return 0; - - if (amount < 1 || amount > stor->storage_[index].amount) - return 0; - - 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); - - return 0; -} - -int storage_guild_storageaddfromcart (struct map_session_data *sd, int index, - int amount) -{ - struct guild_storage *stor; - - nullpo_retr (0, sd); - nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); - - if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) - return 0; - - if (index < 0 || index >= MAX_CART) - return 0; - - if (sd->status.cart[index].nameid <= 0) - return 0; - - if (amount < 1 || amount > sd->status.cart[index].amount) - return 0; - - if (guild_storage_additem (sd, stor, &sd->status.cart[index], amount) == - 0) - pc_cart_delitem (sd, index, amount, 0); - - return 1; -} - -int storage_guild_storagegettocart (struct map_session_data *sd, int index, - int amount) -{ - struct guild_storage *stor; - - nullpo_retr (0, sd); - nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); - - if (!stor->storage_status) - return 0; - - if (index < 0 || index >= MAX_GUILD_STORAGE) - return 0; - - if (stor->storage_[index].nameid <= 0) - return 0; - - if (amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if (pc_cart_additem (sd, &stor->storage_[index], amount) == 0) - guild_storage_delitem (sd, stor, index, amount); - - return 1; -} - -int storage_guild_storagesave (int account_id, int guild_id, int flag) -{ - struct guild_storage *stor = guild2storage2 (guild_id); - - if (stor) - { - if (flag) //Char quitting, close it. - stor->storage_status = 0; - if (stor->dirty) - intif_send_guild_storage (account_id, stor); - return 1; - } - return 0; -} - -int storage_guild_storagesaved (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); //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)); - - if (flag) - { //Only during a guild break flag is 1 (don't save storage) - sd->state.storage_flag = 0; - stor->storage_status = 0; - clif_storageclose (sd); - if (save_settings & 4) - chrif_save (sd); - return 0; - } - - if (stor->storage_status) - { - if (save_settings & 4) - chrif_save (sd); - else - storage_guild_storagesave (sd->status.account_id, - sd->status.guild_id, 1); - } - sd->state.storage_flag = 0; - stor->storage_status = 0; - - return 0; -} diff --git a/src/map/storage.cpp b/src/map/storage.cpp new file mode 100644 index 0000000..d1d3cb0 --- /dev/null +++ b/src/map/storage.cpp @@ -0,0 +1,787 @@ +// 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/db.hpp" +#include "../common/nullpo.hpp" + +#include "storage.hpp" +#include "chrif.hpp" +#include "itemdb.hpp" +#include "clif.hpp" +#include "intif.hpp" +#include "pc.hpp" +#include "guild.hpp" +#include "battle.hpp" +#include "atcommand.hpp" + +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; +} + +static void guild_storage_db_final (db_key_t key, db_val_t data, va_list ap) +{ + struct guild_storage *gstor = (struct guild_storage *) data; + free (gstor); +} + +static void storage_db_final (db_key_t key, db_val_t data, va_list ap) +{ + struct storage *stor = (struct storage *) data; + free (stor); +} + +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 = numdb_init (); + guild_storage_db = numdb_init (); + return 1; +} + +void do_final_storage (void) // by [MC Cameri] +{ + if (storage_db) + numdb_final (storage_db, storage_db_final); + if (guild_storage_db) + numdb_final (guild_storage_db, guild_storage_db_final); +} + +static void storage_reconnect_sub (db_key_t key, db_val_t 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, 0); + } + 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, stor->dirty == 2 ? 1 : 0); + } +} + +//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex +void do_reconnect_storage (void) +{ + numdb_foreach (storage_db, storage_reconnect_sub, 0); + numdb_foreach (guild_storage_db, storage_reconnect_sub, 1); +} + +struct storage *account2storage (int account_id) +{ + struct storage *stor = + (struct storage *) numdb_search (storage_db, account_id); + if (stor == NULL) + { + CREATE (stor, struct storage, 1); + stor->account_id = account_id; + numdb_insert (storage_db, stor->account_id, stor); + } + return stor; +} + +// Just to ask storage, without creation +struct storage *account2storage2 (int account_id) +{ + return (struct storage *) numdb_search (storage_db, account_id); +} + +int storage_delete (int account_id) +{ + struct storage *stor = + (struct storage *) numdb_search (storage_db, account_id); + if (stor) + { + numdb_erase (storage_db, account_id); + free (stor); + } + return 0; +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +int storage_storageopen (struct map_session_data *sd) +{ + struct storage *stor; + nullpo_retr (0, sd); + + if (sd->state.storage_flag) + return 1; //Already open? + + if ((stor = + (struct storage *) numdb_search (storage_db, + sd->status.account_id)) == NULL) + { //Request storage. + intif_request_storage (sd->status.account_id); + return 1; + } + + if (stor->storage_status) + return 1; //Already open/player already has it open... + + stor->storage_status = 1; + sd->state.storage_flag = 1; + clif_storageitemlist (sd, stor); + clif_storageequiplist (sd, stor); + clif_updatestorageamount (sd, stor); + return 0; +} + +/*========================================== + * Internal add-item function. + *------------------------------------------ + */ +static int storage_additem (struct map_session_data *sd, struct storage *stor, + struct item *item_data, int amount) +{ + struct item_data *data; + int i; + + if (item_data->nameid <= 0 || amount <= 0) + return 1; + + data = itemdb_search (item_data->nameid); + + if (!itemdb_isequip2 (data)) + { //Stackable + for (i = 0; i < MAX_STORAGE; i++) + { + if (compare_item (&stor->storage_[i], item_data)) + { + if (amount > MAX_AMOUNT - stor->storage_[i].amount) + return 1; + stor->storage_[i].amount += amount; + clif_storageitemadded (sd, stor, i, amount); + stor->dirty = 1; + return 0; + } + } + } + //Add item + for (i = 0; i < MAX_STORAGE && stor->storage_[i].nameid; i++); + + if (i >= MAX_STORAGE) + return 1; + + 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); + stor->dirty = 1; + return 0; +} + +/*========================================== + * Internal del-item function + *------------------------------------------ + */ +static int storage_delitem (struct map_session_data *sd, struct storage *stor, + int n, int amount) +{ + + 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; +} + +/*========================================== + * Add an item to the storage from the inventory. + *------------------------------------------ + */ +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) + return 0; // storage full / storage closed + + if (index < 0 || index >= MAX_INVENTORY) + return 0; + + if (sd->status.inventory[index].nameid <= 0) + return 0; //No item on that spot + + if (amount < 1 || amount > sd->status.inventory[index].amount) + return 0; + +// log_tostorage(sd, index, 0); + if (storage_additem (sd, stor, &sd->status.inventory[index], amount) == 0) + { + // remove item from inventory + pc_unequipinvyitem (sd, index, 0); + pc_delitem (sd, index, amount, 0); + } + + return 1; +} + +/*========================================== + * Retrieve an item from the storage. + *------------------------------------------ + */ +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 (index < 0 || index >= MAX_STORAGE) + return 0; + + if (stor->storage_[index].nameid <= 0) + return 0; //Nothing there + + if (amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if ((flag = pc_additem (sd, &stor->storage_[index], amount)) == 0) + storage_delitem (sd, stor, index, amount); + else + clif_additem (sd, 0, 0, flag); +// log_fromstorage(sd, index, 0); + return 1; +} + +/*========================================== + * Move an item from cart to storage. + *------------------------------------------ + */ +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) + return 0; // storage full / storage closed + + if (index < 0 || index >= MAX_CART) + return 0; + + if (sd->status.cart[index].nameid <= 0) + return 0; //No item there. + + if (amount < 1 || amount > sd->status.cart[index].amount) + return 0; + + if (storage_additem (sd, stor, &sd->status.cart[index], amount) == 0) + pc_cart_delitem (sd, index, amount, 0); + + return 1; +} + +/*========================================== + * Get from Storage to the Cart + *------------------------------------------ + */ +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) + return 0; + + if (index < 0 || index >= MAX_STORAGE) + return 0; + + if (stor->storage_[index].nameid <= 0) + return 0; //Nothing there. + + if (amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if (pc_cart_additem (sd, &stor->storage_[index], amount) == 0) + storage_delitem (sd, stor, index, amount); + + return 1; +} + +/*========================================== + * 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); + if (stor->storage_status) + { + if (save_settings & 4) + chrif_save (sd); //Invokes the storage saving as well. + else + storage_storage_save (sd->status.account_id, 0); + } + stor->storage_status = 0; + sd->state.storage_flag = 0; + + if (sd->npc_flags.storage) + { + sd->npc_flags.storage = 0; + map_scriptcont (sd, sd->npc_id); + } + + return 0; +} + +/*========================================== + * When quitting the game. + *------------------------------------------ + */ +int storage_storage_quit (struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr (0, sd); + + stor = account2storage2 (sd->status.account_id); + if (stor) + { + chrif_save (sd); //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, int final) +{ + struct storage *stor; + + stor = account2storage2 (account_id); + if (!stor) + return 0; + + if (stor->dirty) + { + if (final) + { + stor->dirty = 2; + stor->storage_status = 0; //To prevent further manipulation of it. + } + intif_send_storage (stor); + return 1; + } + if (final) + { //Clear storage from memory. Nothing to save. + storage_delete (account_id); + 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; +} + +struct guild_storage *guild2storage (int guild_id) +{ + struct guild_storage *gs = NULL; + if (guild_search (guild_id) != NULL) + { + gs = (struct guild_storage *) numdb_search (guild_storage_db, + guild_id); + if (gs == NULL) + { + CREATE (gs, struct guild_storage, 1); + gs->guild_id = guild_id; + numdb_insert (guild_storage_db, gs->guild_id, gs); + } + } + return gs; +} + +struct guild_storage *guild2storage2 (int guild_id) +{ //For just locating a storage without creating one. [Skotlex] + return (struct guild_storage *) numdb_search (guild_storage_db, guild_id); +} + +int guild_storage_delete (int guild_id) +{ + struct guild_storage *gstor = + (struct guild_storage *) numdb_search (guild_storage_db, guild_id); + if (gstor) + { + numdb_erase (guild_storage_db, guild_id); + free (gstor); + } + 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.storage_flag) + return 1; //Can't open both storages at a time. + + if ((gstor = guild2storage2 (sd->status.guild_id)) == NULL) + { + intif_request_guild_storage (sd->status.account_id, + sd->status.guild_id); + return 0; + } + if (gstor->storage_status) + return 1; + + gstor->storage_status = 1; + sd->state.storage_flag = 2; + clif_guildstorageitemlist (sd, gstor); + clif_guildstorageequiplist (sd, gstor); + clif_updateguildstorageamount (sd, gstor); + 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_isequip2 (data)) + { //Stackable + 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); + stor->dirty = 1; + return 0; + } + } + } + //Add item + for (i = 0; i < MAX_GUILD_STORAGE && stor->storage_[i].nameid; i++); + + if (i >= MAX_GUILD_STORAGE) + return 1; + + 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); + 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); + nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); + + if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) + return 0; + + if (index < 0 || index >= MAX_INVENTORY) + return 0; + + if (sd->status.inventory[index].nameid <= 0) + return 0; + + if (amount < 1 || amount > sd->status.inventory[index].amount) + return 0; + +// log_tostorage(sd, index, 1); + if (guild_storage_additem (sd, stor, &sd->status.inventory[index], amount) + == 0) + pc_delitem (sd, index, amount, 0); + + return 1; +} + +int storage_guild_storageget (struct map_session_data *sd, int index, + int amount) +{ + struct guild_storage *stor; + int flag; + + nullpo_retr (0, sd); + nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); + + if (!stor->storage_status) + return 0; + + if (index < 0 || index >= MAX_GUILD_STORAGE) + return 0; + + if (stor->storage_[index].nameid <= 0) + return 0; + + if (amount < 1 || amount > stor->storage_[index].amount) + return 0; + + 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); + + return 0; +} + +int storage_guild_storageaddfromcart (struct map_session_data *sd, int index, + int amount) +{ + struct guild_storage *stor; + + nullpo_retr (0, sd); + nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); + + if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) + return 0; + + if (index < 0 || index >= MAX_CART) + return 0; + + if (sd->status.cart[index].nameid <= 0) + return 0; + + if (amount < 1 || amount > sd->status.cart[index].amount) + return 0; + + if (guild_storage_additem (sd, stor, &sd->status.cart[index], amount) == + 0) + pc_cart_delitem (sd, index, amount, 0); + + return 1; +} + +int storage_guild_storagegettocart (struct map_session_data *sd, int index, + int amount) +{ + struct guild_storage *stor; + + nullpo_retr (0, sd); + nullpo_retr (0, stor = guild2storage2 (sd->status.guild_id)); + + if (!stor->storage_status) + return 0; + + if (index < 0 || index >= MAX_GUILD_STORAGE) + return 0; + + if (stor->storage_[index].nameid <= 0) + return 0; + + if (amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if (pc_cart_additem (sd, &stor->storage_[index], amount) == 0) + guild_storage_delitem (sd, stor, index, amount); + + return 1; +} + +int storage_guild_storagesave (int account_id, int guild_id, int flag) +{ + struct guild_storage *stor = guild2storage2 (guild_id); + + if (stor) + { + if (flag) //Char quitting, close it. + stor->storage_status = 0; + if (stor->dirty) + intif_send_guild_storage (account_id, stor); + return 1; + } + return 0; +} + +int storage_guild_storagesaved (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); //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)); + + if (flag) + { //Only during a guild break flag is 1 (don't save storage) + sd->state.storage_flag = 0; + stor->storage_status = 0; + clif_storageclose (sd); + if (save_settings & 4) + chrif_save (sd); + return 0; + } + + if (stor->storage_status) + { + if (save_settings & 4) + chrif_save (sd); + else + storage_guild_storagesave (sd->status.account_id, + sd->status.guild_id, 1); + } + sd->state.storage_flag = 0; + stor->storage_status = 0; + + return 0; +} diff --git a/src/map/storage.h b/src/map/storage.h deleted file mode 100644 index daaec2c..0000000 --- a/src/map/storage.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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 storage_storage_save (int account_id, int final); -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 flag); -int storage_guild_storagesaved (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/storage.hpp b/src/map/storage.hpp new file mode 100644 index 0000000..22625df --- /dev/null +++ b/src/map/storage.hpp @@ -0,0 +1,54 @@ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef STORAGE_HPP +#define STORAGE_HPP + +#include "../common/mmo.hpp" + +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 storage_storage_save (int account_id, int final); +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 flag); +int storage_guild_storagesaved (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/tmw.c b/src/map/tmw.c deleted file mode 100644 index 3487c1d..0000000 --- a/src/map/tmw.c +++ /dev/null @@ -1,187 +0,0 @@ -// - -#include <string.h> -#include <ctype.h> -#include <stdarg.h> - -#include "tmw.h" - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/version.h" -#include "../common/nullpo.h" - -#include "atcommand.h" -#include "battle.h" -#include "chat.h" -#include "chrif.h" -#include "clif.h" -#include "guild.h" -#include "intif.h" -#include "itemdb.h" -#include "magic.h" -#include "map.h" -#include "mob.h" -#include "npc.h" -#include "party.h" -#include "pc.h" -#include "script.h" -#include "skill.h" -#include "storage.h" -#include "trade.h" - -int tmw_CheckChatSpam (struct map_session_data *sd, char *message) -{ - nullpo_retr (1, sd); - time_t now = time (NULL); - - if (pc_isGM (sd)) - return 0; - - if (now > sd->chat_reset_due) - { - sd->chat_reset_due = now + battle_config.chat_spam_threshold; - sd->chat_lines_in = 0; - } - - if (now > sd->chat_repeat_reset_due) - { - sd->chat_repeat_reset_due = - now + (battle_config.chat_spam_threshold * 60); - sd->chat_total_repeats = 0; - } - - sd->chat_lines_in++; - - // Penalty for repeats. - if (strncmp - (sd->chat_lastmsg, message, - tmw_ShorterStrlen (sd->chat_lastmsg, message)) == 0) - { - sd->chat_lines_in += battle_config.chat_lame_penalty; - sd->chat_total_repeats++; - } - else - { - sd->chat_total_repeats = 0; - } - - // Penalty for lame, it can stack on top of the repeat penalty. - if (tmw_CheckChatLameness (sd, message)) - sd->chat_lines_in += battle_config.chat_lame_penalty; - - strncpy ((char *) sd->chat_lastmsg, message, battle_config.chat_maxline); - - if (sd->chat_lines_in >= battle_config.chat_spam_flood - || sd->chat_total_repeats >= battle_config.chat_spam_flood) - { - sd->chat_lines_in = sd->chat_total_repeats = 0; - - tmw_AutoBan (sd, "chat", battle_config.chat_spam_ban); - - return 1; - } - - if (battle_config.chat_spam_ban && - (sd->chat_lines_in >= battle_config.chat_spam_warn - || sd->chat_total_repeats >= battle_config.chat_spam_warn)) - { - /* "WARNING: You are about to be automatically banned for spam!" */ - clif_displaymessage (sd->fd, msg_txt (506)); - /* "WARNING: Please slow down, do not repeat, and do not SHOUT!" */ - clif_displaymessage (sd->fd, msg_txt (507)); - } - - return 0; -} - -void tmw_AutoBan(struct map_session_data *sd, char *reason, int length) -{ - char anotherbuf[512]; - - if (length == 0 || sd->auto_ban_info.in_progress) - return; - - sd->auto_ban_info.in_progress = 1; - - tmw_GmHackMsg ("%s has been autobanned for %s spam", - sd->status.name, reason); - - gm_log ("%s(%d,%d) Server : @autoban %s %dh (%s spam)", - map[sd->bl.m].name, sd->bl.x, sd->bl.y, - sd->status.name, length, reason); - - /* "You have been banned for %s spamming. Please do not spam." */ - snprintf (anotherbuf, 511, msg_txt (508), reason); - - clif_displaymessage (sd->fd, anotherbuf); - /* type: 2 - ban (year, month, day, hour, minute, second) */ - chrif_char_ask_name (-1, sd->status.name, 2, 0, 0, 0, length, 0, 0); - clif_setwaitclose (sd->fd); -} - -// Compares the length of two strings and returns that of the shorter -int tmw_ShorterStrlen (char *s1, char *s2) -{ - int s1_len = strlen (s1); - int s2_len = strlen (s2); - return (s2_len >= s1_len ? s1_len : s2_len); -} - -// Returns true if more than 50% of input message is caps or punctuation -int tmw_CheckChatLameness (struct map_session_data *sd, char *message) -{ - int count, lame; - - for (count = lame = 0; *message; message++, count++) - if (isupper (*message) || ispunct (*message)) - lame++; - - if (count > 7 && lame > count / 2) - return (1); - - return (0); -} - -// Sends a whisper to all GMs -void tmw_GmHackMsg (const char *fmt, ...) -{ - char buf[512]; - va_list ap; - - va_start (ap, fmt); - vsnprintf (buf, 511, fmt, ap); - va_end (ap); - - char outbuf[512 + 5]; - strcpy (outbuf, "[GM] "); - strcat (outbuf, buf); - - intif_wis_message_to_gm (wisp_server_name, - battle_config.hack_info_GM_level, outbuf, - strlen (outbuf) + 1); -} - -/* Remove leading and trailing spaces from a string, modifying in place. */ -void tmw_TrimStr (char *str) -{ - char *l; - char *a; - char *e; - - if (!*str) - return; - - e = str + strlen (str) - 1; - - /* Skip all leading spaces. */ - for (l = str; *l && isspace (*l); ++l) - ; - - /* Find the end of the string, or the start of trailing spaces. */ - for (a = e; *a && a > l && isspace (*a); --a) - ; - - memmove (str, l, a - l + 1); - str[a - l + 1] = '\0'; -} diff --git a/src/map/tmw.cpp b/src/map/tmw.cpp new file mode 100644 index 0000000..7506270 --- /dev/null +++ b/src/map/tmw.cpp @@ -0,0 +1,187 @@ +// + +#include <string.h> +#include <ctype.h> +#include <stdarg.h> + +#include "tmw.hpp" + +#include "../common/socket.hpp" +#include "../common/timer.hpp" +#include "../common/version.hpp" +#include "../common/nullpo.hpp" + +#include "atcommand.hpp" +#include "battle.hpp" +#include "chat.hpp" +#include "chrif.hpp" +#include "clif.hpp" +#include "guild.hpp" +#include "intif.hpp" +#include "itemdb.hpp" +#include "magic.hpp" +#include "map.hpp" +#include "mob.hpp" +#include "npc.hpp" +#include "party.hpp" +#include "pc.hpp" +#include "script.hpp" +#include "skill.hpp" +#include "storage.hpp" +#include "trade.hpp" + +int tmw_CheckChatSpam (struct map_session_data *sd, char *message) +{ + nullpo_retr (1, sd); + time_t now = time (NULL); + + if (pc_isGM (sd)) + return 0; + + if (now > sd->chat_reset_due) + { + sd->chat_reset_due = now + battle_config.chat_spam_threshold; + sd->chat_lines_in = 0; + } + + if (now > sd->chat_repeat_reset_due) + { + sd->chat_repeat_reset_due = + now + (battle_config.chat_spam_threshold * 60); + sd->chat_total_repeats = 0; + } + + sd->chat_lines_in++; + + // Penalty for repeats. + if (strncmp + (sd->chat_lastmsg, message, + tmw_ShorterStrlen (sd->chat_lastmsg, message)) == 0) + { + sd->chat_lines_in += battle_config.chat_lame_penalty; + sd->chat_total_repeats++; + } + else + { + sd->chat_total_repeats = 0; + } + + // Penalty for lame, it can stack on top of the repeat penalty. + if (tmw_CheckChatLameness (sd, message)) + sd->chat_lines_in += battle_config.chat_lame_penalty; + + strncpy ((char *) sd->chat_lastmsg, message, battle_config.chat_maxline); + + if (sd->chat_lines_in >= battle_config.chat_spam_flood + || sd->chat_total_repeats >= battle_config.chat_spam_flood) + { + sd->chat_lines_in = sd->chat_total_repeats = 0; + + tmw_AutoBan (sd, "chat", battle_config.chat_spam_ban); + + return 1; + } + + if (battle_config.chat_spam_ban && + (sd->chat_lines_in >= battle_config.chat_spam_warn + || sd->chat_total_repeats >= battle_config.chat_spam_warn)) + { + /* "WARNING: You are about to be automatically banned for spam!" */ + clif_displaymessage (sd->fd, msg_txt (506)); + /* "WARNING: Please slow down, do not repeat, and do not SHOUT!" */ + clif_displaymessage (sd->fd, msg_txt (507)); + } + + return 0; +} + +void tmw_AutoBan(struct map_session_data *sd, char *reason, int length) +{ + char anotherbuf[512]; + + if (length == 0 || sd->auto_ban_info.in_progress) + return; + + sd->auto_ban_info.in_progress = 1; + + tmw_GmHackMsg ("%s has been autobanned for %s spam", + sd->status.name, reason); + + gm_log ("%s(%d,%d) Server : @autoban %s %dh (%s spam)", + map[sd->bl.m].name, sd->bl.x, sd->bl.y, + sd->status.name, length, reason); + + /* "You have been banned for %s spamming. Please do not spam." */ + snprintf (anotherbuf, 511, msg_txt (508), reason); + + clif_displaymessage (sd->fd, anotherbuf); + /* type: 2 - ban (year, month, day, hour, minute, second) */ + chrif_char_ask_name (-1, sd->status.name, 2, 0, 0, 0, length, 0, 0); + clif_setwaitclose (sd->fd); +} + +// Compares the length of two strings and returns that of the shorter +int tmw_ShorterStrlen (char *s1, char *s2) +{ + int s1_len = strlen (s1); + int s2_len = strlen (s2); + return (s2_len >= s1_len ? s1_len : s2_len); +} + +// Returns true if more than 50% of input message is caps or punctuation +int tmw_CheckChatLameness (struct map_session_data *sd, char *message) +{ + int count, lame; + + for (count = lame = 0; *message; message++, count++) + if (isupper (*message) || ispunct (*message)) + lame++; + + if (count > 7 && lame > count / 2) + return (1); + + return (0); +} + +// Sends a whisper to all GMs +void tmw_GmHackMsg (const char *fmt, ...) +{ + char buf[512]; + va_list ap; + + va_start (ap, fmt); + vsnprintf (buf, 511, fmt, ap); + va_end (ap); + + char outbuf[512 + 5]; + strcpy (outbuf, "[GM] "); + strcat (outbuf, buf); + + intif_wis_message_to_gm (wisp_server_name, + battle_config.hack_info_GM_level, outbuf, + strlen (outbuf) + 1); +} + +/* Remove leading and trailing spaces from a string, modifying in place. */ +void tmw_TrimStr (char *str) +{ + char *l; + char *a; + char *e; + + if (!*str) + return; + + e = str + strlen (str) - 1; + + /* Skip all leading spaces. */ + for (l = str; *l && isspace (*l); ++l) + ; + + /* Find the end of the string, or the start of trailing spaces. */ + for (a = e; *a && a > l && isspace (*a); --a) + ; + + memmove (str, l, a - l + 1); + str[a - l + 1] = '\0'; +} diff --git a/src/map/tmw.h b/src/map/tmw.h deleted file mode 100644 index 5141159..0000000 --- a/src/map/tmw.h +++ /dev/null @@ -1,14 +0,0 @@ -// -#ifndef _TMW_H_ -#define _TMW_H_ - -#include "map.h" - -int tmw_CheckChatSpam (struct map_session_data *sd, char *message); -int tmw_ShorterStrlen (char *s1, char *s2); -int tmw_CheckChatLameness (struct map_session_data *sd, char *message); -void tmw_GmHackMsg (const char *fmt, ...); -void tmw_AutoBan (struct map_session_data *sd, char *reason, int length); -void tmw_TrimStr (char *str); - -#endif /* _TMW_H_ */ diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp new file mode 100644 index 0000000..5b20a6c --- /dev/null +++ b/src/map/tmw.hpp @@ -0,0 +1,14 @@ +// +#ifndef TMW_HPP +#define TMW_HPP + +#include "map.hpp" + +int tmw_CheckChatSpam (struct map_session_data *sd, char *message); +int tmw_ShorterStrlen (char *s1, char *s2); +int tmw_CheckChatLameness (struct map_session_data *sd, char *message); +void tmw_GmHackMsg (const char *fmt, ...); +void tmw_AutoBan (struct map_session_data *sd, char *reason, int length); +void tmw_TrimStr (char *str); + +#endif /* TMW_H_ */ diff --git a/src/map/trade.c b/src/map/trade.c deleted file mode 100644 index ae70f0d..0000000 --- a/src/map/trade.c +++ /dev/null @@ -1,432 +0,0 @@ -#include <stdio.h> -#include <string.h> - -#include "clif.h" -#include "itemdb.h" -#include "map.h" -#include "trade.h" -#include "pc.h" -#include "npc.h" -#include "battle.h" -#include "storage.h" -#include "../common/nullpo.h" - -/*========================================== - * 取引要請を相手に送る - *------------------------------------------ - */ -void trade_traderequest (struct map_session_data *sd, int target_id) -{ - struct map_session_data *target_sd; - - 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; - } - } - if (target_sd->npc_id) - { - //Trade fails if you are using an NPC. - clif_tradestart (sd, 2); - return; - } - if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) - { - trade_tradecancel (sd); //person is in another trade - } - else - { - if (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->deal_locked = 0; - sd->trade_partner = 0; - target_sd->deal_locked = 0; - target_sd->trade_partner = 0; - } - 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); - } -} - -/*========================================== - * アイテム追加 - *------------------------------------------ - */ -void trade_tradeadditem (struct map_session_data *sd, int index, int amount) -{ - struct map_session_data *target_sd; - struct item_data *id; - int trade_i; - int trade_weight = 0; - int free = 0; - int c; - int i; - - nullpo_retv (sd); - - if (((target_sd = map_id2sd (sd->trade_partner)) != NULL) - && (sd->deal_locked < 1)) - { - if (index < 2 || index >= MAX_INVENTORY + 2) - { - if (index == 0 && amount > 0 && amount <= sd->status.zeny) - { - sd->deal_zeny = amount; - clif_tradeadditem (sd, target_sd, 0, amount); - } - } - else if (amount <= sd->status.inventory[index - 2].amount - && amount > 0) - { - // determine free slots of receiver - for (i = 0; i < MAX_INVENTORY; i++) - { - if (target_sd->status.inventory[i].nameid == 0 - && target_sd->inventory_data[i] == NULL) - free++; - } - for (trade_i = 0; trade_i < 10; trade_i++) - { - if (sd->deal_item_amount[trade_i] == 0) - { - // calculate trade weight - trade_weight += - sd->inventory_data[index - 2]->weight * amount; - - // determine if item is a stackable already in receivers inventory, and up free count - for (i = 0; i < MAX_INVENTORY; i++) - { - if (target_sd->status.inventory[i].nameid == - sd->status.inventory[index - 2].nameid - && target_sd->inventory_data[i] != NULL) - { - id = target_sd->inventory_data[i]; - if (id->type != 4 && id->type != 5 - && id->type != 7 && id->type != 8) - { - free++; - break; - } - } - } - - if (target_sd->weight + trade_weight > - target_sd->max_weight) - { - clif_tradeitemok (sd, index, 0, 1); //fail to add item -- the player was over weighted. - amount = 0; // [MouseJstr] - } - else if (free <= 0) - { - clif_tradeitemok (sd, index, 0, 2); //fail to add item -- no free slots at receiver - amount = 0; // peavey - } - else - { - for (c = 0; c == trade_i - 1; c++) - { // re-deal exploit protection [Valaris] - if (sd->deal_item_index[c] == index) - { - trade_tradecancel (sd); - return; - } - } - pc_unequipinvyitem (sd, index - 2, 0); - sd->deal_item_index[trade_i] = index; - sd->deal_item_amount[trade_i] += amount; - clif_tradeitemok (sd, index, amount, 0); //success to add item - clif_tradeadditem (sd, target_sd, index, amount); - } - break; - } - else - { - // calculate weight for stored deal - trade_weight += - sd->inventory_data[sd->deal_item_index[trade_i] - - 2]->weight * - sd->deal_item_amount[trade_i]; - // count free stackables in stored deal - for (i = 0; i < MAX_INVENTORY; i++) - { - if (target_sd->status.inventory[i].nameid == - sd->status. - inventory[sd->deal_item_index[trade_i] - 2].nameid - && target_sd->inventory_data[i] != NULL) - { - id = target_sd->inventory_data[i]; - if (id->type != 4 && id->type != 5 - && id->type != 7 && id->type != 8) - { - free++; - break; - } - } - } - } - // used a slot, but might be cancelled out by stackable checks above - free--; - } - } - } -} - -/*========================================== - * アイテム追加完了(ok押し) - *------------------------------------------ - */ -void trade_tradeok (struct map_session_data *sd) -{ - struct map_session_data *target_sd; - int trade_i; - - nullpo_retv (sd); - - for (trade_i = 0; trade_i < 10; trade_i++) - { - if (sd->deal_item_amount[trade_i] > - sd->status.inventory[sd->deal_item_index[trade_i] - 2].amount - || sd->deal_item_amount[trade_i] < 0) - { - trade_tradecancel (sd); - return; - } - - } - - if ((target_sd = map_id2sd (sd->trade_partner)) != NULL) - { - sd->deal_locked = 1; - clif_tradeitemok (sd, 0, 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_amount[trade_i] != 0) - { - clif_additem (sd, sd->deal_item_index[trade_i] - 2, - sd->deal_item_amount[trade_i], 0); - sd->deal_item_index[trade_i] = 0; - sd->deal_item_amount[trade_i] = 0; - } - if (target_sd->deal_item_amount[trade_i] != 0) - { - clif_additem (target_sd, - target_sd->deal_item_index[trade_i] - 2, - target_sd->deal_item_amount[trade_i], 0); - target_sd->deal_item_index[trade_i] = 0; - target_sd->deal_item_amount[trade_i] = 0; - } - } - if (sd->deal_zeny) - { - sd->deal_zeny = 0; - clif_updatestatus (sd, SP_ZENY); - } - if (target_sd->deal_zeny) - { - clif_updatestatus (target_sd, SP_ZENY); - target_sd->deal_zeny = 0; - } - sd->deal_locked = 0; - sd->trade_partner = 0; - target_sd->deal_locked = 0; - target_sd->trade_partner = 0; - clif_tradecancelled (sd); - clif_tradecancelled (target_sd); - } -} - -#define MAP_LOG_PC(sd, fmt, args...) MAP_LOG("PC%d %d:%d,%d " fmt, sd->status.char_id, sd->bl.m, sd->bl.x, sd->bl.y, ## args) - -/*========================================== - * 取引許諾(trade押し) - *------------------------------------------ - */ -void trade_tradecommit (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) - { - MAP_LOG_PC (sd, " TRADECOMMIT WITH %d GIVE %d GET %d", - target_sd->status.char_id, sd->deal_zeny, - target_sd->deal_zeny); - if ((sd->deal_locked >= 1) && (target_sd->deal_locked >= 1)) - { // both have pressed 'ok' - if (sd->deal_locked < 2) - { - sd->deal_locked = 2; - } // set locked to 2 - if (target_sd->deal_locked == 2) - { // the other one pressed 'trade' too - if (sd->deal_zeny > sd->status.zeny) - { - sd->deal_zeny = 0; - trade_tradecancel (sd); - MAP_LOG_PC (sd, " TRADECANCEL"); - return; - } - if (target_sd->deal_zeny > target_sd->status.zeny) - { - target_sd->deal_zeny = 0; - trade_tradecancel (sd); - MAP_LOG_PC (sd, " TRADECANCEL"); - return; - } - sd->trade_partner = 0; - target_sd->trade_partner = 0; - for (trade_i = 0; trade_i < 10; trade_i++) - { - if (sd->deal_item_amount[trade_i] != 0) - { - int n = sd->deal_item_index[trade_i] - 2; - int flag; - flag = - pc_additem (target_sd, &sd->status.inventory[n], - sd->deal_item_amount[trade_i]); - if (flag == 0) - pc_delitem (sd, n, sd->deal_item_amount[trade_i], - 1); - else - clif_additem (sd, n, - sd->deal_item_amount[trade_i], 0); - sd->deal_item_index[trade_i] = 0; - sd->deal_item_amount[trade_i] = 0; - } - if (target_sd->deal_item_amount[trade_i] != 0) - { - int n = target_sd->deal_item_index[trade_i] - 2; - int flag; - flag = - pc_additem (sd, &target_sd->status.inventory[n], - target_sd->deal_item_amount[trade_i]); - if (flag == 0) - pc_delitem (target_sd, n, - target_sd->deal_item_amount[trade_i], - 1); - else - clif_additem (target_sd, n, - target_sd->deal_item_amount - [trade_i], 0); - target_sd->deal_item_index[trade_i] = 0; - target_sd->deal_item_amount[trade_i] = 0; - } - } - if (sd->deal_zeny) - { - int deal = sd->deal_zeny; - sd->deal_zeny = 0; - sd->status.zeny -= deal; - clif_updatestatus (sd, SP_ZENY); - target_sd->status.zeny += deal; - clif_updatestatus (target_sd, SP_ZENY); - } - if (target_sd->deal_zeny) - { - int deal = target_sd->deal_zeny; - target_sd->deal_zeny = 0; - target_sd->status.zeny -= deal; - clif_updatestatus (target_sd, SP_ZENY); - sd->status.zeny += deal; - clif_updatestatus (sd, SP_ZENY); - } - sd->deal_locked = 0; - target_sd->deal_locked = 0; - clif_tradecompleted (sd, 0); - clif_tradecompleted (target_sd, 0); - MAP_LOG_PC (sd, " TRADEOK"); - } - } - } -} - -// This is called when a char's zeny is changed -// This helps prevent money duplication and other problems -// [Jaxad0127] -void trade_verifyzeny (struct map_session_data *sd) -{ - struct map_session_data *target_sd; - - nullpo_retv (sd); - - if ((target_sd = map_id2sd (sd->trade_partner)) != NULL) - { - if (sd->deal_zeny > sd->status.zeny) - { - if (sd->deal_locked < 1) - trade_tradeadditem (sd, 0, sd->status.zeny); // Fix money ammount - else - trade_tradecancel (sd); // Or cancel the trade if we can't fix it - } - } -} diff --git a/src/map/trade.cpp b/src/map/trade.cpp new file mode 100644 index 0000000..147db1b --- /dev/null +++ b/src/map/trade.cpp @@ -0,0 +1,432 @@ +#include <stdio.h> +#include <string.h> + +#include "clif.hpp" +#include "itemdb.hpp" +#include "map.hpp" +#include "trade.hpp" +#include "pc.hpp" +#include "npc.hpp" +#include "battle.hpp" +#include "storage.hpp" +#include "../common/nullpo.hpp" + +/*========================================== + * 取引要請を相手に送る + *------------------------------------------ + */ +void trade_traderequest (struct map_session_data *sd, int target_id) +{ + struct map_session_data *target_sd; + + 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; + } + } + if (target_sd->npc_id) + { + //Trade fails if you are using an NPC. + clif_tradestart (sd, 2); + return; + } + if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) + { + trade_tradecancel (sd); //person is in another trade + } + else + { + if (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->deal_locked = 0; + sd->trade_partner = 0; + target_sd->deal_locked = 0; + target_sd->trade_partner = 0; + } + 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); + } +} + +/*========================================== + * アイテム追加 + *------------------------------------------ + */ +void trade_tradeadditem (struct map_session_data *sd, int index, int amount) +{ + struct map_session_data *target_sd; + struct item_data *id; + int trade_i; + int trade_weight = 0; + int free = 0; + int c; + int i; + + nullpo_retv (sd); + + if (((target_sd = map_id2sd (sd->trade_partner)) != NULL) + && (sd->deal_locked < 1)) + { + if (index < 2 || index >= MAX_INVENTORY + 2) + { + if (index == 0 && amount > 0 && amount <= sd->status.zeny) + { + sd->deal_zeny = amount; + clif_tradeadditem (sd, target_sd, 0, amount); + } + } + else if (amount <= sd->status.inventory[index - 2].amount + && amount > 0) + { + // determine free slots of receiver + for (i = 0; i < MAX_INVENTORY; i++) + { + if (target_sd->status.inventory[i].nameid == 0 + && target_sd->inventory_data[i] == NULL) + free++; + } + for (trade_i = 0; trade_i < 10; trade_i++) + { + if (sd->deal_item_amount[trade_i] == 0) + { + // calculate trade weight + trade_weight += + sd->inventory_data[index - 2]->weight * amount; + + // determine if item is a stackable already in receivers inventory, and up free count + for (i = 0; i < MAX_INVENTORY; i++) + { + if (target_sd->status.inventory[i].nameid == + sd->status.inventory[index - 2].nameid + && target_sd->inventory_data[i] != NULL) + { + id = target_sd->inventory_data[i]; + if (id->type != 4 && id->type != 5 + && id->type != 7 && id->type != 8) + { + free++; + break; + } + } + } + + if (target_sd->weight + trade_weight > + target_sd->max_weight) + { + clif_tradeitemok (sd, index, 0, 1); //fail to add item -- the player was over weighted. + amount = 0; // [MouseJstr] + } + else if (free <= 0) + { + clif_tradeitemok (sd, index, 0, 2); //fail to add item -- no free slots at receiver + amount = 0; // peavey + } + else + { + for (c = 0; c == trade_i - 1; c++) + { // re-deal exploit protection [Valaris] + if (sd->deal_item_index[c] == index) + { + trade_tradecancel (sd); + return; + } + } + pc_unequipinvyitem (sd, index - 2, 0); + sd->deal_item_index[trade_i] = index; + sd->deal_item_amount[trade_i] += amount; + clif_tradeitemok (sd, index, amount, 0); //success to add item + clif_tradeadditem (sd, target_sd, index, amount); + } + break; + } + else + { + // calculate weight for stored deal + trade_weight += + sd->inventory_data[sd->deal_item_index[trade_i] - + 2]->weight * + sd->deal_item_amount[trade_i]; + // count free stackables in stored deal + for (i = 0; i < MAX_INVENTORY; i++) + { + if (target_sd->status.inventory[i].nameid == + sd->status. + inventory[sd->deal_item_index[trade_i] - 2].nameid + && target_sd->inventory_data[i] != NULL) + { + id = target_sd->inventory_data[i]; + if (id->type != 4 && id->type != 5 + && id->type != 7 && id->type != 8) + { + free++; + break; + } + } + } + } + // used a slot, but might be cancelled out by stackable checks above + free--; + } + } + } +} + +/*========================================== + * アイテム追加完了(ok押し) + *------------------------------------------ + */ +void trade_tradeok (struct map_session_data *sd) +{ + struct map_session_data *target_sd; + int trade_i; + + nullpo_retv (sd); + + for (trade_i = 0; trade_i < 10; trade_i++) + { + if (sd->deal_item_amount[trade_i] > + sd->status.inventory[sd->deal_item_index[trade_i] - 2].amount + || sd->deal_item_amount[trade_i] < 0) + { + trade_tradecancel (sd); + return; + } + + } + + if ((target_sd = map_id2sd (sd->trade_partner)) != NULL) + { + sd->deal_locked = 1; + clif_tradeitemok (sd, 0, 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_amount[trade_i] != 0) + { + clif_additem (sd, sd->deal_item_index[trade_i] - 2, + sd->deal_item_amount[trade_i], 0); + sd->deal_item_index[trade_i] = 0; + sd->deal_item_amount[trade_i] = 0; + } + if (target_sd->deal_item_amount[trade_i] != 0) + { + clif_additem (target_sd, + target_sd->deal_item_index[trade_i] - 2, + target_sd->deal_item_amount[trade_i], 0); + target_sd->deal_item_index[trade_i] = 0; + target_sd->deal_item_amount[trade_i] = 0; + } + } + if (sd->deal_zeny) + { + sd->deal_zeny = 0; + clif_updatestatus (sd, SP_ZENY); + } + if (target_sd->deal_zeny) + { + clif_updatestatus (target_sd, SP_ZENY); + target_sd->deal_zeny = 0; + } + sd->deal_locked = 0; + sd->trade_partner = 0; + target_sd->deal_locked = 0; + target_sd->trade_partner = 0; + clif_tradecancelled (sd); + clif_tradecancelled (target_sd); + } +} + +#define MAP_LOG_PC(sd, fmt, args...) MAP_LOG("PC%d %d:%d,%d " fmt, sd->status.char_id, sd->bl.m, sd->bl.x, sd->bl.y, ## args) + +/*========================================== + * 取引許諾(trade押し) + *------------------------------------------ + */ +void trade_tradecommit (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) + { + MAP_LOG_PC (sd, " TRADECOMMIT WITH %d GIVE %d GET %d", + target_sd->status.char_id, sd->deal_zeny, + target_sd->deal_zeny); + if ((sd->deal_locked >= 1) && (target_sd->deal_locked >= 1)) + { // both have pressed 'ok' + if (sd->deal_locked < 2) + { + sd->deal_locked = 2; + } // set locked to 2 + if (target_sd->deal_locked == 2) + { // the other one pressed 'trade' too + if (sd->deal_zeny > sd->status.zeny) + { + sd->deal_zeny = 0; + trade_tradecancel (sd); + MAP_LOG_PC (sd, " TRADECANCEL"); + return; + } + if (target_sd->deal_zeny > target_sd->status.zeny) + { + target_sd->deal_zeny = 0; + trade_tradecancel (sd); + MAP_LOG_PC (sd, " TRADECANCEL"); + return; + } + sd->trade_partner = 0; + target_sd->trade_partner = 0; + for (trade_i = 0; trade_i < 10; trade_i++) + { + if (sd->deal_item_amount[trade_i] != 0) + { + int n = sd->deal_item_index[trade_i] - 2; + int flag; + flag = + pc_additem (target_sd, &sd->status.inventory[n], + sd->deal_item_amount[trade_i]); + if (flag == 0) + pc_delitem (sd, n, sd->deal_item_amount[trade_i], + 1); + else + clif_additem (sd, n, + sd->deal_item_amount[trade_i], 0); + sd->deal_item_index[trade_i] = 0; + sd->deal_item_amount[trade_i] = 0; + } + if (target_sd->deal_item_amount[trade_i] != 0) + { + int n = target_sd->deal_item_index[trade_i] - 2; + int flag; + flag = + pc_additem (sd, &target_sd->status.inventory[n], + target_sd->deal_item_amount[trade_i]); + if (flag == 0) + pc_delitem (target_sd, n, + target_sd->deal_item_amount[trade_i], + 1); + else + clif_additem (target_sd, n, + target_sd->deal_item_amount + [trade_i], 0); + target_sd->deal_item_index[trade_i] = 0; + target_sd->deal_item_amount[trade_i] = 0; + } + } + if (sd->deal_zeny) + { + int deal = sd->deal_zeny; + sd->deal_zeny = 0; + sd->status.zeny -= deal; + clif_updatestatus (sd, SP_ZENY); + target_sd->status.zeny += deal; + clif_updatestatus (target_sd, SP_ZENY); + } + if (target_sd->deal_zeny) + { + int deal = target_sd->deal_zeny; + target_sd->deal_zeny = 0; + target_sd->status.zeny -= deal; + clif_updatestatus (target_sd, SP_ZENY); + sd->status.zeny += deal; + clif_updatestatus (sd, SP_ZENY); + } + sd->deal_locked = 0; + target_sd->deal_locked = 0; + clif_tradecompleted (sd, 0); + clif_tradecompleted (target_sd, 0); + MAP_LOG_PC (sd, " TRADEOK"); + } + } + } +} + +// This is called when a char's zeny is changed +// This helps prevent money duplication and other problems +// [Jaxad0127] +void trade_verifyzeny (struct map_session_data *sd) +{ + struct map_session_data *target_sd; + + nullpo_retv (sd); + + if ((target_sd = map_id2sd (sd->trade_partner)) != NULL) + { + if (sd->deal_zeny > sd->status.zeny) + { + if (sd->deal_locked < 1) + trade_tradeadditem (sd, 0, sd->status.zeny); // Fix money ammount + else + trade_tradecancel (sd); // Or cancel the trade if we can't fix it + } + } +} diff --git a/src/map/trade.h b/src/map/trade.h deleted file mode 100644 index a846388..0000000 --- a/src/map/trade.h +++ /dev/null @@ -1,14 +0,0 @@ -// $Id: trade.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ -#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); -void trade_verifyzeny (struct map_session_data *sd); - -#endif // _TRADE_H_ diff --git a/src/map/trade.hpp b/src/map/trade.hpp new file mode 100644 index 0000000..1e885f4 --- /dev/null +++ b/src/map/trade.hpp @@ -0,0 +1,14 @@ +// $Id: trade.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef TRADE_HPP +#define TRADE_HPP + +#include "map.hpp" +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); +void trade_verifyzeny (struct map_session_data *sd); + +#endif // TRADE_HPP diff --git a/src/tool/adduser.c b/src/tool/adduser.c deleted file mode 100644 index 1954b66..0000000 --- a/src/tool/adduser.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - This program adds an user to account.txt - Don't usr it When login-sever is working. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -char *account_txt = "../save/account.txt"; - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars (unsigned char *str) -{ - int i; - int change = 0; - - for (i = 0; str[i]; i++) - { - if (str[i] < 32) - { - str[i] = '_'; - change = 1; - } - } - - return change; -} - -int main (int argc, char *argv[]) -{ - - char username[24]; - char password[24]; - char sex[2]; - - int next_id, id; - char line[1024]; - - // Check to see if account.txt exists. - printf ("Checking if '%s' file exists...\n", account_txt); - FILE *FPaccin = fopen (account_txt, "r"); - if (FPaccin == NULL) - { - printf ("'%s' file not found!\n", account_txt); - printf ("Run the setup wizard please.\n"); - exit (0); - } - - next_id = 2000000; - while (fgets (line, sizeof (line) - 1, FPaccin)) - { - if (line[0] == '/' && line[1] == '/') - { - continue; - } - if (sscanf (line, "%d\t%%newid%%\n", &id) == 1) - { - if (next_id < id) - { - next_id = id; - } - } - else - { - sscanf (line, "%i%[^ ]", &id); - if (next_id <= id) - { - next_id = id + 1; - } - } - } - fclose (FPaccin); - printf ("File exists.\n"); - - printf ("Don't create an account if the login-server is online!!!\n"); - printf - ("If the login-server is online, press ctrl+C now to stop this software.\n"); - printf ("\n"); - - strcpy (username, ""); - while (strlen (username) < 4 || strlen (username) > 23) - { - printf ("Enter an username (4-23 characters): "); - scanf ("%s", &username); - username[23] = 0; - remove_control_chars (username); - } - - strcpy (password, ""); - while (strlen (password) < 4 || strlen (password) > 23) - { - printf ("Enter a password (4-23 characters): "); - scanf ("%s", &password); - password[23] = 0; - remove_control_chars (password); - } - - strcpy (sex, ""); - while (strcmp (sex, "F") != 0 && strcmp (sex, "M") != 0) - { - printf ("Enter a gender (M for male, F for female): "); - scanf ("%s", &sex); - } - - FILE *FPaccout = fopen (account_txt, "r+"); - fseek (FPaccout, 0, SEEK_END); - fprintf (FPaccout, "%i %s %s - %s -\r\n", next_id, username, - password, sex); - fclose (FPaccout); - - printf ("Account added.\n"); -} diff --git a/src/tool/adduser.cpp b/src/tool/adduser.cpp new file mode 100644 index 0000000..1954b66 --- /dev/null +++ b/src/tool/adduser.cpp @@ -0,0 +1,115 @@ +/* + This program adds an user to account.txt + Don't usr it When login-sever is working. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char *account_txt = "../save/account.txt"; + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars (unsigned char *str) +{ + int i; + int change = 0; + + for (i = 0; str[i]; i++) + { + if (str[i] < 32) + { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +int main (int argc, char *argv[]) +{ + + char username[24]; + char password[24]; + char sex[2]; + + int next_id, id; + char line[1024]; + + // Check to see if account.txt exists. + printf ("Checking if '%s' file exists...\n", account_txt); + FILE *FPaccin = fopen (account_txt, "r"); + if (FPaccin == NULL) + { + printf ("'%s' file not found!\n", account_txt); + printf ("Run the setup wizard please.\n"); + exit (0); + } + + next_id = 2000000; + while (fgets (line, sizeof (line) - 1, FPaccin)) + { + if (line[0] == '/' && line[1] == '/') + { + continue; + } + if (sscanf (line, "%d\t%%newid%%\n", &id) == 1) + { + if (next_id < id) + { + next_id = id; + } + } + else + { + sscanf (line, "%i%[^ ]", &id); + if (next_id <= id) + { + next_id = id + 1; + } + } + } + fclose (FPaccin); + printf ("File exists.\n"); + + printf ("Don't create an account if the login-server is online!!!\n"); + printf + ("If the login-server is online, press ctrl+C now to stop this software.\n"); + printf ("\n"); + + strcpy (username, ""); + while (strlen (username) < 4 || strlen (username) > 23) + { + printf ("Enter an username (4-23 characters): "); + scanf ("%s", &username); + username[23] = 0; + remove_control_chars (username); + } + + strcpy (password, ""); + while (strlen (password) < 4 || strlen (password) > 23) + { + printf ("Enter a password (4-23 characters): "); + scanf ("%s", &password); + password[23] = 0; + remove_control_chars (password); + } + + strcpy (sex, ""); + while (strcmp (sex, "F") != 0 && strcmp (sex, "M") != 0) + { + printf ("Enter a gender (M for male, F for female): "); + scanf ("%s", &sex); + } + + FILE *FPaccout = fopen (account_txt, "r+"); + fseek (FPaccout, 0, SEEK_END); + fprintf (FPaccout, "%i %s %s - %s -\r\n", next_id, username, + password, sex); + fclose (FPaccout); + + printf ("Account added.\n"); +} diff --git a/src/tool/convert.c b/src/tool/convert.c deleted file mode 100644 index e256fc9..0000000 --- a/src/tool/convert.c +++ /dev/null @@ -1,302 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> - -#define MAX_INVENTORY 100 -#define MAX_CART 100 -#define MAX_SKILL 350 -#define GLOBAL_REG_NUM 16 - -struct item -{ - int id; - short nameid; - short amount; - short equip; - char identify; - char refine; - char attribute; - short card[4]; -}; -struct point -{ - char map[16]; - short x, y; -}; -struct skill -{ - unsigned short id, lv, flag; -}; -struct global_reg -{ - char str[16]; - int value; -}; - -struct mmo_charstatus -{ - int char_id; - int account_id; - int base_exp, job_exp, zeny; - - short class; - short status_point, skill_point; - short hp, max_hp, sp, max_sp; - short option, karma, manner; - short hair, hair_color, clothes_color; - int party_id, guild_id, pet_id; - - short weapon, shield; - short head_top, head_mid, head_bottom; - - char name[24]; - unsigned char base_level, job_level; - unsigned char str, agi, vit, int_, dex, luk, char_num, sex; - - struct point last_point, save_point, memo_point[3]; - struct item inventory[MAX_INVENTORY], cart[MAX_CART]; - struct skill skill[MAX_SKILL]; - int global_reg_num; - struct global_reg global_reg[GLOBAL_REG_NUM]; -}; - -int mmo_char_tostr (char *str, struct mmo_charstatus *p) -{ - int i; - sprintf (str, "%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%s,%d,%d\t%s,%d,%d", p->char_id, p->account_id, p->char_num, p->name, // - p->class, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, p->hp, p->max_hp, p->sp, p->max_sp, p->str, p->agi, p->vit, p->int_, p->dex, p->luk, p->status_point, p->skill_point, p->option, p->karma, p->manner, // - p->party_id, p->guild_id, p->pet_id, p->hair, p->hair_color, p->clothes_color, p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, p->last_point.map, p->last_point.x, p->last_point.y, // - p->save_point.map, p->save_point.x, p->save_point.y); - strcat (str, "\t"); - for (i = 0; i < 3; i++) - if (p->memo_point[i].map[0]) - { - sprintf (str + strlen (str), "%s,%d,%d", p->memo_point[i].map, - p->memo_point[i].x, p->memo_point[i].y); - } - strcat (str, "\t"); - for (i = 0; i < MAX_INVENTORY; i++) - if (p->inventory[i].nameid) - { - sprintf (str + strlen (str), "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->inventory[i].id, p->inventory[i].nameid, - p->inventory[i].amount, p->inventory[i].equip, - p->inventory[i].identify, p->inventory[i].refine, - p->inventory[i].attribute, p->inventory[i].card[0], - p->inventory[i].card[1], p->inventory[i].card[2], - p->inventory[i].card[3]); - } - strcat (str, "\t"); - for (i = 0; i < MAX_CART; i++) - if (p->cart[i].nameid) - { - sprintf (str + strlen (str), "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->cart[i].id, p->cart[i].nameid, p->cart[i].amount, - p->cart[i].equip, p->cart[i].identify, p->cart[i].refine, - p->cart[i].attribute, p->cart[i].card[0], - p->cart[i].card[1], p->cart[i].card[2], - p->cart[i].card[3]); - } - strcat (str, "\t"); - for (i = 0; i < MAX_SKILL; i++) - if (p->skill[i].id) - { - sprintf (str + strlen (str), "%d,%d ", p->skill[i].id, - p->skill[i].lv); - } - strcat (str, "\t"); - for (i = 0; i < p->global_reg_num; i++) - sprintf (str + strlen (str), "%s,%d ", p->global_reg[i].str, - p->global_reg[i].value); - strcat (str, "\t"); - return 0; -} - -int mmo_char_fromstr (char *str, struct mmo_charstatus *p) -{ - int tmp_int[256]; - int set, next, len, i; - - set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // - &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], p->last_point.map, &tmp_int[34], &tmp_int[35], // - p->save_point.map, &tmp_int[36], &tmp_int[37], &next); - p->char_id = tmp_int[0]; - p->account_id = tmp_int[1]; - p->char_num = tmp_int[2]; - p->class = tmp_int[3]; - p->base_level = tmp_int[4]; - p->job_level = tmp_int[5]; - p->base_exp = tmp_int[6]; - p->job_exp = tmp_int[7]; - p->zeny = tmp_int[8]; - p->hp = tmp_int[9]; - p->max_hp = tmp_int[10]; - p->sp = tmp_int[11]; - p->max_sp = tmp_int[12]; - p->str = tmp_int[13]; - p->agi = tmp_int[14]; - p->vit = tmp_int[15]; - p->int_ = tmp_int[16]; - p->dex = tmp_int[17]; - p->luk = tmp_int[18]; - p->status_point = tmp_int[19]; - p->skill_point = tmp_int[20]; - p->option = tmp_int[21]; - p->karma = tmp_int[22]; - p->manner = tmp_int[23]; - p->party_id = tmp_int[24]; - p->guild_id = tmp_int[25]; - p->pet_id = 0; - p->hair = tmp_int[26]; - p->hair_color = tmp_int[27]; - p->clothes_color = tmp_int[28]; - p->weapon = tmp_int[29]; - p->shield = tmp_int[30]; - p->head_top = tmp_int[31]; - p->head_mid = tmp_int[32]; - p->head_bottom = tmp_int[33]; - p->last_point.x = tmp_int[34]; - p->last_point.y = tmp_int[35]; - p->save_point.x = tmp_int[36]; - p->save_point.y = tmp_int[37]; - if (set != 41) - return 0; - if (str[next] == '\n' || str[next] == '\r') - return 1; // 新規データ - next++; - for (i = 0; str[next] && str[next] != '\t'; i++) - { - set = - sscanf (str + next, "%[^,],%d,%d%n", p->memo_point[i].map, - &tmp_int[0], &tmp_int[1], &len); - if (set != 3) - return 0; - p->memo_point[i].x = tmp_int[0]; - p->memo_point[i].y = tmp_int[1]; - next += len; - if (str[next] == ' ') - next++; - } - next++; - for (i = 0; str[next] && str[next] != '\t'; i++) - { - set = sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len); - if (set != 11) - return 0; - p->inventory[i].id = tmp_int[0]; - p->inventory[i].nameid = tmp_int[1]; - p->inventory[i].amount = tmp_int[2]; - p->inventory[i].equip = tmp_int[3]; - p->inventory[i].identify = tmp_int[4]; - p->inventory[i].refine = tmp_int[5]; - p->inventory[i].attribute = tmp_int[6]; - p->inventory[i].card[0] = tmp_int[7]; - p->inventory[i].card[1] = tmp_int[8]; - p->inventory[i].card[2] = tmp_int[9]; - p->inventory[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - next++; - for (i = 0; str[next] && str[next] != '\t'; i++) - { - set = sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], - &len); - if (set != 11) - return 0; - p->cart[i].id = tmp_int[0]; - p->cart[i].nameid = tmp_int[1]; - p->cart[i].amount = tmp_int[2]; - p->cart[i].equip = tmp_int[3]; - p->cart[i].identify = tmp_int[4]; - p->cart[i].refine = tmp_int[5]; - p->cart[i].attribute = tmp_int[6]; - p->cart[i].card[0] = tmp_int[7]; - p->cart[i].card[1] = tmp_int[8]; - p->cart[i].card[2] = tmp_int[9]; - p->cart[i].card[3] = tmp_int[10]; - next += len; - if (str[next] == ' ') - next++; - } - next++; - for (i = 0; str[next] && str[next] != '\t'; i++) - { - set = sscanf (str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len); - if (set != 2) - return 0; - p->skill[tmp_int[0]].id = tmp_int[0]; - p->skill[tmp_int[0]].lv = tmp_int[1]; - next += len; - if (str[next] == ' ') - next++; - } - next++; - for (i = 0; - str[next] && str[next] != '\t' && str[next] != '\n' - && str[next] != '\r'; i++) - { //global_reg実装以前のathena.txt互換のため一応'\n'チェック - set = sscanf (str + next, "%[^,],%d%n", - p->global_reg[i].str, &p->global_reg[i].value, &len); - if (set != 2) - return 0; - next += len; - if (str[next] == ' ') - next++; - } - p->global_reg_num = i; - return 1; -} - -int mmo_char_convert (char *fname1, char *fname2) -{ - char line[65536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp, *ofp; - - ifp = fopen_ (fname1, "r"); - ofp = fopen_ (fname2, "w"); - if (ifp == NULL) - { - printf ("file not found %s\n", fname1); - return 0; - } - if (ofp == NULL) - { - printf ("file open error %s\n", fname2); - return 0; - } - while (fgets (line, 65535, ifp)) - { - memset (&char_dat, 0, sizeof (struct mmo_charstatus)); - ret = mmo_char_fromstr (line, &char_dat); - if (ret) - { - mmo_char_tostr (line, &char_dat); - fprintf (ofp, "%s\n", line); - } - } - fcloseall (); - return 0; -} - -int main (int argc, char *argv[]) -{ - if (argc < 3) - { - printf ("Usage: convert <input filename> <output filename>\n"); - exit (0); - } - mmo_char_convert (argv[1], argv[2]); - - return 0; -} diff --git a/src/tool/convert.cpp b/src/tool/convert.cpp new file mode 100644 index 0000000..e256fc9 --- /dev/null +++ b/src/tool/convert.cpp @@ -0,0 +1,302 @@ +#include <stdio.h> +#include <stdlib.h> + +#define MAX_INVENTORY 100 +#define MAX_CART 100 +#define MAX_SKILL 350 +#define GLOBAL_REG_NUM 16 + +struct item +{ + int id; + short nameid; + short amount; + short equip; + char identify; + char refine; + char attribute; + short card[4]; +}; +struct point +{ + char map[16]; + short x, y; +}; +struct skill +{ + unsigned short id, lv, flag; +}; +struct global_reg +{ + char str[16]; + int value; +}; + +struct mmo_charstatus +{ + int char_id; + int account_id; + int base_exp, job_exp, zeny; + + short class; + short status_point, skill_point; + short hp, max_hp, sp, max_sp; + short option, karma, manner; + short hair, hair_color, clothes_color; + int party_id, guild_id, pet_id; + + short weapon, shield; + short head_top, head_mid, head_bottom; + + char name[24]; + unsigned char base_level, job_level; + unsigned char str, agi, vit, int_, dex, luk, char_num, sex; + + struct point last_point, save_point, memo_point[3]; + struct item inventory[MAX_INVENTORY], cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; +}; + +int mmo_char_tostr (char *str, struct mmo_charstatus *p) +{ + int i; + sprintf (str, "%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%s,%d,%d\t%s,%d,%d", p->char_id, p->account_id, p->char_num, p->name, // + p->class, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, p->hp, p->max_hp, p->sp, p->max_sp, p->str, p->agi, p->vit, p->int_, p->dex, p->luk, p->status_point, p->skill_point, p->option, p->karma, p->manner, // + p->party_id, p->guild_id, p->pet_id, p->hair, p->hair_color, p->clothes_color, p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, p->last_point.map, p->last_point.x, p->last_point.y, // + p->save_point.map, p->save_point.x, p->save_point.y); + strcat (str, "\t"); + for (i = 0; i < 3; i++) + if (p->memo_point[i].map[0]) + { + sprintf (str + strlen (str), "%s,%d,%d", p->memo_point[i].map, + p->memo_point[i].x, p->memo_point[i].y); + } + strcat (str, "\t"); + for (i = 0; i < MAX_INVENTORY; i++) + if (p->inventory[i].nameid) + { + sprintf (str + strlen (str), "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->inventory[i].id, p->inventory[i].nameid, + p->inventory[i].amount, p->inventory[i].equip, + p->inventory[i].identify, p->inventory[i].refine, + p->inventory[i].attribute, p->inventory[i].card[0], + p->inventory[i].card[1], p->inventory[i].card[2], + p->inventory[i].card[3]); + } + strcat (str, "\t"); + for (i = 0; i < MAX_CART; i++) + if (p->cart[i].nameid) + { + sprintf (str + strlen (str), "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->cart[i].id, p->cart[i].nameid, p->cart[i].amount, + p->cart[i].equip, p->cart[i].identify, p->cart[i].refine, + p->cart[i].attribute, p->cart[i].card[0], + p->cart[i].card[1], p->cart[i].card[2], + p->cart[i].card[3]); + } + strcat (str, "\t"); + for (i = 0; i < MAX_SKILL; i++) + if (p->skill[i].id) + { + sprintf (str + strlen (str), "%d,%d ", p->skill[i].id, + p->skill[i].lv); + } + strcat (str, "\t"); + for (i = 0; i < p->global_reg_num; i++) + sprintf (str + strlen (str), "%s,%d ", p->global_reg[i].str, + p->global_reg[i].value); + strcat (str, "\t"); + return 0; +} + +int mmo_char_fromstr (char *str, struct mmo_charstatus *p) +{ + int tmp_int[256]; + int set, next, len, i; + + set = sscanf (str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" "\t%[^,],%d,%d\t%[^,],%d,%d%n", &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[27], &tmp_int[28], &tmp_int[29], &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], p->last_point.map, &tmp_int[34], &tmp_int[35], // + p->save_point.map, &tmp_int[36], &tmp_int[37], &next); + p->char_id = tmp_int[0]; + p->account_id = tmp_int[1]; + p->char_num = tmp_int[2]; + p->class = tmp_int[3]; + p->base_level = tmp_int[4]; + p->job_level = tmp_int[5]; + p->base_exp = tmp_int[6]; + p->job_exp = tmp_int[7]; + p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; + p->sp = tmp_int[11]; + p->max_sp = tmp_int[12]; + p->str = tmp_int[13]; + p->agi = tmp_int[14]; + p->vit = tmp_int[15]; + p->int_ = tmp_int[16]; + p->dex = tmp_int[17]; + p->luk = tmp_int[18]; + p->status_point = tmp_int[19]; + p->skill_point = tmp_int[20]; + p->option = tmp_int[21]; + p->karma = tmp_int[22]; + p->manner = tmp_int[23]; + p->party_id = tmp_int[24]; + p->guild_id = tmp_int[25]; + p->pet_id = 0; + p->hair = tmp_int[26]; + p->hair_color = tmp_int[27]; + p->clothes_color = tmp_int[28]; + p->weapon = tmp_int[29]; + p->shield = tmp_int[30]; + p->head_top = tmp_int[31]; + p->head_mid = tmp_int[32]; + p->head_bottom = tmp_int[33]; + p->last_point.x = tmp_int[34]; + p->last_point.y = tmp_int[35]; + p->save_point.x = tmp_int[36]; + p->save_point.y = tmp_int[37]; + if (set != 41) + return 0; + if (str[next] == '\n' || str[next] == '\r') + return 1; // 新規データ + next++; + for (i = 0; str[next] && str[next] != '\t'; i++) + { + set = + sscanf (str + next, "%[^,],%d,%d%n", p->memo_point[i].map, + &tmp_int[0], &tmp_int[1], &len); + if (set != 3) + return 0; + p->memo_point[i].x = tmp_int[0]; + p->memo_point[i].y = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + next++; + for (i = 0; str[next] && str[next] != '\t'; i++) + { + set = sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len); + if (set != 11) + return 0; + p->inventory[i].id = tmp_int[0]; + p->inventory[i].nameid = tmp_int[1]; + p->inventory[i].amount = tmp_int[2]; + p->inventory[i].equip = tmp_int[3]; + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; + p->inventory[i].card[0] = tmp_int[7]; + p->inventory[i].card[1] = tmp_int[8]; + p->inventory[i].card[2] = tmp_int[9]; + p->inventory[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + next++; + for (i = 0; str[next] && str[next] != '\t'; i++) + { + set = sscanf (str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], + &len); + if (set != 11) + return 0; + p->cart[i].id = tmp_int[0]; + p->cart[i].nameid = tmp_int[1]; + p->cart[i].amount = tmp_int[2]; + p->cart[i].equip = tmp_int[3]; + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; + p->cart[i].card[0] = tmp_int[7]; + p->cart[i].card[1] = tmp_int[8]; + p->cart[i].card[2] = tmp_int[9]; + p->cart[i].card[3] = tmp_int[10]; + next += len; + if (str[next] == ' ') + next++; + } + next++; + for (i = 0; str[next] && str[next] != '\t'; i++) + { + set = sscanf (str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len); + if (set != 2) + return 0; + p->skill[tmp_int[0]].id = tmp_int[0]; + p->skill[tmp_int[0]].lv = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + next++; + for (i = 0; + str[next] && str[next] != '\t' && str[next] != '\n' + && str[next] != '\r'; i++) + { //global_reg実装以前のathena.txt互換のため一応'\n'チェック + set = sscanf (str + next, "%[^,],%d%n", + p->global_reg[i].str, &p->global_reg[i].value, &len); + if (set != 2) + return 0; + next += len; + if (str[next] == ' ') + next++; + } + p->global_reg_num = i; + return 1; +} + +int mmo_char_convert (char *fname1, char *fname2) +{ + char line[65536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp, *ofp; + + ifp = fopen_ (fname1, "r"); + ofp = fopen_ (fname2, "w"); + if (ifp == NULL) + { + printf ("file not found %s\n", fname1); + return 0; + } + if (ofp == NULL) + { + printf ("file open error %s\n", fname2); + return 0; + } + while (fgets (line, 65535, ifp)) + { + memset (&char_dat, 0, sizeof (struct mmo_charstatus)); + ret = mmo_char_fromstr (line, &char_dat); + if (ret) + { + mmo_char_tostr (line, &char_dat); + fprintf (ofp, "%s\n", line); + } + } + fcloseall (); + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc < 3) + { + printf ("Usage: convert <input filename> <output filename>\n"); + exit (0); + } + mmo_char_convert (argv[1], argv[2]); + + return 0; +} diff --git a/src/tool/eathena-monitor.c b/src/tool/eathena-monitor.c deleted file mode 100644 index 1b1abd5..0000000 --- a/src/tool/eathena-monitor.c +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Name: eAthena processes monitor - * Original Author: Bartosz Waszak <waszi@evil.org.pl> - * Rewrite Author: Ben Longbons <b.r.longbons@gmail.com> - * License: GPL - * Compilation: - * gcc -o eathena-monitor eathena-monitor.c - */ - -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> - -#include <time.h> -#include <fcntl.h> -#include <sys/wait.h> -#include <signal.h> - -#define HOME getenv("HOME") -#define LOGIN_SERVER "./login-server" -#define MAP_SERVER "./map-server" -#define CHAR_SERVER "./char-server" -#define CONFIG "conf/eathena-monitor.conf" -#define LOGFILE "log/eathena-monitor.log" - - -#define SKIP_BLANK(ptr) ptr += skip_blank(ptr) -static inline size_t skip_blank(const char* ptr) { - size_t i = 0; - while ( - (ptr[i] == ' ') || - (ptr[i] == '\b') || - (ptr[i] == '\n') || - (ptr[i] == '\r') - ) ptr++; - return i; -} - -#define GOTO_EQL(ptr) ptr += goto_eql(ptr) -static inline size_t goto_eql(const char* ptr) { - size_t i = 0; - while ( - (ptr[i] != '\0') && - (ptr[i] != '=') && - (ptr[i] != '\n') && - (ptr[i] != '\r') - ) ptr++; - return i; -} - -#define GOTO_EOL(ptr) ptr += goto_newline(ptr) -static inline size_t goto_newline(const char* ptr) { - size_t i = 0; - while ( - (ptr[i] != '\0') && - (ptr[i] != '\n') && - (ptr[i] != '\r') - ) ptr++; - return i; -} - -// initialiized to $HOME/tmwserver -const char *workdir; -//the rest are relative to workdir -const char *login_server = LOGIN_SERVER; -const char *map_server = MAP_SERVER; -const char *char_server = CHAR_SERVER; -const char *logfile = LOGFILE; -// this variable is hard-coded, but the command-line is checked first -const char *config = CONFIG; - -pid_t pid_login, pid_map, pid_char; - -const char* make_path (const char* base, const char* path) { - size_t base_len = strlen(base); - size_t path_len = strlen(path); - char* out = (char *)malloc(base_len + 1 + path_len + 1); - memcpy(out, base, base_len); - out[base_len] = '/'; - memcpy(out + base_len + 1, path, path_len); - out[base_len + 1 + path_len] = '\0'; - return out; -} - -void parse_option (char *name, char *value) { - if (!strcasecmp(name, "login_server")) { - login_server = strdup(value); - } else if (!strcasecmp(name, "map_server")) { - map_server = strdup(value); - } else if (!strcasecmp(name, "char_server")) { - char_server = strdup(value); - } else if (!strcasecmp(name, "workdir")) { - workdir = strdup(value); - } else if (!strcasecmp(name, "logfile")) { - logfile = strdup(value); - } else { - fprintf(stderr, "WARNING: ingnoring invalid option '%s' = '%s'\n", name, value); - } -} - -void read_config(const char *filename) { - FILE *input; - char string[1000]; - - if (!(input = fopen(filename,"r")) && !(input = fopen (config, "r"))) { - perror("Unable to load config file"); - return; - } - - while (1) { - if (fgets (string, sizeof (string) - 1, input) == NULL) - break; - char *str = string, *name, *value; - SKIP_BLANK(str); - string[sizeof (string) - 1] = '\0'; - if (*str == '#') - continue; - if (*str == '\0') - continue; - name = str; - - GOTO_EQL (str); - - if (*str != '=') { - continue; - } - - *str++ = '\0'; - SKIP_BLANK(str); - value = str; - GOTO_EOL(str); - *str = '\0'; - parse_option(name, value); - } - - fclose (input); -} - -pid_t start_process(const char *exec) { - const char *args[2] = {exec, NULL}; - pid_t pid = fork(); - if (pid == -1) { - fprintf(stderr, "Failed to fork"); - return 0; - } - if (pid == 0) { - execv(exec, (char**)args); - perror("Failed to exec"); - kill(getppid(), SIGABRT); - exit(1); - } - return pid; -} - -// Kill all children with the same signal we got, then ourself. -void stop_process(int sig) { - if (pid_map) kill(pid_map, sig); - if (pid_login) kill(pid_login, sig); - if (pid_char) kill(pid_char, sig); - signal(sig, SIG_DFL); - raise(sig); -} - -int main(int argc, char *argv[]) { - // These are all the signals we are likely to get - // The shell handles stop/cont - signal(SIGTERM, stop_process); - signal(SIGINT, stop_process); - signal(SIGQUIT, stop_process); - signal(SIGABRT, stop_process); - - workdir = make_path(HOME, "tmwserver"); - - read_config(argc>1 ? argv[1] : NULL); - - if (chdir(workdir) < 0) perror("Failed to change directory"), exit(1); - - printf ("Starting:\n"); - printf ("* workdir: %s\n", workdir); - printf ("* login_server: %s\n", login_server); - printf ("* map_server: %s\n", map_server); - printf ("* char_server: %s\n", char_server); - { - //make sure all possible file descriptors are free for use by the servers - //if there are file descriptors higher than the max open from before the limit dropped, that's not our problem - int fd = sysconf(_SC_OPEN_MAX); - while (--fd > 2) - if (close(fd) == 0) - fprintf(stderr, "close fd %d\n", fd); - fd = open("/dev/null", O_RDWR); - if (fd < 0) perror("open /dev/null"), exit(1); - dup2(fd, 0); - dup2(fd, 1); - close(fd); - } - while (1) { - // write stuff to stderr - time_t t = time(NULL); - struct tm *tmp = localtime(&t); - char timestamp[256]; - strftime(timestamp, sizeof(timestamp), "%F %T", tmp); - - if (!pid_login) { - pid_login = start_process(login_server); - fprintf (stderr, "[%s] forked login server: %lu\n", timestamp, (unsigned long)pid_login); - } - if (!pid_char) { - pid_char = start_process(char_server); - fprintf (stderr, "[%s] forked char server: %lu\n", timestamp, (unsigned long)pid_char); - } - if (!pid_map) { - pid_map = start_process(map_server); - fprintf (stderr, "[%s] forked map server: %lu\n", timestamp, (unsigned long)pid_map); - } - pid_t dead = wait(NULL); - if (dead < 0) perror("Failed to wait for child"), exit(1); - if (pid_login == dead) pid_login = 0; - if (pid_char == dead) pid_char = 0; - if (pid_map == dead) pid_map = 0; - } -} diff --git a/src/tool/eathena-monitor.cpp b/src/tool/eathena-monitor.cpp new file mode 100644 index 0000000..1b1abd5 --- /dev/null +++ b/src/tool/eathena-monitor.cpp @@ -0,0 +1,223 @@ +/** + * Name: eAthena processes monitor + * Original Author: Bartosz Waszak <waszi@evil.org.pl> + * Rewrite Author: Ben Longbons <b.r.longbons@gmail.com> + * License: GPL + * Compilation: + * gcc -o eathena-monitor eathena-monitor.c + */ + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <time.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <signal.h> + +#define HOME getenv("HOME") +#define LOGIN_SERVER "./login-server" +#define MAP_SERVER "./map-server" +#define CHAR_SERVER "./char-server" +#define CONFIG "conf/eathena-monitor.conf" +#define LOGFILE "log/eathena-monitor.log" + + +#define SKIP_BLANK(ptr) ptr += skip_blank(ptr) +static inline size_t skip_blank(const char* ptr) { + size_t i = 0; + while ( + (ptr[i] == ' ') || + (ptr[i] == '\b') || + (ptr[i] == '\n') || + (ptr[i] == '\r') + ) ptr++; + return i; +} + +#define GOTO_EQL(ptr) ptr += goto_eql(ptr) +static inline size_t goto_eql(const char* ptr) { + size_t i = 0; + while ( + (ptr[i] != '\0') && + (ptr[i] != '=') && + (ptr[i] != '\n') && + (ptr[i] != '\r') + ) ptr++; + return i; +} + +#define GOTO_EOL(ptr) ptr += goto_newline(ptr) +static inline size_t goto_newline(const char* ptr) { + size_t i = 0; + while ( + (ptr[i] != '\0') && + (ptr[i] != '\n') && + (ptr[i] != '\r') + ) ptr++; + return i; +} + +// initialiized to $HOME/tmwserver +const char *workdir; +//the rest are relative to workdir +const char *login_server = LOGIN_SERVER; +const char *map_server = MAP_SERVER; +const char *char_server = CHAR_SERVER; +const char *logfile = LOGFILE; +// this variable is hard-coded, but the command-line is checked first +const char *config = CONFIG; + +pid_t pid_login, pid_map, pid_char; + +const char* make_path (const char* base, const char* path) { + size_t base_len = strlen(base); + size_t path_len = strlen(path); + char* out = (char *)malloc(base_len + 1 + path_len + 1); + memcpy(out, base, base_len); + out[base_len] = '/'; + memcpy(out + base_len + 1, path, path_len); + out[base_len + 1 + path_len] = '\0'; + return out; +} + +void parse_option (char *name, char *value) { + if (!strcasecmp(name, "login_server")) { + login_server = strdup(value); + } else if (!strcasecmp(name, "map_server")) { + map_server = strdup(value); + } else if (!strcasecmp(name, "char_server")) { + char_server = strdup(value); + } else if (!strcasecmp(name, "workdir")) { + workdir = strdup(value); + } else if (!strcasecmp(name, "logfile")) { + logfile = strdup(value); + } else { + fprintf(stderr, "WARNING: ingnoring invalid option '%s' = '%s'\n", name, value); + } +} + +void read_config(const char *filename) { + FILE *input; + char string[1000]; + + if (!(input = fopen(filename,"r")) && !(input = fopen (config, "r"))) { + perror("Unable to load config file"); + return; + } + + while (1) { + if (fgets (string, sizeof (string) - 1, input) == NULL) + break; + char *str = string, *name, *value; + SKIP_BLANK(str); + string[sizeof (string) - 1] = '\0'; + if (*str == '#') + continue; + if (*str == '\0') + continue; + name = str; + + GOTO_EQL (str); + + if (*str != '=') { + continue; + } + + *str++ = '\0'; + SKIP_BLANK(str); + value = str; + GOTO_EOL(str); + *str = '\0'; + parse_option(name, value); + } + + fclose (input); +} + +pid_t start_process(const char *exec) { + const char *args[2] = {exec, NULL}; + pid_t pid = fork(); + if (pid == -1) { + fprintf(stderr, "Failed to fork"); + return 0; + } + if (pid == 0) { + execv(exec, (char**)args); + perror("Failed to exec"); + kill(getppid(), SIGABRT); + exit(1); + } + return pid; +} + +// Kill all children with the same signal we got, then ourself. +void stop_process(int sig) { + if (pid_map) kill(pid_map, sig); + if (pid_login) kill(pid_login, sig); + if (pid_char) kill(pid_char, sig); + signal(sig, SIG_DFL); + raise(sig); +} + +int main(int argc, char *argv[]) { + // These are all the signals we are likely to get + // The shell handles stop/cont + signal(SIGTERM, stop_process); + signal(SIGINT, stop_process); + signal(SIGQUIT, stop_process); + signal(SIGABRT, stop_process); + + workdir = make_path(HOME, "tmwserver"); + + read_config(argc>1 ? argv[1] : NULL); + + if (chdir(workdir) < 0) perror("Failed to change directory"), exit(1); + + printf ("Starting:\n"); + printf ("* workdir: %s\n", workdir); + printf ("* login_server: %s\n", login_server); + printf ("* map_server: %s\n", map_server); + printf ("* char_server: %s\n", char_server); + { + //make sure all possible file descriptors are free for use by the servers + //if there are file descriptors higher than the max open from before the limit dropped, that's not our problem + int fd = sysconf(_SC_OPEN_MAX); + while (--fd > 2) + if (close(fd) == 0) + fprintf(stderr, "close fd %d\n", fd); + fd = open("/dev/null", O_RDWR); + if (fd < 0) perror("open /dev/null"), exit(1); + dup2(fd, 0); + dup2(fd, 1); + close(fd); + } + while (1) { + // write stuff to stderr + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + char timestamp[256]; + strftime(timestamp, sizeof(timestamp), "%F %T", tmp); + + if (!pid_login) { + pid_login = start_process(login_server); + fprintf (stderr, "[%s] forked login server: %lu\n", timestamp, (unsigned long)pid_login); + } + if (!pid_char) { + pid_char = start_process(char_server); + fprintf (stderr, "[%s] forked char server: %lu\n", timestamp, (unsigned long)pid_char); + } + if (!pid_map) { + pid_map = start_process(map_server); + fprintf (stderr, "[%s] forked map server: %lu\n", timestamp, (unsigned long)pid_map); + } + pid_t dead = wait(NULL); + if (dead < 0) perror("Failed to wait for child"), exit(1); + if (pid_login == dead) pid_login = 0; + if (pid_char == dead) pid_char = 0; + if (pid_map == dead) pid_map = 0; + } +} diff --git a/src/tool/itemfrob.c b/src/tool/itemfrob.c deleted file mode 100644 index 4651452..0000000 --- a/src/tool/itemfrob.c +++ /dev/null @@ -1,130 +0,0 @@ -// Compile with -// gcc -m32 -I src/char -I src/common charfrob.c -o charfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_pet.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o - -#include <stdio.h> -#include <stdlib.h> -#include "../common/mmo.h" -// Yes, this is intentional -#include "../char/char.c" - -// Well, this is not terribly elegant, but I don't have that much time. -#define MAX_ITEM_ID 65535 -int inv_translate[MAX_ITEM_ID]; - -void transform_char (struct mmo_charstatus *p) -{ - int k; - for (k = 0; k < MAX_INVENTORY; k++) - p->inventory[k].nameid = inv_translate[p->inventory[k].nameid]; -} - -int mmo_char_convert () -{ - char line[965536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp, *ofp; - - ifp = stdin; - ofp = stdout; - while (fgets (line, 65535, ifp)) - { - memset (&char_dat, 0, sizeof (struct mmo_charstatus)); - ret = mmo_char_fromstr (line, &char_dat); - if (ret) - { - transform_char (&char_dat); - mmo_char_tostr (line, &char_dat); - fprintf (ofp, "%s\n", line); - } - } - return 0; -} - -#define PARSE_MODE_NEXTNUM 0 -#define PARSE_MODE_RANGE 1 - -int init (char *source, char *dest) -{ - int i; - char *end_of_num; - int dest_nr = strtol (dest, &end_of_num, 0); - int range_start; - int mode = PARSE_MODE_NEXTNUM; - - if (*end_of_num) - { - fprintf (stderr, "Invalid inventory ID: `%s'\n", dest); - return 1; - } - - /* init */ - for (i = 0; i < MAX_ITEM_ID; i++) - inv_translate[i] = i; - - while (*source) - { - int nr = strtol (source, &end_of_num, 0); - char sep; - - if (end_of_num == source) - { - fprintf (stderr, "Invalid source range description: `%s'\n", - source); - return 1; - } - - switch (mode) - { - case PARSE_MODE_NEXTNUM: - inv_translate[nr] = dest_nr; - break; - case PARSE_MODE_RANGE: - for (i = range_start; i <= nr; i++) - inv_translate[i] = dest_nr; - break; - default: - fprintf (stderr, "Internal error at %d\n", __LINE__); - return 1; - }; - - sep = *end_of_num++; - - switch (sep) - { - case '-': - range_start = nr; - mode = PARSE_MODE_RANGE; - break; - case ',': - mode = PARSE_MODE_NEXTNUM; - break; - case 0: - return 0; - default: - fprintf (stderr, "Invalid token in range spec: `%c'\n", sep); - return 1; - } - - source = end_of_num; - } - return 0; -} - -int main (int argc, char *argv[]) -{ - if (argc < 3) - { - printf - ("Usage: %s <inventory ID input range> <inventory ID output>\n", - argv[0]); - printf ("e.g., %s 501-555 701\n", argv[0]); - exit (0); - } - if (init (argv[1], argv[2])) - return 1; - - mmo_char_convert (); - - return 0; -} diff --git a/src/tool/itemfrob.cpp b/src/tool/itemfrob.cpp new file mode 100644 index 0000000..81638eb --- /dev/null +++ b/src/tool/itemfrob.cpp @@ -0,0 +1,130 @@ +// Compile with +// gcc -m32 -I src/char -I src/common charfrob.c -o charfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_pet.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o + +#include <stdio.h> +#include <stdlib.h> +#include "../common/mmo.hpp" +// Yes, this is intentional +#include "../char/char.cpp" + +// Well, this is not terribly elegant, but I don't have that much time. +#define MAX_ITEM_ID 65535 +int inv_translate[MAX_ITEM_ID]; + +void transform_char (struct mmo_charstatus *p) +{ + int k; + for (k = 0; k < MAX_INVENTORY; k++) + p->inventory[k].nameid = inv_translate[p->inventory[k].nameid]; +} + +int mmo_char_convert () +{ + char line[965536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp, *ofp; + + ifp = stdin; + ofp = stdout; + while (fgets (line, 65535, ifp)) + { + memset (&char_dat, 0, sizeof (struct mmo_charstatus)); + ret = mmo_char_fromstr (line, &char_dat); + if (ret) + { + transform_char (&char_dat); + mmo_char_tostr (line, &char_dat); + fprintf (ofp, "%s\n", line); + } + } + return 0; +} + +#define PARSE_MODE_NEXTNUM 0 +#define PARSE_MODE_RANGE 1 + +int init (char *source, char *dest) +{ + int i; + char *end_of_num; + int dest_nr = strtol (dest, &end_of_num, 0); + int range_start; + int mode = PARSE_MODE_NEXTNUM; + + if (*end_of_num) + { + fprintf (stderr, "Invalid inventory ID: `%s'\n", dest); + return 1; + } + + /* init */ + for (i = 0; i < MAX_ITEM_ID; i++) + inv_translate[i] = i; + + while (*source) + { + int nr = strtol (source, &end_of_num, 0); + char sep; + + if (end_of_num == source) + { + fprintf (stderr, "Invalid source range description: `%s'\n", + source); + return 1; + } + + switch (mode) + { + case PARSE_MODE_NEXTNUM: + inv_translate[nr] = dest_nr; + break; + case PARSE_MODE_RANGE: + for (i = range_start; i <= nr; i++) + inv_translate[i] = dest_nr; + break; + default: + fprintf (stderr, "Internal error at %d\n", __LINE__); + return 1; + }; + + sep = *end_of_num++; + + switch (sep) + { + case '-': + range_start = nr; + mode = PARSE_MODE_RANGE; + break; + case ',': + mode = PARSE_MODE_NEXTNUM; + break; + case 0: + return 0; + default: + fprintf (stderr, "Invalid token in range spec: `%c'\n", sep); + return 1; + } + + source = end_of_num; + } + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc < 3) + { + printf + ("Usage: %s <inventory ID input range> <inventory ID output>\n", + argv[0]); + printf ("e.g., %s 501-555 701\n", argv[0]); + exit (0); + } + if (init (argv[1], argv[2])) + return 1; + + mmo_char_convert (); + + return 0; +} diff --git a/src/tool/mapfrob.c b/src/tool/mapfrob.c deleted file mode 100644 index 9dc1a5b..0000000 --- a/src/tool/mapfrob.c +++ /dev/null @@ -1,129 +0,0 @@ -// Compile with -// gcc -m32 -I src/char -I src/common charfrob.c -o charfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_pet.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o - -#include <stdio.h> -#include <stdlib.h> -#include "../common/mmo.h" -// Yes, this is intentional -#include "../char/char.c" - -// Well, this is not terribly elegant, but I don't have that much time. -#define MAX_MAP 1024 -#define MAP_NAME_SIZE 32 -int maps_nr = 0; -struct -{ - char old[MAP_NAME_SIZE], new[MAP_NAME_SIZE]; -} maps[MAX_MAP]; - -void transform_point (struct point *p) -{ - int k; - - if (!p->map[0]) - return; - - for (k = 0; k < maps_nr; k++) - if (!strcmp (p->map, maps[k].old)) - { - strcpy (p->map, maps[k].new); - return; - } - - fprintf (stderr, "Warning: untranslated map `%s'\n", p->map); -} - -void transform_char (struct mmo_charstatus *p) -{ - int i; - - transform_point (&p->last_point); - transform_point (&p->save_point); - - for (i = 0; i < 10; i++) - transform_point (&p->memo_point[i]); -} - -int mmo_char_convert () -{ - char line[965536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp, *ofp; - - ifp = stdin; - ofp = stdout; - while (fgets (line, 65535, ifp)) - { - memset (&char_dat, 0, sizeof (struct mmo_charstatus)); - ret = mmo_char_fromstr (line, &char_dat); - if (ret) - { - transform_char (&char_dat); - mmo_char_tostr (line, &char_dat); - fprintf (ofp, "%s\n", line); - } - } - return 0; -} - -#define PARSE_MODE_NEXTNUM 0 -#define PARSE_MODE_RANGE 1 - -int init (int count, char **translates) -{ - int i; - char *suffix = ".gat"; - - for (i = 0; i < count; i++) - { - char *src = translates[i]; - char *dest = strchr (src, ':'); - - if (!dest) - { - fprintf (stderr, "Missing colon in: `%s'\n", src); - return 1; - } - - *dest++ = 0; - - if (strlen (src) + strlen (suffix) >= MAP_NAME_SIZE) - { - fprintf (stderr, "Map name prefix too long: `%s'\n", src); - return 1; - } - - if (strlen (dest) + strlen (suffix) >= MAP_NAME_SIZE) - { - fprintf (stderr, "Map name prefix too long: `%s'\n", dest); - return 1; - } - - strncpy (maps[maps_nr].old, src, MAP_NAME_SIZE); - strcat (maps[maps_nr].old, suffix); - strncpy (maps[maps_nr].new, dest, MAP_NAME_SIZE); - strcat (maps[maps_nr].new, suffix); - - ++maps_nr; - } - - return 0; -} - -int main (int argc, char *argv[]) -{ - if (argc < 2) - { - printf ("Usage: %s oldmap0:newmap0 oldmap1:newmap1 ...\n", argv[0]); - printf ("e.g., %s new_1-1:001-2 new_2-1:001-1\n", argv[0]); - puts ("The extension `.gat' is appended implicitly."); - exit (0); - } - if (init (argc - 1, argv + 1)) - return 1; - - mmo_char_convert (); - - return 0; -} diff --git a/src/tool/mapfrob.cpp b/src/tool/mapfrob.cpp new file mode 100644 index 0000000..8663ae0 --- /dev/null +++ b/src/tool/mapfrob.cpp @@ -0,0 +1,129 @@ +// Compile with +// gcc -m32 -I src/char -I src/common charfrob.c -o charfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_pet.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o + +#include <stdio.h> +#include <stdlib.h> +#include "../common/mmo.hpp" +// Yes, this is intentional +#include "../char/char.cpp" + +// Well, this is not terribly elegant, but I don't have that much time. +#define MAX_MAP 1024 +#define MAP_NAME_SIZE 32 +int maps_nr = 0; +struct +{ + char old[MAP_NAME_SIZE], new[MAP_NAME_SIZE]; +} maps[MAX_MAP]; + +void transform_point (struct point *p) +{ + int k; + + if (!p->map[0]) + return; + + for (k = 0; k < maps_nr; k++) + if (!strcmp (p->map, maps[k].old)) + { + strcpy (p->map, maps[k].new); + return; + } + + fprintf (stderr, "Warning: untranslated map `%s'\n", p->map); +} + +void transform_char (struct mmo_charstatus *p) +{ + int i; + + transform_point (&p->last_point); + transform_point (&p->save_point); + + for (i = 0; i < 10; i++) + transform_point (&p->memo_point[i]); +} + +int mmo_char_convert () +{ + char line[965536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp, *ofp; + + ifp = stdin; + ofp = stdout; + while (fgets (line, 65535, ifp)) + { + memset (&char_dat, 0, sizeof (struct mmo_charstatus)); + ret = mmo_char_fromstr (line, &char_dat); + if (ret) + { + transform_char (&char_dat); + mmo_char_tostr (line, &char_dat); + fprintf (ofp, "%s\n", line); + } + } + return 0; +} + +#define PARSE_MODE_NEXTNUM 0 +#define PARSE_MODE_RANGE 1 + +int init (int count, char **translates) +{ + int i; + char *suffix = ".gat"; + + for (i = 0; i < count; i++) + { + char *src = translates[i]; + char *dest = strchr (src, ':'); + + if (!dest) + { + fprintf (stderr, "Missing colon in: `%s'\n", src); + return 1; + } + + *dest++ = 0; + + if (strlen (src) + strlen (suffix) >= MAP_NAME_SIZE) + { + fprintf (stderr, "Map name prefix too long: `%s'\n", src); + return 1; + } + + if (strlen (dest) + strlen (suffix) >= MAP_NAME_SIZE) + { + fprintf (stderr, "Map name prefix too long: `%s'\n", dest); + return 1; + } + + strncpy (maps[maps_nr].old, src, MAP_NAME_SIZE); + strcat (maps[maps_nr].old, suffix); + strncpy (maps[maps_nr].new, dest, MAP_NAME_SIZE); + strcat (maps[maps_nr].new, suffix); + + ++maps_nr; + } + + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc < 2) + { + printf ("Usage: %s oldmap0:newmap0 oldmap1:newmap1 ...\n", argv[0]); + printf ("e.g., %s new_1-1:001-2 new_2-1:001-1\n", argv[0]); + puts ("The extension `.gat' is appended implicitly."); + exit (0); + } + if (init (argc - 1, argv + 1)) + return 1; + + mmo_char_convert (); + + return 0; +} diff --git a/src/tool/marriage-info.c b/src/tool/marriage-info.c deleted file mode 100644 index 4d7cbc3..0000000 --- a/src/tool/marriage-info.c +++ /dev/null @@ -1,482 +0,0 @@ -/* To build: -gcc -O2 -m32 -Isrc/common -Isrc/char -Isrc/map -Isrc/login -o marriage-info \ -marriage-info.c src/common/socket.o src/common/timer.o src/common/db.o \ -src/common/lock.o src/common/malloc.o src/char/int_guild.o \ -src/char/int_party.o src/char/int_storage.o src/char/inter.o -*/ - -#include <stdio.h> -#include <stdlib.h> -#include "../login/login.h" -#include "../common/mmo.h" -// Yes, this is intentional -#include "../char/char.c" - -int mode; -#define MODE_MARRIED 0 -#define MODE_SINGLES_F 1 -#define MODE_SINGLES_M 2 -#define MODE_SINGLES_A 3 - -#define CHUNK_SIZE 1024 - -/* chunked list to represent leaves */ -typedef struct { - int char_id; - int exp; - char name[24]; - unsigned char level; - unsigned char sex; -} char_leaf_t; - -/* Chunks are in descending order, but chars are in ascending order */ -typedef struct chunklist_node { - int size; - char_leaf_t chars[CHUNK_SIZE]; - struct chunklist_node *next; -} chunklist_node_t; - -chunklist_node_t *list = NULL; - -static void -insert(chunklist_node_t **listp, struct mmo_charstatus *p) -{ - chunklist_node_t *chunk = *listp; - char_leaf_t *l; - - if ((chunk && chunk->size == CHUNK_SIZE) - || (!chunk)) { - chunk = malloc(sizeof(chunklist_node_t)); - chunk->size = 0; - chunk->next = *listp; - *listp = chunk; - } - - l = &chunk->chars[chunk->size++]; - - l->char_id = p->char_id; - l->level = p->base_level; - l->exp = p->base_exp; - l->sex = p->sex; - strcpy(l->name, p->name); -} - -static int -compare_leaf(const void *l, const void *r) -{ - return ((char_leaf_t *)l)->char_id - ((char_leaf_t *)r)->char_id; -} - -static char_leaf_t * -find(chunklist_node_t *list, int char_id) -{ - if (!list) - return NULL; /* fatal error */ - else { - int start = list->chars[0].char_id; - int stop = list->chars[list->size - 1].char_id; - - if (char_id >= start && char_id <= stop) { /* in this chunk */ - char_leaf_t key; - key.char_id = char_id; - return (char_leaf_t *) - bsearch(&key, &list->chars, list->size, sizeof(char_leaf_t), - compare_leaf); - } - else find(list->next, char_id); - } -} - - -void /* replace blanks with percent signs */ -namefrob(char *n) -{ - while (*n++) /* can't start with a blank */ - if (*n == ' ') - *n = '%'; -} - -/* --------------------------------------------------------------------------------- - Copied and pasted due to lacking modularity -*/ - -char account_filename[1024] = "save/account.txt"; -int account_id_count = START_ACCOUNT_NUM; - -struct auth_dat { - int account_id, sex; - char userid[24], pass[24], lastlogin[24]; - int logincount; - int state; - char email[40]; - char error_message[20]; - time_t ban_until_time; - time_t connect_until_time; - char last_ip[16]; - char memo[255]; - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -} *auth_dat; - -int auth_num = 0, auth_max = 0; - - -/* - Reading of the accounts database -*/ -int mmo_auth_init(void) { - FILE *fp; - int account_id, logincount, state, n, i, j, v; - char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; - time_t ban_until_time; - time_t connect_until_time; - char str[2048]; - int GM_count = 0; - int server_count = 0; - - auth_dat = calloc(sizeof(struct auth_dat) * 256, 1); - auth_max = 256; - - fp = fopen(account_filename, "r"); - if (fp == NULL) { - printf("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", account_filename); - return 0; - } - - while(fgets(line, sizeof(line)-1, fp) != NULL) { - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof(line)-1] = '\0'; - p = line; - - if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n)) == 13 && line[n] == '\t') || - ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) { - n = n + 1; - - if (account_id > END_ACCOUNT_NUM) { - continue; - } - userid[23] = '\0'; - remove_control_chars(userid); - for(j = 0; j < auth_num; j++) { - if (auth_dat[j].account_id == account_id) { - break; - } else if (strcmp(auth_dat[j].userid, userid) == 0) { - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) { - auth_max += 256; - auth_dat = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); - } - - memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy(auth_dat[auth_num].userid, userid, 24); - - pass[23] = '\0'; - remove_control_chars(pass); - strncpy(auth_dat[auth_num].pass, pass, 24); - - lastlogin[23] = '\0'; - remove_control_chars(lastlogin); - strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); - - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - - if (e_mail_check(email) == 0) { - strncpy(auth_dat[auth_num].email, "a@a.com", 40); - } else { - remove_control_chars(email); - strncpy(auth_dat[auth_num].email, email, 40); - } - - error_message[19] = '\0'; - remove_control_chars(error_message); - if (error_message[0] == '\0' || state != 7) { - strncpy(auth_dat[auth_num].error_message, "-", 20); - } else { - strncpy(auth_dat[auth_num].error_message, error_message, 20); - } - - if (i == 13) - auth_dat[auth_num].ban_until_time = ban_until_time; - else - auth_dat[auth_num].ban_until_time = 0; - - auth_dat[auth_num].connect_until_time = connect_until_time; - - last_ip[15] = '\0'; - remove_control_chars(last_ip); - strncpy(auth_dat[auth_num].last_ip, last_ip, 16); - - memo[254] = '\0'; - remove_control_chars(memo); - strncpy(auth_dat[auth_num].memo, memo, 255); - - for(j = 0; j < ACCOUNT_REG2_NUM; j++) { - p += n; - if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { - if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { - j--; - continue; - } else - break; - } - str[31] = '\0'; - remove_control_chars(str); - strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); - auth_dat[auth_num].account_reg2[j].value = v; - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM(account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) { - if (account_id > END_ACCOUNT_NUM) { - continue; - } - userid[23] = '\0'; - remove_control_chars(userid); - for(j = 0; j < auth_num; j++) { - if (auth_dat[j].account_id == account_id) { - break; - } else if (strcmp(auth_dat[j].userid, userid) == 0) { - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) { - auth_max += 256; - auth_dat = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); - } - - memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy(auth_dat[auth_num].userid, userid, 24); - - pass[23] = '\0'; - remove_control_chars(pass); - strncpy(auth_dat[auth_num].pass, pass, 24); - - lastlogin[23] = '\0'; - remove_control_chars(lastlogin); - strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); - - if (i >= 6) { - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - } else - auth_dat[auth_num].logincount = 0; - - if (i >= 7) { - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - } else - auth_dat[auth_num].state = 0; - - strncpy(auth_dat[auth_num].email, "a@a.com", 40); - strncpy(auth_dat[auth_num].error_message, "-", 20); - auth_dat[auth_num].ban_until_time = 0; - auth_dat[auth_num].connect_until_time = 0; - strncpy(auth_dat[auth_num].last_ip, "-", 16); - strncpy(auth_dat[auth_num].memo, "-", 255); - - for(j = 0; j < ACCOUNT_REG2_NUM; j++) { - p += n; - if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { - if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { - j--; - continue; - } else - break; - } - str[31] = '\0'; - remove_control_chars(str); - strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); - auth_dat[auth_num].account_reg2[j].value = v; - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM(account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - } else { - i = 0; - if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && - i > 0 && account_id > account_id_count) - account_id_count = account_id; - } - } - fclose(fp); - - if (auth_num == 0) { - sprintf(line, "No account found in %s.", account_filename); - } else { - if (auth_num == 1) { - sprintf(line, "1 account read in %s,", account_filename); - } else { - sprintf(line, "%d accounts read in %s,", auth_num, account_filename); - } - if (GM_count == 0) { - sprintf(str, "%s of which is no GM account and", line); - } else if (GM_count == 1) { - sprintf(str, "%s of which is 1 GM account and", line); - } else { - sprintf(str, "%s of which is %d GM accounts and", line, GM_count); - } - if (server_count == 0) { - sprintf(line, "%s no server account ('S').", str); - } else if (server_count == 1) { - sprintf(line, "%s 1 server account ('S').", str); - } else { - sprintf(line, "%s %d server accounts ('S').", str, server_count); - } - } - - return 0; -} - -/*--------------------------------------------------------------------------------*/ - - -void -mmo_check_dumpworthy(struct mmo_charstatus *p) -{ - int i; - namefrob(p->name); - - for (i = 0; i < auth_num; i++) - if (auth_dat[i].account_id == p->account_id) { - p->sex = auth_dat[i].sex; - break; - } - - if (p->partner_id) { - if (mode != MODE_MARRIED) - return; - - if (p->partner_id < p->char_id) { - char_leaf_t *partner = find(list, p->partner_id); - if (!partner) - fprintf(stderr, "Char %d (%s)'s partner (%d) no longer available\n", - p->char_id, - p->name, - p->partner_id); - else - printf ("%d\t%d\t%s\t%s\t%d\t%s\t%s\t%d--%d,%d\n", - p->base_level + partner->level, - p->base_exp + partner->exp, - p->name, p->sex? "M" : "F", p->base_level, - partner->name, partner->sex? "M" : "F", partner->level); - } else { - insert(&list, p); - } - } else if (p->sex == (mode - 1)) - printf("%d\t%d\t%s\n", p->base_level, p->base_exp, p->name); - else if (mode == MODE_SINGLES_A) - printf("%d\t%d\t%s\t%s\n", p->base_level, p->base_exp, p->name, p->sex? "M" : "F"); -} - -int mmo_char_dump() -{ - char line[965536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp,*ofp; - - ifp=stdin; - while(fgets(line,65535,ifp)){ - memset(&char_dat,0,sizeof(struct mmo_charstatus)); - ret=mmo_char_fromstr(line,&char_dat); - if(ret){ - mmo_check_dumpworthy(&char_dat); - } - } - return 0; -} - - -int init(char *mode_s) -{ - if (!strcmp(mode_s, "-c")) - mode = MODE_MARRIED; - else if (!strcmp(mode_s, "-f")) - mode = MODE_SINGLES_F; - else if (!strcmp(mode_s, "-m")) - mode = MODE_SINGLES_M; - else if (!strcmp(mode_s, "-s")) - mode = MODE_SINGLES_A; - else { - fprintf(stderr, "Unknown mode `%s'\n", mode_s); - return 1; - } - return 0; -} - -int main(int argc,char *argv[]) -{ - mmo_auth_init(); - - if(argc < 2) { - printf("Usage: %s <mode>\n", argv[0]); - printf("Where <mode> is one of -c (couples), -s (singles), -f (female singles), -m (male singles)\n", argv[0]); - exit(0); - } - if (init(argv[1])) - return 1; - - mmo_char_dump(); - - return 0; -} diff --git a/src/tool/marriage-info.cpp b/src/tool/marriage-info.cpp new file mode 100644 index 0000000..3552a9b --- /dev/null +++ b/src/tool/marriage-info.cpp @@ -0,0 +1,482 @@ +/* To build: +gcc -O2 -m32 -Isrc/common -Isrc/char -Isrc/map -Isrc/login -o marriage-info \ +marriage-info.c src/common/socket.o src/common/timer.o src/common/db.o \ +src/common/lock.o src/common/malloc.o src/char/int_guild.o \ +src/char/int_party.o src/char/int_storage.o src/char/inter.o +*/ + +#include <stdio.h> +#include <stdlib.h> +#include "../login/login.hpp" +#include "../common/mmo.hpp" +// Yes, this is intentional +#include "../char/char.cpp" + +int mode; +#define MODE_MARRIED 0 +#define MODE_SINGLES_F 1 +#define MODE_SINGLES_M 2 +#define MODE_SINGLES_A 3 + +#define CHUNK_SIZE 1024 + +/* chunked list to represent leaves */ +typedef struct { + int char_id; + int exp; + char name[24]; + unsigned char level; + unsigned char sex; +} char_leaf_t; + +/* Chunks are in descending order, but chars are in ascending order */ +typedef struct chunklist_node { + int size; + char_leaf_t chars[CHUNK_SIZE]; + struct chunklist_node *next; +} chunklist_node_t; + +chunklist_node_t *list = NULL; + +static void +insert(chunklist_node_t **listp, struct mmo_charstatus *p) +{ + chunklist_node_t *chunk = *listp; + char_leaf_t *l; + + if ((chunk && chunk->size == CHUNK_SIZE) + || (!chunk)) { + chunk = malloc(sizeof(chunklist_node_t)); + chunk->size = 0; + chunk->next = *listp; + *listp = chunk; + } + + l = &chunk->chars[chunk->size++]; + + l->char_id = p->char_id; + l->level = p->base_level; + l->exp = p->base_exp; + l->sex = p->sex; + strcpy(l->name, p->name); +} + +static int +compare_leaf(const void *l, const void *r) +{ + return ((char_leaf_t *)l)->char_id - ((char_leaf_t *)r)->char_id; +} + +static char_leaf_t * +find(chunklist_node_t *list, int char_id) +{ + if (!list) + return NULL; /* fatal error */ + else { + int start = list->chars[0].char_id; + int stop = list->chars[list->size - 1].char_id; + + if (char_id >= start && char_id <= stop) { /* in this chunk */ + char_leaf_t key; + key.char_id = char_id; + return (char_leaf_t *) + bsearch(&key, &list->chars, list->size, sizeof(char_leaf_t), + compare_leaf); + } + else find(list->next, char_id); + } +} + + +void /* replace blanks with percent signs */ +namefrob(char *n) +{ + while (*n++) /* can't start with a blank */ + if (*n == ' ') + *n = '%'; +} + +/* +-------------------------------------------------------------------------------- + Copied and pasted due to lacking modularity +*/ + +char account_filename[1024] = "save/account.txt"; +int account_id_count = START_ACCOUNT_NUM; + +struct auth_dat { + int account_id, sex; + char userid[24], pass[24], lastlogin[24]; + int logincount; + int state; + char email[40]; + char error_message[20]; + time_t ban_until_time; + time_t connect_until_time; + char last_ip[16]; + char memo[255]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat; + +int auth_num = 0, auth_max = 0; + + +/* + Reading of the accounts database +*/ +int mmo_auth_init(void) { + FILE *fp; + int account_id, logincount, state, n, i, j, v; + char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; + time_t ban_until_time; + time_t connect_until_time; + char str[2048]; + int GM_count = 0; + int server_count = 0; + + auth_dat = calloc(sizeof(struct auth_dat) * 256, 1); + auth_max = 256; + + fp = fopen(account_filename, "r"); + if (fp == NULL) { + printf("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", account_filename); + return 0; + } + + while(fgets(line, sizeof(line)-1, fp) != NULL) { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + p = line; + + if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n)) == 13 && line[n] == '\t') || + ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) { + n = n + 1; + + if (account_id > END_ACCOUNT_NUM) { + continue; + } + userid[23] = '\0'; + remove_control_chars(userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + break; + } else if (strcmp(auth_dat[j].userid, userid) == 0) { + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) { + auth_max += 256; + auth_dat = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars(pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars(lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + + if (e_mail_check(email) == 0) { + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + } else { + remove_control_chars(email); + strncpy(auth_dat[auth_num].email, email, 40); + } + + error_message[19] = '\0'; + remove_control_chars(error_message); + if (error_message[0] == '\0' || state != 7) { + strncpy(auth_dat[auth_num].error_message, "-", 20); + } else { + strncpy(auth_dat[auth_num].error_message, error_message, 20); + } + + if (i == 13) + auth_dat[auth_num].ban_until_time = ban_until_time; + else + auth_dat[auth_num].ban_until_time = 0; + + auth_dat[auth_num].connect_until_time = connect_until_time; + + last_ip[15] = '\0'; + remove_control_chars(last_ip); + strncpy(auth_dat[auth_num].last_ip, last_ip, 16); + + memo[254] = '\0'; + remove_control_chars(memo); + strncpy(auth_dat[auth_num].memo, memo, 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { + if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars(str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) { + if (account_id > END_ACCOUNT_NUM) { + continue; + } + userid[23] = '\0'; + remove_control_chars(userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + break; + } else if (strcmp(auth_dat[j].userid, userid) == 0) { + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) { + auth_max += 256; + auth_dat = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars(pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars(lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (i >= 6) { + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + } else + auth_dat[auth_num].logincount = 0; + + if (i >= 7) { + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + } else + auth_dat[auth_num].state = 0; + + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + strncpy(auth_dat[auth_num].error_message, "-", 20); + auth_dat[auth_num].ban_until_time = 0; + auth_dat[auth_num].connect_until_time = 0; + strncpy(auth_dat[auth_num].last_ip, "-", 16); + strncpy(auth_dat[auth_num].memo, "-", 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { + if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars(str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + } else { + i = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && + i > 0 && account_id > account_id_count) + account_id_count = account_id; + } + } + fclose(fp); + + if (auth_num == 0) { + sprintf(line, "No account found in %s.", account_filename); + } else { + if (auth_num == 1) { + sprintf(line, "1 account read in %s,", account_filename); + } else { + sprintf(line, "%d accounts read in %s,", auth_num, account_filename); + } + if (GM_count == 0) { + sprintf(str, "%s of which is no GM account and", line); + } else if (GM_count == 1) { + sprintf(str, "%s of which is 1 GM account and", line); + } else { + sprintf(str, "%s of which is %d GM accounts and", line, GM_count); + } + if (server_count == 0) { + sprintf(line, "%s no server account ('S').", str); + } else if (server_count == 1) { + sprintf(line, "%s 1 server account ('S').", str); + } else { + sprintf(line, "%s %d server accounts ('S').", str, server_count); + } + } + + return 0; +} + +/*--------------------------------------------------------------------------------*/ + + +void +mmo_check_dumpworthy(struct mmo_charstatus *p) +{ + int i; + namefrob(p->name); + + for (i = 0; i < auth_num; i++) + if (auth_dat[i].account_id == p->account_id) { + p->sex = auth_dat[i].sex; + break; + } + + if (p->partner_id) { + if (mode != MODE_MARRIED) + return; + + if (p->partner_id < p->char_id) { + char_leaf_t *partner = find(list, p->partner_id); + if (!partner) + fprintf(stderr, "Char %d (%s)'s partner (%d) no longer available\n", + p->char_id, + p->name, + p->partner_id); + else + printf ("%d\t%d\t%s\t%s\t%d\t%s\t%s\t%d--%d,%d\n", + p->base_level + partner->level, + p->base_exp + partner->exp, + p->name, p->sex? "M" : "F", p->base_level, + partner->name, partner->sex? "M" : "F", partner->level); + } else { + insert(&list, p); + } + } else if (p->sex == (mode - 1)) + printf("%d\t%d\t%s\n", p->base_level, p->base_exp, p->name); + else if (mode == MODE_SINGLES_A) + printf("%d\t%d\t%s\t%s\n", p->base_level, p->base_exp, p->name, p->sex? "M" : "F"); +} + +int mmo_char_dump() +{ + char line[965536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp,*ofp; + + ifp=stdin; + while(fgets(line,65535,ifp)){ + memset(&char_dat,0,sizeof(struct mmo_charstatus)); + ret=mmo_char_fromstr(line,&char_dat); + if(ret){ + mmo_check_dumpworthy(&char_dat); + } + } + return 0; +} + + +int init(char *mode_s) +{ + if (!strcmp(mode_s, "-c")) + mode = MODE_MARRIED; + else if (!strcmp(mode_s, "-f")) + mode = MODE_SINGLES_F; + else if (!strcmp(mode_s, "-m")) + mode = MODE_SINGLES_M; + else if (!strcmp(mode_s, "-s")) + mode = MODE_SINGLES_A; + else { + fprintf(stderr, "Unknown mode `%s'\n", mode_s); + return 1; + } + return 0; +} + +int main(int argc,char *argv[]) +{ + mmo_auth_init(); + + if(argc < 2) { + printf("Usage: %s <mode>\n", argv[0]); + printf("Where <mode> is one of -c (couples), -s (singles), -f (female singles), -m (male singles)\n", argv[0]); + exit(0); + } + if (init(argv[1])) + return 1; + + mmo_char_dump(); + + return 0; +} diff --git a/src/tool/moneycount/athena_text.cpp b/src/tool/moneycount/athena_text.cpp index 59269fa..8cf5457 100644 --- a/src/tool/moneycount/athena_text.cpp +++ b/src/tool/moneycount/athena_text.cpp @@ -1,10 +1,10 @@ -#include "athena_text.h" +#include "athena_text.hpp" #include <stdlib.h> #include <stdio.h> #include <string.h> -#include "mmo.h" +#include "mmo.hpp" //------------------------------------------------------------------------- // Function to set the character from the line (at read of characters file) diff --git a/src/tool/moneycount/athena_text.h b/src/tool/moneycount/athena_text.h deleted file mode 100644 index 705c979..0000000 --- a/src/tool/moneycount/athena_text.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef ATHENA_TEXT_H -#define ATHENA_TEXT_H -#include "mmo.h" - -int mmo_char_fromstr (char *str, struct mmo_charstatus *p); - -int accreg_fromstr (char *str, struct accreg *reg); - -#endif diff --git a/src/tool/moneycount/athena_text.hpp b/src/tool/moneycount/athena_text.hpp new file mode 100644 index 0000000..e4b7025 --- /dev/null +++ b/src/tool/moneycount/athena_text.hpp @@ -0,0 +1,9 @@ +#ifndef ATHENA_TEXT_HPP +#define ATHENA_TEXT_HPP +#include "mmo.hpp" + +int mmo_char_fromstr (char *str, struct mmo_charstatus *p); + +int accreg_fromstr (char *str, struct accreg *reg); + +#endif diff --git a/src/tool/moneycount/main.cpp b/src/tool/moneycount/main.cpp index d15f223..2c3d56c 100644 --- a/src/tool/moneycount/main.cpp +++ b/src/tool/moneycount/main.cpp @@ -7,8 +7,8 @@ #include <algorithm> #include <cmath> -#include "mmo.h" -#include "athena_text.h" +#include "mmo.hpp" +#include "athena_text.hpp" #include "inf.hpp" #define ATHENA_FILE "save/athena.txt" diff --git a/src/tool/moneycount/mmo.h b/src/tool/moneycount/mmo.h deleted file mode 100644 index bd62b49..0000000 --- a/src/tool/moneycount/mmo.h +++ /dev/null @@ -1,309 +0,0 @@ -// $Id: mmo.h,v 1.3 2004/09/25 20:12:25 PoW Exp $ -// Original : mmo.h 2003/03/14 12:07:02 Rev.1.7 - -#ifndef _MMO_H_ -#define _MMO_H_ - -#include <time.h> - -#define FIFOSIZE_SERVERLINK 256*1024 - -// set to 0 to not check IP of player between each server. -// set to another value if you want to check (1) -#define CMP_AUTHFIFO_IP 1 - -#define CMP_AUTHFIFO_LOGIN2 1 - -#define MAX_MAP_PER_SERVER 512 -#define MAX_INVENTORY 100 -#define MAX_AMOUNT 30000 -#define MAX_ZENY 1000000000 // 1G zeny -#define MAX_CART 100 -#define MAX_SKILL 450 -#define GLOBAL_REG_NUM 96 -#define ACCOUNT_REG_NUM 16 -#define ACCOUNT_REG2_NUM 16 -#define DEFAULT_WALK_SPEED 150 -#define MIN_WALK_SPEED 0 -#define MAX_WALK_SPEED 1000 -#define MAX_STORAGE 300 -#define MAX_GUILD_STORAGE 1000 -#define MAX_PARTY 12 -#define MAX_GUILD 120 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW] -#define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] -#define MAX_GUILDEXPLUSION 32 -#define MAX_GUILDALLIANCE 16 -#define MAX_GUILDSKILL 8 -#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] -#define MAX_GUILDLEVEL 50 - -#define MIN_HAIR_STYLE battle_config.min_hair_style -#define MAX_HAIR_STYLE battle_config.max_hair_style -#define MIN_HAIR_COLOR battle_config.min_hair_color -#define MAX_HAIR_COLOR battle_config.max_hair_color -#define MIN_CLOTH_COLOR battle_config.min_cloth_color -#define MAX_CLOTH_COLOR battle_config.max_cloth_color - -// for produce -#define MIN_ATTRIBUTE 0 -#define MAX_ATTRIBUTE 4 -#define ATTRIBUTE_NORMAL 0 -#define MIN_STAR 0 -#define MAX_STAR 3 - -#define MIN_PORTAL_MEMO 0 -#define MAX_PORTAL_MEMO 2 - -#define MAX_STATUS_TYPE 5 - -#define CHAR_CONF_NAME "conf/char_athena.conf" - -struct account -{ - int account_id; - char name[50]; - char password[50]; - char lastlogin[50]; - char sex; - int num_logins; - int state; - char email[50]; - char error_message[50]; - long valitidy_time; - char last_ip[50]; - char memo[50]; - long ban_time; -}; - - -struct item -{ - int id; - short nameid; - short amount; - unsigned short equip; - char identify; - char refine; - char attribute; - short card[4]; - short broken; -}; - -struct point -{ - char map[24]; - short x, y; -}; - -struct skill -{ - unsigned short id, lv, flags; -}; - -struct global_reg -{ - char str[32]; - int value; -}; - -struct accreg -{ - int account_id, reg_num; - struct global_reg reg[ACCOUNT_REG_NUM]; -}; - -struct mmo_charstatus -{ - int char_id; - int account_id; - int partner_id; - - int base_exp, job_exp, zeny; - - short classb; - short status_point, skill_point; - int hp, max_hp, sp, max_sp; - short option, karma, manner; - short hair, hair_color, clothes_color; - int party_id, guild_id; - - short weapon, shield; - short head_top, head_mid, head_bottom; - - char name[24]; - unsigned char base_level, job_level; - short str, agi, vit, int_, dex, luk; - unsigned char char_num, sex; - - unsigned long mapip; - unsigned int mapport; - - struct point last_point, save_point, memo_point[10]; - struct item inventory[MAX_INVENTORY], cart[MAX_CART]; - struct skill skill[MAX_SKILL]; - int global_reg_num; - struct global_reg global_reg[GLOBAL_REG_NUM]; - int account_reg_num; - struct global_reg account_reg[ACCOUNT_REG_NUM]; - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -}; - -struct storage -{ - int dirty; - int account_id; - short storage_status; - short storage_amount; - struct item storage_[MAX_STORAGE]; -}; - -struct guild_storage -{ - int dirty; - int guild_id; - short storage_status; - short storage_amount; - struct item storage_[MAX_GUILD_STORAGE]; -}; - -struct map_session_data; - -struct gm_account -{ - int account_id; - int level; -}; - -struct party_member -{ - int account_id; - char name[24], map[24]; - int leader, online, lv; - struct map_session_data *sd; -}; - -struct party -{ - int party_id; - char name[24]; - int exp; - int item; - struct party_member member[MAX_PARTY]; -}; - -struct guild_member -{ - int account_id, char_id; - short hair, hair_color, gender, classb, lv; - int exp, exp_payper; - short online, position; - int rsv1, rsv2; - char name[24]; - struct map_session_data *sd; -}; - -struct guild_position -{ - char name[24]; - int mode; - int exp_mode; -}; - -struct guild_alliance -{ - int opposition; - int guild_id; - char name[24]; -}; - -struct guild_explusion -{ - char name[24]; - char mes[40]; - char acc[40]; - int account_id; - int rsv1, rsv2, rsv3; -}; - -struct guild_skill -{ - int id, lv; -}; - -struct guild -{ - int guild_id; - short guild_lv, connect_member, max_member, average_lv; - int exp, next_exp, skill_point, castle_id; - char name[24], master[24]; - struct guild_member member[MAX_GUILD]; - struct guild_position position[MAX_GUILDPOSITION]; - char mes1[60], mes2[120]; - int emblem_len, emblem_id; - char emblem_data[2048]; - struct guild_alliance alliance[MAX_GUILDALLIANCE]; - struct guild_explusion explusion[MAX_GUILDEXPLUSION]; - struct guild_skill skill[MAX_GUILDSKILL]; -}; - -struct guild_castle -{ - int castle_id; - char map_name[24]; - char castle_name[24]; - char castle_event[24]; - int guild_id; - int economy; - int defense; - int triggerE; - int triggerD; - int nextTime; - int payTime; - int createTime; - int visibleC; - int visibleG0; - int visibleG1; - int visibleG2; - int visibleG3; - int visibleG4; - int visibleG5; - int visibleG6; - int visibleG7; - int Ghp0; // added Guardian HP [Valaris] - int Ghp1; - int Ghp2; - int Ghp3; - int Ghp4; - int Ghp5; - int Ghp6; - int Ghp7; - int GID0; - int GID1; - int GID2; - int GID3; - int GID4; - int GID5; - int GID6; - int GID7; // end addition [Valaris] -}; -struct square -{ - int val1[5]; - int val2[5]; -}; - -enum -{ - GBI_EXP = 1, // ?M???h??EXP - GBI_GUILDLV = 2, // ?M???h??Lv - GBI_SKILLPOINT = 3, // ?M???h?~X?L???|?C???g - GBI_SKILLLV = 4, // ?M???h?X?L??Lv - - GMI_POSITION = 0, // ?????o?[???E??X - GMI_EXP = 1, // ?????o?[??EXP - -}; - -#endif // _MMO_H_ - diff --git a/src/tool/moneycount/mmo.hpp b/src/tool/moneycount/mmo.hpp new file mode 100644 index 0000000..beb29c5 --- /dev/null +++ b/src/tool/moneycount/mmo.hpp @@ -0,0 +1,309 @@ +// $Id: mmo.h,v 1.3 2004/09/25 20:12:25 PoW Exp $ +// Original : mmo.h 2003/03/14 12:07:02 Rev.1.7 + +#ifndef MMO_HPP +#define MMO_HPP + +#include <time.h> + +#define FIFOSIZE_SERVERLINK 256*1024 + +// set to 0 to not check IP of player between each server. +// set to another value if you want to check (1) +#define CMP_AUTHFIFO_IP 1 + +#define CMP_AUTHFIFO_LOGIN2 1 + +#define MAX_MAP_PER_SERVER 512 +#define MAX_INVENTORY 100 +#define MAX_AMOUNT 30000 +#define MAX_ZENY 1000000000 // 1G zeny +#define MAX_CART 100 +#define MAX_SKILL 450 +#define GLOBAL_REG_NUM 96 +#define ACCOUNT_REG_NUM 16 +#define ACCOUNT_REG2_NUM 16 +#define DEFAULT_WALK_SPEED 150 +#define MIN_WALK_SPEED 0 +#define MAX_WALK_SPEED 1000 +#define MAX_STORAGE 300 +#define MAX_GUILD_STORAGE 1000 +#define MAX_PARTY 12 +#define MAX_GUILD 120 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW] +#define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] +#define MAX_GUILDEXPLUSION 32 +#define MAX_GUILDALLIANCE 16 +#define MAX_GUILDSKILL 8 +#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] +#define MAX_GUILDLEVEL 50 + +#define MIN_HAIR_STYLE battle_config.min_hair_style +#define MAX_HAIR_STYLE battle_config.max_hair_style +#define MIN_HAIR_COLOR battle_config.min_hair_color +#define MAX_HAIR_COLOR battle_config.max_hair_color +#define MIN_CLOTH_COLOR battle_config.min_cloth_color +#define MAX_CLOTH_COLOR battle_config.max_cloth_color + +// for produce +#define MIN_ATTRIBUTE 0 +#define MAX_ATTRIBUTE 4 +#define ATTRIBUTE_NORMAL 0 +#define MIN_STAR 0 +#define MAX_STAR 3 + +#define MIN_PORTAL_MEMO 0 +#define MAX_PORTAL_MEMO 2 + +#define MAX_STATUS_TYPE 5 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +struct account +{ + int account_id; + char name[50]; + char password[50]; + char lastlogin[50]; + char sex; + int num_logins; + int state; + char email[50]; + char error_message[50]; + long valitidy_time; + char last_ip[50]; + char memo[50]; + long ban_time; +}; + + +struct item +{ + int id; + short nameid; + short amount; + unsigned short equip; + char identify; + char refine; + char attribute; + short card[4]; + short broken; +}; + +struct point +{ + char map[24]; + short x, y; +}; + +struct skill +{ + unsigned short id, lv, flags; +}; + +struct global_reg +{ + char str[32]; + int value; +}; + +struct accreg +{ + int account_id, reg_num; + struct global_reg reg[ACCOUNT_REG_NUM]; +}; + +struct mmo_charstatus +{ + int char_id; + int account_id; + int partner_id; + + int base_exp, job_exp, zeny; + + short classb; + short status_point, skill_point; + int hp, max_hp, sp, max_sp; + short option, karma, manner; + short hair, hair_color, clothes_color; + int party_id, guild_id; + + short weapon, shield; + short head_top, head_mid, head_bottom; + + char name[24]; + unsigned char base_level, job_level; + short str, agi, vit, int_, dex, luk; + unsigned char char_num, sex; + + unsigned long mapip; + unsigned int mapport; + + struct point last_point, save_point, memo_point[10]; + struct item inventory[MAX_INVENTORY], cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; + int account_reg_num; + struct global_reg account_reg[ACCOUNT_REG_NUM]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +}; + +struct storage +{ + int dirty; + int account_id; + short storage_status; + short storage_amount; + struct item storage_[MAX_STORAGE]; +}; + +struct guild_storage +{ + int dirty; + int guild_id; + short storage_status; + short storage_amount; + struct item storage_[MAX_GUILD_STORAGE]; +}; + +struct map_session_data; + +struct gm_account +{ + int account_id; + int level; +}; + +struct party_member +{ + int account_id; + char name[24], map[24]; + int leader, online, lv; + struct map_session_data *sd; +}; + +struct party +{ + int party_id; + char name[24]; + int exp; + int item; + struct party_member member[MAX_PARTY]; +}; + +struct guild_member +{ + int account_id, char_id; + short hair, hair_color, gender, classb, lv; + int exp, exp_payper; + short online, position; + int rsv1, rsv2; + char name[24]; + struct map_session_data *sd; +}; + +struct guild_position +{ + char name[24]; + int mode; + int exp_mode; +}; + +struct guild_alliance +{ + int opposition; + int guild_id; + char name[24]; +}; + +struct guild_explusion +{ + char name[24]; + char mes[40]; + char acc[40]; + int account_id; + int rsv1, rsv2, rsv3; +}; + +struct guild_skill +{ + int id, lv; +}; + +struct guild +{ + int guild_id; + short guild_lv, connect_member, max_member, average_lv; + int exp, next_exp, skill_point, castle_id; + char name[24], master[24]; + struct guild_member member[MAX_GUILD]; + struct guild_position position[MAX_GUILDPOSITION]; + char mes1[60], mes2[120]; + int emblem_len, emblem_id; + char emblem_data[2048]; + struct guild_alliance alliance[MAX_GUILDALLIANCE]; + struct guild_explusion explusion[MAX_GUILDEXPLUSION]; + struct guild_skill skill[MAX_GUILDSKILL]; +}; + +struct guild_castle +{ + int castle_id; + char map_name[24]; + char castle_name[24]; + char castle_event[24]; + int guild_id; + int economy; + int defense; + int triggerE; + int triggerD; + int nextTime; + int payTime; + int createTime; + int visibleC; + int visibleG0; + int visibleG1; + int visibleG2; + int visibleG3; + int visibleG4; + int visibleG5; + int visibleG6; + int visibleG7; + int Ghp0; // added Guardian HP [Valaris] + int Ghp1; + int Ghp2; + int Ghp3; + int Ghp4; + int Ghp5; + int Ghp6; + int Ghp7; + int GID0; + int GID1; + int GID2; + int GID3; + int GID4; + int GID5; + int GID6; + int GID7; // end addition [Valaris] +}; +struct square +{ + int val1[5]; + int val2[5]; +}; + +enum +{ + GBI_EXP = 1, // ?M???h??EXP + GBI_GUILDLV = 2, // ?M???h??Lv + GBI_SKILLPOINT = 3, // ?M???h?~X?L???|?C???g + GBI_SKILLLV = 4, // ?M???h?X?L??Lv + + GMI_POSITION = 0, // ?????o?[???E??X + GMI_EXP = 1, // ?????o?[??EXP + +}; + +#endif // MMO_HPP + diff --git a/src/tool/skillfrob.c b/src/tool/skillfrob.c deleted file mode 100644 index 44855ac..0000000 --- a/src/tool/skillfrob.c +++ /dev/null @@ -1,80 +0,0 @@ -// Compile with -// gcc -m32 -Wall -Wno-pointer-sign -fno-strict-aliasing -I src/char -I src/common src/tool/skillfrob.c -o skillfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o - -#include <stdio.h> -#include <stdlib.h> -#include "../common/mmo.h" -#include "../char/char.c" - -unsigned char skills[MAX_SKILL]; - -void transform_char (struct mmo_charstatus *p) -{ - int i; - - for (i = 0; i < MAX_SKILL; i++) - { - if (skills[(*p).skill[i].id]) - { - (*p).skill[i].lv = 0; - (*p).skill[i].flags = 0; - } - } -} - -int mmo_char_convert () -{ - char line[965536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp, *ofp; - - ifp = stdin; - ofp = stdout; - while (fgets (line, 65535, ifp)) - { - memset (&char_dat, 0, sizeof (struct mmo_charstatus)); - ret = mmo_char_fromstr (line, &char_dat); - if (ret) - { - transform_char (&char_dat); - mmo_char_tostr (line, &char_dat); - fprintf (ofp, "%s\n", line); - } - } - fcloseall (); - return 0; -} - -int init (int count, char **translates) -{ - int i, skill; - - memset (skills, 0, sizeof (skills)); - - for (i = 0; i < count; i++) - { - skill = atoi (translates[i]); - if (skill > 0) - { - skills[skill] = 1; - } - } - - return 0; -} - -int main (int argc, char *argv[]) -{ - if (argc < 2) - { - printf ("Usage: %s skillid1 skillid2 ...\n", argv[0]); - exit (0); - } - if (init (argc - 1, argv + 1)) - return 1; - - mmo_char_convert (); - - return 0; -} diff --git a/src/tool/skillfrob.cpp b/src/tool/skillfrob.cpp new file mode 100644 index 0000000..901f765 --- /dev/null +++ b/src/tool/skillfrob.cpp @@ -0,0 +1,80 @@ +// Compile with +// gcc -m32 -Wall -Wno-pointer-sign -fno-strict-aliasing -I src/char -I src/common src/tool/skillfrob.c -o skillfrob src/common/timer.o src/common/malloc.o src/common/socket.o src/common/lock.o src/common/db.o src/char/int_storage.o src/char/inter.o src/char/int_party.o src/char/int_guild.o + +#include <stdio.h> +#include <stdlib.h> +#include "../common/mmo.hpp" +#include "../char/char.cpp" + +unsigned char skills[MAX_SKILL]; + +void transform_char (struct mmo_charstatus *p) +{ + int i; + + for (i = 0; i < MAX_SKILL; i++) + { + if (skills[(*p).skill[i].id]) + { + (*p).skill[i].lv = 0; + (*p).skill[i].flags = 0; + } + } +} + +int mmo_char_convert () +{ + char line[965536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp, *ofp; + + ifp = stdin; + ofp = stdout; + while (fgets (line, 65535, ifp)) + { + memset (&char_dat, 0, sizeof (struct mmo_charstatus)); + ret = mmo_char_fromstr (line, &char_dat); + if (ret) + { + transform_char (&char_dat); + mmo_char_tostr (line, &char_dat); + fprintf (ofp, "%s\n", line); + } + } + fcloseall (); + return 0; +} + +int init (int count, char **translates) +{ + int i, skill; + + memset (skills, 0, sizeof (skills)); + + for (i = 0; i < count; i++) + { + skill = atoi (translates[i]); + if (skill > 0) + { + skills[skill] = 1; + } + } + + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc < 2) + { + printf ("Usage: %s skillid1 skillid2 ...\n", argv[0]); + exit (0); + } + if (init (argc - 1, argv + 1)) + return 1; + + mmo_char_convert (); + + return 0; +} diff --git a/src/webserver/generate.c b/src/webserver/generate.c deleted file mode 100644 index 1fed224..0000000 --- a/src/webserver/generate.c +++ /dev/null @@ -1,35 +0,0 @@ - -void generate_page (char password[25], int sock_in, char *query, char *ip) -{ - char *page = get_param (query, 0); - char *ppass = get_param (query, "password"); - - if ((ppass == 0) || (strcmp (password, ppass) != 0)) - { - web_send (sock_in, html_header ("Enter your password")); - web_send (sock_in, - "<H1>NOT LOGGED IN!</H1><form action=\"/\" method=\"GET\">\n"); - web_send (sock_in, - "Enter your password:<br>\n<input type=\"text\" name=\"password\">\n"); - web_send (sock_in, "<input type=\"submit\" value=\"Login\">\n"); - } - else - { - - //To make this simple, we will have a bunch of if statements - //that then shoot out data off into functions. - - //The 'index' - if (strcmp (page, "/") == 0) - generate_notdone (sock_in, query, ip); - - //About page: - if (strcmp (page, "/about.html") == 0) - generate_about (sock_in, query, ip); - - //Test page: - if (strcmp (page, "/testing/") == 0) - generate_sample (sock_in, query, ip); - - } -} diff --git a/src/webserver/generate.cpp b/src/webserver/generate.cpp new file mode 100644 index 0000000..1fed224 --- /dev/null +++ b/src/webserver/generate.cpp @@ -0,0 +1,35 @@ + +void generate_page (char password[25], int sock_in, char *query, char *ip) +{ + char *page = get_param (query, 0); + char *ppass = get_param (query, "password"); + + if ((ppass == 0) || (strcmp (password, ppass) != 0)) + { + web_send (sock_in, html_header ("Enter your password")); + web_send (sock_in, + "<H1>NOT LOGGED IN!</H1><form action=\"/\" method=\"GET\">\n"); + web_send (sock_in, + "Enter your password:<br>\n<input type=\"text\" name=\"password\">\n"); + web_send (sock_in, "<input type=\"submit\" value=\"Login\">\n"); + } + else + { + + //To make this simple, we will have a bunch of if statements + //that then shoot out data off into functions. + + //The 'index' + if (strcmp (page, "/") == 0) + generate_notdone (sock_in, query, ip); + + //About page: + if (strcmp (page, "/about.html") == 0) + generate_about (sock_in, query, ip); + + //Test page: + if (strcmp (page, "/testing/") == 0) + generate_sample (sock_in, query, ip); + + } +} diff --git a/src/webserver/htmlstyle.c b/src/webserver/htmlstyle.c deleted file mode 100644 index 3007a24..0000000 --- a/src/webserver/htmlstyle.c +++ /dev/null @@ -1,45 +0,0 @@ -char output[10000]; - -char *html_header (char *title) -{ - memset (output, 0x0, 10000); - char *text = - "<body text=\"#000000\" bgcolor=\"#939393\" link=\"#0033FF\">\n" - "<br><table width=\"92%\" cellspacing=\"1\" cellpadding=\"0\" border=\"0\"\n" - "align=\"center\" class=\"bordercolor\"><tbody><tr><td class=\"bordercolor\" width=\"100%\">\n" - "<table bgcolor=\"#ffffff\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">\n" - "<tbody><tr><td><table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#ffffff\">\n" - "<tbody><tr><img src=\"http://eathena.sourceforge.net/athena.jpg\" alt=\"Athena\">\n" - "<td bgcolor=\"#ffffff\"></td></tr></tbody></table></td></tr></tbody></table>\n" - "</td></tr><tr align=\"left\"><td class=\"bordercolor\"><table bgcolor=\"#c6c6c6\" width=\"100%\" cellspacing=\"0\"\n" - "cellpadding=\"0\" style=\"text-align: left; margin-right: auto; margin-left: 0px;\">\n"; - "<tbody><tr><td width=\"100%\" align=\"center\"><table border=\"0\" width=\"100%\" cellpadding=\"3\"\n" - "cellspacing=\"0\" bgcolor=\"#c6c6c6\" align=\"center\"><tbody><tr>" - "<td valign=\"middle\" bgcolor=\"#c6c6c6\" align=\"center\"><a href=\"/cgi-bin/forum/YaBB.cgi\">" - "<span style=\"text-decoration: underline;\"><span style=\"font-weight: bold;\">\n" - "To the Forum</span></span></a><br></td></tr></tbody></table></td></tr></tbody>\n" - "</table></td></tr><tr><td class=\"bordercolor\" align=\"center\">\n" - "<table bgcolor=\"#ffffff\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" align=\"center\">\n" - "<tbody><tr><td width=\"100%\" align=\"center\"><table border=\"0\" width=\"100%\" cellpadding=\"5\"\n" - "cellspacing=\"0\" bgcolor=\"#ffffff\" align=\"center\"><tbody><tr>\n" - "<td valign=\"middle\" bgcolor=\"#ffffff\" align=\"center\"><font size=\"2\" color=\"#6e94b7\">\n" - "<b>Athena</b> « Portal »</font></td></tr></tbody></table></td></tr></tbody>" - "</table></td></tr></tbody></table>\n"; - - sprintf (output, "<title>%s</title>\n%s\n", title, text); - - return output; -} - -char *html_start_form (char *location, char *action) -{ - memset (output, 0x0, 10000); - sprintf (output, "<form action=\"%s\" method=\"%s\">", location, action); - return output; - -} - -char *html_end_forum (void) -{ - return "</form>"; -} diff --git a/src/webserver/htmlstyle.cpp b/src/webserver/htmlstyle.cpp new file mode 100644 index 0000000..3007a24 --- /dev/null +++ b/src/webserver/htmlstyle.cpp @@ -0,0 +1,45 @@ +char output[10000]; + +char *html_header (char *title) +{ + memset (output, 0x0, 10000); + char *text = + "<body text=\"#000000\" bgcolor=\"#939393\" link=\"#0033FF\">\n" + "<br><table width=\"92%\" cellspacing=\"1\" cellpadding=\"0\" border=\"0\"\n" + "align=\"center\" class=\"bordercolor\"><tbody><tr><td class=\"bordercolor\" width=\"100%\">\n" + "<table bgcolor=\"#ffffff\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">\n" + "<tbody><tr><td><table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#ffffff\">\n" + "<tbody><tr><img src=\"http://eathena.sourceforge.net/athena.jpg\" alt=\"Athena\">\n" + "<td bgcolor=\"#ffffff\"></td></tr></tbody></table></td></tr></tbody></table>\n" + "</td></tr><tr align=\"left\"><td class=\"bordercolor\"><table bgcolor=\"#c6c6c6\" width=\"100%\" cellspacing=\"0\"\n" + "cellpadding=\"0\" style=\"text-align: left; margin-right: auto; margin-left: 0px;\">\n"; + "<tbody><tr><td width=\"100%\" align=\"center\"><table border=\"0\" width=\"100%\" cellpadding=\"3\"\n" + "cellspacing=\"0\" bgcolor=\"#c6c6c6\" align=\"center\"><tbody><tr>" + "<td valign=\"middle\" bgcolor=\"#c6c6c6\" align=\"center\"><a href=\"/cgi-bin/forum/YaBB.cgi\">" + "<span style=\"text-decoration: underline;\"><span style=\"font-weight: bold;\">\n" + "To the Forum</span></span></a><br></td></tr></tbody></table></td></tr></tbody>\n" + "</table></td></tr><tr><td class=\"bordercolor\" align=\"center\">\n" + "<table bgcolor=\"#ffffff\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" align=\"center\">\n" + "<tbody><tr><td width=\"100%\" align=\"center\"><table border=\"0\" width=\"100%\" cellpadding=\"5\"\n" + "cellspacing=\"0\" bgcolor=\"#ffffff\" align=\"center\"><tbody><tr>\n" + "<td valign=\"middle\" bgcolor=\"#ffffff\" align=\"center\"><font size=\"2\" color=\"#6e94b7\">\n" + "<b>Athena</b> « Portal »</font></td></tr></tbody></table></td></tr></tbody>" + "</table></td></tr></tbody></table>\n"; + + sprintf (output, "<title>%s</title>\n%s\n", title, text); + + return output; +} + +char *html_start_form (char *location, char *action) +{ + memset (output, 0x0, 10000); + sprintf (output, "<form action=\"%s\" method=\"%s\">", location, action); + return output; + +} + +char *html_end_forum (void) +{ + return "</form>"; +} diff --git a/src/webserver/logs.c b/src/webserver/logs.c deleted file mode 100644 index 67bf55e..0000000 --- a/src/webserver/logs.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <time.h> - -void log_visit (char *query, char *ip) -{ - time_t timer; - timer = time (NULL); - printf ("%s - \"%s\" - %s", ip, query, asctime (localtime (&timer))); -} diff --git a/src/webserver/logs.cpp b/src/webserver/logs.cpp new file mode 100644 index 0000000..67bf55e --- /dev/null +++ b/src/webserver/logs.cpp @@ -0,0 +1,8 @@ +#include <time.h> + +void log_visit (char *query, char *ip) +{ + time_t timer; + timer = time (NULL); + printf ("%s - \"%s\" - %s", ip, query, asctime (localtime (&timer))); +} diff --git a/src/webserver/main.c b/src/webserver/main.c deleted file mode 100644 index 0e227d3..0000000 --- a/src/webserver/main.c +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************** - description - ------------------- - author : (C) 2004 by Michael J. Flickinger - email : mjflick@cpan.org - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/wait.h> -#include <signal.h> - -#define BLOG 10 - -char *header = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"; -char recvin[500], password[25]; -int s_port; - -void sigchld_handler (int s) -{ - while (wait (NULL) > 0); -} - -int main (int argc, char **argv) -{ - if (argc < 3) - { - printf ("eAthena Web Server\n"); - printf ("usage: %s [password] [port]\n", argv[0]); - exit (0); - } - - s_port = atoi (argv[2]); - - if ((s_port < 1) || (s_port > 65534)) - { - printf ("Error: The port you choose is not valid port.\n"); - exit (0); - } - - if (strlen (argv[1]) > 25) - { - printf ("Error: Your password is too long.\n"); - printf ("It must be shorter than 25 characters.\n"); - exit (0); - } - - memset (password, 0x0, 25); - memcpy (password, argv[1], strlen (argv[1])); - - int sockfd, new_fd; - struct sockaddr_in my_addr; - struct sockaddr_in their_addr; - int sin_size; - - struct sigaction sa; - - int yes = 1; - - if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) - { - perror ("Darn, this is broken."); - exit (0); - } - - if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == - -1) - { - perror ("Error... :-("); - } - - //Now we know we have a working socket. :-) - - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons (s_port); - my_addr.sin_addr.s_addr = INADDR_ANY; - memset (&(my_addr.sin_zero), '\0', 8); - - if (bind (sockfd, (struct sockaddr *) &my_addr, sizeof (struct sockaddr)) - == -1) - { - perror ("can not bind to this port"); - exit (0); - } - - if (listen (sockfd, BLOG) == -1) - { - perror ("can not listen on port"); - exit (0); - } - - sa.sa_handler = sigchld_handler; - - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction (SIGCHLD, &sa, NULL) == -1) - { - perror ("sigaction sucks"); - exit (0); - } - - printf ("The eAthena webserver is up and listening on port %i.\n", - s_port); - - while (1) - { - sin_size = sizeof (struct sockaddr_in); - new_fd = accept (sockfd, (struct sockaddr *) &their_addr, &sin_size); - - if (!fork ()) - { - close (sockfd); - memset (recvin, 0x0, 500); - recv (new_fd, recvin, 500, 0); - send (new_fd, header, strlen (header), 0); - generate_page (password, new_fd, get_query (recvin), - inet_ntoa (their_addr.sin_addr)); - log_visit (get_query (recvin), inet_ntoa (their_addr.sin_addr)); - - close (new_fd); - exit (0); - } - close (new_fd); - } - - return 0; -} diff --git a/src/webserver/main.cpp b/src/webserver/main.cpp new file mode 100644 index 0000000..0e227d3 --- /dev/null +++ b/src/webserver/main.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** + description + ------------------- + author : (C) 2004 by Michael J. Flickinger + email : mjflick@cpan.org + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/wait.h> +#include <signal.h> + +#define BLOG 10 + +char *header = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"; +char recvin[500], password[25]; +int s_port; + +void sigchld_handler (int s) +{ + while (wait (NULL) > 0); +} + +int main (int argc, char **argv) +{ + if (argc < 3) + { + printf ("eAthena Web Server\n"); + printf ("usage: %s [password] [port]\n", argv[0]); + exit (0); + } + + s_port = atoi (argv[2]); + + if ((s_port < 1) || (s_port > 65534)) + { + printf ("Error: The port you choose is not valid port.\n"); + exit (0); + } + + if (strlen (argv[1]) > 25) + { + printf ("Error: Your password is too long.\n"); + printf ("It must be shorter than 25 characters.\n"); + exit (0); + } + + memset (password, 0x0, 25); + memcpy (password, argv[1], strlen (argv[1])); + + int sockfd, new_fd; + struct sockaddr_in my_addr; + struct sockaddr_in their_addr; + int sin_size; + + struct sigaction sa; + + int yes = 1; + + if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + perror ("Darn, this is broken."); + exit (0); + } + + if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == + -1) + { + perror ("Error... :-("); + } + + //Now we know we have a working socket. :-) + + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons (s_port); + my_addr.sin_addr.s_addr = INADDR_ANY; + memset (&(my_addr.sin_zero), '\0', 8); + + if (bind (sockfd, (struct sockaddr *) &my_addr, sizeof (struct sockaddr)) + == -1) + { + perror ("can not bind to this port"); + exit (0); + } + + if (listen (sockfd, BLOG) == -1) + { + perror ("can not listen on port"); + exit (0); + } + + sa.sa_handler = sigchld_handler; + + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction (SIGCHLD, &sa, NULL) == -1) + { + perror ("sigaction sucks"); + exit (0); + } + + printf ("The eAthena webserver is up and listening on port %i.\n", + s_port); + + while (1) + { + sin_size = sizeof (struct sockaddr_in); + new_fd = accept (sockfd, (struct sockaddr *) &their_addr, &sin_size); + + if (!fork ()) + { + close (sockfd); + memset (recvin, 0x0, 500); + recv (new_fd, recvin, 500, 0); + send (new_fd, header, strlen (header), 0); + generate_page (password, new_fd, get_query (recvin), + inet_ntoa (their_addr.sin_addr)); + log_visit (get_query (recvin), inet_ntoa (their_addr.sin_addr)); + + close (new_fd); + exit (0); + } + close (new_fd); + } + + return 0; +} diff --git a/src/webserver/pages/about.c b/src/webserver/pages/about.c deleted file mode 100644 index b548da9..0000000 --- a/src/webserver/pages/about.c +++ /dev/null @@ -1,6 +0,0 @@ -void generate_about (int sock_in, char *query, char *ip) -{ -//printf("%s", html_header("About")); - web_send (sock_in, html_header ("About")); - web_send (sock_in, "<center>eAthena Web Server!</center>\n"); -} diff --git a/src/webserver/pages/about.cpp b/src/webserver/pages/about.cpp new file mode 100644 index 0000000..b548da9 --- /dev/null +++ b/src/webserver/pages/about.cpp @@ -0,0 +1,6 @@ +void generate_about (int sock_in, char *query, char *ip) +{ +//printf("%s", html_header("About")); + web_send (sock_in, html_header ("About")); + web_send (sock_in, "<center>eAthena Web Server!</center>\n"); +} diff --git a/src/webserver/pages/notdone.c b/src/webserver/pages/notdone.c deleted file mode 100644 index 1fcd44b..0000000 --- a/src/webserver/pages/notdone.c +++ /dev/null @@ -1,6 +0,0 @@ -void generate_notdone (int sock_in, char *query, char *ip) -{ - web_send (sock_in, "<title>Not here!</title>\n"); - web_send (sock_in, - "<h2><center>This page/feature is not done yet.</center>\n</h2>"); -} diff --git a/src/webserver/pages/notdone.cpp b/src/webserver/pages/notdone.cpp new file mode 100644 index 0000000..1fcd44b --- /dev/null +++ b/src/webserver/pages/notdone.cpp @@ -0,0 +1,6 @@ +void generate_notdone (int sock_in, char *query, char *ip) +{ + web_send (sock_in, "<title>Not here!</title>\n"); + web_send (sock_in, + "<h2><center>This page/feature is not done yet.</center>\n</h2>"); +} diff --git a/src/webserver/pages/sample.c b/src/webserver/pages/sample.c deleted file mode 100644 index 6a33dc0..0000000 --- a/src/webserver/pages/sample.c +++ /dev/null @@ -1,22 +0,0 @@ - -void generate_sample (int sock_in, char *query, char *ip) -{ - - char *name = get_param (query, "name"); - - web_send (sock_in, "<title>SAMPLE</title>\n"); - - //If a name was not entered... - if (name == '\0') - { - web_send (sock_in, "<form action=\"/testing/\" method=\"GET\">\n"); - web_send (sock_in, "<input type=\"text\" name=\"name\">\n"); - web_send (sock_in, "<input type=\"submit\">\n"); - } - else - { - web_send (sock_in, "Your name is: "); - web_send (sock_in, get_param (query, "name")); - } - printf ("OK!\n"); -} diff --git a/src/webserver/pages/sample.cpp b/src/webserver/pages/sample.cpp new file mode 100644 index 0000000..6a33dc0 --- /dev/null +++ b/src/webserver/pages/sample.cpp @@ -0,0 +1,22 @@ + +void generate_sample (int sock_in, char *query, char *ip) +{ + + char *name = get_param (query, "name"); + + web_send (sock_in, "<title>SAMPLE</title>\n"); + + //If a name was not entered... + if (name == '\0') + { + web_send (sock_in, "<form action=\"/testing/\" method=\"GET\">\n"); + web_send (sock_in, "<input type=\"text\" name=\"name\">\n"); + web_send (sock_in, "<input type=\"submit\">\n"); + } + else + { + web_send (sock_in, "Your name is: "); + web_send (sock_in, get_param (query, "name")); + } + printf ("OK!\n"); +} diff --git a/src/webserver/parse.c b/src/webserver/parse.c deleted file mode 100644 index 66ef43b..0000000 --- a/src/webserver/parse.c +++ /dev/null @@ -1,132 +0,0 @@ -#include <stdlib.h> - -char filtered_query[2000]; -char rdata[500]; -char param_n[500]; -char param_d[500]; - -char *get_query (char *inquery) -{ - memset (filtered_query, 0x0, 2000); - sscanf (inquery, "GET %s %[$]", filtered_query); - return (filtered_query); -} - -void web_send (int sockin, char *in_data) -{ - send (sockin, in_data, strlen (in_data), 0); -} - -//THIS IS BAD CODE BE CAREFULL WITH IT! -//Watch out for buffer overflow... -//When using please make sure to check the string size. - -//Also note: -//I take no pride in this code, it is a really bad way of doing this... -char *get_param (char in_string[500], char swhat[500]) -{ - int i = 0; - int marker, iswitch, pint, dint; - char flux[500]; - memset (flux, 0x0, 500); - - //Get the path of out "page" - if (swhat == 0) - { - //while i is not equal to array size - while (i != 500) - { - //if there is a question mark, halt! - if (in_string[i] == '?') - { - i = 499; - } - else - rdata[i] = in_string[i]; - - i++; - } - return rdata; - } - else //so, we want a param... - { - //calculate where param begins - while (i != 500) - { - if (in_string[i] == '?') - { - marker = i + 1; - i = 499; - } - i++; - } - - i = 0; - - //keep morons from trying to crash this - if ((marker > 500) || (marker < 1)) - marker = 500; - - while (marker != 500) - { - if ((in_string[marker] != '&') && (in_string[marker] != '\0')) - { - flux[i] = in_string[marker]; - i++; - } - else - { - - //we have a param, now we must dig through it - - //clear temp vars - memset (param_n, 0x0, 500); - memset (param_d, 0x0, 500); - iswitch = 0; - pint = 0; - dint = 0; - i = 0; - - //split result into param_n and param_d - while (i != 500) - { - if ((flux[i] != '=') && (flux[i] != '\0')) - { - if (iswitch == 0) - { - param_n[pint] = flux[i]; - pint++; - } - else - { - param_d[dint] = flux[i]; - dint++; - } - } - else - { - iswitch = 1; - } - if (flux[i] == '\0') - i = 499; - - i++; - } - - if (strcmp (param_n, swhat) == 0) - { - return param_d; - } - - i = 0; - } - - if (in_string[marker] == '\0') - { - marker = 499; - } - marker++; - } - return 0; - } -} diff --git a/src/webserver/parse.cpp b/src/webserver/parse.cpp new file mode 100644 index 0000000..66ef43b --- /dev/null +++ b/src/webserver/parse.cpp @@ -0,0 +1,132 @@ +#include <stdlib.h> + +char filtered_query[2000]; +char rdata[500]; +char param_n[500]; +char param_d[500]; + +char *get_query (char *inquery) +{ + memset (filtered_query, 0x0, 2000); + sscanf (inquery, "GET %s %[$]", filtered_query); + return (filtered_query); +} + +void web_send (int sockin, char *in_data) +{ + send (sockin, in_data, strlen (in_data), 0); +} + +//THIS IS BAD CODE BE CAREFULL WITH IT! +//Watch out for buffer overflow... +//When using please make sure to check the string size. + +//Also note: +//I take no pride in this code, it is a really bad way of doing this... +char *get_param (char in_string[500], char swhat[500]) +{ + int i = 0; + int marker, iswitch, pint, dint; + char flux[500]; + memset (flux, 0x0, 500); + + //Get the path of out "page" + if (swhat == 0) + { + //while i is not equal to array size + while (i != 500) + { + //if there is a question mark, halt! + if (in_string[i] == '?') + { + i = 499; + } + else + rdata[i] = in_string[i]; + + i++; + } + return rdata; + } + else //so, we want a param... + { + //calculate where param begins + while (i != 500) + { + if (in_string[i] == '?') + { + marker = i + 1; + i = 499; + } + i++; + } + + i = 0; + + //keep morons from trying to crash this + if ((marker > 500) || (marker < 1)) + marker = 500; + + while (marker != 500) + { + if ((in_string[marker] != '&') && (in_string[marker] != '\0')) + { + flux[i] = in_string[marker]; + i++; + } + else + { + + //we have a param, now we must dig through it + + //clear temp vars + memset (param_n, 0x0, 500); + memset (param_d, 0x0, 500); + iswitch = 0; + pint = 0; + dint = 0; + i = 0; + + //split result into param_n and param_d + while (i != 500) + { + if ((flux[i] != '=') && (flux[i] != '\0')) + { + if (iswitch == 0) + { + param_n[pint] = flux[i]; + pint++; + } + else + { + param_d[dint] = flux[i]; + dint++; + } + } + else + { + iswitch = 1; + } + if (flux[i] == '\0') + i = 499; + + i++; + } + + if (strcmp (param_n, swhat) == 0) + { + return param_d; + } + + i = 0; + } + + if (in_string[marker] == '\0') + { + marker = 499; + } + marker++; + } + return 0; + } +} -- cgit v1.2.3-70-g09d2