summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/char/Makefile30
-rw-r--r--src/char/char.c4161
-rw-r--r--src/char/char.h45
-rw-r--r--src/char/int_guild.c1530
-rw-r--r--src/char/int_guild.h19
-rw-r--r--src/char/int_party.c654
-rw-r--r--src/char/int_party.h18
-rw-r--r--src/char/int_pet.c380
-rw-r--r--src/char/int_pet.h16
-rw-r--r--src/char/int_status.c187
-rw-r--r--src/char/int_status.h24
-rw-r--r--src/char/int_storage.c476
-rw-r--r--src/char/int_storage.h19
-rw-r--r--src/char/inter.c656
-rw-r--r--src/char/inter.h28
-rw-r--r--src/char_sql/Makefile27
-rw-r--r--src/char_sql/char.c4307
-rw-r--r--src/char_sql/char.h103
-rw-r--r--src/char_sql/int_guild.c1914
-rw-r--r--src/char_sql/int_guild.h18
-rw-r--r--src/char_sql/int_party.c865
-rw-r--r--src/char_sql/int_party.h14
-rw-r--r--src/char_sql/int_pet.c351
-rw-r--r--src/char_sql/int_pet.h16
-rw-r--r--src/char_sql/int_storage.c364
-rw-r--r--src/char_sql/int_storage.h17
-rw-r--r--src/char_sql/inter.c789
-rw-r--r--src/char_sql/inter.h58
-rw-r--r--src/char_sql/itemdb.c229
-rw-r--r--src/char_sql/itemdb.h38
-rw-r--r--src/char_sql/make.sh10
-rw-r--r--src/common/Makefile56
-rw-r--r--src/common/cbasetypes.h253
-rw-r--r--src/common/core.c269
-rw-r--r--src/common/core.h30
-rw-r--r--src/common/db.c2344
-rw-r--r--src/common/db.h734
-rw-r--r--src/common/ers.c532
-rw-r--r--src/common/ers.h193
-rw-r--r--src/common/graph.c318
-rw-r--r--src/common/graph.h27
-rw-r--r--src/common/grfio.c1146
-rw-r--r--src/common/grfio.h21
-rw-r--r--src/common/lock.c71
-rw-r--r--src/common/lock.h11
-rw-r--r--src/common/malloc.c715
-rw-r--r--src/common/malloc.h153
-rw-r--r--src/common/mapindex.c130
-rw-r--r--src/common/mapindex.h37
-rw-r--r--src/common/mmo.h403
-rw-r--r--src/common/nullpo.c94
-rw-r--r--src/common/nullpo.h237
-rw-r--r--src/common/plugin.h40
-rw-r--r--src/common/plugins.c367
-rw-r--r--src/common/plugins.h61
-rw-r--r--src/common/showmsg.c219
-rw-r--r--src/common/showmsg.h88
-rw-r--r--src/common/socket.c1390
-rw-r--r--src/common/socket.h189
-rw-r--r--src/common/strlib.c133
-rw-r--r--src/common/strlib.h17
-rw-r--r--src/common/timer.c429
-rw-r--r--src/common/timer.h60
-rw-r--r--src/common/utils.c384
-rw-r--r--src/common/utils.h52
-rw-r--r--src/common/version.h30
-rw-r--r--src/ladmin/Makefile17
-rw-r--r--src/ladmin/ladmin.c4410
-rw-r--r--src/ladmin/ladmin.h13
-rw-r--r--src/ladmin/md5calc.c239
-rw-r--r--src/ladmin/md5calc.h10
-rw-r--r--src/login/Makefile25
-rw-r--r--src/login/login.c4153
-rw-r--r--src/login/login.h44
-rw-r--r--src/login/md5calc.c236
-rw-r--r--src/login/md5calc.h7
-rw-r--r--src/login_sql/Makefile22
-rw-r--r--src/login_sql/login.c2256
-rw-r--r--src/login_sql/login.h57
-rw-r--r--src/login_sql/make.sh6
-rw-r--r--src/login_sql/md5calc.c239
-rw-r--r--src/login_sql/md5calc.h10
-rw-r--r--src/map/Makefile99
-rw-r--r--src/map/Makefile.win3299
-rw-r--r--src/map/atcommand.c10039
-rw-r--r--src/map/atcommand.h319
-rw-r--r--src/map/battle.c4412
-rw-r--r--src/map/battle.h428
-rw-r--r--src/map/charcommand.c1794
-rw-r--r--src/map/charcommand.h68
-rw-r--r--src/map/charsave.c516
-rw-r--r--src/map/charsave.h16
-rw-r--r--src/map/chat.c371
-rw-r--r--src/map/chat.h22
-rw-r--r--src/map/chrif.c1572
-rw-r--r--src/map/chrif.h54
-rw-r--r--src/map/clif.c12146
-rw-r--r--src/map/clif.h343
-rw-r--r--src/map/date.c72
-rw-r--r--src/map/date.h17
-rw-r--r--src/map/guild.c1976
-rw-r--r--src/map/guild.h96
-rw-r--r--src/map/intif.c1401
-rw-r--r--src/map/intif.h61
-rw-r--r--src/map/itemdb.c1103
-rw-r--r--src/map/itemdb.h106
-rw-r--r--src/map/log.c956
-rw-r--r--src/map/log.h47
-rw-r--r--src/map/mail.c361
-rw-r--r--src/map/mail.h12
-rw-r--r--src/map/map.c3952
-rw-r--r--src/map/map.h1409
-rw-r--r--src/map/mercenary.c11
-rw-r--r--src/map/mercenary.h11
-rw-r--r--src/map/mob.c5020
-rw-r--r--src/map/mob.h173
-rw-r--r--src/map/npc.c2826
-rw-r--r--src/map/npc.h71
-rw-r--r--src/map/npc_chat.c519
-rw-r--r--src/map/party.c742
-rw-r--r--src/map/party.h47
-rw-r--r--src/map/path.c483
-rw-r--r--src/map/pc.c8305
-rw-r--r--src/map/pc.h251
-rw-r--r--src/map/pcre.h258
-rw-r--r--src/map/pet.c1973
-rw-r--r--src/map/pet.h73
-rw-r--r--src/map/script.c10736
-rw-r--r--src/map/script.h73
-rw-r--r--src/map/skill.c12348
-rw-r--r--src/map/skill.h879
-rw-r--r--src/map/status.c6038
-rw-r--r--src/map/status.h529
-rw-r--r--src/map/storage.c693
-rw-r--r--src/map/storage.h45
-rw-r--r--src/map/trade.c569
-rw-r--r--src/map/trade.h15
-rw-r--r--src/map/vending.c261
-rw-r--r--src/map/vending.h14
-rw-r--r--src/mysql/config-win.h434
-rw-r--r--src/mysql/m_ctype.h478
-rw-r--r--src/mysql/my_alloc.h52
-rw-r--r--src/mysql/my_dbug.h101
-rw-r--r--src/mysql/my_global.h1285
-rw-r--r--src/mysql/my_list.h46
-rw-r--r--src/mysql/my_pthread.h711
-rw-r--r--src/mysql/my_sys.h904
-rw-r--r--src/mysql/mysql-5.0.160
-rw-r--r--src/mysql/mysql.h839
-rw-r--r--src/mysql/mysql_com.h444
-rw-r--r--src/mysql/mysql_time.h56
-rw-r--r--src/mysql/mysql_version.h29
-rw-r--r--src/mysql/raid.h159
-rw-r--r--src/mysql/typelib.h34
-rw-r--r--src/plugins/Makefile46
-rw-r--r--src/plugins/gui.c101
-rw-r--r--src/plugins/gui.txt15
-rw-r--r--src/plugins/httpd.c751
-rw-r--r--src/plugins/httpd.h107
-rw-r--r--src/plugins/httpd.txt20
-rw-r--r--src/plugins/pid.c54
-rw-r--r--src/plugins/sample.c77
-rw-r--r--src/plugins/sig.c211
-rw-r--r--src/plugins/upnp.txt31
-rw-r--r--src/tool/Makefile10
-rw-r--r--src/tool/adduser.c99
-rw-r--r--src/tool/convert.c299
-rw-r--r--src/txt-converter/Makefile15
-rw-r--r--src/txt-converter/char-converter.c1360
-rw-r--r--src/txt-converter/login-converter.c227
-rw-r--r--src/webserver/Makefile20
-rw-r--r--src/webserver/doc/API.txt50
-rw-r--r--src/webserver/doc/README11
-rw-r--r--src/webserver/generate.c38
-rw-r--r--src/webserver/htmlstyle.c51
-rw-r--r--src/webserver/logs.c8
-rw-r--r--src/webserver/main.c142
-rw-r--r--src/webserver/pages/about.c6
-rw-r--r--src/webserver/pages/notdone.c5
-rw-r--r--src/webserver/pages/sample.c24
-rw-r--r--src/webserver/parse.c135
-rw-r--r--src/zlib/Makefile21
-rw-r--r--src/zlib/crypt.h132
-rw-r--r--src/zlib/ioapi.c177
-rw-r--r--src/zlib/ioapi.h75
-rw-r--r--src/zlib/iowin32.c270
-rw-r--r--src/zlib/iowin32.h21
-rw-r--r--src/zlib/unzip.c1602
-rw-r--r--src/zlib/unzip.h354
-rw-r--r--src/zlib/zconf.h332
-rw-r--r--src/zlib/zlib-1.2.3bin0 -> 58 bytes
-rw-r--r--src/zlib/zlib.h1357
192 files changed, 150996 insertions, 0 deletions
diff --git a/src/char/Makefile b/src/char/Makefile
new file mode 100644
index 000000000..ff58f8ed5
--- /dev/null
+++ b/src/char/Makefile
@@ -0,0 +1,30 @@
+all txt: char-server
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/utils.o \
+ ../common/obj/strlib.o ../common/obj/graph.o ../common/obj/grfio.o \
+ ../common/obj/mapindex.o ../common/obj/ers.o ../zlib/unz.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h \
+ ../common/version.h ../common/db.h ../common/plugins.h ../common/lock.h \
+ ../common/malloc.h ../common/showmsg.h ../common/utils.h ../common/strlib.h \
+ ../common/graph.h ../common/grfio.h ../common/mapindex.h
+
+%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+char-server: char.o inter.o int_party.o int_guild.o int_status.o int_storage.o int_pet.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $> $(LIB_S)
+
+clean:
+ rm -f *.o ../../char-server
+
+# DO NOT DELETE
+
+char.o: char.c char.h inter.h int_pet.h $(COMMON_H) ../common/version.h
+inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h char.h $(COMMON_H)
+int_party.o: int_party.c int_party.h inter.h char.h $(COMMON_H)
+int_guild.o: int_guild.c int_guild.h int_storage.h inter.h char.h $(COMMON_H)
+int_storage.o: int_storage.c int_storage.h int_guild.h inter.h char.h $(COMMON_H)
+int_status.o: int_status.c int_status.h char.h $(COMMON_H)
+int_pet.o: int_pet.c int_pet.h inter.h char.h $(COMMON_H)
diff --git a/src/char/char.c b/src/char/char.c
new file mode 100644
index 000000000..ef77102b9
--- /dev/null
+++ b/src/char/char.c
@@ -0,0 +1,4161 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <winsock.h>
+typedef long in_addr_t;
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../common/strlib.h"
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/mmo.h"
+#include "../common/db.h"
+#include "../common/version.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+
+#include "char.h"
+#include "inter.h"
+#include "int_pet.h"
+#include "int_guild.h"
+#include "int_party.h"
+#include "int_storage.h"
+#ifdef ENABLE_SC_SAVING
+#include "int_status.h"
+#endif
+
+struct mmo_map_server server[MAX_MAP_SERVERS];
+int server_fd[MAX_MAP_SERVERS];
+
+int login_fd, char_fd;
+char userid[24];
+char passwd[24];
+char server_name[20];
+char wisp_server_name[NAME_LENGTH] = "Server";
+int login_ip_set_ = 0;
+char login_ip_str[16];
+in_addr_t login_ip;
+int login_port = 6900;
+int char_ip_set_ = 0;
+char char_ip_str[16];
+int bind_ip_set_ = 0;
+char bind_ip_str[16];
+in_addr_t char_ip;
+int char_port = 6121;
+int char_maintenance;
+int char_new;
+int char_new_display;
+int email_creation = 0; // disabled by default
+char char_txt[1024]="save/athena.txt";
+char backup_txt[1024]="save/backup.txt"; //By zanetheinsane
+char friends_txt[1024]="save/friends.txt"; // davidsiaw
+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";
+char db_path[1024]="db";
+//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]
+//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
+#define TRIM_CHARS "\032\t\n "
+char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor]
+
+int log_char = 1; // loggin char or not [devil]
+int log_inter = 1; // loggin inter or not [devil]
+
+struct char_session_data{
+ int account_id, login_id1, login_id2, sex;
+ 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;
+ 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)
+static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex]
+
+int char_id_count = START_CHAR_NUM;
+struct character_data {
+ struct mmo_charstatus status;
+ int global_num;
+ struct global_reg global[GLOBAL_REG_NUM];
+} *char_dat;
+
+int char_num, char_max;
+int max_connect_user = 0;
+int gm_allow_level = 99;
+int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+int save_log = 1;
+int start_zeny = 500;
+int start_weapon = 1201;
+int start_armor = 2301;
+
+// Initial position (it's possible to set it in conf file)
+struct point start_point = { 0, 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
+
+//These are used to aid the map server in identifying valid clients. [Skotlex]
+static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID;
+
+struct online_char_data {
+ int account_id;
+ int char_id;
+ short server;
+ unsigned waiting_disconnect :1;
+};
+
+struct dbt *online_char_db; //Holds all online characters.
+
+time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds)
+
+int console = 0;
+
+//------------------------------
+// Writing function of logs file
+//------------------------------
+int char_log(char *fmt, ...) {
+ if(log_char)
+ {
+ FILE *logfp;
+ va_list ap;
+ time_t raw_time;
+ 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, RETCODE);
+ else {
+ time(&raw_time);
+ strftime(tmpstr, 24, "%d-%m-%Y %H:%M:%S", localtime(&raw_time));
+ sprintf(tmpstr + 19, ": %s", 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) {
+ 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 (stricmp(char_dat[i].status.name, character_name) == 0) {
+ // Strict comparison (if found, we finish the function immediatly with correct value)
+ if (strcmp(char_dat[i].status.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].status.name;
+
+ return unknown_char_name;
+}
+
+static void * create_online_char_data(DBKey key, va_list args) {
+ struct online_char_data* character;
+ character = aCalloc(1, sizeof(struct online_char_data));
+ character->account_id = key.i;
+ character->char_id = -1;
+ character->server = -1;
+ return character;
+}
+
+static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data);
+
+//-------------------------------------------------
+// Set Character online/offline [Wizputer]
+//-------------------------------------------------
+
+void set_char_online(int map_id, int char_id, int account_id) {
+ struct online_char_data* character;
+
+ if ( char_id != 99 && (max_account_id < account_id || max_char_id < char_id))
+ { //Notify map-server of the new max IDs [Skotlex]
+ if (account_id > max_account_id)
+ max_account_id = account_id;
+ if (char_id > max_char_id)
+ max_char_id = char_id;
+ mapif_send_maxid(max_account_id, max_char_id);
+ }
+ character = idb_ensure(online_char_db, account_id, create_online_char_data);
+ if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id)
+ {
+ ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n",
+ character->account_id, character->char_id, character->server, map_id, account_id, char_id);
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ }
+ character->waiting_disconnect = 0;
+ character->char_id = (char_id==99)?-1:char_id;
+ character->server = (char_id==99)?-1:map_id;
+
+ if (login_fd <= 0 || session[login_fd]->eof)
+ return;
+ WFIFOHEAD(login_fd, 6);
+ WFIFOW(login_fd,0) = 0x272b;
+ WFIFOL(login_fd,2) = account_id;
+ WFIFOSET(login_fd,6);
+
+ //printf ("set online\n");
+}
+void set_char_offline(int char_id, int account_id) {
+ struct online_char_data* character;
+
+ if ((character = idb_get(online_char_db, account_id)) != NULL)
+ { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex]
+ character->char_id = -1;
+ character->server = -1;
+ character->waiting_disconnect = 0;
+ }
+ if (login_fd <= 0 || session[login_fd]->eof)
+ return;
+ WFIFOHEAD(login_fd, 6);
+ WFIFOW(login_fd,0) = 0x272c;
+ WFIFOL(login_fd,2) = account_id;
+ WFIFOSET(login_fd,6);
+
+}
+
+static int char_db_setoffline(DBKey key, void* data, va_list ap) {
+ struct online_char_data* character = (struct online_char_data*)data;
+ int server = va_arg(ap, int);
+ if (server == -1) {
+ character->char_id = -1;
+ character->server = -1;
+ character->waiting_disconnect = 0;
+ } else if (character->server == server)
+ character->server = -2; //In some map server that we aren't connected to.
+ return 0;
+}
+
+void set_all_offline(void) {
+ online_char_db->foreach(online_char_db,char_db_setoffline,-1);
+ if (login_fd <= 0 || session[login_fd]->eof)
+ return;
+ WFIFOHEAD(login_fd, 6);
+ WFIFOW(login_fd,0) = 0x272c;
+ WFIFOL(login_fd,2) = 99;
+ WFIFOSET(login_fd,6);
+
+ //printf ("set all offline\n");
+}
+
+/*---------------------------------------------------
+ Make a data line for friends list
+ --------------------------------------------------*/
+
+int mmo_friends_list_data_str(char *str, struct mmo_charstatus *p) {
+ int i;
+ char *str_p = str;
+ str_p += sprintf(str_p, "%d", p->char_id);
+
+ for (i=0;i<MAX_FRIENDS;i++){
+ if (p->friends[i].account_id > 0 && p->friends[i].char_id > 0 && p->friends[i].name[0])
+ str_p += sprintf(str_p, ",%d,%d,%s", p->friends[i].account_id, p->friends[i].char_id, p->friends[i].name);
+ else
+ str_p += sprintf(str_p,",,,");
+ }
+
+ str_p += '\0';
+
+ return 0;
+}
+
+//-------------------------------------------------
+// Function to create the character line (for save)
+//-------------------------------------------------
+int mmo_char_tostr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int reg_num) {
+ int i,j;
+ 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) {
+ p->last_point.map = mapindex_name2id(MAP_PRONTERA);
+ 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,%d,%d,%d,%d\t",
+ 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,
+ mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, //
+ mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y,
+ p->partner_id,p->father,p->mother,p->child,p->fame);
+ for(i = 0; i < 10; i++)
+ if (p->memo_point[i].map) {
+ str_p += sprintf(str_p, "%s,%d,%d", mapindex_id2name(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",
+ 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);
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p,",%d",p->inventory[i].card[j]);
+ str_p += sprintf(str_p," ");
+ }
+ *(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",
+ 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);
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p,",%d",p->cart[i].card[j]);
+ str_p += sprintf(str_p," ");
+ }
+ *(str_p++) = '\t';
+
+ for(i = 0; i < MAX_SKILL; i++)
+ if (p->skill[i].id && p->skill[i].flag != 1) {
+ str_p += sprintf(str_p, "%d,%d ", p->skill[i].id, (p->skill[i].flag == 0) ? p->skill[i].lv : p->skill[i].flag-2);
+ }
+ *(str_p++) = '\t';
+
+ for(i = 0; i < reg_num; i++)
+ if (reg[i].str[0])
+ str_p += sprintf(str_p, "%s,%s ", reg[i].str, 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, struct global_reg *reg, int *reg_num) {
+ char tmp_str[3][128]; //To avoid deleting chars with too long names.
+ int tmp_int[256];
+ int set, next, len, i, j;
+
+ // initilialise character
+ memset(p, '\0', sizeof(struct mmo_charstatus));
+
+ // If it's not char structure of version 1488 and after
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0],
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36],
+ tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39],
+ &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next)) != 47)
+ {
+ tmp_int[43] = 0;
+ // If it's not char structure of version 1363 and after
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39],
+ &tmp_int[40], &tmp_int[41], &tmp_int[42], &next)) != 46)
+ {
+ tmp_int[40] = 0; // father
+ tmp_int[41] = 0; // mother
+ tmp_int[42] = 0; // child
+ // If it's not char structure of version 1008 and before 1363
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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 {
+ set += 3;
+ //printf("char: new char data ver.3\n");
+ }
+ // Char structture of version 1363+
+ } else {
+ set++;
+ //printf("char: new char data ver.4\n");
+ }
+ // Char structure of version 1488+
+ } else {
+ //printf("char: new char data ver.5\n");
+ }
+ if (set != 47)
+ return 0;
+
+ memcpy(p->name, tmp_str[0], NAME_LENGTH-1); //Overflow protection [Skotlex]
+ 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 = 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.map = mapindex_name2id(tmp_str[1]);
+ p->last_point.x = tmp_int[35];
+ p->last_point.y = tmp_int[36];
+ p->save_point.map = mapindex_name2id(tmp_str[2]);
+ p->save_point.x = tmp_int[37];
+ p->save_point.y = tmp_int[38];
+ p->partner_id = tmp_int[39];
+ p->father = tmp_int[40];
+ p->mother = tmp_int[41];
+ p->child = tmp_int[42];
+ p->fame = tmp_int[43];
+
+ // Some checks
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.char_id == p->char_id) {
+ ShowError(CL_RED"mmmo_auth_init: a character has an identical id to another.\n");
+ ShowError(" character id #%d -> new character not readed.\n", p->char_id);
+ ShowError(" Character saved in log file."CL_RESET"\n");
+ return -1;
+ } else if (strcmp(char_dat[i].status.name, p->name) == 0) {
+ ShowError(CL_RED"mmmo_auth_init: a character name already exists.\n");
+ ShowError(" character name '%s' -> new character not read.\n", p->name);
+ ShowError(" Character saved in log file."CL_RESET"\n");
+ return -2;
+ }
+ }
+
+ if (strcmpi(wisp_server_name, p->name) == 0) {
+ ShowWarning("mmo_auth_init: ******WARNING: character name has wisp server name.\n");
+ ShowWarning(" Character name '%s' = wisp server name '%s'.\n", p->name, wisp_server_name);
+ ShowWarning(" 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'." RETCODE,
+ p->name, wisp_server_name);
+ }
+
+ if (str[next] == '\n' || str[next] == '\r')
+ return 1; // V‹Kƒf[ƒ^
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t'; i++) {
+ if (sscanf(str+next, "%[^,],%d,%d%n", tmp_str[0], &tmp_int[0], &tmp_int[1], &len) != 3)
+ return -3;
+ p->memo_point[i].map = mapindex_name2id(tmp_str[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++) {
+ if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8)
+ {
+ 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];
+
+ for(j = 0; j < MAX_SLOTS && tmp_str[0] && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++)
+ p->inventory[i].card[j] = tmp_int[0];
+
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ } else // invalid structure
+ return -4;
+ }
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t'; i++) {
+ if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8)
+ {
+ 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];
+
+ for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++)
+ p->cart[i].card[j] = tmp_int[0];
+
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ } else // invalid structure
+ return -5;
+ }
+
+ 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];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_regŽÀ‘•ˆÈ‘O‚Ìathena.txtŒÝŠ·‚Ì‚½‚߈ꉞ'\n'ƒ`ƒFƒbƒN
+ if (sscanf(str + next, "%[^,],%[^ ] %n", reg[i].str, 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, ",%[^ ] %n", reg[i].value, &len) == 1)
+ i--;
+ else
+ return -7;
+ }
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+ *reg_num = i;
+
+ return 1;
+}
+//---------------------------------
+// Function to read friend list
+//---------------------------------
+
+int parse_friend_txt(struct mmo_charstatus *p)
+{
+ char line[1024], temp[1024];
+ int pos = 0, count = 0, next;
+ int i,len;
+ FILE *fp;
+
+ // Open the file and look for the ID
+ fp = fopen(friends_txt, "r");
+
+ if(fp == NULL)
+ return -1;
+
+ while(fgets(line, sizeof(line)-1, fp)) {
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%d%n",&i, &pos) < 1 || i != p->char_id)
+ continue; //Not this line...
+ //Read friends
+ len = strlen(line);
+ next = pos;
+ for (count = 0; next < len && count < MAX_FRIENDS; count++)
+ { //Read friends.
+ if (sscanf(line+next, ",%d,%d,%23[^,]%n",&p->friends[count].account_id,&p->friends[count].char_id, p->friends[count].name, &pos) < 3)
+ { //Invalid friend?
+ memset(&p->friends[count], 0, sizeof(p->friends[count]));
+ break;
+ }
+ next+=pos;
+ //What IF the name contains a comma? while the next field is not a
+ //number, we assume it belongs to the current name. [Skotlex]
+ //NOTE: Of course, this will fail if someone sets their name to something like
+ //Bob,2005 but... meh, it's the problem of parsing a text file (encasing it in "
+ //won't do as quotes are also valid name chars!)
+ while(next < len && sscanf(line+next, ",%23[^,]%n", temp, &len) > 0)
+ {
+ if (atoi(temp))
+ { //We read the next friend, just continue.
+ break;
+ } else { //Append the name.
+ next+=len;
+ if (strlen(p->friends[count].name) + strlen(temp) +1 < NAME_LENGTH)
+ {
+ strcat(p->friends[count].name, ",");
+ strcat(p->friends[count].name, temp);
+ }
+ }
+ } //End Guess Block
+ } //Friend's for.
+ break; //Finished reading.
+ }
+ /*
+ //Character names must not exceed the 23+\0 limit. [Skotlex]
+ sscanf(line, "%d,%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23s",&cid,
+ &temp[0],p->friend_name[0],
+ &temp[1],p->friend_name[1],
+ &temp[2],p->friend_name[2],
+ &temp[3],p->friend_name[3],
+ &temp[4],p->friend_name[4],
+ &temp[5],p->friend_name[5],
+ &temp[6],p->friend_name[6],
+ &temp[7],p->friend_name[7],
+ &temp[8],p->friend_name[8],
+ &temp[9],p->friend_name[9],
+ &temp[10],p->friend_name[10],
+ &temp[11],p->friend_name[11],
+ &temp[12],p->friend_name[12],
+ &temp[13],p->friend_name[13],
+ &temp[14],p->friend_name[14],
+ &temp[15],p->friend_name[15],
+ &temp[16],p->friend_name[16],
+ &temp[17],p->friend_name[17],
+ &temp[18],p->friend_name[18],
+ &temp[19],p->friend_name[19]);
+ if (cid == p->char_id)
+ break;
+ }
+ // No register of friends list
+ if (cid == 0) {
+ fclose(fp);
+ return 0;
+ }
+
+ // Fill in the list
+
+ for (i=0; i<MAX_FRIENDS; i++)
+ p->friend_id[i] = temp[i];
+*/
+ fclose(fp);
+ return count;
+}
+
+//---------------------------------
+// Function to read characters file
+//---------------------------------
+int mmo_char_init(void) {
+ char line[65536];
+ int ret, line_count;
+ FILE *fp;
+
+ char_max = 256;
+ char_dat = (struct character_data*)aCalloc(sizeof(struct character_data) * 256, 1);
+ if (!char_dat) {
+ ShowFatalError("out of memory: mmo_char_init (calloc of char_dat).\n");
+ exit(1);
+ }
+ char_num = 0;
+
+ fp = fopen(char_txt, "r");
+
+ if (fp == NULL) {
+ ShowError("Characters file not found: %s.\n", char_txt);
+ char_log("Characters file not found: %s." RETCODE, char_txt);
+ char_log("Id for the next created character: %d." RETCODE, 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;
+ char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max);
+ if (!char_dat) {
+ ShowFatalError("Out of memory: mmo_char_init (realloc of char_dat).\n");
+ char_log("Out of memory: mmo_char_init (realloc of char_dat)." RETCODE);
+ exit(1);
+ }
+ }
+
+ ret = mmo_char_fromstr(line, &char_dat[char_num].status, char_dat[char_num].global, &char_dat[char_num].global_num);
+
+ // Initialize friends list
+ parse_friend_txt(&char_dat[char_num].status); // Grab friends for the character
+
+ if (ret > 0) { // negative value or zero for errors
+ if (char_dat[char_num].status.char_id >= char_id_count)
+ char_id_count = char_dat[char_num].status.char_id + 1;
+ char_num++;
+ } else {
+ ShowError("mmo_char_init: in characters file, unable to read the line #%d.\n", line_count);
+ ShowError(" -> Character saved in log file.\n");
+ switch (ret) {
+ case -1:
+ char_log("Duplicate character id in the next character line (character not readed):" RETCODE);
+ break;
+ case -2:
+ char_log("Duplicate character name in the next character line (character not readed):" RETCODE);
+ break;
+ case -3:
+ char_log("Invalid memo point structure in the next character line (character not readed):" RETCODE);
+ break;
+ case -4:
+ char_log("Invalid inventory item structure in the next character line (character not readed):" RETCODE);
+ break;
+ case -5:
+ char_log("Invalid cart item structure in the next character line (character not readed):" RETCODE);
+ break;
+ case -6:
+ char_log("Invalid skill structure in the next character line (character not readed):" RETCODE);
+ break;
+ case -7:
+ char_log("Invalid register structure in the next character line (character not readed):" RETCODE);
+ 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):" RETCODE);
+ break;
+ }
+ char_log("%s", line);
+ }
+ }
+ fclose(fp);
+
+ if (char_num == 0) {
+ ShowNotice("mmo_char_init: No character found in %s.\n", char_txt);
+ char_log("mmo_char_init: No character found in %s." RETCODE, char_txt);
+ } else if (char_num == 1) {
+ ShowStatus("mmo_char_init: 1 character read in %s.\n", char_txt);
+ char_log("mmo_char_init: 1 character read in %s." RETCODE, char_txt);
+ } else {
+ ShowStatus("mmo_char_init: %d characters read in %s.\n", char_num, char_txt);
+ char_log("mmo_char_init: %d characters read in %s." RETCODE, char_num, char_txt);
+ }
+
+ char_log("Id for the next created character: %d." RETCODE, char_id_count);
+
+ return 0;
+}
+
+//---------------------------------------------------------
+// Function to save characters in files (speed up by [Yor])
+//---------------------------------------------------------
+void mmo_char_sync(void) {
+ char line[65536],f_line[1024];
+ int i, j, k;
+ int lock;
+ FILE *fp,*f_fp;
+ //int *id = (int *) aMalloc(sizeof(int) * char_num);
+ CREATE_BUFFER(id, int, 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].status.account_id < char_dat[id[j]].status.account_id) ||
+ // if same account id, we sort by slot.
+ (char_dat[i].status.account_id == char_dat[id[j]].status.account_id &&
+ char_dat[i].status.char_num < char_dat[id[j]].status.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) {
+ ShowWarning("Server can't not save characters.\n");
+ char_log("WARNING: Server can't not save characters." RETCODE);
+ } 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]].status, char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index
+ fprintf(fp, "%s" RETCODE, line);
+ }
+ fprintf(fp, "%d\t%%newid%%" RETCODE, 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) {
+ ShowWarning("Server can't not create backup of characters file.\n");
+ char_log("WARNING: Server can't not create backup of characters file." RETCODE);
+ //aFree(id); // free up the memory before leaving -.- [Ajarn]
+ DELETE_BUFFER(id);
+ 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]].status,char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index
+ fprintf(fp, "%s" RETCODE, line);
+ }
+ fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count);
+ lock_fclose(fp, backup_txt, &lock);
+ }
+
+ // Friends List data save (davidsiaw)
+ f_fp = lock_fopen(friends_txt, &lock);
+ for(i = 0; i < char_num; i++) {
+ mmo_friends_list_data_str(f_line, &char_dat[id[i]].status);
+ fprintf(f_fp, "%s" RETCODE, f_line);
+ }
+
+ lock_fclose(f_fp, friends_txt, &lock);
+
+ //aFree(id);
+ DELETE_BUFFER(id);
+
+ return;
+}
+
+//----------------------------------------------------
+// Function to save (in a periodic way) datas in files
+//----------------------------------------------------
+int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) {
+ if (save_log)
+ ShowInfo("Saving all files...\n");
+ mmo_char_sync();
+ inter_save();
+ return 0;
+}
+
+//-----------------------------------
+// Function to create a new character
+//-----------------------------------
+int make_new_char(int fd, unsigned char *dat) {
+ int i;
+ struct char_session_data *sd;
+ char name[NAME_LENGTH];
+
+ sd = (struct char_session_data*)session[fd]->session_data;
+
+ // remove control characters from the name
+ strncpy(name, dat, NAME_LENGTH);
+ name[NAME_LENGTH-1] = '\0'; //Trunc name to max possible value (23)
+
+ trim(name,TRIM_CHARS); //Trim character name. [Skotlex]
+
+ //check name != main chat nick [LuzZza]
+ if(strcmpi(name, main_chat_nick) == 0) {
+ char_log("Create char failed (%d): this nick (%s) reserved for mainchat messages." RETCODE,
+ sd->account_id, name);
+ return -1;
+ }
+
+ if (remove_control_chars((unsigned char *)name)) {
+ char_log("Make new char error (control char received in the name): (connection #%d, account: %d)." RETCODE,
+ fd, sd->account_id);
+ return -1;
+ }
+
+ // check lenght of character name
+ if (strlen(name) < 4) {
+ char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s')." RETCODE,
+ 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; i < NAME_LENGTH && name[i]; i++)
+ if (strchr(char_name_letters, name[i]) == NULL) {
+ char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE,
+ fd, sd->account_id, name, name[i]);
+ return -1;
+ }
+ } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
+ for (i = 0; i < NAME_LENGTH && name[i]; i++)
+ if (strchr(char_name_letters, name[i]) != NULL) {
+ char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE,
+ 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] >= 24 || // hair style
+ dat[31] >= 9) { // 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" RETCODE,
+ 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" RETCODE,
+ 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;
+ }
+ } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct
+
+ if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) {
+ if (log_char) {
+ char_log("Make new char error (invalid stat value): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE,
+ 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;
+ }
+ } // now when we have passed all stat checks
+
+ for(i = 0; i < char_num; i++) {
+ if ((name_ignoring_case != 0 && strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0) ||
+ (name_ignoring_case == 0 && strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0)) {
+ char_log("Make new char error (name already exists): (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." RETCODE,
+ fd, sd->account_id, dat[30], dat, char_dat[i].status.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].status.account_id == sd->account_id && char_dat[i].status.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: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE,
+ fd, sd->account_id, dat[30], dat, char_dat[i].status.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, name) == 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." RETCODE,
+ fd, sd->account_id, dat[30], name, char_dat[i].status.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;
+ char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max);
+ if (!char_dat) {
+ ShowFatalError("Out of memory: make_new_char (realloc of char_dat).\n");
+ char_log("Out of memory: make_new_char (realloc of char_dat)." RETCODE);
+ exit(1);
+ }
+ }
+
+ 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." RETCODE,
+ fd, sd->account_id, dat[30], 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]);
+
+ memset(&char_dat[i], 0, sizeof(struct character_data));
+
+ char_dat[i].status.char_id = char_id_count++;
+ char_dat[i].status.account_id = sd->account_id;
+ char_dat[i].status.char_num = dat[30];
+ strcpy(char_dat[i].status.name,name);
+ char_dat[i].status.class_ = 0;
+ char_dat[i].status.base_level = 1;
+ char_dat[i].status.job_level = 1;
+ char_dat[i].status.base_exp = 0;
+ char_dat[i].status.job_exp = 0;
+ char_dat[i].status.zeny = start_zeny;
+ char_dat[i].status.str = dat[24];
+ char_dat[i].status.agi = dat[25];
+ char_dat[i].status.vit = dat[26];
+ char_dat[i].status.int_ = dat[27];
+ char_dat[i].status.dex = dat[28];
+ char_dat[i].status.luk = dat[29];
+ char_dat[i].status.max_hp = 40 * (100 + char_dat[i].status.vit) / 100;
+ char_dat[i].status.max_sp = 11 * (100 + char_dat[i].status.int_) / 100;
+ char_dat[i].status.hp = char_dat[i].status.max_hp;
+ char_dat[i].status.sp = char_dat[i].status.max_sp;
+ char_dat[i].status.status_point = 0;
+ char_dat[i].status.skill_point = 0;
+ char_dat[i].status.option = 0;
+ char_dat[i].status.karma = 0;
+ char_dat[i].status.manner = 0;
+ char_dat[i].status.party_id = 0;
+ char_dat[i].status.guild_id = 0;
+ char_dat[i].status.hair = dat[33];
+ char_dat[i].status.hair_color = dat[31];
+ char_dat[i].status.clothes_color = 0;
+ char_dat[i].status.inventory[0].nameid = start_weapon; // Knife
+ char_dat[i].status.inventory[0].amount = 1;
+ char_dat[i].status.inventory[0].equip = 0x02;
+ char_dat[i].status.inventory[0].identify = 1;
+ char_dat[i].status.inventory[1].nameid = start_armor; // Cotton Shirt
+ char_dat[i].status.inventory[1].amount = 1;
+ char_dat[i].status.inventory[1].equip = 0x10;
+ char_dat[i].status.inventory[1].identify = 1;
+ char_dat[i].status.weapon = 1;
+ char_dat[i].status.shield = 0;
+ char_dat[i].status.head_top = 0;
+ char_dat[i].status.head_mid = 0;
+ char_dat[i].status.head_bottom = 0;
+ memcpy(&char_dat[i].status.last_point, &start_point, sizeof(start_point));
+ memcpy(&char_dat[i].status.save_point, &start_point, sizeof(start_point));
+ char_num++;
+
+ mmo_char_sync();
+ return i;
+}
+
+//----------------------------------------------------
+// This function return the name of the job (by [Yor])
+//----------------------------------------------------
+char * job_name(int class_) {
+ switch (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";
+}
+
+static int create_online_files_sub(DBKey key, void* data, va_list va)
+{
+ struct online_char_data *character;
+ int* players;
+ int *id;
+ int j,k,l;
+ character = (struct online_char_data*) data;
+ players = va_arg(va, int*);
+ id = va_arg(va, int*);
+
+ // check if map-server is online
+ if (character->server == -1 || character->char_id == -1) { //Character not currently online.
+ return -1;
+ }
+
+ j = character->server;
+ if (server_fd[j] < 0) {
+ server[j].users = 0;
+ if (kick_on_disconnect)
+ {
+ character->char_id = -1;
+ character->server = -1;
+ }
+ return -1;
+ }
+ // search position of character in char_dat and sort online characters.
+ for(j = 0; j < char_num; j++) {
+ if (char_dat[j].status.char_id != character->char_id)
+ continue;
+ id[*players] = j;
+ // use sorting option
+ switch (online_sorting_option) {
+ case 1: // by name (without case sensitive)
+ for(k = 0; k < *players; k++)
+ if (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0 ||
+ // if same name, we sort with case sensitive.
+ (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) == 0 &&
+ strcmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) {
+ for(l = *players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[*players]
+ break;
+ }
+ break;
+ case 2: // by zeny
+ for(k = 0; k < *players; k++)
+ if (char_dat[j].status.zeny < char_dat[id[k]].status.zeny ||
+ // if same number of zenys, we sort by name.
+ (char_dat[j].status.zeny == char_dat[id[k]].status.zeny &&
+ stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) {
+ for(l = *players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[*players]
+ break;
+ }
+ break;
+ case 3: // by base level
+ for(k = 0; k < *players; k++)
+ if (char_dat[j].status.base_level < char_dat[id[k]].status.base_level ||
+ // if same base level, we sort by base exp.
+ (char_dat[j].status.base_level == char_dat[id[k]].status.base_level &&
+ char_dat[j].status.base_exp < char_dat[id[k]].status.base_exp)) {
+ for(l = *players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[*players]
+ break;
+ }
+ break;
+ case 4: // by job (and job level)
+ for(k = 0; k < *players; k++)
+ if (char_dat[j].status.class_ < char_dat[id[k]].status.class_ ||
+ // if same job, we sort by job level.
+ (char_dat[j].status.class_ == char_dat[id[k]].status.class_ &&
+ char_dat[j].status.job_level < char_dat[id[k]].status.job_level) ||
+ // if same job and job level, we sort by job exp.
+ (char_dat[j].status.class_ == char_dat[id[k]].status.class_ &&
+ char_dat[j].status.job_level == char_dat[id[k]].status.job_level &&
+ char_dat[j].status.job_exp < char_dat[id[k]].status.job_exp)) {
+ for(l = *players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[*players]
+ break;
+ }
+ break;
+ case 5: // by location map name
+ {
+ const char *map1, *map2;
+ map1 = mapindex_id2name(char_dat[j].status.last_point.map);
+
+ for(k = 0; k < *players; k++) {
+ map2 = mapindex_id2name(char_dat[id[k]].status.last_point.map);
+ if (!map1 || !map2 || //Avoid sorting if either one failed to resolve.
+ stricmp(map1, map2) < 0 ||
+ // if same map name, we sort by name.
+ (stricmp(map1, map2) == 0 &&
+ stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) {
+ for(l = *players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[*players]
+ break;
+ }
+ }
+ }
+ break;
+ default: // 0 or invalid value: no sorting
+ break;
+ }
+ (*players)++;
+ break;
+ }
+ return 0;
+}
+//-------------------------------------------------------------
+// Function to create the online files (txt and html). by [Yor]
+//-------------------------------------------------------------
+void create_online_files(void) {
+ unsigned int k, j; // for loop with strlen comparing
+ int i, 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[4096];
+
+ if (online_display_option == 0) // we display nothing, so return
+ return;
+
+ // Get number of online players, id of each online players, and verify if a server is offline
+ players = 0;
+ online_char_db->foreach(online_char_db, create_online_files_sub, &players, &id);
+
+ // 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");
+
+ for (i = 0; i < players; i++) {
+ // if it's the first player
+ if (i == 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");
+ }
+ fprintf(fp2, " <tr>\n");
+ // get id of the character (more speed)
+ j = id[i];
+ // displaying the character name
+ if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display
+ strcpy(temp, char_dat[j].status.name);
+ l = isGM(char_dat[j].status.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; k < strlen(temp); k++) {
+ switch(temp[k]) {
+ case '<': // <
+ fprintf(fp2, "&lt;");
+ break;
+ case '>': // >
+ fprintf(fp2, "&gt;");
+ 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].status.class_);
+ if ((online_display_option & 6) == 6) {
+ fprintf(fp2, " <td>%s %d/%d</td>\n", jobname, char_dat[j].status.base_level, char_dat[j].status.job_level);
+ fprintf(fp, "%-18s %3d/%3d ", jobname, char_dat[j].status.base_level, char_dat[j].status.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].status.base_level, char_dat[j].status.job_level);
+ fprintf(fp, "%3d/%3d ", char_dat[j].status.base_level, char_dat[j].status.job_level);
+ }
+ }
+ // displaying of the map
+ if (online_display_option & 24) { // 8 or 16
+ // prepare map name
+ memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH);
+ temp[MAP_NAME_LENGTH] = '\0';
+ if (strstr(temp, ".gat") != NULL) {
+ temp[strstr(temp, ".gat") - temp] = 0; // suppress the '.gat'
+ }
+ // write map name
+ if (online_display_option & 16) { // map-name AND coordinates
+ fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y);
+ fprintf(fp, "%-12s (%3d,%3d) ", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y);
+ } else {
+ fprintf(fp2, " <td>%s</td>\n", temp);
+ fprintf(fp, "%-12s ", temp);
+ }
+ }
+ // displaying nimber of zenys
+ if (online_display_option & 32) {
+ // write number of zenys
+ if (char_dat[j].status.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].status.zeny);
+ fprintf(fp, "%13d z ", char_dat[j].status.zeny);
+ }
+ }
+ fprintf(fp, "\n");
+ fprintf(fp2, " </tr>\n");
+ }
+ // If we display at least 1 player
+ if (players > 0) {
+ 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");
+ } else if (players == 1) {
+ fprintf(fp2, " <p>%d user is online.</p>\n", players);
+ fprintf(fp, "%d user is online.\n", players);
+ } 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;
+}
+
+//----------------------------------------
+// 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;
+//#ifdef NEW_006b
+ const int offset = 24;
+//#else
+// const int offset = 4;
+//#endif
+
+ set_char_online(0, 99,sd->account_id);
+
+ found_num = 0;
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.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;
+
+ WFIFOHEAD(fd, offset + found_num * 106);
+ 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]].status;
+ 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) = p->job_level;
+
+ WFIFOL(fd,j+20) = 0;
+ WFIFOL(fd,j+24) = 0;
+ 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->class_;
+ WFIFOW(fd,j+54) = p->hair;
+
+ // pecopeco knights/crusaders crash fix
+ if (p->class_ == 13 || p->class_ == 21 ||
+ p->class_ == 4014 || p->class_ == 4022 ||
+ p->class_ == 4036 || p->class_ == 4044)
+ WFIFOW(fd,j+56) = 0;
+ else WFIFOW(fd,j+56) = p->weapon;
+
+ 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) = p->clothes_color;
+
+ memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH);
+
+ 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;
+}
+
+// —£¥(char휎ž‚ÉŽg—p)
+int char_divorce(struct mmo_charstatus *cs) {
+ if (cs == NULL)
+ return 0;
+
+ if (cs->partner_id > 0){
+ int i, j;
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.char_id == cs->partner_id && char_dat[i].status.partner_id == cs->char_id) {
+ cs->partner_id = 0;
+ char_dat[i].status.partner_id = 0;
+ for(j = 0; j < MAX_INVENTORY; j++)
+ if (char_dat[i].status.inventory[j].nameid == WEDDING_RING_M || char_dat[i].status.inventory[j].nameid == WEDDING_RING_F)
+ memset(&char_dat[i].status.inventory[j], 0, sizeof(char_dat[i].status.inventory[0]));
+ if (cs->inventory[j].nameid == WEDDING_RING_M || cs->inventory[j].nameid == WEDDING_RING_F)
+ memset(&cs->inventory[j], 0, sizeof(cs->inventory[0]));
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+int char_married(int pl1,int pl2) {
+ return (char_dat[pl1].status.char_id == char_dat[pl2].status.partner_id && char_dat[pl2].status.char_id == char_dat[pl1].status.partner_id);
+}
+
+int char_child(int parent_id, int child_id) {
+ return (char_dat[parent_id].status.child == char_dat[child_id].status.char_id &&
+ ((char_dat[parent_id].status.char_id == char_dat[child_id].status.father) ||
+ (char_dat[parent_id].status.char_id == char_dat[child_id].status.mother)));
+}
+
+//------------------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid). by [Yor]
+//------------------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++) {
+ if (strchr(last_arobas, ch) != NULL) {
+ return 0;
+ break;
+ }
+ }
+
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
+ return 0;
+
+ // all correct
+ return 1;
+}
+
+//----------------------------------------------------------------------
+// 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;
+}
+
+// ƒLƒƒƒ‰íœ‚É”º‚¤ƒf[ƒ^íœ
+static int char_delete(struct mmo_charstatus *cs) {
+ int j;
+
+ // ƒyƒbƒgíœ
+ if (cs->pet_id)
+ inter_pet_delete(cs->pet_id);
+ for (j = 0; j < MAX_INVENTORY; j++)
+ if (cs->inventory[j].card[0] == (short)0xff00)
+ inter_pet_delete(MakeDWord(cs->inventory[j].card[1],cs->inventory[j].card[2]));
+ for (j = 0; j < MAX_CART; j++)
+ if (cs->cart[j].card[0] == (short)0xff00)
+ inter_pet_delete( MakeDWord(cs->cart[j].card[1],cs->cart[j].card[2]) );
+ // ƒMƒ‹ƒh’E‘Þ
+ if (cs->guild_id)
+ inter_guild_leave(cs->guild_id, cs->account_id, cs->char_id);
+ // ƒp[ƒeƒB[’E‘Þ
+ if (cs->party_id)
+ inter_party_leave(cs->party_id, cs->account_id, cs->char_id);
+ // —£¥
+ if (cs->partner_id){
+ // —£¥î•ñ‚ðmap‚É’Ê’m
+ unsigned char buf[10];
+ WBUFW(buf,0) = 0x2b12;
+ WBUFL(buf,2) = cs->char_id;
+ WBUFL(buf,6) = cs->partner_id;
+ mapif_sendall(buf,10);
+ // —£¥
+ char_divorce(cs);
+ }
+ return 0;
+}
+
+int parse_tologin(int fd) {
+ int i;
+ struct char_session_data *sd;
+ RFIFOHEAD(fd);
+
+ // 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 = 1;
+ if(session[fd]->eof) {
+ if (fd == login_fd) {
+ ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
+ login_fd = -1;
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ sd = (struct char_session_data*)session[fd]->session_data;
+
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+// 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 0;
+ if (RFIFOB(fd,2)) {
+// printf("connect login server error : %d\n", RFIFOB(fd,2));
+ ShowError("Can not connect to the login-server.\n");
+ ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
+ ShowInfo("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n");
+ ShowInfo("The communication passwords can be changed in map_athena.conf and char_athena.conf\n");
+ exit(1);
+ } else {
+ ShowStatus("Connected to login-server (connection #%d).\n", fd);
+ if (kick_on_disconnect)
+ set_all_offline();
+ // 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]) // if map-server online and at least 1 map
+ break;
+ if (i == MAX_MAP_SERVERS)
+ ShowStatus("Awaiting maps from map-server.\n");
+ }
+ RFIFOSKIP(fd,3);
+ break;
+
+ case 0x2713:
+ if (RFIFOREST(fd) < 51)
+ return 0;
+// 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) {
+ WFIFOHEAD(i, 3);
+ 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 if(isGM(sd->account_id) >= gm_allow_level) {
+ 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);
+ WFIFOHEAD(fd, 3);
+ 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 0;
+ 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;
+
+ // login-server alive packet
+ case 0x2718:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ RFIFOSKIP(fd,2);
+ break;
+
+ // Receiving authentification from Freya-type login server (to avoid char->login->char)
+ case 0x2719:
+ if (RFIFOREST(fd) < 18)
+ return 0;
+ // to conserv a maximum of authentification, search if account is already authentified and replace it
+ // that will reduce multiple connection too
+ for(i = 0; i < AUTH_FIFO_SIZE; i++)
+ if (auth_fifo[i].account_id == RFIFOL(fd,2))
+ break;
+ // if not found, use next value
+ if (i == AUTH_FIFO_SIZE) {
+ if (auth_fifo_pos >= AUTH_FIFO_SIZE)
+ auth_fifo_pos = 0;
+ i = auth_fifo_pos;
+ auth_fifo_pos++;
+ }
+ auth_fifo[i].account_id = RFIFOL(fd,2);
+ auth_fifo[i].char_id = 0;
+ auth_fifo[i].login_id1 = RFIFOL(fd,6);
+ auth_fifo[i].login_id2 = RFIFOL(fd,10);
+ auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified
+ auth_fifo[i].char_pos = 0;
+ auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server)
+ auth_fifo[i].ip = RFIFOL(fd,14);
+ RFIFOSKIP(fd,18);
+ break;
+
+ case 0x2721: // gm reply
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ 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 0;
+ {
+ 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 < AUTH_FIFO_SIZE; i++) {
+ if (auth_fifo[i].account_id == acc)
+ auth_fifo[i].sex = sex;
+ }
+ for (i = 0; i < char_num; i++) {
+ if (char_dat[i].status.account_id == acc) {
+ int jobclass = char_dat[i].status.class_;
+ char_dat[i].status.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].status.class_ = (sex) ? 19 : 20;
+ } else if (jobclass == 4020 || jobclass == 4021) {
+ char_dat[i].status.class_ = (sex) ? 4020 : 4021;
+ } else if (jobclass == 4042 || jobclass == 4043) {
+ char_dat[i].status.class_ = (sex) ? 4042 : 4043;
+ }
+ // remove specifical skills of classes 19, 4020 and 4042
+ for(j = 315; j <= 322; j++) {
+ if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) {
+ char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv;
+ char_dat[i].status.skill[j].id = 0;
+ char_dat[i].status.skill[j].lv = 0;
+ }
+ }
+ // remove specifical skills of classes 20, 4021 and 4043
+ for(j = 323; j <= 330; j++) {
+ if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) {
+ char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv;
+ char_dat[i].status.skill[j].id = 0;
+ char_dat[i].status.skill[j].lv = 0;
+ }
+ }
+ }
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for (j = 0; j < MAX_INVENTORY; j++) {
+ if (char_dat[i].status.inventory[j].nameid && char_dat[i].status.inventory[j].equip)
+ char_dat[i].status.inventory[j].equip = 0;
+ }
+ char_dat[i].status.weapon = 0;
+ char_dat[i].status.shield = 0;
+ char_dat[i].status.head_top = 0;
+ char_dat[i].status.head_mid = 0;
+ char_dat[i].status.head_bottom = 0;
+
+ if (char_dat[i].status.guild_id) //If there is a guild, update the guild_member data [Skotlex]
+ inter_guild_sex_changed(char_dat[i].status.guild_id, acc, char_dat[i].status.char_id, sex);
+ }
+ }
+ // 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 0;
+ if (RFIFOL(fd,4) < 1)
+ char_log("Receiving a message for broadcast, but message is void." RETCODE);
+ 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." RETCODE);
+ else {
+ unsigned char buf[128];
+ char message[4096]; // +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((unsigned char *)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." RETCODE);
+ // 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)" RETCODE,
+ message);
+ lp = 4;
+ } else {
+ char_log("'ladmin': Receiving a message for broadcast (message (in blue): %s)" RETCODE,
+ 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•ÏX’Ê’m
+ case 0x2729:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ { //Receive account_reg2 registry, forward to map servers.
+ unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16];
+ memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2));
+// WBUFW(buf,0) = 0x2b11;
+ WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex]
+ mapif_sendall(buf, WBUFW(buf,2));
+ RFIFOSKIP(fd, RFIFOW(fd,2));
+ }
+ break;
+
+ // Account deletion notification (from login-server)
+ case 0x2730:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ // Deletion of all characters of the account
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.account_id == RFIFOL(fd,2)) {
+ char_delete(&char_dat[i].status);
+ if (i < char_num - 1) {
+ memcpy(&char_dat[i], &char_dat[char_num-1], sizeof(struct character_data));
+ // if moved character owns to deleted account, check again it's character
+ if (char_dat[i].status.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].status.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 0;
+ // 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 0;
+ {
+ unsigned char buf[32000];
+ if (gm_account != NULL)
+ aFree(gm_account);
+ gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1);
+ 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++;
+ }
+ ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num);
+ char_log("From login-server: receiving information of %d GM accounts." RETCODE, 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;
+
+ // Receive GM accounts [Freya login server packet by Yor]
+ case 0x2733:
+ // add test here to remember that the login-server is Freya-type
+ // sprintf (login_server_type, "Freya");
+ if (RFIFOREST(fd) < 7)
+ return 0;
+ {
+ unsigned char buf[32000];
+ int new_level = 0;
+ for(i = 0; i < GM_num; i++)
+ if (gm_account[i].account_id == RFIFOL(fd,2)) {
+ if (gm_account[i].level != (int)RFIFOB(fd,6)) {
+ gm_account[i].level = (int)RFIFOB(fd,6);
+ new_level = 1;
+ }
+ break;
+ }
+ // if not found, add it
+ if (i == GM_num) {
+ // 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)
+ if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) {
+ if (GM_num == 0) {
+ gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account));
+ } else {
+ gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1));
+ }
+ gm_account[GM_num].account_id = RFIFOL(fd,2);
+ gm_account[GM_num].level = (int)RFIFOB(fd,6);
+ new_level = 1;
+ GM_num++;
+ if (GM_num >= 4000) {
+ ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n");
+ char_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE);
+ }
+ }
+ }
+ if (new_level == 1) {
+ int len;
+ ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6));
+ char_log("From login-server: receiving a GM account information (%d: level %d)." RETCODE, RFIFOL(fd,2), (int)RFIFOB(fd,6));
+ //create_online_files(); // not change online file for only 1 player (in next timer, that will be done
+ // send gm acccounts level to map-servers
+ len = 4;
+ WBUFW(buf,0) = 0x2b15;
+
+ for(i = 0; i < GM_num; i++) {
+ WBUFL(buf, len) = gm_account[i].account_id;
+ WBUFB(buf, len+4) = (unsigned char)gm_account[i].level;
+ len += 5;
+ }
+ WBUFW(buf, 2) = len;
+ mapif_sendall(buf, len);
+ }
+ }
+ RFIFOSKIP(fd,7);
+ break;
+
+ //Login server request to kick a character out. [Skotlex]
+ case 0x2734:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ {
+ struct online_char_data* character;
+ int aid = RFIFOL(fd,2);
+ if ((character = idb_get(online_char_db, aid)) != NULL)
+ { //Kick out this player.
+ if (character->server > -1)
+ { //Kick it from the map server it is on.
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ if (!character->waiting_disconnect)
+ add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0);
+ character->waiting_disconnect = 1;
+ } else { //Manual kick from char server.
+ struct char_session_data *tsd;
+ int i;
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid)
+ {
+ WFIFOHEAD(fd, 3);
+ WFIFOW(i,0) = 0x81;
+ WFIFOB(i,2) = 2;
+ WFIFOSET(i,3);
+ break;
+ }
+ }
+ if (i == fd_max) //Shouldn't happen, but just in case.
+ set_char_offline(99, aid);
+ }
+ }
+ RFIFOSKIP(fd,6);
+ }
+ break;
+ default:
+ ShowWarning("Unknown packet 0x%04x received from login-server, disconnecting.\n", RFIFOW(fd,0));
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ RFIFOFLUSH(fd);
+
+ return 0;
+}
+
+int request_accreg2(int account_id, int char_id) {
+ if (login_fd > 0) {
+ WFIFOW(login_fd, 0) = 0x272e;
+ WFIFOL(login_fd, 2) = account_id;
+ WFIFOL(login_fd, 6) = char_id;
+ WFIFOSET(login_fd, 10);
+ return 1;
+ }
+ return 0;
+}
+
+//Send packet forward to login-server for account saving
+int save_accreg2(unsigned char* buf, int len) {
+ if (login_fd > 0) {
+ WFIFOHEAD(login_fd, len+4);
+ memcpy(WFIFOP(login_fd,4), buf, len);
+ WFIFOW(login_fd,0) = 0x2728;
+ WFIFOW(login_fd,2) = len+4;
+ WFIFOSET(login_fd,len+4);
+ return 1;
+ }
+ return 0;
+}
+
+//Receive Registry information for a character.
+int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int buf_len) {
+ int i,j,p,len;
+ for (i = 0; i < char_num; i++) {
+ if (char_dat[i].status.account_id == account_id && char_dat[i].status.char_id == char_id)
+ break;
+ }
+ if(i >= char_num) //Character not found?
+ return 1;
+ for(j=0,p=0;j<GLOBAL_REG_NUM && p<buf_len;j++){
+ sscanf(WBUFP(buf,p), "%31c%n",char_dat[i].global[j].str,&len);
+ char_dat[i].global[j].str[len]='\0';
+ p +=len+1; //+1 to skip the '\0' between strings.
+ sscanf(WBUFP(buf,p), "%255c%n",char_dat[i].global[j].value,&len);
+ char_dat[i].global[j].value[len]='\0';
+ p +=len+1;
+ }
+ char_dat[i].global_num = j;
+ return 0;
+}
+
+//Reply to map server with acc reg values.
+int char_account_reg_reply(int fd,int account_id,int char_id) {
+ int i,j,p;
+ WFIFOHEAD(login_fd, GLOBAL_REG_NUM*288 + 13);
+ WFIFOW(fd,0)=0x3804;
+ WFIFOL(fd,4)=account_id;
+ WFIFOL(fd,8)=char_id;
+ WFIFOB(fd,12)=3; //Type 3: char acc reg.
+ for (i = 0;i < char_num; i++) {
+ if (char_dat[i].status.account_id == account_id && char_dat[i].status.char_id == char_id)
+ break;
+ }
+ if(i >= char_num){ //Character not found? Sent empty packet.
+ WFIFOW(fd,2)=13;
+ }else{
+ for (p=13,j = 0; j < char_dat[i].global_num; j++) {
+ if (char_dat[i].global[j].str[0]) {
+ p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].value)+1;
+ }
+ }
+ WFIFOW(fd,2)=p;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+int search_mapserver(unsigned short map, long ip, short port);
+
+int parse_frommap(int fd) {
+ int i, j;
+ int id;
+ RFIFOHEAD(fd);
+
+ for(id = 0; id < MAX_MAP_SERVERS; id++)
+ if (server_fd[id] == fd)
+ break;
+ if(id==MAX_MAP_SERVERS)
+ session[fd]->eof=1;
+ if(session[fd]->eof){
+ if (id < MAX_MAP_SERVERS) {
+ unsigned char buf[16384];
+ ShowStatus("Map-server %d has disconnected.\n", id);
+ //Notify other map servers that this one is gone. [Skotlex]
+ WBUFW(buf,0) = 0x2b20;
+ WBUFL(buf,4) = server[id].ip;
+ WBUFW(buf,8) = server[id].port;
+ j = 0;
+ for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ if (server[id].map[i])
+ WBUFW(buf,10+(j++)*4) = server[id].map[i];
+ if (j > 0) {
+ WBUFW(buf,2) = j * 4 + 10;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+ }
+ server_fd[id] = -1;
+ online_char_db->foreach(online_char_db,char_db_setoffline,i); //Tag relevant chars as 'in disconnected' server.
+ }
+ do_close(fd);
+ create_online_files();
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+// printf("parse_frommap: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd));
+
+ switch(RFIFOW(fd,0)) {
+
+ // map-server alive packet
+ case 0x2718:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ RFIFOSKIP(fd,2);
+ break;
+
+ // 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
+ WFIFOHEAD(login_fd, 2);
+ 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 0;
+ memset(server[id].map, 0, sizeof(server[id].map));
+ j = 0;
+ for(i = 4; i < RFIFOW(fd,2); i += 4) {
+ server[id].map[j] = RFIFOW(fd,i);
+ j++;
+ }
+ {
+ unsigned char *p = (unsigned char *)&server[id].ip;
+ ShowStatus("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);
+ ShowStatus("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." RETCODE,
+ id, j, p[0], p[1], p[2], p[3], server[id].port, id);
+ if (kick_on_disconnect)
+ set_all_offline();
+ if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID)
+ mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex]
+ }
+ WFIFOHEAD(fd, 3 + NAME_LENGTH);
+ WFIFOW(fd,0) = 0x2afb;
+ WFIFOB(fd,2) = 0;
+ memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); // name for wisp to player
+ WFIFOSET(fd,3+NAME_LENGTH);
+ //WFIFOSET(fd,27);
+ {
+ unsigned char buf[16384];
+ int x;
+ if (j == 0) {
+ ShowWarning("Map-Server %d have NO map.\n", id);
+ char_log("WARNING: Map-Server %d have NO map." RETCODE, id);
+ // Transmitting maps information to the other map-servers
+ } else {
+ WBUFW(buf,0) = 0x2b04;
+ WBUFW(buf,2) = j * 4 + 10;
+ WBUFL(buf,4) = server[id].ip;
+ WBUFW(buf,8) = server[id].port;
+ memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4);
+ 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])
+ WFIFOW(fd,10+(j++)*4) = server[x].map[i];
+ if (j > 0) {
+ WFIFOW(fd,2) = j * 4 + 10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ }
+ }
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+
+ //Packet command is now used for sc_data request. [Skotlex]
+ case 0x2afc:
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+#ifdef ENABLE_SC_SAVING
+ int aid, cid;
+ struct scdata *data;
+ aid = RFIFOL(fd,2);
+ cid = RFIFOL(fd,6);
+#endif
+ RFIFOSKIP(fd, 10);
+#ifdef ENABLE_SC_SAVING
+ data = status_search_scdata(aid, cid);
+ if (data->count > 0)
+ { //Deliver status change data.
+ int i;
+
+ WFIFOW(fd,0) = 0x2b1d;
+ WFIFOW(fd,2) = 14 + data->count*sizeof(struct status_change_data);
+ WFIFOL(fd,4) = aid;
+ WFIFOL(fd,8) = cid;
+ WFIFOW(fd,12) = data->count;
+ for (i = 0; i < data->count; i++)
+ memcpy(WFIFOP(fd,14+i*sizeof(struct status_change_data)), &data->data[i], sizeof(struct status_change_data));
+ WFIFOSET(fd, WFIFOW(fd,2));
+ status_delete_scdata(aid, cid); //Data sent, so it needs be discarded now.
+ }
+#endif
+ break;
+ }
+
+ //set MAP user count
+ case 0x2afe:
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ if (RFIFOW(fd,2) != server[id].users) {
+ server[id].users = RFIFOW(fd,2);
+ ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id);
+ }
+ RFIFOSKIP(fd, 4);
+ break;
+ //set MAP users
+ case 0x2aff:
+ if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ server[id].users = RFIFOW(fd,4);
+ // add online players in the list by [Yor], adapted to use dbs by [Skotlex]
+ j = 0;
+ online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown'
+ for(i = 0; i < server[id].users; i++) {
+ int aid, cid;
+ struct online_char_data* character;
+ aid = RFIFOL(fd,6+i*8);
+ cid = RFIFOL(fd,6+i*8+4);
+ character = idb_ensure(online_char_db, aid, create_online_char_data);
+ if (online_check && character->server > -1 && character->server != id)
+ {
+ ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n",
+ character->account_id, character->char_id, character->server, id, aid, cid);
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ }
+ character->char_id = cid;
+ character->server = id;
+ }
+ 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.
+ }
+ //If any chars remain in -2, they will be cleaned in the cleanup timer.
+ RFIFOSKIP(fd,6+i*8);
+ break;
+
+ // ƒLƒƒƒ‰ƒf[ƒ^•Û‘¶
+ // Recieve character data from map-server
+ case 0x2b01:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.account_id == RFIFOL(fd,4) &&
+ char_dat[i].status.char_id == RFIFOL(fd,8))
+ break;
+ }
+ if (i != char_num)
+ memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
+ if (RFIFOB(fd,12)) { //Flag, set character offline. [Skotlex]
+ set_char_offline(RFIFOL(fd,8),RFIFOL(fd,4));
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+
+ // ƒLƒƒƒ‰ƒZƒŒ—v‹
+ case 0x2b02:
+ if (RFIFOREST(fd) < 18)
+ return 0;
+ if (auth_fifo_pos >= AUTH_FIFO_SIZE)
+ auth_fifo_pos = 0;
+ 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;
+
+ // request "change map server"
+ case 0x2b05:
+ if (RFIFOREST(fd) < 35)
+ return 0;
+ {
+ unsigned short name;
+ int map_id, map_fd = -1, i;
+ struct online_char_data* data;
+ struct mmo_charstatus* char_data;
+
+ name = RFIFOW(fd,18);
+ map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port.
+ if (map_id >= 0)
+ map_fd = server_fd[map_id];
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.account_id == RFIFOL(fd,2) &&
+ char_dat[i].status.char_id == RFIFOL(fd,14))
+ break;
+ }
+ char_data = i< char_num? &char_dat[i].status:NULL;
+ //Tell the new map server about this player using Kevin's new auth packet. [Skotlex]
+ if (map_fd>=0 && session[map_fd] && char_data)
+ { //Send the map server the auth of this player.
+ //Update the "last map" as this is where the player must be spawned on the new map server.
+ char_data->last_point.map = RFIFOW(fd,18);
+ char_data->last_point.x = RFIFOW(fd,20);
+ char_data->last_point.y = RFIFOW(fd,22);
+
+ WFIFOW(map_fd,0) = 0x2afd;
+ WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus);
+ WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID
+ WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1
+ WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2
+ WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now?
+ memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus));
+ WFIFOSET(map_fd, WFIFOW(map_fd,2));
+ data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data);
+ data->char_id = char_data->char_id;
+ data->server = map_id; //Update server where char is.
+
+ //Reply with an ack.
+ WFIFOW(fd, 0) = 0x2b06;
+ memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+ WFIFOSET(fd, 30);
+ } else { //Reply with nak
+ WFIFOW(fd, 0) = 0x2b06;
+ memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+ WFIFOL(fd, 6) = 0; //Set login1 to 0.
+ WFIFOSET(fd, 30);
+ }
+ RFIFOSKIP(fd, 35);
+ }
+ break;
+
+ // ƒLƒƒƒ‰–¼ŒŸõ
+ case 0x2b08:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.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].status.name, NAME_LENGTH);
+ else
+ memcpy(WFIFOP(fd,6), unknown_char_name, NAME_LENGTH);
+ WFIFOSET(fd,6+NAME_LENGTH);
+ //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 0;
+// 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 0;
+ 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 0;
+ {
+ char character_name[NAME_LENGTH];
+ int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody)
+ memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH-1);
+ character_name[NAME_LENGTH -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), NAME_LENGTH); // put correct name if found
+ WFIFOW(fd,6+NAME_LENGTH) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline
+ //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].status.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].status.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].status.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].status.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].status.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].status.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].status.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].status.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].status.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].status.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, NAME_LENGTH);
+ WFIFOW(fd,8+NAME_LENGTH) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline
+ //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);
+ WFIFOSET(fd, 10+NAME_LENGTH);
+ }
+ RFIFOSKIP(fd, 44);
+ break;
+ }
+
+// case 0x2b0f: not more used (available for futur usage)
+
+ //Packet 0x2b10 deprecated in favor of packet 0x3004 for registry saving. [Skotlex]
+ //case 0x2b10:
+
+ // Recieve rates [Wizputer]
+ case 0x2b16:
+ if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8))
+ return 0;
+ // Txt doesn't need this packet, so just skip it
+ RFIFOSKIP(fd,RFIFOW(fd,8));
+ break;
+
+ // Character disconnected set online 0 [Wizputer]
+ case 0x2b17:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ //printf("Setting %d char offline\n",RFIFOL(fd,2));
+ set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6));
+ RFIFOSKIP(fd,10);
+ break;
+
+ // Reset all chars to offline [Wizputer]
+ case 0x2b18:
+ ShowNotice("Map server [%d] requested to set all characters offline.\n", id);
+ set_all_offline();
+ RFIFOSKIP(fd,2);
+ break;
+
+ // Character set online [Wizputer]
+ case 0x2b19:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ //printf("Setting %d char online\n",RFIFOL(fd,2));
+ set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6));
+ RFIFOSKIP(fd,10);
+ break;
+
+ // Request sending of fame list
+ case 0x2b1a:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ {
+ int i, j, k, len = 8;
+ unsigned char buf[32000];
+ struct fame_list fame_item;
+ //struct mmo_charstatus *dat;
+ //dat = (struct mmo_charstatus *)aCalloc(char_num, sizeof(struct mmo_charstatus *));
+ CREATE_BUFFER(id, int, char_num);
+
+ // copy character list into buffer
+ //for (i = 0; i < char_num; i++)
+ // dat[i] = char_dat[i];
+ // sort according to fame
+ // qsort(dat, char_num, sizeof(struct mmo_charstatus *), sort_fame);
+
+ for(i = 0; i < char_num; i++) {
+ id[i] = i;
+ for(j = 0; j < i; j++) {
+ if (char_dat[i].status.fame > char_dat[id[j]].status.fame) {
+ for(k = i; k > j; k--)
+ id[k] = id[k-1];
+ id[j] = i; // id[i]
+ break;
+ }
+ }
+ }
+
+ // starting to send to map
+ WBUFW(buf,0) = 0x2b1b;
+ // add list for blacksmiths
+ for (i = 0, j = 0; i < char_num && j < 10; i++) {
+ if (char_dat[id[i]].status.fame && (char_dat[id[i]].status.class_ == 10 ||
+ char_dat[id[i]].status.class_ == 4011 ||
+ char_dat[id[i]].status.class_ == 4033))
+ {
+ fame_item.id = char_dat[id[i]].status.char_id;
+ fame_item.fame = char_dat[id[i]].status.fame;
+ strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH);
+
+ memcpy(WBUFP(buf, len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ j++;
+ }
+ }
+ // add blacksmith's block length
+ WBUFW(buf, 6) = len;
+
+ // add list for alchemists
+ for (i = 0, j = 0; i < char_num && j < 10; i++) {
+ if (char_dat[id[i]].status.fame && (char_dat[id[i]].status.class_ == 18 ||
+ char_dat[id[i]].status.class_ == 4019 ||
+ char_dat[id[i]].status.class_ == 4041))
+ {
+ fame_item.id = char_dat[id[i]].status.char_id;
+ fame_item.fame = char_dat[id[i]].status.fame;
+ strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH);
+
+ memcpy(WBUFP(buf, len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ j++;
+ }
+ }
+ // add alchemist's block length
+ WBUFW(buf, 4) = len;
+
+ // adding list for taekwons
+ for (i = 0, j = 0; i < char_num && j < 10; i++) {
+ if (char_dat[id[i]].status.fame && char_dat[id[i]].status.class_ == 4046)
+ {
+ fame_item.id = char_dat[id[i]].status.char_id;
+ fame_item.fame = char_dat[id[i]].status.fame;
+ strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH);
+
+ memcpy(WBUFP(buf, len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ j++;
+ }
+ }
+ // add total packet length
+ WBUFW(buf, 2) = len;
+
+ // sending to all maps
+ mapif_sendall(buf, len);
+ // done!
+ //aFree(dat);
+ DELETE_BUFFER(id);
+ RFIFOSKIP(fd,2);
+ break;
+ }
+ //Request to save status change data. [Skotlex]
+ case 0x2b1c:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+#ifdef ENABLE_SC_SAVING
+ int count, aid, cid, i;
+ struct scdata *data;
+ aid = RFIFOL(fd, 4);
+ cid = RFIFOL(fd, 8);
+ count = RFIFOW(fd, 12);
+ data = status_search_scdata(aid, cid);
+ if (data->count != count)
+ {
+ data->count = count;
+ data->data = aRealloc(data->data, count*sizeof(struct status_change_data));
+ }
+ for (i = 0; i < count; i++)
+ memcpy (&data->data[i], RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
+#endif
+ RFIFOSKIP(fd, RFIFOW(fd, 2));
+ break;
+ }
+ default:
+ // inter serverˆ—‚É“n‚·
+ {
+ int r = inter_parse_frommap(fd);
+ if (r == 1) // ˆ—‚Å‚«‚½
+ break;
+ if (r == 2) // ƒpƒPƒbƒg’·‚ª‘«‚è‚È‚¢
+ return 0;
+ }
+ // inter serverˆ—‚Å‚à‚È‚¢ê‡‚ÍØ’f
+ ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int search_mapserver(unsigned short map, long ip, short port) {
+ int i, j;
+
+ for(i = 0; i < MAX_MAP_SERVERS; i++)
+ if (server_fd[i] >= 0)
+ for (j = 0; server[i].map[j]; j++)
+ if (server[i].map[j] == map) {
+ if (ip > 0 && server[i].ip != ip)
+ continue;
+ if (port > 0 && server[i].port != port)
+ continue;
+ return i;
+ }
+
+ return -1;
+}
+
+// char_mapif‚̉Šú‰»ˆ—iŒ»Ý‚Íinter_mapif‰Šú‰»‚Ì‚Ýj
+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;
+ }
+ }
+ ShowInfo("LAN test (result): %s source"CL_RESET".\n", (lancheck) ? CL_CYAN"LAN" : CL_GREEN"WAN");
+ return lancheck;
+}
+
+int parse_char(int fd) {
+ int i, ch;
+ unsigned short cmd;
+ char email[40];
+ int map_fd;
+ struct char_session_data *sd;
+ unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ RFIFOHEAD(fd);
+
+ sd = (struct char_session_data*)session[fd]->session_data;
+
+ if (login_fd < 0)
+ session[fd]->eof = 1;
+ if(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;
+ if (sd != NULL)
+ {
+ struct online_char_data* data = idb_get(online_char_db, sd->account_id);
+ if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex]
+ set_char_offline(99,sd->account_id);
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+ cmd = RFIFOW(fd,0);
+ // crc32‚̃XƒLƒbƒv—p
+ if( sd==NULL && // –¢ƒƒOƒCƒ“orŠÇ—ƒpƒPƒbƒg
+ RFIFOREST(fd)>=4 && // Å’áƒoƒCƒg”§ŒÀ • 0x7530,0x7532ŠÇ—ƒpƒPœ‹Ž
+ RFIFOREST(fd)<=21 && // Å‘åƒoƒCƒg”§ŒÀ • ƒT[ƒo[ƒƒOƒCƒ“œ‹Ž
+ cmd!=0x20b && // md5’Ê’mƒpƒPƒbƒgœ‹Ž
+ (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // ŽŸ‚ɉ½‚©ƒpƒPƒbƒg‚ª—ˆ‚Ä‚é‚È‚çAÚ‘±‚Å‚È‚¢‚Æ‚¾‚ß
+ RFIFOSKIP(fd,4);
+ cmd = RFIFOW(fd,0);
+ ShowDebug("parse_char : %d crc32 skipped\n",fd);
+ if(RFIFOREST(fd)==0)
+ return 0;
+ }
+
+//For use in packets that depend on an sd being present [Skotlex]
+#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } }
+
+ switch(cmd){
+ case 0x20b: //20040622ˆÃ†‰»ragexe‘Ήž
+ if (RFIFOREST(fd) < 19)
+ return 0;
+ RFIFOSKIP(fd,19);
+ break;
+
+ case 0x65: // Ú‘±—v‹
+ if (RFIFOREST(fd) < 17)
+ return 0;
+ {
+ int GM_value;
+ if ((GM_value = isGM(RFIFOL(fd,2))))
+ ShowInfo("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value);
+ else
+ ShowInfo("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2));
+ if (sd == NULL) {
+ sd = (struct char_session_data*)aCalloc(sizeof(struct char_session_data), 1);
+ session[fd]->session_data = sd;
+
+// memset(sd, 0, sizeof(struct char_session_data)); aCalloc does this [Skotlex]
+ strncpy(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->sex = RFIFOB(fd,16);
+ // send back account_id
+ WFIFOHEAD(fd, 4);
+ 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 (online_check)
+ { // check if character is not online already. [Skotlex]
+ struct online_char_data* character;
+ character = idb_get(online_char_db, sd->account_id);
+
+ if (character)
+ {
+ if(character->server > -1)
+ { //Kick it from the map server it is on.
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ if (!character->waiting_disconnect)
+ add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0);
+ character->waiting_disconnect = 1;
+ /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex]
+ } else { //Manual kick from char server.
+ struct char_session_data *tsd;
+ int i;
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && i!=fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id)
+ {
+ WFIFOW(i,0) = 0x81;
+ WFIFOB(i,2) = 2;
+ WFIFOSET(i,3);
+ break;
+ }
+ }
+ if (i == fd_max) //Shouldn't happen, but just in case.
+ set_char_offline(99, sd->account_id);
+ */
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 8;
+ WFIFOSET(fd,3);
+ break;
+ }
+ }
+ }
+
+ 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);
+ }
+ // 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: // ƒLƒƒƒ‰‘I‘ð
+ FIFOSD_CHECK(3);
+ {
+ int char_num = RFIFOB(fd,2);
+ struct mmo_charstatus *cd;
+ RFIFOSKIP(fd,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
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd, 0) = 0x70;
+ WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address
+ WFIFOSET(fd, 3);
+ break;
+ }
+ // otherwise, load the character
+ for (ch = 0; ch < 9; ch++)
+ if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num)
+ break;
+ if (ch == 9)
+ { //Not found?? May be forged packet.
+ break;
+ }
+ cd = &char_dat[sd->found_char[ch]].status;
+ char_log("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s." RETCODE,
+ sd->account_id, char_num, cd->name);
+
+ cd->sex = sd->sex;
+
+ // searching map server
+ i = search_mapserver(cd->last_point.map,-1,-1);
+ // if map is not found, we check major cities
+ if (i < 0) {
+ unsigned short j;
+ ShowWarning("Unable to find map-server for '%s', resorting to sending to a major city.\n", mapindex_id2name(cd->last_point.map));
+ if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 273; // savepoint coordinates
+ cd->last_point.y = 354;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 120; // savepoint coordinates
+ cd->last_point.y = 100;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 160; // savepoint coordinates
+ cd->last_point.y = 94;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 116; // savepoint coordinates
+ cd->last_point.y = 57;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 87; // savepoint coordinates
+ cd->last_point.y = 117;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) {
+ cd->last_point.map = j;
+ cd->last_point.x = 94; // savepoint coordinates
+ cd->last_point.y = 103;
+ } else {
+ // 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]) { // change save point to one of map found on the server (the first)
+ i = j;
+ cd->last_point.map = server[j].map[0];
+ ShowInfo("Map-server #%d found with a map: '%s'.\n", j, mapindex_id2name(server[j].map[0]));
+ // coordinates are unknown
+ break;
+ }
+ // if no map-server is connected, we send: server closed
+ if (j == MAX_MAP_SERVERS) {
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ break;
+ }
+ }
+ }
+ WFIFOHEAD(fd, 28);
+ WFIFOW(fd,0) = 0x71;
+ WFIFOL(fd,2) = cd->char_id;
+ memcpy(WFIFOP(fd,6), mapindex_id2name(cd->last_point.map), MAP_NAME_LENGTH);
+ ShowInfo("Character selection '%s' (account: %d, slot: %d).\n", cd->name, sd->account_id, ch);
+ 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;
+ auth_fifo[auth_fifo_pos].account_id = sd->account_id;
+ auth_fifo[auth_fifo_pos].char_id = cd->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;
+
+ //Send NEW auth packet [Kevin]
+ if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL)
+ { //0 Should not be a valid server_fd [Skotlex]
+ ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
+ server_fd[i] = -1;
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ //Send server closed.
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ break;
+ }
+ WFIFOW(map_fd,0) = 0x2afd;
+ WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus);
+ WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id;
+ WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1;
+ WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2;
+ WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time;
+ memcpy(WFIFOP(map_fd,20), cd, sizeof(struct mmo_charstatus));
+ WFIFOSET(map_fd, WFIFOW(map_fd,2));
+
+ set_char_online(i, cd->char_id, cd->account_id);
+ //Sets char online in the party and breaks even share if needed.
+ inter_party_logged(cd->party_id, cd->account_id, cd->char_id);
+
+ auth_fifo_pos++;
+ }
+ break;
+
+ case 0x67: // ì¬
+ FIFOSD_CHECK(37);
+
+ if(char_new == 0) //turn character creation on/off [Kevin]
+ i = -2;
+ else
+ i = make_new_char(fd, RFIFOP(fd,2));
+
+ if(i == -1){ //added some better faile reporting to client on the txt version [Kevin]
+ //already exists
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x00;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }else if(i == -2){
+ //denied
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x02;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }else if(i == -3){
+ //underaged XD
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x01;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }
+
+ WFIFOHEAD(fd, 108);
+ WFIFOW(fd,0) = 0x6d;
+ memset(WFIFOP(fd,2), 0, 106);
+
+ WFIFOL(fd,2) = char_dat[i].status.char_id;
+ WFIFOL(fd,2+4) = char_dat[i].status.base_exp;
+ WFIFOL(fd,2+8) = char_dat[i].status.zeny;
+ WFIFOL(fd,2+12) = char_dat[i].status.job_exp;
+ WFIFOL(fd,2+16) = char_dat[i].status.job_level;
+
+ WFIFOL(fd,2+28) = char_dat[i].status.karma;
+ WFIFOL(fd,2+32) = char_dat[i].status.manner;
+
+ WFIFOW(fd,2+40) = 0x30;
+ WFIFOW(fd,2+42) = (char_dat[i].status.hp > 0x7fff) ? 0x7fff : char_dat[i].status.hp;
+ WFIFOW(fd,2+44) = (char_dat[i].status.max_hp > 0x7fff) ? 0x7fff : char_dat[i].status.max_hp;
+ WFIFOW(fd,2+46) = (char_dat[i].status.sp > 0x7fff) ? 0x7fff : char_dat[i].status.sp;
+ WFIFOW(fd,2+48) = (char_dat[i].status.max_sp > 0x7fff) ? 0x7fff : char_dat[i].status.max_sp;
+ WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].status.speed;
+ WFIFOW(fd,2+52) = char_dat[i].status.class_;
+ WFIFOW(fd,2+54) = char_dat[i].status.hair;
+
+ WFIFOW(fd,2+58) = char_dat[i].status.base_level;
+ WFIFOW(fd,2+60) = char_dat[i].status.skill_point;
+
+ WFIFOW(fd,2+64) = char_dat[i].status.shield;
+ WFIFOW(fd,2+66) = char_dat[i].status.head_top;
+ WFIFOW(fd,2+68) = char_dat[i].status.head_mid;
+ WFIFOW(fd,2+70) = char_dat[i].status.hair_color;
+
+ memcpy(WFIFOP(fd,2+74), char_dat[i].status.name, NAME_LENGTH);
+
+ WFIFOB(fd,2+98) = (char_dat[i].status.str > 255) ? 255 : char_dat[i].status.str;
+ WFIFOB(fd,2+99) = (char_dat[i].status.agi > 255) ? 255 : char_dat[i].status.agi;
+ WFIFOB(fd,2+100) = (char_dat[i].status.vit > 255) ? 255 : char_dat[i].status.vit;
+ WFIFOB(fd,2+101) = (char_dat[i].status.int_ > 255) ? 255 : char_dat[i].status.int_;
+ WFIFOB(fd,2+102) = (char_dat[i].status.dex > 255) ? 255 : char_dat[i].status.dex;
+ WFIFOB(fd,2+103) = (char_dat[i].status.luk > 255) ? 255 : char_dat[i].status.luk;
+ WFIFOB(fd,2+104) = char_dat[i].status.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
+ FIFOSD_CHECK(46);
+
+ 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]].status.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]].status.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 (strcmpi(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 ((cs = &char_dat[sd->found_char[i]].status)->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].status.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: // ƒ}ƒbƒvƒT[ƒo[ƒƒOƒCƒ“
+ if (RFIFOREST(fd) < 60)
+ return 0;
+ WFIFOW(fd,0) = 0x2af9;
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ if (server_fd[i] < 0)
+ break;
+ }
+ if (i == MAX_MAP_SERVERS || strcmp((char*)RFIFOP(fd,2), userid) || strcmp((char*)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;
+ 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 0;
+ }
+ break;
+
+ case 0x187: // AliveM†H
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ 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 0;
+
+ case 0x7532: // Ú‘±‚ÌØ’f(default‚ƈ—‚͈ꂾ‚ª–¾Ž¦“I‚É‚·‚邽‚ß)
+ default:
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ RFIFOFLUSH(fd);
+ return 0;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)aCalloc(64,1);
+ command = (char *)aCalloc(64,1);
+
+// memset(type,0,64);
+// memset(command,0,64);
+
+ ShowStatus("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ ShowDebug("Type of command: %s || Command: %s \n",type,command);
+
+ if(buf) aFree(buf);
+ if(type) aFree(type);
+ if(command) aFree(command);
+
+ return 0;
+}
+
+// ‘S‚Ä‚ÌMAPƒT[ƒo[‚Ƀf[ƒ^‘—Mi‘—M‚µ‚½mapŽI‚Ì”‚ð•Ô‚·j
+int mapif_sendall(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) {
+ if (session[fd] == NULL)
+ { //Could this be the crash's source? [Skotlex]
+ ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i);
+ server_fd[i] = -1;
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ continue;
+ }
+ WFIFOHEAD(fd, len);
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ c++;
+ }
+ }
+ return c;
+}
+
+// Ž©•ªˆÈŠO‚Ì‘S‚Ä‚ÌMAPƒT[ƒo[‚Ƀf[ƒ^‘—Mi‘—M‚µ‚½mapŽI‚Ì”‚ð•Ô‚·j
+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) {
+ WFIFOHEAD(fd, len);
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd, len);
+ c++;
+ }
+ }
+ return c;
+}
+// MAPƒT[ƒo[‚Ƀf[ƒ^‘—MimapŽI¶‘¶Šm”F—L‚èj
+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]) {
+ WFIFOHEAD(fd, len);
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int send_users_tologin(int tid, unsigned int tick, int id, int data) {
+ int users = count_users();
+ unsigned char buf[16];
+
+ if (login_fd > 0 && session[login_fd]) {
+ // send number of user to login server
+ WFIFOHEAD(login_fd, 6);
+ 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);
+
+ return 0;
+}
+
+static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) {
+ struct online_char_data* character = (struct online_char_data*)data;
+ int *i = va_arg(ap, int*);
+ int count = va_arg(ap, int);
+ if ((*i) >= count)
+ return 0; //This is an error that shouldn't happen....
+ if(character->server > -1) {
+ WFIFOHEAD(login_fd, 8+count*4);
+ WFIFOL(login_fd, 8+(*i)*4) =character->account_id;
+ (*i)++;
+ return 1;
+ }
+ return 0;
+}
+
+int send_accounts_tologin(int tid, unsigned int tick, int id, int data) {
+ int users = count_users(), i=0;
+
+ if (login_fd > 0 && session[login_fd]) {
+ // send account list to login server
+ WFIFOHEAD(login_fd, 8+users*4);
+ WFIFOW(login_fd,0) = 0x272d;
+ WFIFOL(login_fd,4) = users;
+ online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i);
+ WFIFOW(login_fd,2) = 8+ i*4;
+ if (i > 0)
+ WFIFOSET(login_fd,WFIFOW(login_fd,2));
+ }
+ return 0;
+}
+
+int check_connect_login_server(int tid, unsigned int tick, int id, int data) {
+ if (login_fd <= 0 || session[login_fd] == NULL) {
+ ShowInfo("Attempt to connect to login-server...\n");
+ login_fd = make_connection(login_ip, login_port);
+ if (login_fd == -1)
+ { //Try again later... [Skotlex]
+ login_fd = 0;
+ return 0;
+ }
+ session[login_fd]->func_parse = parse_tologin;
+ realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+ WFIFOHEAD(login_fd, 86);
+ 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_display; //only display (New) if they want to [Kevin]
+
+ WFIFOSET(login_fd,86);
+ }
+ return 0;
+}
+
+//------------------------------------------------
+//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't
+//replies/disconnect the player we tried to kick. [Skotlex]
+//------------------------------------------------
+static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data)
+{
+ struct online_char_data* character;
+ if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect)
+ { //Mark it offline due to timeout.
+ set_char_offline(character->char_id, character->account_id);
+ }
+ return 0;
+}
+
+//----------------------------------------------------------
+// Return numerical value of a switch configuration by [Yor]
+// on/off, english, français, deutsch, español
+//----------------------------------------------------------
+int config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+
+ return atoi(str);
+}
+
+//-------------------------------------------
+// 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) {
+ ShowError("LAN support configuration file not found: %s\n", lancfgName);
+ return 1;
+ }
+
+ ShowInfo("reading configuration file %s...\n", lancfgName);
+
+ 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((unsigned char *)w1);
+ remove_control_chars((unsigned char *)w2);
+ if (strcmpi(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;
+ }
+ ShowStatus("LAN IP of map-server: %s.\n", lan_map_ip);
+ } else if (strcmpi(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]);
+ }
+ ShowStatus("Sub-network of the map-server: %d.%d.%d.%d.\n", subneti[0], subneti[1], subneti[2], subneti[3]);
+ } else if (strcmpi(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]);
+ }
+ ShowStatus("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;
+ ShowInfo("LAN test of LAN IP of the map-server...\n");
+ if (lan_ip_check(p) == 0) {
+ ShowError(CL_RED" LAN IP of the map-server doesn't belong to the specified Sub-network."CL_RESET"\n");
+ }
+ }
+
+ ShowInfo("done reading %s.\n", lancfgName);
+
+ 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) {
+ ShowFatalError("Configuration file not found: %s.\n", cfgName);
+ exit(1);
+ }
+
+ ShowInfo("Reading 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)
+ continue;
+
+ remove_control_chars((unsigned char *)w1);
+ remove_control_chars((unsigned char *)w2);
+ if(strcmpi(w1,"timestamp_format") == 0) {
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "userid") == 0) {
+ memcpy(userid, w2, 24);
+ } else if (strcmpi(w1, "passwd") == 0) {
+ memcpy(passwd, w2, 24);
+ } else if (strcmpi(w1, "server_name") == 0) {
+ memcpy(server_name, w2, sizeof(server_name));
+ server_name[sizeof(server_name) - 1] = '\0';
+ ShowStatus("%s server has been initialized\n", w2);
+ } else if (strcmpi(w1, "wisp_server_name") == 0) {
+ if (strlen(w2) >= 4) {
+ memcpy(wisp_server_name, w2, sizeof(wisp_server_name));
+ wisp_server_name[sizeof(wisp_server_name) - 1] = '\0';
+ }
+ } else if (strcmpi(w1, "login_ip") == 0) {
+ login_ip_set_ = 1;
+ h = gethostbyname(w2);
+ if (h != NULL) {
+ ShowStatus("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 (strcmpi(w1, "login_port") == 0) {
+ login_port = atoi(w2);
+ } else if (strcmpi(w1, "char_ip") == 0) {
+ char_ip_set_ = 1;
+ h = gethostbyname(w2);
+ if (h != NULL) {
+ ShowStatus("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 (strcmpi(w1, "bind_ip") == 0) {
+ bind_ip_set_ = 1;
+ h = gethostbyname(w2);
+ if (h != NULL) {
+ ShowStatus("Character server binding 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(bind_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(bind_ip_str, w2, 16);
+ } else if (strcmpi(w1, "char_port") == 0) {
+ char_port = atoi(w2);
+ } else if (strcmpi(w1, "char_maintenance") == 0) {
+ char_maintenance = atoi(w2);
+ } else if (strcmpi(w1, "char_new") == 0) {
+ char_new = atoi(w2);
+ } else if (strcmpi(w1, "char_new_display") == 0) {
+ char_new_display = atoi(w2);
+ } else if (strcmpi(w1, "email_creation") == 0) {
+ email_creation = config_switch(w2);
+ } else if (strcmpi(w1, "char_txt") == 0) {
+ strcpy(char_txt, w2);
+ } else if (strcmpi(w1, "scdata_txt") == 0) { //By Skotlex
+ strcpy(scdata_txt, w2);
+ } else if (strcmpi(w1, "backup_txt") == 0) { //By zanetheinsane
+ strcpy(backup_txt, w2);
+ } else if (strcmpi(w1, "friends_txt") == 0) { //By davidsiaw
+ strcpy(friends_txt, w2);
+ } else if (strcmpi(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 (strcmpi(w1, "max_connect_user") == 0) {
+ max_connect_user = atoi(w2);
+ if (max_connect_user < 0)
+ max_connect_user = 0; // unlimited online players
+ } else if(strcmpi(w1, "gm_allow_level") == 0) {
+ gm_allow_level = atoi(w2);
+ if(gm_allow_level < 0)
+ gm_allow_level = 99;
+ } else if (strcmpi(w1, "check_ip_flag") == 0) {
+ check_ip_flag = config_switch(w2);
+ } else if (strcmpi(w1, "online_check") == 0) {
+ online_check = config_switch(w2);
+ } else if (strcmpi(w1, "autosave_time") == 0) {
+ autosave_interval = atoi(w2)*1000;
+ if (autosave_interval <= 0)
+ autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+ } else if (strcmpi(w1, "save_log") == 0) {
+ save_log = config_switch(w2);
+ } else if (strcmpi(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
+ start_point.map = mapindex_name2id(map);
+ if (!start_point.map) {
+ ShowError("Specified start_point %s not found in map-index cache.\n", map);
+ start_point.map = 0;
+ }
+ start_point.x = x;
+ start_point.y = y;
+ }
+ } else if(strcmpi(w1,"log_char")==0) { //log char or not [devil]
+ log_char = atoi(w2);
+ } else if (strcmpi(w1, "start_zeny") == 0) {
+ start_zeny = atoi(w2);
+ if (start_zeny < 0)
+ start_zeny = 0;
+ } else if (strcmpi(w1, "start_weapon") == 0) {
+ start_weapon = atoi(w2);
+ if (start_weapon < 0)
+ start_weapon = 0;
+ } else if (strcmpi(w1, "start_armor") == 0) {
+ start_armor = atoi(w2);
+ if (start_armor < 0)
+ start_armor = 0;
+ } else if (strcmpi(w1, "unknown_char_name") == 0) {
+ strcpy(unknown_char_name, w2);
+ unknown_char_name[NAME_LENGTH-1] = '\0';
+ } else if (strcmpi(w1, "char_log_filename") == 0) {
+ strcpy(char_log_filename, w2);
+ } else if (strcmpi(w1, "name_ignoring_case") == 0) {
+ name_ignoring_case = config_switch(w2);
+ } else if (strcmpi(w1, "char_name_option") == 0) {
+ char_name_option = atoi(w2);
+ } else if (strcmpi(w1, "char_name_letters") == 0) {
+ strcpy(char_name_letters, w2);
+// online files options
+ } else if (strcmpi(w1, "online_txt_filename") == 0) {
+ strcpy(online_txt_filename, w2);
+ } else if (strcmpi(w1, "online_html_filename") == 0) {
+ strcpy(online_html_filename, w2);
+ } else if (strcmpi(w1, "online_sorting_option") == 0) {
+ online_sorting_option = atoi(w2);
+ } else if (strcmpi(w1, "online_display_option") == 0) {
+ online_display_option = atoi(w2);
+ } else if (strcmpi(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 (strcmpi(w1, "online_refresh_html") == 0) {
+ online_refresh_html = atoi(w2);
+ if (online_refresh_html < 1)
+ online_refresh_html = 1;
+ } else if(strcmpi(w1,"db_path")==0) {
+ strcpy(db_path,w2);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ } else if (strcmpi(w1, "import") == 0) {
+ char_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ ShowInfo("done reading %s.\n", cfgName);
+ return 0;
+}
+
+int chardb_final(int key, void* data, va_list va)
+{
+ aFree(data);
+ return 0;
+}
+void do_final(void) {
+ ShowStatus("Terminating server.\n");
+ // write online players files with no player
+ online_char_db->clear(online_char_db, NULL); //clean the db...
+ create_online_files();
+ online_char_db->destroy(online_char_db, NULL); //dispose the db...
+
+ mmo_char_sync();
+ inter_save();
+ set_all_offline();
+
+ if(gm_account) aFree(gm_account);
+ if(char_dat) aFree(char_dat);
+
+ delete_session(login_fd);
+ delete_session(char_fd);
+
+#ifdef ENABLE_SC_SAVING
+ status_final();
+#endif
+ inter_final();
+ mapindex_final();
+
+ char_log("----End of char-server (normal end with closing of all files)." RETCODE);
+}
+
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_CHAR;
+}
+
+static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
+{
+ struct online_char_data *character= (struct online_char_data*)data;
+ if (character->server == -2) //Unknown server.. set them offline
+ set_char_offline(character->char_id, character->account_id);
+ if (character->server < 0)
+ //Free data from players that have not been online for a while.
+ db_remove(online_char_db, key);
+ return 0;
+}
+
+static int online_data_cleanup(int tid, unsigned int tick, int id, int data)
+{
+ online_char_db->foreach(online_char_db, online_data_cleanup_sub);
+ return 0;
+}
+
+int do_init(int argc, char **argv) {
+ int i;
+
+ mapindex_init(); //Needed here for the start-point reading.
+ start_point.map = mapindex_name2id("new_1-1.gat");
+ char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]);
+ lan_config_read((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME);
+
+ // a newline in the log...
+ char_log("");
+ // moved behind char_config_read in case we changed the filename [celest]
+ char_log("The char-server starting..." RETCODE);
+
+ if ((naddr_ != 0) && (login_ip_set_ == 0 || char_ip_set_ == 0)) {
+ // The char server should know what IP address it is running on
+ // - MouseJstr
+ int localaddr = ntohl(addr_[0]);
+ unsigned char *ptr = (unsigned char *) &localaddr;
+ char buf[16];
+ sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);;
+ if (naddr_ != 1)
+ ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ ShowStatus("Defaulting to %s as our IP address\n", buf);
+ if (login_ip_set_ == 0)
+ strcpy(login_ip_str, buf);
+ if (char_ip_set_ == 0)
+ strcpy(char_ip_str, buf);
+
+ if (ptr[0] == 192 && ptr[1] == 168)
+ ShowWarning("Firewall detected.. edit lan_support.conf and char_athena.conf\n");
+ }
+
+ 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;
+ }
+
+ online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ mmo_char_init();
+#ifdef ENABLE_SC_SAVING
+ status_init();
+#endif
+ 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_defaultparse(parse_char);
+
+ if (bind_ip_set_)
+ char_fd = make_listen_bind(inet_addr(bind_ip_str),char_port);
+ else
+ char_fd = make_listen_bind(INADDR_ANY,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(send_accounts_tologin, "send_accounts_tologin");
+ add_timer_func_list(mmo_char_sync_timer, "mmo_char_sync_timer");
+ add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect");
+ add_timer_func_list(online_data_cleanup, "online_data_cleanup");
+
+ add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000);
+ add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
+ add_timer_interval(gettick() + 1000, send_users_tologin, 0, 0, 5 * 1000);
+ add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600*1000); //Sync online accounts every hour
+ add_timer_interval(gettick() + autosave_interval, mmo_char_sync_timer, 0, 0, autosave_interval);
+
+ if(console) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ char_log("The char-server is ready (Server is listening on the port %d)." RETCODE, char_port);
+
+ ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (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
new file mode 100644
index 000000000..743890c68
--- /dev/null
+++ b/src/char/char.h
@@ -0,0 +1,45 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHAR_H_
+#define _CHAR_H_
+
+#include "../common/mmo.h"
+#include "../common/mapindex.h"
+
+#define START_CHAR_NUM 150000
+#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;
+ unsigned short map[MAX_MAP_PER_SERVER];
+};
+
+int search_character_index(char* character_name);
+char * search_character_name(int index);
+
+int mapif_sendall(unsigned 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_married(int pl1,int pl2);
+int char_child(int parent_id, int child_id);
+
+int char_log(char *fmt, ...);
+
+int request_accreg2(int account_id, int char_id);
+int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int len);
+int save_accreg2(unsigned char *buf, int len);
+int char_account_reg_reply(int fd,int account_id,int char_id);
+extern int autosave_interval;
+extern char db_path[];
+
+#endif
diff --git a/src/char/int_guild.c b/src/char/int_guild.c
new file mode 100644
index 000000000..a1f4d418e
--- /dev/null
+++ b/src/char/int_guild.c
@@ -0,0 +1,1530 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/mmo.h"
+#include "../common/socket.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+#include "char.h"
+#include "inter.h"
+#include "int_storage.h"
+#include "int_guild.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);
+int guild_break_sub(DBKey key, void *data, va_list ap);
+
+// ƒMƒ‹ƒhƒf[ƒ^‚Ì•¶Žš—ñ‚Ö‚Ì•ÏŠ·
+int inter_guild_tostr(char *str, struct guild *g) {
+ int i, c, len;
+
+ // Šî–{ƒf[ƒ^
+ 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);
+ // ƒƒ“ƒo[
+ 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->class_, m->lv, m->exp, m->exp_payper, m->position,
+ ((m->account_id > 0) ? m->name : "-"));
+ }
+ // –ðE
+ 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);
+ }
+ // ƒGƒ“ƒuƒŒƒ€
+ 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");
+ // “¯–¿ƒŠƒXƒg
+ 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);
+ }
+ // ’Ç•úƒŠƒXƒg
+ 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 );
+ }
+ // ƒMƒ‹ƒhƒXƒLƒ‹
+ 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;
+}
+
+// ƒMƒ‹ƒhƒf[ƒ^‚Ì•¶Žš—ñ‚©‚ç‚Ì•ÏŠ·
+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;
+
+ // Šî–{ƒf[ƒ^
+ 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], NAME_LENGTH-1);
+ memcpy(g->master, tmp_str[1], NAME_LENGTH-1);
+ 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++) // ˆÊ’uƒXƒLƒbƒv
+ str = strchr(str + 1, '\t');
+// printf("GuildBaseInfo OK\n");
+
+ // ƒƒ“ƒo[
+ 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 = tmp_int[1];
+ m->hair = tmp_int[2];
+ m->hair_color = tmp_int[3];
+ m->gender = tmp_int[4];
+ m->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], NAME_LENGTH-1);
+
+ for(j = 0; j < 2 && str != NULL; j++) // ˆÊ’uƒXƒLƒbƒv
+ str = strchr(str + 1, '\t');
+ }
+// printf("GuildMemberInfo OK\n");
+ // –ðE
+ 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], NAME_LENGTH-1);
+
+ for(j = 0; j < 2 && str != NULL; j++) // ˆÊ’uƒXƒLƒbƒv
+ str = strchr(str+1, '\t');
+ i++;
+ }
+// printf("GuildPositionInfo OK\n");
+ // ƒGƒ“ƒuƒŒƒ€
+ 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'); // ˆÊ’uƒXƒLƒbƒv
+
+ // “¯–¿ƒŠƒXƒg
+ if (sscanf(str + 1, "%d\t", &c) < 1)
+ return 1;
+ str = strchr(str + 1, '\t'); // ˆÊ’uƒXƒLƒbƒv
+ 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], NAME_LENGTH-1);
+
+ for(j = 0; j < 2 && str != NULL; j++) // ˆÊ’uƒXƒLƒbƒv
+ str = strchr(str + 1, '\t');
+ }
+// printf("GuildAllianceInfo OK\n");
+ // ’Ç•úƒŠƒXƒg
+ if (sscanf(str+1, "%d\t", &c) < 1)
+ return 1;
+ str = strchr(str + 1, '\t'); // ˆÊ’uƒXƒLƒbƒv
+ 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], NAME_LENGTH-1);
+ 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++) // ˆÊ’uƒXƒLƒbƒv
+ str = strchr(str + 1, '\t');
+ }
+// printf("GuildExplusionInfo OK\n");
+ // ƒMƒ‹ƒhƒXƒLƒ‹
+ 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;
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^‚Ì•¶Žš—ñ‚Ö‚Ì•ÏŠ·
+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->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible,
+ gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible,
+ gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp,
+ gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^‚Ì•¶Žš—ñ‚©‚ç‚Ì•ÏŠ·
+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->guardian[0].visible = tmp_int[10];
+ gc->guardian[1].visible = tmp_int[11];
+ gc->guardian[2].visible = tmp_int[12];
+ gc->guardian[3].visible = tmp_int[13];
+ gc->guardian[4].visible = tmp_int[14];
+ gc->guardian[5].visible = tmp_int[15];
+ gc->guardian[6].visible = tmp_int[16];
+ gc->guardian[7].visible = tmp_int[17];
+ gc->guardian[0].hp = tmp_int[18];
+ gc->guardian[1].hp = tmp_int[19];
+ gc->guardian[2].hp = tmp_int[20];
+ gc->guardian[3].hp = tmp_int[21];
+ gc->guardian[4].hp = tmp_int[22];
+ gc->guardian[5].hp = tmp_int[23];
+ gc->guardian[6].hp = tmp_int[24];
+ gc->guardian[7].hp = 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) {
+ int i;
+
+ 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->guardian[0].visible = tmp_int[10];
+ gc->guardian[1].visible = tmp_int[11];
+ gc->guardian[2].visible = tmp_int[12];
+ gc->guardian[3].visible = tmp_int[13];
+ gc->guardian[4].visible = tmp_int[14];
+ gc->guardian[5].visible = tmp_int[15];
+ gc->guardian[6].visible = tmp_int[16];
+ gc->guardian[7].visible = tmp_int[17];
+
+ for (i = 0; i < MAX_GUARDIANS; i++)
+ {
+ if (gc->guardian[i].visible)
+ gc->guardian[i].hp = 15000 + 2000 * gc->defense;
+ else
+ gc->guardian[i].hp = 0;
+ }
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhŠÖ˜Aƒf[ƒ^ƒx[ƒX“Ç‚Ýž‚Ý
+int inter_guild_readdb(void) {
+ int i;
+ FILE *fp;
+ char line[1024];
+ char path[1024];
+
+ sprintf(path, "%s%s", db_path, "/exp_guild.txt");
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ ShowError("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;
+}
+
+// ƒMƒ‹ƒhƒf[ƒ^‚Ì“Ç‚Ýž‚Ý
+int inter_guild_init() {
+ char line[16384];
+ struct guild *g;
+ struct guild_castle *gc;
+ FILE *fp;
+ int i, j, c = 0;
+
+ inter_guild_readdb();
+
+ guild_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ castle_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ 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;
+ }
+
+ g = (struct guild *) aCalloc(sizeof(struct guild), 1);
+ if(g == NULL){
+ ShowFatalError("int_guild: out of memory!\n");
+ exit(0);
+ }
+// memset(g, 0, sizeof(struct guild)); not needed...
+ if (inter_guild_fromstr(line, g) == 0 && g->guild_id > 0) {
+ if (g->guild_id >= guild_newid)
+ guild_newid = g->guild_id + 1;
+ idb_put(guild_db, g->guild_id, g);
+ guild_check_empty(g);
+ guild_calcinfo(g);
+ } else {
+ ShowError("int_guild: broken data [%s] line %d\n", guild_txt, c);
+ aFree(g);
+ }
+ c++;
+ }
+ fclose(fp);
+// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c);
+
+ c = 0;//ƒJƒEƒ“ƒ^‰Šú‰»
+
+ if ((fp = fopen(castle_txt, "r")) == NULL) {
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line)-1, fp)) {
+ gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1);
+ if(gc == NULL){
+ ShowFatalError("int_guild: out of memory!\n");
+ exit(0);
+ }
+// memset(gc, 0, sizeof(struct guild_castle)); No need...
+ if (inter_guildcastle_fromstr(line, gc) == 0) {
+ idb_put(castle_db, gc->castle_id, gc);
+ } else {
+ ShowError("int_guild: broken data [%s] line %d\n", castle_txt, c);
+ aFree(gc);
+ }
+ c++;
+ }
+
+ if (!c) {
+ ShowStatus(" %s - making Default Data...\n", castle_txt);
+ //ƒfƒtƒHƒ‹ƒgƒf[ƒ^‚ðì¬
+ for(i = 0; i < MAX_GUILDCASTLE; i++) {
+ gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1);
+ if (gc == NULL) {
+ ShowFatalError("int_guild: out of memory!\n");
+ exit(0);
+ }
+ gc->castle_id = i;
+ idb_put(castle_db, gc->castle_id, gc);
+ }
+ ShowStatus(" %s - making done\n",castle_txt);
+ return 0;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+void inter_guild_final() {
+ castle_db->destroy(castle_db, NULL);
+ guild_db->destroy(guild_db, NULL);
+ return;
+}
+
+struct guild *inter_guild_search(int guild_id) {
+ return idb_get(guild_db, guild_id);
+}
+
+// ƒMƒ‹ƒhƒf[ƒ^‚̃Z[ƒu—p
+int inter_guild_save_sub(DBKey key,void *data,va_list ap) {
+ char line[16384];
+ FILE *fp;
+
+ inter_guild_tostr(line,(struct guild *)data);
+ fp=va_arg(ap,FILE *);
+ fprintf(fp,"%s" RETCODE,line);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^‚̃Z[ƒu—p
+int inter_castle_save_sub(DBKey key, void *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" RETCODE, line);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒf[ƒ^‚̃Z[ƒu
+int inter_guild_save() {
+ FILE *fp;
+ int lock;
+
+ if ((fp = lock_fopen(guild_txt, &lock)) == NULL) {
+ ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", guild_txt);
+ return 1;
+ }
+ guild_db->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) {
+ ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", castle_txt);
+ return 1;
+ }
+ castle_db->foreach(castle_db, inter_castle_save_sub, fp);
+ lock_fclose(fp, castle_txt, &lock);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒh–¼ŒŸõ—p
+int search_guildname_sub(DBKey key, void *data, va_list ap) {
+ struct guild *g = (struct guild *)data, **dst;
+ char *str;
+
+ str = va_arg(ap, char *);
+ dst = va_arg(ap, struct guild **);
+ if (strcmpi(g->name, str) == 0)
+ *dst = g;
+ return 0;
+}
+
+// ƒMƒ‹ƒh–¼ŒŸõ
+struct guild* search_guildname(char *str) {
+ struct guild *g = NULL;
+ guild_db->foreach(guild_db, search_guildname_sub, str, &g);
+ return g;
+}
+
+// ƒMƒ‹ƒh‚ª‹ó‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+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;
+ }
+ }
+ // ’N‚à‚¢‚È‚¢‚̂ʼnðŽU
+ guild_db->foreach(guild_db, guild_break_sub, g->guild_id);
+ inter_guild_storage_delete(g->guild_id);
+ mapif_guild_broken(g->guild_id, 0);
+ idb_remove(guild_db, g->guild_id);
+ return 1;
+}
+
+// ƒLƒƒƒ‰‚Ì‹£‡‚ª‚È‚¢‚©ƒ`ƒFƒbƒN—p
+int guild_check_conflict_sub(DBKey key, void *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 0;
+
+ for(i = 0; i < MAX_GUILD; i++) {
+ if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) {
+ // •Ê‚̃Mƒ‹ƒh‚É‹U‚ÌŠ‘®ƒf[ƒ^‚ª‚ ‚é‚Ì‚Å’E‘Þ
+ ShowWarning("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, char_id, 0, "**ƒf[ƒ^‹£‡**");
+ }
+ }
+
+ return 0;
+}
+// ƒLƒƒƒ‰‚Ì‹£‡‚ª‚È‚¢‚©ƒ`ƒFƒbƒN
+int guild_check_conflict(int guild_id, int account_id, int char_id) {
+ guild_db->foreach(guild_db, guild_check_conflict_sub, guild_id, account_id, char_id);
+
+ return 0;
+}
+
+int guild_nextexp (int level)
+{
+ if (level == 0)
+ return 1;
+ if (level > 0 && level < 100)
+ return guild_exp[level-1];
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒXƒLƒ‹‚ª‚ ‚é‚©Šm”F
+int guild_checkskill(struct guild *g, int id) {
+ int idx = id - GD_SKILLBASE;
+
+
+ if(idx < 0 || idx >= MAX_GUILDSKILL)
+
+ return 0;
+
+ return g->skill[idx].lv;
+}
+
+// ƒMƒ‹ƒh‚Ìî•ñ‚ÌÄŒvŽZ
+int guild_calcinfo(struct guild *g) {
+ int i, c, nextexp;
+ struct guild before = *g;
+
+ // ƒXƒLƒ‹ID‚ÌÝ’è
+ for(i = 0; i < MAX_GUILDSKILL; i++)
+ g->skill[i].id=i+GD_SKILLBASE;
+
+ // ƒMƒ‹ƒhƒŒƒxƒ‹
+ if (g->guild_lv <= 0)
+ g->guild_lv = 1;
+ nextexp = guild_nextexp(g->guild_lv);
+ if (nextexp > 0) {
+ while(g->exp >= nextexp && nextexp > 0) { //fixed guild exp overflow [Kevin]
+ g->exp -= nextexp;
+ g->guild_lv++;
+ g->skill_point++;
+ nextexp = guild_nextexp(g->guild_lv);
+ }
+ }
+
+ // ƒMƒ‹ƒh‚ÌŽŸ‚ÌŒoŒ±’l
+ g->next_exp = guild_nextexp(g->guild_lv);
+
+ // ƒƒ“ƒoãŒÀiƒMƒ‹ƒhŠg’£“K—pj
+ g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; //Guild Extention skill - currently adds 6 to max per skill lv.
+ if(g->max_member > MAX_GUILD)
+ {
+ ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD);
+ g->max_member = MAX_GUILD;
+ }
+
+ // •½‹ÏƒŒƒxƒ‹‚ƃIƒ“ƒ‰ƒCƒ“l”
+ 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++;
+ }
+ }
+ if(c) g->average_lv /= c;
+
+ // ‘Sƒf[ƒ^‚ð‘—‚é•K—v‚ª‚ ‚è‚»‚¤
+ 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‚Ö‚Ì’ÊM
+
+// ƒMƒ‹ƒh쬉”Û
+int mapif_guild_created(int fd, int account_id, struct guild *g) {
+ WFIFOHEAD(fd, 10);
+ WFIFOW(fd,0) = 0x3830;
+ WFIFOL(fd,2) = account_id;
+ if (g != NULL) {
+ WFIFOL(fd,6) = g->guild_id;
+ ShowInfo("Created Guild (%d %s)\n", g->guild_id, g->name);
+ }else{
+ WFIFOL(fd,6) = 0;
+ }
+ WFIFOSET(fd,10);
+ return 0;
+}
+
+// ƒMƒ‹ƒhî•ñŒ©‚‚©‚炸
+int mapif_guild_noinfo(int fd, int guild_id) {
+ WFIFOHEAD(fd, 8);
+ WFIFOW(fd,0) = 0x3831;
+ WFIFOW(fd,2) = 8;
+ WFIFOL(fd,4) = guild_id;
+ WFIFOSET(fd,8);
+ ShowNotice("int_guild: info not found %d\n", guild_id);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhî•ñ‚Ü‚Æ‚ß‘—‚è
+int mapif_guild_info(int fd, struct guild *g) {
+ unsigned char buf[8+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;
+}
+
+// ƒƒ“ƒo’ljÁ‰Â”Û
+int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) {
+ WFIFOHEAD(fd, 15);
+ WFIFOW(fd,0) = 0x3832;
+ WFIFOL(fd,2) = guild_id;
+ WFIFOL(fd,6) = account_id;
+ WFIFOL(fd,10) = char_id;
+ WFIFOB(fd,14) = flag;
+ WFIFOSET(fd, 15);
+
+ return 0;
+}
+
+// ’E‘Þ/’Ç•ú’Ê’m
+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) = char_id;
+ WBUFB(buf,14) = flag;
+ memcpy(WBUFP(buf,15), mes, 40);
+ memcpy(WBUFP(buf,55), name, NAME_LENGTH);
+ mapif_sendall(buf, 55+NAME_LENGTH);
+// mapif_sendall(buf, 79);
+ ShowInfo("Character left guild (Guild %d, %d - %s: %s)\n", guild_id, account_id, name, mes);
+
+ return 0;
+}
+
+// ƒIƒ“ƒ‰ƒCƒ“ó‘Ô‚ÆLvXV’Ê’m
+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) = g->member[idx].char_id;
+ WBUFB(buf,14) = (unsigned char)g->member[idx].online;
+ WBUFW(buf,15) = g->member[idx].lv;
+ WBUFW(buf,17) = g->member[idx].class_;
+ mapif_sendall(buf, 19);
+ return 0;
+}
+
+// ‰ðŽU’Ê’m
+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);
+ ShowInfo("Guild Break (%d)\n", guild_id);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒh“à”­Œ¾
+int mapif_guild_message(int guild_id, int account_id, char *mes, int len, int sfd) {
+ unsigned char buf[2048];
+
+ 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_sendallwos(sfd, buf, len + 12);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhŠî–{î•ñ•ÏX’Ê’m
+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;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒoî•ñ•ÏX’Ê’m
+int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) {
+ unsigned char buf[4096];
+
+ WBUFW(buf, 0) = 0x383a;
+ WBUFW(buf, 2) = len + 18;
+ WBUFL(buf, 4) = guild_id;
+ WBUFL(buf, 8) = account_id;
+ WBUFL(buf,12) = char_id;
+ WBUFW(buf,16) = type;
+ memcpy(WBUFP(buf,18), data, len);
+ mapif_sendall(buf,len+18);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒXƒLƒ‹ƒAƒbƒv’Ê’m
+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;
+}
+
+// ƒMƒ‹ƒh“¯–¿/“G‘Î’Ê’m
+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, NAME_LENGTH);
+ memcpy(WBUFP(buf,19+NAME_LENGTH), name2, NAME_LENGTH);
+ mapif_sendall(buf,19+2*NAME_LENGTH);
+/*
+ memcpy(WBUFP(buf,43), name2, NAME_LENGTH);
+ mapif_sendall(buf, 67);
+*/
+ return 0;
+}
+
+// ƒMƒ‹ƒh–ðE•ÏX’Ê’m
+int mapif_guild_position(struct guild *g, int idx) {
+ unsigned char buf[2048];
+
+ 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;
+}
+
+// ƒMƒ‹ƒh’m•ÏX’Ê’m
+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;
+}
+
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX’Ê’m
+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_master_changed(struct guild *g, int position)
+{
+ unsigned char buf[12];
+ WBUFW(buf,0)=0x3843;
+ WBUFL(buf,2)=g->guild_id;
+ WBUFL(buf,6)=position;
+ mapif_sendall(buf,10);
+ 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;
+}
+
+int mapif_guild_castle_alldataload_sub(DBKey key, void *data, va_list ap) {
+ int fd = va_arg(ap, int);
+ int *p = va_arg(ap, int*);
+
+ WFIFOHEAD(fd, sizeof(struct guild_castle));
+ memcpy(WFIFOP(fd,*p), (struct guild_castle*)data, sizeof(struct guild_castle));
+ (*p) += sizeof(struct guild_castle);
+
+ return 0;
+}
+
+int mapif_guild_castle_alldataload(int fd) {
+ int len = 4;
+
+ WFIFOHEAD(fd, 0);
+ WFIFOW(fd,0) = 0x3842;
+ castle_db->foreach(castle_db, mapif_guild_castle_alldataload_sub, fd, &len);
+ WFIFOW(fd,2) = len;
+ WFIFOSET(fd, len);
+
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// map server‚©‚ç‚Ì’ÊM
+
+// ƒMƒ‹ƒh쬗v‹
+int mapif_parse_CreateGuild(int fd, int account_id, char *name, struct guild_member *master) {
+ struct guild *g;
+ int i;
+
+ for(i = 0; i < NAME_LENGTH && name[i]; i++) {
+ if (!(name[i] & 0xe0) || name[i] == 0x7f) {
+ ShowInfo("Create Guild: illegal guild name [%s]\n", name);
+ mapif_guild_created(fd, account_id, NULL);
+ return 0;
+ }
+ }
+
+ if ((g = search_guildname(name)) != NULL) {
+ ShowInfo("Create Guild: same name guild exists [%s]\n", name);
+ mapif_guild_created(fd, account_id, NULL);
+ return 0;
+ }
+ g = (struct guild *) aCalloc(sizeof(struct guild), 1);
+ if (g == NULL) {
+ ShowFatalError("int_guild: CreateGuild: out of memory !\n");
+ mapif_guild_created(fd, account_id, NULL);
+ exit(0);
+ }
+// memset(g, 0, sizeof(struct guild)); Meh...
+ g->guild_id = guild_newid++;
+ memcpy(g->name, name, NAME_LENGTH-1);
+ memcpy(g->master, master->name, NAME_LENGTH-1);
+ 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);
+
+ // ‚±‚±‚ŃMƒ‹ƒhî•ñŒvŽZ‚ª•K—v‚ÆŽv‚í‚ê‚é
+ g->max_member = 16;
+ g->average_lv = master->lv;
+ for(i = 0; i < MAX_GUILDSKILL; i++)
+ g->skill[i].id=i + GD_SKILLBASE;
+
+ idb_put(guild_db, g->guild_id, g);
+
+ mapif_guild_created(fd, account_id, g);
+ mapif_guild_info(fd, g);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE,
+ name, g->guild_id, master->name, master->account_id);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhî•ñ—v‹
+int mapif_parse_GuildInfo(int fd, int guild_id) {
+ struct guild *g;
+
+ g = idb_get(guild_db, guild_id);
+ if (g != NULL){
+ guild_calcinfo(g);
+ mapif_guild_info(fd, g);
+ } else
+ mapif_guild_noinfo(fd, guild_id);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒo’ljÁ—v‹
+int mapif_parse_GuildAddMember(int fd, int guild_id, struct guild_member *m) {
+ struct guild *g;
+ int i;
+
+ g = idb_get(guild_db, guild_id);
+ if (g == NULL) {
+ mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1);
+ return 0;
+ }
+
+ for(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, m->char_id, 0);
+ guild_calcinfo(g);
+ mapif_guild_info(-1, g);
+
+ return 0;
+ }
+ }
+ mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒh’E‘Þ/’Ç•ú—v‹
+int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes) {
+ struct guild *g = NULL;
+ int i, j;
+
+ g = idb_get(guild_db, guild_id);
+ if (g != NULL) {
+ for(i = 0; i < MAX_GUILD; i++) {
+ if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) {
+// printf("%d %d\n", i, (int)(&g->member[i]));
+// printf("%d %s\n", i, g->member[i].name);
+
+ if (flag) { // ’Ç•ú‚ÌꇒǕúƒŠƒXƒg‚É“ü‚ê‚é
+ for(j = 0; j < MAX_GUILDEXPLUSION; j++) {
+ if (g->explusion[j].account_id == 0)
+ break;
+ }
+ if (j == MAX_GUILDEXPLUSION) { // ˆê”t‚Ȃ̂Ō¢‚Ì‚ðÁ‚·
+ 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", NAME_LENGTH-1);
+ memcpy(g->explusion[j].name, g->member[i].name, NAME_LENGTH-1);
+ memcpy(g->explusion[j].mes, mes, 40);
+ }
+
+ mapif_guild_leaved(guild_id, account_id, 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);// ‚Ü‚¾l‚ª‚¢‚é‚̂Ńf[ƒ^‘—M
+
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+// ƒIƒ“ƒ‰ƒCƒ“/LvXV
+int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int class_) {
+ struct guild *g;
+ int i, alv, c;
+
+ g = idb_get(guild_db, guild_id);
+ if (g == NULL)
+ return 0;
+
+ g->connect_member = 0;
+
+ alv = 0;
+ c = 0;
+ for(i = 0; i < MAX_GUILD; i++) {
+ if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) {
+ g->member[i].online = online;
+ g->member[i].lv = lv;
+ g->member[i].class_ = 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++;
+ }
+
+ if (c)
+ // •½‹ÏƒŒƒxƒ‹
+ g->average_lv = alv / c;
+
+ return 0;
+}
+
+// ƒMƒ‹ƒh‰ðŽUˆ——pi“¯–¿/“G‘΂ð‰ðœj
+int guild_break_sub(DBKey key, void *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;
+ }
+ return 0;
+}
+
+// ƒMƒ‹ƒh‰ðŽU—v‹
+int mapif_parse_BreakGuild(int fd, int guild_id) {
+ struct guild *g;
+
+ g = idb_get(guild_db, guild_id);
+ if(g == NULL)
+ return 0;
+
+ guild_db->foreach(guild_db, guild_break_sub, guild_id);
+ inter_guild_storage_delete(guild_id);
+ mapif_guild_broken(guild_id, 0);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) broken" RETCODE, g->name, guild_id);
+
+ idb_remove(guild_db, guild_id);
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒbƒZ[ƒW‘—M
+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, fd);
+}
+
+// ƒMƒ‹ƒhŠî–{ƒf[ƒ^•ÏX—v‹
+int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const char *data, int len) {
+ struct guild *g;
+ short dw = *((short *)data);
+
+ g = idb_get(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:
+ ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n", type);
+ break;
+ }
+ mapif_guild_basicinfochanged(guild_id, type, data, len);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒoƒf[ƒ^•ÏX—v‹
+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;
+
+ g = idb_get(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 && g->member[i].char_id == char_id)
+ break;
+ if (i == g->max_member) {
+ ShowWarning("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: // –ðE
+ 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ƒAƒbƒv”»’f
+ mapif_guild_basicinfochanged(guild_id, GBI_EXP, &g->exp, 4);
+ break;
+ }
+ case GMI_HAIR:
+ {
+ g->member[i].hair=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ break;
+ }
+ case GMI_HAIR_COLOR:
+ {
+ g->member[i].hair_color=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ break;
+ }
+ case GMI_GENDER:
+ {
+ g->member[i].gender=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ break;
+ }
+ case GMI_CLASS:
+ {
+ g->member[i].class_=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ break;
+ }
+ case GMI_LEVEL:
+ {
+ g->member[i].lv=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ break;
+ }
+
+ default:
+ ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n", type);
+ break;
+ }
+ mapif_guild_memberinfochanged(guild_id, account_id, char_id, type, data, len);
+
+ return 0;
+}
+
+int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender)
+{
+ return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender));
+}
+
+// ƒMƒ‹ƒh–ðE–¼•ÏX—v‹
+int mapif_parse_GuildPosition(int fd, int guild_id, int idx, struct guild_position *p) {
+ struct guild *g = idb_get(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);
+ ShowInfo("int_guild: position [%d] changed\n", idx);
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒXƒLƒ‹ƒAƒbƒv—v‹
+int mapif_parse_GuildSkillUp(int fd, int guild_id, int skill_num, int account_id) {
+ struct guild *g = idb_get(guild_db, guild_id);
+ int idx = skill_num - GD_SKILLBASE;
+
+ if (g == NULL || idx < 0 || idx >= MAX_GUILDSKILL)
+ 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);
+ ShowInfo("int_guild: skill %d up\n", skill_num);
+ }
+
+ return 0;
+}
+
+//Manual deletion of an alliance when partnering guild does not exists. [Skotlex]
+static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag)
+{
+ int i;
+ char name[NAME_LENGTH];
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == guild_id)
+ {
+ strcpy(name, g->alliance[i].name);
+ g->alliance[i].guild_id=0;
+ break;
+ }
+ if (i == MAX_GUILDALLIANCE)
+ return -1;
+
+ mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name);
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿—v‹
+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] = idb_get(guild_db, guild_id1);
+ g[1] = idb_get(guild_db, guild_id2);
+
+ if(g[0] && g[1]==NULL && (flag&0x8)) //Requested to remove an alliance with a not found guild.
+ return mapif_parse_GuildDeleteAlliance(g[0], guild_id2,
+ account_id1, account_id2, flag); //Try to do a manual removal of said guild.
+
+ 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, NAME_LENGTH-1);
+ g[i]->alliance[j].opposition = flag & 1;
+ break;
+ }
+ }
+ } else { // ŠÖŒW‰ðÁ
+ 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;
+}
+
+// ƒMƒ‹ƒh’m•ÏX—v‹
+int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) {
+ struct guild *g;
+
+ g = idb_get(guild_db, guild_id);
+ if (g == NULL)
+ return 0;
+ memcpy(g->mes1, mes1, 60);
+ memcpy(g->mes2, mes2, 120);
+
+ return mapif_guild_notice(g);
+}
+
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX—v‹
+int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) {
+ struct guild *g;
+
+ g = idb_get(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 = idb_get(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:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-10].visible);
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-18].hp); // end additions [Valaris]
+
+ default:
+ ShowError("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= idb_get(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 = idb_get(guild_db, gid);
+ if(log_inter)
+ inter_log("guild %s (id=%d) %s castle id=%d" RETCODE,
+ (g) ? g->name : "??", gid, (value) ? "occupy" : "abandon", castle_id);
+ }
+ gc->guild_id = value;
+ break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break; // end additions [Valaris]
+ default:
+ ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+
+ return mapif_guild_castle_datasave(gc->castle_id, index, value);
+}
+
+// ƒMƒ‹ƒhƒ`ƒFƒbƒN—v‹
+int mapif_parse_GuildCheck(int fd, int guild_id, int account_id, int char_id) {
+ return guild_check_conflict(guild_id, account_id, char_id);
+}
+
+int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len)
+{
+ struct guild *g = idb_get(guild_db, guild_id);
+ struct guild_member gm;
+ int pos;
+
+ if(g==NULL || g->guild_id<=0 || len > NAME_LENGTH)
+ return 0;
+
+ for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++);
+
+ if (pos == g->max_member)
+ return 0; //Character not found??
+
+ memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
+ memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
+ memcpy(&g->member[0], &gm, sizeof(struct guild_member));
+
+ g->member[pos].position = g->member[0].position;
+ g->member[0].position = 0; //Position 0: guild Master.
+ strncpy(g->master, name, len);
+ if (len < NAME_LENGTH)
+ g->master[len] = '\0';
+
+ ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",name, guild_id, g->name);
+ return mapif_guild_master_changed(g, pos);
+}
+
+// map server ‚©‚ç‚Ì’ÊM
+// E‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// EƒpƒPƒbƒg’·ƒf[ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// EƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// EƒGƒ‰[‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_guild_parse_frommap(int fd) {
+ RFIFOHEAD(fd);
+ switch(RFIFOW(fd,0)) {
+ case 0x3030: mapif_parse_CreateGuild(fd, RFIFOL(fd,4), (char*)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 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break;
+ case 0x3034: mapif_parse_GuildLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), (const char*)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), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break;
+ case 0x3038: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break;
+ case 0x3039: mapif_parse_GuildBasicInfoChange(fd, RFIFOL(fd,4), RFIFOW(fd,8), (const char*)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), (const char*)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), (const char*)RFIFOP(fd,6), (const char*)RFIFOP(fd,66)); break;
+ case 0x303F: mapif_parse_GuildEmblem(fd, RFIFOW(fd,2)-12, RFIFOL(fd,4), RFIFOL(fd,8), (const char*)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;
+}
+
+// ƒ}ƒbƒvƒT[ƒo[‚ÌÚ‘±Žžˆ—
+int inter_guild_mapif_init(int fd) {
+ return mapif_guild_castle_alldataload(fd);
+}
+
+// ƒT[ƒo[‚©‚ç’E‘Þ—v‹iƒLƒƒƒ‰íœ—pj
+int inter_guild_leave(int guild_id, int account_id, int char_id) {
+ return mapif_parse_GuildLeave(-1, guild_id, account_id, char_id, 0, "** Character Deleted **");
+}
diff --git a/src/char/int_guild.h b/src/char/int_guild.h
new file mode 100644
index 000000000..0b0105af3
--- /dev/null
+++ b/src/char/int_guild.h
@@ -0,0 +1,19 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_GUILD_H_
+#define _INT_GUILD_H_
+
+int inter_guild_init(void);
+void inter_guild_final(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);
+int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender);
+
+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
new file mode 100644
index 000000000..1226da650
--- /dev/null
+++ b/src/char/int_party.c
@@ -0,0 +1,654 @@
+// 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/mmo.h"
+#include "../common/socket.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+#include "char.h"
+#include "inter.h"
+#include "int_party.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 char_id);
+
+// ƒp?ƒeƒBƒf?ƒ^‚Ì•¶Žš—ñ‚Ö‚Ì?Š·
+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,%d\t%s\t", m->account_id, m->char_id, m->leader, ((m->account_id > 0) ? m->name : "NoMember"));
+ }
+
+ return 0;
+}
+
+// ƒp?ƒeƒBƒf?ƒ^‚Ì•¶Žš—ñ‚©‚ç‚Ì?Š·
+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%255[^\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];
+ memcpy(p->name, tmp_str, NAME_LENGTH-1);
+ p->exp = tmp_int[1]?1:0;
+ 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,%d\t%255[^\t]\t", &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str) != 4)
+ return 1;
+
+ m->account_id = tmp_int[0];
+ m->char_id = tmp_int[1];
+ m->leader = tmp_int[2]?1:0;
+ memcpy(m->name, tmp_str, NAME_LENGTH-1);
+// 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;
+}
+
+// ƒp?ƒeƒBƒf?ƒ^‚̃?ƒh
+int inter_party_init() {
+ char line[8192];
+ struct party *p;
+ FILE *fp;
+ int c = 0;
+ int i, j;
+
+ party_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ 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;
+ }
+
+ p = (struct party*)aCalloc(sizeof(struct party), 1);
+ if (p == NULL){
+ ShowFatalError("int_party: out of memory!\n");
+ exit(0);
+ }
+ memset(p, 0, sizeof(struct party));
+ if (inter_party_fromstr(line, p) == 0 && p->party_id > 0) {
+ if (p->party_id >= party_newid)
+ party_newid = p->party_id + 1;
+ idb_put(party_db, p->party_id, p);
+ party_check_empty(p);
+ } else {
+ ShowError("int_party: broken data [%s] line %d\n", party_txt, c + 1);
+ aFree(p);
+ }
+ c++;
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+void inter_party_final()
+{
+ party_db->destroy(party_db, NULL);
+ return;
+}
+
+// ƒp?ƒeƒB?ƒf?ƒ^‚̃Z?ƒu—p
+int inter_party_save_sub(DBKey key, void *data, va_list ap) {
+ char line[8192];
+ FILE *fp;
+
+ inter_party_tostr(line, (struct party *)data);
+ fp = va_arg(ap, FILE *);
+ fprintf(fp, "%s" RETCODE, line);
+
+ return 0;
+}
+
+// ƒp?ƒeƒB?ƒf?ƒ^‚̃Z?ƒu
+int inter_party_save() {
+ FILE *fp;
+ int lock;
+
+ if ((fp = lock_fopen(party_txt, &lock)) == NULL) {
+ ShowError("int_party: cant write [%s] !!! data is lost !!!\n", party_txt);
+ return 1;
+ }
+ party_db->foreach(party_db, inter_party_save_sub, fp);
+ lock_fclose(fp,party_txt, &lock);
+ return 0;
+}
+
+// ƒp?ƒeƒB–¼?õ—p
+int search_partyname_sub(DBKey key,void *data,va_list ap) {
+ struct party *p = (struct party *)data,**dst;
+ char *str;
+
+ str = va_arg(ap, char *);
+ dst = va_arg(ap, struct party **);
+ if (strncmpi(p->name, str, NAME_LENGTH) == 0)
+ *dst = p;
+
+ return 0;
+}
+
+// ƒp?ƒeƒB–¼?õ
+struct party* search_partyname(char *str) {
+ struct party *p = NULL;
+ party_db->foreach(party_db, search_partyname_sub, str, &p);
+ return p;
+}
+
+// EXPŒö•½•ª”z‚Å‚«‚é‚©ƒ`ƒFƒbƒN
+int party_check_exp_share(struct party *p) {
+ int i, oi[MAX_PARTY], dudes=0;
+ 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;
+ if( lv >= 70 )
+ dudes+=1000;
+ oi[dudes%1000] = i;
+ dudes++;
+ }
+ }
+ if((dudes/1000 >= 2) && (dudes%1000 == 3) && maxlv-minlv>party_share_level) {
+ int pl1=0,pl2=0,pl3=0;
+ pl1=search_character_index(p->member[oi[0]].name);
+ pl2=search_character_index(p->member[oi[1]].name);
+ pl3=search_character_index(p->member[oi[2]].name);
+ ShowDebug("PARTY: group of 3 Id1 %d lv %d name %s Id2 %d lv %d name %s Id3 %d lv %d name %s\n",pl1,p->member[oi[0]].lv,p->member[oi[0]].name,pl2,p->member[oi[1]].lv,p->member[oi[1]].name,pl3,p->member[oi[2]].lv,p->member[oi[2]].name);
+ if (char_married(pl1,pl2) && char_child(pl1,pl3))
+ return 1;
+ if (char_married(pl1,pl3) && char_child(pl1,pl2))
+ return 1;
+ if (char_married(pl2,pl3) && char_child(pl2,pl1))
+ return 1;
+ }
+ return (maxlv==0 || maxlv-minlv<=party_share_level);
+}
+
+// ƒp?ƒeƒB‚ª‹ó‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+int party_check_empty(struct party *p) {
+ int i;
+
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id > 0) {
+ return 0;
+ }
+ }
+ mapif_party_broken(p->party_id, 0);
+ idb_remove(party_db, p->party_id);
+
+ return 1;
+}
+
+// ƒLƒƒƒ‰‚Ì‹£‡‚ª‚È‚¢‚©ƒ`ƒFƒbƒN—p
+int party_check_conflict_sub(DBKey key, void *data, va_list ap) {
+ struct party *p = (struct party *)data;
+ int party_id, account_id, char_id, i;
+
+ party_id=va_arg(ap, int);
+ account_id=va_arg(ap, int);
+ char_id=va_arg(ap, int);
+
+ if (p->party_id == party_id) //No conflict to check
+ return 0;
+
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id) {
+ ShowWarning("int_party: party conflict! %d %d %d\n", account_id, party_id, p->party_id);
+ mapif_parse_PartyLeave(-1, p->party_id, account_id, char_id);
+ }
+ }
+
+ return 0;
+}
+
+// ƒLƒƒƒ‰‚Ì‹£‡‚ª‚È‚¢‚©ƒ`ƒFƒbƒN
+int party_check_conflict(int party_id, int account_id, int char_id) {
+ party_db->foreach(party_db, party_check_conflict_sub, party_id, account_id, char_id);
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// map server‚Ö‚Ì’ÊM
+
+// ƒp?ƒeƒB쬉”Û
+int mapif_party_created(int fd,int account_id, int char_id, struct party *p) {
+ WFIFOHEAD(fd, 39);
+ WFIFOW(fd,0) = 0x3820;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ if (p != NULL) {
+ WFIFOB(fd,10) = 0;
+ WFIFOL(fd,11) = p->party_id;
+ memcpy(WFIFOP(fd,15), p->name, NAME_LENGTH);
+ ShowInfo("Created party (%d - %s)\n", p->party_id, p->name);
+ } else {
+ WFIFOB(fd,10) = 1;
+ WFIFOL(fd,11) = 0;
+ memset(WFIFOP(fd,15), 0, NAME_LENGTH);
+ }
+ WFIFOSET(fd,39);
+ return 0;
+}
+
+// ƒp?ƒeƒBî•ñŒ©‚‚©‚炸
+int mapif_party_noinfo(int fd, int party_id) {
+ WFIFOHEAD(fd, 8);
+ WFIFOW(fd,0) = 0x3821;
+ WFIFOW(fd,2) = 8;
+ WFIFOL(fd,4) = party_id;
+ WFIFOSET(fd,8);
+ ShowWarning("int_party: info not found %d\n", party_id);
+
+ return 0;
+}
+
+// ƒp?ƒeƒBî•ñ‚Ü‚Æ‚ß‘—‚è
+int mapif_party_info(int fd, struct party *p) {
+ unsigned char buf[2048];
+
+ 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));
+ return 0;
+}
+
+// ƒp?ƒeƒBƒƒ“ƒo’ljÁ‰Â”Û
+int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) {
+ WFIFOHEAD(fd, 15);
+ WFIFOW(fd,0) = 0x3822;
+ WFIFOL(fd,2) = party_id;
+ WFIFOL(fd,6) = account_id;
+ WFIFOL(fd,10) = char_id;
+ WFIFOB(fd,14) = flag;
+ WFIFOSET(fd,15);
+
+ return 0;
+}
+
+// ƒp?ƒeƒBÝ’è?X’Ê’m
+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);
+ return 0;
+}
+
+//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex]
+int inter_party_logged(int party_id, int account_id, int char_id)
+{
+ struct party *p;
+ int i;
+ if (!party_id)
+ return 0;
+
+ p = idb_get(party_db, party_id);
+ if(p==NULL)
+ return 0;
+ for (i = 0; i < MAX_PARTY; i++)
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ {
+ p->member[i].online = 1;
+ break;
+ }
+ if(p->exp && !party_check_exp_share(p))
+ {
+ p->exp=0;
+ mapif_party_optionchanged(0,p,0,0);
+ return 1;
+ }
+ return 0;
+}
+
+// ƒp?ƒeƒB?‘Þ’Ê’m
+int mapif_party_leaved(int party_id,int account_id, int char_id) {
+ unsigned char buf[16];
+
+ WBUFW(buf,0) = 0x3824;
+ WBUFL(buf,2) = party_id;
+ WBUFL(buf,6) = account_id;
+ WBUFL(buf,10) = char_id;
+ mapif_sendall(buf, 14);
+ return 0;
+}
+
+// ƒp?ƒeƒBƒ}ƒbƒvXV’Ê’m
+int mapif_party_membermoved(struct party *p, int idx) {
+ unsigned char buf[20];
+
+ WBUFW(buf,0) = 0x3825;
+ WBUFL(buf,2) = p->party_id;
+ WBUFL(buf,6) = p->member[idx].account_id;
+ WBUFL(buf,10) = p->member[idx].char_id;
+ WBUFW(buf,14) = p->member[idx].map;
+ WBUFB(buf,16) = p->member[idx].online;
+ WBUFW(buf,17) = p->member[idx].lv;
+ mapif_sendall(buf, 19);
+ return 0;
+}
+
+// ƒp?ƒeƒB‰ðŽU’Ê’m
+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);
+ ShowInfo("Party broken (%d)\n", party_id);
+
+ return 0;
+}
+
+// ƒp?ƒeƒB??Œ¾
+int mapif_party_message(int party_id, int account_id, char *mes, int len, int sfd) {
+ unsigned char buf[2048];
+
+ 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_sendallwos(sfd, buf,len + 12);
+
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// map server‚©‚ç‚Ì’ÊM
+
+
+// ƒp?ƒeƒB
+int mapif_parse_CreateParty(int fd, int account_id, int char_id, char *name, char *nick, unsigned short map, int lv, int item, int item2) {
+ struct party *p;
+ int i;
+
+ for(i = 0; i < NAME_LENGTH && name[i]; i++) {
+ if (!(name[i] & 0xe0) || name[i] == 0x7f) {
+ ShowInfo("int_party: illegal party name [%s]\n", name);
+ mapif_party_created(fd, account_id, char_id, NULL);
+ return 0;
+ }
+ }
+
+ if ((p = search_partyname(name)) != NULL) {
+ ShowInfo("int_party: same name party exists [%s]\n", name);
+ mapif_party_created(fd, account_id, char_id, NULL);
+ return 0;
+ }
+ p = (struct party *) aCalloc(sizeof(struct party), 1);
+ if (p == NULL) {
+ ShowFatalError("int_party: out of memory !\n");
+ mapif_party_created(fd,account_id,char_id,NULL);
+ return 0;
+ }
+ p->party_id = party_newid++;
+ memcpy(p->name, name, NAME_LENGTH);
+ p->exp = 0;
+ p->item=(item?1:0)|(item2?2:0);
+
+ p->member[0].account_id = account_id;
+ p->member[0].char_id = char_id;
+ memcpy(p->member[0].name, nick, NAME_LENGTH);
+ p->member[0].map = map;
+ p->member[0].leader = 1;
+ p->member[0].online = 1;
+ p->member[0].lv = lv;
+
+ idb_put(party_db, p->party_id, p);
+
+ mapif_party_created(fd, account_id, char_id, p);
+ mapif_party_info(fd, p);
+
+ return 0;
+}
+
+// ƒp?ƒeƒBî•ñ—v‹
+int mapif_parse_PartyInfo(int fd, int party_id) {
+ struct party *p;
+
+ p = idb_get(party_db, party_id);
+ if (p != NULL)
+ mapif_party_info(fd, p);
+ else
+ mapif_party_noinfo(fd, party_id);
+
+ return 0;
+}
+
+// ƒp?ƒeƒB’ljÁ—v‹
+int mapif_parse_PartyAddMember(int fd, int party_id, int account_id, int char_id, char *nick, unsigned short map, int lv) {
+ struct party *p;
+ int i;
+
+ p = idb_get(party_db, party_id);
+ if (p == NULL) {
+ mapif_party_memberadded(fd, party_id, account_id, char_id, 1);
+ return 0;
+ }
+
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == 0) {
+ int flag = 0;
+
+ p->member[i].account_id = account_id;
+ p->member[i].char_id = char_id;
+ memcpy(p->member[i].name, nick, NAME_LENGTH);
+ p->member[i].map = map;
+ p->member[i].leader = 0;
+ p->member[i].online = 1;
+ p->member[i].lv = lv;
+ mapif_party_memberadded(fd, party_id, account_id, char_id, 0);
+ mapif_party_info(-1, p);
+
+ if (p->exp && !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, char_id, 1);
+
+ return 0;
+}
+
+// ƒp?ƒeƒB?Ý’è?X—v‹
+int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int flag) {
+ struct party *p;
+ //NOTE: No clue what that flag is about, in all observations so far it always comes as 0. [Skotlex]
+ flag = 0;
+
+ p = idb_get(party_db, party_id);
+ if (p == NULL)
+ return 0;
+
+ p->exp = exp;
+ if (exp>0 && !party_check_exp_share(p)) {
+ flag |= 0x01;
+ p->exp = 0;
+ }
+
+ mapif_party_optionchanged(fd, p, account_id, flag);
+ return 0;
+}
+
+// ƒp?ƒeƒB?‘Þ—v‹
+int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) {
+ struct party *p;
+ int i;
+
+ p = idb_get(party_db, party_id);
+ if (p != NULL) {
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ {
+ mapif_party_leaved(party_id, account_id, char_id);
+ memset(&p->member[i], 0, sizeof(struct party_member));
+ if (party_check_empty(p) == 0)
+ mapif_party_info(-1, p);// ‚Ü‚¾l‚ª‚¢‚é‚̂Ńf?ƒ^‘—M
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// ƒp?ƒeƒBƒ}ƒbƒvXV—v‹
+int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, int lv) {
+ struct party *p;
+ int i;
+
+ p = idb_get(party_db, party_id);
+ if (p == NULL)
+ return 0;
+
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ {
+ p->member[i].map = map;
+ p->member[i].online = online;
+ if (p->member[i].lv != lv) {
+ p->member[i].lv = lv;
+ if (p->exp && !party_check_exp_share(p)) {
+ p->exp = 0;
+ mapif_party_optionchanged(fd, p, 0, 0);
+ }
+ }
+ mapif_party_membermoved(p, i);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+// ƒp?ƒeƒB‰ðŽU—v‹
+int mapif_parse_BreakParty(int fd, int party_id) {
+ struct party *p;
+
+ p = idb_get(party_db, party_id);
+ if (p == NULL)
+ return 0;
+
+ idb_remove(party_db, party_id);
+ mapif_party_broken(fd, party_id);
+
+ return 0;
+}
+
+// ƒp?ƒeƒBƒƒbƒZ?ƒW‘—M
+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, fd);
+}
+// ƒp?ƒeƒBƒ`ƒFƒbƒN—v‹
+int mapif_parse_PartyCheck(int fd, int party_id, int account_id, int char_id) {
+ return party_check_conflict(party_id, account_id, char_id);
+}
+
+int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id)
+{
+ struct party *p;
+ int i;
+
+ p = idb_get(party_db, party_id);
+ if (p == NULL)
+ return 0;
+
+ for (i = 0; i < MAX_PARTY; i++)
+ {
+ if(p->member[i].leader)
+ p->member[i].leader = 0;
+ if(p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ p->member[i].leader = 1;
+ }
+ return 1;
+}
+
+// map server ‚©‚ç‚Ì’ÊM
+// ?‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// ?ƒpƒPƒbƒg’·ƒf?ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// ?ƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// ?ƒGƒ‰?‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_party_parse_frommap(int fd) {
+ RFIFOHEAD(fd);
+ switch(RFIFOW(fd,0)) {
+ case 0x3020: mapif_parse_CreateParty(fd, RFIFOL(fd,2), RFIFOL(fd,6),(char*)RFIFOP(fd,10), (char*)RFIFOP(fd,34), RFIFOW(fd,58), RFIFOW(fd,60), RFIFOB(fd,62), RFIFOB(fd,63)); break;
+ case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break;
+ case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), (char*)RFIFOP(fd,14), RFIFOW(fd,38), RFIFOW(fd,40)); 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), RFIFOL(fd,10)); break;
+ case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break;
+ case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break;
+ case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break;
+ case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
+ case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+// ƒT?ƒo?‚©‚ç?‘Þ—v‹iƒLƒƒƒ‰íœ—pj
+int inter_party_leave(int party_id, int account_id, int char_id) {
+ return mapif_parse_PartyLeave(-1, party_id, account_id, char_id);
+}
+
diff --git a/src/char/int_party.h b/src/char/int_party.h
new file mode 100644
index 000000000..8b54948b0
--- /dev/null
+++ b/src/char/int_party.h
@@ -0,0 +1,18 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_PARTY_H_
+#define _INT_PARTY_H_
+
+int inter_party_init(void);
+void inter_party_final(void);
+int inter_party_save(void);
+
+int inter_party_parse_frommap(int fd);
+
+int inter_party_leave(int party_id,int account_id, int char_id);
+int inter_party_logged(int party_id, int account_id, int char_id);
+
+extern char party_txt[1024];
+
+#endif
diff --git a/src/char/int_pet.c b/src/char/int_pet.c
new file mode 100644
index 000000000..a81acf3ed
--- /dev/null
+++ b/src/char/int_pet.c
@@ -0,0 +1,380 @@
+// 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/mmo.h"
+#include "../common/socket.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+#include "char.h"
+#include "inter.h"
+#include "int_pet.h"
+
+char pet_txt[1024]="save/pet.txt";
+
+static struct dbt *pet_db;
+static int pet_newid = 100;
+
+int inter_pet_tostr(char *str,struct s_pet *p)
+{
+ int len;
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+
+ len=sprintf(str,"%d,%d,%s\t%d,%d,%d,%d,%d,%d,%d,%d,%d",
+ p->pet_id,p->class_,p->name,p->account_id,p->char_id,p->level,p->egg_id,
+ p->equip,p->intimate,p->hungry,p->rename_flag,p->incuvate);
+
+ return 0;
+}
+
+int inter_pet_fromstr(char *str,struct s_pet *p)
+{
+ int s;
+ int tmp_int[16];
+ char tmp_str[256];
+
+ memset(p,0,sizeof(struct s_pet));
+
+// printf("sscanf pet main info\n");
+ s=sscanf(str,"%d,%d,%[^\t]\t%d,%d,%d,%d,%d,%d,%d,%d,%d",&tmp_int[0],&tmp_int[1],tmp_str,&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]);
+
+ if(s!=12)
+ return 1;
+
+ p->pet_id = tmp_int[0];
+ p->class_ = tmp_int[1];
+ memcpy(p->name,tmp_str,NAME_LENGTH-1);
+ p->account_id = tmp_int[2];
+ p->char_id = tmp_int[3];
+ p->level = tmp_int[4];
+ p->egg_id = tmp_int[5];
+ p->equip = tmp_int[6];
+ p->intimate = tmp_int[7];
+ p->hungry = tmp_int[8];
+ p->rename_flag = tmp_int[9];
+ p->incuvate = tmp_int[10];
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+
+ return 0;
+}
+
+int inter_pet_init()
+{
+ char line[8192];
+ struct s_pet *p;
+ FILE *fp;
+ int c=0;
+
+ pet_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ if( (fp=fopen(pet_txt,"r"))==NULL )
+ return 1;
+ while(fgets(line,sizeof(line),fp)){
+ p = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1);
+ if(p==NULL){
+ ShowFatalError("int_pet: out of memory!\n");
+ exit(0);
+ }
+ memset(p,0,sizeof(struct s_pet));
+ if(inter_pet_fromstr(line,p)==0 && p->pet_id>0){
+ if( p->pet_id >= pet_newid)
+ pet_newid=p->pet_id+1;
+ idb_put(pet_db,p->pet_id,p);
+ }else{
+ ShowError("int_pet: broken data [%s] line %d\n",pet_txt,c);
+ aFree(p);
+ }
+ c++;
+ }
+ fclose(fp);
+// printf("int_pet: %s read done (%d pets)\n",pet_txt,c);
+ return 0;
+}
+
+void inter_pet_final()
+{
+ pet_db->destroy(pet_db, NULL);
+ return;
+}
+
+int inter_pet_save_sub(DBKey key,void *data,va_list ap)
+{
+ char line[8192];
+ FILE *fp;
+ inter_pet_tostr(line,(struct s_pet *)data);
+ fp=va_arg(ap,FILE *);
+ fprintf(fp,"%s" RETCODE,line);
+ return 0;
+}
+
+int inter_pet_save()
+{
+ FILE *fp;
+ int lock;
+ if( (fp=lock_fopen(pet_txt,&lock))==NULL ){
+ ShowError("int_pet: cant write [%s] !!! data is lost !!!\n",pet_txt);
+ return 1;
+ }
+ pet_db->foreach(pet_db,inter_pet_save_sub,fp);
+ lock_fclose(fp,pet_txt,&lock);
+// printf("int_pet: %s saved.\n",pet_txt);
+ return 0;
+}
+
+int inter_pet_delete(int pet_id)
+{
+ struct s_pet *p;
+ p = idb_get(pet_db,pet_id);
+ if( p == NULL)
+ return 1;
+ else {
+ idb_remove(pet_db,pet_id);
+ ShowInfo("Deleted pet (pet_id: %d)\n",pet_id);
+ }
+ return 0;
+}
+
+int mapif_pet_created(int fd,int account_id,struct s_pet *p)
+{
+ WFIFOHEAD(fd, 11);
+ WFIFOW(fd,0)=0x3880;
+ WFIFOL(fd,2)=account_id;
+ if(p!=NULL){
+ WFIFOB(fd,6)=0;
+ WFIFOL(fd,7)=p->pet_id;
+ ShowInfo("Created pet (%d - %s)\n",p->pet_id,p->name);
+ }else{
+ WFIFOB(fd,6)=1;
+ WFIFOL(fd,7)=0;
+ }
+ WFIFOSET(fd,11);
+
+ return 0;
+}
+
+int mapif_pet_info(int fd,int account_id,struct s_pet *p)
+{
+ WFIFOHEAD(fd, sizeof(struct s_pet) + 9);
+ WFIFOW(fd,0)=0x3881;
+ WFIFOW(fd,2)=sizeof(struct s_pet) + 9;
+ WFIFOL(fd,4)=account_id;
+ WFIFOB(fd,8)=0;
+ memcpy(WFIFOP(fd,9),p,sizeof(struct s_pet));
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+int mapif_pet_noinfo(int fd,int account_id)
+{
+ WFIFOHEAD(fd, sizeof(struct s_pet) + 9);
+ WFIFOW(fd,0)=0x3881;
+ WFIFOW(fd,2)=sizeof(struct s_pet) + 9;
+ WFIFOL(fd,4)=account_id;
+ WFIFOB(fd,8)=1;
+ memset(WFIFOP(fd,9),0,sizeof(struct s_pet));
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+int mapif_save_pet_ack(int fd,int account_id,int flag)
+{
+ WFIFOHEAD(fd, 7);
+ WFIFOW(fd,0)=0x3882;
+ WFIFOL(fd,2)=account_id;
+ WFIFOB(fd,6)=flag;
+ WFIFOSET(fd,7);
+
+ return 0;
+}
+
+int mapif_delete_pet_ack(int fd,int flag)
+{
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd,0)=0x3883;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,3);
+
+ return 0;
+}
+
+int mapif_create_pet(int fd,int account_id,int char_id,short pet_class,short pet_lv,short pet_egg_id,
+ short pet_equip,short intimate,short hungry,char rename_flag,char incuvate,char *pet_name)
+{
+ struct s_pet *p;
+ p= (struct s_pet *) aCalloc(sizeof(struct s_pet), 1);
+ if(p==NULL){
+ ShowFatalError("int_pet: out of memory !\n");
+ mapif_pet_created(fd,account_id,NULL);
+ return 0;
+ }
+// memset(p,0,sizeof(struct s_pet)); unnecessary after aCalloc [Skotlex]
+ p->pet_id = pet_newid++;
+ memcpy(p->name,pet_name,NAME_LENGTH-1);
+ if(incuvate == 1)
+ p->account_id = p->char_id = 0;
+ else {
+ p->account_id = account_id;
+ p->char_id = char_id;
+ }
+ p->class_ = pet_class;
+ p->level = pet_lv;
+ p->egg_id = pet_egg_id;
+ p->equip = pet_equip;
+ p->intimate = intimate;
+ p->hungry = hungry;
+ p->rename_flag = rename_flag;
+ p->incuvate = incuvate;
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+
+ idb_put(pet_db,p->pet_id,p);
+
+ mapif_pet_created(fd,account_id,p);
+
+ return 0;
+}
+
+int mapif_load_pet(int fd,int account_id,int char_id,int pet_id)
+{
+ struct s_pet *p;
+ p= idb_get(pet_db,pet_id);
+ if(p!=NULL) {
+ if(p->incuvate == 1) {
+ p->account_id = p->char_id = 0;
+ mapif_pet_info(fd,account_id,p);
+ }
+ else if(account_id == p->account_id && char_id == p->char_id)
+ mapif_pet_info(fd,account_id,p);
+ else
+ mapif_pet_noinfo(fd,account_id);
+ }
+ else
+ mapif_pet_noinfo(fd,account_id);
+
+ return 0;
+}
+
+static void* create_pet(DBKey key, va_list args) {
+ struct s_pet *p;
+ p=(struct s_pet *)aCalloc(sizeof(struct s_pet),1);
+ p->pet_id = key.i;
+ return p;
+}
+int mapif_save_pet(int fd,int account_id,struct s_pet *data)
+{
+ struct s_pet *p;
+ int pet_id, len;
+ RFIFOHEAD(fd);
+ len=RFIFOW(fd,2);
+
+ if(sizeof(struct s_pet)!=len-8) {
+ ShowError("inter pet: data size error %d %d\n",sizeof(struct s_pet),len-8);
+ }
+ else{
+ pet_id = data->pet_id;
+ if (pet_id == 0)
+ pet_id = data->pet_id = pet_newid++;
+ p= idb_ensure(pet_db,pet_id,create_pet);
+ if(data->hungry < 0)
+ data->hungry = 0;
+ else if(data->hungry > 100)
+ data->hungry = 100;
+ if(data->intimate < 0)
+ data->intimate = 0;
+ else if(data->intimate > 1000)
+ data->intimate = 1000;
+ memcpy(p,data,sizeof(struct s_pet));
+ if(p->incuvate == 1)
+ p->account_id = p->char_id = 0;
+
+ mapif_save_pet_ack(fd,account_id,0);
+ }
+
+ return 0;
+}
+
+int mapif_delete_pet(int fd,int pet_id)
+{
+ mapif_delete_pet_ack(fd,inter_pet_delete(pet_id));
+
+ return 0;
+}
+
+int mapif_parse_CreatePet(int fd)
+{
+ RFIFOHEAD(fd);
+ mapif_create_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOW(fd,14),RFIFOW(fd,16),RFIFOL(fd,18),
+ RFIFOL(fd,20),RFIFOB(fd,22),RFIFOB(fd,23),(char*)RFIFOP(fd,24));
+ return 0;
+}
+
+int mapif_parse_LoadPet(int fd)
+{
+ RFIFOHEAD(fd);
+ mapif_load_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ return 0;
+}
+
+int mapif_parse_SavePet(int fd)
+{
+ RFIFOHEAD(fd);
+ mapif_save_pet(fd,RFIFOL(fd,4),(struct s_pet *)RFIFOP(fd,8));
+ return 0;
+}
+
+int mapif_parse_DeletePet(int fd)
+{
+ RFIFOHEAD(fd);
+ mapif_delete_pet(fd,RFIFOL(fd,2));
+ return 0;
+}
+
+// map server ‚©‚ç‚Ì’ÊM
+// E‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// EƒpƒPƒbƒg’·ƒf[ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// EƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// EƒGƒ‰[‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_pet_parse_frommap(int fd)
+{
+ RFIFOHEAD(fd);
+ switch(RFIFOW(fd,0)){
+ case 0x3080: mapif_parse_CreatePet(fd); break;
+ case 0x3081: mapif_parse_LoadPet(fd); break;
+ case 0x3082: mapif_parse_SavePet(fd); break;
+ case 0x3083: mapif_parse_DeletePet(fd); break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
diff --git a/src/char/int_pet.h b/src/char/int_pet.h
new file mode 100644
index 000000000..0495994fe
--- /dev/null
+++ b/src/char/int_pet.h
@@ -0,0 +1,16 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_PET_H_
+#define _INT_PET_H_
+
+int inter_pet_init(void);
+void inter_pet_final(void);
+int inter_pet_save(void);
+int inter_pet_delete(int pet_id);
+
+int inter_pet_parse_frommap(int fd);
+
+extern char pet_txt[1024];
+
+#endif
diff --git a/src/char/int_status.c b/src/char/int_status.c
new file mode 100644
index 000000000..3c3b65dc4
--- /dev/null
+++ b/src/char/int_status.c
@@ -0,0 +1,187 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+
+#include "int_status.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+static struct dbt * scdata_db = NULL; //Contains all the status change data in-memory. [Skotlex]
+char scdata_txt[1024]="save/scdata.txt"; //By [Skotlex]
+
+#ifdef ENABLE_SC_SAVING
+static void* create_scdata(DBKey key, va_list args) {
+ struct scdata *data;
+ data = aCalloc(1, sizeof(struct scdata));
+ data->account_id = va_arg(args, int);
+ data->char_id = key.i;
+ return data;
+}
+
+/*==========================================
+ * Loads status change data of the player given. [Skotlex]
+ *------------------------------------------
+ */
+struct scdata* status_search_scdata(int aid, int cid)
+{
+ struct scdata *data;
+ data = scdata_db->ensure(scdata_db, i2key(cid), create_scdata, aid);
+ return data;
+}
+
+/*==========================================
+ * Deletes status change data of the player given. [Skotlex]
+ * Should be invoked after the data of said player was successfully loaded.
+ *------------------------------------------
+ */
+void status_delete_scdata(int aid, int cid)
+{
+ struct scdata *scdata = idb_remove(scdata_db, cid);
+ if (scdata)
+ {
+ if (scdata->data)
+ aFree(scdata->data);
+ aFree(scdata);
+ }
+}
+
+
+static void inter_status_tostr(char* line, struct scdata *sc_data)
+{
+ int i, len;
+
+ len = sprintf(line, "%d,%d,%d\t", sc_data->account_id, sc_data->char_id, sc_data->count);
+ for(i = 0; i < sc_data->count; i++) {
+ len += sprintf(line + len, "%d,%d,%d,%d,%d,%d\t", sc_data->data[i].type, sc_data->data[i].tick,
+ sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4);
+ }
+ return;
+}
+
+static int inter_scdata_fromstr(char *line, struct scdata *sc_data)
+{
+ int i, len, next;
+
+ if (sscanf(line,"%d,%d,%d\t%n",&sc_data->account_id, &sc_data->char_id, &sc_data->count, &next) < 3)
+ return 0;
+
+ if (sc_data->count < 1)
+ return 0;
+
+ sc_data->data = aCalloc(sc_data->count, sizeof (struct status_change_data));
+
+ for (i = 0; i < sc_data->count; i++)
+ {
+ if (sscanf(line + next, "%hu,%d,%d,%d,%d,%d\t%n", &sc_data->data[i].type, &sc_data->data[i].tick,
+ &sc_data->data[i].val1, &sc_data->data[i].val2, &sc_data->data[i].val3, &sc_data->data[i].val4, &len) < 6)
+ {
+ aFree(sc_data->data);
+ return 0;
+ }
+ next+=len;
+ }
+ return 1;
+}
+/*==========================================
+ * Loads all scdata from the given filename.
+ *------------------------------------------
+ */
+void status_load_scdata(const char* filename)
+{
+ FILE *fp;
+ int sd_count=0, sc_count=0;
+ char line[8192];
+ struct scdata *sc;
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ ShowError("status_load_scdata: Cannot open file %s!\n", filename);
+ return;
+ }
+
+ while(fgets(line, sizeof(line) - 1, fp)) {
+ sc = (struct scdata*)aCalloc(1, sizeof(struct scdata));
+ if (inter_scdata_fromstr(line, sc)) {
+ sd_count++;
+ sc_count+= sc->count;
+ sc = idb_put(scdata_db, sc->char_id, sc);
+ if (sc) {
+ ShowError("Duplicate entry in %s for character %d\n", filename, sc->char_id);
+ if (sc->data) aFree(sc->data);
+ aFree(sc);
+ }
+ } else {
+ ShowError("status_load_scdata: Broken line data: %s\n", line);
+ aFree(sc);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Loaded %d saved status changes for %d characters.\n", sc_count, sd_count);
+}
+
+static int inter_status_save_sub(DBKey key, void *data, va_list ap) {
+ char line[8192];
+ struct scdata * sc_data;
+ FILE *fp;
+
+ sc_data = (struct scdata *)data;
+ if (sc_data->count < 1)
+ return 0;
+
+ fp = va_arg(ap, FILE *);
+ inter_status_tostr(line, sc_data);
+ fprintf(fp, "%s" RETCODE, line);
+ return 0;
+}
+
+/*==========================================
+ * Saves all scdata to the given filename.
+ *------------------------------------------
+ */
+void inter_status_save()
+{
+ FILE *fp;
+ int lock;
+
+ if ((fp = lock_fopen(scdata_txt, &lock)) == NULL) {
+ ShowError("int_status: cant write [%s] !!! data is lost !!!\n", scdata_txt);
+ return;
+ }
+ scdata_db->foreach(scdata_db, inter_status_save_sub, fp);
+ lock_fclose(fp,scdata_txt, &lock);
+}
+
+/*==========================================
+ * Initializes db.
+ *------------------------------------------
+ */
+void status_init()
+{
+ scdata_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ status_load_scdata(scdata_txt);
+}
+
+/*==========================================
+ * Frees up memory.
+ *------------------------------------------
+ */
+static int scdata_db_final(DBKey k,void *d,va_list ap)
+{
+ struct scdata *data = (struct scdata*)d;
+ if (data->data)
+ aFree(data->data);
+ aFree(data);
+ return 0;
+}
+
+/*==========================================
+ * Final cleanup.
+ *------------------------------------------
+ */
+void status_final(void)
+{
+ scdata_db->destroy(scdata_db, scdata_db_final);
+}
+#endif
diff --git a/src/char/int_status.h b/src/char/int_status.h
new file mode 100644
index 000000000..0e22fb201
--- /dev/null
+++ b/src/char/int_status.h
@@ -0,0 +1,24 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef __INT_STATUS__
+#define __INT_STATUS__
+
+#include "char.h"
+
+struct scdata {
+ int account_id, char_id;
+ int count;
+ struct status_change_data* data;
+};
+
+extern char scdata_txt[1024];
+
+#ifdef ENABLE_SC_SAVING
+struct scdata *status_search_scdata(int aid, int cid);
+void status_delete_scdata(int aid, int cid);
+void inter_status_save(void);
+void status_init(void);
+void status_final(void);
+#endif
+#endif
diff --git a/src/char/int_storage.c b/src/char/int_storage.c
new file mode 100644
index 000000000..f8ae42c0f
--- /dev/null
+++ b/src/char/int_storage.c
@@ -0,0 +1,476 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../common/mmo.h"
+#include "../common/socket.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+#include "char.h"
+#include "inter.h"
+#include "int_storage.h"
+#include "int_pet.h"
+#include "int_guild.h"
+
+// ƒtƒ@ƒCƒ‹–¼‚̃fƒtƒHƒ‹ƒg
+// 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;
+
+// ‘qŒÉƒf[ƒ^‚𕶎š—ñ‚É•ÏŠ·
+int storage_tostr(char *str,struct storage *p)
+{
+ int i,j,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",
+ 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);
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p,",%d",p->storage_[i].card[j]);
+ str_p += sprintf(str_p," ");
+ f++;
+ }
+
+ *(str_p++)='\t';
+
+ *str_p='\0';
+ if(!f)
+ str[0]=0;
+ return 0;
+}
+
+// •¶Žš—ñ‚ð‘qŒÉƒf[ƒ^‚É•ÏŠ·
+int storage_fromstr(char *str,struct storage *p)
+{
+ int tmp_int[256];
+ char tmp_str[256];
+ int set,next,len,i,j;
+
+ 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%[0-9,-]%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) {
+ 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];
+
+ for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++)
+ p->storage_[i].card[j] = tmp_int[0];
+
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+ else return 1;
+ }
+ if (i >= MAX_STORAGE && str[next] && str[next]!='\t')
+ ShowWarning("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,j,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",
+ 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);
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p,",%d",p->storage_[i].card[j]);
+ str_p += sprintf(str_p," ");
+ 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];
+ char tmp_str[256];
+ int set,next,len,i,j;
+
+ 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%[0-9,-]%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8)
+ {
+ 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];
+ for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++)
+ p->storage_[i].card[j] = tmp_int[0];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+ else return 1;
+ }
+ if (i >= MAX_GUILD_STORAGE && str[next] && str[next]!='\t')
+ ShowWarning("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;
+}
+
+static void* create_storage(DBKey key, va_list args) {
+ struct storage *s;
+ s = (struct storage *) aCalloc(sizeof(struct storage), 1);
+ s->account_id=key.i;
+ return s;
+}
+
+// ƒAƒJƒEƒ“ƒg‚©‚ç‘qŒÉƒf[ƒ^ƒCƒ“ƒfƒbƒNƒX‚𓾂éiV‹K‘qŒÉ’ljÁ‰Â”\j
+struct storage *account2storage(int account_id)
+{
+ struct storage *s;
+ s= idb_ensure(storage_db, account_id, create_storage);
+ return s;
+}
+
+static void* create_guildstorage(DBKey key, va_list args) {
+ struct guild_storage *gs = NULL;
+ gs = (struct guild_storage *) aCalloc(sizeof(struct guild_storage), 1);
+ gs->guild_id=key.i;
+ return gs;
+}
+
+struct guild_storage *guild2storage(int guild_id)
+{
+ struct guild_storage *gs = NULL;
+ if(inter_guild_search(guild_id) != NULL)
+ gs= idb_ensure(guild_storage_db, guild_id, create_guildstorage);
+ return gs;
+}
+
+//---------------------------------------------------------
+// ‘qŒÉƒf[ƒ^‚ð“Ç‚Ýž‚Þ
+int inter_storage_init()
+{
+ char line[65536];
+ int c=0,tmp_int;
+ struct storage *s;
+ struct guild_storage *gs;
+ FILE *fp;
+
+ storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ fp=fopen(storage_txt,"r");
+ if(fp==NULL){
+ ShowError("cant't read : %s\n",storage_txt);
+ return 1;
+ }
+ while(fgets(line,65535,fp)){
+ sscanf(line,"%d",&tmp_int);
+ s = (struct storage*)aCalloc(sizeof(struct storage), 1);
+ if(s==NULL){
+ ShowFatalError("int_storage: out of memory!\n");
+ exit(0);
+ }
+// memset(s,0,sizeof(struct storage)); aCalloc does this...
+ s->account_id=tmp_int;
+ if(s->account_id > 0 && storage_fromstr(line,s) == 0) {
+ idb_put(storage_db,s->account_id,s);
+ }
+ else{
+ ShowError("int_storage: broken data [%s] line %d\n",storage_txt,c);
+ aFree(s);
+ }
+ c++;
+ }
+ fclose(fp);
+
+ c = 0;
+ guild_storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ fp=fopen(guild_storage_txt,"r");
+ if(fp==NULL){
+ ShowError("cant't read : %s\n",guild_storage_txt);
+ return 1;
+ }
+ while(fgets(line,65535,fp)){
+ sscanf(line,"%d",&tmp_int);
+ gs = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1);
+ if(gs==NULL){
+ ShowFatalError("int_storage: out of memory!\n");
+ exit(0);
+ }
+// memset(gs,0,sizeof(struct guild_storage)); aCalloc...
+ gs->guild_id=tmp_int;
+ if(gs->guild_id > 0 && guild_storage_fromstr(line,gs) == 0) {
+ idb_put(guild_storage_db,gs->guild_id,gs);
+ }
+ else{
+ ShowError("int_storage: broken data [%s] line %d\n",guild_storage_txt,c);
+ aFree(gs);
+ }
+ c++;
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+void inter_storage_final() {
+ storage_db->destroy(storage_db, NULL);
+ guild_storage_db->destroy(guild_storage_db, NULL);
+ return;
+}
+
+int inter_storage_save_sub(DBKey key,void *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" RETCODE,line);
+ return 0;
+}
+//---------------------------------------------------------
+// ‘qŒÉƒf[ƒ^‚ð‘‚«ž‚Þ
+int inter_storage_save()
+{
+ FILE *fp;
+ int lock;
+ if( (fp=lock_fopen(storage_txt,&lock))==NULL ){
+ ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",storage_txt);
+ return 1;
+ }
+ storage_db->foreach(storage_db,inter_storage_save_sub,fp);
+ lock_fclose(fp,storage_txt,&lock);
+// printf("int_storage: %s saved.\n",storage_txt);
+ return 0;
+}
+
+int inter_guild_storage_save_sub(DBKey key,void *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" RETCODE,line);
+ }
+ return 0;
+}
+//---------------------------------------------------------
+// ‘qŒÉƒf[ƒ^‚ð‘‚«ž‚Þ
+int inter_guild_storage_save()
+{
+ FILE *fp;
+ int lock;
+ if( (fp=lock_fopen(guild_storage_txt,&lock))==NULL ){
+ ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",guild_storage_txt);
+ return 1;
+ }
+ guild_storage_db->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;
+}
+
+// ‘qŒÉƒf[ƒ^íœ
+int inter_storage_delete(int account_id)
+{
+ struct storage *s = idb_get(storage_db,account_id);
+ if(s) {
+ int i;
+ for(i=0;i<s->storage_amount;i++){
+ if(s->storage_[i].card[0] == (short)0xff00)
+ inter_pet_delete( MakeDWord(s->storage_[i].card[1],s->storage_[i].card[2]) );
+ }
+ idb_remove(storage_db,account_id);
+ }
+ return 0;
+}
+
+// ƒMƒ‹ƒh‘qŒÉƒf[ƒ^íœ
+int inter_guild_storage_delete(int guild_id)
+{
+ struct guild_storage *gs = idb_get(guild_storage_db,guild_id);
+ if(gs) {
+ int i;
+ for(i=0;i<gs->storage_amount;i++){
+ if(gs->storage_[i].card[0] == (short)0xff00)
+ inter_pet_delete( MakeDWord(gs->storage_[i].card[1],gs->storage_[i].card[2]) );
+ }
+ idb_remove(guild_storage_db,guild_id);
+ }
+ return 0;
+}
+
+//---------------------------------------------------------
+// map server‚Ö‚Ì’ÊM
+
+// ‘qŒÉƒf[ƒ^‚Ì‘—M
+int mapif_load_storage(int fd,int account_id)
+{
+ struct storage *s=account2storage(account_id);
+ WFIFOHEAD(fd, sizeof(struct storage)+8);
+ 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;
+}
+// ‘qŒÉƒf[ƒ^•Û‘¶Š®—¹‘—M
+int mapif_save_storage_ack(int fd,int account_id)
+{
+ WFIFOHEAD(fd, 7);
+ 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);
+ WFIFOHEAD(fd, sizeof(struct guild_storage)+12);
+ 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)
+{
+ WFIFOHEAD(fd, 11);
+ 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‚©‚ç‚Ì’ÊM
+
+// ‘qŒÉƒf[ƒ^—v‹ŽóM
+int mapif_parse_LoadStorage(int fd)
+{
+ RFIFOHEAD(fd);
+ mapif_load_storage(fd,RFIFOL(fd,2));
+ return 0;
+}
+// ‘qŒÉƒf[ƒ^ŽóM••Û‘¶
+int mapif_parse_SaveStorage(int fd)
+{
+ struct storage *s;
+ int account_id, len;
+ RFIFOHEAD(fd);
+ account_id=RFIFOL(fd,4);
+ len=RFIFOW(fd,2);
+ if(sizeof(struct storage)!=len-8){
+ ShowError("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)
+{
+ RFIFOHEAD(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, len;
+ RFIFOHEAD(fd);
+ guild_id=RFIFOL(fd,8);
+ len=RFIFOW(fd,2);
+ if(sizeof(struct guild_storage)!=len-12){
+ ShowError("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 ‚©‚ç‚Ì’ÊM
+// E‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// EƒpƒPƒbƒg’·ƒf[ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// EƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// EƒGƒ‰[‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_storage_parse_frommap(int fd)
+{
+ RFIFOHEAD(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
new file mode 100644
index 000000000..678312b1d
--- /dev/null
+++ b/src/char/int_storage.h
@@ -0,0 +1,19 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#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);
+
+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
new file mode 100644
index 000000000..adedc6a7b
--- /dev/null
+++ b/src/char/inter.c
@@ -0,0 +1,656 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../common/db.h"
+#include "../common/mmo.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/lock.h"
+#include "../common/showmsg.h"
+
+#include "char.h"
+#include "inter.h"
+#include "int_party.h"
+#include "int_guild.h"
+#include "int_status.h"
+#include "int_storage.h"
+#include "int_pet.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 main_chat_nick[16] = "Main";
+
+char accreg_txt[1024] = "save/accreg.txt";
+static struct dbt *accreg_db = NULL;
+
+struct accreg {
+ int account_id, char_id;
+ int reg_num;
+ struct global_reg reg[ACCOUNT_REG_NUM];
+};
+
+int party_share_level = 10;
+int kick_on_disconnect = 1;
+
+// ‘—MƒpƒPƒbƒg’·ƒŠƒXƒg
+int inter_send_packet_length[] = {
+ -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f
+ -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,
+};
+// recv. packet list
+int inter_recv_packet_length[] = {
+ -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f
+ 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f
+ 64, 6,42,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f
+ -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f
+ 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, //0x3080-0x308f
+};
+
+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;
+
+
+//--------------------------------------------------------
+
+// ƒAƒJƒEƒ“ƒg•Ï”‚𕶎š—ñ‚Ö•ÏŠ·
+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,%s ", reg->reg[j].str, reg->reg[j].value);
+ }
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”‚𕶎š—ñ‚©‚ç•ÏŠ·
+int inter_accreg_fromstr(const char *str, struct accreg *reg) {
+ int j, n;
+ const char *p = str;
+
+ if (sscanf(p, "%d\t%n", &reg->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, "%[^,],%[^ ] %n", reg->reg[j].str, reg->reg[j].value, &n) != 2)
+ break;
+ }
+ reg->reg_num = j;
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”‚Ì“Ç‚Ýž‚Ý
+int inter_accreg_init(void) {
+ char line[8192];
+ FILE *fp;
+ int c = 0;
+ struct accreg *reg;
+
+ accreg_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ if( (fp = fopen(accreg_txt, "r")) == NULL)
+ return 1;
+ while(fgets(line, sizeof(line)-1, fp)){
+ line[sizeof(line)-1] = '\0';
+
+ reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1);
+ if (reg == NULL) {
+ ShowFatalError("inter: accreg: out of memory!\n");
+ exit(0);
+ }
+ if (inter_accreg_fromstr(line, reg) == 0 && reg->account_id > 0) {
+ idb_put(accreg_db, reg->account_id, reg);
+ } else {
+ ShowError("inter: accreg: broken data [%s] line %d\n", accreg_txt, c);
+ aFree(reg);
+ }
+ c++;
+ }
+ fclose(fp);
+// printf("inter: %s read done (%d)\n", accreg_txt, c);
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”‚̃Z[ƒu—p
+int inter_accreg_save_sub(DBKey key, void *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" RETCODE, line);
+ }
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”‚̃Z[ƒu
+int inter_accreg_save(void) {
+ FILE *fp;
+ int lock;
+
+ if ((fp = lock_fopen(accreg_txt,&lock)) == NULL) {
+ ShowError("int_accreg: cant write [%s] !!! data is lost !!!\n", accreg_txt);
+ return 1;
+ }
+ accreg_db->foreach(accreg_db, inter_accreg_save_sub,fp);
+ lock_fclose(fp, accreg_txt, &lock);
+// printf("inter: %s saved.\n", accreg_txt);
+
+ return 0;
+}
+
+//--------------------------------------------------------
+
+/*==========================================
+ * Ý’èƒtƒ@ƒCƒ‹‚ð“Ç‚Ýž‚Þ
+ *------------------------------------------
+ */
+int inter_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp = fopen(cfgName, "r");
+ if (fp == NULL) {
+ ShowError("file not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ line[sizeof(line)-1] = '\0';
+
+ if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+
+ if (strcmpi(w1, "storage_txt") == 0) {
+ strncpy(storage_txt, w2, sizeof(storage_txt));
+ } else if (strcmpi(w1, "party_txt") == 0) {
+ strncpy(party_txt, w2, sizeof(party_txt));
+ } else if (strcmpi(w1, "guild_txt") == 0) {
+ strncpy(guild_txt, w2, sizeof(guild_txt));
+ } else if (strcmpi(w1, "pet_txt") == 0) {
+ strncpy(pet_txt, w2, sizeof(pet_txt));
+ } else if (strcmpi(w1, "castle_txt") == 0) {
+ strncpy(castle_txt, w2, sizeof(castle_txt));
+ } else if (strcmpi(w1, "accreg_txt") == 0) {
+ strncpy(accreg_txt, w2, sizeof(accreg_txt));
+ } else if (strcmpi(w1, "guild_storage_txt") == 0) {
+ strncpy(guild_storage_txt, w2, sizeof(guild_storage_txt));
+ } else if (strcmpi(w1, "kick_on_disconnect") == 0) {
+ kick_on_disconnect = atoi(w2);
+ } else if (strcmpi(w1, "party_share_level") == 0) {
+ party_share_level = atoi(w2);
+ if (party_share_level < 0)
+ party_share_level = 0;
+ } else if (strcmpi(w1, "inter_log_filename") == 0) {
+ strncpy(inter_log_filename, w2, sizeof(inter_log_filename));
+ } else if(strcmpi(w1,"log_inter")==0) {
+ log_inter = atoi(w2);
+ } else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza]
+ strcpy(main_chat_nick, w2); //
+ } else if (strcmpi(w1, "import") == 0) {
+ inter_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+// ƒƒO‘‚«o‚µ
+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;
+}
+
+// ƒZ[ƒu
+int inter_save(void) {
+#ifdef ENABLE_SC_SAVING
+ inter_status_save();
+#endif
+ inter_party_save();
+ inter_guild_save();
+ inter_storage_save();
+ inter_guild_storage_save();
+ inter_pet_save();
+ inter_accreg_save();
+
+ return 0;
+}
+
+// ‰Šú‰»
+int inter_init(const char *file) {
+ inter_config_read(file);
+
+ wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ inter_party_init();
+ inter_guild_init();
+ inter_storage_init();
+ inter_pet_init();
+ inter_accreg_init();
+
+ return 0;
+}
+
+// finalize
+void inter_final(void) {
+ accreg_db->destroy(accreg_db, NULL);
+ wis_db->destroy(wis_db, NULL);
+
+ inter_party_final();
+ inter_guild_final();
+ inter_storage_final();
+ inter_pet_final();
+
+ return;
+}
+
+// ƒ}ƒbƒvƒT[ƒo[Ú‘±
+int inter_mapif_init(int fd) {
+ inter_guild_mapif_init(fd);
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// sended packets to map-server
+
+//Sends to map server the current max Account/Char id [Skotlex]
+void mapif_send_maxid(int account_id, int char_id)
+{
+ unsigned char buf[12];
+
+ WBUFW(buf,0) = 0x2b07;
+ WBUFL(buf,2) = account_id;
+ WBUFL(buf,6) = char_id;
+ mapif_sendall(buf, 10);
+}
+
+// GMƒƒbƒZ[ƒW‘—M
+int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) {
+ unsigned char buf[2048];
+
+ if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex]
+ WBUFW(buf,0) = 0x3800;
+ WBUFW(buf,2) = len;
+ WBUFL(buf,4) = color;
+ memcpy(WBUFP(buf,8), mes, len - 8);
+ mapif_sendallwos(sfd, 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[2048];
+ if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex]
+
+ WBUFW(buf, 0) = 0x3801;
+ WBUFW(buf, 2) = 56 + wd->len;
+ WBUFL(buf, 4) = wd->id;
+ memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH);
+ memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH);
+ 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;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”‘—M
+int mapif_account_reg(int fd, unsigned char *src) {
+ unsigned char *buf = aCalloc(1,WBUFW(src,2));
+
+ memcpy(WBUFP(buf,0),src,WBUFW(src,2));
+ WBUFW(buf, 0) = 0x3804;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+
+ aFree(buf);
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”—v‹•ÔM
+int mapif_account_reg_reply(int fd,int account_id, int char_id) {
+ struct accreg *reg = idb_get(accreg_db,account_id);
+
+ WFIFOHEAD(fd, ACCOUNT_REG_NUM * 288+ 13);
+ WFIFOW(fd,0) = 0x3804;
+ WFIFOL(fd,4) = account_id;
+ WFIFOL(fd,8) = char_id;
+ WFIFOB(fd,12) = 2; //Acc Reg
+ if (reg == NULL) {
+ WFIFOW(fd,2) = 13;
+ } else {
+ int i, p;
+ for (p=13,i = 0; i < reg->reg_num; i++) {
+ p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1;
+ }
+ WFIFOW(fd,2)=p;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+//Request to kick char from a certain map server. [Skotlex]
+int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason)
+{
+ if (fd < 0)
+ return -1;
+
+ WFIFOHEAD(fd, 7);
+ WFIFOW(fd,0) = 0x2b1f;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = reason;
+ WFIFOSET(fd,7);
+
+ return 0;
+}
+
+//--------------------------------------------------------
+
+// Existence check of WISP data
+int check_ttl_wisdata_sub(DBKey key, void *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;
+
+ return 0;
+}
+
+int check_ttl_wisdata(void) {
+ unsigned long tick = gettick();
+ int i;
+
+ do {
+ wis_delnum = 0;
+ wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick);
+ for(i = 0; i < wis_delnum; i++) {
+ struct WisData *wd = idb_get(wis_db, wis_dellist[i]);
+ ShowWarning("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
+ idb_remove(wis_db, wd->id);
+ }
+ } while(wis_delnum >= WISDELLIST_MAX);
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// received packets from map-server
+
+// GMƒƒbƒZ[ƒW‘—M
+int mapif_parse_GMmessage(int fd) {
+ RFIFOHEAD(fd);
+ mapif_GMmessage(RFIFOP(fd,8), RFIFOW(fd,2), RFIFOL(fd,4), fd);
+
+ return 0;
+}
+
+// Wisp/page request to send
+int mapif_parse_WisRequest(int fd) {
+ struct WisData* wd;
+ char name[NAME_LENGTH];
+ static int wisid = 0;
+ int index;
+ RFIFOHEAD(fd);
+
+ if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) {
+ ShowWarning("inter: Wis message size too long.\n");
+ return 0;
+ } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows...
+ ShowError("inter: Wis message doesn't exist.\n");
+ return 0;
+ }
+
+ memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex]
+ name[NAME_LENGTH-1]= '\0';
+ // search if character exists before to ask all map-servers
+ if ((index = search_character_index(name)) == -1) {
+ unsigned char buf[27];
+ WBUFW(buf, 0) = 0x3802;
+ memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
+ 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(name, 0, NAME_LENGTH);
+ strncpy(name, search_character_name(index), NAME_LENGTH);
+ // if source is destination, don't ask other servers.
+ if (strcmp((char*)RFIFOP(fd,4),name) == 0) {
+ unsigned char buf[27];
+ WBUFW(buf, 0) = 0x3802;
+ memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
+ 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 {
+
+ wd = (struct WisData *)aCalloc(sizeof(struct WisData), 1);
+ if (wd == NULL){
+ ShowFatalError("inter: WisRequest: out of memory !\n");
+ return 0;
+ }
+
+ // 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), NAME_LENGTH);
+ memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH);
+ memcpy(wd->msg, RFIFOP(fd,52), wd->len);
+ wd->tick = gettick();
+ idb_put(wis_db, wd->id, wd);
+ mapif_wis_message(wd);
+ }
+ }
+
+ return 0;
+}
+
+// Wisp/page transmission result
+int mapif_parse_WisReply(int fd) {
+ int id, flag;
+ struct WisData *wd;
+ RFIFOHEAD(fd);
+ id = RFIFOL(fd,2);
+ flag = RFIFOB(fd,6);
+ wd = idb_get(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
+ idb_remove(wis_db, id);
+ }
+
+ 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[2048]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B
+ RFIFOHEAD(fd);
+ memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2));
+ WBUFW(buf, 0) = 0x3803;
+ mapif_sendall(buf, RFIFOW(fd,2));
+
+ return 0;
+}
+
+static void* create_accreg(DBKey key, va_list args) {
+ struct accreg *reg;
+ reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1);
+ reg->account_id = key.i;
+ return reg;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”•Û‘¶—v‹
+int mapif_parse_Registry(int fd) {
+ int j, p, len;
+ struct accreg *reg;
+ RFIFOHEAD(fd);
+
+ switch (RFIFOB(fd,12)) {
+ case 3: //Character registry
+ return char_parse_Registry(RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,13), RFIFOW(fd,2)-13);
+ case 2: //Acc Reg
+ break;
+ case 1: //Acc Reg2, forward to login
+ return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4);
+ default: //Error?
+ return 1;
+ }
+ reg = idb_ensure(accreg_db, RFIFOL(fd,4), create_accreg);
+
+ for(j=0,p=13;j<ACCOUNT_REG_NUM && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n",reg->reg[j].str,&len);
+ reg->reg[j].str[len]='\0';
+ p +=len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len);
+ reg->reg[j].value[len]='\0';
+ p +=len+1;
+ }
+ reg->reg_num=j;
+ mapif_account_reg(fd, RFIFOP(fd,0)); // ‘¼‚ÌMAPƒT[ƒo[‚É‘—M
+
+ return 0;
+}
+
+// Request the value of all registries.
+int mapif_parse_RegistryRequest(int fd)
+{
+ //Load Char Registry
+ if (RFIFOB(fd,12))
+ char_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6));
+ //Load Account Registry
+ if (RFIFOB(fd,11))
+ mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6));
+ //Ask Login Server for Account2 values.
+ if (RFIFOB(fd,10))
+ request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2);
+ return 1;
+}
+
+//--------------------------------------------------------
+
+// map server ‚©‚ç‚Ì’ÊMi‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æj
+// ƒGƒ‰[‚È‚ç0(false)Aˆ—‚Å‚«‚½‚È‚ç1A
+// ƒpƒPƒbƒg’·‚ª‘«‚è‚È‚¯‚ê‚Î2‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_parse_frommap(int fd) {
+ int cmd, len;
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ len = 0;
+
+ // interŽIŠÇŠ‚©‚𒲂ׂé
+ if (cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length) / sizeof(inter_recv_packet_length[0])))
+ return 0;
+
+ if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex]
+ return 0;
+
+ // ƒpƒPƒbƒg’·‚𒲂ׂé
+ 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_Registry(fd); break;
+ case 0x3005: mapif_parse_RegistryRequest(fd); break;
+ default:
+ if (inter_party_parse_frommap(fd))
+ break;
+ if (inter_guild_parse_frommap(fd))
+ break;
+ if (inter_storage_parse_frommap(fd))
+ break;
+ if (inter_pet_parse_frommap(fd))
+ break;
+ return 0;
+ }
+ RFIFOSKIP(fd, len);
+ return 1;
+}
+
+// RFIFO‚̃pƒPƒbƒg’·Šm”F
+// •K—vƒpƒPƒbƒg’·‚ª‚ ‚ê‚΃pƒPƒbƒg’·A‚Ü‚¾‘«‚è‚È‚¯‚ê‚Î0
+int inter_check_length(int fd, int length) {
+ if (length == -1) { // ‰Â•ÏƒpƒPƒbƒg’·
+ RFIFOHEAD(fd);
+ if (RFIFOREST(fd) < 4) // ƒpƒPƒbƒg’·‚ª–¢’…
+ return 0;
+ length = RFIFOW(fd,2);
+ }
+
+ if ((int)RFIFOREST(fd) < length) // ƒpƒPƒbƒg‚ª–¢’…
+ return 0;
+
+ return length;
+}
+
diff --git a/src/char/inter.h b/src/char/inter.h
new file mode 100644
index 000000000..ac8709cdc
--- /dev/null
+++ b/src/char/inter.h
@@ -0,0 +1,28 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INTER_H_
+#define _INTER_H_
+
+int inter_init(const char *file);
+void inter_final(void);
+int inter_save(void);
+int inter_parse_frommap(int fd);
+int inter_mapif_init(int fd);
+void mapif_send_maxid(int, int);
+int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason);
+
+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 int kick_on_disconnect;
+extern char inter_log_filename[1024];
+extern int log_inter;
+
+extern char main_chat_nick[16];
+
+#endif
diff --git a/src/char_sql/Makefile b/src/char_sql/Makefile
new file mode 100644
index 000000000..f67a95315
--- /dev/null
+++ b/src/char_sql/Makefile
@@ -0,0 +1,27 @@
+all sql: char-server_sql
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/utils.o \
+ ../common/obj/strlib.o ../common/obj/graph.o ../common/obj/grfio.o \
+ ../common/obj/mapindex.o ../common/obj/ers.o ../zlib/unz.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h \
+ ../common/version.h ../common/db.h ../common/plugins.h ../common/lock.h \
+ ../common/malloc.h ../common/showmsg.h ../common/utils.h ../common/strlib.h \
+ ../common/graph.h ../common/grfio.h ../common/mapindex.h
+
+char-server_sql: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o itemdb.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIB_S)
+
+clean:
+ rm -f *.o ../../char-server_sql
+
+# DO NOT DELETE
+
+char.o: char.c char.h ../common/strlib.h itemdb.h ../common/showmsg.h
+inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h ../common/mmo.h char.h ../common/socket.h ../common/showmsg.h
+int_party.o: int_party.c int_party.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/timer.h ../common/db.h ../common/showmsg.h
+int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h ../common/showmsg.h
+int_storage.o: int_storage.c int_storage.h char.h itemdb.h ../common/showmsg.h
+int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h ../common/showmsg.h
+itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h ../common/showmsg.h
diff --git a/src/char_sql/char.c b/src/char_sql/char.c
new file mode 100644
index 000000000..7faed1e87
--- /dev/null
+++ b/src/char_sql/char.c
@@ -0,0 +1,4307 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by Jioh L. Jung
+// TXT 1.105
+#include <sys/types.h>
+
+#ifdef _WIN32
+#include <winsock.h>
+typedef long in_addr_t;
+//#pragma lib <libmysql.lib> // Not required [Lance]
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+
+#include <time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "char.h"
+#include "../common/utils.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "itemdb.h"
+#include "inter.h"
+#include "db.h"
+#include "malloc.h"
+#include "int_guild.h"
+
+static struct dbt *char_db_;
+
+char char_db[256] = "char";
+char scdata_db[256] = "sc_data";
+char cart_db[256] = "cart_inventory";
+char inventory_db[256] = "inventory";
+char charlog_db[256] = "charlog";
+char storage_db[256] = "storage";
+char interlog_db[256] = "interlog";
+char reg_db[256] = "global_reg_value";
+char skill_db[256] = "skill";
+char memo_db[256] = "memo";
+char guild_db[256] = "guild";
+char guild_alliance_db[256] = "guild_alliance";
+char guild_castle_db[256] = "guild_castle";
+char guild_expulsion_db[256] = "guild_expulsion";
+char guild_member_db[256] = "guild_member";
+char guild_position_db[256] = "guild_position";
+char guild_skill_db[256] = "guild_skill";
+char guild_storage_db[256] = "guild_storage";
+char party_db[256] = "party";
+char pet_db[256] = "pet";
+char friend_db[256] = "friends";
+int db_use_sqldbs;
+
+char login_db_account_id[32] = "account_id";
+char login_db_level[32] = "level";
+
+int lowest_gm_level = 1;
+
+char *SQL_CONF_NAME = "conf/inter_athena.conf";
+
+struct mmo_map_server server[MAX_MAP_SERVERS];
+int server_fd[MAX_MAP_SERVERS];
+
+int login_fd, char_fd;
+char userid[24];
+char passwd[24];
+char server_name[20];
+char wisp_server_name[NAME_LENGTH] = "Server";
+int login_ip_set_ = 0;
+char login_ip_str[128];
+in_addr_t login_ip;
+int login_port = 6900;
+int char_ip_set_ = 0;
+char char_ip_str[128];
+int bind_ip_set_ = 0;
+char bind_ip_str[128];
+in_addr_t char_ip;
+int char_port = 6121;
+int char_maintenance;
+int char_new;
+int char_new_display;
+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 used to authorise or not a name of a character. by [Yor]
+//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
+#define TRIM_CHARS "\032\t\n "
+int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius]
+
+int log_char = 1; // loggin char or not [devil]
+int log_inter = 1; // loggin inter or not [devil]
+
+char lan_map_ip[128]; // Lan map ip added by kashy
+int subnetmaski[4]; // Subnetmask added by kashy
+char unknown_char_name[NAME_LENGTH] = "Unknown";
+char db_path[1024]="db";
+
+//These are used to aid the map server in identifying valid clients. [Skotlex]
+static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID;
+static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex]
+
+struct char_session_data{
+ int account_id, login_id1, login_id2,sex;
+ 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;
+ 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 = START_CHAR_NUM;
+struct mmo_charstatus *char_dat;
+int char_num,char_max;
+int max_connect_user = 0;
+int gm_allow_level = 99;
+int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+int save_log = 1;
+int start_zeny = 500;
+int start_weapon = 1201;
+int start_armor = 2301;
+
+// check for exit signal
+// 0 is saving complete
+// other is char_id
+unsigned int save_flag = 0;
+
+// start point (you can reset point on conf file)
+struct point start_point = { 0, 53, 111};
+
+struct gm_account *gm_account = NULL;
+int GM_num = 0;
+
+int console = 0;
+
+//Structure for holding in memory which characters are online on the map servers connected.
+struct online_char_data {
+ int account_id;
+ int char_id;
+ short server;
+ unsigned waiting_disconnect :1;
+};
+
+struct dbt *online_char_db; //Holds all online characters.
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data);
+
+static void * create_online_char_data(DBKey key, va_list args) {
+ struct online_char_data* character;
+ character = aCalloc(1, sizeof(struct online_char_data));
+ character->account_id = key.i;
+ character->char_id = -1;
+ character->server = -1;
+ return character;
+}
+
+//-------------------------------------------------
+// Set Character online/offline [Wizputer]
+//-------------------------------------------------
+
+void set_char_online(int map_id, int char_id, int account_id) {
+ struct online_char_data* character;
+ if ( char_id != 99 ) {
+ sprintf(tmp_sql, "UPDATE `%s` SET `online`='1' WHERE `char_id`='%d'",char_db,char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if (max_account_id < account_id || max_char_id < char_id)
+ { //Notify map-server of the new max IDs [Skotlex]
+ if (account_id > max_account_id)
+ max_account_id = account_id;
+ if (char_id > max_char_id)
+ max_char_id = char_id;
+ mapif_send_maxid(max_account_id, max_char_id);
+ }
+ }
+
+ character = idb_ensure(online_char_db, account_id, create_online_char_data);
+ if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id)
+ {
+ ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n",
+ character->account_id, character->char_id, character->server, map_id, account_id, char_id);
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ }
+ character->char_id = (char_id==99)?-1:char_id;
+ character->server = (char_id==99)?-1:map_id;
+ character->waiting_disconnect = 0;
+ if (char_id != 99)
+ { //Set char online in guild cache. If char is in memory, use the guild id on it, otherwise seek it.
+ struct mmo_charstatus *cp;
+ cp = idb_get(char_db_,char_id);
+ inter_guild_CharOnline(char_id, cp?cp->guild_id:-1);
+ }
+ if (login_fd <= 0 || session[login_fd]->eof)
+ return;
+
+ WFIFOW(login_fd,0) = 0x272b;
+ WFIFOL(login_fd,2) = account_id;
+ WFIFOSET(login_fd,6);
+}
+
+void set_char_offline(int char_id, int account_id) {
+ struct mmo_charstatus *cp;
+ struct online_char_data* character;
+
+ if ( char_id == 99 )
+ sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id);
+ else {
+ cp = idb_get(char_db_,char_id);
+ inter_guild_CharOffline(char_id, cp?cp->guild_id:-1);
+ if (cp)
+ idb_remove(char_db_,char_id);
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if ((character = idb_get(online_char_db, account_id)) != NULL)
+ { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex]
+ character->char_id = -1;
+ character->server = -1;
+ character->waiting_disconnect = 0;
+ }
+
+ if (login_fd <= 0 || session[login_fd]->eof)
+ return;
+
+ WFIFOW(login_fd,0) = 0x272c;
+ WFIFOL(login_fd,2) = account_id;
+ WFIFOSET(login_fd,6);
+}
+
+static int char_db_setoffline(DBKey key, void* data, va_list ap) {
+ struct online_char_data* character = (struct online_char_data*)data;
+ int server = va_arg(ap, int);
+ if (server == -1) {
+ character->char_id = -1;
+ character->server = -1;
+ character->waiting_disconnect = 0;
+ } else if (character->server == server)
+ character->server = -2; //In some map server that we aren't connected to.
+ return 0;
+}
+
+void set_all_offline(void) {
+ MYSQL_RES* sql_res2; //Needed because it is used inside inter_guild_CharOffline; [Skotlex]
+ int char_id;
+ sprintf(tmp_sql, "SELECT `account_id`, `char_id`, `guild_id` FROM `%s` WHERE `online`='1'",char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ ShowNotice("Sending all users offline.\n");
+ sql_res2 = mysql_store_result(&mysql_handle);
+ if (sql_res2) {
+ while((sql_row = mysql_fetch_row(sql_res2))) {
+ char_id = atoi(sql_row[1]);
+ inter_guild_CharOffline(char_id, atoi(sql_row[2]));
+
+ if ( login_fd > 0 ) {
+ ShowInfo("send user offline: %d\n",char_id);
+ WFIFOW(login_fd,0) = 0x272c;
+ WFIFOL(login_fd,2) = char_id;
+ WFIFOSET(login_fd,6);
+ }
+ }
+ mysql_free_result(sql_res2);
+ }
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `online`='1'", char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ online_char_db->foreach(online_char_db,char_db_setoffline,-1);
+}
+//----------------------------------------------------------------------
+// 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)
+//----------------------------------------------------------------------
+// Removed since nothing GM related goes on in the char server [CLOWNISIUS]
+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;
+}
+
+
+int compare_item(struct item *a, struct item *b) {
+
+ if(a->id == b->id &&
+ a->nameid == b->nameid &&
+ a->amount == b->amount &&
+ a->equip == b->equip &&
+ a->identify == b->identify &&
+ a->refine == b->refine &&
+ a->attribute == b->attribute)
+ {
+ int i;
+ for (i=0; i<MAX_SLOTS && a->card[i]==b->card[i]; i++);
+ return (i == MAX_SLOTS);
+ }
+ return 0;
+}
+
+static void* create_charstatus(DBKey key, va_list args) {
+ struct mmo_charstatus *cp;
+ cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
+ cp->char_id = key.i;
+ return cp;
+}
+
+int mmo_char_tosql(int char_id, struct mmo_charstatus *p){
+ int i=0,j,party_exist,guild_exist;
+ int count = 0;
+ int diff = 0;
+ char *tmp_ptr; //Building a single query should be more efficient than running
+ //multiple queries for each thing about to be saved, right? [Skotlex]
+ char save_status[128]; //For displaying save information. [Skotlex]
+ struct mmo_charstatus *cp;
+ struct itemtmp mapitem[MAX_GUILD_STORAGE];
+
+ if (char_id!=p->char_id) return 0;
+
+ cp = idb_ensure(char_db_, char_id, create_charstatus);
+
+// ShowInfo("Saving char "CL_WHITE"%d"CL_RESET" (%s)...\n",char_id,char_dat[0].name);
+ memset(save_status, 0, sizeof(save_status));
+ diff = 0;
+ //map inventory data
+ for(i=0;i<MAX_INVENTORY;i++){
+ if (!compare_item(&p->inventory[i], &cp->inventory[i]))
+ diff = 1;
+ if(p->inventory[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->inventory[i].id;
+ mapitem[count].nameid=p->inventory[i].nameid;
+ mapitem[count].amount = p->inventory[i].amount;
+ mapitem[count].equip = p->inventory[i].equip;
+ mapitem[count].identify = p->inventory[i].identify;
+ mapitem[count].refine = p->inventory[i].refine;
+ mapitem[count].attribute = p->inventory[i].attribute;
+ for (j=0; j<MAX_SLOTS; j++)
+ mapitem[count].card[j] = p->inventory[i].card[j];
+ count++;
+ }
+ }
+ //printf("- Save item data to MySQL!\n");
+ if (diff)
+ if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_INVENTORY))
+ strcat(save_status, " inventory");
+
+ count = 0;
+ diff = 0;
+
+ //map cart data
+ for(i=0;i<MAX_CART;i++){
+ if (!compare_item(&p->cart[i], &cp->cart[i]))
+ diff = 1;
+ if(p->cart[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->cart[i].id;
+ mapitem[count].nameid=p->cart[i].nameid;
+ mapitem[count].amount = p->cart[i].amount;
+ mapitem[count].equip = p->cart[i].equip;
+ mapitem[count].identify = p->cart[i].identify;
+ mapitem[count].refine = p->cart[i].refine;
+ mapitem[count].attribute = p->cart[i].attribute;
+ for (j=0; j<MAX_SLOTS; j++)
+ mapitem[count].card[j] = p->cart[i].card[j];
+ count++;
+ }
+ }
+
+ if (diff)
+ if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_CART))
+ strcat(save_status, " cart");
+
+ if (
+ (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) ||
+ (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) ||
+ (p->zeny != cp->zeny) ||
+ (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) ||
+ (p->max_hp != cp->max_hp) || (p->hp != cp->hp) ||
+ (p->max_sp != cp->max_sp) || (p->sp != cp->sp) ||
+ (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) ||
+ (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) ||
+ (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) ||
+ (p->option != cp->option) ||
+ (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) ||
+ (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) ||
+ (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
+ (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom)
+ )
+ { //Save status
+ //Check for party
+ party_exist=1;
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `party_id` = '%d'",party_db, p->party_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else { //In case of failure, don't touch the data. [Skotlex
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row)
+ party_exist = atoi(sql_row[0]);
+ mysql_free_result(sql_res);
+ }
+
+ //check guild_exist
+ guild_exist=1;
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id` = '%d'",guild_db, p->guild_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else { //If we fail to confirm, don't touch the data.
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row)
+ guild_exist = atoi(sql_row[0]);
+ mysql_free_result(sql_res);
+ }
+
+ if (guild_exist==0) p->guild_id=0;
+ if (party_exist==0) p->party_id=0;
+
+ //query
+ sprintf(tmp_sql ,"UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
+ "`base_exp`='%d', `job_exp`='%d', `zeny`='%d',"
+ "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
+ "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
+ "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+ "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
+ "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'"
+ " WHERE `account_id`='%d' AND `char_id` = '%d'",
+ char_db, p->base_level, p->job_level,
+ p->base_exp, p->job_exp, p->zeny,
+ p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
+ p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
+ p->option, p->party_id, p->guild_id, p->pet_id,
+ p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
+ mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
+ mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y,
+ p->account_id, p->char_id
+ );
+
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " status");
+ }
+
+ //Values that will seldom change (to speed up saving)
+ if (
+ (p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) ||
+ (p->class_ != cp->class_) ||
+ (p->partner_id != cp->partner_id) || (p->father != cp->father) ||
+ (p->mother != cp->mother) || (p->child != cp->child) ||
+ (p->karma != cp->karma) || (p->manner != cp->manner) ||
+ (p->fame != cp->fame)
+ )
+ {
+ sprintf(tmp_sql ,"UPDATE `%s` SET `class`='%d',"
+ "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',"
+ "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d',"
+ "`karma`='%d',`manner`='%d', `fame`='%d'"
+ " WHERE `account_id`='%d' AND `char_id` = '%d'",
+ char_db, p->class_,
+ p->hair, p->hair_color, p->clothes_color,
+ p->partner_id, p->father, p->mother, p->child,
+ p->karma, p->manner, p->fame,
+ p->account_id, p->char_id
+ );
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " status2");
+ }
+
+
+ diff = 0;
+
+ for(i=0;i<MAX_MEMOPOINTS;i++){
+ if(p->memo_point[i].map == cp->memo_point[i].map && p->memo_point[i].x == cp->memo_point[i].x && p->memo_point[i].y == cp->memo_point[i].y)
+ continue;
+ diff = 1;
+ break;
+ }
+
+ if (diff)
+ { //Save memo
+ //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, p->char_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //insert here.
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db);
+ count = 0;
+ for(i=0;i<MAX_MEMOPOINTS;i++){
+ if(p->memo_point[i].map){
+ tmp_ptr += sprintf(tmp_ptr,"('%d', '%s', '%d', '%d'),",
+ char_id, mapindex_id2name(p->memo_point[i].map), p->memo_point[i].x, p->memo_point[i].y);
+ count++;
+ }
+ }
+ if (count)
+ { //Dangerous? Only if none of the above sprintf worked. [Skotlex]
+ tmp_ptr[-1] = '\0'; //Remove the trailing comma.
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " memo");
+ } else //Memo Points cleared (how is this possible?).
+ strcat(save_status, " memo");
+ }
+
+ diff = 0;
+ for(i=0;i<MAX_SKILL;i++) {
+ if ((p->skill[i].lv != 0) && (p->skill[i].id == 0))
+ p->skill[i].id = i; // Fix skill tree
+
+ if((p->skill[i].id != cp->skill[i].id) || (p->skill[i].lv != cp->skill[i].lv) ||
+ (p->skill[i].flag != cp->skill[i].flag))
+ {
+ diff = 1;
+ break;
+ }
+ }
+
+ if (diff)
+ { //Save skills
+
+ //`skill` (`char_id`, `id`, `lv`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, p->char_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s`(`char_id`,`id`,`lv`) VALUES ", skill_db);
+ count = 0;
+ //insert here.
+ for(i=0;i<MAX_SKILL;i++){
+ if(p->skill[i].id && p->skill[i].flag!=1)
+ {
+ tmp_ptr += sprintf(tmp_ptr,"('%d','%d','%d'),",
+ char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2);
+ count++;
+ }
+ }
+
+ if (count)
+ {
+ tmp_ptr[-1] = '\0'; //Remove trailing comma.
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " skills");
+ } else //Skills removed (reset?)
+ strcat(save_status, " skills");
+ }
+/* Saving of global registry values is now handled by the inter-server. [Skotlex]
+ diff = 0;
+ for(i=0;i<p->global_reg_num;i++) {
+ if ((p->global_reg[i].str == NULL) && (cp->global_reg[i].str == NULL))
+ continue;
+ if (((p->global_reg[i].str == NULL) != (cp->global_reg[i].str == NULL)) ||
+ strcmp(p->global_reg[i].value, cp->global_reg[i].value) != 0 ||
+ strcmp(p->global_reg[i].str, cp->global_reg[i].str) != 0
+ ) {
+ diff = 1;
+ break;
+ }
+ }
+
+ if (diff)
+ { //Save global registry.
+ //`global_reg_value` (`char_id`, `str`, `value`)
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, p->char_id);
+ if (mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //insert here.
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s` (`type`, `char_id`, `str`, `value`) VALUES", reg_db);
+ count = 0;
+ for(i=0;i<p->global_reg_num;i++)
+ {
+ if (p->global_reg[i].str && p->global_reg[i].value)
+ {
+ tmp_ptr += sprintf(tmp_ptr,"('3','%d','%s','%s'),",
+ char_id, jstrescapecpy(temp_str,p->global_reg[i].str), jstrescapecpy(temp_str2,p->global_reg[i].value));
+ if (++count%100 == 0)
+ { //Save every X registers to avoid overflowing tmp_sql [Skotlex]
+ tmp_ptr[-1] = '\0';
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " global_reg");
+
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s` (`type`, `char_id`, `str`, `value`) VALUES", reg_db);
+ count = 0;
+ }
+ }
+ }
+
+ if (count)
+ {
+ tmp_ptr[-1] = '\0';
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " global_reg");
+ } else //Values cleared.
+ strcat(save_status, " global_reg");
+ }
+*/
+ diff = 0;
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(p->friends[i].char_id != cp->friends[i].char_id ||
+ p->friends[i].account_id != cp->friends[i].account_id){
+ diff = 1;
+ break;
+ }
+ }
+
+ if(diff == 1)
+ { //Save friends
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db);
+ count = 0;
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(p->friends[i].char_id > 0)
+ {
+ tmp_ptr += sprintf(tmp_ptr, "('%d','%d','%d'),", char_id, p->friends[i].account_id, p->friends[i].char_id);
+ count++;
+ }
+ }
+ if (count)
+ {
+ tmp_ptr[-1] = '\0'; //Remove the last comma. [Skotlex]
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else
+ strcat(save_status, " friends");
+ } else //Friend list cleared.
+ strcat(save_status, " friends");
+
+ }
+
+ if (save_status[0]!='\0' && save_log)
+ ShowInfo("Saved char %d - %s:%s.\n", char_id, char_dat[0].name, save_status);
+ memcpy(cp, p, sizeof(struct mmo_charstatus));
+
+ return 0;
+}
+
+// [Ilpalazzo-sama]
+int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id, int tableswitch)
+{
+ int i,j, flag, id;
+ char *tablename;
+ char selectoption[16];
+ char * str_p = tmp_sql;
+
+ switch (tableswitch) {
+ case TABLE_INVENTORY:
+ tablename = inventory_db; // no need for sprintf here as *_db are char*.
+ sprintf(selectoption,"char_id");
+ break;
+ case TABLE_CART:
+ tablename = cart_db;
+ sprintf(selectoption,"char_id");
+ break;
+ case TABLE_STORAGE:
+ tablename = storage_db;
+ sprintf(selectoption,"account_id");
+ break;
+ case TABLE_GUILD_STORAGE:
+ tablename = guild_storage_db;
+ sprintf(selectoption,"guild_id");
+ break;
+ default:
+ ShowError("Invalid table name!\n");
+ return 1;
+ }
+
+ //=======================================mysql database data > memory===============================================
+
+ str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+
+ for (j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while ((sql_row = mysql_fetch_row(sql_res))) {
+ flag = 0;
+ id = atoi(sql_row[0]);
+ for(i = 0; i < count; i++) {
+ if(mapitem[i].flag == 1)
+ continue;
+ if(mapitem[i].nameid == atoi(sql_row[1])
+ && mapitem[i].card[0] == atoi(sql_row[7])
+ && mapitem[i].card[2] == atoi(sql_row[9])
+ && mapitem[i].card[3] == atoi(sql_row[10])
+ ) { //They are the same item.
+ for (j = 0; j<MAX_SLOTS && mapitem[i].card[j] == atoi(sql_row[7+j]); j++);
+ if (j == MAX_SLOTS &&
+ mapitem[i].amount == atoi(sql_row[2]) &&
+ mapitem[i].equip == atoi(sql_row[3]) &&
+ mapitem[i].identify == atoi(sql_row[4]) &&
+ mapitem[i].refine == atoi(sql_row[5]) &&
+ mapitem[i].attribute == atoi(sql_row[6]))
+ { //Do nothing.
+ } else
+//==============================================Memory data > SQL ===============================
+ if(!itemdb_isequip(mapitem[i].nameid))
+ { //Quick update of stackable items. Update Qty and Equip should be enough, but in case we are also updating identify
+ sprintf(tmp_sql,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `amount`='%d' WHERE `id`='%d' LIMIT 1",
+ tablename, mapitem[i].equip, mapitem[i].identify,mapitem[i].amount, id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else
+ { //Equipment or Misc item, just update all fields.
+ str_p = tmp_sql;
+ str_p += sprintf(str_p,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d'",
+ tablename, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine, mapitem[i].attribute);
+
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`=%d", j, mapitem[i].card[j]);
+
+ str_p += sprintf(str_p,", `amount`='%d' WHERE `id`='%d' LIMIT 1",
+ mapitem[i].amount, id);
+
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ flag = mapitem[i].flag = 1; //Item dealt with,
+ break; //skip to next item in the db.
+ }
+ }
+ if(!flag) { //Item not updated, remove it.
+ sprintf(tmp_sql,"DELETE from `%s` where `id`='%d'", tablename, id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+
+ for(i = 0; i < count; i++) {
+ if(!mapitem[i].flag) {
+ str_p = tmp_sql;
+ str_p += sprintf(str_p,"INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`",
+ tablename, selectoption);
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p,", `card%d`", j);
+
+ str_p += sprintf(str_p,") VALUES ( '%d','%d', '%d', '%d', '%d', '%d', '%d'",
+ char_id, mapitem[i].nameid, mapitem[i].amount, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine,
+ mapitem[i].attribute);
+
+ for(j=0; j<MAX_SLOTS; j++)
+ str_p +=sprintf(str_p,", '%d'",mapitem[i].card[j]);
+
+ strcat(tmp_sql, ")");
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ return 0;
+}
+
+//=====================================================================================================
+int mmo_char_fromsql(int char_id, struct mmo_charstatus *p){
+ int i,j, n;
+ char t_msg[128];
+ char *str_p = tmp_sql;
+ struct mmo_charstatus *cp;
+
+ memset(p, 0, sizeof(struct mmo_charstatus));
+ t_msg[0]= '\0';
+
+ p->char_id = char_id;
+ if (save_log)
+ ShowInfo("Char load request (%d)\n", char_id);
+ //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9
+ //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15
+ //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21
+ //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27
+ //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35
+ //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`)
+ //splite 2 parts. cause veeeery long SQL syntax
+
+ sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
+ "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ if (!sql_row)
+ { //Just how does this happens? [Skotlex]
+ ShowError("Requested non-existant character id: %d!\n", char_id);
+ return 0;
+ }
+
+ p->char_id = char_id;
+ p->account_id = atoi(sql_row[1]);
+ p->char_num = atoi(sql_row[2]);
+ strcpy(p->name, sql_row[3]);
+ p->class_ = atoi(sql_row[4]);
+ p->base_level = atoi(sql_row[5]);
+ p->job_level = atoi(sql_row[6]);
+ p->base_exp = atoi(sql_row[7]);
+ p->job_exp = atoi(sql_row[8]);
+ p->zeny = atoi(sql_row[9]);
+ p->str = atoi(sql_row[10]);
+ p->agi = atoi(sql_row[11]);
+ p->vit = atoi(sql_row[12]);
+ p->int_ = atoi(sql_row[13]);
+ p->dex = atoi(sql_row[14]);
+ p->luk = atoi(sql_row[15]);
+ p->max_hp = atoi(sql_row[16]);
+ p->hp = atoi(sql_row[17]);
+ p->max_sp = atoi(sql_row[18]);
+ p->sp = atoi(sql_row[19]);
+ p->status_point = atoi(sql_row[20]);
+ p->skill_point = atoi(sql_row[21]);
+ //free mysql result.
+ mysql_free_result(sql_res);
+ strcat (t_msg, " status");
+ } else
+ ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!?
+
+ sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`,"
+ "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,"
+ "`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`"
+ "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row) {
+
+ p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]);
+ p->party_id = atoi(sql_row[3]); p->guild_id = atoi(sql_row[4]); p->pet_id = atoi(sql_row[5]);
+
+ p->hair = atoi(sql_row[6]); p->hair_color = atoi(sql_row[7]); p->clothes_color = atoi(sql_row[8]);
+ p->weapon = atoi(sql_row[9]); p->shield = atoi(sql_row[10]);
+ p->head_top = atoi(sql_row[11]); p->head_mid = atoi(sql_row[12]); p->head_bottom = atoi(sql_row[13]);
+ p->last_point.map = mapindex_name2id(sql_row[14]); p->last_point.x = atoi(sql_row[15]); p->last_point.y = atoi(sql_row[16]);
+ p->save_point.map = mapindex_name2id(sql_row[17]); p->save_point.x = atoi(sql_row[18]); p->save_point.y = atoi(sql_row[19]);
+ p->partner_id = atoi(sql_row[20]); p->father = atoi(sql_row[21]); p->mother = atoi(sql_row[22]); p->child = atoi(sql_row[23]);
+ p->fame = atoi(sql_row[24]);
+
+ //free mysql result.
+ mysql_free_result(sql_res);
+ strcat (t_msg, " status2");
+ } else
+ ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!?
+
+ if (p->last_point.x == 0 || p->last_point.y == 0 || p->last_point.map == 0)
+ memcpy(&p->last_point, &start_point, sizeof(start_point));
+
+ if (p->save_point.x == 0 || p->save_point.y == 0 || p->save_point.map == 0)
+ memcpy(&p->save_point, &start_point, sizeof(start_point));
+
+ //read memo data
+ //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
+ sprintf(tmp_sql, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`='%d' ORDER by `memo_id`",memo_db, char_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res) {
+ for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){
+ p->memo_point[i].map = mapindex_name2id(sql_row[0]);
+ p->memo_point[i].x=atoi(sql_row[1]);
+ p->memo_point[i].y=atoi(sql_row[2]);
+ //i ++;
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " memo");
+ }
+
+ //read inventory
+ //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`)
+ str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+
+ for (j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, " FROM `%s` WHERE `char_id`='%d'", inventory_db, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){
+ p->inventory[i].id = atoi(sql_row[0]);
+ p->inventory[i].nameid = atoi(sql_row[1]);
+ p->inventory[i].amount = atoi(sql_row[2]);
+ p->inventory[i].equip = atoi(sql_row[3]);
+ p->inventory[i].identify = atoi(sql_row[4]);
+ p->inventory[i].refine = atoi(sql_row[5]);
+ p->inventory[i].attribute = atoi(sql_row[6]);
+ for (j=0; j<MAX_SLOTS; j++)
+ p->inventory[i].card[j] = atoi(sql_row[7+j]);
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " inventory");
+ }
+
+ //read cart.
+ //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`)
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+
+ for (j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, " FROM `%s` WHERE `char_id`='%d'", cart_db, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){
+ p->cart[i].id = atoi(sql_row[0]);
+ p->cart[i].nameid = atoi(sql_row[1]);
+ p->cart[i].amount = atoi(sql_row[2]);
+ p->cart[i].equip = atoi(sql_row[3]);
+ p->cart[i].identify = atoi(sql_row[4]);
+ p->cart[i].refine = atoi(sql_row[5]);
+ p->cart[i].attribute = atoi(sql_row[6]);
+ for(j=0; j<MAX_SLOTS; j++)
+ p->cart[i].card[j] = atoi(sql_row[7+j]);
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " cart");
+ }
+
+ //read skill
+ //`skill` (`char_id`, `id`, `lv`)
+ sprintf(tmp_sql, "SELECT `id`, `lv` FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){
+ n = atoi(sql_row[0]);
+ p->skill[n].id = n; //memory!? shit!.
+ p->skill[n].lv = atoi(sql_row[1]);
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " skills");
+ }
+/* Global-reg loading is now handled by the inter-server.
+ //global_reg
+ //`global_reg_value` (`char_id`, `str`, `value`)
+ sprintf(tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ i = 0;
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){
+ strcpy (p->global_reg[i].str, sql_row[0]);
+ strcpy (p->global_reg[i].value, sql_row[1]);
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " reg_values");
+ }
+ p->global_reg_num=i;
+*/
+ //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
+ //Friend list
+ sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM `%s` f LEFT JOIN `%s` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", friend_db, char_db, char_id);
+
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res)
+ {
+ for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
+ {
+ if(sql_row) { //need to check if we have sql_row before we check if we have sql_row[2] because we don't want a segfault
+ if(sql_row[2]) {
+ p->friends[i].account_id = atoi(sql_row[0]);
+ p->friends[i].char_id = atoi(sql_row[1]);
+ strncpy(p->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex]
+ }
+ }
+ }
+ mysql_free_result(sql_res);
+ strcat (t_msg, " friends");
+ }
+
+ if (save_log)
+ ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly!
+
+ cp = idb_ensure(char_db_, char_id, create_charstatus);
+ memcpy(cp, p, sizeof(struct mmo_charstatus));
+ return 1;
+}
+
+// For quick selection of data when displaying the char menu. [Skotlex]
+//
+int mmo_char_fromsql_short(int char_id, struct mmo_charstatus *p){
+ char t_msg[128];
+
+ memset(p, 0, sizeof(struct mmo_charstatus));
+ t_msg[0]= '\0';
+
+ p->char_id = char_id;
+// ShowInfo("Quick Char load request (%d)\n", char_id);
+ //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9
+ //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15
+ //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21
+ //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27
+ //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35
+ //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`)
+ //splite 2 parts. cause veeeery long SQL syntax
+
+ sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
+ "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ if (!sql_row)
+ { //Just how does this happens? [Skotlex]
+ ShowError("Requested non-existant character id: %d!\n", char_id);
+ return 0;
+ }
+ p->char_id = char_id;
+ p->account_id = atoi(sql_row[1]);
+ p->char_num = atoi(sql_row[2]);
+ strcpy(p->name, sql_row[3]);
+ p->class_ = atoi(sql_row[4]);
+ p->base_level = atoi(sql_row[5]);
+ p->job_level = atoi(sql_row[6]);
+ p->base_exp = atoi(sql_row[7]);
+ p->job_exp = atoi(sql_row[8]);
+ p->zeny = atoi(sql_row[9]);
+ p->str = atoi(sql_row[10]);
+ p->agi = atoi(sql_row[11]);
+ p->vit = atoi(sql_row[12]);
+ p->int_ = atoi(sql_row[13]);
+ p->dex = atoi(sql_row[14]);
+ p->luk = atoi(sql_row[15]);
+ p->max_hp = atoi(sql_row[16]);
+ p->hp = atoi(sql_row[17]);
+ p->max_sp = atoi(sql_row[18]);
+ p->sp = atoi(sql_row[19]);
+ p->status_point = atoi(sql_row[20]);
+ p->skill_point = atoi(sql_row[21]);
+ //free mysql result.
+ mysql_free_result(sql_res);
+ strcat (t_msg, " status");
+ } else
+ ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!?
+
+ sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`hair`,`hair_color`,"
+ "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`"
+ "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row) {
+
+ p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]);
+ p->hair = atoi(sql_row[3]); p->hair_color = atoi(sql_row[4]); p->clothes_color = atoi(sql_row[5]);
+ p->weapon = atoi(sql_row[6]); p->shield = atoi(sql_row[7]);
+ p->head_top = atoi(sql_row[8]); p->head_mid = atoi(sql_row[9]); p->head_bottom = atoi(sql_row[10]);
+
+ //free mysql result.
+ mysql_free_result(sql_res);
+ strcat (t_msg, " status2");
+ } else
+ ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!?
+
+ if (save_log)
+ ShowInfo("Quick Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly!
+
+ return 1;
+}
+//==========================================================================================================
+int mmo_char_sql_init(void) {
+ int charcount;
+
+
+ ShowInfo("Begin Initializing.......\n");
+ char_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA, sizeof(int));
+ // memory initialize
+ // no need to set twice size in this routine. but some cause segmentation error. :P
+ // The hell? Why segmentation faults? Sounds like a bug that needs addressing... [Skotlex]
+ ShowDebug("initializing char memory...(%d byte)\n",sizeof(struct mmo_charstatus)*2);
+ CREATE(char_dat, struct mmo_charstatus, 2);
+
+ memset(char_dat, 0, sizeof(struct mmo_charstatus)*2);
+
+ //Check for max id (in case new chars would get their IDs set below 150K) [Skotlex]
+ sprintf(tmp_sql , "SELECT max(`char_id`) FROM `%s`", char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else {
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res)
+ {
+ if (mysql_num_rows(sql_res) > 0 &&
+ (sql_row = mysql_fetch_row(sql_res)) != NULL &&
+ sql_row[0] != NULL && atoi(sql_row[0]) >= char_id_count)
+ char_id_count = 0; //No need for setting the char id.
+ mysql_free_result(sql_res);
+ }
+ }
+
+ sprintf(tmp_sql, "SELECT `char_id` FROM `%s`", char_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }else{
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res){
+ charcount = (int)mysql_num_rows(sql_res);
+ ShowStatus("total char data -> '%d'.......\n", charcount);
+ mysql_free_result(sql_res);
+ }else{
+ ShowStatus("total char data -> '0'.......\n");
+ }
+ }
+
+ if(char_per_account == 0){
+ ShowStatus("Chars per Account: 'Unlimited'.......\n");
+ }else{
+ ShowStatus("Chars per Account: '%d'.......\n", char_per_account);
+ }
+
+ //the 'set offline' part is now in check_login_conn ...
+ //if the server connects to loginserver
+ //it will dc all off players
+ //and send the loginserver the new state....
+
+ ShowInfo("Finished initilizing.......\n");
+
+ return 0;
+}
+
+//==========================================================================================================
+
+int make_new_char_sql(int fd, unsigned char *dat) {
+ struct char_session_data *sd;
+ char name[NAME_LENGTH];
+ char t_name[NAME_LENGTH*2];
+ unsigned int i; // Used in for loop and comparing with strlen, safe to be unsigned. [Lance]
+ int char_id, temp;
+
+ strncpy(name, dat, NAME_LENGTH);
+ name[NAME_LENGTH-1] = '\0'; //Always terminate string.
+ trim(name,TRIM_CHARS); //Trim character name. [Skotlex]
+ jstrescapecpy(t_name, name);
+
+ // disabled until fixed >.>
+ // Note: escape characters should be added to jstrescape()!
+ //mysql_real_escape_string(&mysql_handle, t_name, t_name_temp, sizeof(t_name_temp));
+
+ if (!session_isValid(fd) || !(sd = (struct char_session_data*)session[fd]->session_data))
+ return -2;
+
+ ShowInfo("New character request (%d)\n", sd->account_id);
+
+ //check name != main chat nick [LuzZza]
+ if(strcmpi(name, main_chat_nick) == 0) {
+ ShowInfo("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name);
+ return -2;
+ }
+
+ //check for charcount (maxchars) :)
+ if(char_per_account != 0){
+ sprintf(tmp_sql, "SELECT `account_id` FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res){
+ //ok
+ temp = (int)mysql_num_rows(sql_res);
+ if(temp >= char_per_account){
+ //hehe .. limit exceeded :P
+ ShowInfo("Create char failed (%d): charlimit exceeded.\n", sd->account_id);
+ mysql_free_result(sql_res);
+ return -2;
+ }
+ mysql_free_result(sql_res);
+ }
+ }
+
+ // 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; i < NAME_LENGTH && name[i]; i++)
+ if (strchr(char_name_letters, name[i]) == NULL)
+ return -2;
+ } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
+ for (i = 0; i < NAME_LENGTH && name[i]; i++)
+ if (strchr(char_name_letters, name[i]) != NULL)
+ return -2;
+ } // else, all letters/symbols are authorised (except control char removed before)
+
+ //check stat error
+ if ((dat[24]+dat[25]+dat[26]+dat[27]+dat[28]+dat[29]!=6*5 ) || // stats
+ (dat[30] >= 9) || // slots (dat[30] can not be negativ)
+ (dat[33] <= 0) || (dat[33] >= 24) || // hair style
+ (dat[31] >= 9)) { // hair color (dat[31] can not be negativ)
+ if (log_char) {
+ // char.log to charlog
+ sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]);
+ //query
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id);
+ return -2;
+ } // for now we have checked: stat points used <31, char slot is less then 9, hair style/color values are acceptable
+
+ // check individual stat value
+ for(i = 24; i <= 29; i++) {
+ if (dat[i] < 1 || dat[i] > 9) {
+ if (log_char) {
+ // char.log to charlog
+ sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]);
+ //query
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id);
+ return -2;
+ }
+ } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct
+
+ if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) {
+ if (log_char) {
+ // char.log to charlog
+ sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]);
+ //query
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id);
+ return -2;
+ } // now when we have passed all stat checks
+
+ if (log_char) {
+ // char.log to charlog
+ sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ charlog_db,"make new char", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]);
+ //query
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ //printf("make new char %d-%d %s %d, %d, %d, %d, %d, %d - %d, %d" RETCODE,
+ // fd, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]);
+
+ //Check Name (already in use?)
+ sprintf(tmp_sql, "SELECT `name` FROM `%s` WHERE `name` = '%s'",char_db, t_name);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -2;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res){
+ temp = (int)mysql_num_rows(sql_res);
+ mysql_free_result(sql_res);
+ if (temp > 0) {
+ ShowInfo("Create char failed: charname already in use\n");
+ return -1;
+ }
+ }
+
+ // check char slot.
+ sprintf(tmp_sql, "SELECT `account_id`, `char_num` FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'",char_db, sd->account_id, dat[30]);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if(sql_res){
+ temp = (int)mysql_num_rows(sql_res);
+ mysql_free_result(sql_res);
+ if (temp > 0) {
+ ShowWarning("Create char failed (%d, slot: %d), slot already in use\n", sd->account_id, dat[30]);
+ return -2;
+ }
+ }
+
+ //New Querys [Sirius]
+ //Insert the char to the 'chardb' ^^
+ if (char_id_count) //Force initial char id. [Skotlex]
+ sprintf(tmp_sql, "INSERT INTO `%s` (`char_id`,`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
+ "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
+ "'%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
+ char_db, char_id_count, sd->account_id , dat[30] , t_name, start_zeny, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29],
+ (40 * (100 + dat[26])/100) , (40 * (100 + dat[26])/100 ), (11 * (100 + dat[27])/100), (11 * (100 + dat[27])/100), dat[33], dat[31],
+ mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y);
+ else
+ sprintf(tmp_sql, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
+ "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
+ "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
+ char_db, sd->account_id , dat[30] , t_name, start_zeny, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29],
+ (40 * (100 + dat[26])/100) , (40 * (100 + dat[26])/100 ), (11 * (100 + dat[27])/100), (11 * (100 + dat[27])/100), dat[33], dat[31],
+ mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -2; //No, stop the procedure!
+ }
+
+ if (char_id_count) //Clear this out for future inserts.
+ char_id_count = 0;
+
+ //Now we need the charid from sql!
+ sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id , dat[30] , t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ //delete the char ..(no trash in DB!)
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -2; //XD end of the (World? :P) .. charcreate (denied)
+ } else {
+ //query ok -> get the data!
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res){
+ sql_row = mysql_fetch_row(sql_res);
+ char_id = sql_row?atoi(sql_row[0]):0; //char id :)
+ mysql_free_result(sql_res);
+ if(char_id <= 0){
+ ShowError("failed (get char id..) CHARID (%d) wrong!\n", char_id);
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -2; //charcreate denied ..
+ }
+ }else{
+ //prevent to crash (if its false, and we want to free -> segfault :)
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -2; //end ...... -> charcreate failed :)
+ }
+ }
+
+ //Give the char the default items
+ //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`)
+ if (start_weapon > 0) { //add Start Weapon (Knife?)
+ sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_weapon,1,0x02,1);
+ if (mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -2;//end XD
+ }
+ }
+ if (start_armor > 0) { //Add default armor (cotton shirt?)
+ sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_armor,1,0x10,1);
+ if (mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d'", inventory_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -2; //end....
+ }
+ }
+
+ ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, dat[30], name);
+ return char_id;
+}
+
+/*----------------------------------------------------------------------------------------------------------*/
+/* Delete char - davidsiaw */
+/*----------------------------------------------------------------------------------------------------------*/
+/* Returns 0 if successful
+ * Returns < 0 for error
+ */
+int delete_char_sql(int char_id, int partner_id)
+{
+ char char_name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Name needs be escaped.
+ int account_id=0, party_id=0, guild_id=0;
+
+ sprintf(tmp_sql, "SELECT `name`,`account_id`,`party_id`,`guild_id` FROM `%s` WHERE `char_id`='%d'",char_db, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if(sql_res)
+ sql_row = mysql_fetch_row(sql_res);
+
+ if (sql_res == NULL || sql_row == NULL)
+ {
+ ShowError("delete_char_sql: Unable to fetch character data, deletion aborted.\n");
+ return -1;
+ }
+ strncpy(char_name, sql_row[0], NAME_LENGTH);
+ char_name[NAME_LENGTH-1] = '\0';
+ jstrescapecpy(t_name, char_name); //Escape string for sql use... [Skotlex]
+ account_id = atoi(sql_row[1]);
+ party_id = atoi(sql_row[2]);
+ guild_id = atoi(sql_row[3]);
+ mysql_free_result(sql_res); //Let's free this as soon as possible to avoid problems later on.
+
+ /* Divorce [Wizputer] */
+ if (partner_id) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d'",char_db,partner_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND `char_id`='%d'",inventory_db,WEDDING_RING_M,WEDDING_RING_F,partner_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ //Make the character leave the party [Skotlex]
+ if (party_id)
+ inter_party_leave(party_id, account_id, char_id);
+
+ /* delete char's pet */
+ //Delete the hatched pet if you have one...
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'",pet_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ // Komurka's suggested way to clear pets, modified by [Skotlex] (because I always personalize what I do :X)
+ //Removing pets that are in the char's inventory....
+ { //NOTE: The syntax for multi-table deletes is a bit changed between 4.0 and 4.1 regarding aliases, so we have to consider the version... [Skotlex]
+ //Since we only care about the major and minor version, a double conversion is good enough. (4.1.20 -> 4.10000)
+ double mysql_version = atof(mysql_get_server_info(&mysql_handle));
+
+ sprintf(tmp_sql,
+ "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))",
+ (mysql_version<4.1?pet_db:"p"), char_db, inventory_db, pet_db, char_id);
+
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //Removing pets that are in the char's cart....
+ sprintf(tmp_sql,
+ "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))",
+ (mysql_version<4.1?pet_db:"p"), char_db, cart_db, pet_db, char_id);
+
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ /* delete char's friends list */
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d'",friend_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete char from other's friend list */
+ //NOTE: Won't this cause problems for people who are already online? [Skotlex]
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `friend_id` = '%d'",friend_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete inventory */
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",inventory_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete cart inventory */
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",cart_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete memo areas */
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete skills */
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* delete character */
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",char_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ /* No need as we used inter_guild_leave [Skotlex]
+ // Also delete info from guildtables.
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",guild_member_db, char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ */
+
+ sprintf(tmp_sql, "SELECT `guild_id` FROM `%s` WHERE `master` = '%s'", guild_db, t_name);
+
+ if (mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else {
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res == NULL) {
+ if (mysql_errno(&mysql_handle) != 0)
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return -1;
+ } else {
+ int rows = (int)mysql_num_rows(sql_res);
+ mysql_free_result(sql_res);
+ if (rows > 0) {
+ mapif_parse_BreakGuild(0,guild_id);
+ }
+ else if (guild_id) //Leave your guild.
+ inter_guild_leave(guild_id, account_id, char_id);
+ }
+ }
+ return 0;
+}
+
+//==========================================================================================================
+
+void mmo_char_sync(void)
+{
+ ShowWarning("mmo_char_sync() - nothing to do\n");
+}
+
+// to do
+///////////////////////////
+
+int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) {
+ ShowWarning("mmo_char_sync_timer() tic - no works to do\n");
+ return 0;
+}
+
+int count_users(void) {
+ int i, users;
+
+ if (login_fd > 0 && session[login_fd]){
+ users = 0;
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ if (server_fd[i] > 0) {
+ users += server[i].users;
+ }
+ }
+ return users;
+ }
+ return 0;
+}
+
+int mmo_char_send006b(int fd, struct char_session_data *sd) {
+ int i, j, found_num = 0;
+ struct mmo_charstatus *p = NULL;
+// hehe. commented other. anyway there's no need to use older version.
+// if use older packet version just uncomment that!
+//#ifdef NEW_006b
+ const int offset = 24;
+//#else
+// int offset = 4;
+//#endif
+
+// ShowDebug("mmo_char_send006b start.. (account:%d)\n",sd->account_id);
+// printf("offset -> %d...\n",offset);
+
+ set_char_online(-1, 99,sd->account_id);
+
+ //search char.
+ sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id` = '%d'",char_db, sd->account_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ found_num = (int)mysql_num_rows(sql_res);
+// ShowInfo("number of chars: %d\n", found_num);
+ i = 0;
+ while((sql_row = mysql_fetch_row(sql_res))) {
+ sd->found_char[i] = atoi(sql_row[0]);
+ i++;
+ }
+ mysql_free_result(sql_res);
+ }
+
+// printf("char fetching end (total: %d)....\n", found_num);
+
+ 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;
+
+ if (save_log)
+ ShowInfo("Request Char Data ("CL_BOLD"%d"CL_RESET"):\n",sd->account_id);
+
+ for(i = 0; i < found_num; i++) {
+ mmo_char_fromsql_short(sd->found_char[i], char_dat);
+
+ p = &char_dat[0];
+
+ 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) = p->job_level;
+
+ WFIFOL(fd,j+20) = 0;
+ WFIFOL(fd,j+24) = 0;
+ 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->class_;
+ WFIFOW(fd,j+54) = p->hair;
+
+ // pecopeco knights/crusaders crash fix
+ if (p->class_ == 13 || p->class_ == 21 ||
+ p->class_ == 4014 || p->class_ == 4022 ||
+ p->class_ == 4036 || p->class_ == 4044)
+ WFIFOW(fd,j+56) = 0;
+ else WFIFOW(fd,j+56) = p->weapon;
+
+ 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) = p->clothes_color;
+
+ memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH);
+
+ 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));
+// printf("mmo_char_send006b end..\n");
+ return 0;
+}
+
+int 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.
+ //session eof check!
+ if(fd != login_fd)
+ session[fd]->eof = 1;
+ if(session[fd]->eof) {
+ if (fd == login_fd) {
+ ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
+ login_fd = -1;
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ sd = (struct char_session_data*)session[fd]->session_data;
+
+ // hehe. no need to set user limit on SQL version. :P
+ // but char limitation is good way to maintain server. :D
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+// printf("parse_tologin : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0));
+
+ switch(RFIFOW(fd, 0)){
+ case 0x2711:
+ if (RFIFOREST(fd) < 3)
+ return 0;
+ if (RFIFOB(fd, 2)) {
+ //printf("connect login server error : %d\n", RFIFOB(fd, 2));
+ ShowError("Can not connect to login-server.\n");
+ ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
+ ShowError("Also, please make sure your login db has the correct coounication username/passwords and the gender of the account is S.\n");
+ ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n");
+ return 0;
+ //exit(1); //fixed for server shutdown.
+ }else {
+ ShowStatus("Connected to login-server (connection #%d).\n", fd);
+ if (kick_on_disconnect)
+ set_all_offline();
+ // 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]) // if map-server online and at least 1 map
+ break;
+ if (i == MAX_MAP_SERVERS)
+ ShowStatus("Awaiting maps from map-server.\n");
+ }
+ RFIFOSKIP(fd, 3);
+ break;
+
+ case 0x2713:
+ if(RFIFOREST(fd)<51)
+ return 0;
+ 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);
+ sd->connect_until_time = (time_t)RFIFOL(fd,47);
+ memcpy(sd->email, RFIFOP(fd, 7), 40);
+ // send characters to player
+ mmo_char_send006b(i, sd);
+ } else if(isGM(sd->account_id) >= gm_allow_level) {
+ sd->connect_until_time = (time_t)RFIFOL(fd,47);
+ memcpy(sd->email, RFIFOP(fd, 7), 40);
+ // 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);
+ }
+ }
+ }
+ RFIFOSKIP(fd,51);
+ break;
+
+ case 0x2717:
+ if (RFIFOREST(fd) < 50)
+ return 0;
+ 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)) {
+ sd->connect_until_time = (time_t)RFIFOL(fd,46);
+ break;
+ }
+ }
+ }
+ RFIFOSKIP(fd,50);
+ break;
+
+ // login-server alive packet
+ case 0x2718:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ RFIFOSKIP(fd,2);
+ break;
+
+ // Receiving authentification from Freya-type login server (to avoid char->login->char)
+ case 0x2719:
+ if (RFIFOREST(fd) < 18)
+ return 0;
+ // to conserv a maximum of authentification, search if account is already authentified and replace it
+ // that will reduce multiple connection too
+ for(i = 0; i < AUTH_FIFO_SIZE; i++)
+ if (auth_fifo[i].account_id == RFIFOL(fd,2))
+ break;
+ // if not found, use next value
+ if (i == AUTH_FIFO_SIZE) {
+ if (auth_fifo_pos >= AUTH_FIFO_SIZE)
+ auth_fifo_pos = 0;
+ i = auth_fifo_pos;
+ auth_fifo_pos++;
+ }
+ //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", i, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10));
+ auth_fifo[i].account_id = RFIFOL(fd,2);
+ auth_fifo[i].char_id = 0;
+ auth_fifo[i].login_id1 = RFIFOL(fd,6);
+ auth_fifo[i].login_id2 = RFIFOL(fd,10);
+ auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified
+ auth_fifo[i].char_pos = 0;
+ auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server)
+ auth_fifo[i].ip = RFIFOL(fd,14);
+ //auth_fifo[i].map_auth = 0;
+ RFIFOSKIP(fd,18);
+ break;
+
+/* case 0x2721: // gm reply. I don't want to support this function.
+ printf("0x2721:GM reply\n");
+ {
+ int oldacc, newacc;
+ unsigned char buf[64];
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ oldacc = RFIFOL(fd, 2);
+ newacc = RFIFOL(fd, 6);
+ RFIFOSKIP(fd, 10);
+ if (newacc > 0) {
+ for(i=0;i<char_num;i++){
+ if(char_dat[i].account_id==oldacc)
+ char_dat[i].account_id=newacc;
+ }
+ }
+ WBUFW(buf,0)=0x2b0b;
+ WBUFL(buf,2)=oldacc;
+ WBUFL(buf,6)=newacc;
+ mapif_sendall(buf,10);
+// printf("char -> map\n");
+ }
+ break;
+*/
+ case 0x2723: // changesex reply (modified by [Yor])
+ if (RFIFOREST(fd) < 7)
+ return 0;
+ {
+ int acc, sex;
+ unsigned char buf[16];
+
+ acc = RFIFOL(fd,2);
+ sex = RFIFOB(fd,6);
+ RFIFOSKIP(fd, 7);
+ if (acc > 0) {
+ sprintf(tmp_sql, "SELECT `char_id`,`class`,`skill_point`,`guild_id` FROM `%s` WHERE `account_id` = '%d'",char_db, acc);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+
+ while(sql_res && (sql_row = mysql_fetch_row(sql_res))) {
+ int char_id, guild_id, jobclass, skill_point, class_;
+ char_id = atoi(sql_row[0]);
+ jobclass = atoi(sql_row[1]);
+ skill_point = atoi(sql_row[2]);
+ guild_id = atoi(sql_row[3]);
+ class_ = jobclass;
+ if (jobclass == 19 || jobclass == 20 ||
+ jobclass == 4020 || jobclass == 4021 ||
+ jobclass == 4042 || jobclass == 4043) {
+ // job modification
+ if (jobclass == 19 || jobclass == 20) {
+ class_ = (sex) ? 19 : 20;
+ } else if (jobclass == 4020 || jobclass == 4021) {
+ class_ = (sex) ? 4020 : 4021;
+ } else if (jobclass == 4042 || jobclass == 4043) {
+ class_ = (sex) ? 4042 : 4043;
+ }
+ // remove specifical skills of classes 19,20 4020,4021 and 4042,4043
+ sprintf(tmp_sql, "SELECT `lv` FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while(( sql_row = mysql_fetch_row(sql_res))) {
+ skill_point += atoi(sql_row[0]);
+ }
+ }
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ sprintf(tmp_sql, "UPDATE `%s` SET `equip` = '0' WHERE `char_id` = '%d'",inventory_db, char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d' , `skill_point`='%d' , `weapon`='0' , `shield='0' , `head_top`='0' , `head_mid`='0' , `head_bottom`='0' WHERE `char_id` = '%d'",char_db, class_, skill_point, char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if (guild_id) //If there is a guild, update the guild_member data [Skotlex]
+ inter_guild_sex_changed(guild_id, acc, char_id, sex);
+ }
+ }
+ // 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 == acc) {
+ session[i]->eof = 1;
+ break;
+ }
+ }
+ }
+
+ WBUFW(buf,0) = 0x2b0d;
+ WBUFL(buf,2) = acc;
+ WBUFB(buf,6) = sex;
+
+ mapif_sendall(buf, 7);
+ }
+ break;
+
+ // account_reg2•ÏX’Ê’m
+ case 0x2729:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ { //Receive account_reg2 registry, forward to map servers.
+ unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16];
+ memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2));
+// WBUFW(buf,0) = 0x2b11;
+ WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex]
+ mapif_sendall(buf, WBUFW(buf,2));
+ RFIFOSKIP(fd, RFIFOW(fd,2));
+ }
+ break;
+
+ // State change of account/ban notification (from login-server) by [Yor]
+ case 0x2731:
+ if (RFIFOREST(fd) < 11)
+ return 0;
+ // send to all map-servers to disconnect the player
+ {
+ unsigned char buf[16];
+ 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
+ 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)) {
+ session[i]->eof = 1;
+ break;
+ }
+ }
+ }
+ RFIFOSKIP(fd,11);
+ break;
+
+ case 0x2732:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+ unsigned char buf[32000];
+ if (gm_account != NULL)
+ aFree(gm_account);
+ gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1);
+ 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++;
+ }
+ ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num);
+ // 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;
+
+ // Receive GM accounts [Freya login server packet by Yor]
+ case 0x2733:
+ // add test here to remember that the login-server is Freya-type
+ // sprintf (login_server_type, "Freya");
+ if (RFIFOREST(fd) < 7)
+ return 0;
+ {
+ int new_level = 0;
+ for(i = 0; i < GM_num; i++)
+ if (gm_account[i].account_id == RFIFOL(fd,2)) {
+ if (gm_account[i].level != (int)RFIFOB(fd,6)) {
+ gm_account[i].level = (int)RFIFOB(fd,6);
+ new_level = 1;
+ }
+ break;
+ }
+ // if not found, add it
+ if (i == GM_num) {
+ // 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)
+ if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) {
+ if (GM_num == 0) {
+ gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account));
+ } else {
+ gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1));
+ }
+ gm_account[GM_num].account_id = RFIFOL(fd,2);
+ gm_account[GM_num].level = (int)RFIFOB(fd,6);
+ new_level = 1;
+ GM_num++;
+ if (GM_num >= 4000)
+ ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n");
+ }
+ }
+ if (new_level == 1) {
+ ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6));
+ mapif_send_gmaccounts();
+
+ //create_online_files(); // not change online file for only 1 player (in next timer, that will be done
+ // send gm acccounts level to map-servers
+ }
+ }
+ RFIFOSKIP(fd,7);
+ break;
+
+ //Login server request to kick a character out. [Skotlex]
+ case 0x2734:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ {
+ struct online_char_data* character;
+ int aid = RFIFOL(fd,2);
+ if ((character = idb_get(online_char_db, aid)) != NULL)
+ { //Kick out this player.
+ if (character->server > -1)
+ { //Kick it from the map server it is on.
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ if (!character->waiting_disconnect)
+ add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0);
+ character->waiting_disconnect = 1;
+ } else { //Manual kick from char server.
+ struct char_session_data *tsd;
+ int i;
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid)
+ {
+ WFIFOW(i,0) = 0x81;
+ WFIFOB(i,2) = 2;
+ WFIFOSET(i,3);
+ break;
+ }
+ }
+ if (i == fd_max) //Shouldn't happen, but just in case.
+ set_char_offline(99, aid);
+ }
+ }
+ RFIFOSKIP(fd,6);
+ }
+ break;
+
+ default:
+ ShowError("Unknown packet 0x%04x from login server, disconnecting.\n", RFIFOW(fd, 0));
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+
+ RFIFOFLUSH(fd);
+
+ return 0;
+}
+
+int request_accreg2(int account_id, int char_id) {
+ if (login_fd > 0) {
+ WFIFOW(login_fd, 0) = 0x272e;
+ WFIFOL(login_fd, 2) = account_id;
+ WFIFOL(login_fd, 6) = char_id;
+ WFIFOSET(login_fd, 10);
+ return 1;
+ }
+ return 0;
+}
+//Send packet forward to login-server for account saving
+int save_accreg2(unsigned char* buf, int len) {
+ if (login_fd > 0) {
+ WFIFOHEAD(login_fd, len+4);
+ memcpy(WFIFOP(login_fd,4), buf, len);
+ WFIFOW(login_fd,0) = 0x2728;
+ WFIFOW(login_fd,2) = len+4;
+ WFIFOSET(login_fd,len+4);
+ return 1;
+ }
+ return 0;
+}
+int search_mapserver(unsigned short map, long ip, short port);
+
+int parse_frommap(int fd) {
+ int i = 0, j = 0;
+ int id;
+// int auth_fifo_flag=0;
+
+ // Sometimes fd=0, and it will cause server crash. Don't know why. :(
+ if (fd <= 0) {
+ ShowError("parse_frommap error fd=%d\n", fd);
+ return 0;
+ }
+
+ for(id = 0; id < MAX_MAP_SERVERS; id++)
+ if (server_fd[id] == fd)
+ break;
+ if(id == MAX_MAP_SERVERS)
+ session[fd]->eof = 1;
+ if(session[fd]->eof) {
+ if (id < MAX_MAP_SERVERS) {
+ unsigned char buf[16384];
+ ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd);
+ //Notify other map servers that this one is gone. [Skotlex]
+ WBUFW(buf,0) = 0x2b20;
+ WBUFL(buf,4) = server[id].ip;
+ WBUFW(buf,8) = server[id].port;
+ j = 0;
+ for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ if (server[id].map[i])
+ WBUFW(buf,10+(j++)*4) = server[id].map[i];
+ if (j > 0) {
+ WBUFW(buf,2) = j * 4 + 10;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+ }
+ memset(&server[id], 0, sizeof(struct mmo_map_server));
+ sprintf(tmp_sql, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server_fd[id]);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ server_fd[id] = -1;
+ online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+// printf("parse_frommap : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd,0));
+
+ switch(RFIFOW(fd, 0)) {
+
+ // map-server alive packet
+ case 0x2718:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ RFIFOSKIP(fd,2);
+ break;
+
+ case 0x2af7:
+ RFIFOSKIP(fd,2);
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd,0) = 0x2709;
+ WFIFOSET(login_fd, 2);
+ }
+ break;
+
+ // mapserver -> map names recv.
+ case 0x2afa:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ memset(server[id].map, 0, sizeof(server[id].map));
+ j = 0;
+ for(i = 4; i < RFIFOW(fd,2); i += 4) {
+ server[id].map[j] = RFIFOW(fd,i);
+// printf("set map %d.%d : %s\n", id, j, server[id].map[j]);
+ j++;
+ }
+ i = server[id].ip;
+ {
+ unsigned char *p = (unsigned char *)&server[id].ip;
+ ShowStatus("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);
+ ShowStatus("Map-server %d loading complete.\n", id);
+ if (kick_on_disconnect)
+ set_all_offline();
+ if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID)
+ mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex]
+ }
+ WFIFOW(fd,0) = 0x2afb;
+ WFIFOB(fd,2) = 0;
+ memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); // name for wisp to player
+ WFIFOSET(fd,3+NAME_LENGTH);
+ //WFIFOSET(fd,27);
+ {
+ unsigned char buf[16384];
+ int x;
+ if (j == 0) {
+ ShowWarning("Map-Server %d have NO maps.\n", id);
+ // Transmitting maps information to the other map-servers
+ } else {
+ WBUFW(buf,0) = 0x2b04;
+ WBUFW(buf,2) = j * 4 + 10;
+ WBUFL(buf,4) = server[id].ip;
+ WBUFW(buf,8) = server[id].port;
+ memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4);
+ 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])
+ WFIFOW(fd,10+(j++)*4) = server[x].map[i];
+ if (j > 0) {
+ WFIFOW(fd,2) = j * 4 + 10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ }
+ }
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ //Packet command is now used for sc_data request. [Skotlex]
+ case 0x2afc:
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ int aid, cid;
+ aid = RFIFOL(fd,2);
+ cid = RFIFOL(fd,6);
+ RFIFOSKIP(fd, 10);
+#ifdef ENABLE_SC_SAVING
+ sprintf(tmp_sql, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
+ scdata_db, aid, cid);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ break;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ struct status_change_data data;
+ int count = 0;
+ WFIFOW(fd, 0) = 0x2b1d;
+ WFIFOL(fd, 4) = aid;
+ WFIFOL(fd, 8) = cid;
+ while((sql_row = mysql_fetch_row(sql_res)))
+ {
+ data.type = atoi(sql_row[0]);
+ data.tick = atoi(sql_row[1]);
+ data.val1 = atoi(sql_row[2]);
+ data.val2 = atoi(sql_row[3]);
+ data.val3 = atoi(sql_row[4]);
+ data.val4 = atoi(sql_row[5]);
+ memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &data, sizeof(struct status_change_data));
+ count++;
+ }
+ if (count > 0)
+ {
+ WFIFOW(fd, 2) = 14 + count*sizeof(struct status_change_data);
+ WFIFOW(fd, 12) = count;
+ WFIFOSET(fd, WFIFOW(fd,2));
+
+ //Clear the data once loaded.
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+#endif
+ break;
+ }
+ //set MAP user count
+ case 0x2afe:
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ if (RFIFOW(fd,2) != server[id].users) {
+ server[id].users = RFIFOW(fd,2);
+ ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id);
+ }
+ RFIFOSKIP(fd, 4);
+ break;
+ // set MAP user
+ case 0x2aff:
+ if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+ int i, aid, cid;
+ struct online_char_data* character;
+
+ online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown'
+ server[id].users = RFIFOW(fd,4);
+ for(i = 0; i < server[id].users; i++) {
+ aid = RFIFOL(fd,6+i*8);
+ cid = RFIFOL(fd,6+i*8+4);
+ character = idb_ensure(online_char_db, aid, create_online_char_data);
+ if (character->server > -1 && character->server != id)
+ {
+ ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n",
+ character->account_id, character->char_id, character->server, id, aid, cid);
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ }
+ character->server = id;
+ character->char_id = cid;
+ }
+ //If any chars remain in -2, they will be cleaned in the cleanup timer.
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ // char saving
+ case 0x2b01:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+ int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8);
+ i = 0;
+ //check account
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",char_db, aid, cid); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row) i = atoi(sql_row[0]);
+ if (sql_res) mysql_free_result(sql_res);
+
+ if (i == 1) {
+ memcpy(&char_dat[0], RFIFOP(fd,13), sizeof(struct mmo_charstatus));
+ mmo_char_tosql(cid, char_dat);
+ //save to DB
+ } else
+ ShowError("parse_from_map (save-char): Received data for non-existant character (%d:%d)!\n", aid, cid);
+ if (RFIFOB(fd,12)) { //Flag? Set character offline after saving [Skotlex]
+ set_char_offline(cid, aid);
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ // req char selection
+ case 0x2b02:
+ if (RFIFOREST(fd) < 18)
+ return 0;
+
+ if (auth_fifo_pos >= AUTH_FIFO_SIZE)
+ auth_fifo_pos = 0;
+
+ 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;
+
+ // request "change map server"
+ case 0x2b05:
+ if (RFIFOREST(fd) < 35)
+ return 0;
+ {
+ unsigned short name;
+ int map_id, map_fd = -1;
+ struct online_char_data* data;
+ struct mmo_charstatus* char_data;
+
+
+ name = RFIFOW(fd,18);
+ map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port.
+ if (map_id >= 0)
+ map_fd = server_fd[map_id];
+ //Char should just had been saved before this packet, so this should be safe. [Skotlex]
+ char_data = uidb_get(char_db_,RFIFOL(fd,14));
+ if (char_data == NULL)
+ { //Really shouldn't happen.
+ mmo_char_fromsql(RFIFOL(fd,14), char_dat);
+ char_data = char_dat;
+ }
+ //Tell the new map server about this player using Kevin's new auth packet. [Skotlex]
+ if (map_fd>=0 && session[map_fd] && char_data)
+ { //Send the map server the auth of this player.
+ //Update the "last map" as this is where the player must be spawned on the new map server.
+ char_data->last_point.map = RFIFOW(fd,18);
+ char_data->last_point.x = RFIFOW(fd,20);
+ char_data->last_point.y = RFIFOW(fd,22);
+
+ WFIFOW(map_fd,0) = 0x2afd;
+ WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus);
+ WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID
+ WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1
+ WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2
+ WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now?
+ memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus));
+ WFIFOSET(map_fd, WFIFOW(map_fd,2));
+ data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data);
+ data->char_id = char_data->char_id;
+ data->server = map_id; //Update server where char is.
+
+ //Reply with an ack.
+ WFIFOW(fd, 0) = 0x2b06;
+ memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+ WFIFOSET(fd, 30);
+ } else { //Reply with nak
+ WFIFOW(fd, 0) = 0x2b06;
+ memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+ WFIFOL(fd, 6) = 0; //Set login1 to 0.
+ WFIFOSET(fd, 30);
+ }
+ RFIFOSKIP(fd, 35);
+ }
+ break;
+
+ // char name check
+ case 0x2b08:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+
+ sprintf(tmp_sql, "SELECT `name` FROM `%s` WHERE `char_id`='%d'", char_db, (int)RFIFOL(fd,2));
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+
+ WFIFOW(fd,0) = 0x2b09;
+ WFIFOL(fd,2) = RFIFOL(fd,2);
+
+ if (sql_row)
+ memcpy(WFIFOP(fd,6), sql_row[0], NAME_LENGTH);
+ else
+ memcpy(WFIFOP(fd,6), unknown_char_name, NAME_LENGTH);
+ if (sql_res) mysql_free_result(sql_res);
+
+ WFIFOSET(fd,30);
+
+ RFIFOSKIP(fd,6);
+ break;
+
+ // I want become GM - fuck!
+ case 0x2b0a:
+ if(RFIFOREST(fd)<4)
+ return 0;
+ if(RFIFOREST(fd)<RFIFOW(fd,2))
+ return 0;
+ /*
+ memcpy(WFIFOP(login_fd,2),RFIFOP(fd,2),RFIFOW(fd,2)-2);
+ WFIFOW(login_fd,0)=0x2720;
+ WFIFOSET(login_fd,RFIFOW(fd,2));
+// printf("char : change gm -> login %d %s %d\n", RFIFOL(fd, 4), RFIFOP(fd, 8), RFIFOW(fd, 2));
+ */
+ ShowWarning("packet 0x2ba (become GM) is not supported by the Char-Server.\n");
+ RFIFOSKIP(fd, RFIFOW(fd, 2));
+ break;
+
+ //Packet 0x2b10 deprecated in favor of packet 0x3004 for registry saving. [Skotlex]
+ //case 0x2b10:
+
+ // Map server send information to change an email of an account -> login-server
+ case 0x2b0c:
+ if (RFIFOREST(fd) < 86)
+ return 0;
+ 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;
+
+ // Receiving from map-server a status change resquest. Transmission to login-server (by Yor)
+ case 0x2b0e:
+ if (RFIFOREST(fd) < 44)
+ return 0;
+ {
+ char character_name[NAME_LENGTH], t_name[NAME_LENGTH*2];
+ int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody)
+ memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH);
+ character_name[NAME_LENGTH-1] = '\0';
+ jstrescapecpy(t_name, character_name); //Escape string for sql use... [Skotlex]
+ // 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
+ sprintf(tmp_sql, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'",char_db, t_name);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res) {
+ if (mysql_num_rows(sql_res)) {
+ sql_row = mysql_fetch_row(sql_res);
+ memcpy(WFIFOP(fd,6), sql_row[1], NAME_LENGTH); // 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(atoi(sql_row[0]))) {
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd,0) = 0x2724;
+ WFIFOL(login_fd,2) = atoi(sql_row[0]); // 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(atoi(sql_row[0]))) {
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd, 0) = 0x2725;
+ WFIFOL(login_fd, 2) = atoi(sql_row[0]); // 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(atoi(sql_row[0]))) {
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd,0) = 0x2724;
+ WFIFOL(login_fd,2) = atoi(sql_row[0]); // 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(atoi(sql_row[0]))) {
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd, 0) = 0x272a;
+ WFIFOL(login_fd, 2) = atoi(sql_row[0]); // 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(atoi(sql_row[0]))) {
+ if (login_fd > 0) { // don't send request if no login-server
+ WFIFOW(login_fd, 0) = 0x2727;
+ WFIFOL(login_fd, 2) = atoi(sql_row[0]); // 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, NAME_LENGTH);
+ 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;
+
+ // Recieve rates [Wizputer]
+ case 0x2b16:
+ if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8))
+ return 0;
+ {
+ char motd[256], t_name[512]; //Required for jstrescapecpy [Skotlex]
+ strncpy(motd, RFIFOP(fd,10), 255); //First copy it to make sure the motd fits.
+ motd[255]='\0';
+ jstrescapecpy(t_name,motd);
+
+ sprintf(tmp_sql, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d',`motd`='%s'",
+ fd, server_name, RFIFOW(fd,2), RFIFOW(fd,4), RFIFOW(fd,6), t_name);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,8));
+ break;
+ }
+
+ // Character disconnected set online 0 [Wizputer]
+ case 0x2b17:
+ if (RFIFOREST(fd) < 6 )
+ return 0;
+ //printf("Setting %d char offline\n",RFIFOL(fd,2));
+ set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6));
+ RFIFOSKIP(fd,10);
+ break;
+ // Reset all chars to offline [Wizputer]
+ case 0x2b18:
+ ShowNotice("Map server [%d] requested to set all characters offline.\n", id);
+ set_all_offline();
+ RFIFOSKIP(fd,2);
+ break;
+ // Character set online [Wizputer]
+ case 0x2b19:
+ if (RFIFOREST(fd) < 6 )
+ return 0;
+ set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6));
+ RFIFOSKIP(fd,10);
+ break;
+
+ // Request sending of fame list
+ case 0x2b1a:
+ if (RFIFOREST(fd) < 2)
+ return 0;
+ {
+ int len = 8, num = 0;
+ unsigned char buf[32000];
+ struct fame_list fame_item;
+
+ WBUFW(buf,0) = 0x2b1b;
+ sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='10' OR `class`='4011'OR `class`='4033') ORDER BY `fame` DESC LIMIT 0,10", char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) {
+ fame_item.id = atoi(sql_row[0]);
+ fame_item.fame = atoi(sql_row[1]);
+ strncpy(fame_item.name, sql_row[2], NAME_LENGTH);
+
+ memcpy(WBUFP(buf,len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ if (++num == 10)
+ break;
+ }
+ }
+ mysql_free_result(sql_res);
+ WBUFW(buf, 6) = len; //Blacksmith block size
+
+ num = 0;
+ sprintf(tmp_sql, "SELECT `char_id`,`fame`,`name` FROM `%s` WHERE `fame`>0 AND (`class`='18' OR `class`='4019' OR `class`='4041') ORDER BY `fame` DESC LIMIT 0,10", char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) {
+ fame_item.id = atoi(sql_row[0]);
+ fame_item.fame = atoi(sql_row[1]);
+ strncpy(fame_item.name, sql_row[2], NAME_LENGTH);
+ memcpy(WBUFP(buf,len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ if (++num == 10)
+ break;
+ }
+ }
+ mysql_free_result(sql_res);
+ WBUFW(buf, 4) = len; //Alchemist block size
+
+ num = 0;
+ sprintf(tmp_sql, "SELECT `char_id`,`fame`,`name` FROM `%s` WHERE `fame`>0 AND `class`='4046' ORDER BY `fame` DESC LIMIT 0,10", char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) {
+ fame_item.id = atoi(sql_row[0]);
+ fame_item.fame = atoi(sql_row[1]);
+ strncpy(fame_item.name, sql_row[2], NAME_LENGTH);
+ memcpy(WBUFP(buf,len), &fame_item, sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ if (++num == 10)
+ break;
+ }
+ }
+ mysql_free_result(sql_res);
+ WBUFW(buf, 2) = len; //Total packet length
+
+ mapif_sendall(buf, len);
+ RFIFOSKIP(fd,2);
+ break;
+ }
+ //Request saving sc_data of a player. [Skotlex]
+ case 0x2b1c:
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+#ifdef ENABLE_SC_SAVING
+ int count, aid, cid, i;
+ struct status_change_data data;
+
+ aid = RFIFOL(fd, 4);
+ cid = RFIFOL(fd, 8);
+ count = RFIFOW(fd, 12);
+
+ sprintf(tmp_sql, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db);
+
+ for (i = 0; i < count; i++)
+ {
+ memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
+ sprintf (tmp_sql, "%s ('%d','%d','%hu','%d','%d','%d','%d','%d'),", tmp_sql, aid, cid,
+ data.type, data.tick, data.val1, data.val2, data.val3, data.val4);
+ }
+ if (count > 0)
+ {
+ tmp_sql[strlen(tmp_sql)-1] = '\0'; //Remove final comma.
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ RFIFOSKIP(fd, RFIFOW(fd, 2));
+ break;
+ }
+
+ default:
+ // inter server - packet
+ {
+ int r = inter_parse_frommap(fd);
+ if (r == 1) break; // processed
+ if (r == 2) return 0; // need more packet
+ }
+
+ // no inter server packet. no char server packet -> disconnect
+ ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int search_mapserver(unsigned short map, long ip, short port) {
+ int i, j;
+
+ if (!map)
+ return -1;
+
+ for(i = 0; i < MAX_MAP_SERVERS; i++)
+ if (server_fd[i] > 0)
+ for (j = 0; server[i].map[j]; j++)
+ if (server[i].map[j] == map) {
+ if (ip > 0 && server[i].ip != ip)
+ continue;
+ if (port > 0 && server[i].port != port)
+ continue;
+ return i;
+ }
+
+ return -1;
+}
+
+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;
+ int subneti[4];
+ unsigned int k0, k1, k2, k3;
+
+ sscanf(lan_map_ip, "%d.%d.%d.%d", &k0, &k1, &k2, &k3);
+ subneti[0] = k0; subneti[1] = k1; subneti[2] = k2; subneti[3] = k3;
+
+// 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;
+ }
+ }
+ return lancheck;
+}
+
+int parse_char(int fd) {
+ int i, ch = 0;
+ char email[40];
+ unsigned char buf[64];
+ unsigned short cmd;
+ int map_fd;
+ struct char_session_data *sd;
+ unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr;
+
+ sd = (struct char_session_data*)session[fd]->session_data;
+
+ if(login_fd < 0)
+ session[fd]->eof = 1;
+ if(session[fd]->eof) {
+ if (fd == login_fd)
+ login_fd = -1;
+ if (sd != NULL)
+ {
+ struct online_char_data* data = idb_get(online_char_db, sd->account_id);
+ if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex]
+ set_char_offline(99,sd->account_id);
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
+ cmd = RFIFOW(fd,0);
+ // crc32‚̃XƒLƒbƒv—p
+ if( sd==NULL && // –¢ƒƒOƒCƒ“orŠÇ—ƒpƒPƒbƒg
+ RFIFOREST(fd)>=4 && // Å’áƒoƒCƒg”§ŒÀ • 0x7530,0x7532ŠÇ—ƒpƒPœ‹Ž
+ RFIFOREST(fd)<=21 && // Å‘åƒoƒCƒg”§ŒÀ • ƒT[ƒo[ƒƒOƒCƒ“œ‹Ž
+ cmd!=0x20b && // md5’Ê’mƒpƒPƒbƒgœ‹Ž
+ (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // ŽŸ‚ɉ½‚©ƒpƒPƒbƒg‚ª—ˆ‚Ä‚é‚È‚çAÚ‘±‚Å‚È‚¢‚Æ‚¾‚ß
+ RFIFOSKIP(fd,4);
+ cmd = RFIFOW(fd,0);
+ ShowDebug("parse_char : %d crc32 skipped\n",fd);
+ if(RFIFOREST(fd)==0)
+ return 0;
+ }
+
+//For use in packets that depend on an sd being present [Skotlex]
+#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } }
+
+ switch(cmd){
+ case 0x20b: //20040622 encryption ragexe correspondence
+ if (RFIFOREST(fd) < 19)
+ return 0;
+ RFIFOSKIP(fd,19);
+ break;
+
+ case 0x65: // request to connect
+ ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10));
+ if (RFIFOREST(fd) < 17)
+ return 0;
+ {
+ if (sd == NULL) {
+ CREATE(session[fd]->session_data, struct char_session_data, 1);
+ sd = (struct char_session_data*)session[fd]->session_data;
+ 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->sex = RFIFOB(fd, 16);
+
+ WFIFOL(fd, 0) = RFIFOL(fd, 2);
+ WFIFOSET(fd, 4);
+
+ 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 (online_check)
+ { // check if character is not online already. [Skotlex]
+ struct online_char_data* character;
+ character = idb_get(online_char_db, sd->account_id);
+
+ if (character)
+ {
+ if (character->server > -1)
+ { //Character already online. KICK KICK KICK
+ mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2);
+ if (!character->waiting_disconnect)
+ add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0);
+ character->waiting_disconnect = 1;
+ /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex]
+ } else { //Kick from char server.
+ struct char_session_data *tsd;
+ int i;
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && i != fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id)
+ {
+ WFIFOW(i,0) = 0x81;
+ WFIFOB(i,2) = 2;
+ WFIFOSET(i,3);
+ break;
+ }
+ if (i == fd_max) //Shouldn't happen, but just in case.
+ set_char_offline(99, sd->account_id);
+ }
+ */
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 8;
+ WFIFOSET(fd,3);
+ break;
+ }
+ }
+ }
+
+ 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);
+ }
+ // 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);
+ }
+// printf("connection request> set delflag 1(o:2)- account_id:%d/login_id1:%d(fifo_id:%d)\n", sd->account_id, sd->login_id1, i);
+ break;
+ }
+ }
+ 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;
+ 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;
+ WFIFOB(fd,2) = 0;
+ WFIFOSET(fd,3);
+ }
+ }
+ }
+ RFIFOSKIP(fd, 17);
+ break;
+
+ case 0x66: // char select
+ FIFOSD_CHECK(3);
+
+ sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'",char_db, sd->account_id, RFIFOB(fd, 2));
+ RFIFOSKIP(fd, 3);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+
+ if (sql_row)
+ {
+ mmo_char_fromsql(atoi(sql_row[0]), char_dat);
+ char_dat[0].sex = sd->sex;
+ } else {
+ mysql_free_result(sql_res);
+ break;
+ }
+
+ if (log_char) {
+ char escaped_name[NAME_LENGTH*2];
+ sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')",
+ charlog_db, sd->account_id, RFIFOB(fd, 2), jstrescapecpy(escaped_name, char_dat[0].name));
+ //query
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo("Selected char: (Account %d: %d - %s)" RETCODE, sd->account_id, RFIFOB(fd, 2), char_dat[0].name);
+
+ i = search_mapserver(char_dat[0].last_point.map, -1, -1);
+
+ // if map is not found, we check major cities
+ if (i < 0) {
+ unsigned short j;
+ ShowWarning("Unable to find map-server for '%s', resorting to sending to a major city.\n", mapindex_id2name(char_dat[0].last_point.map));
+ if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 273; // savepoint coordinates
+ char_dat[0].last_point.y = 354;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 120; // savepoint coordinates
+ char_dat[0].last_point.y = 100;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 160; // savepoint coordinates
+ char_dat[0].last_point.y = 94;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 116; // savepoint coordinates
+ char_dat[0].last_point.y = 57;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 87; // savepoint coordinates
+ char_dat[0].last_point.y = 117;
+ } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) {
+ char_dat[0].last_point.map = j;
+ char_dat[0].last_point.x = 94; // savepoint coordinates
+ char_dat[0].last_point.y = 103;
+ } else {
+ // get first online server
+ i = 0;
+ for(j = 0; j < MAX_MAP_SERVERS; j++)
+ if (server_fd[j] > 0 && server[j].map[0]) {
+ i = j;
+ ShowDebug("Map-server #%d found with a map: '%s'.\n", j, server[j].map[0]);
+ break;
+ }
+ // if no map-servers are connected, we send: server closed
+ if (j == MAX_MAP_SERVERS) {
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ break;
+ }
+ }
+ }
+ WFIFOW(fd, 0) =0x71;
+ WFIFOL(fd, 2) =char_dat[0].char_id;
+ memcpy(WFIFOP(fd,6), mapindex_id2name(char_dat[0].last_point.map), MAP_NAME_LENGTH);
+ //Lan check added by Kashy
+ 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;
+ }
+ auth_fifo[auth_fifo_pos].account_id = sd->account_id;
+ auth_fifo[auth_fifo_pos].char_id = char_dat[0].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 = 0;
+ 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;
+
+ //Send NEW auth packet [Kevin]
+ if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL)
+ {
+ ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
+ server_fd[i] = -1;
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ break;
+ }
+
+ WFIFOW(map_fd,0) = 0x2afd;
+ WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus);
+ WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id;
+ WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1;
+ WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2;
+ WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time;
+ memcpy(WFIFOP(map_fd,20), &char_dat[0], sizeof(struct mmo_charstatus));
+ WFIFOSET(map_fd, WFIFOW(map_fd,2));
+
+ set_char_online(i, auth_fifo[auth_fifo_pos].char_id, auth_fifo[auth_fifo_pos].account_id);
+ //Checks to see if the even share setting of the party must be broken.
+ inter_party_logged(char_dat[0].party_id, char_dat[0].account_id, char_dat[0].char_id);
+ auth_fifo_pos++;
+ break;
+
+ case 0x67: // make new
+ FIFOSD_CHECK(37);
+
+ if(char_new == 0) //turn character creation on/off [Kevin]
+ i = -2;
+ else
+ i = make_new_char_sql(fd, RFIFOP(fd, 2));
+
+ //if (i < 0) {
+ // WFIFOW(fd, 0) = 0x6e;
+ // WFIFOB(fd, 2) = 0x00;
+ // WFIFOSET(fd, 3);
+ // RFIFOSKIP(fd, 37);
+ // break;
+ //}
+ //Changed that we can support 'Charname already exists' (-1) amd 'Char creation denied' (-2)
+ //And 'You are underaged' (-3) (XD) [Sirius]
+ if(i == -1){
+ //already exists
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x00;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }else if(i == -2){
+ //denied
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x02;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }else if(i == -3){
+ //underaged XD
+ WFIFOW(fd, 0) = 0x6e;
+ WFIFOB(fd, 2) = 0x01;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 37);
+ break;
+ }
+
+ WFIFOW(fd, 0) = 0x6d;
+ memset(WFIFOP(fd, 2), 0x00, 106);
+
+ mmo_char_fromsql_short(i, char_dat); //Only the short data is needed.
+ //mmo_char_fromsql(i, char_dat);
+ i = 0;
+ 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].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, NAME_LENGTH);
+
+ 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);
+
+ //to do
+ for(ch = 0; ch < 9; ch++) {
+ if (sd->found_char[ch] == -1) {
+ sd->found_char[ch] = char_dat[i].char_id;
+ break;
+ }
+ }
+ break;
+ case 0x68: /* delete char */
+ FIFOSD_CHECK(46);
+ {
+ int cid = RFIFOL(fd,2);
+ ShowInfo(CL_RED" Request Char Deletion:"CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid);
+ memcpy(email, RFIFOP(fd,6), 40);
+
+ /* Check if e-mail is correct */
+ if(strcmpi(email, sd->email)){
+ if(strcmp("a@a.com", sd->email) == 0){
+ if(strcmp("a@a.com", email) == 0 || strcmp("", email) == 0){
+ //ignore
+ }else{
+ //del fail
+ WFIFOW(fd, 0) = 0x70;
+ WFIFOB(fd, 2) = 0;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 46);
+ break;
+ }
+ }else{
+ //del fail
+ WFIFOW(fd, 0) = 0x70;
+ WFIFOB(fd, 2) = 0;
+ WFIFOSET(fd, 3);
+ RFIFOSKIP(fd, 46);
+ break;
+ }
+ }
+
+ for(i = 0; i < 9; i++) {
+ /* Debug:
+ printf("Checking if char to be deleted: %d - %d (%d)\n", sd->found_char[i], RFIFOL(fd, 2), sd->account_id);
+ */
+ if (sd->found_char[i] == cid) {
+ for(ch = i; ch < 9-1; ch++)
+ sd->found_char[ch] = sd->found_char[ch+1];
+ sd->found_char[8] = -1;
+ break;
+ }
+ }
+ /* Such a character does not exist in the account */
+ /* If so, you are so screwed. */
+ if (i == 9) {
+ WFIFOW(fd, 0) = 0x70;
+ WFIFOB(fd, 2) = 0;
+ WFIFOSET(fd, 3);
+ break;
+ }
+
+ /* Grab the partner id */
+ sprintf(tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id`='%d'",char_db, cid);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if(sql_res)
+ {
+ int char_pid=0;
+ sql_row = mysql_fetch_row(sql_res);
+ if (sql_row)
+ char_pid = atoi(sql_row[0]);
+ mysql_free_result(sql_res);
+
+ /* Delete character and partner (if any) */
+ delete_char_sql(cid, char_pid);
+ if (char_pid != 0)
+ { /* If there is partner, tell map server to do divorce */
+ WBUFW(buf,0) = 0x2b12;
+ WBUFL(buf,2) = RFIFOL(fd,2);
+ WBUFL(buf,6) = char_pid;
+ mapif_sendall(buf,10);
+ }
+ }
+ /* Char successfully deleted. <- For sure? There could had been an sql db error, what is done then?. [Skotlex] */
+ WFIFOW(fd, 0) = 0x6f;
+ WFIFOSET(fd, 2);
+
+ RFIFOSKIP(fd, 46);
+ break;
+ }
+ case 0x2af8: // login as map-server
+ if (RFIFOREST(fd) < 60)
+ return 0;
+ WFIFOW(fd, 0) = 0x2af9;
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ if (server_fd[i] <= 0)
+ break;
+ }
+ if (i == MAX_MAP_SERVERS || strcmp((const char*)RFIFOP(fd,2), userid) || strcmp((const char*)RFIFOP(fd,26), passwd)) {
+ WFIFOB(fd,2) = 3;
+ WFIFOSET(fd, 3);
+ } else {
+// int len;
+ WFIFOB(fd,2) = 0;
+ WFIFOSET(fd, 3);
+ session[fd]->func_parse = parse_frommap;
+ server_fd[i] = fd;
+ 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));
+ realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+ char_mapif_init(fd);
+ // send gm acccounts level to map-servers
+/* removed by CLOWNISIUS due to isGM
+ 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);*/
+ }
+ RFIFOSKIP(fd,60);
+ break;
+
+ case 0x187: // Alive?
+ if (RFIFOREST(fd) < 6) {
+ return 0;
+ }
+ RFIFOSKIP(fd, 6);
+ break;
+
+ case 0x7530: // Athena info get
+ 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 0;
+
+ case 0x7532: // disconnect(default also disconnect)
+ default:
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ RFIFOFLUSH(fd);
+
+ return 0;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)aMalloc(64);
+ command = (char *)aMalloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ ShowNotice("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ ShowNotice("Type of command: %s || Command: %s \n",type,command);
+
+ if(buf) aFree(buf);
+ if(type) aFree(type);
+ if(command) aFree(command);
+
+ return 0;
+}
+
+// MAP send all
+int mapif_sendall(unsigned char *buf, unsigned int len) {
+ int i, c;
+ int fd;
+
+ c = 0;
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ if ((fd = server_fd[i]) > 0) { //0 Should not be a valid server_fd [Skotlex]
+ if (session[fd] == NULL)
+ { //Could this be the crash's source? [Skotlex]
+ ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i);
+ server_fd[i] = -1;
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ continue;
+ }
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ c++;
+ }
+ }
+
+ return c;
+}
+
+int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) {
+ int i, c;
+ int fd;
+
+ c = 0;
+ for(i=0, c=0;i<MAX_MAP_SERVERS;i++){
+ if ((fd = server_fd[i]) > 0 && fd != sfd) {
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd, len);
+ c++;
+ }
+ }
+
+ return c;
+}
+
+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]) {
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int send_users_tologin(int tid, unsigned int tick, int id, int data) {
+ int users = count_users();
+ unsigned 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);
+
+ return 0;
+}
+
+static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) {
+ struct online_char_data* character = (struct online_char_data*)data;
+ int *i = va_arg(ap, int*);
+ int count = va_arg(ap, int);
+ if ((*i) >= count)
+ return 0; //This is an error that shouldn't happen....
+ if(character->server > -1) {
+ WFIFOHEAD(login_fd, 8+count*4);
+ WFIFOL(login_fd, 8+(*i)*4) =character->account_id;
+ (*i)++;
+ return 1;
+ }
+ return 0;
+}
+
+int send_accounts_tologin(int tid, unsigned int tick, int id, int data) {
+ int users = count_users(), i=0;
+
+ if (login_fd > 0 && session[login_fd]) {
+ // send account list to login server
+ WFIFOHEAD(login_fd, 8+users*4);
+ WFIFOW(login_fd,0) = 0x272d;
+ WFIFOL(login_fd,4) = users;
+ online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i, users);
+ WFIFOW(login_fd,2) = 8+ i*4;
+ if (i > 0)
+ WFIFOSET(login_fd,WFIFOW(login_fd,2));
+ }
+ return 0;
+}
+
+int check_connect_login_server(int tid, unsigned int tick, int id, int data) {
+ if (login_fd <= 0 || session[login_fd] == NULL) {
+ struct char_session_data *sd;
+ int i, cc;
+ unsigned char buf[16];
+
+ ShowInfo("Attempt to connect to login-server...\n");
+ login_fd = make_connection(login_ip, login_port);
+ if (login_fd == -1)
+ { //Try again later. [Skotlex]
+ login_fd = 0;
+ return 0;
+ }
+ 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_display; //only display (New) if they want to [Kevin]
+
+ WFIFOSET(login_fd,86);
+
+ //(re)connected to login-server,
+ //now wi'll look in sql which player's are ON and set them OFF
+ //AND send to all mapservers (if we have one / ..) to kick the players
+ //so the bug is fixed, if'ure using more than one charservers (worlds)
+ //that the player'S got reejected from server after a 'world' crash^^
+ //2b1f AID.L B1
+
+ sprintf(tmp_sql, "SELECT `account_id`, `online` FROM `%s` WHERE `online` = '1'", char_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ if(sql_res){
+ cc = (int)mysql_num_rows(sql_res);
+ ShowStatus("Setting %d Players offline\n", cc);
+ while((sql_row = mysql_fetch_row(sql_res))){
+ //sql_row[0] == AID
+ //tell the loginserver
+ WFIFOW(login_fd, 0) = 0x272c; //set off
+ WFIFOL(login_fd, 2) = atoi(sql_row[0]); //AID
+ WFIFOSET(login_fd, 6);
+
+ //tell map to 'kick' the player (incase of 'on' ..)
+ WBUFW(buf, 0) = 0x2b1f;
+ WBUFL(buf, 2) = atoi(sql_row[0]);
+ WBUFB(buf, 6) = 1;
+ mapif_sendall(buf, 7);
+
+ //kick the player if he's on charselect
+ for(i = 0; i < fd_max; i++){
+ if(session[i] && (sd = (struct char_session_data*)session[i]->session_data)){
+ if(sd->account_id == atoi(sql_row[0])){
+ session[i]->eof = 1;
+ break;
+ }
+ }
+ }
+
+ }
+ mysql_free_result(sql_res);
+ }else{
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ //Now Update all players to 'OFFLINE'
+ sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", char_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", guild_member_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql, "UPDATE `%s` SET `connect_member` = '0'", guild_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ }
+ return 0;
+}
+
+//------------------------------------------------
+//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't
+//replies/disconnect the player we tried to kick. [Skotlex]
+//------------------------------------------------
+static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data)
+{
+ struct online_char_data* character;
+ if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect)
+ { //Mark it offline due to timeout.
+ set_char_offline(character->char_id, character->account_id);
+ }
+ return 0;
+}
+
+//----------------------------------------------------------
+// Return numerical value of a switch configuration by [Yor]
+// on/off, english, français, deutsch, español
+//----------------------------------------------------------
+int config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+
+ return atoi(str);
+}
+
+// Lan Support conf reading added by Kashy
+int char_lan_config_read(const char *lancfgName){
+ char subnetmask[128];
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ struct hostent * h = NULL;
+
+ if ((fp = fopen(lancfgName, "r")) == NULL) {
+ ShowError("file not found: %s\n", lancfgName);
+ return 1;
+ }
+
+ ShowInfo("Reading file %s...\n", lancfgName);
+
+ while(fgets(line, sizeof(line)-1, fp)){
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+
+ else if (strcmpi(w1, "lan_map_ip") == 0) {
+ 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;
+ }
+ ShowStatus("set Lan_map_IP : %s\n", lan_map_ip);
+ }
+
+ else if (strcmpi(w1, "subnetmask") == 0) {
+ unsigned int k0, k1, k2, k3;
+ strcpy(subnetmask, w2);
+ sscanf(subnetmask, "%d.%d.%d.%d", &k0, &k1, &k2, &k3);
+ subnetmaski[0] = k0; subnetmaski[1] = k1; subnetmaski[2] = k2; subnetmaski[3] = k3;
+ ShowStatus("set subnetmask : %s\n", w2);
+ }
+ }
+ fclose(fp);
+
+ ShowInfo("Done reading %s.\n", lancfgName);
+ return 0;
+}
+
+void do_final(void) {
+ ShowInfo("Doing final stage...\n");
+ //mmo_char_sync();
+ //inter_save();
+ do_final_itemdb();
+ //check SQL save progress.
+ //wait until save char complete
+
+ set_all_offline();
+
+ inter_final();
+
+ flush_fifos();
+
+ mapindex_final();
+
+ sprintf(tmp_sql,"DELETE FROM `ragsrvinfo");
+ if (mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if(gm_account) {
+ aFree(gm_account);
+ gm_account = 0;
+ }
+
+ if(char_dat) {
+ aFree(char_dat);
+ char_dat = 0;
+ }
+
+ delete_session(login_fd);
+ delete_session(char_fd);
+ char_db_->destroy(char_db_, NULL);
+ online_char_db->destroy(online_char_db, NULL);
+
+ mysql_close(&mysql_handle);
+ mysql_close(&lmysql_handle);
+
+ ShowInfo("ok! all done...\n");
+}
+
+void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ ShowInfo("Reading file %s...\n", cfgName);
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowFatalError("file not found: %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)
+ continue;
+
+ if(strcmpi(w1,"char_db")==0){
+ strcpy(char_db,w2);
+ }else if(strcmpi(w1,"scdata_db")==0){
+ strcpy(scdata_db,w2);
+ }else if(strcmpi(w1,"cart_db")==0){
+ strcpy(cart_db,w2);
+ }else if(strcmpi(w1,"inventory_db")==0){
+ strcpy(inventory_db,w2);
+ }else if(strcmpi(w1,"charlog_db")==0){
+ strcpy(charlog_db,w2);
+ }else if(strcmpi(w1,"storage_db")==0){
+ strcpy(storage_db,w2);
+ }else if(strcmpi(w1,"reg_db")==0){
+ strcpy(reg_db,w2);
+ }else if(strcmpi(w1,"skill_db")==0){
+ strcpy(skill_db,w2);
+ }else if(strcmpi(w1,"interlog_db")==0){
+ strcpy(interlog_db,w2);
+ }else if(strcmpi(w1,"memo_db")==0){
+ strcpy(memo_db,w2);
+ }else if(strcmpi(w1,"guild_db")==0){
+ strcpy(guild_db,w2);
+ }else if(strcmpi(w1,"guild_alliance_db")==0){
+ strcpy(guild_alliance_db,w2);
+ }else if(strcmpi(w1,"guild_castle_db")==0){
+ strcpy(guild_castle_db,w2);
+ }else if(strcmpi(w1,"guild_expulsion_db")==0){
+ strcpy(guild_expulsion_db,w2);
+ }else if(strcmpi(w1,"guild_member_db")==0){
+ strcpy(guild_member_db,w2);
+ }else if(strcmpi(w1,"guild_skill_db")==0){
+ strcpy(guild_skill_db,w2);
+ }else if(strcmpi(w1,"guild_position_db")==0){
+ strcpy(guild_position_db,w2);
+ }else if(strcmpi(w1,"guild_storage_db")==0){
+ strcpy(guild_storage_db,w2);
+ }else if(strcmpi(w1,"party_db")==0){
+ strcpy(party_db,w2);
+ }else if(strcmpi(w1,"pet_db")==0){
+ strcpy(pet_db,w2);
+ }else if(strcmpi(w1,"friend_db")==0){
+ strcpy(friend_db,w2);
+ }else if(strcmpi(w1,"db_path")==0){
+ strcpy(db_path,w2);
+ //Map server option to use SQL db or not
+ }else if(strcmpi(w1,"use_sql_db")==0){ // added for sql item_db read for char server [Valaris]
+ db_use_sqldbs = config_switch(w2);
+ ShowStatus("Using SQL dbs: %s\n",w2);
+ //custom columns for login database
+ }else if(strcmpi(w1,"login_db_level")==0){
+ strcpy(login_db_level,w2);
+ }else if(strcmpi(w1,"login_db_account_id")==0){
+ strcpy(login_db_account_id,w2);
+ }else if(strcmpi(w1,"lowest_gm_level")==0){
+ lowest_gm_level = atoi(w2);
+ ShowStatus("set lowest_gm_level : %s\n",w2);
+ //support the import command, just like any other config
+ }else if(strcmpi(w1,"import")==0){
+ sql_config_read(w2);
+ }
+
+ }
+ fclose(fp);
+ ShowInfo("done reading %s.\n", cfgName);
+}
+
+int char_config_read(const char *cfgName) {
+ struct hostent *h = NULL;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowFatalError("Configuration file not found: %s.\n", cfgName);
+ exit(1);
+ }
+
+ ShowInfo("Reading 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)
+ continue;
+
+ remove_control_chars((unsigned char *) w1);
+ remove_control_chars((unsigned char *) w2);
+ if(strcmpi(w1,"timestamp_format")==0) {
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "userid") == 0) {
+ memcpy(userid, w2, 24);
+ } else if (strcmpi(w1, "passwd") == 0) {
+ memcpy(passwd, w2, 24);
+ } else if (strcmpi(w1, "server_name") == 0) {
+ memcpy(server_name, w2, sizeof(server_name));
+ server_name[sizeof(server_name) - 1] = '\0';
+ ShowStatus("%s server has been initialized\n", w2);
+ } else if (strcmpi(w1, "wisp_server_name") == 0) {
+ if (strlen(w2) >= 4) {
+ memcpy(wisp_server_name, w2, sizeof(wisp_server_name));
+ wisp_server_name[sizeof(wisp_server_name) - 1] = '\0';
+ }
+ } else if (strcmpi(w1, "login_ip") == 0) {
+ login_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowStatus("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 (strcmpi(w1, "login_port") == 0) {
+ login_port=atoi(w2);
+ } else if (strcmpi(w1, "char_ip") == 0) {
+ char_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if(h != NULL) {
+ ShowStatus("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 (strcmpi(w1, "bind_ip") == 0) {
+ bind_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if(h != NULL) {
+ ShowStatus("Character server binding 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(bind_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(bind_ip_str, w2, 16);
+ } else if (strcmpi(w1, "char_port") == 0) {
+ char_port = atoi(w2);
+ } else if (strcmpi(w1, "char_maintenance") == 0) {
+ char_maintenance = atoi(w2);
+ } else if (strcmpi(w1, "char_new")==0){
+ char_new = atoi(w2);
+ } else if (strcmpi(w1, "char_new_display")==0){
+ char_new_display = atoi(w2);
+ } else if (strcmpi(w1, "max_connect_user") == 0) {
+ max_connect_user = atoi(w2);
+ if (max_connect_user < 0)
+ max_connect_user = 0; // unlimited online players
+ } else if(strcmpi(w1, "gm_allow_level") == 0) {
+ gm_allow_level = atoi(w2);
+ if(gm_allow_level < 0)
+ gm_allow_level = 99;
+ } else if (strcmpi(w1, "check_ip_flag") == 0) {
+ check_ip_flag = config_switch(w2);
+ } else if (strcmpi(w1, "online_check") == 0) {
+ online_check = config_switch(w2);
+ } else if (strcmpi(w1, "autosave_time") == 0) {
+ autosave_interval = atoi(w2)*1000;
+ if (autosave_interval <= 0)
+ autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+ } else if (strcmpi(w1, "save_log") == 0) {
+ save_log = config_switch(w2);
+ } else if (strcmpi(w1, "start_point") == 0) {
+ char map[MAP_NAME_LENGTH];
+ int x, y;
+ if (sscanf(w2,"%16[^,],%d,%d", map, &x, &y) < 3)
+ continue;
+ if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name
+ start_point.map = mapindex_name2id(map);
+ if (!start_point.map)
+ ShowError("Specified start_point %s not found in map-index cache.\n", map);
+ start_point.x = x;
+ start_point.y = y;
+ }
+ } else if (strcmpi(w1, "start_zeny") == 0) {
+ start_zeny = atoi(w2);
+ if (start_zeny < 0)
+ start_zeny = 0;
+ } else if (strcmpi(w1, "start_weapon") == 0) {
+ start_weapon = atoi(w2);
+ if (start_weapon < 0)
+ start_weapon = 0;
+ } else if (strcmpi(w1, "start_armor") == 0) {
+ start_armor = atoi(w2);
+ if (start_armor < 0)
+ start_armor = 0;
+ } else if(strcmpi(w1,"log_char")==0){ //log char or not [devil]
+ log_char = atoi(w2);
+ } else if (strcmpi(w1, "unknown_char_name") == 0) {
+ strcpy(unknown_char_name, w2);
+ unknown_char_name[NAME_LENGTH-1] = 0;
+ } else if (strcmpi(w1, "name_ignoring_case") == 0) {
+ name_ignoring_case = config_switch(w2);
+ } else if (strcmpi(w1, "char_name_option") == 0) {
+ char_name_option = atoi(w2);
+ } else if (strcmpi(w1, "char_name_letters") == 0) {
+ strcpy(char_name_letters, w2);
+ } else if (strcmpi(w1, "check_ip_flag") == 0) {
+ check_ip_flag = config_switch(w2);
+ } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
+ char_per_account = atoi(w2);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ } else if (strcmpi(w1, "import") == 0) {
+ char_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ ShowInfo("Done reading %s.\n", cfgName);
+
+ return 0;
+}
+
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_CHAR;
+}
+
+static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
+{
+ struct online_char_data *character= (struct online_char_data*)data;
+ if (character->server == -2) //Unknown server.. set them offline
+ set_char_offline(character->char_id, character->account_id);
+ if (character->server < 0)
+ //Free data from players that have not been online for a while.
+ db_remove(online_char_db, key);
+ return 0;
+}
+
+static int online_data_cleanup(int tid, unsigned int tick, int id, int data)
+{
+ online_char_db->foreach(online_char_db, online_data_cleanup_sub);
+ return 0;
+}
+
+int do_init(int argc, char **argv){
+ int i;
+
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ server_fd[i] = -1;
+ }
+
+ //Read map indexes
+ mapindex_init();
+ start_point.map = mapindex_name2id("new_1-1.gat");
+
+ char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]);
+ char_lan_config_read((argc > 1) ? argv[1] : LAN_CONF_NAME);
+ sql_config_read(SQL_CONF_NAME);
+
+ ShowInfo("Finished reading the char-server configuration.\n");
+
+ inter_init((argc > 2) ? argv[2] : inter_cfgName); // inter server ÃʱâÈ­
+ ShowInfo("Finished reading the inter-server configuration.\n");
+
+ //Read ItemDB
+ do_init_itemdb();
+
+ ShowInfo("Initializing char server.\n");
+ online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ mmo_char_sql_init();
+ ShowInfo("char server initialized.\n");
+
+// ShowDebug("set parser -> parse_char()...\n");
+ set_defaultparse(parse_char);
+
+// ShowDebug("set terminate function -> do_final().....\n");
+
+ if ((naddr_ != 0) && (login_ip_set_ == 0 || char_ip_set_ == 0)) {
+ // The char server should know what IP address it is running on
+ // - MouseJstr
+ int localaddr = ntohl(addr_[0]);
+ unsigned char *ptr = (unsigned char *) &localaddr;
+ char buf[16];
+ sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
+ if (naddr_ != 1)
+ ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ ShowStatus("Defaulting to %s as our IP address\n", buf);
+ if (login_ip_set_ == 0)
+ strcpy(login_ip_str, buf);
+ if (char_ip_set_ == 0)
+ strcpy(char_ip_str, buf);
+
+ if (ptr[0] == 192 && ptr[1] == 168)
+ ShowWarning("Firewall detected.. edit lan_support.conf and char_athena.conf\n");
+ }
+
+ login_ip = inet_addr(login_ip_str);
+ char_ip = inet_addr(char_ip_str);
+
+ ShowInfo("open port %d.....\n",char_port);
+ //char_fd = make_listen_port(char_port);
+ if (bind_ip_set_)
+ char_fd = make_listen_bind(inet_addr(bind_ip_str),char_port);
+ else
+ char_fd = make_listen_bind(INADDR_ANY,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(send_accounts_tologin, "send_accounts_tologin");
+ add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect");
+
+ add_timer_func_list(online_data_cleanup, "online_data_cleanup");
+ add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000);
+
+ // send ALIVE PING to login server.
+ add_timer_interval(gettick() + 10, check_connect_login_server, 0, 0, 10 * 1000);
+ // send USER COUNT PING to login server.
+ add_timer_interval(gettick() + 10, send_users_tologin, 0, 0, 5 * 1000);
+ add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour.
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ //Cleaning the tables for NULL entrys @ startup [Sirius]
+ //Chardb clean
+ ShowInfo("Cleaning the '%s' table...\n", char_db);
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `account_id` = '0'", char_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //guilddb clean
+ ShowInfo("Cleaning the '%s' table...\n", guild_db);
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_lv` = '0' AND `max_member` = '0' AND `exp` = '0' AND `next_exp` = '0' AND `average_lv` = '0'", guild_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //guildmemberdb clean
+ ShowInfo("Cleaning the '%s' table...\n", guild_member_db);
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '0' AND `account_id` = '0' AND `char_id` = '0'", guild_member_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ ShowInfo("End of char server initilization function.\n");
+ ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port);
+ return 0;
+}
+
+#undef mysql_query
+
+int debug_mysql_query(char *file, int line, void *mysql, const char *q) {
+#ifdef TWILIGHT
+ ShowDebug("sql: %s:%d# %s\n", file, line, q);
+#endif
+ return mysql_query((MYSQL *) mysql, q);
+}
+
+int char_child(int parent_id, int child_id) {
+ int tmp_id = 0;
+ sprintf (tmp_sql, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id);
+ if (mysql_query (&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result (&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row (sql_res):NULL;
+ if (sql_row)
+ tmp_id = atoi (sql_row[0]);
+ else
+ ShowError("CHAR: child Failed!\n");
+ if (sql_res) mysql_free_result (sql_res);
+ if ( tmp_id == child_id )
+ return 1;
+ else
+ return 0;
+}
+
+int char_married(int pl1,int pl2) {
+ int tmp_id = 0;
+ sprintf (tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1);
+ if (mysql_query (&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result (&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row (sql_res):NULL;
+ if (sql_row)
+ tmp_id = atoi (sql_row[0]);
+ else
+ ShowError("CHAR: married Failed!\n");
+ if (sql_res) mysql_free_result (sql_res);
+ if ( tmp_id == pl2 )
+ return 1;
+ else
+ return 0;
+}
+
+int char_nick2id (char *name) {
+ int char_id = 0;
+ sprintf (tmp_sql, "SELECT `char_id` FROM `%s` WHERE `name` = '%s'", char_db, name);
+ if (mysql_query (&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result (&mysql_handle);
+ sql_row = sql_res?mysql_fetch_row(sql_res):NULL;
+ if (sql_row)
+ char_id = atoi (sql_row[0]);
+ else
+ ShowError ("CHAR: nick2id Failed!\n");
+ if (sql_res) mysql_free_result (sql_res);
+ return char_id;
+}
diff --git a/src/char_sql/char.h b/src/char_sql/char.h
new file mode 100644
index 000000000..db1a0ca95
--- /dev/null
+++ b/src/char_sql/char.h
@@ -0,0 +1,103 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/mmo.h"
+#include "../common/version.h"
+#include "../common/db.h"
+#include "../common/mapindex.h"
+
+#ifndef _CHAR_H_
+#define _CHAR_H_
+
+#define START_CHAR_NUM 150000
+#define MAX_MAP_SERVERS 30
+
+#define LAN_CONF_NAME "conf/lan_support.conf"
+
+#define DEFAULT_AUTOSAVE_INTERVAL 300*1000
+
+struct mmo_map_server{
+ long ip;
+ short port;
+ int users;
+ unsigned short map[MAX_MAP_PER_SERVER];
+};
+struct itemtmp {
+ int flag;//checked = 1 else 0
+ int id;
+ short nameid;
+ short amount;
+ unsigned short equip;
+ char identify;
+ char refine;
+ char attribute;
+ short card[4];
+};
+enum {
+ TABLE_INVENTORY,
+ TABLE_CART,
+ TABLE_STORAGE,
+ TABLE_GUILD_STORAGE,
+};
+struct itemtemp{
+ struct itemtmp equip[MAX_GUILD_STORAGE],notequip[MAX_GUILD_STORAGE];
+};
+int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch);
+
+//int memitemdataNEW_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch);
+int mapif_sendall(unsigned 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_nick2id (char *name);
+int char_married(int pl1,int pl2);
+int char_child(int parent_id, int child_id);
+
+int request_accreg2(int account_id, int char_id);
+int save_accreg2(unsigned char* buf, int len);
+
+extern int autosave_interval;
+extern int save_log;
+extern int charsave_method;
+extern char db_path[];
+extern char char_db[256];
+extern char scdata_db[256];
+extern char cart_db[256];
+extern char inventory_db[256];
+extern char charlog_db[256];
+extern char storage_db[256];
+extern char interlog_db[256];
+extern char reg_db[256];
+extern char skill_db[256];
+extern char memo_db[256];
+extern char guild_db[256];
+extern char guild_alliance_db[256];
+extern char guild_castle_db[256];
+extern char guild_expulsion_db[256];
+extern char guild_member_db[256];
+extern char guild_position_db[256];
+extern char guild_skill_db[256];
+extern char guild_storage_db[256];
+extern char party_db[256];
+extern char pet_db[256];
+
+extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris]
+extern char login_db_level[32];
+extern char login_db_account_id[32];
+
+extern int lowest_gm_level;
+extern int GM_num;
+extern struct gm_account *gm_account;
+
+extern int debug_mysql_query(char *file, int line, void *mysql, const char *q);
+
+#endif
+
+#include "inter.h"
+#include "int_pet.h"
+#include "int_guild.h"
+#include "int_party.h"
+#include "int_storage.h"
diff --git a/src/char_sql/int_guild.c b/src/char_sql/int_guild.c
new file mode 100644
index 000000000..6e1f24ed6
--- /dev/null
+++ b/src/char_sql/int_guild.c
@@ -0,0 +1,1914 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by hack
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "char.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+// #include "int_storage.h"
+#include "inter.h"
+#include "int_guild.h"
+#include "mmo.h"
+#include "socket.h"
+#include "db.h"
+#include "malloc.h"
+
+//Guild cache
+static struct dbt *guild_db_;
+
+struct guild_castle castles[MAX_GUILDCASTLE];
+
+static int guild_newid=30000;
+
+static int guild_exp[100];
+
+#define GS_BASIC 0x01
+#define GS_MEMBER 0x02
+#define GS_POSITION 0x04
+#define GS_ALLIANCE 0x08
+#define GS_EXPULSION 0x10
+#define GS_SKILL 0x20
+#define GS_MASK 0x7F
+#define GS_REMOVE 0x80
+
+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);
+int guild_break_sub(int key,void *data,va_list ap);
+int inter_guild_tosql(struct guild *g,int flag);
+
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+static int guild_save(DBKey key, void *data, va_list ap) {
+ struct guild *g = (struct guild*) data;
+ int *last_id = va_arg(ap, int *);
+ int *state = va_arg(ap, int *);
+
+ if ((*state) == 0 && g->guild_id == (*last_id))
+ (*state)++; //Save next guild in the list.
+ else if (g->save_flag&GS_MASK && (*state) == 1) {
+ inter_guild_tosql(g, g->save_flag&GS_MASK);
+ g->save_flag &= ~GS_MASK;
+
+ //Some guild saved.
+ (*last_id) = g->guild_id;
+ (*state)++;
+ }
+
+ if(g->save_flag == GS_REMOVE) { //Nothing to save, guild is ready for removal.
+ if (save_log)
+ ShowInfo("Guild Unloaded (%d - %s)\n", g->guild_id, g->name);
+ db_remove(guild_db_, key);
+ }
+ return 0;
+}
+
+static int guild_save_timer(int tid, unsigned int tick, int id, int data) {
+ static int last_id = 0; //To know in which guild we were.
+ int state = 0; //0: Have not reached last guild. 1: Reached last guild, ready for save. 2: Some guild saved, don't do further saving.
+ if (!last_id) //Save the first guild in the list.
+ state = 1;
+ guild_db_->foreach(guild_db_, guild_save, &last_id, &state);
+ if (state != 2) //Reached the end of the guild db without saving.
+ last_id = 0; //Reset guild saved, return to beginning.
+
+ state = guild_db_->size(guild_db_);
+ if (state < 1) state = 1; //Calculate the time slot for the next save.
+ add_timer(tick + autosave_interval/state, guild_save_timer, 0, 0);
+ return 0;
+}
+
+// Save guild into sql
+int inter_guild_tosql(struct guild *g,int flag)
+{
+ // GS_BASIC `guild` (`guild_id`, `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data`)
+ // GS_MEMBER `guild_member` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name`)
+ // GS_POSITION `guild_position` (`guild_id`,`position`,`name`,`mode`,`exp_mode`)
+ // GS_ALLIANCE `guild_alliance` (`guild_id`,`opposition`,`alliance_id`,`name`)
+ // GS_EXPULSION `guild_expulsion` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`)
+ // GS_SKILL `guild_skill` (`guild_id`,`id`,`lv`)
+
+ // temporary storage for str convertion. They must be twice the size of the
+ // original string to ensure no overflows will occur. [Skotlex]
+ char t_name[NAME_LENGTH*2],
+ t_master[NAME_LENGTH*2],
+ t_mes1[120],
+ t_mes2[240],
+ t_member[NAME_LENGTH*2],
+ t_position[NAME_LENGTH*2],
+ t_alliance[NAME_LENGTH*2],
+ t_ename[NAME_LENGTH*2],
+ t_emes[80],
+ t_info[240];
+ char emblem_data[4096];
+ int i=0;
+
+ if (g->guild_id<=0) return -1;
+
+#ifdef NOISY
+ ShowInfo("Save guild request ("CL_BOLD"%d"CL_RESET" - flag 0x%x).",g->guild_id, flag);
+#endif
+
+ jstrescapecpy(t_name, g->name);
+
+ t_info[0]='\0';
+ // Insert new guild to sqlserver
+ if (flag&GS_BASIC){
+ int len=0;
+ char updateflag=1;
+ strcat(t_info, " guild");
+
+ // Check if the guild exists.
+ sprintf(tmp_sql,"SELECT guild_id FROM `%s` WHERE guild_id='%d'",guild_db, g->guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ updateflag = 0; //Assume insert?
+ } else {
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res==NULL || mysql_num_rows(sql_res)<=0) { //Guild does not exists
+ updateflag = 0;
+ }
+ mysql_free_result(sql_res); //Don't need it anymore...
+ }
+
+ //printf("- Insert guild %d to guild\n",g->guild_id);
+ for(i=0;i<g->emblem_len;i++){
+ len+=sprintf(emblem_data+len,"%02x",(unsigned char)(g->emblem_data[i]));
+ //printf("%02x",(unsigned char)(g->emblem_data[i]));
+ }
+ emblem_data[len] = '\0';
+ //printf("- emblem_len = %d \n",g->emblem_len);
+ if (updateflag) {
+ sprintf(tmp_sql,"UPDATE `%s` SET"
+ " `guild_id`=%d, `name`='%s', `master`='%s',`guild_lv`=%d, `connect_member`=%d,`max_member`=%d, "
+ "`average_lv`=%d,`exp`=%d,`next_exp`=%d,`skill_point`=%d,`mes1`='%s',`mes2`='%s',"
+ "`emblem_len`=%d,`emblem_id`=%d,`emblem_data`='%s',`char_id`=%d WHERE `guild_id`=%d",
+ guild_db, g->guild_id,t_name,jstrescapecpy(t_master,g->master),
+ g->guild_lv,g->connect_member,g->max_member,g->average_lv,g->exp,g->next_exp,g->skill_point,
+ jstrescapecpy(t_mes1,g->mes1),jstrescapecpy(t_mes2,g->mes2),g->emblem_len,g->emblem_id,emblem_data,
+ g->member[0].char_id, g->guild_id);
+ //printf(" %s\n",tmp_sql);
+
+ } else {
+ sprintf(tmp_sql,"INSERT INTO `%s` "
+ "(`guild_id`, `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data`,`char_id`) "
+ "VALUES ('%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%d', '%s', '%d')",
+ guild_db, g->guild_id,t_name,jstrescapecpy(t_master,g->master),
+ g->guild_lv,g->connect_member,g->max_member,g->average_lv,g->exp,g->next_exp,g->skill_point,
+ jstrescapecpy(t_mes1,g->mes1),jstrescapecpy(t_mes2,g->mes2),g->emblem_len,g->emblem_id,emblem_data,
+ g->member[0].char_id);
+ //printf(" %s\n",tmp_sql);
+
+ }
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if (flag&GS_MEMBER){
+ struct guild_member *m;
+ strcat(t_info, " members");
+ // Re-writing from scratch (Aru)
+ sprintf(tmp_sql,"DELETE from `%s` where `guild_id` = '%d'",
+ guild_member_db, g->guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '0' WHERE `guild_id` = '%d'",
+ char_db, g->guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i=0;i<g->max_member;i++){
+ m = &g->member[i];
+ if(m->account_id) {
+ //Since nothing references guild member table as foreign keys, it's safe to use REPLACE INTO
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name`) "
+ "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%s')",
+ guild_member_db, g->guild_id, m->account_id,m->char_id,
+ m->hair,m->hair_color,m->gender,
+ m->class_,m->lv,m->exp,m->exp_payper,m->online,m->position,
+ jstrescapecpy(t_member,m->name));
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '%d' WHERE `char_id` = '%d'",
+ char_db, g->guild_id, m->char_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+
+ if (flag&GS_POSITION){
+ strcat(t_info, " positions");
+ //printf("- Insert guild %d to guild_position\n",g->guild_id);
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ struct guild_position *p = &g->position[i];
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) VALUES ('%d','%d', '%s','%d','%d')",
+ guild_position_db, g->guild_id, i, jstrescapecpy(t_position,p->name),p->mode,p->exp_mode);
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ if (flag&GS_ALLIANCE){
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d' OR `alliance_id`='%d'",guild_alliance_db, g->guild_id,g->guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ else
+ {
+ //printf("- Insert guild %d to guild_alliance\n",g->guild_id);
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ struct guild_alliance *a=&g->alliance[i];
+ if(a->guild_id>0){
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`opposition`,`alliance_id`,`name`) "
+ "VALUES ('%d','%d','%d','%s')",
+ guild_alliance_db, g->guild_id,a->opposition,a->guild_id,jstrescapecpy(t_alliance,a->name));
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`opposition`,`alliance_id`,`name`) "
+ "VALUES ('%d','%d','%d','%s')",
+ guild_alliance_db, a->guild_id,a->opposition,g->guild_id,t_name);
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+ }
+
+ if (flag&GS_EXPULSION){
+ strcat(t_info, " expulsions");
+ //printf("- Insert guild %d to guild_expulsion\n",g->guild_id);
+ for(i=0;i<MAX_GUILDEXPLUSION;i++){
+ struct guild_explusion *e=&g->explusion[i];
+ if(e->account_id>0){
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) "
+ "VALUES ('%d','%s','%s','%s','%d','%d','%d','%d')",
+ guild_expulsion_db, g->guild_id,
+ jstrescapecpy(t_ename,e->name),jstrescapecpy(t_emes,e->mes),e->acc,e->account_id,e->rsv1,e->rsv2,e->rsv3 );
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+
+ if (flag&GS_SKILL){
+ strcat(t_info, " skills");
+ //printf("- Insert guild %d to guild_skill\n",g->guild_id);
+ for(i=0;i<MAX_GUILDSKILL;i++){
+ if (g->skill[i].id>0 && g->skill[i].lv>0){
+ sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`id`,`lv`) VALUES ('%d','%d','%d')",
+ guild_skill_db, g->guild_id,g->skill[i].id,g->skill[i].lv);
+ //printf("%s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+
+ if (save_log)
+ ShowInfo("Saved guild (%d - %s):%s\n",g->guild_id,g->name,t_info);
+ return 0;
+}
+
+// Read guild from sql
+struct guild * inter_guild_fromsql(int guild_id)
+{
+ int i;
+ char emblem_data[4096];
+ char *pstr;
+ struct guild *g;
+
+ if (guild_id<=0) return NULL;
+
+ g = idb_get(guild_db_,guild_id);
+ if (g) return g;
+
+ g = (struct guild*)aCalloc(sizeof(struct guild), 1);
+
+#ifdef NOISY
+ ShowInfo("Guild load request (%d)...\n", guild_id);
+#endif
+
+ sprintf(tmp_sql,"SELECT `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data` "
+ "FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id);
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res==NULL || mysql_num_rows(sql_res)<1) {
+ //Guild does not exists.
+ if (sql_res) mysql_free_result(sql_res);
+ aFree(g);
+ return NULL;
+ }
+
+ sql_row = mysql_fetch_row(sql_res);
+ if (sql_row==NULL) {
+ mysql_free_result(sql_res);
+ aFree(g);
+ return NULL;
+ }
+
+ g->guild_id=guild_id;
+ strncpy(g->name,sql_row[0],NAME_LENGTH-1);
+ strncpy(g->master,sql_row[1],NAME_LENGTH-1);
+ g->guild_lv=atoi(sql_row[2]);
+ g->connect_member=atoi(sql_row[3]);
+ if (atoi(sql_row[4]) > MAX_GUILD) // Fix reduction of MAX_GUILD [PoW]
+ g->max_member = MAX_GUILD;
+ else
+ g->max_member = atoi(sql_row[4]);
+ g->average_lv=atoi(sql_row[5]);
+ g->exp=atoi(sql_row[6]);
+ g->next_exp=atoi(sql_row[7]);
+ g->skill_point=atoi(sql_row[8]);
+ //There shouldn't be a need to copy the very last char, as it's the \0 [Skotlex]
+ strncpy(g->mes1,sql_row[9],59);
+ strncpy(g->mes2,sql_row[10],119);
+ g->emblem_len=atoi(sql_row[11]);
+ g->emblem_id=atoi(sql_row[12]);
+ strncpy(emblem_data,sql_row[13],4096);
+ for(i=0,pstr=emblem_data;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;
+ }
+ mysql_free_result(sql_res);
+
+ //printf("- Read guild_member %d from sql \n",guild_id);
+ sprintf(tmp_sql,"SELECT `guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name` "
+ "FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", guild_member_db, guild_id);
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ int i;
+ for(i=0;((sql_row = mysql_fetch_row(sql_res))&&i<g->max_member);i++){
+ struct guild_member *m = &g->member[i];
+ m->account_id=atoi(sql_row[1]);
+ m->char_id=atoi(sql_row[2]);
+ m->hair=atoi(sql_row[3]);
+ m->hair_color=atoi(sql_row[4]);
+ m->gender=atoi(sql_row[5]);
+ m->class_=atoi(sql_row[6]);
+ m->lv=atoi(sql_row[7]);
+ m->exp=atoi(sql_row[8]);
+ m->exp_payper=atoi(sql_row[9]);
+ m->online=atoi(sql_row[10]);
+ if (atoi(sql_row[11]) >= MAX_GUILDPOSITION) // Fix reduction of MAX_GUILDPOSITION [PoW]
+ m->position = MAX_GUILDPOSITION - 1;
+ else
+ m->position = atoi(sql_row[11]);
+
+ strncpy(m->name,sql_row[14],NAME_LENGTH-1);
+ }
+ }
+ mysql_free_result(sql_res);
+
+ //printf("- Read guild_position %d from sql \n",guild_id);
+ sprintf(tmp_sql,"SELECT `guild_id`,`position`,`name`,`mode`,`exp_mode` FROM `%s` WHERE `guild_id`='%d'",guild_position_db, guild_id);
+ //printf(" %s\n",tmp_sql);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ int i;
+ for(i=0;((sql_row = mysql_fetch_row(sql_res))&&i<MAX_GUILDPOSITION);i++){
+ int position = atoi(sql_row[1]);
+ struct guild_position *p = &g->position[position];
+ strncpy(p->name,sql_row[2],NAME_LENGTH-1);
+ p->mode=atoi(sql_row[3]);
+ p->exp_mode=atoi(sql_row[4]);
+ }
+ }
+ mysql_free_result(sql_res);
+
+ //printf("- Read guild_alliance %d from sql \n",guild_id);
+ sprintf(tmp_sql,"SELECT `guild_id`,`opposition`,`alliance_id`,`name` FROM `%s` WHERE `guild_id`='%d'",guild_alliance_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ int i;
+ for(i=0;((sql_row = mysql_fetch_row(sql_res))&&i<MAX_GUILDALLIANCE);i++){
+ struct guild_alliance *a = &g->alliance[i];
+ a->opposition=atoi(sql_row[1]);
+ a->guild_id=atoi(sql_row[2]);
+ strncpy(a->name,sql_row[3],NAME_LENGTH-1);
+ }
+ }
+ mysql_free_result(sql_res);
+
+ //printf("- Read guild_expulsion %d from sql \n",guild_id);
+ sprintf(tmp_sql,"SELECT `guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3` FROM `%s` WHERE `guild_id`='%d'",guild_expulsion_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ int i;
+ for(i=0;((sql_row = mysql_fetch_row(sql_res))&&i<MAX_GUILDEXPLUSION);i++){
+ struct guild_explusion *e = &g->explusion[i];
+
+ strncpy(e->name,sql_row[1],NAME_LENGTH-1);
+ //No need to copy char 40, the null terminator. [Skotlex]
+ strncpy(e->mes,sql_row[2],39);
+ strncpy(e->acc,sql_row[3],39);
+ e->account_id=atoi(sql_row[4]);
+ e->rsv1=atoi(sql_row[5]);
+ e->rsv2=atoi(sql_row[6]);
+ e->rsv3=atoi(sql_row[7]);
+ }
+ }
+ mysql_free_result(sql_res);
+
+ //printf("- Read guild_skill %d from sql \n",guild_id);
+ sprintf(tmp_sql,"SELECT `guild_id`,`id`,`lv` FROM `%s` WHERE `guild_id`='%d' ORDER BY `id`",guild_skill_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(g);
+ return NULL;
+ }
+
+ for(i = 0; i < MAX_GUILDSKILL; i++)
+ { //Skill IDs must always be initialized. [Skotlex]
+ g->skill[i].id = i + GD_SKILLBASE;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ while ((sql_row = mysql_fetch_row(sql_res))){
+ int id = atoi(sql_row[1])-GD_SKILLBASE;
+ if (id >= 0 && id < MAX_GUILDSKILL)
+ //I know this seems ridiculous, but the skills HAVE to be placed on their 'correct' array slot or things break x.x [Skotlex]
+ g->skill[id].lv=atoi(sql_row[2]);
+ }
+ }
+ mysql_free_result(sql_res);
+
+ idb_put(guild_db_, guild_id, g); //Add to cache
+ g->save_flag |= GS_REMOVE; //But set it to be removed, in case it is not needed for long.
+
+ if (save_log)
+ ShowInfo("Guild loaded (%d - %s)\n", guild_id, g->name);
+
+ return g;
+}
+
+int inter_guildcastle_tosql(struct guild_castle *gc){
+ // `guild_castle` (`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`, `visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`)
+
+ if (gc==NULL) return 0;
+ #ifdef GUILD_DEBUG
+ShowDebug("Save guild_castle (%d)\n", gc->castle_id);
+ #endif
+
+// sql_query("DELETE FROM `%s` WHERE `castle_id`='%d'",guild_castle_db, gc->castle_id);
+
+ sprintf(tmp_sql,"REPLACE INTO `%s` "
+ "(`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`,"
+ "`visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`,"
+ "`Ghp0`, `Ghp1`, `Ghp2`, `Ghp3`, `Ghp4`, `Ghp5`, `Ghp6`, `Ghp7`)"
+ "VALUES ('%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')",
+ guild_castle_db, gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime,
+ gc->createTime, gc->visibleC,
+ gc->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible, gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible,
+ gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp, gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp);
+
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+
+ memcpy(&castles[gc->castle_id],gc,sizeof(struct guild_castle));
+
+ return 0;
+}
+
+
+// Read guild_castle from sql
+int inter_guildcastle_fromsql(int castle_id,struct guild_castle *gc)
+{
+ static int castles_init=0;
+ if (gc==NULL) return 0;
+ if (castle_id==-1) return 0;
+
+ if(!castles_init)
+ {
+ int i;
+ for(i=0;i<MAX_GUILDCASTLE;i++)
+ castles[i].castle_id=-1;
+ castles_init = 1;
+ }
+
+ if(castles[castle_id].castle_id == castle_id)
+ {
+ memcpy(gc,&castles[castle_id],sizeof(struct guild_castle));
+ return 1;
+ }
+
+ memset(gc,0,sizeof(struct guild_castle));
+ sprintf(tmp_sql,"SELECT `castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`, "
+ "`visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`,"
+ "`Ghp0`, `Ghp1`, `Ghp2`, `Ghp3`, `Ghp4`, `Ghp5`, `Ghp6`, `Ghp7`"
+ " FROM `%s` WHERE `castle_id`='%d'",guild_castle_db, castle_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ // ARU: This needs to be set even if there are no SQL results
+ gc->castle_id=castle_id;
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ sql_row = mysql_fetch_row(sql_res);
+ if (sql_row==NULL){
+ mysql_free_result(sql_res);
+ return 1; //Assume empty castle.
+ }
+ gc->guild_id = atoi (sql_row[1]);
+ gc->economy = atoi (sql_row[2]);
+ gc->defense = atoi (sql_row[3]);
+ gc->triggerE = atoi (sql_row[4]);
+ gc->triggerD = atoi (sql_row[5]);
+ gc->nextTime = atoi (sql_row[6]);
+ gc->payTime = atoi (sql_row[7]);
+ gc->createTime = atoi (sql_row[8]);
+ gc->visibleC = atoi (sql_row[9]);
+ gc->guardian[0].visible = atoi (sql_row[10]);
+ gc->guardian[1].visible = atoi (sql_row[11]);
+ gc->guardian[2].visible = atoi (sql_row[12]);
+ gc->guardian[3].visible = atoi (sql_row[13]);
+ gc->guardian[4].visible = atoi (sql_row[14]);
+ gc->guardian[5].visible = atoi (sql_row[15]);
+ gc->guardian[6].visible = atoi (sql_row[16]);
+ gc->guardian[7].visible = atoi (sql_row[17]);
+ gc->guardian[0].hp = atoi (sql_row[18]);
+ gc->guardian[1].hp = atoi (sql_row[19]);
+ gc->guardian[2].hp = atoi (sql_row[20]);
+ gc->guardian[3].hp = atoi (sql_row[21]);
+ gc->guardian[4].hp = atoi (sql_row[22]);
+ gc->guardian[5].hp = atoi (sql_row[23]);
+ gc->guardian[6].hp = atoi (sql_row[24]);
+ gc->guardian[7].hp = atoi (sql_row[25]);
+
+ if (save_log)
+ ShowInfo("Loaded Castle %d (guild %d)\n",castle_id,gc->guild_id);
+
+ }
+ mysql_free_result(sql_res) ; //resource free
+
+ memcpy(&castles[castle_id],gc,sizeof(struct guild_castle));
+
+ return 1;
+}
+
+
+// Read exp_guild.txt
+int inter_guild_ReadEXP()
+{
+ int i;
+ FILE *fp;
+ char line[1024];
+ for (i=0;i<100;i++) guild_exp[i]=0;
+
+ sprintf(line, "%s/exp_guild.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+ i=0;
+ while(fgets(line,256,fp) && i<100){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ guild_exp[i]=atoi(line);
+ i++;
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+
+int inter_guild_CharOnline(int char_id, int guild_id) {
+
+ struct guild *g;
+ int i;
+
+ if (guild_id == -1) {
+ //Get guild_id from the database
+ sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res == NULL)
+ return 0; //Eh? No guild?
+
+ sql_row = mysql_fetch_row(sql_res);
+ guild_id = sql_row?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+ }
+ if (guild_id == 0)
+ return 0; //No guild...
+
+ g = inter_guild_fromsql(guild_id);
+ if(!g) {
+ ShowError("Character %d's guild %d not found!\n", char_id, guild_id);
+ return 0;
+ }
+
+ //Member has logged in before saving, tell saver not to delete
+ if(g->save_flag & GS_REMOVE)
+ g->save_flag &= ~GS_REMOVE;
+
+ //Set member online
+ for(i=0; i<g->max_member; i++) {
+ if (g->member[i].char_id == char_id) {
+ g->member[i].online = 1;
+ break;
+ }
+ }
+ return 1;
+}
+
+int inter_guild_CharOffline(int char_id, int guild_id) {
+ struct guild *g=NULL;
+ int online_count=0, i;
+
+ if (guild_id == -1) {
+ //Get guild_id from the database
+ sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res == NULL)
+ return 0; //Eh? No guild?
+
+ sql_row = mysql_fetch_row(sql_res);
+ guild_id = sql_row?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+ }
+ if (guild_id == 0)
+ return 0; //No guild...
+
+ //Character has a guild, set character offline and check if they were the only member online
+ g = inter_guild_fromsql(guild_id);
+ if (g == NULL) //Guild not found?
+ return 0;
+
+ //Set member offline
+ for(i=0; i<g->max_member; i++) {
+ if(g->member[i].char_id == char_id)
+ g->member[i].online = 0;
+ if(g->member[i].online)
+ online_count++;
+ }
+
+ if(online_count == 0)
+ g->save_flag |= GS_REMOVE;
+
+ return 1;
+}
+
+// Initialize guild sql
+int inter_guild_sql_init()
+{
+ int i;
+
+ //Initialize the guild cache
+ guild_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ //Read exp file
+ inter_guild_ReadEXP();
+
+ //Set the new guild ID
+ sprintf (tmp_sql , "SELECT max(`guild_id`) FROM `%s`",guild_db);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ exit(0);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ if(sql_row[0]) {
+ if((i = atoi(sql_row[0])) != 0) {
+ guild_newid = i+1;
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+
+ add_timer_func_list(guild_save_timer, "guild_save_timer");
+ add_timer(gettick() + 10000, guild_save_timer, 0, 0);
+ return 0;
+}
+
+static int guild_db_final(DBKey key, void *data, va_list ap)
+{
+ struct guild *g = (struct guild*)data;
+ if (g->save_flag&GS_MASK) {
+ inter_guild_tosql(g, g->save_flag&GS_MASK);
+ return 1;
+ }
+ return 0;
+}
+
+void inter_guild_sql_final()
+{
+ guild_db_->destroy(guild_db_, guild_db_final);
+ return;
+}
+
+// Get guild_id by its name. Returns 0 if not found, -1 on error.
+int search_guildname(char *str)
+{
+ int guild_id;
+
+ //Lookup guilds with the same name
+ sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE name='%s'",guild_db,jstrescape(str));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res) sql_row = mysql_fetch_row(sql_res);
+
+ guild_id = (sql_row&&sql_res&&sql_row[0])?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+ return guild_id;
+}
+
+// Check if guild is empty
+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;
+ }
+ }
+ //Let the calling function handle the guild removal in case they need
+ //to do something else with it before freeing the data. [Skotlex]
+ return 1;
+}
+
+int guild_nextexp(int level)
+{
+ if (level == 0)
+ return 1;
+ if (level < 100 && level > 0) // Change by hack
+ return guild_exp[level-1];
+
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒXƒLƒ‹‚ªE‚é‚©Šm”F
+int guild_checkskill(struct guild *g,int id) {
+
+ int idx = id - GD_SKILLBASE;
+
+
+ if(idx < 0 || idx >= MAX_GUILDSKILL)
+
+ return 0;
+
+ return g->skill[idx].lv;
+
+}
+
+// ƒMƒ‹ƒh‚Ìî•ñ‚ÌÄŒvŽZ
+int guild_calcinfo(struct guild *g)
+{
+ int i,c,nextexp;
+ struct guild before=*g;
+
+ // ƒXƒLƒ‹ID‚ÌÝ’è
+ for(i=0;i<MAX_GUILDSKILL;i++)
+ g->skill[i].id=i+GD_SKILLBASE;
+
+ // ƒMƒ‹ƒhƒŒƒxƒ‹
+ if(g->guild_lv<=0) g->guild_lv=1;
+ nextexp = guild_nextexp(g->guild_lv);
+ if(nextexp > 0) {
+ while(g->exp >= nextexp && nextexp > 0){ //fixed guild exp overflow [Kevin]
+ g->exp-=nextexp;
+ g->guild_lv++;
+ g->skill_point++;
+ nextexp = guild_nextexp(g->guild_lv);
+ }
+ }
+
+ // ƒMƒ‹ƒh‚ÌŽŸ‚ÌŒoŒ±’l
+ g->next_exp = guild_nextexp(g->guild_lv);
+
+ // ƒƒ“ƒoãŒÀiƒMƒ‹ƒhŠg’£“K—pj
+ g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; //Guild Extention skill - currently adds 6 to max per skill lv.
+ if(g->max_member > MAX_GUILD)
+ {
+ ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD);
+ g->max_member = MAX_GUILD;
+ }
+
+ // •½‹ÏƒŒƒxƒ‹‚ƃIƒ“ƒ‰ƒCƒ“l”
+ g->average_lv=0;
+ g->connect_member=0;
+ for(i=c=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++;
+ }
+ }
+ if(c) g->average_lv/=c;
+
+ // ‘Sƒf[ƒ^‚ð‘—‚é•K—v‚ªE‚è‚»‚¤
+ 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‚Ö‚Ì’ÊM
+
+// ƒMƒ‹ƒh쬉”Û
+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;
+ ShowInfo("int_guild: Guild created (%d - %s)\n",g->guild_id,g->name);
+ }else{
+ WFIFOL(fd,6)=0;
+ }
+ WFIFOSET(fd,10);
+ return 0;
+}
+// ƒMƒ‹ƒhî•ñŒ©‚‚©‚炸
+int mapif_guild_noinfo(int fd,int guild_id)
+{
+ unsigned char buf[12];
+ WBUFW(buf,0)=0x3831;
+ WBUFW(buf,2)=8;
+ WBUFL(buf,4)=guild_id;
+ ShowWarning("int_guild: info not found %d\n",guild_id);
+ if(fd<0)
+ mapif_sendall(buf,8);
+ else
+ mapif_send(fd,buf,8);
+ return 0;
+}
+// ƒMƒ‹ƒhî•ñ‚Ü‚Æ‚ß‘—‚è
+int mapif_guild_info(int fd,struct guild *g)
+{
+ unsigned char buf[8+sizeof(struct guild)];
+ WBUFW(buf,0)=0x3831;
+ memcpy(buf+4,g,sizeof(struct guild));
+ WBUFW(buf,2)=4+sizeof(struct guild);
+ if(fd<0)
+ mapif_sendall(buf,WBUFW(buf,2));
+ else
+ mapif_send(fd,buf,WBUFW(buf,2));
+ return 0;
+}
+
+// ƒƒ“ƒo’ljÁ‰Â”Û
+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)=char_id;
+ WFIFOB(fd,14)=flag;
+ WFIFOSET(fd,15);
+ return 0;
+}
+// ’E‘Þ/’Ç•ú’Ê’m
+int mapif_guild_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes)
+{
+ unsigned char buf[128];
+ WBUFW(buf, 0)=0x3834;
+ WBUFL(buf, 2)=guild_id;
+ WBUFL(buf, 6)=account_id;
+ WBUFL(buf,10)=char_id;
+ WBUFB(buf,14)=flag;
+ memcpy(WBUFP(buf,15),mes,40);
+ memcpy(WBUFP(buf,55),name,NAME_LENGTH);
+ mapif_sendall(buf,55+NAME_LENGTH);
+// mapif_sendall(buf,79);
+ ShowInfo("int_guild: guild leaved (%d - %d: %s - %s)\n",guild_id,account_id,name,mes);
+ return 0;
+}
+
+// ƒIƒ“ƒ‰ƒCƒ“ó‘Ô‚ÆLvXV’Ê’m
+int mapif_guild_memberinfoshort(struct guild *g,int idx)
+{
+ unsigned char buf[32];
+ WBUFW(buf, 0)=0x3835;
+ WBUFL(buf, 2)=g->guild_id;
+ WBUFL(buf, 6)=g->member[idx].account_id;
+ WBUFL(buf,10)=g->member[idx].char_id;
+ WBUFB(buf,14)=(unsigned char)g->member[idx].online;
+ WBUFW(buf,15)=g->member[idx].lv;
+ WBUFW(buf,17)=g->member[idx].class_;
+ mapif_sendall(buf,19);
+ return 0;
+}
+
+// ‰ðŽU’Ê’m
+int mapif_guild_broken(int guild_id,int flag)
+{
+ unsigned char buf[16];
+ WBUFW(buf,0)=0x3836;
+ WBUFL(buf,2)=guild_id;
+ WBUFB(buf,6)=flag;
+ mapif_sendall(buf,7);
+ ShowInfo("int_guild: Guild broken (%d)\n",guild_id);
+ return 0;
+}
+
+// ƒMƒ‹ƒh“à”­Œ¾
+int mapif_guild_message(int guild_id,int account_id,char *mes,int len, int sfd)
+{
+ unsigned char buf[512];
+ 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_sendallwos(sfd, buf,len+12);
+ return 0;
+}
+
+// ƒMƒ‹ƒhŠî–{î•ñ•ÏX’Ê’m
+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;
+}
+// ƒMƒ‹ƒhƒƒ“ƒoî•ñ•ÏX’Ê’m
+int mapif_guild_memberinfochanged(int guild_id,int account_id,int char_id,
+ int type,const void *data,int len)
+{
+ unsigned char buf[2048];
+ WBUFW(buf, 0)=0x383a;
+ WBUFW(buf, 2)=len+18;
+ WBUFL(buf, 4)=guild_id;
+ WBUFL(buf, 8)=account_id;
+ WBUFL(buf,12)=char_id;
+ WBUFW(buf,16)=type;
+ memcpy(WBUFP(buf,18),data,len);
+ mapif_sendall(buf,len+18);
+ return 0;
+}
+// ƒMƒ‹ƒhƒXƒLƒ‹ƒAƒbƒv’Ê’m
+int mapif_guild_skillupack(int guild_id,int skill_num,int account_id)
+{
+ unsigned char buf[16];
+ 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;
+}
+// ƒMƒ‹ƒh“¯–¿/“G‘Î’Ê’m
+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[128];
+ 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,NAME_LENGTH);
+ memcpy(WBUFP(buf,19+NAME_LENGTH),name2,NAME_LENGTH);
+ mapif_sendall(buf,19+2*NAME_LENGTH);
+// memcpy(WBUFP(buf,43),name2,NAME_LENGTH);
+// mapif_sendall(buf,67);
+ return 0;
+}
+
+// ƒMƒ‹ƒh–ðE•ÏX’Ê’m
+int mapif_guild_position(struct guild *g,int idx)
+{
+ unsigned char buf[128];
+ 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;
+}
+
+// ƒMƒ‹ƒh’m•ÏX’Ê’m
+int mapif_guild_notice(struct guild *g)
+{
+ unsigned char buf[256];
+ 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;
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX’Ê’m
+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_master_changed(struct guild *g, int position)
+{
+ unsigned char buf[12];
+ WBUFW(buf,0)=0x3843;
+ WBUFL(buf,2)=g->guild_id;
+ WBUFL(buf,6)=position;
+ mapif_sendall(buf,10);
+ return 0;
+}
+
+int mapif_guild_castle_dataload(int castle_id,int index,int value) // <Agit>
+{
+ unsigned char buf[16];
+ 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) // <Agit>
+{
+ unsigned char buf[16];
+ WBUFW(buf, 0)=0x3841;
+ WBUFW(buf, 2)=castle_id;
+ WBUFB(buf, 4)=index;
+ WBUFL(buf, 5)=value;
+ mapif_sendall(buf,9);
+ return 0;
+}
+
+int mapif_guild_castle_alldataload(int fd) {
+ struct guild_castle* gc = (struct guild_castle *)aMalloc(sizeof(struct guild_castle));
+ int i, len = 4;
+
+ WFIFOW(fd,0) = 0x3842;
+ sprintf(tmp_sql,"SELECT * FROM `%s` ORDER BY `castle_id`", guild_castle_db);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res != NULL && mysql_num_rows(sql_res) > 0) {
+ for(i = 0; ((sql_row = mysql_fetch_row(sql_res)) && i < MAX_GUILDCASTLE); i++) {
+ memset(gc, 0, sizeof(struct guild_castle));
+ gc->castle_id = atoi(sql_row[0]);
+ gc->guild_id = atoi(sql_row[1]);
+ gc->economy = atoi(sql_row[2]);
+ gc->defense = atoi(sql_row[3]);
+ gc->triggerE = atoi(sql_row[4]);
+ gc->triggerD = atoi(sql_row[5]);
+ gc->nextTime = atoi(sql_row[6]);
+ gc->payTime = atoi(sql_row[7]);
+ gc->createTime = atoi(sql_row[8]);
+ gc->visibleC = atoi(sql_row[9]);
+ gc->guardian[0].visible = atoi(sql_row[10]);
+ gc->guardian[1].visible = atoi(sql_row[11]);
+ gc->guardian[2].visible = atoi(sql_row[12]);
+ gc->guardian[3].visible = atoi(sql_row[13]);
+ gc->guardian[4].visible = atoi(sql_row[14]);
+ gc->guardian[5].visible = atoi(sql_row[15]);
+ gc->guardian[6].visible = atoi(sql_row[16]);
+ gc->guardian[7].visible = atoi(sql_row[17]);
+ gc->guardian[0].visible = atoi(sql_row[18]);
+ gc->guardian[1].visible = atoi(sql_row[19]);
+ gc->guardian[2].visible = atoi(sql_row[20]);
+ gc->guardian[3].visible = atoi(sql_row[21]);
+ gc->guardian[4].visible = atoi(sql_row[22]);
+ gc->guardian[5].visible = atoi(sql_row[23]);
+ gc->guardian[6].visible = atoi(sql_row[24]);
+ gc->guardian[7].visible = atoi(sql_row[25]);
+ memcpy(WFIFOP(fd,len), gc, sizeof(struct guild_castle));
+ len += sizeof(struct guild_castle);
+ }
+ }
+ mysql_free_result(sql_res);
+ WFIFOW(fd,2) = len;
+ WFIFOSET(fd,len);
+
+ aFree(gc);
+
+ return 0;
+}
+
+
+//-------------------------------------------------------------------
+// map server‚©‚ç‚Ì’ÊM
+
+
+// ƒMƒ‹ƒh쬗v‹
+int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member *master)
+{
+ struct guild *g;
+ int i=0;
+#ifdef NOISY
+ ShowInfo("Creating Guild (%s)\n", name);
+#endif
+ if(search_guildname(name) != 0){
+ ShowInfo("int_guild: guild with same name exists [%s]\n",name);
+ mapif_guild_created(fd,account_id,NULL);
+ return 0;
+ }
+ g = (struct guild *)aMalloc(sizeof(struct guild));
+ memset(g,0,sizeof(struct guild));
+ g->guild_id=guild_newid++;
+ if (inter_guild_fromsql(g->guild_id) != NULL) {
+ ShowWarning("mapif_parse_CreateGuild: New Guild ID [%d] already exists!\n", g->guild_id);
+ mapif_guild_created(fd,account_id,NULL);
+ aFree(g);
+ return 0;
+ }
+ memcpy(g->name,name,NAME_LENGTH);
+ memcpy(g->master,master->name,NAME_LENGTH);
+ 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);
+
+ // Initialize guild property
+ g->max_member=16;
+ g->average_lv=master->lv;
+ for(i=0;i<MAX_GUILDSKILL;i++)
+ g->skill[i].id=i + GD_SKILLBASE;
+ //Add to cache
+ ShowInfo("Created Guild %d - %s (Guild Master: %s)\n", g->guild_id, g->name, g->master);
+ idb_put(guild_db_, g->guild_id, g);
+ inter_guild_tosql(g,GS_BASIC|GS_POSITION|GS_SKILL); //Better save the whole guild right now.
+
+ // Report to client
+ mapif_guild_created(fd,account_id,g);
+ mapif_guild_info(fd,g);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE,
+ name, g->guild_id, master->name, master->account_id );
+
+ return 0;
+}
+// Return guild info to client
+int mapif_parse_GuildInfo(int fd,int guild_id)
+{
+ struct guild * g = inter_guild_fromsql(guild_id); //We use this because on start-up the info of castle-owned guilds is requied. [Skotlex]
+ if(g){
+ if (!guild_calcinfo(g))
+ mapif_guild_info(fd,g);
+ //inter_guild_tosql(g,GS_BASIC); // Change guild
+ }else
+ mapif_guild_noinfo(fd,guild_id);
+ return 0;
+}
+// Add member to guild
+int mapif_parse_GuildAddMember(int fd,int guild_id,struct guild_member *m)
+{
+ struct guild * g;
+ int i;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL){
+ mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1);
+ return 0;
+ }
+
+ for(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,m->char_id,0);
+ if (!guild_calcinfo(g)) //Send members if it was not invoked.
+ mapif_guild_info(fd,g);
+ g->save_flag |= (GS_BASIC|GS_MEMBER);
+ if (g->save_flag&GS_REMOVE)
+ g->save_flag&=~GS_REMOVE;
+ return 0;
+ }
+ }
+ mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1);
+ return 0;
+}
+// Delete member from guild
+int mapif_parse_GuildLeave(int fd,int guild_id,int account_id,int char_id,int flag,const char *mes)
+{
+ struct guild * g = inter_guild_fromsql(guild_id);
+
+ if(g){
+ int i;
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id){
+ if(flag){ // ’Ç•ú‚ÌꇒǕúƒŠƒXƒg‚É“ü‚ê‚é
+ int j;
+ for(j=0;j<MAX_GUILDEXPLUSION;j++){
+ if(g->explusion[j].account_id==0)
+ break;
+ }
+ if(j==MAX_GUILDEXPLUSION){ // ˆê”t‚Ȃ̂Ō¢‚Ì‚ðÁ‚·
+ 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",NAME_LENGTH-1);
+ memcpy(g->explusion[j].name,g->member[i].name,NAME_LENGTH-1);
+ memcpy(g->explusion[j].mes,mes,40);
+ }
+
+ mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes);
+ memset(&g->member[i],0,sizeof(struct guild_member));
+
+ if(!guild_check_empty(g)) {
+ break;
+ }
+ //Guild empty? break it.
+ mapif_parse_BreakGuild(-1,guild_id); //Break the guild.
+ return 0;
+ }
+ }
+ //Update member info.
+ if (!guild_calcinfo(g))
+ mapif_guild_info(fd,g);
+ g->save_flag |= (GS_BASIC|GS_MEMBER|GS_EXPULSION);
+ }else{
+ sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'",char_db, account_id,char_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ /* mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes); */
+ }
+
+ return 0;
+}
+// Change member info
+int mapif_parse_GuildChangeMemberInfoShort(int fd,int guild_id,
+ int account_id,int char_id,int online,int lv,int class_)
+{
+ // Could speed up by manipulating only guild_member
+ struct guild * g;
+ int i,alv,c;
+ int prev_count;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+
+ prev_count = g->connect_member;
+ g->connect_member=0;
+
+ for(i=0,alv=0,c=0;i<g->max_member;i++){
+ if(g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id){
+
+ g->member[i].online=online;
+ g->member[i].lv=lv;
+ g->member[i].class_=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++;
+ }
+
+ if (c)
+ {
+ alv= alv/c;
+ if (g->connect_member != prev_count || g->average_lv != alv)
+ {
+ g->average_lv=alv;
+ g->save_flag |= GS_BASIC; //FIXME: Save the base guild just because the avl/connect count changed?
+ }
+ if (g->save_flag & GS_REMOVE)
+ g->save_flag &= ~GS_REMOVE;
+ }
+ g->save_flag |= GS_MEMBER; //Update guild member data
+ return 0;
+}
+
+// BreakGuild
+int mapif_parse_BreakGuild(int fd,int guild_id)
+{
+ struct guild * g;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+
+ // Delete guild from sql
+ //printf("- Delete guild %d from guild\n",guild_id);
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ //printf("- Update guild %d of char\n",guild_id);
+ sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'",char_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ mapif_guild_broken(guild_id,0);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) broken" RETCODE,g->name,guild_id);
+
+ //Remove the guild from memory. [Skotlex]
+ g = idb_remove(guild_db_, guild_id);
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒbƒZ[ƒW‘—M
+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, fd);
+}
+// ƒMƒ‹ƒhŠî–{ƒf[ƒ^•ÏX—v‹
+int mapif_parse_GuildBasicInfoChange(int fd,int guild_id,
+ int type,const char *data,int len)
+{
+ struct guild * g;
+// int dd=*((int *)data);
+ short dw=*((short *)data);
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+
+ switch(type){
+ case GBI_GUILDLV: {
+ ShowDebug("GBI_GUILDLV\n");
+ 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);
+ g->save_flag |= GS_BASIC;
+ } return 0;
+ default:
+ ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n",type);
+ break;
+ }
+ mapif_guild_basicinfochanged(guild_id,type,data,len);
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒoƒf[ƒ^•ÏX—v‹
+int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,int account_id,int char_id,
+ int type,const char *data,int len)
+{
+ // Could make some improvement in speed, because only change guild_member
+ int i;
+ struct guild * g;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id )
+ break;
+ if(i==g->max_member){
+ ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in guild (%d - %s)\n",
+ account_id,char_id,guild_id,g->name);
+ return 0;
+ }
+ switch(type){
+ case GMI_POSITION: // –ðE
+ {
+ g->member[i].position=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= (GS_BASIC|GS_MEMBER);
+ 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ƒAƒbƒv”»’f
+ mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= (GS_BASIC|GS_MEMBER);
+ break;
+ }
+ case GMI_HAIR:
+ {
+ g->member[i].hair=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= GS_MEMBER; //Save new data.
+ break;
+ }
+ case GMI_HAIR_COLOR:
+ {
+ g->member[i].hair_color=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= GS_MEMBER; //Save new data.
+ break;
+ }
+ case GMI_GENDER:
+ {
+ g->member[i].gender=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= GS_MEMBER; //Save new data.
+ break;
+ }
+ case GMI_CLASS:
+ {
+ g->member[i].class_=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= GS_MEMBER; //Save new data.
+ break;
+ }
+ case GMI_LEVEL:
+ {
+ g->member[i].lv=*((int *)data);
+ mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len);
+ g->save_flag |= GS_MEMBER; //Save new data.
+ break;
+ }
+ default:
+ ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n",type);
+ break;
+ }
+ return 0;
+}
+
+int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender)
+{
+ return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender));
+}
+
+// ƒMƒ‹ƒh–ðE–¼•ÏX—v‹
+int mapif_parse_GuildPosition(int fd,int guild_id,int idx,struct guild_position *p)
+{
+ // Could make some improvement in speed, because only change guild_position
+ struct guild * g;
+
+ g = inter_guild_fromsql(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);
+ ShowInfo("int_guild: position data changed (Guild %d, position %d)\n",guild_id, idx);
+ g->save_flag |= GS_POSITION; // Change guild_position
+ return 0;
+}
+// ƒMƒ‹ƒhƒXƒLƒ‹ƒAƒbƒv—v‹
+int mapif_parse_GuildSkillUp(int fd,int guild_id,int skill_num,int account_id)
+{
+ // Could make some improvement in speed, because only change guild_position
+ struct guild * g;
+ int idx = skill_num - GD_SKILLBASE;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g == NULL || idx < 0 || idx >= MAX_GUILDSKILL)
+ 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))
+ mapif_guild_info(-1,g);
+ mapif_guild_skillupack(guild_id,skill_num,account_id);
+ ShowDebug("int_guild: skill %d up\n",skill_num);
+ g->save_flag |= (GS_BASIC|GS_SKILL); // Change guild & guild_skill
+ }
+
+ return 0;
+}
+
+//Manual deletion of an alliance when partnering guild does not exists. [Skotlex]
+static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag)
+{
+ int i;
+ char name[NAME_LENGTH];
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == guild_id)
+ {
+ strcpy(name, g->alliance[i].name);
+ g->alliance[i].guild_id=0;
+ break;
+ }
+ if (i == MAX_GUILDALLIANCE)
+ return -1;
+
+ mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name);
+ g->save_flag |= GS_ALLIANCE;
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿—v‹
+int mapif_parse_GuildAlliance(int fd,int guild_id1,int guild_id2,
+ int account_id1,int account_id2,int flag)
+{
+ // Could speed up
+ struct guild *g[2];
+ int j,i;
+ g[0] = inter_guild_fromsql(guild_id1);
+ g[1] = inter_guild_fromsql(guild_id2);
+
+ if(g[0] && g[1]==NULL && (flag&0x8)) //Requested to remove an alliance with a not found guild.
+ return mapif_parse_GuildDeleteAlliance(g[0], guild_id2,
+ account_id1, account_id2, flag); //Try to do a manual removal of said guild.
+
+ 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,NAME_LENGTH-1);
+ g[i]->alliance[j].opposition=flag&1;
+ break;
+ }
+ }
+ }else{ // ŠÖŒW‰ðÁ
+ 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);
+ g[0]->save_flag |= GS_ALLIANCE;
+ g[1]->save_flag |= GS_ALLIANCE;
+ return 0;
+}
+// ƒMƒ‹ƒh’m•ÏX—v‹
+int mapif_parse_GuildNotice(int fd,int guild_id,const char *mes1,const char *mes2)
+{
+ struct guild *g;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->mes1,mes1,60);
+ memcpy(g->mes2,mes2,120);
+ g->save_flag |= GS_BASIC; //Change mes of guild
+ return mapif_guild_notice(g);
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX—v‹
+int mapif_parse_GuildEmblem(int fd,int len,int guild_id,int dummy,const char *data)
+{
+ struct guild * g;
+
+ g = inter_guild_fromsql(guild_id);
+ if(g==NULL)
+ return 0;
+ memcpy(g->emblem_data,data,len);
+ g->emblem_len=len;
+ g->emblem_id++;
+ g->save_flag |= GS_BASIC; //Change guild
+ return mapif_guild_emblem(g);
+}
+
+int mapif_parse_GuildCastleDataLoad(int fd,int castle_id,int index) // <Agit>
+{
+ struct guild_castle gc;
+ if (!inter_guildcastle_fromsql(castle_id, &gc)) {
+ 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); break;
+ case 2: return mapif_guild_castle_dataload(gc.castle_id,index,gc.economy); break;
+ case 3: return mapif_guild_castle_dataload(gc.castle_id,index,gc.defense); break;
+ case 4: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerE); break;
+ case 5: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerD); break;
+ case 6: return mapif_guild_castle_dataload(gc.castle_id,index,gc.nextTime); break;
+ case 7: return mapif_guild_castle_dataload(gc.castle_id,index,gc.payTime); break;
+ case 8: return mapif_guild_castle_dataload(gc.castle_id,index,gc.createTime); break;
+ case 9: return mapif_guild_castle_dataload(gc.castle_id,index,gc.visibleC); break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-10].visible); break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-18].hp); break;
+ default:
+ ShowError("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+}
+
+int mapif_parse_GuildCastleDataSave(int fd,int castle_id,int index,int value) // <Agit>
+{
+ struct guild_castle gc;
+ if(!inter_guildcastle_fromsql(castle_id, &gc))
+ 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=idb_get(guild_db_, gid);
+ if(log_inter)
+ inter_log("guild %s (id=%d) %s castle id=%d" RETCODE,
+ (g)?g->name:"??" ,gid, (value)?"occupy":"abandon", castle_id);
+ }
+ gc.guild_id = value;
+ break;
+ case 2: gc.economy = value; break;
+ case 3: gc.defense = value; break;
+ case 4: gc.triggerE = value; break;
+ case 5: gc.triggerD = value; break;
+ case 6: gc.nextTime = value; break;
+ case 7: gc.payTime = value; break;
+ case 8: gc.createTime = value; break;
+ case 9: gc.visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc.guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc.guardian[index-18].hp = value; break; // end additions [Valaris]
+ default:
+ ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ inter_guildcastle_tosql(&gc);
+ mapif_guild_castle_datasave(gc.castle_id,index,value);
+ return 0;
+}
+
+int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len)
+{
+ struct guild * g;
+ struct guild_member gm;
+ int pos;
+
+ g = inter_guild_fromsql(guild_id);
+
+ if(g==NULL || len > NAME_LENGTH)
+ return 0;
+
+ for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++);
+
+ if (pos == g->max_member)
+ return 0; //Character not found??
+
+ memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
+ memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
+ memcpy(&g->member[0], &gm, sizeof(struct guild_member));
+
+ g->member[pos].position = g->member[0].position;
+ g->member[0].position = 0; //Position 0: guild Master.
+ strncpy(g->master, name, len);
+ if (len < NAME_LENGTH)
+ g->master[len] = '\0';
+
+ ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",g->master, guild_id, g->name);
+ g->save_flag |= (GS_BASIC|GS_POSITION); //Save main data and member data.
+ return mapif_guild_master_changed(g, pos);
+}
+
+// ƒMƒ‹ƒhƒ`ƒFƒbƒN—v‹
+int mapif_parse_GuildCheck(int fd,int guild_id,int account_id,int char_id)
+{
+ // What does this mean? Check if belong to another guild?
+ return 0;
+}
+
+// map server ‚©‚ç‚Ì’ÊM
+// E‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// EƒpƒPƒbƒg’·ƒf[ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// EƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// EƒGƒ‰[‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_guild_parse_frommap(int fd)
+{
+ switch(RFIFOW(fd,0)){
+ case 0x3030: mapif_parse_CreateGuild(fd,RFIFOL(fd,4),(char*)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 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break;
+ case 0x3034: mapif_parse_GuildLeave(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),(const char*)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),(char*)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),(const char*)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),(const char*)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),(const char*)RFIFOP(fd,6),(const char*)RFIFOP(fd,66)); break;
+ case 0x303F: mapif_parse_GuildEmblem(fd,RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8),(const char*)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);
+}
+
+// ƒT[ƒo[‚©‚ç’E‘Þ—v‹iƒLƒƒƒ‰íœ—pj
+int inter_guild_leave(int guild_id,int account_id,int char_id)
+{
+ return mapif_parse_GuildLeave(-1,guild_id,account_id,char_id,0,"** Character Deleted **");
+}
+
+int inter_guild_broken(int guild_id)
+{
+ return mapif_guild_broken(guild_id, 0);
+}
diff --git a/src/char_sql/int_guild.h b/src/char_sql/int_guild.h
new file mode 100644
index 000000000..452d55612
--- /dev/null
+++ b/src/char_sql/int_guild.h
@@ -0,0 +1,18 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_GUILD_H_
+#define _INT_GUILD_H_
+
+int inter_guild_parse_frommap(int fd);
+int inter_guild_sql_init();
+void inter_guild_sql_final();
+int inter_guild_mapif_init(int fd);
+int inter_guild_leave(int guild_id,int account_id,int char_id);
+int mapif_parse_BreakGuild(int fd,int guild_id);
+int inter_guild_broken(int guild_id);
+int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender);
+int inter_guild_CharOnline(int char_id, int guild_id);
+int inter_guild_CharOffline(int char_id, int guild_id);
+
+#endif
diff --git a/src/char_sql/int_party.c b/src/char_sql/int_party.c
new file mode 100644
index 000000000..0479df848
--- /dev/null
+++ b/src/char_sql/int_party.c
@@ -0,0 +1,865 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by hack
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "char.h"
+#include "../common/db.h"
+#include "../common/strlib.h"
+#include "../common/socket.h"
+#include "../common/showmsg.h"
+
+static struct party *party_pt;
+static int party_newid = 100;
+static struct dbt *party_db_;
+
+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 char_id);
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+//Party Flags on what to save/delete.
+//Create a new party entry (index holds leader's info)
+#define PS_CREATE 0x01
+//Update basic party info.
+#define PS_BASIC 0x02
+//Update party's leader
+#define PS_LEADER 0x04
+//Specify new party member (index specifies which party member)
+#define PS_ADDMEMBER 0x08
+//Specify member that left (index specifies which party member)
+#define PS_DELMEMBER 0x10
+//Specify that this party must be deleted.
+#define PS_BREAK 0x20
+
+// Save party to mysql
+int inter_party_tosql(int party_id,struct party *p, int flag, int index)
+{
+ // 'party' ('party_id','name','exp','item','leader_id','leader_char')
+ char t_name[NAME_LENGTH*2]; //Required for jstrescapecpy [Skotlex]
+ int party_exist = 0;
+ if (p == NULL || party_id == 0 || p->party_id == 0 || party_id != p->party_id) {
+ ShowError("Party pointer or party_id error (id: %d)\n", party_id);
+ return 0;
+ }
+#ifdef NOISY
+ ShowInfo("Save party request ("CL_BOLD"%d"CL_RESET" - %s).\n", party_id, p->name);
+#endif
+ jstrescapecpy(t_name, p->name);
+
+ if (flag&PS_BREAK) { //Break the party
+ // we'll skip name-checking and just reset everyone with the same party id [celest]
+ sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `party_id`='%d'", party_db, party_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ //Remove from memory
+ idb_remove(party_db_, party_id);
+ return 1;
+ }
+
+ if(flag&PS_CREATE) {
+ //Create party, first check if ID exists.
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `party_id`='%d'", party_db, party_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res != NULL && mysql_num_rows(sql_res) > 0) {
+ sql_row = mysql_fetch_row(sql_res);
+ party_exist = atoi (sql_row[0]);
+ }
+ mysql_free_result(sql_res);
+ if (party_exist) { //TODO: Can't we just use an index, and then retrieve the new party's index from SQL? [Skotlex]
+ ShowError("inter_party_tosql: Creating party with already existing ID %d!\n", party_id);
+ aFree(p); //Free party, couldn't create it.
+ return 0;
+ }
+ sprintf(tmp_sql, "INSERT INTO `%s` (`party_id`, `name`, `exp`, `item`, `leader_id`, `leader_char`) VALUES ('%d', '%s', '%d', '%d', '%d', '%d')",
+ party_db, party_id, t_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(p); //Free party, couldn't create it.
+ return 0;
+ }
+ //Add party to db
+ idb_put(party_db_, party_id, p);
+ }
+
+ if (flag&PS_BASIC) {
+ //Update party info.
+ sprintf(tmp_sql, "UPDATE `%s` SET `name`='%s', `exp`='%d', `item`='%d' WHERE `party_id`='%d'",
+ party_db, t_name, p->exp, p->item, party_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if (flag&PS_LEADER) {
+ //Update leader
+ sprintf(tmp_sql, "UPDATE `%s` SET `leader_id`='%d', `leader_char`='%d' WHERE `party_id`='%d'",
+ party_db, p->member[index].account_id, p->member[index].char_id, party_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if (flag&PS_ADDMEMBER) {
+ //Add one party member.
+ sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='%d' WHERE `account_id`='%d' AND `char_id`='%d'",
+ char_db, party_id, p->member[index].account_id, p->member[index].char_id);
+ if (mysql_query (&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if (flag&PS_DELMEMBER) {
+ //Remove one party member.
+ sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d' AND `account_id`='%d' AND `char_id`='%d'",
+ char_db, party_id, p->member[index].account_id, p->member[index].char_id);
+ if (mysql_query (&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if (save_log)
+ ShowInfo("Party Saved (%d - %s)\n", party_id, p->name);
+ return 1;
+}
+
+// Read party from mysql
+struct party *inter_party_fromsql(int party_id)
+{
+ int leader_id = 0, leader_char = 0;
+ struct party *p;
+#ifdef NOISY
+ ShowInfo("Load party request ("CL_BOLD"%d"CL_RESET")\n", party_id);
+#endif
+ if (party_id <=0)
+ return NULL;
+
+ //Load from memory
+ if ((p = idb_get(party_db_, party_id)) != NULL)
+ return p;
+
+ p = party_pt;
+ memset(p, 0, sizeof(struct party));
+
+ sprintf(tmp_sql, "SELECT `party_id`, `name`,`exp`,`item`, `leader_id`, `leader_char` FROM `%s` WHERE `party_id`='%d'",
+ party_db, party_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return NULL;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res != NULL && mysql_num_rows(sql_res) > 0) {
+ sql_row = mysql_fetch_row(sql_res);
+ p->party_id = party_id;
+ memcpy(p->name, sql_row[1], NAME_LENGTH-1);
+ p->exp = atoi(sql_row[2])?1:0;
+ p->item = atoi(sql_row[3]);
+ leader_id = atoi(sql_row[4]);
+ leader_char = atoi(sql_row[5]);
+ } else {
+ mysql_free_result(sql_res);
+ return NULL;
+ }
+ mysql_free_result(sql_res);
+
+ // Load members
+ sprintf(tmp_sql,"SELECT `account_id`,`char_id`,`name`,`base_level`,`last_map`,`online` FROM `%s` WHERE `party_id`='%d'",
+ char_db, party_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return NULL;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res != NULL && mysql_num_rows(sql_res) > 0) {
+ int i;
+ for (i = 0; (sql_row = mysql_fetch_row(sql_res)); i++) {
+ struct party_member *m = &p->member[i];
+ m->account_id = atoi(sql_row[0]);
+ m->char_id = atoi(sql_row[1]);
+ m->leader = (m->account_id == leader_id && m->char_id == leader_char)?1:0;
+ memcpy(m->name, sql_row[2], NAME_LENGTH);
+ m->lv = atoi(sql_row[3]);
+ m->map = mapindex_name2id(sql_row[4]);
+ m->online = atoi(sql_row[5])?1:0;
+ }
+ }
+ mysql_free_result(sql_res);
+
+ if (save_log)
+ ShowInfo("Party loaded (%d - %s).\n",party_id, p->name);
+ //Add party to memory.
+ p = aCalloc(1, sizeof(struct party));
+ memcpy(p, party_pt, sizeof(struct party));
+ idb_put(party_db_, party_id, p);
+ return p;
+}
+
+int inter_party_sql_init(){
+ int i;
+
+ //memory alloc
+ party_db_ = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ party_pt = (struct party*)aCalloc(sizeof(struct party), 1);
+ if (!party_pt) {
+ ShowFatalError("inter_party_sql_init: Out of Memory!\n");
+ exit(1);
+ }
+ sprintf (tmp_sql , "SELECT count(*) FROM `%s`", party_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ sql_row = mysql_fetch_row(sql_res);
+ ShowStatus("total party data -> '%s'.......\n",sql_row[0]);
+ i = atoi (sql_row[0]);
+ mysql_free_result(sql_res);
+
+ if (i > 0) {
+ //set party_newid
+ sprintf (tmp_sql , "SELECT max(`party_id`) FROM `%s`", party_db);
+ if(mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+
+ sql_row = mysql_fetch_row(sql_res);
+ party_newid = atoi (sql_row[0])+1;
+ mysql_free_result(sql_res);
+ }
+
+ ShowDebug("set party_newid: %d.......\n", party_newid);
+
+ /* Uncomment the following if you want to do a party_db cleanup (remove parties with no members) on startup.[Skotlex]
+ ShowStatus("cleaning party table...\n");
+ sprintf (tmp_sql,
+ "DELETE FROM `%s` USING `%s` LEFT JOIN `%s` ON `%s`.leader_id =`%s`.account_id AND `%s`.leader_char = `%s`.char_id WHERE `%s`.account_id IS NULL",
+ party_db, party_db, char_db, party_db, char_db, party_db, char_db, char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ */
+ return 0;
+}
+
+void inter_party_sql_final()
+{
+ party_db_->destroy(party_db_, NULL);
+ aFree(party_pt);
+ return;
+}
+
+// Search for the party according to its name
+struct party* search_partyname(char *str)
+{
+ char t_name[NAME_LENGTH*2];
+ int party_id;
+
+ sprintf(tmp_sql,"SELECT `party_id` FROM `%s` WHERE `name`='%s'",party_db, jstrescapecpy(t_name,str));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res==NULL || mysql_num_rows(sql_res)<=0)
+ {
+ if (sql_res) mysql_free_result(sql_res);
+ return NULL;
+ }
+ sql_row = mysql_fetch_row(sql_res);
+ party_id = sql_row?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+
+ return inter_party_fromsql(party_id);
+}
+
+// EXPŒö•½•ª”z‚Å‚«‚é‚©ƒ`ƒFƒbƒN
+int party_check_exp_share(struct party *p)
+{
+ int i, oi[MAX_PARTY], dudes=0;
+ int maxlv=0,minlv=0x7fffffff;
+
+ for(i=0;i<MAX_PARTY;i++){
+ int lv=p->member[i].lv;
+ if (!lv) continue;
+ if( p->member[i].online ){
+ if( lv < minlv ) minlv=lv;
+ if( maxlv < lv ) maxlv=lv;
+ if( lv >= 70 ) dudes+=1000;
+ oi[dudes%1000] = i;
+ dudes++;
+ }
+ }
+ if((dudes/1000 >= 2) && (dudes%1000 == 3) && maxlv-minlv>party_share_level)
+ {
+ int pl1=0,pl2=0,pl3=0;
+ pl1=char_nick2id(p->member[oi[0]].name);
+ pl2=char_nick2id(p->member[oi[1]].name);
+ pl3=char_nick2id(p->member[oi[2]].name);
+ ShowDebug("PARTY: group of 3 Id1 %d lv %d name %s Id2 %d lv %d name %s Id3 %d lv %d name %s\n",pl1,p->member[oi[0]].lv,p->member[oi[0]].name,pl2,p->member[oi[1]].lv,p->member[oi[1]].name,pl3,p->member[oi[2]].lv,p->member[oi[2]].name);
+ if (char_married(pl1,pl2) && char_child(pl1,pl3))
+ return 1;
+ if (char_married(pl1,pl3) && char_child(pl1,pl2))
+ return 1;
+ if (char_married(pl2,pl3) && char_child(pl2,pl1))
+ return 1;
+ }
+ return (maxlv==0 || maxlv-minlv<=party_share_level);
+}
+
+// Is there any member in the party?
+int party_check_empty(struct party *p)
+{
+ int i;
+ if (p==NULL||p->party_id==0) return 1;
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id>0){
+ return 0;
+ }
+ }
+ // If there is no member, then break the party
+ mapif_party_broken(p->party_id,0);
+ inter_party_tosql(p->party_id,p, PS_BREAK, 0);
+ return 1;
+}
+
+
+// Check if a member is in two party, not necessary :)
+int party_check_conflict(int party_id,int account_id,int char_id)
+{
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// map server‚Ö‚Ì’ÊM
+
+// ƒp[ƒeƒB쬉”Û
+int mapif_party_created(int fd,int account_id,int char_id,struct party *p)
+{
+ WFIFOW(fd,0)=0x3820;
+ WFIFOL(fd,2)=account_id;
+ WFIFOL(fd,6)=char_id;
+ if(p!=NULL){
+ WFIFOB(fd,10)=0;
+ WFIFOL(fd,11)=p->party_id;
+ memcpy(WFIFOP(fd,15),p->name,NAME_LENGTH);
+ ShowInfo("int_party: Party created (%d - %s)\n",p->party_id,p->name);
+ }else{
+ WFIFOB(fd,10)=1;
+ WFIFOL(fd,11)=0;
+ memset(WFIFOP(fd,15),0,NAME_LENGTH);
+ }
+ WFIFOSET(fd,39);
+
+ return 0;
+}
+
+// ƒp[ƒeƒBî•ñŒ©‚‚©‚炸
+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);
+ ShowWarning("int_party: info not found %d\n",party_id);
+ return 0;
+}
+// ƒp[ƒeƒBî•ñ‚Ü‚Æ‚ß‘—‚è
+int mapif_party_info(int fd,struct party *p)
+{
+ unsigned char buf[10+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));
+ return 0;
+}
+// ƒp[ƒeƒBƒƒ“ƒo’ljÁ‰Â”Û
+int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) {
+ WFIFOHEAD(fd, 15);
+ WFIFOW(fd,0) = 0x3822;
+ WFIFOL(fd,2) = party_id;
+ WFIFOL(fd,6) = account_id;
+ WFIFOL(fd,10) = char_id;
+ WFIFOB(fd,14) = flag;
+ WFIFOSET(fd,15);
+
+ return 0;
+}
+
+// ƒp[ƒeƒBÝ’è•ÏX’Ê’m
+int mapif_party_optionchanged(int fd,struct party *p,int account_id,int flag)
+{
+ unsigned char buf[16];
+ 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);
+ return 0;
+}
+
+//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex]
+int inter_party_logged(int party_id, int account_id, int char_id)
+{
+ struct party *p;
+ int i;
+
+ if (party_id <= 0)
+ return 0;
+
+ if (!party_id)
+ return 0;
+ p = inter_party_fromsql(party_id);
+ if(!p) //Non existant party?
+ return 0;
+
+ for(i = 0; i < MAX_PARTY; i++)
+ if (p->member[i].account_id==account_id && p->member[i].char_id==char_id) {
+ p->member[i].online = 1;
+ break;
+ }
+
+ if(p->exp && !party_check_exp_share(p))
+ {
+ p->exp=0;
+ mapif_party_optionchanged(0,p,0,0);
+ return 1;
+ }
+ return 0;
+}
+
+// ƒp[ƒeƒB’E‘Þ’Ê’m
+int mapif_party_leaved(int party_id,int account_id, int char_id) {
+ unsigned char buf[16];
+
+ WBUFW(buf,0) = 0x3824;
+ WBUFL(buf,2) = party_id;
+ WBUFL(buf,6) = account_id;
+ WBUFL(buf,10) = char_id;
+ mapif_sendall(buf, 14);
+ return 0;
+}
+
+// ƒp[ƒeƒBƒ}ƒbƒvXV’Ê’m
+int mapif_party_membermoved(struct party *p,int idx)
+{
+ unsigned char buf[20];
+
+ WBUFW(buf,0) = 0x3825;
+ WBUFL(buf,2) = p->party_id;
+ WBUFL(buf,6) = p->member[idx].account_id;
+ WBUFL(buf,10) = p->member[idx].char_id;
+ WBUFW(buf,14) = p->member[idx].map;
+ WBUFB(buf,16) = p->member[idx].online;
+ WBUFW(buf,17) = p->member[idx].lv;
+ mapif_sendall(buf, 19);
+ return 0;
+}
+
+// ƒp[ƒeƒB‰ðŽU’Ê’m
+int mapif_party_broken(int party_id,int flag)
+{
+ unsigned char buf[16];
+ 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;
+}
+// ƒp[ƒeƒB“à”­Œ¾
+int mapif_party_message(int party_id,int account_id,char *mes,int len, int sfd)
+{
+ unsigned char buf[512];
+ 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_sendallwos(sfd, buf,len+12);
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// map server‚©‚ç‚Ì’ÊM
+
+
+// Create Party
+int mapif_parse_CreateParty(int fd, int account_id, int char_id, char *name, char *nick, unsigned short map, int lv, int item, int item2)
+{
+ struct party *p;
+ if( (p=search_partyname(name))!=NULL){
+ mapif_party_created(fd,account_id,char_id,NULL);
+ return 0;
+ }
+ p= aCalloc(1, sizeof(struct party));
+
+ p->party_id=party_newid++;
+ memcpy(p->name,name,NAME_LENGTH);
+ p->exp=0;
+ p->item=(item?1:0)|(item2?2:0);
+ p->itemc = 0;
+
+ p->member[0].account_id=account_id;
+ p->member[0].char_id =char_id;
+ memcpy(p->member[0].name,nick,NAME_LENGTH-1);
+ p->member[0].map = map;
+ p->member[0].leader=1;
+ p->member[0].online=1;
+ p->member[0].lv=lv;
+
+ if (inter_party_tosql(p->party_id,p,PS_CREATE|PS_ADDMEMBER,0)) {
+ mapif_party_created(fd,account_id,char_id,p);
+ mapif_party_info(fd,p);
+ } else //Failed to create party.
+ mapif_party_created(fd,account_id,char_id,NULL);
+
+ return 0;
+}
+// ƒp[ƒeƒBî•ñ—v‹
+int mapif_parse_PartyInfo(int fd,int party_id)
+{
+ struct party *p;
+ p = inter_party_fromsql(party_id);
+
+ if (p)
+ mapif_party_info(fd,p);
+ else
+ mapif_party_noinfo(fd,party_id);
+ return 0;
+}
+// ƒp[ƒeƒB’ljÁ—v‹
+int mapif_parse_PartyAddMember(int fd, int party_id, int account_id, int char_id, char *nick, unsigned short map, int lv) {
+ struct party *p;
+ int i;
+
+ p = inter_party_fromsql(party_id);
+
+ if(!p){
+ mapif_party_memberadded(fd,party_id,account_id,char_id,1);
+ return 0;
+ }
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id==0){
+ int flag=0;
+
+ p->member[i].account_id=account_id;
+ p->member[i].char_id=char_id;
+ memcpy(p->member[i].name,nick,NAME_LENGTH);
+ p->member[i].map = map;
+ p->member[i].leader=0;
+ p->member[i].online=1;
+ p->member[i].lv=lv;
+ mapif_party_memberadded(fd,party_id,account_id,char_id,0);
+ mapif_party_info(-1,p);
+
+ if( p->exp && !party_check_exp_share(p) ){
+ p->exp=0;
+ flag=0x01;
+ }
+ if(flag)
+ mapif_party_optionchanged(fd,p,0,0);
+
+ inter_party_tosql(party_id, p, PS_ADDMEMBER, i);
+ return 0;
+ }
+ }
+ mapif_party_memberadded(fd,party_id,account_id,char_id,1);
+ return 0;
+}
+// ƒp[ƒeƒB[Ý’è•ÏX—v‹
+int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int flag)
+{
+ struct party *p;
+ //NOTE: No clue what that flag is about, in all observations so far it always comes as 0. [Skotlex]
+ flag = 0;
+ p = inter_party_fromsql(party_id);
+
+ if(!p)
+ return 0;
+
+ p->exp=exp;
+ if( exp && !party_check_exp_share(p) ){
+ flag|=0x01;
+ p->exp=0;
+ }
+ mapif_party_optionchanged(fd,p,account_id,flag);
+ inter_party_tosql(party_id, p, PS_BASIC, 0);
+ return 0;
+}
+// ƒp[ƒeƒB’E‘Þ—v‹
+int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id)
+{
+ struct party *p;
+ int i;
+
+ p = inter_party_fromsql(party_id);
+ if (!p) { //Party does not exists?
+ sprintf(tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+ }
+
+ for (i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ break;
+ }
+ if (i>= MAX_PARTY)
+ return 0; //Member not found?
+
+ mapif_party_leaved(party_id, account_id, char_id);
+
+ if (p->member[i].leader){
+ int j;
+ for (j = 0; j < MAX_PARTY; j++) {
+ if (p->member[j].account_id > 0 && j != i) {
+ mapif_party_leaved(party_id, p->member[j].account_id, p->member[j].char_id);
+ p->member[j].account_id = 0;
+ }
+ p->member[i].account_id = 0;
+ }
+ //Party gets deleted on the check_empty call below.
+ } else {
+ inter_party_tosql(party_id,p,PS_DELMEMBER,i);
+ memset(&p->member[i], 0, sizeof(struct party_member));
+ }
+
+ if (party_check_empty(p) == 0)
+ mapif_party_info(-1,p);
+ return 0;
+}
+// When member goes to other map
+int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, int lv)
+{
+ struct party *p;
+ int i;
+
+ p = inter_party_fromsql(party_id);
+ if (p == NULL)
+ return 0;
+
+ for(i = 0; i < MAX_PARTY; i++) {
+ if (p->member[i].account_id == account_id && p->member[i].char_id == char_id)
+ {
+ p->member[i].map = map;
+ p->member[i].online = online;
+ if (p->member[i].lv != lv) {
+ p->member[i].lv = lv;
+ if (p->exp && !party_check_exp_share(p)) {
+ p->exp = 0;
+ mapif_party_optionchanged(fd, p, 0, 0);
+ }
+ }
+ mapif_party_membermoved(p, i);
+ break;
+ }
+ }
+ return 0;
+}
+// ƒp[ƒeƒB‰ðŽU—v‹
+int mapif_parse_BreakParty(int fd,int party_id)
+{
+ struct party *p;
+
+ p = inter_party_fromsql(party_id);
+
+ if(!p)
+ return 0;
+ inter_party_tosql(party_id,p,PS_BREAK,0);
+ mapif_party_broken(fd,party_id);
+ return 0;
+}
+// ƒp[ƒeƒBƒƒbƒZ[ƒW‘—M
+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, fd);
+}
+// ƒp[ƒeƒBƒ`ƒFƒbƒN—v‹
+int mapif_parse_PartyCheck(int fd,int party_id,int account_id,int char_id)
+{
+ return party_check_conflict(party_id,account_id,char_id);
+}
+
+int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id)
+{
+ struct party *p;
+ int i;
+
+ p = inter_party_fromsql(party_id);
+
+ if(!p)
+ return 0;
+
+ for (i = 0; i < MAX_PARTY; i++)
+ {
+ if(p->member[i].leader)
+ p->member[i].leader = 0;
+ if(p->member[i].account_id == account_id && p->member[i].char_id == char_id) {
+ p->member[i].leader = 1;
+ inter_party_tosql(party_id,p,PS_LEADER, i);
+ }
+ }
+ return 1;
+}
+
+// map server ‚©‚ç‚Ì’ÊM
+// E‚PƒpƒPƒbƒg‚̂݉ðÍ‚·‚邱‚Æ
+// EƒpƒPƒbƒg’·ƒf[ƒ^‚Íinter.c‚ɃZƒbƒg‚µ‚Ä‚¨‚­‚±‚Æ
+// EƒpƒPƒbƒg’·ƒ`ƒFƒbƒN‚âARFIFOSKIP‚͌ĂÑo‚µŒ³‚Ås‚í‚ê‚é‚Ì‚Ås‚Á‚Ä‚Í‚È‚ç‚È‚¢
+// EƒGƒ‰[‚È‚ç0(false)A‚»‚¤‚Å‚È‚¢‚È‚ç1(true)‚ð‚©‚¦‚³‚È‚¯‚ê‚΂Ȃç‚È‚¢
+int inter_party_parse_frommap(int fd)
+{
+ RFIFOHEAD(fd);
+ switch(RFIFOW(fd,0)) {
+ case 0x3020: mapif_parse_CreateParty(fd, RFIFOL(fd,2), RFIFOL(fd,6),(char*)RFIFOP(fd,10), (char*)RFIFOP(fd,34), RFIFOW(fd,58), RFIFOW(fd,60), RFIFOB(fd,62), RFIFOB(fd,63)); break;
+ case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break;
+ case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), (char*)RFIFOP(fd,14), RFIFOW(fd,38), RFIFOW(fd,40)); 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), RFIFOL(fd,10)); break;
+ case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break;
+ case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break;
+ case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break;
+ case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
+ case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+// ƒT[ƒo[‚©‚ç’E‘Þ—v‹iƒLƒƒƒ‰íœ—pj
+int inter_party_leave(int party_id,int account_id, int char_id)
+{
+ return mapif_parse_PartyLeave(-1,party_id,account_id, char_id);
+}
+
+int inter_party_CharOnline(int char_id, int party_id) {
+ struct party *p;
+ int i;
+
+ if (party_id == -1) {
+ //Get guild_id from the database
+ sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res == NULL)
+ return 0; //Eh? No party?
+
+ sql_row = mysql_fetch_row(sql_res);
+ party_id = sql_row?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+ }
+ if (party_id == 0)
+ return 0; //No party...
+
+ p = inter_party_fromsql(party_id);
+ if(!p) {
+ ShowError("Character %d's party %d not found!\n", char_id, party_id);
+ return 0;
+ }
+
+ //Set member online
+ for(i=0; i<MAX_PARTY; i++) {
+ if (p->member[i].char_id == char_id) {
+ p->member[i].online = 1;
+ break;
+ }
+ }
+ return 1;
+}
+
+int inter_party_CharOffline(int char_id, int party_id) {
+ struct party *p=NULL;
+ int online_count=0, i;
+
+ if (party_id == -1) {
+ //Get guild_id from the database
+ sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if(sql_res == NULL)
+ return 0; //Eh? No party?
+
+ sql_row = mysql_fetch_row(sql_res);
+ party_id = sql_row?atoi(sql_row[0]):0;
+ mysql_free_result(sql_res);
+ }
+ if (party_id == 0)
+ return 0; //No party...
+
+ //Character has a party, set character offline and check if they were the only member online
+ if ((p = inter_party_fromsql(party_id)) == NULL)
+ return 0;
+
+ //Set member offline
+ for(i=0; i< MAX_PARTY; i++) {
+ if(p->member[i].char_id == char_id)
+ p->member[i].online = 0;
+ if(p->member[i].online)
+ online_count++;
+ }
+
+ if(online_count == 0)
+ //Parties don't have any data that needs be saved at this point... so just remove it from memory.
+ idb_remove(party_db_, party_id);
+ return 1;
+}
diff --git a/src/char_sql/int_party.h b/src/char_sql/int_party.h
new file mode 100644
index 000000000..353787a2d
--- /dev/null
+++ b/src/char_sql/int_party.h
@@ -0,0 +1,14 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_PARTY_H_
+#define _INT_PARTY_H_
+
+int inter_party_parse_frommap(int fd);
+int inter_party_sql_init();
+void inter_party_sql_final();
+int inter_party_leave(int party_id,int account_id, int char_id);
+int inter_party_logged(int party_id, int account_id, int char_id);
+int inter_party_CharOnline(int char_id, int party_id);
+int inter_party_CharOffline(int char_id, int party_id);
+#endif
diff --git a/src/char_sql/int_pet.c b/src/char_sql/int_pet.c
new file mode 100644
index 000000000..cc0e9330e
--- /dev/null
+++ b/src/char_sql/int_pet.c
@@ -0,0 +1,351 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by Jioh L. Jung
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "char.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+
+struct s_pet *pet_pt;
+static int pet_newid = 100;
+
+
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+//---------------------------------------------------------
+int inter_pet_tosql(int pet_id, struct s_pet *p) {
+ //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`)
+ char t_name[NAME_LENGTH*2];
+
+// ShowInfo("Saving pet (%d)...\n",pet_id);
+
+ jstrescapecpy(t_name, p->name);
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+ sprintf(tmp_sql,"SELECT * FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0)
+ //row reside -> updating
+ sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incuvate`='%d' WHERE `pet_id`='%d'",
+ pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id,
+ p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate, p->pet_id);
+ else //no row -> insert
+ sprintf(tmp_sql,"INSERT INTO `%s` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) VALUES ('%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ pet_db, pet_id, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id,
+ p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate);
+ mysql_free_result(sql_res) ; //resource free
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if (save_log)
+ ShowInfo("Pet saved %d - %s.\n", pet_id, p->name);
+ return 0;
+}
+
+int inter_pet_fromsql(int pet_id, struct s_pet *p){
+
+#ifdef NOISY
+ ShowInfo("Loading pet (%d)...\n",pet_id);
+#endif
+ memset(p, 0, sizeof(struct s_pet));
+
+ //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`)
+
+ sprintf(tmp_sql,"SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate` FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ sql_row = mysql_fetch_row(sql_res);
+
+ p->pet_id = pet_id;
+ p->class_ = atoi(sql_row[1]);
+ memcpy(p->name, sql_row[2],NAME_LENGTH-1);
+ p->account_id = atoi(sql_row[3]);
+ p->char_id = atoi(sql_row[4]);
+ p->level = atoi(sql_row[5]);
+ p->egg_id = atoi(sql_row[6]);
+ p->equip = atoi(sql_row[7]);
+ p->intimate = atoi(sql_row[8]);
+ p->hungry = atoi(sql_row[9]);
+ p->rename_flag = atoi(sql_row[10]);
+ p->incuvate = atoi(sql_row[11]);
+ }
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+
+ mysql_free_result(sql_res);
+
+ if (save_log)
+ ShowInfo("Pet loaded (%d - %s).\n", pet_id, p->name);
+ return 0;
+}
+//----------------------------------------------
+
+int inter_pet_sql_init(){
+ int i;
+
+ //memory alloc
+ ShowDebug("interserver pet memory initialize.... (%d byte)\n",sizeof(struct s_pet));
+ pet_pt = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1);
+
+ sprintf (tmp_sql , "SELECT count(*) FROM `%s`", pet_db);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ exit(0);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ sql_row = mysql_fetch_row(sql_res);
+ ShowStatus("total pet data: '%s'\n",sql_row[0]);
+ i = atoi (sql_row[0]);
+ mysql_free_result(sql_res);
+
+ if (i > 0) {
+ //set pet_newid
+ sprintf (tmp_sql , "SELECT max(`pet_id`) FROM `%s`",pet_db );
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+
+ sql_row = mysql_fetch_row(sql_res);
+ pet_newid = atoi (sql_row[0])+1; //should SET MAX existing PET ID + 1 [Lupus]
+ mysql_free_result(sql_res);
+ }
+
+ ShowDebug("set pet_newid: %d.\n",pet_newid);
+
+ return 0;
+}
+void inter_pet_sql_final(){
+ if (pet_pt) aFree(pet_pt);
+ return;
+}
+//----------------------------------
+int inter_pet_delete(int pet_id){
+ ShowInfo("delete pet request: %d...\n",pet_id);
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+//------------------------------------------------------
+int mapif_pet_created(int fd, int account_id, struct s_pet *p)
+{
+ WFIFOW(fd, 0) =0x3880;
+ WFIFOL(fd, 2) =account_id;
+ if(p!=NULL){
+ WFIFOB(fd, 6)=0;
+ WFIFOL(fd, 7) =p->pet_id;
+ ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name);
+ }else{
+ WFIFOB(fd, 6)=1;
+ WFIFOL(fd, 7)=0;
+ }
+ WFIFOSET(fd, 11);
+
+ return 0;
+}
+
+int mapif_pet_info(int fd, int account_id, struct s_pet *p){
+ WFIFOW(fd, 0) =0x3881;
+ WFIFOW(fd, 2) =sizeof(struct s_pet) + 9;
+ WFIFOL(fd, 4) =account_id;
+ WFIFOB(fd, 8)=0;
+ memcpy(WFIFOP(fd, 9), p, sizeof(struct s_pet));
+ WFIFOSET(fd, WFIFOW(fd, 2));
+
+ return 0;
+}
+
+int mapif_pet_noinfo(int fd, int account_id){
+ WFIFOW(fd, 0) =0x3881;
+ WFIFOW(fd, 2) =sizeof(struct s_pet) + 9;
+ WFIFOL(fd, 4) =account_id;
+ WFIFOB(fd, 8)=1;
+ memset(WFIFOP(fd, 9), 0, sizeof(struct s_pet));
+ WFIFOSET(fd, WFIFOW(fd, 2));
+
+ return 0;
+}
+
+int mapif_save_pet_ack(int fd, int account_id, int flag){
+ WFIFOW(fd, 0) =0x3882;
+ WFIFOL(fd, 2) =account_id;
+ WFIFOB(fd, 6) =flag;
+ WFIFOSET(fd, 7);
+
+ return 0;
+}
+
+int mapif_delete_pet_ack(int fd, int flag){
+ WFIFOW(fd, 0) =0x3883;
+ WFIFOB(fd, 2) =flag;
+ WFIFOSET(fd, 3);
+
+ return 0;
+}
+
+int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id,
+ short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name){
+
+ memset(pet_pt, 0, sizeof(struct s_pet));
+ pet_pt->pet_id = pet_newid++;
+ memcpy(pet_pt->name, pet_name, NAME_LENGTH-1);
+ if(incuvate == 1)
+ pet_pt->account_id = pet_pt->char_id = 0;
+ else {
+ pet_pt->account_id = account_id;
+ pet_pt->char_id = char_id;
+ }
+ pet_pt->class_ = pet_class;
+ pet_pt->level = pet_lv;
+ pet_pt->egg_id = pet_egg_id;
+ pet_pt->equip = pet_equip;
+ pet_pt->intimate = intimate;
+ pet_pt->hungry = hungry;
+ pet_pt->rename_flag = rename_flag;
+ pet_pt->incuvate = incuvate;
+
+ if(pet_pt->hungry < 0)
+ pet_pt->hungry = 0;
+ else if(pet_pt->hungry > 100)
+ pet_pt->hungry = 100;
+ if(pet_pt->intimate < 0)
+ pet_pt->intimate = 0;
+ else if(pet_pt->intimate > 1000)
+ pet_pt->intimate = 1000;
+
+ inter_pet_tosql(pet_pt->pet_id,pet_pt);
+
+ mapif_pet_created(fd, account_id, pet_pt);
+
+ return 0;
+}
+
+int mapif_load_pet(int fd, int account_id, int char_id, int pet_id){
+ memset(pet_pt, 0, sizeof(struct s_pet));
+
+ inter_pet_fromsql(pet_id, pet_pt);
+
+ if(pet_pt!=NULL) {
+ if(pet_pt->incuvate == 1) {
+ pet_pt->account_id = pet_pt->char_id = 0;
+ mapif_pet_info(fd, account_id, pet_pt);
+ }
+ else if(account_id == pet_pt->account_id && char_id == pet_pt->char_id)
+ mapif_pet_info(fd, account_id, pet_pt);
+ else
+ mapif_pet_noinfo(fd, account_id);
+ }
+ else
+ mapif_pet_noinfo(fd, account_id);
+
+ return 0;
+}
+
+int mapif_save_pet(int fd, int account_id, struct s_pet *data) {
+ //here process pet save request.
+ int len=RFIFOW(fd, 2);
+ if(sizeof(struct s_pet)!=len-8) {
+ ShowError("inter pet: data size error %d %d\n", sizeof(struct s_pet), len-8);
+ }
+
+ else{
+ if(data->hungry < 0)
+ data->hungry = 0;
+ else if(data->hungry > 100)
+ data->hungry = 100;
+ if(data->intimate < 0)
+ data->intimate = 0;
+ else if(data->intimate > 1000)
+ data->intimate = 1000;
+ inter_pet_tosql(data->pet_id,data);
+ mapif_save_pet_ack(fd, account_id, 0);
+ }
+
+ return 0;
+}
+
+int mapif_delete_pet(int fd, int pet_id){
+ mapif_delete_pet_ack(fd, inter_pet_delete(pet_id));
+
+ return 0;
+}
+
+int mapif_parse_CreatePet(int fd){
+ mapif_create_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), RFIFOW(fd, 16), RFIFOL(fd, 18),
+ RFIFOL(fd, 20), RFIFOB(fd, 22), RFIFOB(fd, 23), (char*)RFIFOP(fd, 24));
+ return 0;
+}
+
+int mapif_parse_LoadPet(int fd){
+ mapif_load_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10));
+ return 0;
+}
+
+int mapif_parse_SavePet(int fd){
+ mapif_save_pet(fd, RFIFOL(fd, 4), (struct s_pet *) RFIFOP(fd, 8));
+ return 0;
+}
+
+int mapif_parse_DeletePet(int fd){
+ mapif_delete_pet(fd, RFIFOL(fd, 2));
+ return 0;
+}
+
+int inter_pet_parse_frommap(int fd){
+ switch(RFIFOW(fd, 0)){
+ case 0x3080: mapif_parse_CreatePet(fd); break;
+ case 0x3081: mapif_parse_LoadPet(fd); break;
+ case 0x3082: mapif_parse_SavePet(fd); break;
+ case 0x3083: mapif_parse_DeletePet(fd); break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
diff --git a/src/char_sql/int_pet.h b/src/char_sql/int_pet.h
new file mode 100644
index 000000000..d39574344
--- /dev/null
+++ b/src/char_sql/int_pet.h
@@ -0,0 +1,16 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_PET_H_
+#define _INT_PET_H_
+
+int inter_pet_init();
+void inter_pet_sql_final();
+int inter_pet_save();
+int inter_pet_delete(int pet_id);
+
+int inter_pet_parse_frommap(int fd);
+int inter_pet_sql_init();
+//extern char pet_txt[256];
+
+#endif
diff --git a/src/char_sql/int_storage.c b/src/char_sql/int_storage.c
new file mode 100644
index 000000000..1dac9dc51
--- /dev/null
+++ b/src/char_sql/int_storage.c
@@ -0,0 +1,364 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by Jioh L. Jung
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "char.h"
+#include "itemdb.h"
+#include "../common/showmsg.h"
+
+#define STORAGE_MEMINC 16
+
+// reset by inter_config_read()
+struct storage *storage_pt=NULL;
+struct guild_storage *guild_storage_pt=NULL;
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+// storage data -> DB conversion
+int storage_tosql(int account_id,struct storage *p){
+ int i,j;
+// int eqcount=1;
+// int noteqcount=1;
+ int count=0;
+ struct itemtmp mapitem[MAX_GUILD_STORAGE];
+ for(i=0;i<MAX_STORAGE;i++){
+ if(p->storage_[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->storage_[i].id;
+ mapitem[count].nameid=p->storage_[i].nameid;
+ mapitem[count].amount = p->storage_[i].amount;
+ mapitem[count].equip = p->storage_[i].equip;
+ mapitem[count].identify = p->storage_[i].identify;
+ mapitem[count].refine = p->storage_[i].refine;
+ mapitem[count].attribute = p->storage_[i].attribute;
+ for(j=0; j<MAX_SLOTS; j++)
+ mapitem[count].card[j] = p->storage_[i].card[j];
+ count++;
+ }
+ }
+
+ memitemdata_to_sql(mapitem, count, account_id,TABLE_STORAGE);
+
+ //printf ("storage dump to DB - id: %d (total: %d)\n", account_id, j);
+ return 0;
+}
+
+// DB -> storage data conversion
+int storage_fromsql(int account_id, struct storage *p){
+ int i=0,j;
+ char * str_p = tmp_sql;
+
+ memset(p,0,sizeof(struct storage)); //clean up memory
+ p->storage_amount = 0;
+ p->account_id = account_id;
+
+ // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`}
+ str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`");
+
+ for (j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p," FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id);
+
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) { //start to fetch
+ p->storage_[i].id= atoi(sql_row[0]);
+ p->storage_[i].nameid= atoi(sql_row[1]);
+ p->storage_[i].amount= atoi(sql_row[2]);
+ p->storage_[i].equip= atoi(sql_row[3]);
+ p->storage_[i].identify= atoi(sql_row[4]);
+ p->storage_[i].refine= atoi(sql_row[5]);
+ p->storage_[i].attribute= atoi(sql_row[6]);
+ for (j=0; j<MAX_SLOTS; j++)
+ p->storage_[i].card[j]= atoi(sql_row[7+j]);
+ p->storage_amount = ++i;
+ }
+ mysql_free_result(sql_res);
+ }
+
+ ShowInfo ("storage load complete from DB - id: %d (total: %d)\n", account_id, p->storage_amount);
+ return 1;
+}
+
+// Save guild_storage data to sql
+int guild_storage_tosql(int guild_id, struct guild_storage *p){
+ int i,j;
+// int eqcount=1;
+// int noteqcount=1;
+ int count=0;
+ struct itemtmp mapitem[MAX_GUILD_STORAGE];
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(p->storage_[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->storage_[i].id;
+ mapitem[count].nameid=p->storage_[i].nameid;
+ mapitem[count].amount = p->storage_[i].amount;
+ mapitem[count].equip = p->storage_[i].equip;
+ mapitem[count].identify = p->storage_[i].identify;
+ mapitem[count].refine = p->storage_[i].refine;
+ mapitem[count].attribute = p->storage_[i].attribute;
+ for (j=0; j<MAX_SLOTS; j++)
+ mapitem[count].card[j] = p->storage_[i].card[j];
+ count++;
+ }
+ }
+
+ memitemdata_to_sql(mapitem, count, guild_id,TABLE_GUILD_STORAGE);
+
+ ShowInfo ("guild storage save to DB - id: %d (total: %d)\n", guild_id,i);
+ return 0;
+}
+
+// Load guild_storage data to mem
+int guild_storage_fromsql(int guild_id, struct guild_storage *p){
+ int i=0,j;
+ struct guild_storage *gs=guild_storage_pt;
+ char * str_p = tmp_sql;
+ p=gs;
+
+ memset(p,0,sizeof(struct guild_storage)); //clean up memory
+ p->storage_amount = 0;
+ p->guild_id = guild_id;
+
+ // storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`}
+ str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`");
+
+ for (j=0; j<MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p," FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id);
+
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) { //start to fetch
+ p->storage_[i].id= atoi(sql_row[0]);
+ p->storage_[i].nameid= atoi(sql_row[1]);
+ p->storage_[i].amount= atoi(sql_row[2]);
+ p->storage_[i].equip= atoi(sql_row[3]);
+ p->storage_[i].identify= atoi(sql_row[4]);
+ p->storage_[i].refine= atoi(sql_row[5]);
+ p->storage_[i].attribute= atoi(sql_row[6]);
+ for (j=0; j<MAX_SLOTS; j++)
+ p->storage_[i].card[j] = atoi(sql_row[7+j]);
+ p->storage_amount = ++i;
+ if (i >= MAX_GUILD_STORAGE)
+ break;
+ }
+ mysql_free_result(sql_res);
+ }
+ ShowInfo ("guild storage load complete from DB - id: %d (total: %d)\n", guild_id, p->storage_amount);
+ return 0;
+}
+
+//---------------------------------------------------------
+// storage data initialize
+int inter_storage_sql_init(){
+
+ //memory alloc
+ ShowDebug("interserver storage memory initialize....(%d byte)\n",sizeof(struct storage));
+ storage_pt = (struct storage*)aCalloc(sizeof(struct storage), 1);
+ guild_storage_pt = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1);
+// memset(storage_pt,0,sizeof(struct storage)); //Calloc sets stuff to 0 already. [Skotlex]
+// memset(guild_storage_pt,0,sizeof(struct guild_storage));
+
+ return 1;
+}
+// storage data finalize
+void inter_storage_sql_final()
+{
+ if (storage_pt) aFree(storage_pt);
+ if (guild_storage_pt) aFree(guild_storage_pt);
+ return;
+}
+// q?f[^?
+int inter_storage_delete(int account_id)
+{
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id`='%d'",storage_db, account_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+int inter_guild_storage_delete(int guild_id)
+{
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_storage_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+
+//---------------------------------------------------------
+// packet from map server
+
+// recive packet about storage data
+int mapif_load_storage(int fd,int account_id){
+ //load from DB
+ storage_fromsql(account_id, storage_pt);
+ WFIFOW(fd,0)=0x3810;
+ WFIFOW(fd,2)=sizeof(struct storage)+8;
+ WFIFOL(fd,4)=account_id;
+ memcpy(WFIFOP(fd,8),storage_pt,sizeof(struct storage));
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+// send ack to map server which is "storage data save ok."
+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)
+{
+ int guild_exist=1;
+ WFIFOW(fd,0)=0x3818;
+
+#if 0 // innodb guilds should render this check unnecessary [Aru]
+ // Check if guild exists, I may write a function for this later, coz I use it several times.
+ //printf("- Check if guild %d exists\n",g->guild_id);
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ sql_row = mysql_fetch_row(sql_res);
+ guild_exist = atoi (sql_row[0]);
+ //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes"));
+ }
+ mysql_free_result(sql_res) ; //resource free
+#endif
+ if(guild_exist==1) {
+ guild_storage_fromsql(guild_id,guild_storage_pt);
+ WFIFOW(fd,2)=sizeof(struct guild_storage)+12;
+ WFIFOL(fd,4)=account_id;
+ WFIFOL(fd,8)=guild_id;
+ memcpy(WFIFOP(fd,12),guild_storage_pt,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;
+}
+
+//---------------------------------------------------------
+// packet from map server
+
+// recive request about storage data
+int mapif_parse_LoadStorage(int fd){
+ mapif_load_storage(fd,RFIFOL(fd,2));
+ return 0;
+}
+// storage data recive and save
+int mapif_parse_SaveStorage(int fd){
+ int account_id=RFIFOL(fd,4);
+ int len=RFIFOW(fd,2);
+
+ if(sizeof(struct storage)!=len-8){
+ ShowError("inter storage: data size error %d %d\n",sizeof(struct storage),len-8);
+ }else{
+ memcpy(&storage_pt[0],RFIFOP(fd,8),sizeof(struct storage));
+ storage_tosql(account_id, storage_pt);
+ 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)
+{
+ int guild_exist=1;
+ int guild_id=RFIFOL(fd,8);
+ int len=RFIFOW(fd,2);
+ if(sizeof(struct guild_storage)!=len-12){
+ ShowError("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12);
+ }
+ else {
+#if 0 // Again, innodb key checks make the check pointless
+ // Check if guild exists, I may write a function for this later, coz I use it several times.
+ //printf("- Check if guild %d exists\n",g->guild_id);
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0) {
+ sql_row = mysql_fetch_row(sql_res);
+ guild_exist = atoi (sql_row[0]);
+ //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes"));
+ }
+ mysql_free_result(sql_res) ; //resource free
+#endif
+ if(guild_exist==1) {
+ memcpy(guild_storage_pt,RFIFOP(fd,12),sizeof(struct guild_storage));
+ guild_storage_tosql(guild_id,guild_storage_pt);
+ 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;
+}
+
+
+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_sql/int_storage.h b/src/char_sql/int_storage.h
new file mode 100644
index 000000000..f3b56a6d1
--- /dev/null
+++ b/src/char_sql/int_storage.h
@@ -0,0 +1,17 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_STORAGE_H_
+#define _INT_STORAGE_H_
+
+int inter_storage_sql_init();
+void inter_storage_sql_final();
+int inter_storage_delete(int account_id);
+int inter_guild_storage_delete(int guild_id);
+
+int inter_storage_parse_frommap(int fd);
+
+
+//extern char storage_txt[256];
+
+#endif
diff --git a/src/char_sql/inter.c b/src/char_sql/inter.c
new file mode 100644
index 000000000..0c6145bf9
--- /dev/null
+++ b/src/char_sql/inter.c
@@ -0,0 +1,789 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// original code from athena
+// SQL conversion by Jioh L. Jung
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "char.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "inter.h"
+#include "int_party.h"
+#include "int_guild.h"
+#include "int_storage.h"
+#include "int_pet.h"
+
+#define WISDATA_TTL (60*1000) // Wisƒf[ƒ^‚̶‘¶ŽžŠÔ(60•b)
+#define WISDELLIST_MAX 256 // Wisƒf[ƒ^휃ŠƒXƒg‚Ì—v‘f”
+
+
+struct accreg {
+ int account_id, char_id;
+ int reg_num;
+ struct global_reg reg[MAX_REG_NUM];
+};
+
+static struct accreg *accreg_pt;
+
+
+int party_share_level = 10;
+int kick_on_disconnect = 1;
+MYSQL mysql_handle;
+MYSQL_RES* sql_res ;
+MYSQL_ROW sql_row ;
+int sql_fields, sql_cnt;
+char tmp_sql[65535];
+
+MYSQL lmysql_handle;
+MYSQL_RES* lsql_res ;
+MYSQL_ROW lsql_row ;
+
+int char_server_port = 3306;
+char char_server_ip[32] = "127.0.0.1";
+char char_server_id[32] = "ragnarok";
+char char_server_pw[32] = "ragnarok";
+char char_server_db[32] = "ragnarok";
+char default_codepage[32] = ""; //Feature by irmin.
+
+int login_server_port = 3306;
+char login_server_ip[32] = "127.0.0.1";
+char login_server_id[32] = "ragnarok";
+char login_server_pw[32] = "ragnarok";
+char login_server_db[32] = "ragnarok";
+
+char main_chat_nick[16] = "Main";
+
+// sending packet list
+// NOTE: This variable ain't used at all! And it's confusing.. where do I add that the length of packet 0x2b07 is 10? x.x [Skotlex]
+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,
+};
+// recv. packet list
+int inter_recv_packet_length[]={
+ -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f
+ 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f
+ 64, 6,42,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f
+ -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f
+ 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, //0x3080-0x308f
+};
+
+struct WisData {
+ int id, fd, count,len;
+ unsigned long tick;
+ unsigned char src[24], dst[24], msg[512];
+};
+static struct dbt * wis_db = NULL;
+static int wis_dellist[WISDELLIST_MAX], wis_delnum;
+
+int inter_sql_test (void);
+
+//--------------------------------------------------------
+// Save registry to sql
+int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type){
+
+ int j;
+ char temp_str[64]; //Needs be twice the source to ensure it fits [Skotlex]
+ char temp_str2[512];
+ if (account_id<=0) return 0;
+ reg->account_id=account_id;
+ reg->char_id = char_id;
+
+ switch (type) {
+ case 3: //Char Reg
+ //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id);
+ break;
+ case 2: //Account Reg
+ //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, account_id);
+ break;
+ case 1: //Account2 Reg
+ ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
+ return 0;
+ default:
+ ShowError("inter_accreg_tosql: Invalid type %d\n", type);
+ return 0;
+
+ }
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if (reg->reg_num<=0) return 0;
+
+ for(j=0;j<reg->reg_num;j++){
+ if(reg->reg[j].str != NULL){
+ sprintf(tmp_sql,"INSERT INTO `%s` (`type`, `account_id`, `char_id`, `str`, `value`) VALUES ('%d','%d','%d','%s','%s')",
+ reg_db, type, type!=3?reg->account_id:0, type==3?reg->char_id:0,
+ jstrescapecpy(temp_str,reg->reg[j].str), jstrescapecpy(temp_str2,reg->reg[j].value));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ return 1;
+}
+
+// Load account_reg from sql (type=2)
+int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int type)
+{
+ int j=0;
+ if (reg==NULL) return 0;
+ memset(reg, 0, sizeof(struct accreg));
+ reg->account_id=account_id;
+ reg->char_id=char_id;
+
+ //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
+ switch (type) {
+ case 3: //char reg
+ sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, reg->char_id);
+ break;
+ case 2: //account reg
+ sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, reg->account_id);
+ break;
+ case 1: //account2 reg
+ ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
+ return 0;
+ }
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+
+ if (sql_res) {
+ for(j=0;(sql_row = mysql_fetch_row(sql_res));j++){
+ strcpy(reg->reg[j].str, sql_row[0]);
+ strcpy(reg->reg[j].value, sql_row[1]);
+ }
+ mysql_free_result(sql_res);
+ }
+ reg->reg_num=j;
+ return 1;
+}
+
+// Initialize
+int inter_accreg_sql_init()
+{
+ CREATE(accreg_pt, struct accreg, 1);
+ return 0;
+
+}
+
+/*==========================================
+ * read config file
+ *------------------------------------------
+ */
+int inter_config_read(const char *cfgName) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ ShowInfo("reading file %s...\n",cfgName);
+
+ while(fgets(line, 1020, fp)){
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+
+ if(strcmpi(w1,"char_server_ip")==0){
+ strcpy(char_server_ip, w2);
+ ShowStatus ("set char_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"char_server_port")==0){
+ char_server_port=atoi(w2);
+ ShowStatus ("set char_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"char_server_id")==0){
+ strcpy(char_server_id, w2);
+ ShowStatus ("set char_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"char_server_pw")==0){
+ strcpy(char_server_pw, w2);
+ ShowStatus ("set char_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"char_server_db")==0){
+ strcpy(char_server_db, w2);
+ ShowStatus ("set char_server_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"default_codepage")==0){
+ strcpy(default_codepage, w2);
+ ShowStatus ("set default_codepage : %s\n",w2);
+ }
+ //Logins information to be read from the inter_athena.conf
+ //for character deletion (checks email in the loginDB)
+
+ else if(strcmpi(w1,"login_server_ip")==0){
+ strcpy(login_server_ip, w2);
+ ShowStatus ("set login_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_port")==0){
+ login_server_port=atoi(w2);
+ ShowStatus ("set login_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_id")==0){
+ strcpy(login_server_id, w2);
+ ShowStatus ("set login_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_pw")==0){
+ strcpy(login_server_pw, w2);
+ ShowStatus ("set login_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_db")==0){
+ strcpy(login_server_db, w2);
+ ShowStatus ("set login_server_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"kick_on_disconnect")==0){
+ kick_on_disconnect=atoi(w2);
+ }
+ else if(strcmpi(w1,"party_share_level")==0){
+ party_share_level=atoi(w2);
+ if(party_share_level < 0) party_share_level = 0;
+ }
+ else if(strcmpi(w1,"log_inter")==0){
+ log_inter = atoi(w2);
+ }
+ else if(strcmpi(w1,"login_server_db")==0){
+ strcpy(login_server_db, w2);
+ ShowStatus ("set login_server_db : %s\n",w2);
+ }
+ else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza]
+ strcpy(main_chat_nick, w2); //
+ }
+ else if(strcmpi(w1,"import")==0){
+ inter_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ ShowInfo ("done reading %s.\n", cfgName);
+
+ return 0;
+}
+
+// Save interlog into sql
+int inter_log(char *fmt,...)
+{
+ char str[255];
+ char temp_str[510]; //Needs be twice as long as str[] //Skotlex
+ va_list ap;
+ va_start(ap,fmt);
+
+ vsprintf(str,fmt,ap);
+ sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `log`) VALUES (NOW(), '%s')",interlog_db, jstrescapecpy(temp_str,str));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ va_end(ap);
+ return 0;
+}
+
+
+// initialize
+int inter_init(const char *file)
+{
+ //int i;
+
+ ShowInfo ("interserver initialize...\n");
+ inter_config_read(file);
+
+ //DB connection initialized
+ mysql_init(&mysql_handle);
+ ShowInfo("Connect Character DB server.... (Character Server)\n");
+ if(!mysql_real_connect(&mysql_handle, char_server_ip, char_server_id, char_server_pw,
+ char_server_db ,char_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowFatalError("%s\n",mysql_error(&mysql_handle));
+ exit(1);
+ }
+ else if (inter_sql_test()) {
+ ShowStatus("Connect Success! (Character Server)\n");
+ }
+
+ mysql_init(&lmysql_handle);
+ ShowInfo("Connect Character DB server.... (login server)\n");
+ if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw,
+ login_server_db ,login_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowFatalError("%s\n",mysql_error(&lmysql_handle));
+ exit(1);
+ }else {
+ ShowStatus ("Connect Success! (Login Server)\n");
+ }
+ if(strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ if (mysql_query(&lmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ inter_guild_sql_init();
+ inter_storage_sql_init();
+ inter_party_sql_init();
+
+ inter_pet_sql_init();
+ inter_accreg_sql_init();
+
+ //printf ("interserver timer initializing : %d sec...\n",autosave_interval);
+ //i=add_timer_interval(gettick()+autosave_interval,inter_save_timer,0,0,autosave_interval);
+
+ return 0;
+}
+
+int inter_sql_test (void)
+{
+ const char fields[][24] = {
+ "father", // version 1363
+ "fame", // version 1491
+ };
+ char buf[1024] = "";
+ int i;
+
+ sprintf(tmp_sql, "EXPLAIN `%s`",char_db);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ // store DB fields
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))) {
+ strcat (buf, sql_row[0]);
+ strcat (buf, " ");
+ }
+ }
+
+ // check DB strings
+ for (i = 0; i < (int)(sizeof(fields) / sizeof(fields[0])); i++) {
+ if(!strstr(buf, fields[i])) {
+ ShowSQL ("Field `%s` not be found in `%s`. Consider updating your database!\n", fields[i], char_db);
+ exit(1);
+ }
+ }
+
+ mysql_free_result(sql_res);
+
+ return 1;
+}
+
+// finalize
+void inter_final() {
+ wis_db->destroy(wis_db, NULL);
+
+ inter_guild_sql_final();
+ inter_storage_sql_final();
+ inter_party_sql_final();
+ inter_pet_sql_final();
+
+ if (accreg_pt) aFree(accreg_pt);
+ return;
+}
+
+int inter_mapif_init(int fd) {
+ inter_guild_mapif_init(fd);
+
+ return 0;
+}
+
+
+//--------------------------------------------------------
+
+// GM message sending
+int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) {
+ unsigned char buf[2048];
+
+ if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex]
+ WBUFW(buf, 0) = 0x3800;
+ WBUFW(buf, 2) = len;
+ WBUFL(buf, 4) = color;
+ memcpy(WBUFP(buf, 8), mes, len-8);
+ mapif_sendallwos(sfd, buf, len);
+ return 0;
+}
+
+// Wis sending
+int mapif_wis_message(struct WisData *wd) {
+ unsigned char buf[2048];
+ if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex]
+
+ WBUFW(buf, 0) = 0x3801;
+ WBUFW(buf, 2) = 56 +wd->len;
+ WBUFL(buf, 4) = wd->id;
+ memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH);
+ memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH);
+ memcpy(WBUFP(buf,56), wd->msg, wd->len);
+ wd->count = mapif_sendall(buf,WBUFW(buf,2));
+
+ return 0;
+}
+// Wis sending result
+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;
+ mapif_send(wd->fd,buf,27);
+// printf("inter server wis_end %d\n",flag);
+ return 0;
+}
+
+int mapif_account_reg(int fd,unsigned char *src)
+{
+// unsigned char buf[WBUFW(src,2)]; <- Hey, can this really be done? [Skotlex]
+ unsigned char *buf = aCalloc(1,WBUFW(src,2)); // [Lance] - Skot... Dynamic allocation is better :D
+ memcpy(WBUFP(buf,0),src,WBUFW(src,2));
+ WBUFW(buf, 0)=0x3804;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+ aFree(buf);
+ return 0;
+}
+
+// Send the requested account_reg
+int mapif_account_reg_reply(int fd,int account_id,int char_id, int type)
+{
+ struct accreg *reg=accreg_pt;
+ inter_accreg_fromsql(account_id,char_id,reg,type);
+
+ WFIFOW(fd,0)=0x3804;
+ WFIFOL(fd,4)=account_id;
+ WFIFOL(fd,8)=char_id;
+ WFIFOB(fd,12)=type;
+ if(reg->reg_num==0){
+ WFIFOW(fd,2)=13;
+ }else{
+ int i,p;
+ for (p=13,i = 0; i < reg->reg_num; i++) {
+ p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1;
+ }
+ WFIFOW(fd,2)=p;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+int mapif_send_gmaccounts()
+{
+ int i, len = 4;
+ unsigned char buf[32000];
+
+ // forward the gm accounts to the map server
+ len = 4;
+ WBUFW(buf,0) = 0x2b15;
+
+ for(i = 0; i < GM_num; i++) {
+ WBUFL(buf, len) = gm_account[i].account_id;
+ WBUFB(buf, len+4) = (unsigned char)gm_account[i].level;
+ len += 5;
+ }
+ WBUFW(buf, 2) = len;
+ mapif_sendall(buf, len);
+
+ return 0;
+}
+
+//Sends to map server the current max Account/Char id [Skotlex]
+void mapif_send_maxid(int account_id, int char_id)
+{
+ unsigned char buf[12];
+
+ WBUFW(buf,0) = 0x2b07;
+ WBUFL(buf,2) = account_id;
+ WBUFL(buf,6) = char_id;
+ mapif_sendall(buf, 10);
+}
+
+//Request to kick char from a certain map server. [Skotlex]
+int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason)
+{
+ if (fd < 0)
+ return -1;
+
+ WFIFOW(fd,0) = 0x2b1f;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = reason;
+ WFIFOSET(fd,7);
+ return 0;
+}
+
+//--------------------------------------------------------
+
+// Existence check of WISP data
+int check_ttl_wisdata_sub(DBKey key, void *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;
+
+ return 0;
+}
+
+int check_ttl_wisdata() {
+ unsigned long tick = gettick();
+ int i;
+
+ do {
+ wis_delnum = 0;
+ wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick);
+ for(i = 0; i < wis_delnum; i++) {
+ struct WisData *wd = idb_get(wis_db, wis_dellist[i]);
+ ShowWarning("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
+ idb_remove(wis_db, wd->id);
+ }
+ } while(wis_delnum >= WISDELLIST_MAX);
+
+ return 0;
+}
+
+//--------------------------------------------------------
+
+// GM message sending
+int mapif_parse_GMmessage(int fd)
+{
+ mapif_GMmessage(RFIFOP(fd, 8), RFIFOW(fd, 2), RFIFOL(fd, 4), fd);
+ return 0;
+}
+
+
+// Wisp/page request to send
+int mapif_parse_WisRequest(int fd) {
+ struct WisData* wd;
+ static int wisid = 0;
+ char name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Needs space to allocate names with escaped chars [Skotlex]
+
+ if ( fd <= 0 ) {return 0;} // check if we have a valid fd
+
+ if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) {
+ ShowWarning("inter: Wis message size too long.\n");
+ return 0;
+ } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows...
+ ShowError("inter: Wis message doesn't exist.\n");
+ return 0;
+ }
+ memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex]
+ name[NAME_LENGTH-1]= '\0';
+
+ sprintf (tmp_sql, "SELECT `name` FROM `%s` WHERE `name`='%s'",
+ char_db, jstrescapecpy(t_name, name));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+
+ // search if character exists before to ask all map-servers
+ if (!(sql_row = mysql_fetch_row(sql_res))) {
+ unsigned char buf[27];
+ WBUFW(buf, 0) = 0x3802;
+ memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
+ 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(name, 0, NAME_LENGTH);
+ strncpy(name, sql_row[0], NAME_LENGTH);
+ // if source is destination, don't ask other servers.
+ if (strcmp((char*)RFIFOP(fd,4),name) == 0) {
+ unsigned char buf[27];
+ WBUFW(buf, 0) = 0x3802;
+ memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
+ 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), NAME_LENGTH);
+ memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH);
+ memcpy(wd->msg, RFIFOP(fd,52), wd->len);
+ wd->tick = gettick();
+ idb_put(wis_db, wd->id, wd);
+ mapif_wis_message(wd);
+ }
+ }
+
+ //Freeing ... O.o
+ if(sql_res){
+ mysql_free_result(sql_res);
+ }
+
+ return 0;
+}
+
+
+// Wisp/page transmission result
+int mapif_parse_WisReply(int fd) {
+ int id = RFIFOL(fd,2), flag = RFIFOB(fd,6);
+ struct WisData *wd = idb_get(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
+ idb_remove(wis_db, id);
+ }
+
+ 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[2048]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B
+
+ ShowDebug("Sent packet back!\n");
+ memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2));
+ WBUFW(buf, 0) = 0x3803;
+ mapif_sendall(buf, RFIFOW(fd,2));
+
+ return 0;
+}
+
+// Save account_reg into sql (type=2)
+int mapif_parse_Registry(int fd)
+{
+ int j,p,len, max;
+ struct accreg *reg=accreg_pt;
+
+ memset(accreg_pt,0,sizeof(struct accreg));
+ switch (RFIFOB(fd, 12)) {
+ case 3: //Character registry
+ max = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account Registry
+ max = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 registry, must be sent over to login server.
+ return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4);
+ default:
+ return 1;
+ }
+ for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n",reg->reg[j].str,&len);
+ reg->reg[j].str[len]='\0';
+ p +=len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len);
+ reg->reg[j].value[len]='\0';
+ p +=len+1;
+ }
+ reg->reg_num=j;
+
+ inter_accreg_tosql(RFIFOL(fd,4),RFIFOL(fd,8),reg, RFIFOB(fd,12));
+ mapif_account_reg(fd,RFIFOP(fd,0)); // Send updated accounts to other map servers.
+ return 0;
+}
+
+// Request the value of all registries.
+int mapif_parse_RegistryRequest(int fd)
+{
+ RFIFOHEAD(fd);
+ //Load Char Registry
+ if (RFIFOB(fd,12))
+ mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),3);
+ //Load Account Registry
+ if (RFIFOB(fd,11))
+ mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2);
+ //Ask Login Server for Account2 values.
+ if (RFIFOB(fd,10))
+ request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2);
+ return 1;
+}
+
+//--------------------------------------------------------
+int inter_parse_frommap(int fd)
+{
+ int cmd=RFIFOW(fd,0);
+ int len=0;
+
+ // interŽIŠÇŠ‚©‚𒲂ׂé
+ if(cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length)/
+ sizeof(inter_recv_packet_length[0]) ) )
+ return 0;
+
+ if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex]
+ return 0;
+
+ // ƒpƒPƒbƒg’·‚𒲂ׂé
+ 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_Registry(fd); break;
+ case 0x3005: mapif_parse_RegistryRequest(fd); break;
+ default:
+ if(inter_party_parse_frommap(fd))
+ break;
+ if(inter_guild_parse_frommap(fd))
+ break;
+ if(inter_storage_parse_frommap(fd))
+ break;
+ if(inter_pet_parse_frommap(fd))
+ break;
+ return 0;
+ }
+
+ RFIFOSKIP(fd, len);
+ return 1;
+}
+
+// RFIFO check
+int inter_check_length(int fd, int length)
+{
+ if(length==-1){ // v-len packet
+ if(RFIFOREST(fd)<4) // packet not yet
+ return 0;
+ length = RFIFOW(fd, 2);
+ }
+
+ if((int)RFIFOREST(fd) < length) // packet not yet
+ return 0;
+
+ return length;
+}
diff --git a/src/char_sql/inter.h b/src/char_sql/inter.h
new file mode 100644
index 000000000..c1b40db01
--- /dev/null
+++ b/src/char_sql/inter.h
@@ -0,0 +1,58 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INTER_H_
+#define _INTER_H_
+
+int inter_init(const char *file);
+void inter_final();
+int inter_parse_frommap(int fd);
+int inter_mapif_init(int fd);
+int mapif_send_gmaccounts();
+void mapif_send_maxid(int, int);
+int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason);
+
+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 int kick_on_disconnect; //For deciding whether characters are kicked or not on reconnections. [Skotlex]
+extern char inter_log_filename[1024];
+
+#ifdef __WIN32
+//Windows.h need to be included before mysql.h
+#include <windows.h>
+#endif
+//add include for DBMS(mysql)
+#include <mysql.h>
+
+extern MYSQL mysql_handle;
+extern char tmp_sql[65535];
+extern MYSQL_RES* sql_res ;
+extern MYSQL_ROW sql_row ;
+extern int sql_cnt;
+
+extern MYSQL lmysql_handle;
+extern MYSQL_RES* lsql_res ;
+extern MYSQL_ROW lsql_row ;
+
+extern int char_server_port;
+extern char char_server_ip[32];
+extern char char_server_id[32];
+extern char char_server_pw[32];
+extern char char_server_db[32];
+
+extern int login_db_server_port;
+extern char login_db_server_ip[32];
+extern char login_db_server_id[32];
+extern char login_db_server_pw[32];
+extern char login_db_server_db[32];
+
+extern int log_inter;
+
+extern char main_chat_nick[16];
+
+#endif
diff --git a/src/char_sql/itemdb.c b/src/char_sql/itemdb.c
new file mode 100644
index 000000000..25c55e73d
--- /dev/null
+++ b/src/char_sql/itemdb.c
@@ -0,0 +1,229 @@
+// 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 "itemdb.h"
+#include "db.h"
+#include "inter.h"
+#include "char.h"
+#include "utils.h"
+#include "../common/showmsg.h"
+
+#define MAX_RANDITEM 2000
+
+// ** ITEMDB_OVERRIDE_NAME_VERBOSE **
+// ’è‹`‚·‚é‚ÆAitemdb.txt‚Ægrf‚Å–¼‘O‚ªˆÙ‚È‚éê‡A•\Ž¦‚µ‚Ü‚·.
+//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1
+
+char item_db_db[256]="item_db"; // added to specify item_db sql table [Valaris]
+
+static struct dbt* item_db;
+
+static void* create_item(DBKey key, va_list args) {
+ struct item_data *id;
+ int nameid = key.i;
+
+ CREATE(id, struct item_data, 1);
+ id->nameid = nameid;
+ if(nameid>500 && nameid<600)
+ id->type=0; //heal item
+ else if(nameid>600 && nameid<700)
+ id->type=2; //use item
+ else if((nameid>700 && nameid<1100) ||
+ (nameid>7000 && nameid<8000))
+ id->type=3; //correction
+ else if(nameid>=1750 && nameid<1771)
+ id->type=10; //arrow
+ else if(nameid>1100 && nameid<2000)
+ id->type=4; //weapon
+ else if((nameid>2100 && nameid<3000) ||
+ (nameid>5000 && nameid<6000))
+ id->type=5; //armor
+ else if(nameid>4000 && nameid<5000)
+ id->type=6; //card
+ else if(nameid>9000 && nameid<10000)
+ id->type=7; //egg
+ else if(nameid>10000)
+ id->type=8; //petequip
+ return id;
+}
+/*==========================================
+ * DB‚ÌŒŸõ
+ *------------------------------------------
+ */
+struct item_data* itemdb_search(int nameid)
+{
+ return idb_ensure(item_db,nameid,create_item);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+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;
+}
+
+
+
+/*==========================================
+ * ƒAƒCƒeƒ€ƒf[ƒ^ƒx[ƒX‚Ì“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static int itemdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,j;
+ char *str[128],*p,*np;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_db.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", str);
+ exit(1);
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,np=p=line;j<17 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p){ *p++=0; np=p; }
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>=20000)
+ continue;
+ 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],ITEM_NAME_LENGTH-1);
+ memcpy(id->jname,str[2],ITEM_NAME_LENGTH-1);
+ id->type=atoi(str[3]);
+
+ }
+ fclose(fp);
+ ShowStatus("done reading item_db.txt (count=%d)\n",ln);
+ return 0;
+}
+
+static int itemdb_read_sqldb(void) // sql item_db read, shortened version of map-server item_db read [Valaris]
+{
+ unsigned int nameid; // Type should be "unsigned short int", but currently isn't for compatibility with numdb_insert()
+ struct item_data *id;
+
+ // ----------
+
+ // Output query to retrieve all rows from the item database table
+ sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_db);
+
+ // Execute the query; if the query execution fails, output an error
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ // Store the query result
+ sql_res = mysql_store_result(&mysql_handle);
+
+ // If the storage of the query result succeeded
+ if (sql_res) {
+ // Parse each row in the query result into sql_row
+ while ((sql_row = mysql_fetch_row(sql_res))) {
+ nameid = atoi(sql_row[0]);
+
+ // If the identifier is not within the valid range, process the next row
+ if (nameid == 0 || nameid >= 20000) { // Should ">= 20000" be "> 20000"?
+ continue;
+ }
+
+ // ----------
+
+ // Update/Insert row into the item database
+ id=itemdb_search(nameid);
+
+ memcpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1);
+ memcpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1);
+
+ id->type = atoi(sql_row[3]);
+ }
+
+ // If the retrieval failed, output an error
+ if (mysql_errno(&mysql_handle)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ ShowInfo("read %s done (count = %lu)\n", item_db_db, (unsigned long) mysql_num_rows(sql_res));
+
+ // Free the query result
+ mysql_free_result(sql_res);
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+static int itemdb_final(DBKey key,void *data,va_list ap)
+{
+ struct item_data *id = (struct item_data*)data;
+ if(id->use_script)
+ aFree(id->use_script);
+ if(id->equip_script)
+ aFree(id->equip_script);
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void do_final_itemdb(void)
+{
+ if(item_db){
+ item_db->destroy(item_db,itemdb_final);
+ item_db=NULL;
+ }
+}
+int do_init_itemdb(void)
+{
+ item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ if (db_use_sqldbs) // it db_use_sqldbs in inter config are yes, will read from item_db for char server display [Valaris]
+ itemdb_read_sqldb();
+ else
+ itemdb_readdb();
+ return 0;
+}
diff --git a/src/char_sql/itemdb.h b/src/char_sql/itemdb.h
new file mode 100644
index 000000000..350e42b2c
--- /dev/null
+++ b/src/char_sql/itemdb.h
@@ -0,0 +1,38 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ITEMDB_H_
+#define _ITEMDB_H_
+#include "mmo.h"
+
+struct item_data {
+ int nameid;
+ char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
+ int value_buy,value_sell,value_notdc,value_notoc;
+ int type;
+ int class_;
+ int sex;
+ int equip;
+ int weight;
+ int atk;
+ int def;
+ int range;
+ int slot;
+ int look;
+ int elv;
+ int wlv;
+ char *use_script; // ‰ñ•œ‚Æ‚©‚à‘S•”‚±‚Ì’†‚Å‚â‚낤‚©‚È‚Æ
+ char *equip_script; // UŒ‚,–hŒä‚Ì‘®«Ý’è‚à‚±‚Ì’†‚ʼn”\‚©‚È?
+ char available;
+};
+
+struct item_data* itemdb_search(int nameid);
+#define itemdb_type(n) itemdb_search(n)->type
+
+int itemdb_isequip(int);
+int itemdb_isequip2(struct item_data *);
+
+void do_final_itemdb(void);
+int do_init_itemdb(void);
+
+#endif
diff --git a/src/char_sql/make.sh b/src/char_sql/make.sh
new file mode 100644
index 000000000..d5f109f3e
--- /dev/null
+++ b/src/char_sql/make.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+ rsqlt=`rm -rf *.o`
+ gcc -c char.c -I/usr/local/include/mysql/
+ gcc -c int_guild.c -I/usr/local/include/mysql/
+ gcc -c int_party.c -I/usr/local/include/mysql/
+ gcc -c int_pet.c -I/usr/local/include/mysql/
+ gcc -c int_storage.c -I/usr/local/include/mysql/
+ gcc -c inter.c -I/usr/local/include/mysql/
+ gcc -c itemdb.c -I../common/
+ gcc -o ../char-server inter.o char.o int_pet.o int_storage.o int_guild.o int_party.o ../common/strlib.o itemdb.o ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o -L/usr/local/lib/mysql -lmysqlclient -lz
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 000000000..60b588b1e
--- /dev/null
+++ b/src/common/Makefile
@@ -0,0 +1,56 @@
+txt sql all: obj common
+
+obj:
+ mkdir obj
+
+common: obj/core.o obj/socket.o obj/timer.o obj/db.o obj/plugins.o obj/lock.o \
+ obj/nullpo.o obj/malloc.o obj/showmsg.o obj/strlib.o obj/utils.o \
+ obj/graph.o obj/grfio.o obj/minicore.o obj/minisocket.o obj/minimalloc.o \
+ obj/mapindex.o obj/unz.o obj/ers.o
+
+
+obj/%.o: %.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+obj/mini%.o: %.c
+ $(COMPILE.c) -DMINICORE $(OUTPUT_OPTION) $<
+
+obj/unz.o:
+ $(MAKE) -C ../zlib
+ @touch $@
+
+
+clean:
+ rm -rf *.o obj
+
+HAVESVN = $(shell which svnversion)
+
+ifeq ($(findstring /,$(HAVESVN)), /)
+svnversion.h: ../../Changelog-SVN.txt
+ @printf "#define SVNVERSION " > svnversion.h
+ @svnversion . >> svnversion.h
+else
+svnversion.h:
+ @printf "" > svnversion.h
+endif
+
+obj/minicore.o: core.c core.h
+obj/minisocket.o: socket.c socket.h
+obj/minimalloc.o: malloc.c malloc.h
+
+# DO NOT DELETE
+
+obj/core.o: core.c core.h showmsg.h svnversion.h
+obj/socket.o: socket.c socket.h mmo.h showmsg.h plugins.h
+obj/timer.o: timer.c timer.h showmsg.h
+obj/ers.o: ers.c ers.h cbasetypes.h
+obj/db.o: db.c db.h showmsg.h ers.h
+obj/lock.o: lock.c lock.h showmsg.h
+obj/grfio.o: grfio.c grfio.h
+obj/graph.o: graph.c graph.h
+obj/nullpo.o: nullpo.c nullpo.h showmsg.h
+obj/malloc.o: malloc.c malloc.h showmsg.h
+obj/plugins.o: plugins.c plugins.h plugin.h
+obj/showmsg.o: showmsg.c showmsg.h
+obj/strlib.o: strlib.c strlib.h utils.h
+obj/mapindex.o: mapindex.c mapindex.h
diff --git a/src/common/cbasetypes.h b/src/common/cbasetypes.h
new file mode 100644
index 000000000..ec539a3db
--- /dev/null
+++ b/src/common/cbasetypes.h
@@ -0,0 +1,253 @@
+#ifndef _CBASETYPES_H_
+#define _CBASETYPES_H_
+/* +--------+-----------+--------+---------+
+ * | ILP32 | LP64 | ILP64 | (LL)P64 |
+ * +------------+--------+-----------+--------+---------+
+ * | ints | 32-bit | 32-bit | 64-bit | 32-bit |
+ * | longs | 32-bit | 64-bit | 64-bit | 32-bit |
+ * | long-longs | 64-bit | 64-bit | 64-bit | 64-bit |
+ * | pointers | 32-bit | 64-bit | 64-bit | 64-bit |
+ * +------------+--------+-----------+--------+---------+
+ * | where | -- | Tiger | Alpha | Windows |
+ * | used | | Unix | Cray | |
+ * | | | Sun & SGI | | |
+ * +------------+--------+-----------+--------+---------+
+ * Taken from http://developer.apple.com/macosx/64bit.html
+ */
+
+//////////////////////////////////////////////////////////////////////////
+// basic include for all basics
+// introduces types and global functions
+//////////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////
+// setting some defines on platforms
+//////////////////////////////////////////////////////////////////////////
+#if (defined(__WIN32__) || defined(__WIN32) || defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER) || defined(__BORLANDC__)) && !defined(WIN32)
+#define WIN32
+#endif
+
+// __APPLE__ is the only predefined macro on MacOS X
+#if defined(__APPLE__)
+#define __DARWIN__
+#endif
+
+// 64bit OS
+#if defined(_M_IA64) || defined(_M_X64) || defined(_WIN64) || defined(_LP64) || defined(_ILP64) || defined(__LP64__) || defined(__ppc64__)
+#define __64BIT__
+#endif
+
+#if defined(_ILP64)
+#error "this specific 64bit architecture is not supported"
+#endif
+
+// debug mode
+#if defined(_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+// disable attributed stuff on non-GNU
+#ifndef __GNUC__
+# define __attribute__(x)
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////
+// useful typedefs
+//////////////////////////////////////////////////////////////////////////
+typedef unsigned char uchar;
+typedef signed char schar;
+typedef signed short sshort;
+typedef unsigned short ushort;
+typedef signed int sint; // don't use (only for ie. scanf)
+typedef unsigned int uint; // don't use
+typedef signed long slong; // don't use (only for ie. file-io)
+typedef unsigned long ulong; // don't use
+
+typedef char* pchar;
+typedef const char* cchar;
+typedef unsigned char* puchar;
+typedef void* ptr;
+typedef int* pint;
+
+
+//////////////////////////////////////////////////////////////////////////
+// typedefs to compensate type size change from 32bit to 64bit
+// MS implements LLP64 model, normal unix does LP64,
+// only Silicon Graphics/Cray goes ILP64 so don't care (and don't support)
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+// Integers with guaranteed _exact_ size.
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////
+#ifdef WIN32
+//////////////////////////////
+typedef __int8 int8;
+typedef __int16 int16;
+typedef __int32 int32;
+
+typedef signed __int8 sint8;
+typedef signed __int16 sint16;
+typedef signed __int32 sint32;
+
+typedef unsigned __int8 uint8;
+typedef unsigned __int16 uint16;
+typedef unsigned __int32 uint32;
+//////////////////////////////
+#else // GNU
+//////////////////////////////
+typedef char int8;
+typedef short int16;
+typedef int int32;
+
+typedef signed char sint8;
+typedef signed short sint16;
+typedef signed int sint32;
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+//////////////////////////////
+#endif
+//////////////////////////////
+
+#undef UINT8_MIN
+#undef UINT16_MIN
+#undef UINT32_MIN
+#define UINT8_MIN (uint8) 0
+#define UINT16_MIN (uint16)0
+#define UINT32_MIN (uint32)0
+
+#undef UINT8_MAX
+#undef UINT16_MAX
+#undef UINT32_MAX
+#define UINT8_MAX (uint8) 0xFF
+#define UINT16_MAX (uint16)0xFFFF
+#define UINT32_MAX (uint32)0xFFFFFFFF
+
+#undef SINT8_MIN
+#undef SINT16_MIN
+#undef SINT32_MIN
+#define SINT8_MIN (sint8) 0x80
+#define SINT16_MIN (sint16)0x8000
+#define SINT32_MIN (sint32)0x80000000
+
+#undef SINT8_MAX
+#undef SINT16_MAX
+#undef SINT32_MAX
+#define SINT8_MAX (sint8) 0x7F
+#define SINT16_MAX (sint16)0x7FFF
+#define SINT32_MAX (sint32)0x7FFFFFFF
+
+
+//////////////////////////////////////////////////////////////////////////
+// Integers with guaranteed _minimum_ size.
+// These could be larger than you expect,
+// they are designed for speed.
+//////////////////////////////////////////////////////////////////////////
+typedef long int ppint;
+typedef long int ppint8;
+typedef long int ppint16;
+typedef long int ppint32;
+
+typedef unsigned long int ppuint;
+typedef unsigned long int ppuint8;
+typedef unsigned long int ppuint16;
+typedef unsigned long int ppuint32;
+
+
+//////////////////////////////////////////////////////////////////////////
+// integer with exact processor width (and best speed)
+// size_t already defined in stdio.h
+//////////////////////////////
+#ifdef WIN32 // does not have a signed size_t
+//////////////////////////////
+#if defined(_WIN64) // naive 64bit windows platform
+typedef __int64 ssize_t;
+#else
+typedef int ssize_t;
+#endif
+//////////////////////////////
+#endif
+//////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////
+// portable 64-bit integers
+//////////////////////////////////////////////////////////////////////////
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 int64;
+typedef signed __int64 sint64;
+typedef unsigned __int64 uint64;
+#define LLCONST(a) (a##i64)
+#else
+typedef long long int64;
+typedef signed long long sint64;
+typedef unsigned long long uint64;
+#define LLCONST(a) (a##ll)
+#endif
+
+#ifndef INT64_MIN
+#define INT64_MIN (LLCONST(-9223372036854775807)-1)
+#endif
+#ifndef INT64_MAX
+#define INT64_MAX (LLCONST(9223372036854775807))
+#endif
+#ifndef UINT64_MAX
+#define UINT64_MAX (LLCONST(18446744073709551615u))
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////
+// some redefine of function redefines for some Compilers
+//////////////////////////////////////////////////////////////////////////
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+// keyword replacement in windows
+#ifdef _WIN32
+#define inline __inline
+#endif
+
+/////////////////////////////
+// for those still not building c++
+#ifndef __cplusplus
+//////////////////////////////
+
+// boolean types for C
+typedef int bool;
+#define false (1==0)
+#define true (1==1)
+
+//////////////////////////////
+#endif // not cplusplus
+//////////////////////////////
+
+#ifdef swap // just to be sure
+#undef swap
+#endif
+// hmm only ints?
+//#define swap(a,b) { int temp=a; a=b; b=temp;}
+// if using macros then something that is type independent
+#define swap(a,b) ((a == b) || ((a ^= b), (b ^= a), (a ^= b)))
+
+//////////////////////////////////////////////////////////////////////////
+// should not happen
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// number of bits in a byte
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+#endif /* _CBASETYPES_H_ */
diff --git a/src/common/core.c b/src/common/core.c
new file mode 100644
index 000000000..b0b847ca2
--- /dev/null
+++ b/src/common/core.c
@@ -0,0 +1,269 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <string.h>
+
+#include "core.h"
+#include "../common/db.h"
+#include "../common/mmo.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/graph.h"
+#include "../common/grfio.h"
+#include "../common/plugins.h"
+#include "../common/version.h"
+#include "../common/showmsg.h"
+
+#ifndef _WIN32
+ #include "svnversion.h"
+#endif
+
+int runflag = 1;
+int arg_c = 0;
+char **arg_v = NULL;
+
+char *SERVER_NAME = NULL;
+char SERVER_TYPE = ATHENA_SERVER_NONE;
+static void (*term_func)(void) = NULL;
+
+/*======================================
+ * CORE : Set function
+ *--------------------------------------
+ */
+void set_termfunc(void (*termfunc)(void))
+{
+ term_func = termfunc;
+}
+
+#ifndef MINICORE // minimalist Core
+// 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_.
+//
+#ifdef WIN32 // windows don't have SIGPIPE
+#define SIGPIPE SIGINT
+#endif
+
+#ifndef POSIX
+#define compat_signal(signo, func) signal(signo, func)
+#else
+sigfunc *compat_signal(int signo, sigfunc *func)
+{
+ struct sigaction sact, oact;
+
+ sact.sa_handler = func;
+ sigemptyset(&sact.sa_mask);
+ sact.sa_flags = 0;
+#ifdef SA_INTERRUPT
+ sact.sa_flags |= SA_INTERRUPT; /* SunOS */
+#endif
+
+ if (sigaction(signo, &sact, &oact) < 0)
+ return (SIG_ERR);
+
+ return (oact.sa_handler);
+}
+#endif
+
+/*======================================
+ * CORE : Signal Sub Function
+ *--------------------------------------
+ */
+static void sig_proc(int sn)
+{
+ static int is_called = 0;
+
+ switch (sn) {
+ case SIGINT:
+ case SIGTERM:
+ if (++is_called > 3)
+ exit(0);
+ runflag = 0;
+ break;
+#ifndef _WIN32
+ case SIGXFSZ:
+ // ignore and allow it to set errno to EFBIG
+ ShowWarning ("Max file size reached!\n");
+ //run_flag = 0; // should we quit?
+ break;
+ case SIGPIPE:
+ ShowMessage ("Broken pipe found... closing socket\n"); // set to eof in socket.c
+ break; // does nothing here
+#endif
+ }
+}
+
+void signals_init (void)
+{
+ compat_signal(SIGTERM, sig_proc);
+ compat_signal(SIGINT, sig_proc);
+
+ // Signal to create coredumps by system when necessary (crash)
+ compat_signal(SIGSEGV, SIG_DFL);
+ compat_signal(SIGFPE, SIG_DFL);
+ compat_signal(SIGILL, SIG_DFL);
+ #ifndef _WIN32
+ compat_signal(SIGXFSZ, sig_proc);
+ compat_signal(SIGPIPE, sig_proc);
+ compat_signal(SIGBUS, SIG_DFL);
+ compat_signal(SIGTRAP, SIG_DFL);
+ #endif
+}
+#endif
+
+#ifdef SVNVERSION
+ #define xstringify(x) stringify(x)
+ #define stringify(x) #x
+ const char *get_svn_revision(void)
+ {
+ return xstringify(SVNVERSION);
+ }
+#else
+const char* get_svn_revision(void)
+{
+ static char version[10];
+ FILE *fp;
+
+ if ((fp = fopen(".svn/entries", "r")) != NULL) {
+ char line[1024];
+ int rev;
+ while (fgets(line,1023,fp))
+ if (strstr(line,"revision=")) break;
+ fclose(fp);
+ if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) {
+ sprintf(version, "%d", rev);
+ return version;
+ }
+ }
+
+ // if getting revision has failed
+ return "Unknown";
+}
+#endif
+
+/*======================================
+ * CORE : Display title
+ *--------------------------------------
+ */
+
+static void display_title(void)
+{
+ ClearScreen(); // clear screen and go up/left (0, 0 position in text)
+ ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); // white writing (37) on blue background (44), \033[K clean until end of file
+ ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" (c)2005 eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d "CL_XXBL")"CL_CLL""CL_NORMAL"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char
+ ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" Advanced Fusion Maps (c) 2003-2005 The Fusion Project "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33)
+ ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); // reset color
+
+ ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision());
+}
+
+// Warning if logged in as superuser (root)
+void usercheck(void){
+#ifndef _WIN32
+ if ((getuid() == 0) && (getgid() == 0)) {
+ ShowWarning ("You are running eAthena as the root superuser.\n");
+ ShowWarning ("It is unnecessary and unsafe to run eAthena with root privileges.\n");
+ sleep(3);
+ }
+#endif
+}
+
+/*======================================
+ * CORE : MAINROUTINE
+ *--------------------------------------
+ */
+#ifndef MINICORE // minimalist Core
+int main (int argc, char **argv)
+{
+ int next;
+
+ // initialise program arguments
+ {
+ char *p = SERVER_NAME = argv[0];
+ while ((p = strchr(p, '/')) != NULL)
+ SERVER_NAME = ++p;
+ arg_c = argc;
+ arg_v = argv;
+ }
+
+ set_server_type();
+ display_title();
+ usercheck();
+
+ malloc_init(); /* ˆê”Ôʼn‚ÉŽÀs‚·‚é•K—v‚ª‚ ‚é */
+ db_init();
+ signals_init();
+
+ timer_init();
+ socket_init();
+ plugins_init();
+
+ do_init(argc,argv);
+ graph_init();
+ plugin_event_trigger("Athena_Init");
+
+ while (runflag) {
+ next = do_timer(gettick_nocache());
+ do_sendrecv(next);
+#ifndef TURBO
+ do_parsepacket();
+#endif
+ }
+
+ plugin_event_trigger("Athena_Final");
+ graph_final();
+ do_final();
+
+ timer_final();
+ plugins_final();
+ socket_final();
+ db_final();
+ malloc_final();
+
+ return 0;
+}
+#else
+int main (int argc, char **argv)
+{
+ // initialise program arguments
+ {
+ char *p = SERVER_NAME = argv[0];
+ while ((p = strchr(p, '/')) != NULL)
+ SERVER_NAME = ++p;
+ arg_c = argc;
+ arg_v = argv;
+ }
+
+ display_title();
+ usercheck();
+ do_init(argc,argv);
+ do_final();
+
+ return 0;
+}
+#endif
+
+#ifdef BCHECK
+unsigned int __invalid_size_argument_for_IOC;
+#endif
diff --git a/src/common/core.h b/src/common/core.h
new file mode 100644
index 000000000..22f625a5d
--- /dev/null
+++ b/src/common/core.h
@@ -0,0 +1,30 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+//#define SQL_DEBUG //uncomment for debug_mysql_query instead of mysql_real_query
+
+/* REMOVED because these type of function defines with va_args are a GCC feature and won't compile under Windows [Skotlex]
+//Added here, so its avail in 'all' files ..
+#define eprintf(mes, args...) \
+ fprintf(stderr, "%s:%d: "mes"", __FILE__, __LINE__, args);
+#define eprint(mes) \
+ fprintf(stderr, "%s:%d: "mes"", __FILE__, __LINE__);
+*/
+
+extern int arg_c;
+extern char **arg_v;
+
+extern int runflag;
+extern char *SERVER_NAME;
+extern char SERVER_TYPE;
+
+extern const char *get_svn_revision(void);
+extern int do_init(int,char**);
+extern void set_server_type(void);
+extern void set_termfunc(void (*termfunc)(void));
+extern void do_final(void);
+
+#endif // _CORE_H_
diff --git a/src/common/db.c b/src/common/db.c
new file mode 100644
index 000000000..65abecea7
--- /dev/null
+++ b/src/common/db.c
@@ -0,0 +1,2344 @@
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * This file is separated in five sections: *
+ * (1) Private typedefs, enums, structures, defines and gblobal variables *
+ * (2) Private functions *
+ * (3) Protected functions used internally *
+ * (4) Protected functions used in the interface of the database *
+ * (5) Public functions *
+ * *
+ * The databases are structured as a hashtable of RED-BLACK trees. *
+ * *
+ * <B>Properties of the RED-BLACK trees being used:</B> *
+ * 1. The value of any node is greater than the value of its left child and *
+ * less than the value of its right child. *
+ * 2. Every node is colored either RED or BLACK. *
+ * 3. Every red node that is not a leaf has only black children. *
+ * 4. Every path from the root to a leaf contains the same number of black *
+ * nodes. *
+ * 5. The root node is black. *
+ * An <code>n</code> node in a RED-BLACK tree has the property that its *
+ * height is <code>O(lg(n))</code>. *
+ * Another important property is that after adding a node to a RED-BLACK *
+ * tree, the tree can be readjusted in <code>O(lg(n))</code> time. *
+ * Similarly, after deleting a node from a RED-BLACK tree, the tree can be *
+ * readjusted in <code>O(lg(n))</code> time. *
+ * {@link http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic18/} *
+ * *
+ * <B>How to add new database types:</B> *
+ * 1. Add the identifier of the new database type to the enum DBType *
+ * 2. If not already there, add the data type of the key to the union DBKey *
+ * 3. If the key can be considered NULL, update the function db_is_key_null *
+ * 4. If the key can be duplicated, update the functions db_dup_key and *
+ * db_dup_key_free *
+ * 5. Create a comparator and update the function db_default_cmp *
+ * 6. Create a hasher and update the function db_default_hash *
+ * 7. If the new database type requires or does not support some options, *
+ * update the function db_fix_options *
+ * *
+ * TODO: *
+ * - create test cases to test the database system thoroughly *
+ * - make data an enumeration *
+ * - finish this header describing the database system *
+ * - create custom database allocator *
+ * - make the system thread friendly *
+ * - change the structure of the database to T-Trees *
+ * - create a db that organizes itself by splaying *
+ * *
+ * HISTORY: *
+ * 2.1 (Athena build #???#) - Portability fix *
+ * - Fixed the portability of casting to union and added the functions *
+ * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
+ * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
+ * 2.0 (Athena build 4859) - Transition version *
+ * - Almost everything recoded with a strategy similar to objects, *
+ * database structure is maintained. *
+ * 1.0 (up to Athena build 4706) *
+ * - Previous database system. *
+ * *
+ * @version 2.1 (Athena build #???#) - Portability fix *
+ * @author (Athena build 4859) Flavio @ Amazon Project *
+ * @author (up to Athena build 4706) Athena Dev Teams *
+ * @encoding US-ASCII *
+ * @see common#db.h *
+\*****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "db.h"
+#include "../common/mmo.h"
+#include "../common/utils.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/ers.h"
+
+/*****************************************************************************\
+ * (1) Private typedefs, enums, structures, defines and global variables of *
+ * the database system. *
+ * DB_ENABLE_STATS - Define to enable database statistics. *
+ * HASH_SIZE - Define with the size of the hashtable. *
+ * DBNColor - Enumeration of colors of the nodes. *
+ * DBNode - Structure of a node in RED-BLACK trees. *
+ * struct db_free - Structure that holds a deleted node to be freed. *
+ * Database - Struture of the database. *
+ * stats - Statistics about the database system. *
+\*****************************************************************************/
+
+/**
+ * If defined statistics about database nodes, database creating/destruction
+ * and function usage are keept and displayed when finalizing the database
+ * system.
+ * WARNING: This adds overhead to every database operation (not shure how much).
+ * @private
+ * @see #DBStats
+ * @see #stats
+ * @see #db_final(void)
+ */
+//#define DB_ENABLE_STATS
+
+/**
+ * Size of the hashtable in the database.
+ * @private
+ * @see Database#ht
+ */
+#define HASH_SIZE (256+27)
+
+/**
+ * A node in a RED-BLACK tree of the database.
+ * @param parent Parent node
+ * @param left Left child node
+ * @param right Right child node
+ * @param key Key of this database entry
+ * @param data Data of this database entry
+ * @param deleted If the node is deleted
+ * @param color Color of the node
+ * @private
+ * @see Database#ht
+ */
+typedef struct dbn {
+ // Tree structure
+ struct dbn *parent;
+ struct dbn *left;
+ struct dbn *right;
+ // Node data
+ DBKey key;
+ void *data;
+ // Other
+ enum {RED, BLACK} color;
+ unsigned deleted : 1;
+} *DBNode;
+
+/**
+ * Structure that holds a deleted node.
+ * @param node Deleted node
+ * @param root Address to the root of the tree
+ * @private
+ * @see Database#free_list
+ */
+struct db_free {
+ DBNode node;
+ DBNode *root;
+};
+
+/**
+ * Complete database structure.
+ * @param dbi Interface of the database
+ * @param alloc_file File where the database was allocated
+ * @param alloc_line Line in the file where the database was allocated
+ * @param free_list Array of deleted nodes to be freed
+ * @param free_count Number of deleted nodes in free_list
+ * @param free_max Current maximum capacity of free_list
+ * @param free_lock Lock for freeing the nodes
+ * @param nodes Manager of reusable tree nodes
+ * @param cmp Comparator of the database
+ * @param hash Hasher of the database
+ * @param release Releaser of the database
+ * @param ht Hashtable of RED-BLACK trees
+ * @param type Type of the database
+ * @param options Options of the database
+ * @param item_count Number of items in the database
+ * @param maxlen Maximum length of strings in DB_STRING and DB_ISTRING databases
+ * @param global_lock Global lock of the database
+ * @private
+ * @see common\db.h#DBInterface
+ * @see #HASH_SIZE
+ * @see #DBNode
+ * @see #struct db_free
+ * @see common\db.h#DBComparator(void *,void *)
+ * @see common\db.h#DBHasher(void *)
+ * @see common\db.h#DBReleaser(void *,void *,DBRelease)
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBType
+ * @see #db_alloc(const char *,int,DBOptions,DBType,...)
+ */
+typedef struct db {
+ // Database interface
+ struct dbt dbi;
+ // File and line of allocation
+ const char *alloc_file;
+ int alloc_line;
+ // Lock system
+ struct db_free *free_list;
+ unsigned int free_count;
+ unsigned int free_max;
+ unsigned int free_lock;
+ // Other
+ ERInterface nodes;
+ DBComparator cmp;
+ DBHasher hash;
+ DBReleaser release;
+ DBNode ht[HASH_SIZE];
+ DBType type;
+ DBOptions options;
+ unsigned int item_count;
+ unsigned short maxlen;
+ unsigned global_lock : 1;
+} *Database;
+
+#ifdef DB_ENABLE_STATS
+/**
+ * Structure with what is counted when the database estatistics are enabled.
+ * @private
+ * @see #DB_ENABLE_STATS
+ * @see #stats
+ */
+static struct {
+ // Node alloc/free
+ unsigned int db_node_alloc;
+ unsigned int db_node_free;
+ // Database creating/destruction counters
+ unsigned int db_int_alloc;
+ unsigned int db_uint_alloc;
+ unsigned int db_string_alloc;
+ unsigned int db_istring_alloc;
+ unsigned int db_int_destroy;
+ unsigned int db_uint_destroy;
+ unsigned int db_string_destroy;
+ unsigned int db_istring_destroy;
+ // Function usage counters
+ unsigned int db_rotate_left;
+ unsigned int db_rotate_right;
+ unsigned int db_rebalance;
+ unsigned int db_rebalance_erase;
+ unsigned int db_is_key_null;
+ unsigned int db_dup_key;
+ unsigned int db_dup_key_free;
+ unsigned int db_free_add;
+ unsigned int db_free_remove;
+ unsigned int db_free_lock;
+ unsigned int db_free_unlock;
+ unsigned int db_int_cmp;
+ unsigned int db_uint_cmp;
+ unsigned int db_string_cmp;
+ unsigned int db_istring_cmp;
+ unsigned int db_int_hash;
+ unsigned int db_uint_hash;
+ unsigned int db_string_hash;
+ unsigned int db_istring_hash;
+ unsigned int db_release_nothing;
+ unsigned int db_release_key;
+ unsigned int db_release_data;
+ unsigned int db_release_both;
+ unsigned int db_get;
+ unsigned int db_getall;
+ unsigned int db_vgetall;
+ unsigned int db_ensure;
+ unsigned int db_vensure;
+ unsigned int db_put;
+ unsigned int db_remove;
+ unsigned int db_foreach;
+ unsigned int db_vforeach;
+ unsigned int db_clear;
+ unsigned int db_vclear;
+ unsigned int db_destroy;
+ unsigned int db_vdestroy;
+ unsigned int db_size;
+ unsigned int db_type;
+ unsigned int db_options;
+ unsigned int db_fix_options;
+ unsigned int db_default_cmp;
+ unsigned int db_default_hash;
+ unsigned int db_default_release;
+ unsigned int db_custom_release;
+ unsigned int db_alloc;
+ unsigned int db_i2key;
+ unsigned int db_ui2key;
+ unsigned int db_str2key;
+ unsigned int db_init;
+ unsigned int db_final;
+} stats = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif /* DB_ENABLE_STATS */
+
+/*****************************************************************************\
+ * (2) Section of private functions used by the database system. *
+ * db_rotate_left - Rotate a tree node to the left. *
+ * db_rotate_right - Rotate a tree node to the right. *
+ * db_rebalance - Rebalance the tree. *
+ * db_rebalance_erase - Rebalance the tree after a BLACK node was erased. *
+ * db_is_key_null - Returns not 0 if the key is considered NULL. *
+ * db_dup_key - Duplicate a key for internal use. *
+ * db_dup_key_free - Free the duplicated key. *
+ * db_free_add - Add a node to the free_list of a database. *
+ * db_free_remove - Remove a node from the free_list of a database. *
+ * db_free_lock - Increment the free_lock of a database. *
+ * db_free_unlock - Decrement the free_lock of a database. *
+ * If it was the last lock, frees the nodes in free_list. *
+ * NOTE: Keeps the database trees balanced. *
+\*****************************************************************************/
+
+/**
+ * Rotate a node to the left.
+ * @param node Node to be rotated
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rebalance(DBNode,DBNode *)
+ * @see #db_rebalance_erase(DBNode,DBNode *)
+ */
+static void db_rotate_left(DBNode node, DBNode *root)
+{
+ DBNode y = node->right;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rotate_left != (unsigned int)~0) stats.db_rotate_left++;
+#endif /* DB_ENABLE_STATS */
+ // put the left of y at the right of node
+ node->right = y->left;
+ if (y->left)
+ y->left->parent = node;
+ y->parent = node->parent;
+ // link y and node's parent
+ if (node == *root) {
+ *root = y; // node was root
+ } else if (node == node->parent->left) {
+ node->parent->left = y; // node was at the left
+ } else {
+ node->parent->right = y; // node was at the right
+ }
+ // put node at the left of y
+ y->left = node;
+ node->parent = y;
+}
+
+/**
+ * Rotate a node to the right
+ * @param node Node to be rotated
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rebalance(DBNode,DBNode *)
+ * @see #db_rebalance_erase(DBNode,DBNode *)
+ */
+static void db_rotate_right(DBNode node, DBNode *root)
+{
+ DBNode y = node->left;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rotate_right != (unsigned int)~0) stats.db_rotate_right++;
+#endif /* DB_ENABLE_STATS */
+ // put the right of y at the left of node
+ node->left = y->right;
+ if (y->right != 0)
+ y->right->parent = node;
+ y->parent = node->parent;
+ // link y and node's parent
+ if (node == *root) {
+ *root = y; // node was root
+ } else if (node == node->parent->right) {
+ node->parent->right = y; // node was at the right
+ } else {
+ node->parent->left = y; // node was at the left
+ }
+ // put node at the right of y
+ y->right = node;
+ node->parent = y;
+}
+
+/**
+ * Rebalance the RED-BLACK tree.
+ * Called when the node and it's parent are both RED.
+ * @param node Node to be rebalanced
+ * @param root Pointer to the root of the tree
+ * @private
+ * @see #db_rotate_left(DBNode,DBNode *)
+ * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_put(DBInterface,DBKey,void *)
+ */
+static void db_rebalance(DBNode node, DBNode *root)
+{
+ DBNode y;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rebalance != (unsigned int)~0) stats.db_rebalance++;
+#endif /* DB_ENABLE_STATS */
+ // Restore the RED-BLACK properties
+ node->color = RED;
+ while (node != *root && node->parent->color == RED) {
+ if (node->parent == node->parent->parent->left) {
+ // If node's parent is a left, y is node's right 'uncle'
+ y = node->parent->parent->right;
+ if (y && y->color == RED) { // case 1
+ // change the colors and move up the tree
+ node->parent->color = BLACK;
+ y->color = BLACK;
+ node->parent->parent->color = RED;
+ node = node->parent->parent;
+ } else {
+ if (node == node->parent->right) { // case 2
+ // move up and rotate
+ node = node->parent;
+ db_rotate_left(node, root);
+ }
+ // case 3
+ node->parent->color = BLACK;
+ node->parent->parent->color = RED;
+ db_rotate_right(node->parent->parent, root);
+ }
+ } else {
+ // If node's parent is a right, y is node's left 'uncle'
+ y = node->parent->parent->left;
+ if (y && y->color == RED) { // case 1
+ // change the colors and move up the tree
+ node->parent->color = BLACK;
+ y->color = BLACK;
+ node->parent->parent->color = RED;
+ node = node->parent->parent;
+ } else {
+ if (node == node->parent->left) { // case 2
+ // move up and rotate
+ node = node->parent;
+ db_rotate_right(node, root);
+ }
+ // case 3
+ node->parent->color = BLACK;
+ node->parent->parent->color = RED;
+ db_rotate_left(node->parent->parent, root);
+ }
+ }
+ }
+ (*root)->color = BLACK; // the root can and should always be black
+}
+
+/**
+ * Erase a node from the RED-BLACK tree, keeping the tree balanced.
+ * @param node Node to be erased from the tree
+ * @param root Root of the tree
+ * @private
+ * @see #db_rotate_left(DBNode,DBNode *)
+ * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_free_unlock(Database)
+ */
+static void db_rebalance_erase(DBNode node, DBNode *root)
+{
+ DBNode y = node;
+ DBNode x = NULL;
+ DBNode x_parent = NULL;
+ DBNode w;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_rebalance_erase != (unsigned int)~0) stats.db_rebalance_erase++;
+#endif /* DB_ENABLE_STATS */
+ // Select where to change the tree
+ if (y->left == NULL) { // no left
+ x = y->right;
+ } else if (y->right == NULL) { // no right
+ x = y->left;
+ } else { // both exist, go to the leftmost node of the right sub-tree
+ y = y->right;
+ while (y->left != NULL)
+ y = y->left;
+ x = y->right;
+ }
+
+ // Remove the node from the tree
+ if (y != node) { // both childs existed
+ // put the left of 'node' in the left of 'y'
+ node->left->parent = y;
+ y->left = node->left;
+
+ // 'y' is not the direct child of 'node'
+ if (y != node->right) {
+ // put 'x' in the old position of 'y'
+ x_parent = y->parent;
+ if (x) x->parent = y->parent;
+ y->parent->left = x;
+ // put the right of 'node' in 'y'
+ y->right = node->right;
+ node->right->parent = y;
+ // 'y' is a direct child of 'node'
+ } else {
+ x_parent = y;
+ }
+
+ // link 'y' and the parent of 'node'
+ if (*root == node) {
+ *root = y; // 'node' was the root
+ } else if (node->parent->left == node) {
+ node->parent->left = y; // 'node' was at the left
+ } else {
+ node->parent->right = y; // 'node' was at the right
+ }
+ y->parent = node->parent;
+ // switch colors
+ {
+ int tmp = y->color;
+ y->color = node->color;
+ node->color = tmp;
+ }
+ y = node;
+ } else { // one child did not exist
+ // put x in node's position
+ x_parent = y->parent;
+ if (x) x->parent = y->parent;
+ // link x and node's parent
+ if (*root == node) {
+ *root = x; // node was the root
+ } else if (node->parent->left == node) {
+ node->parent->left = x; // node was at the left
+ } else {
+ node->parent->right = x; // node was at the right
+ }
+ }
+
+ // Restore the RED-BLACK properties
+ if (y->color != RED) {
+ while (x != *root && (x == NULL || x->color == BLACK)) {
+ if (x == x_parent->left) {
+ 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 == NULL || w->left->color == BLACK) &&
+ (w->right == NULL || w->right->color == BLACK)) {
+ w->color = RED;
+ x = x_parent;
+ x_parent = x_parent->parent;
+ } else {
+ if (w->right == NULL || 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 {
+ 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 == NULL || w->right->color == BLACK) &&
+ (w->left == NULL || w->left->color == BLACK)) {
+ w->color = RED;
+ x = x_parent;
+ x_parent = x_parent->parent;
+ } else {
+ if (w->left == NULL || 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;
+ }
+}
+
+/**
+ * Returns not 0 if the key is considerd to be NULL.
+ * @param type Type of database
+ * @param key Key being tested
+ * @return not 0 if considered NULL, 0 otherwise
+ * @private
+ * @see common\db.h#DBType
+ * @see common\db.h#DBKey
+ * @see #db_get(DBInterface,DBKey)
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_remove(DBInterface,DBKey)
+ */
+static int db_is_key_null(DBType type, DBKey key)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_is_key_null != (unsigned int)~0) stats.db_is_key_null++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ return (key.str == NULL);
+
+ default: // Not a pointer
+ return 0;
+ }
+}
+
+/**
+ * Duplicate the key used in the database.
+ * @param db Database the key is being used in
+ * @param key Key to be duplicated
+ * @param Duplicated key
+ * @private
+ * @see #db_free_add(Database,DBNode,DBNode *)
+ * @see #db_free_remove(Database,DBNode)
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_dup_key_free(Database,DBKey)
+ */
+static DBKey db_dup_key(Database db, DBKey key)
+{
+ unsigned char *str;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_dup_key != (unsigned int)~0) stats.db_dup_key++;
+#endif /* DB_ENABLE_STATS */
+ switch (db->type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ if (db->maxlen) {
+ CREATE(str, unsigned char, db->maxlen +1);
+ memcpy(str, key.str, db->maxlen);
+ str[db->maxlen] = '\0';
+ key.str = str;
+ } else {
+ key.str = (unsigned char *)aStrdup((const char *)key.str);
+ }
+ return key;
+
+ default:
+ return key;
+ }
+}
+
+/**
+ * Free a key duplicated by db_dup_key.
+ * @param db Database the key is being used in
+ * @param key Key to be freed
+ * @private
+ * @see #db_dup_key(Database,DBKey)
+ */
+static void db_dup_key_free(Database db, DBKey key)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_dup_key_free != (unsigned int)~0) stats.db_dup_key_free++;
+#endif /* DB_ENABLE_STATS */
+ switch (db->type) {
+ case DB_STRING:
+ case DB_ISTRING:
+ aFree(key.str);
+ return;
+
+ default:
+ return;
+ }
+}
+
+/**
+ * Add a node to the free_list of the database.
+ * Marks the node as deleted.
+ * If the key isn't duplicated, the key is duplicated and released.
+ * @param db Target database
+ * @param root Root of the tree from the node
+ * @param node Target node
+ * @private
+ * @see #struct db_free
+ * @see Database#free_list
+ * @see Database#free_count
+ * @see Database#free_max
+ * @see #db_remove(DBInterface,DBKey)
+ * @see #db_free_remove(Database,DBNode)
+ */
+static void db_free_add(Database db, DBNode node, DBNode *root)
+{
+ DBKey old_key;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_add != (unsigned int)~0) stats.db_free_add++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == (unsigned int)~0) {
+ ShowFatalError("db_free_add: free_lock overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ if (!(db->options&DB_OPT_DUP_KEY)) { // Make shure we have a key until the node is freed
+ old_key = node->key;
+ node->key = db_dup_key(db, node->key);
+ db->release(old_key, node->data, DB_RELEASE_KEY);
+ }
+ if (db->free_count == db->free_max) { // No more space, expand free_list
+ db->free_max = (db->free_max<<2) +3; // = db->free_max*4 +3
+ if (db->free_max <= db->free_count) {
+ if (db->free_count == (unsigned int)~0) {
+ ShowFatalError("db_free_add: free_count overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ db->free_max = (unsigned int)~0;
+ }
+ RECREATE(db->free_list, struct db_free, db->free_max);
+ }
+ node->deleted = 1;
+ db->free_list[db->free_count].node = node;
+ db->free_list[db->free_count].root = root;
+ db->free_count++;
+ db->item_count--;
+}
+
+/**
+ * Remove a node from the free_list of the database.
+ * Marks the node as not deleted.
+ * NOTE: Frees the duplicated key of the node.
+ * @param db Target database
+ * @param node Node being removed from free_list
+ * @private
+ * @see #struct db_free
+ * @see Database#free_list
+ * @see Database#free_count
+ * @see #db_put(DBInterface,DBKey,void *)
+ * @see #db_free_add(Database,DBNode *,DBNode)
+ */
+static void db_free_remove(Database db, DBNode node)
+{
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_remove != (unsigned int)~0) stats.db_free_remove++;
+#endif /* DB_ENABLE_STATS */
+ for (i = 0; i < db->free_count; i++) {
+ if (db->free_list[i].node == node) {
+ if (i < db->free_count -1) // copy the last item to where the removed one was
+ memcpy(&db->free_list[i], &db->free_list[db->free_count -1], sizeof(struct db_free));
+ db_dup_key_free(db, node->key);
+ break;
+ }
+ }
+ node->deleted = 0;
+ if (i == db->free_count) {
+ ShowWarning("db_free_remove: node was not found - database allocated at %s:%d\n", db->alloc_file, db->alloc_line);
+ } else {
+ db->free_count--;
+ }
+ db->item_count++;
+}
+
+/**
+ * Increment the free_lock of the database.
+ * @param db Target database
+ * @private
+ * @see Database#free_lock
+ * @see #db_unlock(Database)
+ */
+static void db_free_lock(Database db)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_lock != (unsigned int)~0) stats.db_free_lock++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == (unsigned int)~0) {
+ ShowFatalError("db_free_lock: free_lock overflow\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ exit(EXIT_FAILURE);
+ }
+ db->free_lock++;
+}
+
+/**
+ * Decrement the free_lock of the database.
+ * If it was the last lock, frees the nodes of the database.
+ * Keeps the tree balanced.
+ * NOTE: Frees the duplicated keys of the nodes
+ * @param db Target database
+ * @private
+ * @see Database#free_lock
+ * @see #db_free_dbn(DBNode)
+ * @see #db_lock(Database)
+ */
+static void db_free_unlock(Database db)
+{
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_free_unlock != (unsigned int)~0) stats.db_free_unlock++;
+#endif /* DB_ENABLE_STATS */
+ if (db->free_lock == 0) {
+ ShowWarning("db_free_unlock: free_lock was already 0\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ } else {
+ db->free_lock--;
+ }
+ if (db->free_lock)
+ return; // Not last lock
+
+ for (i = 0; i < db->free_count ; i++) {
+ db_rebalance_erase(db->free_list[i].node, db->free_list[i].root);
+ db_dup_key_free(db, db->free_list[i].node->key);
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
+#endif /* DB_ENABLE_STATS */
+ ers_free(db->nodes, db->free_list[i].node);
+ }
+ db->free_count = 0;
+}
+
+/*****************************************************************************\
+ * (3) Section of protected functions used internally. *
+ * NOTE: the protected functions used in the database interface are in the *
+ * next section. *
+ * db_int_cmp - Default comparator for DB_INT databases. *
+ * db_uint_cmp - Default comparator for DB_UINT databases. *
+ * db_string_cmp - Default comparator for DB_STRING databases. *
+ * db_istring_cmp - Default comparator for DB_ISTRING databases. *
+ * db_int_hash - Default hasher for DB_INT databases. *
+ * db_uint_hash - Default hasher for DB_UINT databases. *
+ * db_string_hash - Default hasher for DB_STRING databases. *
+ * db_istring_hash - Default hasher for DB_ISTRING databases. *
+ * db_release_nothing - Releaser that releases nothing. *
+ * db_release_key - Releaser that only releases the key. *
+ * db_release_data - Releaser that only releases the data. *
+ * db_release_both - Releaser that releases key and data. *
+\*****************************************************************************/
+
+/**
+ * Default comparator for DB_INT databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_INT
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_int_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_int_cmp != (unsigned int)~0) stats.db_int_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (key1.i < key2.i) return -1;
+ if (key1.i > key2.i) return 1;
+ return 0;
+}
+
+/**
+ * Default comparator for DB_UINT databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * <code>maxlen</code> is ignored.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_UINT
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_uint_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_uint_cmp != (unsigned int)~0) stats.db_uint_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (key1.ui < key2.ui) return -1;
+ if (key1.ui > key2.ui) return 1;
+ return 0;
+}
+
+/**
+ * Default comparator for DB_STRING databases.
+ * Compares key1 to key2.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_STRING
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_string_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_string_cmp != (unsigned int)~0) stats.db_string_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0) maxlen = (unsigned short)~0;
+ return strncmp((const char *)key1.str, (const char *)key2.str, maxlen);
+}
+
+/**
+ * Default comparator for DB_ISTRING databases.
+ * Compares key1 to key2 case insensitively.
+ * Return 0 if equal, negative if lower and positive if higher.
+ * @param key1 Key to be compared
+ * @param key2 Key being compared to
+ * @param maxlen Maximum length of the key to hash
+ * @return 0 if equal, negative if lower and positive if higher
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_ISTRING
+ * @see common\db.h#DBComparator
+ * @see #db_default_cmp(DBType)
+ */
+static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_istring_cmp != (unsigned int)~0) stats.db_istring_cmp++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0) maxlen = (unsigned short)~0;
+ return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen);
+}
+
+/**
+ * Default hasher for DB_INT databases.
+ * Returns the value of the key as an unsigned int.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_INT
+ * @see common\db.h#DBHasher
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_int_hash != (unsigned int)~0) stats.db_int_hash++;
+#endif /* DB_ENABLE_STATS */
+ return (unsigned int)key.i;
+}
+
+/**
+ * Default hasher for DB_UINT databases.
+ * Just returns the value of the key.
+ * <code>maxlen</code> is ignored.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_UINT
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_uint_hash != (unsigned int)~0) stats.db_uint_hash++;
+#endif /* DB_ENABLE_STATS */
+ return key.ui;
+}
+
+/**
+ * Default hasher for DB_STRING databases.
+ * If maxlen if 0, the maximum number of maxlen is used instead.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_STRING
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
+{
+ unsigned char *k = key.str;
+ unsigned int hash = 0;
+ unsigned short i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_string_hash != (unsigned int)~0) stats.db_string_hash++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0)
+ maxlen = (unsigned short)~0; // Maximum
+
+ for (i = 0; *k; i++) {
+ hash = (hash*33 + *k++)^(hash>>24);
+ if (i == maxlen)
+ break;
+ }
+
+ return hash;
+}
+
+/**
+ * Default hasher for DB_ISTRING databases.
+ * If maxlen if 0, the maximum number of maxlen is used instead.
+ * @param key Key to be hashed
+ * @param maxlen Maximum length of the key to hash
+ * @return hash of the key
+ * @see common\db.h#DBKey
+ * @see common\db.h\DBType#DB_ISTRING
+ * @see #db_default_hash(DBType)
+ */
+static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
+{
+ unsigned char *k = key.str;
+ unsigned int hash = 0;
+ unsigned short i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_istring_hash != (unsigned int)~0) stats.db_istring_hash++;
+#endif /* DB_ENABLE_STATS */
+ if (maxlen == 0)
+ maxlen = (unsigned short)~0; // Maximum
+
+ for (i = 0; *k; i++) {
+ hash = (hash*33 + LOWER(*k))^(hash>>24);
+ k++;
+ if (i == maxlen)
+ break;
+ }
+
+ return hash;
+}
+
+/**
+ * Releaser that releases nothing.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_releaser(DBType,DBOptions)
+ */
+static void db_release_nothing(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_nothing != (unsigned int)~0) stats.db_release_nothing++;
+#endif /* DB_ENABLE_STATS */
+}
+
+/**
+ * Releaser that only releases the key.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_key(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_key != (unsigned int)~0) stats.db_release_key++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
+}
+
+/**
+ * Releaser that only releases the data.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_data(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_data != (unsigned int)~0) stats.db_release_data++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_DATA) aFree(data);
+}
+
+/**
+ * Releaser that releases both key and data.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ */
+static void db_release_both(DBKey key, void *data, DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_release_both != (unsigned int)~0) stats.db_release_both++;
+#endif /* DB_ENABLE_STATS */
+ if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer
+ if (which&DB_RELEASE_DATA) aFree(data);
+}
+
+/*****************************************************************************\
+ * (4) Section with protected functions used in the interface of the *
+ * database. *
+ * db_obj_get - Get the data identified by the key. *
+ * db_obj_vgetall - Get the data of the matched entries. *
+ * db_obj_getall - Get the data of the matched entries. *
+ * db_obj_vensure - Get the data identified by the key, creating if it *
+ * doesn't exist yet. *
+ * db_obj_ensure - Get the data identified by the key, creating if it *
+ * doesn't exist yet. *
+ * db_obj_put - Put data identified by the key in the database. *
+ * db_obj_remove - Remove an entry from the database. *
+ * db_obj_vforeach - Apply a function to every entry in the database. *
+ * db_obj_foreach - Apply a function to every entry in the database. *
+ * db_obj_vclear - Remove all entries from the database. *
+ * db_obj_clear - Remove all entries from the database. *
+ * db_obj_vdestroy - Destroy the database, freeing all the used memory. *
+ * db_obj_destroy - Destroy the database, freeing all the used memory. *
+ * db_obj_size - Return the size of the database. *
+ * db_obj_type - Return the type of the database. *
+ * db_obj_options - Return the options of the database. *
+\*****************************************************************************/
+
+/**
+ * Get the data of the entry identifid by the key.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @return Data of the entry or NULL if not found
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#get(DBInterface,DBKey)
+ */
+static void *db_obj_get(DBInterface self, DBKey key)
+{
+ Database db = (Database)self;
+ DBNode node;
+ int c;
+ void *data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_get != (unsigned int)~0) stats.db_get++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_get: Attempted to retrieve non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE];
+ while (node) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ data = node->data;
+ break;
+ }
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param self Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
+ * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ */
+static unsigned int db_obj_vgetall(DBInterface self, void **buf, unsigned int max, DBMatcher match, va_list args)
+{
+ Database db = (Database)self;
+ unsigned int i;
+ DBNode node;
+ DBNode parent;
+ unsigned int ret = 0;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vgetall != (unsigned int)~0) stats.db_vgetall++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (match == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Match in the order: current node, left tree, right tree
+ node = db->ht[i];
+ while (node) {
+ parent = node->parent;
+ if (!(node->deleted) && match(node->key, node->data, args) == 0) {
+ if (buf && ret < max)
+ buf[ret] = node->data;
+ ret++;
+ }
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ while (node) {
+ parent = node->parent;
+ if (parent && parent->right && parent->left == node) {
+ node = parent->right;
+ break;
+ }
+ node = parent;
+ }
+ }
+ }
+ db_free_unlock(db);
+ return ret;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param self Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see common\db.h\DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ */
+static unsigned int db_obj_getall(DBInterface self, void **buf, unsigned int max, DBMatcher match, ...)
+{
+ va_list args;
+ unsigned int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_getall != (unsigned int)~0) stats.db_getall++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, match);
+ ret = self->vgetall(self, buf, max, match, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param args Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBCreateData
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ */
+static void *db_obj_vensure(DBInterface self, DBKey key, DBCreateData create, va_list args)
+{
+ Database db = (Database)self;
+ DBNode node;
+ DBNode parent = NULL;
+ unsigned int hash;
+ int c = 0;
+ void *data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vensure != (unsigned int)~0) stats.db_vensure++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (create == NULL) {
+ ShowError("db_ensure: Create function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_ensure: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ node = db->ht[hash];
+ while (node) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ break;
+ }
+ parent = node;
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ // Create node if necessary
+ if (node == NULL) {
+ if (db->item_count == (unsigned int)~0) {
+ ShowError("db_vensure: item_count overflow, aborting item insertion.\n"
+ "Database allocated at %s:%d",
+ db->alloc_file, db->alloc_line);
+ return NULL;
+ }
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
+#endif /* DB_ENABLE_STATS */
+ node = ers_alloc(db->nodes, struct dbn);
+ node->left = NULL;
+ node->right = NULL;
+ node->deleted = 0;
+ db->item_count++;
+ if (c == 0) { // hash entry is empty
+ node->color = BLACK;
+ node->parent = NULL;
+ db->ht[hash] = node;
+ } else {
+ node->color = RED;
+ if (c < 0) { // put at the left
+ parent->left = node;
+ node->parent = parent;
+ } else { // put at the right
+ parent->right = node;
+ node->parent = parent;
+ }
+ if (parent->color == RED) // two consecutive RED nodes, must rebalance
+ db_rebalance(node, &db->ht[hash]);
+ }
+ // put key and data in the node
+ if (db->options&DB_OPT_DUP_KEY) {
+ node->key = db_dup_key(db, key);
+ if (db->options&DB_OPT_RELEASE_KEY)
+ db->release(key, data, DB_RELEASE_KEY);
+ } else {
+ node->key = key;
+ }
+ node->data = create(key, args);
+ }
+ data = node->data;
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param ... Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBCreateData
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see common\db.h\DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+static void *db_obj_ensure(DBInterface self, DBKey key, DBCreateData create, ...)
+{
+ va_list args;
+ void *ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_ensure != (unsigned int)~0) stats.db_ensure++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, create);
+ ret = self->vensure(self, key, create, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Put the data identified by the key in the database.
+ * Returns the previous data if the entry exists or NULL.
+ * NOTE: Uses the new key, the old one is released.
+ * @param self Interface of the database
+ * @param key Key that identifies the data
+ * @param data Data to be put in the database
+ * @return The previous data if the entry exists or NULL
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see #db_malloc_dbn(void)
+ * @see common\db.h\DBInterface#put(DBInterface,DBKey,void *)
+ */
+static void *db_obj_put(DBInterface self, DBKey key, void *data)
+{
+ Database db = (Database)self;
+ DBNode node;
+ DBNode parent = NULL;
+ int c = 0;
+ unsigned int hash;
+ void *old_data = NULL;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_put != (unsigned int)~0) stats.db_put++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_put: Database is being destroyed, aborting entry insertion.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_put: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(data || db->options&DB_OPT_ALLOW_NULL_DATA)) {
+ ShowError("db_put: Attempted to use non-allowed NULL data for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ if (db->item_count == (unsigned int)~0) {
+ ShowError("db_put: item_count overflow, aborting item insertion.\n"
+ "Database allocated at %s:%d",
+ db->alloc_file, db->alloc_line);
+ return NULL;
+ }
+ // search for an equal node
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ for (node = db->ht[hash]; node; ) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) { // equal entry, replace
+ if (node->deleted) {
+ db_free_remove(db, node);
+ } else {
+ db->release(node->key, node->data, DB_RELEASE_BOTH);
+ }
+ old_data = node->data;
+ break;
+ }
+ parent = node;
+ if (c < 0) {
+ node = node->left;
+ } else {
+ node = node->right;
+ }
+ }
+ // allocate a new node if necessary
+ if (node == NULL) {
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++;
+#endif /* DB_ENABLE_STATS */
+ node = ers_alloc(db->nodes, struct dbn);
+ node->left = NULL;
+ node->right = NULL;
+ node->deleted = 0;
+ db->item_count++;
+ if (c == 0) { // hash entry is empty
+ node->color = BLACK;
+ node->parent = NULL;
+ db->ht[hash] = node;
+ } else {
+ node->color = RED;
+ if (c < 0) { // put at the left
+ parent->left = node;
+ node->parent = parent;
+ } else { // put at the right
+ parent->right = node;
+ node->parent = parent;
+ }
+ if (parent->color == RED) // two consecutive RED nodes, must rebalance
+ db_rebalance(node, &db->ht[hash]);
+ }
+ }
+ // put key and data in the node
+ if (db->options&DB_OPT_DUP_KEY) {
+ node->key = db_dup_key(db, key);
+ if (db->options&DB_OPT_RELEASE_KEY)
+ db->release(key, data, DB_RELEASE_KEY);
+ } else {
+ node->key = key;
+ }
+ node->data = data;
+ db_free_unlock(db);
+ return old_data;
+}
+
+/**
+ * Remove an entry from the database.
+ * Returns the data of the entry.
+ * NOTE: The key (of the database) is released in {@link #db_free_add(Database,DBNode,DBNode *)}.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @return The data of the entry or NULL if not found
+ * @protected
+ * @see common\db.h#DBKey
+ * @see common\db.h#DBInterface
+ * @see #db_free_add(Database,DBNode,DBNode *)
+ * @see common\db.h\DBInterface#remove(DBInterface,DBKey)
+ */
+static void *db_obj_remove(DBInterface self, DBKey key)
+{
+ Database db = (Database)self;
+ void *data = NULL;
+ DBNode node;
+ unsigned int hash;
+ int c = 0;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_remove != (unsigned int)~0) stats.db_remove++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return NULL; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_remove: Database is being destroyed. Aborting entry deletion.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return NULL; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ hash = db->hash(key, db->maxlen)%HASH_SIZE;
+ for(node = db->ht[hash]; node; ){
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ if (!(node->deleted)) {
+ data = node->data;
+ db->release(node->key, node->data, DB_RELEASE_DATA);
+ db_free_add(db, node, &db->ht[hash]);
+ }
+ break;
+ }
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ db_free_unlock(db);
+ return data;
+}
+
+/**
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param self Interface of the database
+ * @param func Function to be applyed
+ * @param args Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vforeach(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ unsigned int i;
+ int sum = 0;
+ DBNode node;
+ DBNode parent;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vforeach != (unsigned int)~0) stats.db_vforeach++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (func == NULL) {
+ ShowError("db_foreach: Passed function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
+ return 0; // nullpo candidate
+ }
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Apply func in the order: current node, left node, right node
+ node = db->ht[i];
+ while (node) {
+ parent = node->parent;
+ if (!(node->deleted))
+ sum += func(node->key, node->data, args);
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ while (node) {
+ parent = node->parent;
+ if (parent && parent->right && parent->left == node) {
+ node = parent->right;
+ break;
+ }
+ node = parent;
+ }
+ }
+ }
+ db_free_unlock(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)}.
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param self Interface of the database
+ * @param func Function to be applyed
+ * @param ... Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#foreach(DBInterface,DBApply,...)
+ */
+static int db_obj_foreach(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_foreach != (unsigned int)~0) stats.db_foreach++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vforeach(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vclear(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ int sum = 0;
+ unsigned int i;
+ DBNode node;
+ DBNode parent;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vclear != (unsigned int)~0) stats.db_vclear++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ for (i = 0; i < HASH_SIZE; i++) {
+ // Apply the func and delete in the order: left tree, right tree, current node
+ node = db->ht[i];
+ db->ht[i] = NULL;
+ while (node) {
+ parent = node->parent;
+ if (node->left) {
+ node = node->left;
+ continue;
+ }
+ if (node->right) {
+ node = node->right;
+ continue;
+ }
+ if (node->deleted) {
+ db_dup_key_free(db, node->key);
+ } else {
+ if (func)
+ sum += func(node->key, node->data, args);
+ db->release(node->key, node->data, DB_RELEASE_BOTH);
+ node->deleted = 1;
+ }
+#ifdef DB_ENABLE_STATS
+ if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++;
+#endif /* DB_ENABLE_STATS */
+ ers_free(db->nodes, node);
+ if (parent) {
+ if (parent->left == node)
+ parent->left = NULL;
+ else
+ parent->right = NULL;
+ }
+ node = parent;
+ }
+ db->ht[i] = NULL;
+ }
+ db->free_count = 0;
+ db->item_count = 0;
+ db_free_unlock(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)}.
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#clear(DBInterface,DBApply,...)
+ */
+static int db_obj_clear(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_clear != (unsigned int)~0) stats.db_clear++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vclear(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ */
+static int db_obj_vdestroy(DBInterface self, DBApply func, va_list args)
+{
+ Database db = (Database)self;
+ int sum;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_vdestroy != (unsigned int)~0) stats.db_vdestroy++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+ if (db->global_lock) {
+ ShowError("db_vdestroy: Database is already locked for destruction. Aborting second database destruction.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line);
+ return 0;
+ }
+ if (db->free_lock)
+ ShowWarning("db_vdestroy: Database is still in use, %u lock(s) left. Continuing database destruction.\n"
+ "Database allocated at %s:%d\n",
+ db->alloc_file, db->alloc_line, db->free_lock);
+
+#ifdef DB_ENABLE_STATS
+ switch (db->type) {
+ case DB_INT:
+ stats.db_int_destroy++;
+ break;
+ case DB_UINT:
+ stats.db_uint_destroy++;
+ break;
+ case DB_STRING:
+ stats.db_string_destroy++;
+ break;
+ case DB_ISTRING:
+ stats.db_istring_destroy++;
+ break;
+ }
+#endif /* DB_ENABLE_STATS */
+ db_free_lock(db);
+ db->global_lock = 1;
+ sum = self->vclear(self, func, args);
+ aFree(db->free_list);
+ db->free_list = NULL;
+ db->free_max = 0;
+ ers_destroy(db->nodes);
+ db_free_unlock(db);
+ aFree(db);
+ return sum;
+}
+
+/**
+ * Just calls {@link common\db.h\DBInterface#db_vdestroy(DBInterface,DBApply,va_list)}.
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted.
+ * @param self Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see common\db.h#DBApply(DBKey,void *,va_list)
+ * @see common\db.h#DBInterface
+ * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see common\db.h\DBInterface#destroy(DBInterface,DBApply,...)
+ */
+static int db_obj_destroy(DBInterface self, DBApply func, ...)
+{
+ va_list args;
+ int ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_destroy != (unsigned int)~0) stats.db_destroy++;
+#endif /* DB_ENABLE_STATS */
+ if (self == NULL) return 0; // nullpo candidate
+
+ va_start(args, func);
+ ret = self->vdestroy(self, func, args);
+ va_end(args);
+ return ret;
+}
+
+/**
+ * Return the size of the database (number of items in the database).
+ * @param self Interface of the database
+ * @return Size of the database
+ * @protected
+ * @see common\db.h#DBInterface
+ * @see Database#item_count
+ * @see common\db.h\DBInterface#size(DBInterface)
+ */
+static unsigned int db_obj_size(DBInterface self)
+{
+ Database db = (Database)self;
+ unsigned int item_count;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_size != (unsigned int)~0) stats.db_size++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return 0; // nullpo candidate
+
+ db_free_lock(db);
+ item_count = db->item_count;
+ db_free_unlock(db);
+
+ return item_count;
+}
+
+/**
+ * Return the type of database.
+ * @param self Interface of the database
+ * @return Type of the database
+ * @protected
+ * @see common\db.h#DBType
+ * @see common\db.h#DBInterface
+ * @see Database#type
+ * @see common\db.h\DBInterface#type(DBInterface)
+ */
+static DBType db_obj_type(DBInterface self)
+{
+ Database db = (Database)self;
+ DBType type;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_type != (unsigned int)~0) stats.db_type++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return -1; // nullpo candidate - TODO what should this return?
+
+ db_free_lock(db);
+ type = db->type;
+ db_free_unlock(db);
+
+ return type;
+}
+
+/**
+ * Return the options of the database.
+ * @param self Interface of the database
+ * @return Options of the database
+ * @protected
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBInterface
+ * @see Database#options
+ * @see common\db.h\DBInterface#options(DBInterface)
+ */
+static DBOptions db_obj_options(DBInterface self)
+{
+ Database db = (Database)self;
+ DBOptions options;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_options != (unsigned int)~0) stats.db_options++;
+#endif /* DB_ENABLE_STATS */
+ if (db == NULL) return DB_OPT_BASE; // nullpo candidate - TODO what should this return?
+
+ db_free_lock(db);
+ options = db->options;
+ db_free_unlock(db);
+
+ return options;
+}
+
+/*****************************************************************************\
+ * (5) Section with public functions. *
+ * db_fix_options - Apply database type restrictions to the options. *
+ * db_default_cmp - Get the default comparator for a type of database. *
+ * db_default_hash - Get the default hasher for a type of database. *
+ * db_default_release - Get the default releaser for a type of database *
+ * with the specified options. *
+ * db_custom_release - Get a releaser that behaves a certains way. *
+ * db_alloc - Allocate a new database. *
+ * db_i2key - Manual cast from 'int' to 'DBKey'. *
+ * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
+ * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
+ * db_init - Initialize the database system. *
+ * db_final - Finalize the database system. *
+\*****************************************************************************/
+
+/**
+ * Returns the fixed options according to the database type.
+ * Sets required options and unsets unsupported options.
+ * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
+ * @param type Type of the database
+ * @param options Original options of the database
+ * @return Fixed options of the database
+ * @private
+ * @see common\db.h#DBType
+ * @see common\db.h#DBOptions
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ * @see common\db.h#db_fix_options(DBType,DBOptions)
+ */
+DBOptions db_fix_options(DBType type, DBOptions options)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_fix_options != (unsigned int)~0) stats.db_fix_options++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT:
+ case DB_UINT: // Numeric database, do nothing with the keys
+ return options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY);
+
+ default:
+ ShowError("db_fix_options: Unknown database type %u with options %x\n", type, options);
+ case DB_STRING:
+ case DB_ISTRING: // String databases, no fix required
+ return options;
+ }
+}
+
+/**
+ * Returns the default comparator for the specified type of database.
+ * @param type Type of database
+ * @return Comparator for the type of database or NULL if unknown database
+ * @public
+ * @see common\db.h#DBType
+ * @see #db_int_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_uint_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_string_cmp(DBKey,DBKey,unsigned short)
+ * @see #db_istring_cmp(DBKey,DBKey,unsigned short)
+ * @see common\db.h#db_default_cmp(DBType)
+ */
+DBComparator db_default_cmp(DBType type)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_cmp != (unsigned int)~0) stats.db_default_cmp++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT: return db_int_cmp;
+ case DB_UINT: return db_uint_cmp;
+ case DB_STRING: return db_string_cmp;
+ case DB_ISTRING: return db_istring_cmp;
+ default:
+ ShowError("db_default_cmp: Unknown database type %u\n", type);
+ return NULL;
+ }
+}
+
+/**
+ * Returns the default hasher for the specified type of database.
+ * @param type Type of database
+ * @return Hasher of the type of database or NULL if unknown database
+ * @public
+ * @see common\db.h#DBType
+ * @see #db_int_hash(DBKey,unsigned short)
+ * @see #db_uint_hash(DBKey,unsigned short)
+ * @see #db_string_hash(DBKey,unsigned short)
+ * @see #db_istring_hash(DBKey,unsigned short)
+ * @see common\db.h#db_default_hash(DBType)
+ */
+DBHasher db_default_hash(DBType type)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_hash != (unsigned int)~0) stats.db_default_hash++;
+#endif /* DB_ENABLE_STATS */
+ switch (type) {
+ case DB_INT: return db_int_hash;
+ case DB_UINT: return db_uint_hash;
+ case DB_STRING: return db_string_hash;
+ case DB_ISTRING: return db_istring_hash;
+ default:
+ ShowError("db_default_hash: Unknown database type %u\n", type);
+ return NULL;
+ }
+}
+
+/**
+ * Returns the default releaser for the specified type of database with the
+ * specified options.
+ * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
+ * before choosing the releaser.
+ * @param type Type of database
+ * @param options Options of the database
+ * @return Default releaser for the type of database with the specified options
+ * @public
+ * @see common\db.h#DBType
+ * @see common\db.h#DBOptions
+ * @see common\db.h#DBReleaser
+ * @see #db_release_nothing(DBKey,void *,DBRelease)
+ * @see #db_release_key(DBKey,void *,DBRelease)
+ * @see #db_release_data(DBKey,void *,DBRelease)
+ * @see #db_release_both(DBKey,void *,DBRelease)
+ * @see #db_custom_release(DBRelease)
+ * @see common\db.h#db_default_release(DBType,DBOptions)
+ */
+DBReleaser db_default_release(DBType type, DBOptions options)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_default_release != (unsigned int)~0) stats.db_default_release++;
+#endif /* DB_ENABLE_STATS */
+ options = db_fix_options(type, options);
+ if (options&DB_OPT_RELEASE_DATA) { // Release data, what about the key?
+ if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
+ return db_release_both; // Release both key and data
+ return db_release_data; // Only release data
+ }
+ if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY))
+ return db_release_key; // Only release key
+ return db_release_nothing; // Release nothing
+}
+
+/**
+ * Returns the releaser that releases the specified release options.
+ * @param which Options that specified what the releaser releases
+ * @return Releaser for the specified release options
+ * @public
+ * @see common\db.h#DBRelease
+ * @see common\db.h#DBReleaser
+ * @see #db_release_nothing(DBKey,void *,DBRelease)
+ * @see #db_release_key(DBKey,void *,DBRelease)
+ * @see #db_release_data(DBKey,void *,DBRelease)
+ * @see #db_release_both(DBKey,void *,DBRelease)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.h#db_custom_release(DBRelease)
+ */
+DBReleaser db_custom_release(DBRelease which)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_custom_release != (unsigned int)~0) stats.db_custom_release++;
+#endif /* DB_ENABLE_STATS */
+ switch (which) {
+ case DB_RELEASE_NOTHING: return db_release_nothing;
+ case DB_RELEASE_KEY: return db_release_key;
+ case DB_RELEASE_DATA: return db_release_data;
+ case DB_RELEASE_BOTH: return db_release_both;
+ default:
+ ShowError("db_custom_release: Unknown release options %u\n", which);
+ return NULL;
+ }
+}
+
+/**
+ * Allocate a new database of the specified type.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before creating the database.
+ * @param file File where the database is being allocated
+ * @param line Line of the file where the database is being allocated
+ * @param type Type of database
+ * @param options Options of the database
+ * @param maxlen Maximum length of the string to be used as key in string
+ * databases
+ * @return The interface of the database
+ * @public
+ * @see common\db.h#DBType
+ * @see common\db.h#DBInterface
+ * @see #Database
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see common\db.h#db_alloc(const char *,int,DBType,unsigned short)
+ */
+DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen)
+{
+ Database db;
+ unsigned int i;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_alloc != (unsigned int)~0) stats.db_alloc++;
+ switch (type) {
+ case DB_INT:
+ stats.db_int_alloc++;
+ break;
+ case DB_UINT:
+ stats.db_uint_alloc++;
+ break;
+ case DB_STRING:
+ stats.db_string_alloc++;
+ break;
+ case DB_ISTRING:
+ stats.db_istring_alloc++;
+ break;
+ }
+#endif /* DB_ENABLE_STATS */
+ CREATE(db, struct db, 1);
+
+ options = db_fix_options(type, options);
+ /* Interface of the database */
+ db->dbi.get = db_obj_get;
+ db->dbi.getall = db_obj_getall;
+ db->dbi.vgetall = db_obj_vgetall;
+ db->dbi.ensure = db_obj_ensure;
+ db->dbi.vensure = db_obj_vensure;
+ db->dbi.put = db_obj_put;
+ db->dbi.remove = db_obj_remove;
+ db->dbi.foreach = db_obj_foreach;
+ db->dbi.vforeach = db_obj_vforeach;
+ db->dbi.clear = db_obj_clear;
+ db->dbi.vclear = db_obj_vclear;
+ db->dbi.destroy = db_obj_destroy;
+ db->dbi.vdestroy = db_obj_vdestroy;
+ db->dbi.size = db_obj_size;
+ db->dbi.type = db_obj_type;
+ db->dbi.options = db_obj_options;
+ /* File and line of allocation */
+ db->alloc_file = file;
+ db->alloc_line = line;
+ /* Lock system */
+ db->free_list = NULL;
+ db->free_count = 0;
+ db->free_max = 0;
+ db->free_lock = 0;
+ /* Other */
+ db->nodes = ers_new((uint32)sizeof(struct dbn));
+ db->cmp = db_default_cmp(type);
+ db->hash = db_default_hash(type);
+ db->release = db_default_release(type, options);
+ for (i = 0; i < HASH_SIZE; i++)
+ db->ht[i] = NULL;
+ db->type = type;
+ db->options = options;
+ db->item_count = 0;
+ db->maxlen = maxlen;
+ db->global_lock = 0;
+
+ return &db->dbi;
+}
+
+#ifdef DB_MANUAL_CAST_TO_UNION
+/**
+ * Manual cast from 'int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.h#db_i2key(int)
+ */
+DBKey db_i2key(int key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_i2key != (unsigned int)~0) stats.db_i2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.i = key;
+ return ret;
+}
+
+/**
+ * Manual cast from 'unsigned int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.h#db_ui2key(unsigned int)
+ */
+DBKey db_ui2key(unsigned int key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_ui2key != (unsigned int)~0) stats.db_ui2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.ui = key;
+ return ret;
+}
+
+/**
+ * Manual cast from 'unsigned char *' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see common\db.h#DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see common\db.h#db_str2key(unsigned char *)
+ */
+DBKey db_str2key(unsigned char *key)
+{
+ DBKey ret;
+
+#ifdef DB_ENABLE_STATS
+ if (stats.db_str2key != (unsigned int)~0) stats.db_str2key++;
+#endif /* DB_ENABLE_STATS */
+ ret.str = key;
+ return ret;
+}
+#endif /* DB_MANUAL_CAST_TO_UNION */
+
+/**
+ * Initialize the database system.
+ * @public
+ * @see #db_final(void)
+ * @see common\db.h#db_init(void)
+ */
+void db_init(void)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_init != (unsigned int)~0) stats.db_init++;
+#endif /* DB_ENABLE_STATS */
+}
+
+/**
+ * Finalize the database system.
+ * Frees the memory used by the block reusage system.
+ * @public
+ * @see common\db.h#DB_FINAL_NODE_CHECK
+ * @see #db_init(void)
+ * @see common\db.h#db_final(void)
+ */
+void db_final(void)
+{
+#ifdef DB_ENABLE_STATS
+ if (stats.db_final != (unsigned int)~0)
+ stats.db_final++;
+ ShowInfo(CL_WHITE"Database nodes"CL_RESET":\n"
+ "allocated %u, freed %u\n",
+ stats.db_node_alloc, stats.db_node_free);
+ ShowInfo(CL_WHITE"Database types"CL_RESET":\n"
+ "DB_INT : allocated %10u, destroyed %10u\n"
+ "DB_UINT : allocated %10u, destroyed %10u\n"
+ "DB_STRING : allocated %10u, destroyed %10u\n"
+ "DB_ISTRING : allocated %10u, destroyed %10u\n",
+ stats.db_int_alloc, stats.db_int_destroy,
+ stats.db_uint_alloc, stats.db_uint_destroy,
+ stats.db_string_alloc, stats.db_string_destroy,
+ stats.db_istring_alloc, stats.db_istring_destroy);
+ ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n"
+ "db_rotate_left %10u, db_rotate_right %10u,\n"
+ "db_rebalance %10u, db_rebalance_erase %10u,\n"
+ "db_is_key_null %10u,\n"
+ "db_dup_key %10u, db_dup_key_free %10u,\n"
+ "db_free_add %10u, db_free_remove %10u,\n"
+ "db_free_lock %10u, db_free_unlock %10u,\n"
+ "db_int_cmp %10u, db_uint_cmp %10u,\n"
+ "db_string_cmp %10u, db_istring_cmp %10u,\n"
+ "db_int_hash %10u, db_uint_hash %10u,\n"
+ "db_string_hash %10u, db_istring_hash %10u,\n"
+ "db_release_nothing %10u, db_release_key %10u,\n"
+ "db_release_data %10u, db_release_both %10u,\n"
+ "db_get %10u,\n"
+ "db_getall %10u, db_vgetall %10u,\n"
+ "db_ensure %10u, db_vensure %10u,\n"
+ "db_put %10u, db_remove %10u,\n"
+ "db_foreach %10u, db_vforeach %10u,\n"
+ "db_clear %10u, db_vclear %10u,\n"
+ "db_destroy %10u, db_vdestroy %10u,\n"
+ "db_size %10u, db_type %10u,\n"
+ "db_options %10u, db_fix_options %10u,\n"
+ "db_default_cmp %10u, db_default_hash %10u,\n"
+ "db_default_release %10u, db_custom_release %10u,\n"
+ "db_alloc %10u, db_i2key %10u,\n"
+ "db_ui2key %10u, db_str2key %10u,\n"
+ "db_init %10u, db_final %10u\n",
+ stats.db_rotate_left, stats.db_rotate_right,
+ stats.db_rebalance, stats.db_rebalance_erase,
+ stats.db_is_key_null,
+ stats.db_dup_key, stats.db_dup_key_free,
+ stats.db_free_add, stats.db_free_remove,
+ stats.db_free_lock, stats.db_free_unlock,
+ stats.db_int_cmp, stats.db_uint_cmp,
+ stats.db_string_cmp, stats.db_istring_cmp,
+ stats.db_int_hash, stats.db_uint_hash,
+ stats.db_string_hash, stats.db_istring_hash,
+ stats.db_release_nothing, stats.db_release_key,
+ stats.db_release_data, stats.db_release_both,
+ stats.db_get,
+ stats.db_getall, stats.db_vgetall,
+ stats.db_ensure, stats.db_vensure,
+ stats.db_put, stats.db_remove,
+ stats.db_foreach, stats.db_vforeach,
+ stats.db_clear, stats.db_vclear,
+ stats.db_destroy, stats.db_vdestroy,
+ stats.db_size, stats.db_type,
+ stats.db_options, stats.db_fix_options,
+ stats.db_default_cmp, stats.db_default_hash,
+ stats.db_default_release, stats.db_custom_release,
+ stats.db_alloc, stats.db_i2key,
+ stats.db_ui2key, stats.db_str2key,
+ stats.db_init, stats.db_final);
+#endif /* DB_ENABLE_STATS */
+}
+
diff --git a/src/common/db.h b/src/common/db.h
new file mode 100644
index 000000000..dcc583bfa
--- /dev/null
+++ b/src/common/db.h
@@ -0,0 +1,734 @@
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * This file is separated in two sections: *
+ * (1) public typedefs, enums, unions, structures and defines *
+ * (2) public functions *
+ * *
+ * <B>Notes on the release system:</B> *
+ * Whenever an entry is removed from the database both the key and the *
+ * data are requested to be released. *
+ * At least one entry is removed when replacing an entry, removing an *
+ * entry, clearing the database or destroying the database. *
+ * What is actually released is defined by the release function, the *
+ * functions of the database only ask for the key and/or data to be *
+ * released. *
+ * *
+ * TODO: *
+ * - create an enum for the data (with int, unsigned int and void *) *
+ * - create a custom database allocator *
+ * - see what functions need or should be added to the database interface *
+ * *
+ * HISTORY: *
+ * 2.1 (Athena build #???#) - Portability fix *
+ * - Fixed the portability of casting to union and added the functions *
+ * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and *
+ * {@link DBInterface#clear(DBInterface,DBApply,...)}. *
+ * 2.0 (Athena build 4859) - Transition version *
+ * - Almost everything recoded with a strategy similar to objects, *
+ * database structure is maintained. *
+ * 1.0 (up to Athena build 4706) *
+ * - Previous database system. *
+ * *
+ * @version 2.1 (Athena build #???#) - Portability fix *
+ * @author (Athena build 4859) Flavio @ Amazon Project *
+ * @author (up to Athena build 4706) Athena Dev Teams *
+ * @encoding US-ASCII *
+ * @see common#db.c *
+\*****************************************************************************/
+#ifndef _DB_H_
+#define _DB_H_
+
+#include <stdarg.h>
+
+/*****************************************************************************\
+ * (1) Section with public typedefs, enums, unions, structures and defines. *
+ * DB_MANUAL_CAST_TO_UNION - Define when the compiler doesn't allow casting *
+ * to unions. *
+ * DBRelease - Enumeration of release options. *
+ * DBType - Enumeration of database types. *
+ * DBOptions - Bitfield enumeration of database options. *
+ * DBKey - Union of used key types. *
+ * DBApply - Format of functions applyed to the databases. *
+ * DBMatcher - Format of matchers used in DBInterface->getall. *
+ * DBComparator - Format of the comparators used by the databases. *
+ * DBHasher - Format of the hashers used by the databases. *
+ * DBReleaser - Format of the releasers used by the databases. *
+ * DBInterface - Structure of the interface of the database. *
+\*****************************************************************************/
+
+/**
+ * Define this to enable the functions that cast to unions.
+ * Required when the compiler doesn't support casting to unions.
+ * NOTE: It is recommened that the conditional tests to determine if this
+ * should be defined be located in a makefile or a header file specific for
+ * of compatibility and portability issues.
+ * @public
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ */
+//#define DB_MANUAL_CAST_TO_UNION
+
+/**
+ * Bitfield with what should be released by the releaser function (if the
+ * function supports it).
+ * @public
+ * @see #DBReleaser
+ * @see #db_custom_release(DBRelease)
+ */
+typedef enum {
+ DB_RELEASE_NOTHING = 0,
+ DB_RELEASE_KEY = 1,
+ DB_RELEASE_DATA = 2,
+ DB_RELEASE_BOTH = 3
+} DBRelease;
+
+/**
+ * Supported types of database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * types of databases.
+ * @param DB_INT Uses int's for keys
+ * @param DB_UINT Uses unsigned int's for keys
+ * @param DB_STRING Uses strings for keys.
+ * @param DB_ISTRING Uses case insensitive strings for keys.
+ * @public
+ * @see #DBOptions
+ * @see #DBKey
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_default_cmp(DBType)
+ * @see #db_default_hash(DBType)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef enum {
+ DB_INT,
+ DB_UINT,
+ DB_STRING,
+ DB_ISTRING
+} DBType;
+
+/**
+ * Bitfield of options that define the behaviour of the database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
+ * types of databases.
+ * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
+ * and does not allow NULL keys or NULL data.
+ * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
+ * is defined, the real key is freed as soon as the entry is added.
+ * @param DB_OPT_RELEASE_KEY Releases the key.
+ * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
+ * from the database.
+ * WARNING: for funtions that return the data (like DBInterface->remove),
+ * a dangling pointer will be returned.
+ * @param DB_OPT_RELEASE_BOTH Releases both key and data.
+ * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
+ * @param DB_OPT_ALLOW_NULL_DATA Allow NULL data in the database.
+ * @public
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef enum {
+ DB_OPT_BASE = 0,
+ DB_OPT_DUP_KEY = 1,
+ DB_OPT_RELEASE_KEY = 2,
+ DB_OPT_RELEASE_DATA = 4,
+ DB_OPT_RELEASE_BOTH = 6,
+ DB_OPT_ALLOW_NULL_KEY = 8,
+ DB_OPT_ALLOW_NULL_DATA = 16,
+} DBOptions;
+
+/**
+ * Union of key types used by the database.
+ * @param i Type of key for DB_INT databases
+ * @param ui Type of key for DB_UINT databases
+ * @param str Type of key for DB_STRING and DB_ISTRING databases
+ * @public
+ * @see #DBType
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBMatcher(DBKey,void *,va_list)
+ * @see #DBComparator(DBKey,DBKey,unsigned short)
+ * @see #DBHasher(DBKey,unsigned short)
+ * @see #DBReleaser(DBKey,void *,DBRelease)
+ * @see DBInterface#get(DBInterface,DBKey)
+ * @see DBInterface#put(DBInterface,DBKey,void *)
+ * @see DBInterface#remove(DBInterface,DBKey)
+ */
+typedef union {
+ int i;
+ unsigned int ui;
+ unsigned char *str;
+} DBKey;
+
+/**
+ * Format of funtions that create the data for the key when the entry doesn't
+ * exist in the database yet.
+ * @param key Key of the database entry
+ * @param args Extra arguments of the funtion
+ * @return Data identified by the key to be put in the database
+ * @public
+ * @see #DBKey
+ * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+typedef void *(*DBCreateData)(DBKey key, va_list args);
+
+/**
+ * Format of functions to be applyed to an unspecified quantity of entries of
+ * a database.
+ * Any function that applyes this function to the database will return the sum
+ * of values returned by this function.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param args Extra arguments of the funtion
+ * @return Value to be added up by the funtion that is applying this
+ * @public
+ * @see #DBKey
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ */
+typedef int (*DBApply)(DBKey key, void *data, va_list args);
+
+/**
+ * Format of functions that match database entries.
+ * The purpose of the match depends on the function that is calling the matcher.
+ * Returns 0 if it is a match, another number otherwise.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param args Extra arguments of the function
+ * @return 0 if a match, another number otherwise
+ * @public
+ * @see #DBKey
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatcher,...)
+ */
+typedef int (*DBMatcher)(DBKey key, void *data, va_list args);
+
+/**
+ * Format of the comparators used internally by the database system.
+ * Compares key1 to key2.
+ * <code>maxlen</code> is the maximum number of character used in DB_STRING and
+ * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
+ * Returns 0 is equal, negative if lower and positive is higher.
+ * @param key1 Key being compared
+ * @param key2 Key we are comparing to
+ * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
+ * databases.
+ * @return 0 if equal, negative if lower and positive if higher
+ * @public
+ * @see #DBKey
+ * @see #db_default_cmp(DBType)
+ */
+typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen);
+
+/**
+ * Format of the hashers used internally by the database system.
+ * Creates the hash of the key.
+ * <code>maxlen</code> is the maximum number of character used in DB_STRING and
+ * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K).
+ * @param key Key being hashed
+ * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING
+ * databases.
+ * @return Hash of the key
+ * @public
+ * @see #DBKey
+ * @see #db_default_hash(DBType)
+ */
+typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen);
+
+/**
+ * Format of the releaser used by the database system.
+ * Releases nothing, the key, the data or both.
+ * All standard releasers use aFree to release.
+ * @param key Key of the database entry
+ * @param data Data of the database entry
+ * @param which What is being requested to be released
+ * @public
+ * @see #DBRelease
+ * @see #DBKey
+ * @see #db_default_releaser(DBType,DBOptions)
+ * @see #db_custom_release(DBRelease)
+ */
+typedef void (*DBReleaser)(DBKey key, void *data, DBRelease which);
+
+/**
+ * Public interface of a database. Only contains funtions.
+ * All the functions take the interface as the first argument.
+ * @public
+ * @see DBInterface#get(DBInterface,DBKey)
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see DBInterface#put(DBInterface,DBKey,void *)
+ * @see DBInterface#remove(DBInterface,DBKey)
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ * @see DBInterface#destroy(DBInterface,DBApply,va_list)
+ * @see DBInterface#size(DBInterface)
+ * @see DBInterface#type(DBInterface)
+ * @see DBInterface#options(DBInterface)
+ * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+typedef struct dbt {
+
+ /**
+ * Get the data of the entry identifid by the key.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @return Data of the entry or NULL if not found
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_get(DBInterface,DBKey)
+ */
+ void *(*get)(struct dbt *dbi, DBKey key);
+
+ /**
+ * Just calls {@link DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}.
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param dbi Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see #DBMatcher(DBKey key, void *data, va_list args)
+ * @see #DBInterface
+ * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ * @see common\db.c#db_getall(DBInterface,void **,unsigned int,DBMatch,...)
+ */
+ unsigned int (*getall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, ...);
+
+ /**
+ * Get the data of the entries matched by <code>match</code>.
+ * It puts a maximum of <code>max</code> entries into <code>buf</code>.
+ * If <code>buf</code> is NULL, it only counts the matches.
+ * Returns the number of entries that matched.
+ * NOTE: if the value returned is greater than <code>max</code>, only the
+ * first <code>max</code> entries found are put into the buffer.
+ * @param dbi Interface of the database
+ * @param buf Buffer to put the data of the matched entries
+ * @param max Maximum number of data entries to be put into buf
+ * @param match Function that matches the database entries
+ * @param ... Extra arguments for match
+ * @return The number of entries that matched
+ * @protected
+ * @see #DBMatcher(DBKey key, void *data, va_list args)
+ * @see #DBInterface
+ * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...)
+ * @see common\db.c#db_vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)
+ */
+ unsigned int (*vgetall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, va_list args);
+
+ /**
+ * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}.
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param ... Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see #DBKey
+ * @see #DBCreateData
+ * @see #DBInterface
+ * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)
+ * @see common\db.c#db_ensure(DBInterface,DBKey,DBCreateData,...)
+ */
+ void *(*ensure)(struct dbt *dbi, DBKey key, DBCreateData create, ...);
+
+ /**
+ * Get the data of the entry identified by the key.
+ * If the entry does not exist, an entry is added with the data returned by
+ * <code>create</code>.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @param create Function used to create the data if the entry doesn't exist
+ * @param args Extra arguments for create
+ * @return Data of the entry
+ * @protected
+ * @see #DBKey
+ * @see #DBCreateData
+ * @see #DBInterface
+ * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)
+ * @see common\db.c#db_vensure(DBInterface,DBKey,DBCreateData,va_list)
+ */
+ void *(*vensure)(struct dbt *dbi, DBKey key, DBCreateData create, va_list args);
+
+ /**
+ * Put the data identified by the key in the database.
+ * Returns the previous data if the entry exists or NULL.
+ * NOTE: Uses the new key, the old one is released.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the data
+ * @param data Data to be put in the database
+ * @return The previous data if the entry exists or NULL
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_put(DBInterface,DBKey,void *)
+ */
+ void *(*put)(struct dbt *dbi, DBKey key, void *data);
+
+ /**
+ * Remove an entry from the database.
+ * Returns the data of the entry.
+ * NOTE: The key (of the database) is released.
+ * @param dbi Interface of the database
+ * @param key Key that identifies the entry
+ * @return The data of the entry or NULL if not found
+ * @protected
+ * @see #DBKey
+ * @see #DBInterface
+ * @see common\db.c#db_remove(DBInterface,DBKey)
+ */
+ void *(*remove)(struct dbt *dbi, DBKey key);
+
+ /**
+ * Just calls {@link DBInterface#vforeach(DBInterface,DBApply,va_list)}.
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed
+ * @param ... Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see #DBInterface
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see DBInterface#vforeach(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_foreach(DBInterface,DBApply,...)
+ */
+ int (*foreach)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Apply <code>func</code> to every entry in the database.
+ * Returns the sum of values returned by func.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed
+ * @param args Extra arguments for func
+ * @return Sum of the values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#foreach(DBInterface,DBApply,...)
+ * @see common\db.c#db_vforeach(DBInterface,DBApply,va_list)
+ */
+ int (*vforeach)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Just calls {@link DBInterface#vclear(DBInterface,DBApply,va_list)}.
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#vclear(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_clear(DBInterface,DBApply,...)
+ */
+ int (*clear)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Removes all entries from the database.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#clear(DBInterface,DBApply,...)
+ * @see common\db.c#vclear(DBInterface,DBApply,va_list)
+ */
+ int (*vclear)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Just calls {@link DBInterface#vdestroy(DBInterface,DBApply,va_list)}.
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Releases the key and the data.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param ... Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see #DBInterface
+ * @see DBInterface#vdestroy(DBInterface,DBApply,va_list)
+ * @see common\db.c#db_destroy(DBInterface,DBApply,...)
+ */
+ int (*destroy)(struct dbt *dbi, DBApply func, ...);
+
+ /**
+ * Finalize the database, feeing all the memory it uses.
+ * Before deleting an entry, func is applyed to it.
+ * Returns the sum of values returned by func, if it exists.
+ * NOTE: This locks the database globally. Any attempt to insert or remove
+ * a database entry will give an error and be aborted (except for clearing).
+ * @param dbi Interface of the database
+ * @param func Function to be applyed to every entry before deleting
+ * @param args Extra arguments for func
+ * @return Sum of values returned by func
+ * @protected
+ * @see #DBInterface
+ * @see #DBApply(DBKey,void *,va_list)
+ * @see DBInterface#destroy(DBInterface,DBApply,...)
+ * @see common\db.c#db_vdestroy(DBInterface,DBApply,va_list)
+ */
+ int (*vdestroy)(struct dbt *dbi, DBApply func, va_list args);
+
+ /**
+ * Return the size of the database (number of items in the database).
+ * @param dbi Interface of the database
+ * @return Size of the database
+ * @protected
+ * @see #DBInterface
+ * @see common\db.c#db_size(DBInterface)
+ */
+ unsigned int (*size)(struct dbt *dbi);
+
+ /**
+ * Return the type of the database.
+ * @param dbi Interface of the database
+ * @return Type of the database
+ * @protected
+ * @see #DBType
+ * @see #DBInterface
+ * @see common\db.c#db_type(DBInterface)
+ */
+ DBType (*type)(struct dbt *dbi);
+
+ /**
+ * Return the options of the database.
+ * @param dbi Interface of the database
+ * @return Options of the database
+ * @protected
+ * @see #DBOptions
+ * @see #DBInterface
+ * @see common\db.c#db_options(DBInterface)
+ */
+ DBOptions (*options)(struct dbt *dbi);
+
+} *DBInterface;
+
+//For easy access to the common functions.
+#ifdef DB_MANUAL_CAST_TO_UNION
+# define i2key db_i2key
+# define ui2key db_ui2key
+# define str2key db_str2key
+#else /* not DB_MANUAL_CAST_TO_UNION */
+# define i2key(k) ((DBKey)(int)(k))
+# define ui2key(k) ((DBKey)(unsigned int)(k))
+# define str2key(k) ((DBKey)(unsigned char *)(k))
+#endif /* DB_MANUAL_CAST_TO_UNION / not DB_MANUAL_CAST_TO_UNION */
+
+#define db_get(db,k) (db)->get((db),(k))
+#define idb_get(db,k) (db)->get((db),i2key(k))
+#define uidb_get(db,k) (db)->get((db),ui2key(k))
+#define strdb_get(db,k) (db)->get((db),str2key(k))
+
+#define db_put(db,k,d) (db)->put((db),(k),(d))
+#define idb_put(db,k,d) (db)->put((db),i2key(k),(d))
+#define uidb_put(db,k,d) (db)->put((db),ui2key(k),(d))
+#define strdb_put(db,k,d) (db)->put((db),str2key(k),(d))
+
+#define db_remove(db,k) (db)->remove((db),(k))
+#define idb_remove(db,k) (db)->remove((db),i2key(k))
+#define uidb_remove(db,k) (db)->remove((db),ui2key(k))
+#define strdb_remove(db,k) (db)->remove((db),str2key(k))
+
+//These are discarding the possible vargs you could send to the function, so those
+//that require vargs must not use these defines.
+#define db_ensure(db,k,f) (db)->ensure((db),(k),f)
+#define idb_ensure(db,k,f) (db)->ensure((db),i2key(k),f)
+#define uidb_ensure(db,k,f) (db)->ensure((db),ui2key(k),f)
+#define strdb_ensure(db,k,f) (db)->ensure((db),str2key(k),f)
+
+/*****************************************************************************\
+ * (2) Section with public functions. *
+ * db_fix_options - Fix the options for a type of database. *
+ * db_default_cmp - Get the default comparator for a type of database. *
+ * db_default_hash - Get the default hasher for a type of database. *
+ * db_default_release - Get the default releaser for a type of database *
+ * with the fixed options. *
+ * db_custom_release - Get the releaser that behaves as specified. *
+ * db_alloc - Allocate a new database. *
+ * db_i2key - Manual cast from 'int' to 'DBKey'. *
+ * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. *
+ * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. *
+ * db_init - Initialise the database system. *
+ * db_final - Finalise the database system. *
+\*****************************************************************************/
+
+/**
+ * Returns the fixed options according to the database type.
+ * Sets required options and unsets unsupported options.
+ * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset.
+ * @param type Type of the database
+ * @param options Original options of the database
+ * @return Fixed options of the database
+ * @private
+ * @see #DBType
+ * @see #DBOptions
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.c#db_fix_options(DBType,DBOptions)
+ */
+DBOptions db_fix_options(DBType type, DBOptions options);
+
+/**
+ * Returns the default comparator for the type of database.
+ * @param type Type of database
+ * @return Comparator for the type of database or NULL if unknown database
+ * @public
+ * @see #DBType
+ * @see #DBComparator
+ * @see common\db.c#db_default_cmp(DBType)
+ */
+DBComparator db_default_cmp(DBType type);
+
+/**
+ * Returns the default hasher for the specified type of database.
+ * @param type Type of database
+ * @return Hasher of the type of database or NULL if unknown database
+ * @public
+ * @see #DBType
+ * @see #DBHasher
+ * @see common\db.c#db_default_hash(DBType)
+ */
+DBHasher db_default_hash(DBType type);
+
+/**
+ * Returns the default releaser for the specified type of database with the
+ * specified options.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before choosing the releaser
+ * @param type Type of database
+ * @param options Options of the database
+ * @return Default releaser for the type of database with the fixed options
+ * @public
+ * @see #DBType
+ * @see #DBOptions
+ * @see #DBReleaser
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see #db_custom_release(DBRelease)
+ * @see common\db.c#db_default_release(DBType,DBOptions)
+ */
+DBReleaser db_default_release(DBType type, DBOptions options);
+
+/**
+ * Returns the releaser that behaves as <code>which</code> specifies.
+ * @param which Defines what the releaser releases
+ * @return Releaser for the specified release options
+ * @public
+ * @see #DBRelease
+ * @see #DBReleaser
+ * @see #db_default_release(DBType,DBOptions)
+ * @see common\db.c#db_custom_release(DBRelease)
+ */
+DBReleaser db_custom_release(DBRelease which);
+
+/**
+ * Allocate a new database of the specified type.
+ * It uses the default comparator, hasher and releaser of the specified
+ * database type and fixed options.
+ * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
+ * before creating the database.
+ * @param file File where the database is being allocated
+ * @param line Line of the file where the database is being allocated
+ * @param type Type of database
+ * @param options Options of the database
+ * @param maxlen Maximum length of the string to be used as key in string
+ * databases
+ * @return The interface of the database
+ * @public
+ * @see #DBType
+ * @see #DBInterface
+ * @see #db_default_cmp(DBType)
+ * @see #db_default_hash(DBType)
+ * @see #db_default_release(DBType,DBOptions)
+ * @see #db_fix_options(DBType,DBOptions)
+ * @see common\db.c#db_alloc(const char *,int,DBType,DBOptions,unsigned short)
+ */
+DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen);
+
+#ifdef DB_MANUAL_CAST_TO_UNION
+/**
+ * Manual cast from 'int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_ui2key(unsigned int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.c#db_i2key(int)
+ */
+DBKey db_i2key(int key);
+
+/**
+ * Manual cast from 'unsigned int' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_str2key(unsigned char *)
+ * @see common\db.c#db_ui2key(unsigned int)
+ */
+DBKey db_ui2key(unsigned int key);
+
+/**
+ * Manual cast from 'unsigned char *' to the union DBKey.
+ * Created for compilers that don't support casting to unions.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ * @see #DB_MANUAL_CAST_TO_UNION
+ * @see #db_i2key(int)
+ * @see #db_ui2key(unsigned int)
+ * @see common\db.c#db_str2key(unsigned char *)
+ */
+DBKey db_str2key(unsigned char *key);
+#endif /* DB_MANUAL_CAST_TO_UNION */
+
+/**
+ * Initialize the database system.
+ * @public
+ * @see #db_final(void)
+ * @see common\db.c#db_init(void)
+ */
+void db_init(void);
+
+/**
+ * Finalize the database system.
+ * Frees the memory used by the block reusage system.
+ * @public
+ * @see #db_init(void)
+ * @see common\db.c#db_final(void)
+ */
+void db_final(void);
+
+#endif
diff --git a/src/common/ers.c b/src/common/ers.c
new file mode 100644
index 000000000..b54d22977
--- /dev/null
+++ b/src/common/ers.c
@@ -0,0 +1,532 @@
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * <H1>Entry Reusage System</H1> *
+ * *
+ * There are several root entry managers, each with a different entry size. *
+ * Each manager will keep track of how many instances have been 'created'. *
+ * They will only automatically destroy themselves after the last instance *
+ * is destroyed. *
+ * *
+ * Entries can be allocated from the managers. *
+ * If it has reusable entries (freed entry), it uses one. *
+ * So no assumption should be made about the data of the entry. *
+ * Entries should be freed in the manager they where allocated from. *
+ * Failure to do so can lead to unexpected behaviours. *
+ * *
+ * <H2>Advantages:</H2> *
+ * - The same manager is used for entries of the same size. *
+ * So entries freed in one instance of the manager can be used by other *
+ * instances of the manager. *
+ * - Much less memory allocation/deallocation - program will be faster. *
+ * - Avoids memory fragmentaion - program will run better for longer. *
+ * *
+ * <H2>Disavantages:</H2> *
+ * - Unused entries are almost inevitable - memory being wasted. *
+ * - A manager will only auto-destroy when all of its instances are *
+ * destroyed so memory will usually only be recovered near the end. *
+ * - Always wastes space for entries smaller than a pointer. *
+ * *
+ * WARNING: The system is not thread-safe at the moment. *
+ * *
+ * HISTORY: *
+ * 0.1 - Initial version *
+ * *
+ * @version 0.1 - Initial version *
+ * @author Flavio @ Amazon Project *
+ * @encoding US-ASCII *
+ * @see common#ers.h *
+\*****************************************************************************/
+#include <stdlib.h>
+
+#include "ers.h"
+#include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
+#include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
+
+#ifndef DISABLE_ERS
+/*****************************************************************************\
+ * (1) Private defines, structures and global variables. *
+ * ERS_BLOCK_ENTRIES - Number of entries in each block. *
+ * ERS_ROOT_SIZE - Maximum number of root entry managers. *
+ * ERLinkedList - Structure of a linked list of reusable entries. *
+ * ERSystem - Class of an entry manager. *
+ * ers_root - Array of root entry managers. *
+ * ers_num - Number of root entry managers in the array. *
+\*****************************************************************************/
+
+/**
+ * Number of entries in each block.
+ * @private
+ * @see #ers_obj_alloc_entry(ERInterface eri)
+ */
+#define ERS_BLOCK_ENTRIES 4096
+
+/**
+ * Maximum number of root entry managers.
+ * @private
+ * @see #ers_root
+ * @see #ers_num
+ */
+#define ERS_ROOT_SIZE 256
+
+/**
+ * Linked list of reusable entries.
+ * The minimum size of the entries is the size of this structure.
+ * @private
+ * @see ERSystem#reuse
+ */
+typedef struct ers_ll {
+ struct ers_ll *next;
+} *ERLinkedList;
+
+/**
+ * Class of the object that manages entries of a certain size.
+ * @param eri Public interface of the object
+ * @param reuse Linked list of reusable data entries
+ * @param blocks Array with blocks of entries
+ * @param free Number of unused entries in the last block
+ * @param num Number of blocks in the array
+ * @param max Current maximum capacity of the array
+ * @param destroy Destroy lock
+ * @param size Size of the entries of the manager
+ * @private
+ */
+typedef struct ers {
+
+ /**
+ * Public interface of the entry manager.
+ * @param alloc Allocate an entry from this manager
+ * @param free Free an entry allocated from this manager
+ * @param entry_size Return the size of the entries of this manager
+ * @param destroy Destroy this instance of the manager
+ * @public
+ * @see #ERSystem
+ * @see common\ers.h#ERInterface
+ */
+ struct eri eri;
+
+ /**
+ * Linked list of reusable entries.
+ * @private
+ * @see #ERSystem
+ */
+ ERLinkedList reuse;
+
+ /**
+ * Array with blocks of entries.
+ * @private
+ * @see #ERSystem
+ */
+ uint8 **blocks;
+
+ /**
+ * Number of unused entries in the last block.
+ * @private
+ * @see #ERSystem
+ */
+ uint32 free;
+
+ /**
+ * Number of blocks in the array.
+ * @private
+ * @see #ERSystem
+ */
+ uint32 num;
+
+ /**
+ * Current maximum capacity of the array.
+ * @private
+ * @see #ERSystem
+ */
+ uint32 max;
+
+ /**
+ * Destroy lock.
+ * @private
+ * @see #ERSystem
+ */
+ uint32 destroy;
+
+ /**
+ * Size of the entries of the manager.
+ * @private
+ * @see #ERSystem
+ */
+ uint32 size;
+
+} *ERSystem;
+
+/**
+ * Root array with entry managers.
+ * @private
+ * @static
+ * @see #ERS_ROOT_SIZE
+ * @see #ers_num
+ */
+static ERSystem ers_root[ERS_ROOT_SIZE];
+
+/**
+ * Number of entry managers in the root array.
+ * @private
+ * @static
+ * @see #ERS_ROOT_SIZE
+ * @see #ers_root
+ */
+static uint32 ers_num = 0;
+
+/*****************************************************************************\
+ * (2) Protected functions. *
+ * ers_obj_alloc_entry - Allocate an entry from the manager. *
+ * ers_obj_free_entry - Free an entry allocated from the manager. *
+ * ers_obj_entry_size - Return the size of the entries of the manager. *
+ * ers_obj_destroy - Destroy the instance of the manager. *
+\*****************************************************************************/
+
+/**
+ * Allocate an entry from this entry manager.
+ * If there are reusable entries available, it reuses one instead.
+ * @param self Interface of the entry manager
+ * @return An entry
+ * @protected
+ * @see #ERS_BLOCK_ENTRIES
+ * @see #ERLinkedList
+ * @see #ERSystem
+ * @see common\ers.h\ERInterface#alloc(ERInterface)
+ */
+static void *ers_obj_alloc_entry(ERInterface self)
+{
+ ERSystem obj = (ERSystem)self;
+ void *ret;
+
+ if (obj == NULL) {
+ ShowError("ers_obj_alloc_entry: NULL object, aborting entry allocation.\n");
+ return NULL;
+ }
+
+ if (obj->reuse) { // Reusable entry
+ ret = obj->reuse;
+ obj->reuse = obj->reuse->next;
+ } else if (obj->free) { // Unused entry
+ obj->free--;
+ ret = &obj->blocks[obj->num -1][obj->free*obj->size];
+ } else { // allocate a new block
+ if (obj->num == obj->max) { // expand the block array
+ if (obj->max == UINT32_MAX) { // No more space for blocks
+ ShowFatalError("ers_obj_alloc_entry: maximum number of blocks reached, increase ERS_BLOCK_ENTRIES.\n"
+ "exiting the program...\n");
+ exit(EXIT_FAILURE);
+ }
+ obj->max = (obj->max<<2) +3; // = obj->max*4 +3; - overflow won't happen
+ RECREATE(obj->blocks, uint8 *, obj->max);
+ }
+ CREATE(obj->blocks[obj->num], uint8, obj->size*ERS_BLOCK_ENTRIES);
+ obj->free = ERS_BLOCK_ENTRIES -1;
+ ret = &obj->blocks[obj->num][obj->free*obj->size];
+ obj->num++;
+ }
+ return ret;
+}
+
+/**
+ * Free an entry allocated from this manager.
+ * WARNING: Does not check if the entry was allocated by this manager.
+ * Freeing such an entry can lead to unexpected behaviour.
+ * @param self Interface of the entry manager
+ * @param entry Entry to be freed
+ * @protected
+ * @see #ERLinkedList
+ * @see #ERSystem
+ * @see ERSystem#reuse
+ * @see common\ers.h\ERInterface#free(ERInterface,void *)
+ */
+static void ers_obj_free_entry(ERInterface self, void *entry)
+{
+ ERSystem obj = (ERSystem)self;
+ ERLinkedList reuse;
+
+ if (obj == NULL) {
+ ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
+ return;
+ } else if (entry == NULL) {
+ ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
+ return;
+ }
+
+ reuse = (ERLinkedList)entry;
+ reuse->next = obj->reuse;
+ obj->reuse = reuse;
+}
+
+/**
+ * Return the size of the entries allocated from this manager.
+ * @param self Interface of the entry manager
+ * @return Size of the entries of this manager in bytes
+ * @protected
+ * @see #ERSystem
+ * @see ERSystem#size
+ * @see common\ers.h\ERInterface#enty_size(ERInterface)
+ */
+static uint32 ers_obj_entry_size(ERInterface self)
+{
+ ERSystem obj = (ERSystem)self;
+
+ if (obj == NULL) {
+ ShowError("ers_obj_entry_size: NULL object, returning 0.\n");
+ return 0;
+ }
+
+ return obj->size;
+}
+
+/**
+ * Destroy this instance of the manager.
+ * The manager is actually only destroyed when all the instances are destroyed.
+ * When destroying the manager a warning is shown if the manager has
+ * missing/extra entries.
+ * @param self Interface of the entry manager
+ * @protected
+ * @see #ERLinkedList
+ * @see #ERSystem
+ * @see common\ers.h\ERInterface#destroy(ERInterface)
+ */
+static void ers_obj_destroy(ERInterface self)
+{
+ ERSystem obj = (ERSystem)self;
+ ERLinkedList reuse;
+ uint32 i, count;
+
+ if (obj == NULL) {
+ ShowError("ers_obj_destroy: NULL object, aborting instance destruction.\n");
+ return;
+ }
+
+ obj->destroy--;
+ if (obj->destroy)
+ return; // Not last instance
+
+ // Remove manager from root array
+ for (i = 0; i < ers_num; i++) {
+ if (ers_root[i] == obj) {
+ ers_num--;
+ if (i < ers_num) // put the last manager in the free slot
+ ers_root[i] = ers_root[ers_num];
+ break;
+ }
+ }
+ reuse = obj->reuse;
+ count = 0;
+ // Check for missing/extra entries
+ for (i = 0; i < obj->num; i++) {
+ if (i == 0) {
+ count = ERS_BLOCK_ENTRIES -obj->free;
+ } else if (count > UINT32_MAX -ERS_BLOCK_ENTRIES) {
+ count = UINT32_MAX;
+ break;
+ } else {
+ count += ERS_BLOCK_ENTRIES;
+ }
+ while (reuse && count) {
+ count--;
+ reuse = reuse->next;
+ }
+ }
+ if (count) { // missing entries
+ ShowWarning("ers_obj_destroy: %u entries missing, continuing destruction.\n"
+ "Manager for entries of size %u.\n",
+ count, obj->size);
+ } else if (reuse) { // extra entries
+ while (reuse && count != UINT32_MAX) {
+ count++;
+ reuse = reuse->next;
+ }
+ ShowWarning("ers_obj_destroy: %u extra entries found, continuing destruction.\n"
+ "Manager for entries of size %u.\n",
+ count, obj->size);
+ }
+ // destroy the entry manager
+ if (obj->max) {
+ for (i = 0; i < obj->num; i++)
+ aFree(obj->blocks[i]); // release block of entries
+ aFree(obj->blocks); // release array of blocks
+ }
+ aFree(obj); // release manager
+}
+
+/*****************************************************************************\
+ * (3) Public functions. *
+ * ers_new - Get a new instance of an entry manager. *
+ * ers_report - Print a report about the current state. *
+ * ers_force_destroy_all - Force the destruction of all the managers. *
+\*****************************************************************************/
+
+/**
+ * Get a new instance of the manager that handles the specified entry size.
+ * Size has to greater than 0.
+ * If the specified size is smaller than a pointer, the size of a pointer is
+ * used instead.
+ * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
+ * ERS_ALIGNED that is greater or equal to size is what's actually used.
+ * @param The requested size of the entry in bytes
+ * @return Interface of the object
+ * @public
+ * @see #ERSystem
+ * @see #ers_root
+ * @see #ers_num
+ * @see common\ers.h#ERInterface
+ * @see common\ers.h\ERInterface#destroy(ERInterface)
+ * @see common\ers.h#ers_new_(uint32)
+ */
+ERInterface ers_new(uint32 size)
+{
+ ERSystem obj;
+ uint32 i;
+
+ if (size == 0) {
+ ShowError("ers_new: invalid size %u, aborting instance creation.\n",
+ size);
+ return NULL;
+ }
+
+ if (size < sizeof(struct ers_ll)) // Minimum size
+ size = sizeof(struct ers_ll);
+ if (size%ERS_ALIGNED) // Align size
+ size += ERS_ALIGNED -size%ERS_ALIGNED;
+
+ for (i = 0; i < ers_num; i++) {
+ obj = ers_root[i];
+ if (obj->size == size) {
+ // found a manager that handles the entry size
+ obj->destroy++;
+ return &obj->eri;
+ }
+ }
+ // create a new manager to handle the entry size
+ if (ers_num == ERS_ROOT_SIZE) {
+ ShowFatalError("ers_alloc: too many root objects, increase ERS_ROOT_SIZE.\n"
+ "exiting the program...\n");
+ exit(EXIT_FAILURE);
+ }
+ obj = (ERSystem)aMalloc(sizeof(struct ers));
+ // Public interface
+ obj->eri.alloc = ers_obj_alloc_entry;
+ obj->eri.free = ers_obj_free_entry;
+ obj->eri.entry_size = ers_obj_entry_size;
+ obj->eri.destroy = ers_obj_destroy;
+ // Block reusage system
+ obj->reuse = NULL;
+ obj->blocks = NULL;
+ obj->free = 0;
+ obj->num = 0;
+ obj->max = 0;
+ obj->destroy = 1;
+ // Properties
+ obj->size = size;
+ ers_root[ers_num++] = obj;
+ return &obj->eri;
+}
+
+/**
+ * Print a report about the current state of the Entry Reusage System.
+ * Shows information about the global system and each entry manager.
+ * The number of entries are checked and a warning is shown if extra reusable
+ * entries are found.
+ * The extra entries are included in the count of reusable entries.
+ * @public
+ * @see #ERLinkedList
+ * @see #ERSystem
+ * @see #ers_root
+ * @see #ers_num
+ * @see common\ers.h#ers_report(void)
+ */
+void ers_report(void)
+{
+ uint32 i, j, used, reusable, extra;
+ ERLinkedList reuse;
+ ERSystem obj;
+
+ // Root system report
+ ShowMessage(CL_BOLD"Entry Reusage System report:\n"CL_NORMAL);
+ ShowMessage("root array size : %u\n", ERS_ROOT_SIZE);
+ ShowMessage("root entry managers : %u\n", ers_num);
+ ShowMessage("entries per block : %u\n", ERS_BLOCK_ENTRIES);
+ for (i = 0; i < ers_num; i++) {
+ obj = ers_root[i];
+ reuse = obj->reuse;
+ used = 0;
+ reusable = 0;
+ // Count used and reusable entries
+ for (j = 0; j < obj->num; j++) {
+ if (j == 0) { // take into acount the free entries
+ used = ERS_BLOCK_ENTRIES -obj->free;
+ } else if (reuse) { // counting reusable entries
+ used = ERS_BLOCK_ENTRIES;
+ } else { // no more reusable entries, count remaining used entries
+ for (; j < obj->num; j++) {
+ if (used > UINT32_MAX -ERS_BLOCK_ENTRIES) { // overflow
+ used = UINT32_MAX;
+ break;
+ }
+ used += ERS_BLOCK_ENTRIES;
+ }
+ break;
+ }
+ while (used && reuse) { // count reusable entries
+ used--;
+ if (reusable != UINT32_MAX)
+ reusable++;
+ reuse = reuse->next;
+ }
+ }
+ // Count extra reusable entries
+ extra = 0;
+ while (reuse && extra != UINT32_MAX) {
+ extra++;
+ reuse = reuse->next;
+ }
+ // Entry manager report
+ ShowMessage(CL_BOLD"[Entry manager #%u report]\n"CL_NORMAL, i);
+ ShowMessage("\tinstances : %u\n", obj->destroy);
+ ShowMessage("\tentry size : %u\n", obj->size);
+ ShowMessage("\tblock array size : %u\n", obj->max);
+ ShowMessage("\tallocated blocks : %u\n", obj->num);
+ ShowMessage("\tentries being used : %u\n", used);
+ ShowMessage("\tunused entries : %u\n", obj->free);
+ ShowMessage("\treusable entries : %u\n", reusable);
+ if (extra)
+ ShowMessage("\tWARNING - %u extra reusable entries were found.\n", extra);
+ }
+ ShowMessage("End of report\n");
+}
+
+/**
+ * Forcibly destroy all the entry managers, checking for nothing.
+ * The system is left as if no instances or entries had ever been allocated.
+ * All previous entries and instances of the managers become invalid.
+ * The use of this is NOT recommended.
+ * It should only be used in extreme situations to make shure all the memory
+ * allocated by this system is released.
+ * @public
+ * @see #ERSystem
+ * @see #ers_root
+ * @see #ers_num
+ * @see common\ers.h#ers_force_destroy_all(void)
+ */
+void ers_force_destroy_all(void)
+{
+ uint32 i, j;
+ ERSystem obj;
+
+ for (i = 0; i < ers_num; i++) {
+ obj = ers_root[i];
+ if (obj->max) {
+ for (j = 0; j < obj->num; j++)
+ aFree(obj->blocks[j]); // block of entries
+ aFree(obj->blocks); // array of blocks
+ }
+ aFree(obj); // entry manager object
+ }
+ ers_num = 0;
+}
+#endif /* not DISABLE_ERS */
+
diff --git a/src/common/ers.h b/src/common/ers.h
new file mode 100644
index 000000000..a512f6365
--- /dev/null
+++ b/src/common/ers.h
@@ -0,0 +1,193 @@
+/*****************************************************************************\
+ * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
+ * For more information, see LICENCE in the main folder *
+ * *
+ * <H1>Entry Reusage System</H1> *
+ * *
+ * There are several root entry managers, each with a different entry size. *
+ * Each manager will keep track of how many instances have been 'created'. *
+ * They will only automatically destroy themselves after the last instance *
+ * is destroyed. *
+ * *
+ * Entries can be allocated from the managers. *
+ * If it has reusable entries (freed entry), it uses one. *
+ * So no assumption should be made about the data of the entry. *
+ * Entries should be freed in the manager they where allocated from. *
+ * Failure to do so can lead to unexpected behaviours. *
+ * *
+ * <H2>Advantages:</H2> *
+ * - The same manager is used for entries of the same size. *
+ * So entries freed in one instance of the manager can be used by other *
+ * instances of the manager. *
+ * - Much less memory allocation/deallocation - program will be faster. *
+ * - Avoids memory fragmentaion - program will run better for longer. *
+ * *
+ * <H2>Disavantages:</H2> *
+ * - Unused entries are almost inevitable - memory being wasted. *
+ * - A manager will only auto-destroy when all of its instances are *
+ * destroyed so memory will usually only be recovered near the end. *
+ * - Always wastes space for entries smaller than a pointer. *
+ * *
+ * WARNING: The system is not thread-safe at the moment. *
+ * *
+ * HISTORY: *
+ * 0.1 - Initial version *
+ * *
+ * @version 0.1 - Initial version *
+ * @author Flavio @ Amazon Project *
+ * @encoding US-ASCII *
+ * @see common#ers.c *
+\*****************************************************************************/
+#ifndef _ERS_H_
+#define _ERS_H_
+
+#include "../common/cbasetypes.h"
+
+/*****************************************************************************\
+ * (1) All public parts of the Entry Reusage System. *
+ * DISABLE_ERS - Define to disable this system. *
+ * ERS_ALIGNED - Alignment of the entries in the blocks. *
+ * ERInterface - Interface of the entry manager. *
+ * ers_new - Allocate an instance of an entry manager. *
+ * ers_report - Print a report about the current state. *
+ * ers_force_destroy_all - Force the destruction of all the managers. *
+\*****************************************************************************/
+
+/**
+ * Define this to disable the Entry Reusage System.
+ * All code except the typedef of ERInterface will be disabled.
+ * To allow a smooth transition,
+ * @public
+ */
+//#define DISABLE_ERS
+
+/**
+ * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
+ * By default it aligns to one byte, using the "natural order" of the entries.
+ * This should NEVER be set to zero or less.
+ * If greater than one, some memory can be wasted. This should never be needed
+ * but is here just in case some aligment issues arise.
+ * @public
+ * @see #ers_new(uint32)
+ */
+#ifndef ERS_ALIGNED
+# define ERS_ALIGNED 1
+#endif /* not ERS_ALIGN_ENTRY */
+
+/**
+ * Public interface of the entry manager.
+ * @param alloc Allocate an entry from this manager
+ * @param free Free an entry allocated from this manager
+ * @param entry_size Return the size of the entries of this manager
+ * @param destroy Destroy this instance of the manager
+ * @public
+ * @see #ers_new(uint32)
+ */
+typedef struct eri {
+
+ /**
+ * Allocate an entry from this entry manager.
+ * If there are reusable entries available, it reuses one instead.
+ * @param self Interface of the entry manager
+ * @return An entry
+ * @protected
+ * @see #ERInterface
+ * @see ERInterface#free(ERInterface,void *)
+ */
+ void *(*alloc)(struct eri *self);
+
+ /**
+ * Free an entry allocated from this manager.
+ * WARNING: Does not check if the entry was allocated by this manager.
+ * Freeing such an entry can lead to unexpected behaviour.
+ * @param self Interface of the entry manager
+ * @param entry Entry to be freed
+ * @protected
+ * @see #ERInterface
+ * @see ERInterface#alloc(ERInterface)
+ */
+ void (*free)(struct eri *self, void *entry);
+
+ /**
+ * Return the size of the entries allocated from this manager.
+ * @param self Interface of the entry manager
+ * @return Size of the entries of this manager in bytes
+ * @protected
+ * @see #ERInterface
+ */
+ uint32 (*entry_size)(struct eri *self);
+
+ /**
+ * Destroy this instance of the manager.
+ * The manager is actually only destroyed when all the instances are destroyed.
+ * When destroying the manager a warning is shown if the manager has
+ * missing/extra entries.
+ * @param self Interface of the entry manager
+ * @protected
+ * @see #ERInterface
+ * @see #ers_new(uint32)
+ */
+ void (*destroy)(struct eri *self);
+
+} *ERInterface;
+
+#ifdef DISABLE_ERS
+// Use memory manager to allocate/free and disable other interface functions
+# define ers_alloc(obj,type) (type *)aMalloc(sizeof(type))
+# define ers_free(obj,entry) aFree(entry)
+# define ers_entry_size(obj) (uint32)0
+# define ers_destroy(obj)
+// Disable the public functions
+# define ers_new(size) NULL
+# define ers_report()
+# define ers_force_destroy_all()
+#else /* not DISABLE_ERS */
+// These defines should be used to allow the code to keep working whenever
+// the system is disabled
+# define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
+# define ers_free(obj,entry) (obj)->free((obj),(entry))
+# define ers_entry_size(obj) (obj)->entry_size(obj)
+# define ers_destroy(obj) (obj)->destroy(obj)
+
+/**
+ * Get a new instance of the manager that handles the specified entry size.
+ * Size has to greater than 0.
+ * If the specified size is smaller than a pointer, the size of a pointer is
+ * used instead.
+ * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
+ * ERS_ALIGNED that is greater or equal to size is what's actually used.
+ * @param The requested size of the entry in bytes
+ * @return Interface of the object
+ * @public
+ * @see #ERS_ALIGNED
+ * @see #ERInterface
+ * @see ERInterface#destroy(ERInterface)
+ * @see common\ers.c#ers_new(uint32)
+ */
+ERInterface ers_new(uint32 size);
+
+/**
+ * Print a report about the current state of the Entry Reusage System.
+ * Shows information about the global system and each entry manager.
+ * The number of entries are checked and a warning is shown if extra reusable
+ * entries are found.
+ * The extra entries are included in the count of reusable entries.
+ * @public
+ * @see common\ers.c#ers_report(void)
+ */
+void ers_report(void);
+
+/**
+ * Forcibly destroy all the entry managers, checking for nothing.
+ * The system is left as if no instances or entries had ever been allocated.
+ * All previous entries and instances of the managers become invalid.
+ * The use of this is NOT recommended.
+ * It should only be used in extreme situations to make shure all the memory
+ * allocated by this system is released.
+ * @public
+ * @see common\ers.c#ers_force_destroy_all(void)
+ */
+void ers_force_destroy_all(void);
+#endif /* DISABLE_ERS / not DISABLE_ERS */
+
+#endif /* _ERS_H_ */
diff --git a/src/common/graph.c b/src/common/graph.c
new file mode 100644
index 000000000..a68d39ce0
--- /dev/null
+++ b/src/common/graph.c
@@ -0,0 +1,318 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// graph creation is enabled
+#define ENABLE_GRAPH
+
+#ifdef ENABLE_GRAPH
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+ #include <unistd.h>
+#endif
+#ifdef MINGW
+ #include <io.h>
+#endif
+
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "graph.h"
+
+struct graph {
+ int width;
+ int height;
+ int pallet_count;
+ int png_len;
+ int png_dirty;
+ unsigned char* raw_data;
+ unsigned char* png_data;
+ int * graph_value;
+ int graph_max;
+};
+
+void graph_write_dword(unsigned char* p,unsigned int v) {
+ p[0] = (unsigned char)((v >> 24) & 0xFF);
+ p[1] = (unsigned char)((v >> 16) & 0xFF);
+ p[2] = (unsigned char)((v >> 8) & 0xFF);
+ p[3] = (unsigned char)(v & 0xFF);
+}
+
+struct graph* graph_create(unsigned int x,unsigned int y) {
+ struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1);
+ if(g == NULL) return NULL;
+ // 256 * 3 : ƒpƒŒƒbƒgƒf[ƒ^
+ // x * y * 2 : ƒCƒ[ƒW‚̃oƒbƒtƒ@
+ // 256 : ƒ`ƒƒƒ“ƒNƒf[ƒ^‚È‚Ç‚Ì—\”õ
+ g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2);
+ g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1);
+ memcpy(
+ g->png_data,
+ "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF"
+ "\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30
+ );
+ graph_write_dword(g->png_data + 0x10,x);
+ graph_write_dword(g->png_data + 0x14,y);
+ graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11));
+ g->pallet_count = 1;
+ g->width = x;
+ g->height = y;
+ g->png_dirty = 1;
+ g->graph_value = (int *) aCalloc(x,sizeof(int));
+ g->graph_max = 1;
+ return g;
+}
+
+void graph_pallet(struct graph* g, int index,unsigned long c) {
+ if(g == NULL || c >= 256) return;
+
+ if(g->pallet_count <= index) {
+ memset(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3);
+ g->pallet_count = index + 1;
+ }
+ g->png_data[0x29 + index * 3 ] = (unsigned char)((c >> 16) & 0xFF); // R
+ g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >> 8) & 0xFF); // G
+ g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c & 0xFF); // B
+ graph_write_dword(g->png_data + 0x21,g->pallet_count * 3);
+ graph_write_dword(
+ g->png_data + 0x29 + g->pallet_count * 3,
+ grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4)
+ );
+ g->png_dirty = 1;
+}
+
+void graph_setpixel(struct graph* g,int x,int y,int color) {
+ if(g == NULL || color >= 256) { return; }
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+ if(x >= g->width) { x = g->width - 1; }
+ if(y >= g->height) { y = g->height - 1; }
+ if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); }
+
+ g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color;
+ g->png_dirty = 1;
+}
+
+int graph_getpixel(struct graph* g,int x,int y) {
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+ if(x >= g->width) { x = g->width - 1; }
+ if(y >= g->height) { y = g->height - 1; }
+ return g->raw_data[y * (g->width + 1) + x + 1];
+}
+
+const unsigned char* graph_output(struct graph* g,int *len) {
+ unsigned long inflate_len;
+ unsigned char *p;
+
+ if(g == NULL) return NULL;
+ if(g->png_dirty == 0) {
+ *len = g->png_len;
+ return g->png_data;
+ }
+
+ p = g->png_data + 0x2D + 3 * g->pallet_count;
+ inflate_len = 2 * (g->width + 1) * g->height;
+ memcpy(p + 4,"IDAT",4);
+ encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height);
+ graph_write_dword(p,inflate_len);
+ graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4));
+
+ p += 0x0C + inflate_len;
+ memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C);
+ p += 0x0C;
+ g->png_len = p - g->png_data;
+ g->png_dirty = 0;
+ *len = g->png_len;
+ return g->png_data;
+}
+
+void graph_free(struct graph* g) {
+ if(g != NULL) {
+ aFree(g->png_data);
+ aFree(g->raw_data);
+ aFree(g->graph_value);
+ aFree(g);
+ }
+}
+
+// ‚Æ‚è‚ ‚¦‚¸•sŒø—¦”ÅBŒã‚Ù‚Ç‘‚«’¼‚µ—\’è
+void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) {
+ int i,j;
+ if(g == NULL) return;
+ if(x < 0) { x = 0; }
+ if(y < 0) { y = 0; }
+ if(xe > g->width) { xe = g->width; }
+ if(ye > g->height) { ye = g->height; }
+ for(i = y;i < ye ; i++) {
+ for(j = x; j < xe ; j++) {
+ graph_setpixel(g,j,i,color);
+ }
+ }
+}
+
+// ‚Æ‚è‚ ‚¦‚¸•sŒø—¦”ÅBŒã‚Ù‚Ç‘‚«’¼‚µ—\’è
+void graph_scroll(struct graph* g,int n,int color) {
+ int x,y;
+ if(g == NULL) return;
+ for(y = 0; y < g->height; y++) {
+ for(x = 0; x < g->width - n; x++) {
+ graph_setpixel(g,x,y,graph_getpixel(g,x + n,y));
+ }
+ for( ; x < g->width; x++) {
+ graph_setpixel(g,x,y,color);
+ }
+ }
+}
+
+void graph_data(struct graph* g,int value) {
+ int i, j, start;
+ if(g == NULL) return;
+ memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1));
+ g->graph_value[g->width - 1] = value;
+ if(value > g->graph_max) {
+ // Å‘å’l‚ð’´‚¦‚½‚Ì‚ÅÄ•`‰æ
+ g->graph_max = value;
+ graph_square(g,0,0,g->width,g->height,0);
+ start = 0;
+ } else {
+ // ƒXƒNƒ[ƒ‹‚µ‚ă|ƒCƒ“ƒg‘Å‚Â
+ graph_scroll(g,1,0);
+ start = g->width - 1;
+ }
+ for(i = start; i < g->width; i++) {
+ int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max;
+ int h1 = (g->graph_value[i] ) * g->height / g->graph_max;
+ int h2 = (h0 < h1 ? 1 : -1);
+ for(j = h0; j != h1; j += h2) {
+ graph_setpixel(g,i,g->height - 1 - j,1);
+ }
+ graph_setpixel(g,i,g->height - 1 - h1,1);
+ }
+}
+
+// ã‚ÌŠÖ”ŒQ‚ð—˜—p‚µ‚ÄAŽ©“®“I‚ɃOƒ‰ƒt‚ð쬂·‚éƒ^ƒCƒ}[ŒQ
+
+#define GRP_WIDTH 300 // ƒOƒ‰ƒt‚Ì•
+#define GRP_HEIGHT 200 // ƒOƒ‰ƒt‚Ì‚‚³
+#define GRP_COLOR graph_rgb(0,0,255) // ƒOƒ‰ƒt‚ÌF
+#define GRP_INTERVEL 60*1000 // ƒOƒ‰ƒt‚ÌXVŠÔŠu
+
+#define GRP_PATH "httpd/"
+
+struct graph_sensor {
+ struct graph* graph;
+ char* str;
+ char hash[32];
+ int scanid;
+ int drawid;
+ int interval;
+ unsigned int (*func)(void);
+};
+
+static struct graph_sensor *sensor;
+static int sensor_max;
+
+static int graph_scan_timer(int tid,unsigned int tick,int id,int data)
+{
+ if(id >= 0 && id < sensor_max)
+ graph_data(sensor[id].graph,sensor[id].func());
+ return 0;
+}
+
+// modified by Celest -- i'm trying to separate it from httpd if possible ^^;
+static int graph_draw_timer(int tid,unsigned int tick,int id,int data)
+{
+ char png_file[24];
+ FILE *fp;
+
+ // create/update the png file
+ do {
+ const char *png_data;
+ int len;
+ sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash);
+ fp = fopen(png_file, "w");
+ // if another png of the same hash exists
+ // (i.e 2nd login server with the same sensors)
+ // this will fail = not good >.<
+ if (fp == NULL)
+ break;
+ png_data = graph_output(sensor[id].graph, &len);
+ fwrite(png_data,1,len,fp);
+ fclose(fp);
+ } while (0);
+
+ // create/update text snippet
+ do {
+ char buf[8192], *p;
+ p = buf;
+ sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash);
+ fp = fopen(png_file, "w");
+ if (fp == NULL)
+ break;
+ p += sprintf(p,"<h2>%s</h2>\n\n",
+ sensor[id].str);
+ p += sprintf(p,"<p><img src=\"%s.png\" width=\"%d\" height=\"%d\"></p>\n",
+ sensor[id].hash, GRP_WIDTH,GRP_HEIGHT);
+ p += sprintf(p,"<p>Max: %d, Interval: %d sec</p>\n\n",
+ sensor[id].graph->graph_max, sensor[id].interval / 1000);
+ fprintf(fp, buf);
+ fclose(fp);
+ } while (0);
+
+ return 0;
+}
+
+void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void))
+{
+ int draw_interval = interval * 2;
+ struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT);
+ graph_pallet(g,1,GRP_COLOR);
+
+ sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1));
+ sensor[sensor_max].graph = g;
+ sensor[sensor_max].str = aStrdup(string);
+ // create crc32 hash of the sensor's name
+ sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE);
+ sensor[sensor_max].func = callback_func;
+ sensor[sensor_max].scanid = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval);
+ sensor[sensor_max].drawid = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval);
+ sensor[sensor_max].interval = interval;
+ sensor_max++;
+
+}
+
+void graph_final (void)
+{
+ int i;
+ for(i = 0; i < sensor_max; i++) {
+ char png_file[24];
+ // remove the png and snippet file
+ sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash);
+ unlink (png_file);
+ sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash);
+ unlink (png_file);
+ graph_free(sensor[i].graph);
+ aFree(sensor[i].str);
+ //delete_timer(sensor[i].scanid,graph_scan_timer);
+ //delete_timer(sensor[i].drawid,graph_draw_timer);
+ }
+ aFree(sensor);
+ sensor_max = 0;
+}
+
+void graph_init (void)
+{
+ graph_add_sensor ("Memory Usage", 1000, malloc_usage);
+ add_timer_func_list(graph_scan_timer, "graph_scan_timer");
+ add_timer_func_list(graph_draw_timer, "graph_draw_timer");
+}
+
+#else
+void graph_init (void) {}
+void graph_final (void) {}
+#endif
diff --git a/src/common/graph.h b/src/common/graph.h
new file mode 100644
index 000000000..6c80dd41c
--- /dev/null
+++ b/src/common/graph.h
@@ -0,0 +1,27 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+void graph_init (void);
+void graph_final (void);
+
+struct graph* graph_create(unsigned int x,unsigned int y);
+void graph_pallet(struct graph* g, int index,unsigned long c);
+const unsigned char* graph_output(struct graph* g,int *len);
+void graph_setpixel(struct graph* g,int x,int y,int color);
+void graph_scroll(struct graph* g,int n,int color);
+void graph_square(struct graph* g,int x,int y,int xe,int ye,int color);
+
+// athena‚Ìó‘Ԃ𒲸‚·‚éƒZƒ“ƒT[‚ð’ljÁ‚·‚éB
+// string : ƒZƒ“ƒT[‚Ì–¼Ì(Login Users ‚È‚Ç)
+// inetrval : ƒZƒ“ƒT[‚Ì’l‚ðŠ“¾‚·‚éŠÔŠu(msec)
+// callback_func : ƒZƒ“ƒT[‚Ì’l‚ð•Ô‚·ŠÖ”( unsigned int login_users(void); ‚È‚Ç)
+
+void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void));
+
+#define graph_rgb(r,g,b) (((r) << 16) | ((g) << 8) | (b))
+
+#endif
+
diff --git a/src/common/grfio.c b/src/common/grfio.c
new file mode 100644
index 000000000..a3907a7f2
--- /dev/null
+++ b/src/common/grfio.c
@@ -0,0 +1,1146 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+/*********************************************************************
+ *
+ * Ragnarok Online Emulator : grfio.c -- grf file I/O Module
+ *--------------------------------------------------------------------
+ * special need library : zlib
+ *********************************************************************
+ * $Id: grfio.c,v 1.2 2004/09/29 17:31:49 kalaspuff Exp $
+ *
+ * 2002/12/18... the original edition
+ * 2003/01/23 ... Code correction
+ * 2003/02/01 ... An addition and decryption processing are improved for LocalFile and two or more GRF(s) check processing.
+ * 2003/02/02 ... Even if there is no grf it does not stop -- as -- correction
+ * 2003/02/02... grf reading specification can be added later -- as -- correction (grfio_add function addition)
+ * 2003/02 / 03... at the time of grfio_resourcecheck processing the entry addition processing method -- correction
+ * 2003/02/05... change of the processing in grfio_init
+ * 2003/02/23... a local file check -- GRFIO_LOCAL -- switch (Defoe -- Function Off)
+ * 2003/10/21 ... The data of alpha client was read.
+ * 2003/11/10 ... Ready new grf format.
+ * 2003/11/11 ... version check fix & bug fix
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "grfio.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+#include "../zlib/unzip.h"
+
+#define CHUNK 16384
+
+#ifdef __WIN32
+ #include "../zlib/zlib.h"
+ #include "../zlib/iowin32.h"
+#else
+ #include <zlib.h>
+#endif
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+//static char data_file[1024] = ""; // "data.grf";
+//static char sdata_file[1024] = ""; // "sdata.grf";
+//static char adata_file[1024] = ""; // "adata.grf";
+static char data_dir[1024] = ""; // "../";
+
+//----------------------------
+// file entry table struct
+//----------------------------
+typedef struct {
+ int srclen; // compressed size
+ int srclen_aligned; //
+ int declen; // original size
+ int srcpos;
+ short next;
+ char cycle;
+ char type;
+ char fn[128-4*5]; // file name
+ char gentry; // read grf file select
+} FILELIST;
+//gentry ... 0 : It acquires from a local file.
+// It acquires from the resource file of 1>=:gentry_table[gentry-1].
+// 1<=: Check a local file.
+// If it is, after re-setting to 0, it acquires from a local file.
+// If there is nothing, mark reversal will be carried out, and it will re-set, and will acquire from a resource file as well as 1>=.
+
+//Since char defines *FILELIST.gentry, the maximum which can be added by grfio_add becomes by 127 pieces.
+
+#define GENTRY_LIMIT 127
+#define FILELIST_LIMIT 65536 // temporary maximum, and a theory top maximum are 2G.
+
+static FILELIST *filelist = NULL;
+static int filelist_entrys = 0;
+static int filelist_maxentry = 0;
+
+static char **gentry_table = NULL;
+static int gentry_entrys = 0;
+static int gentry_maxentry = 0;
+
+#define RESNAME_LIMIT 1024
+#define RESNAME_ADDS 16
+
+typedef struct resname_entry {
+ char src[64];
+ char dst[64];
+} Resname;
+static struct resname_entry *localresname = NULL;
+static int resname_entrys = 0;
+static int resname_maxentrys = 0;
+
+//----------------------------
+// file list hash table
+//----------------------------
+static int filelist_hash[256];
+
+//----------------------------
+// grf decode data table
+//----------------------------
+static unsigned char BitMaskTable[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static char BitSwapTable1[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
+};
+static char BitSwapTable2[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
+};
+static char BitSwapTable3[32] = {
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
+};
+
+static unsigned char NibbleData[4][64]={
+ {
+ 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e,
+ 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85,
+ 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72,
+ 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9,
+ }, {
+ 0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3,
+ 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19,
+ 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78,
+ 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce,
+ }, {
+ 0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15,
+ 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68,
+ 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda,
+ 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d,
+ }, {
+ 0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4,
+ 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62,
+ 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d,
+ 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb,
+ }
+};
+/*-----------------
+ * long data get
+ */
+static unsigned int getlong(unsigned char *p)
+{
+// return *p+p[1]*256+(p[2]+p[3]*256)*65536;
+ return p[0]
+ | p[1] << 0x08
+ | p[2] << 0x10
+ | p[3] << 0x18; // Shinomori
+}
+
+/*==========================================
+ * Grf data decode : Subs
+ *------------------------------------------
+ */
+static void NibbleSwap(BYTE *Src, int len)
+{
+ for(;0<len;len--,Src++) {
+ *Src = (*Src>>4) | (*Src<<4);
+ }
+}
+
+static void BitConvert(BYTE *Src,char *BitSwapTable)
+{
+ int lop,prm;
+ BYTE tmp[8];
+// *(DWORD*)tmp=*(DWORD*)(tmp+4)=0;
+ memset(tmp,0,8);
+ for(lop=0;lop!=64;lop++) {
+ prm = BitSwapTable[lop]-1;
+ if (Src[(prm >> 3) & 7] & BitMaskTable[prm & 7]) {
+ tmp[(lop >> 3) & 7] |= BitMaskTable[lop & 7];
+ }
+ }
+// *(DWORD*)Src = *(DWORD*)tmp;
+// *(DWORD*)(Src+4) = *(DWORD*)(tmp+4);
+ memcpy(Src,tmp,8);
+}
+
+static void BitConvert4(BYTE *Src)
+{
+ int lop,prm;
+ BYTE tmp[8];
+ tmp[0] = ((Src[7]<<5) | (Src[4]>>3)) & 0x3f; // ..0 vutsr
+ tmp[1] = ((Src[4]<<1) | (Src[5]>>7)) & 0x3f; // ..srqpo n
+ tmp[2] = ((Src[4]<<5) | (Src[5]>>3)) & 0x3f; // ..o nmlkj
+ tmp[3] = ((Src[5]<<1) | (Src[6]>>7)) & 0x3f; // ..kjihg f
+ tmp[4] = ((Src[5]<<5) | (Src[6]>>3)) & 0x3f; // ..g fedcb
+ tmp[5] = ((Src[6]<<1) | (Src[7]>>7)) & 0x3f; // ..cba98 7
+ tmp[6] = ((Src[6]<<5) | (Src[7]>>3)) & 0x3f; // ..8 76543
+ tmp[7] = ((Src[7]<<1) | (Src[4]>>7)) & 0x3f; // ..43210 v
+
+ for(lop=0;lop!=4;lop++) {
+ tmp[lop] = (NibbleData[lop][tmp[lop*2]] & 0xf0)
+ | (NibbleData[lop][tmp[lop*2+1]] & 0x0f);
+ }
+
+ *(DWORD*)(tmp+4)=0;
+ for(lop=0;lop!=32;lop++) {
+ prm = BitSwapTable3[lop]-1;
+ if (tmp[prm >> 3] & BitMaskTable[prm & 7]) {
+ tmp[(lop >> 3) + 4] |= BitMaskTable[lop & 7];
+ }
+ }
+// *(DWORD*)Src ^= *(DWORD*)(tmp+4);
+ Src[0] ^= tmp[4];
+ Src[1] ^= tmp[5];
+ Src[2] ^= tmp[6];
+ Src[3] ^= tmp[7];
+}
+
+static void decode_des_etc(BYTE *buf,int len,int type,int cycle)
+{
+ int lop,cnt=0;
+ if(cycle<3) cycle=3;
+ else if(cycle<5) cycle++;
+ else if(cycle<7) cycle+=9;
+ else cycle+=15;
+
+ for(lop=0;lop*8<len;lop++,buf+=8) {
+ if(lop<20 || (type==0 && lop%cycle==0)){ // des
+ BitConvert(buf,BitSwapTable1);
+ BitConvert4(buf);
+ BitConvert(buf,BitSwapTable2);
+ } else {
+ if(cnt==7 && type==0){
+ int a;
+ BYTE tmp[8];
+ *(DWORD*)tmp = *(DWORD*)buf;
+ *(DWORD*)(tmp+4) = *(DWORD*)(buf+4);
+ cnt=0;
+ buf[0]=tmp[3];
+ buf[1]=tmp[4];
+ buf[2]=tmp[6];
+ buf[3]=tmp[0];
+ buf[4]=tmp[1];
+ buf[5]=tmp[2];
+ buf[6]=tmp[5];
+ a=tmp[7];
+ if(a==0x00) a=0x2b;
+ else if(a==0x2b) a=0x00;
+ else if(a==0x01) a=0x68;
+ else if(a==0x68) a=0x01;
+ else if(a==0x48) a=0x77;
+ else if(a==0x77) a=0x48;
+ else if(a==0x60) a=0xff;
+ else if(a==0xff) a=0x60;
+ else if(a==0x6c) a=0x80;
+ else if(a==0x80) a=0x6c;
+ else if(a==0xb9) a=0xc0;
+ else if(a==0xc0) a=0xb9;
+ else if(a==0xeb) a=0xfe;
+ else if(a==0xfe) a=0xeb;
+ buf[7]=a;
+ }
+ cnt++;
+ }
+ }
+}
+/*==========================================
+ * Grf data decode sub : zip
+ *------------------------------------------
+ */
+int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen)
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = (Bytef*) dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
+
+int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen) {
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = (Bytef*) dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = deflateInit(&stream,Z_DEFAULT_COMPRESSION);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/*==========================================
+* Decompress from file source to file dest until stream ends or EOF.
+* inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+* allocated for processing, Z_DATA_ERROR if the deflate data is
+* invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+* the version of the library linked do not match, or Z_ERRNO if there
+* is an error reading or writing the files.
+*
+* Version 1.2 9 November 2004 Mark Adler
+*------------------------------------------
+*/
+int decode_file (FILE *source, FILE *dest)
+{
+ int err;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate inflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+
+ err = inflateInit(&strm);
+ if (err != Z_OK) return 0; //return err;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ inflateEnd(&strm);
+ return 0;
+ }
+ if (strm.avail_in == 0)
+ break;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ err = inflate(&strm, Z_NO_FLUSH);
+ Assert(err != Z_STREAM_ERROR); /* state not clobbered */
+ switch (err) {
+ case Z_NEED_DICT:
+ err = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ //return err;
+ return 0;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ inflateEnd(&strm);
+ //return Z_ERRNO;
+ return 0;
+ }
+ } while (strm.avail_out == 0);
+ Assert(strm.avail_in == 0); /* all input will be used */
+
+ /* done when inflate() says it's done */
+ } while (err != Z_STREAM_END);
+
+ /* clean up and return */
+ inflateEnd(&strm);
+ return err == Z_STREAM_END ? 1 : 0;
+}
+
+/* ===================================
+* Unzips a file. 1: success, 0: error
+* Adapted from miniunz.c [Celest]
+* Version 1.01b, May 30th, 2004
+* Copyright (C) 1998-2004 Gilles Vollant
+* -------------------------------------
+*/
+int deflate_file (const char *source, const char *filename)
+{
+#ifdef _WIN32
+ zlib_filefunc_def ffunc;
+#endif
+ unzFile uf = NULL;
+ int err = UNZ_OK;
+ uInt size_buf = 8192;
+ FILE *fout = NULL;
+ void *buf;
+
+#ifdef _WIN32
+ fill_win32_filefunc(&ffunc);
+ uf = unzOpen2(source, &ffunc);
+#else
+ uf = unzOpen(source);
+#endif
+
+ if (uf == NULL) {
+ //printf("Cannot open %s\n", source);
+ return 0;
+ }
+ //printf("%s opened\n", source);
+
+ if (unzLocateFile(uf, filename, 0) != UNZ_OK) {
+ //printf("file %s not found in the zipfile\n", filename);
+ return 0;
+ }
+
+ err = unzOpenCurrentFilePassword(uf, NULL);
+ //if (err != UNZ_OK)
+ // printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
+
+ fout = fopen(filename,"wb");
+ if (fout == NULL) {
+ //printf("error opening %s\n", filename);
+ return 0;
+ }
+
+ buf = (void *)aMalloc(size_buf);
+ do {
+ err = unzReadCurrentFile(uf, buf, size_buf);
+ if (err < 0) {
+ //printf("error %d with zipfile in unzReadCurrentFile\n", err);
+ break;
+ }
+ if (err > 0 &&
+ fwrite(buf, err, 1, fout)!=1)
+ {
+ //printf("error in writing extracted file\n");
+ err = UNZ_ERRNO;
+ break;
+ }
+ } while (err > 0);
+
+ if (fout) fclose(fout);
+
+ if (err == UNZ_OK) {
+ err = unzCloseCurrentFile (uf);
+ //if (err != UNZ_OK)
+ // printf("error %d with zipfile in unzCloseCurrentFile\n", err);
+ aFree(buf);
+ return (err == UNZ_OK);
+ }
+
+ unzCloseCurrentFile(uf); /* don't lose the error */
+
+ return 0;
+}
+
+unsigned long grfio_crc32 (const char *buf, unsigned int len)
+{
+ return crc32(crc32(0L, Z_NULL, 0), buf, len);
+}
+
+/***********************************************************
+ *** File List Sobroutines ***
+ ***********************************************************/
+
+/*==========================================
+ * File List : Hash make
+ *------------------------------------------
+ */
+static int filehash(unsigned char *fname)
+{
+ unsigned int hash=0;
+ while(*fname) {
+ hash = ((hash<<1)+(hash>>7)*9+tolower(*fname));
+ fname++;
+ }
+ return hash & 255;
+}
+
+/*==========================================
+ * File List : Hash initalize
+ *------------------------------------------
+ */
+static void hashinit(void)
+{
+ int lop;
+ for (lop = 0; lop < 256; lop++)
+ filelist_hash[lop] = -1;
+}
+
+/*==========================================
+ * File List : File find
+ *------------------------------------------
+ */
+FILELIST *filelist_find(char *fname)
+{
+ int hash;
+
+ if (!filelist)
+ return NULL;
+
+ for (hash = filelist_hash[filehash((unsigned char *) fname)]; hash >= 0; hash = filelist[hash].next) {
+ if(strcmpi(filelist[hash].fn, fname) == 0)
+ break;
+ }
+
+ return (hash >= 0) ? &filelist[hash] : NULL;
+}
+
+/*==========================================
+ * File List : Filelist add
+ *------------------------------------------
+ */
+#define FILELIST_ADDS 1024 // number increment of file lists `
+
+static FILELIST* filelist_add(FILELIST *entry)
+{
+ int hash;
+
+ if (filelist_entrys >= FILELIST_LIMIT) {
+ ShowFatalError("filelist limit : filelist_add\n");
+ exit(1);
+ }
+
+ if (filelist_entrys >= filelist_maxentry) {
+ filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST));
+ memset(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST));
+ filelist_maxentry += FILELIST_ADDS;
+ }
+
+ memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST));
+
+ hash = filehash((unsigned char *) entry->fn);
+ filelist[filelist_entrys].next = filelist_hash[hash];
+ filelist_hash[hash] = filelist_entrys;
+
+ filelist_entrys++;
+
+ return &filelist[filelist_entrys - 1];
+}
+
+static FILELIST* filelist_modify(FILELIST *entry)
+{
+ FILELIST *fentry;
+ if ((fentry = filelist_find(entry->fn)) != NULL) {
+ int tmp = fentry->next;
+ memcpy(fentry, entry, sizeof(FILELIST));
+ fentry->next = tmp;
+ } else {
+ fentry = filelist_add(entry);
+ }
+ return fentry;
+}
+
+/*==========================================
+ * File List : filelist size adjust
+ *------------------------------------------
+ */
+static void filelist_adjust(void)
+{
+ if (filelist != NULL) {
+ if (filelist_maxentry > filelist_entrys) {
+ filelist = (FILELIST *)aRealloc(
+ filelist, filelist_entrys * sizeof(FILELIST));
+ filelist_maxentry = filelist_entrys;
+ }
+ }
+}
+
+/***********************************************************
+ *** Grfio Sobroutines ***
+ ***********************************************************/
+/*==========================================
+ * Grfio : Local Resnametable replace
+ *------------------------------------------
+ */
+static void grfio_resnametable(char *src, char *dest)
+{
+ int lop;
+ if (localresname == NULL ||
+ sscanf(src, "%*5s%s", dest) < 1)
+ {
+ // if not found copy the unresolved name into buffer
+ strcpy(dest, src);
+ return;
+ }
+
+ for (lop = 0; lop < resname_entrys; lop++) {
+ if (strcmpi(localresname[lop].src, dest) == 0) {
+ sprintf(dest, "data\\%s", localresname[lop].dst);
+ return;
+ }
+ }
+
+ return;
+}
+
+/*==========================================
+ * Grfio : Local Resnametable Initialize
+ *------------------------------------------
+ */
+static void grfio_resnameinit (void)
+{
+ FILE *fp;
+ char *p;
+ // max length per entry is 34 in resnametable
+ char w1[64], w2[64], restable[256], line[256];
+
+ sprintf(restable, "%sdata\\resnametable.txt", data_dir);
+ for (p = &restable[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/';
+
+ fp = fopen(restable,"rb");
+ if (fp == NULL) {
+ //ShowError("%s not found (grfio_resnameinit)\n", restable);
+ return;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)){
+ if (sscanf(line, "%[^#]#%[^#]#", w1, w2) != 2)
+ continue;
+ // only save up necessary resource files
+ if (strstr(w1, ".gat") == NULL &&
+ strstr(w1, ".txt") == NULL)
+ continue;
+ if (resname_entrys >= RESNAME_LIMIT)
+ break;
+ if (resname_entrys >= resname_maxentrys) {
+ resname_maxentrys += RESNAME_ADDS;
+ localresname = (Resname*) aRealloc (localresname, resname_maxentrys * sizeof(Resname));
+ memset(localresname + (resname_maxentrys - RESNAME_ADDS), '\0', sizeof(Resname) * RESNAME_ADDS);
+ }
+ strcpy(localresname[resname_entrys].src, w1);
+ strcpy(localresname[resname_entrys].dst, w2);
+ resname_entrys++;
+ }
+ fclose(fp);
+
+ // free up unused sections
+ if (resname_maxentrys > resname_entrys) {
+ localresname = (Resname*) aRealloc (localresname, resname_entrys * sizeof(Resname));
+ resname_maxentrys = resname_entrys;
+ }
+}
+
+/*==========================================
+ * Grfio : Resource file size get
+ *------------------------------------------
+ */
+int grfio_size(char *fname)
+{
+ FILELIST *entry;
+
+ entry = filelist_find(fname);
+
+ if (entry == NULL || entry->gentry < 0) { // LocalFileCheck
+ char lfname[256], rname[256], *p;
+ FILELIST lentry;
+ struct stat st;
+
+ grfio_resnametable(fname, rname);
+ sprintf(lfname, "%s%s", data_dir, rname);
+
+ for (p = &lfname[0]; *p != 0; p++)
+ if (*p=='\\') *p = '/'; // * At the time of Unix
+
+ if (stat(lfname, &st) == 0) {
+ strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
+ lentry.declen = st.st_size;
+ lentry.gentry = 0; // 0:LocalFile
+ entry = filelist_modify(&lentry);
+ } else if (entry == NULL) {
+ ShowError("%s not found (grfio_size)\n", fname);
+ //exit(1);
+ return -1;
+ }
+ }
+ return entry->declen;
+}
+
+/*==========================================
+ * Grfio : Resource file read & size get
+ *------------------------------------------
+ */
+void* grfio_reads(char *fname, int *size)
+{
+ FILE *in;
+ FILELIST *entry;
+ unsigned char *buf2 = NULL;
+
+ entry = filelist_find(fname);
+
+ if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck
+ char lfname[256], rname[256], *p;
+ FILELIST lentry;
+
+ // resolve filename into rname
+ grfio_resnametable(fname, rname);
+ sprintf(lfname, "%s%s", data_dir, rname);
+
+ for (p = &lfname[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ in = fopen(lfname, "rb");
+ if (in != NULL) {
+ if (entry != NULL && entry->gentry == 0) {
+ lentry.declen = entry->declen;
+ } else {
+ fseek(in,0,2); // SEEK_END
+ lentry.declen = ftell(in);
+ }
+ fseek(in,0,0); // SEEK_SET
+ buf2 = (unsigned char *)aCallocA(lentry.declen + 1024, 1);
+ fread(buf2, 1, lentry.declen, in);
+ fclose(in);
+ strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1);
+ lentry.gentry = 0; // 0:LocalFile
+ entry = filelist_modify(&lentry);
+ } else {
+ if (entry != NULL && entry->gentry < 0) {
+ entry->gentry = -entry->gentry; // local file checked
+ } else {
+ ShowError("%s not found (grfio_reads - local file %s)\n", fname, lfname);
+ return NULL;
+ }
+ }
+ }
+ if (entry != NULL && entry->gentry > 0) { // Archive[GRF] File Read
+ char *gfname = gentry_table[entry->gentry - 1];
+ in = fopen(gfname, "rb");
+ if(in != NULL) {
+ unsigned char *buf = (unsigned char *)aCallocA(entry->srclen_aligned + 1024, 1);
+ fseek(in, entry->srcpos, 0);
+ fread(buf, 1, entry->srclen_aligned, in);
+ fclose(in);
+ buf2 = (unsigned char *)aCallocA(entry->declen + 1024, 1);
+ if (entry->type == 1 || entry->type == 3 || entry->type == 5) {
+ uLongf len;
+ if (entry->cycle >= 0)
+ decode_des_etc(buf, entry->srclen_aligned, entry->cycle == 0, entry->cycle);
+ len = entry->declen;
+ decode_zip(buf2, &len, buf, entry->srclen);
+ if (len != entry->declen) {
+ ShowError("decode_zip size miss match err: %d != %d\n", (int)len, entry->declen);
+ aFree(buf);
+ aFree(buf2);
+ return NULL;
+ }
+ } else {
+ memcpy(buf2, buf, entry->declen);
+ }
+ aFree(buf);
+ } else {
+ ShowError("%s not found (grfio_reads - grf file %s)\n", fname, gfname);
+ return NULL;
+ }
+ }
+ if (size != NULL && entry != NULL)
+ *size = entry->declen;
+
+ return buf2;
+}
+
+/*==========================================
+ * Grfio : Resource file read
+ *------------------------------------------
+ */
+void* grfio_read(char *fname)
+{
+ return grfio_reads(fname, NULL);
+}
+
+/*==========================================
+ * Resource filename decode
+ *------------------------------------------
+ */
+static char * decode_filename(unsigned char *buf,int len)
+{
+ int lop;
+ for(lop=0;lop<len;lop+=8) {
+ NibbleSwap(&buf[lop],8);
+ BitConvert(&buf[lop],BitSwapTable1);
+ BitConvert4(&buf[lop]);
+ BitConvert(&buf[lop],BitSwapTable2);
+ }
+ return (char*)buf;
+}
+
+/*==========================================
+ * Grfio : Entry table read
+ *------------------------------------------
+ */
+static int grfio_entryread(char *gfname,int gentry)
+{
+ FILE *fp;
+ long grf_size,list_size;
+ unsigned char grf_header[0x2e];
+ int lop,entry,entrys,ofs,grf_version;
+ char *fname;
+ unsigned char *grf_filelist;
+
+ fp = fopen(gfname, "rb");
+ if (fp == NULL) {
+ ShowWarning("GRF Data File not found: '"CL_WHITE"%s"CL_RESET"'.\n",gfname);
+ return 1; // 1:not found error
+ }
+
+ fseek(fp,0,2); // SEEK_END
+ grf_size = ftell(fp);
+ fseek(fp,0,0); // SEEK_SET
+ fread(grf_header,1,0x2e,fp);
+ if (strcmp((const char *) grf_header,"Master of Magic") ||
+ fseek(fp,getlong(grf_header+0x1e),1)) // SEEK_CUR
+ {
+ fclose(fp);
+ ShowError("%s read error\n",gfname);
+ return 2; // 2:file format error
+ }
+
+ grf_version = getlong(grf_header+0x2a) >> 8;
+
+ if (grf_version == 0x01) { //****** Grf version 01xx ******
+ list_size = grf_size - ftell(fp);
+ grf_filelist = (unsigned char *) aCallocA(list_size, 1);
+ /*if (grf_filelist == NULL){
+ fclose(fp);
+ ShowError("out of memory : grf_filelist\n");
+ return 3; // 3:memory alloc error
+ }*/
+ fread(grf_filelist,1,list_size,fp);
+ fclose(fp);
+
+ entrys = getlong(grf_header+0x26) - getlong(grf_header+0x22) - 7;
+
+ // Get an entry
+ for (entry = 0,ofs = 0; entry < entrys; entry++) {
+ int ofs2, srclen, srccount, type;
+ char *period_ptr;
+ FILELIST aentry;
+
+ ofs2 = ofs+getlong(grf_filelist+ofs)+4;
+ type = grf_filelist[ofs2+12];
+ if (type != 0) { // Directory Index ... skip
+ fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6);
+ if (strlen(fname) > sizeof(aentry.fn) - 1) {
+ ShowFatalError("file name too long : %s\n",fname);
+ aFree(grf_filelist);
+ exit(1);
+ }
+ srclen = 0;
+ if ((period_ptr = strrchr(fname, '.')) != NULL) {
+ for(lop = 0; lop < 4; lop++) {
+ if (strcmpi(period_ptr, ".gnd\0.gat\0.act\0.str"+lop*5) == 0)
+ break;
+ }
+ srclen = getlong(grf_filelist+ofs2) - getlong(grf_filelist+ofs2+8) - 715;
+ if(lop == 4) {
+ for(lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
+ } else {
+ srccount = 0;
+ }
+ } else {
+ srccount = 0;
+ }
+
+ aentry.srclen = srclen;
+ aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579;
+ aentry.declen = getlong(grf_filelist+ofs2+8);
+ aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
+ aentry.cycle = srccount;
+ aentry.type = type;
+ strncpy(aentry.fn, fname,sizeof(aentry.fn)-1);
+#ifdef GRFIO_LOCAL
+ aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
+#else
+ aentry.gentry = gentry+1; // With no first time LocalFileCheck
+#endif
+ filelist_modify(&aentry);
+ }
+ ofs = ofs2 + 17;
+ }
+ aFree(grf_filelist);
+
+ } else if (grf_version == 0x02) { //****** Grf version 02xx ******
+ unsigned char eheader[8];
+ unsigned char *rBuf;
+ uLongf rSize, eSize;
+
+ fread(eheader,1,8,fp);
+ rSize = getlong(eheader); // Read Size
+ eSize = getlong(eheader+4); // Extend Size
+
+ if ((long)rSize > grf_size-ftell(fp)) { // Warning fix [Lance]
+ fclose(fp);
+ ShowError("Illegal data format : grf compress entry size\n");
+ return 4;
+ }
+
+ rBuf = (unsigned char *)aCallocA(rSize , 1); // Get a Read Size
+ /*if (rBuf==NULL) {
+ fclose(fp);
+ ShowError("out of memory : grf compress entry table buffer\n");
+ return 3;
+ }*/
+ grf_filelist = (unsigned char *)aCallocA(eSize , 1); // Get a Extend Size
+ /*if (grf_filelist==NULL) {
+ aFree(rBuf);
+ fclose(fp);
+ ShowError("out of memory : grf extract entry table buffer\n");
+ return 3;
+ }*/
+ fread(rBuf,1,rSize,fp);
+ fclose(fp);
+ decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function
+ list_size = eSize;
+ aFree(rBuf);
+
+ entrys = getlong(grf_header+0x26) - 7;
+
+ // Get an entry
+ for(entry = 0, ofs = 0; entry < entrys; entry++){
+ int ofs2, srclen, srccount, type;
+ FILELIST aentry;
+
+ fname = (char*)(grf_filelist+ofs);
+ if (strlen(fname) > sizeof(aentry.fn)-1) {
+ ShowFatalError("grf : file name too long : %s\n",fname);
+ aFree(grf_filelist);
+ exit(1);
+ }
+ //ofs2 = ofs+strlen((char*)(grf_filelist+ofs))+1;
+ ofs2 = ofs + strlen(fname)+1;
+ type = grf_filelist[ofs2+12];
+ if (type == 1 || type == 3 || type == 5) {
+ srclen = getlong(grf_filelist+ofs2);
+ if (grf_filelist[ofs2+12] == 3) {
+ for (lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++);
+ } else if (grf_filelist[ofs2+12] == 5) {
+ srccount = 0;
+ } else { // if (grf_filelist[ofs2+12]==1) {
+ srccount = -1;
+ }
+
+ aentry.srclen = srclen;
+ aentry.srclen_aligned = getlong(grf_filelist+ofs2+4);
+ aentry.declen = getlong(grf_filelist+ofs2+8);
+ aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
+ aentry.cycle = srccount;
+ aentry.type = type;
+ strncpy(aentry.fn,fname,sizeof(aentry.fn)-1);
+#ifdef GRFIO_LOCAL
+ aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
+#else
+ aentry.gentry = gentry+1; // With no first time LocalFileCheck
+#endif
+ filelist_modify(&aentry);
+ }
+ ofs = ofs2 + 17;
+ }
+ aFree(grf_filelist);
+
+ } else { //****** Grf Other version ******
+ fclose(fp);
+ ShowError("not support grf versions : %04x\n",getlong(grf_header+0x2a));
+ return 4;
+ }
+
+ filelist_adjust(); // Unnecessary area release of filelist
+
+ return 0; // 0:no error
+}
+
+/*==========================================
+ * Grfio : Resource file check
+ *------------------------------------------
+ */
+static void grfio_resourcecheck(void)
+{
+ int size;
+ char *buf, *ptr;
+ char w1[256], w2[256], src[256], dst[256];
+ FILELIST *entry;
+
+ buf = (char *)grfio_reads("data\\resnametable.txt", &size);
+ if (buf == NULL)
+ return;
+ buf[size] = 0;
+
+ for (ptr = buf; ptr - buf < size;) {
+ if (sscanf(ptr,"%[^#]#%[^#]#",w1,w2) == 2) {
+ if (strstr(w2, "bmp")) {
+ sprintf(src, "data\\texture\\%s", w1);
+ sprintf(dst, "data\\texture\\%s", w2);
+ } else {
+ sprintf(src, "data\\%s", w1);
+ sprintf(dst, "data\\%s", w2);
+ }
+ entry = filelist_find(dst);
+ if (entry != NULL) {
+ FILELIST fentry;
+ memcpy(&fentry, entry, sizeof(FILELIST));
+ strncpy(fentry.fn, src, sizeof(fentry.fn) - 1);
+ filelist_modify(&fentry);
+ } else {
+ //ShowError("file not found in data.grf : %s < %s\n",dst,src);
+ }
+ }
+ ptr = strchr(ptr,'\n'); // Next line
+ if (!ptr) break;
+ ptr++;
+ }
+ aFree(buf);
+ filelist_adjust(); // Unnecessary area release of filelist
+}
+
+/*==========================================
+ * Grfio : Resource add
+ *------------------------------------------
+ */
+#define GENTRY_ADDS 16 // The number increment of gentry_table entries
+
+int grfio_add(char *fname)
+{
+ int len,result;
+ char *buf;
+
+ if (gentry_entrys >= GENTRY_LIMIT) {
+ ShowFatalError("gentrys limit : grfio_add\n");
+ exit(1);
+ }
+
+ if (gentry_entrys >= gentry_maxentry) {
+ gentry_maxentry += GENTRY_ADDS;
+ gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*));
+ memset(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS);
+ }
+ len = strlen( fname );
+ buf = (char*)aCallocA(len + 1, 1);
+ strcpy(buf, fname);
+ gentry_table[gentry_entrys++] = buf;
+
+ result = grfio_entryread(fname, gentry_entrys - 1);
+ if (result == 0)
+ // Resource check
+ grfio_resourcecheck();
+
+ return result;
+}
+
+/*==========================================
+ * Grfio : Finalize
+ *------------------------------------------
+ */
+void grfio_final(void)
+{
+ if (filelist != NULL)
+ aFree(filelist);
+ filelist_entrys = filelist_maxentry = 0;
+
+ if (gentry_table != NULL) {
+ int lop;
+ for (lop = 0; lop < gentry_entrys; lop++) {
+ if (gentry_table[lop] != NULL)
+ aFree(gentry_table[lop]);
+ }
+ aFree(gentry_table);
+ }
+ gentry_table = NULL;
+ gentry_entrys = gentry_maxentry = 0;
+
+ if (localresname) aFree(localresname);
+}
+
+/*==========================================
+ * Grfio : Initialize
+ *------------------------------------------
+ */
+void grfio_init(char *fname)
+{
+ FILE *data_conf;
+ char line[1024], w1[1024], w2[1024];
+ int result = 0;
+
+ hashinit(); // hash table initialization
+
+ data_conf = fopen(fname, "r");
+ // It will read, if there is grf-files.txt.
+ if (data_conf) {
+ while(fgets(line, sizeof(line) - 1, data_conf)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+ // Entry table reading
+ if(strcmp(w1, "grf") == 0 ||
+ strcmp(w1, "data") == 0 || // Primary data file
+ strcmp(w1, "sdata") == 0 || // Sakray data file
+ strcmp(w1, "adata") == 0) // Alpha version data file
+ // increment if successfully loaded
+ result += (grfio_add(w2) == 0);
+ else if(strcmp(w1,"data_dir") == 0) // Data directory
+ strcpy(data_dir, w2);
+ }
+
+ fclose(data_conf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname);
+ } // end of reading grf-files.txt
+
+ if (result == 0) {
+ ShowInfo("No grf's loaded.. using default data directory\n");
+ //exit(1); // It ends, if a resource cannot read one.
+ }
+
+ // initialise Resnametable
+ grfio_resnameinit();
+
+ return;
+}
diff --git a/src/common/grfio.h b/src/common/grfio.h
new file mode 100644
index 000000000..a7faafc1c
--- /dev/null
+++ b/src/common/grfio.h
@@ -0,0 +1,21 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GRFIO_H_
+#define _GRFIO_H_
+
+void grfio_init(char*); // GRFIO Initialize
+void grfio_final(void); // GRFIO Finalize
+int grfio_add(char*); // GRFIO Resource file add
+void* grfio_read(char*); // GRFIO data file read
+void* grfio_reads(char*,int*); // GRFIO data file read & size get
+int grfio_size(char*); // GRFIO data file size get
+unsigned long grfio_crc32(const char *buf, unsigned int len);
+
+int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
+int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen);
+int decode_file (FILE *source, FILE *dest);
+
+int deflate_file (const char *source, const char *filename);
+
+#endif // _GRFIO_H_
diff --git a/src/common/lock.c b/src/common/lock.c
new file mode 100644
index 000000000..c7bf623e5
--- /dev/null
+++ b/src/common/lock.c
@@ -0,0 +1,71 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <windows.h>
+#define F_OK 0x0
+#define R_OK 0x4
+#endif
+#include "lock.h"
+#include "showmsg.h"
+
+#ifndef _WIN32
+ #define exists(filename) (!access(filename, F_OK))
+#else
+// could be speed up maybe?
+int exists(char *file) {
+ FILE *fp;
+ if ((fp = fopen(file,"r")) && fclose(fp) == 0) return 1;
+ return 0;
+}
+#endif
+
+// ‘‚«ž‚݃tƒ@ƒCƒ‹‚̕ی숗
+// i‘‚«ž‚Ý‚ªI‚í‚é‚Ü‚ÅA‹Œƒtƒ@ƒCƒ‹‚ð•ÛŠÇ‚µ‚Ä‚¨‚­j
+
+// V‚µ‚¢ƒtƒ@ƒCƒ‹‚Ì‘‚«ž‚ÝŠJŽn
+FILE* lock_fopen (const char* filename, int *info) {
+ char newfile[512];
+ FILE *fp;
+ int no = 0;
+
+ // ˆÀ‘S‚ȃtƒ@ƒCƒ‹–¼‚𓾂éiŽè”²‚«j
+ do {
+ sprintf(newfile, "%s_%04d.tmp", filename, ++no);
+ } while((fp = fopen(newfile,"r")) && (fclose(fp), no < 9999));
+ *info = no;
+ return fopen(newfile,"w");
+}
+
+// ‹Œƒtƒ@ƒCƒ‹‚ð휕Vƒtƒ@ƒCƒ‹‚ðƒŠƒl[ƒ€
+int lock_fclose (FILE *fp, const char* filename, int *info) {
+ int ret = 1;
+ char newfile[512];
+ char oldfile[512];
+ if (fp != NULL) {
+ ret = fclose(fp);
+ sprintf(newfile, "%s_%04d.tmp", filename, *info);
+ sprintf(oldfile, "%s.bak", filename); // old backup file
+
+ if (exists(oldfile)) remove(oldfile); // remove backup file if it already exists
+ rename (filename, oldfile); // backup our older data instead of deleting it
+
+ // ‚±‚̃^ƒCƒ~ƒ“ƒO‚Å—Ž‚¿‚é‚Æň«B
+ if ((ret = rename(newfile,filename)) != 0) { // rename our temporary file to its correct name
+#if defined(__NETBSD__) || defined(_WIN32) || defined(sun) || defined (_sun) || defined (__sun__)
+ ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror(errno), newfile);
+#else
+ char ebuf[255];
+ ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror_r(errno, ebuf, sizeof(ebuf)), newfile);
+#endif
+ }
+ }
+
+ return ret;
+}
+
diff --git a/src/common/lock.h b/src/common/lock.h
new file mode 100644
index 000000000..5c846eb73
--- /dev/null
+++ b/src/common/lock.h
@@ -0,0 +1,11 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOCK_H_
+#define _LOCK_H_
+
+FILE* lock_fopen(const char* filename,int *info);
+int lock_fclose(FILE *fp,const char* filename,int *info);
+
+#endif
+
diff --git a/src/common/malloc.c b/src/common/malloc.c
new file mode 100644
index 000000000..1e3af2d40
--- /dev/null
+++ b/src/common/malloc.c
@@ -0,0 +1,715 @@
+// 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 "malloc.h"
+#include "../common/core.h"
+#include "../common/showmsg.h"
+
+#ifdef MINICORE
+ #undef LOG_MEMMGR
+#endif
+
+void* aMalloc_ (size_t size, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ void *ret = MALLOC(size);
+#else
+ void *ret = mwMalloc(size, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: malloc %d\n",file,line,func,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: malloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+
+ return ret;
+}
+void* aMallocA_ (size_t size, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ void *ret = MALLOCA(size);
+#else
+ void *ret = mwMalloc(size, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: malloc %d\n",file,line,func,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: malloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+
+ return ret;
+}
+void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ void *ret = CALLOC(num, size);
+#else
+ void *ret = mwCalloc(num, size, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: calloc %d %d\n",file,line,func,num,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: calloc error out of memory!\n", file, line, func);
+ exit(1);
+ }
+ return ret;
+}
+void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ void *ret = CALLOCA(num, size);
+#else
+ void *ret = mwCalloc(num, size, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: calloc %d %d\n",file,line,func,num,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: calloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+ return ret;
+}
+void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ void *ret = REALLOC(p, size);
+#else
+ void *ret = mwRealloc(p, size, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: realloc %p %d\n",file,line,func,p,size);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: realloc error out of memory!\n",file,line,func);
+ exit(1);
+ }
+ return ret;
+}
+char* aStrdup_ (const char *p, const char *file, int line, const char *func)
+{
+#ifndef MEMWATCH
+ char *ret = STRDUP(p);
+#else
+ char *ret = mwStrdup(p, file, line);
+#endif
+ // ShowMessage("%s:%d: in func %s: strdup %p\n",file,line,func,p);
+ if (ret == NULL){
+ ShowFatalError("%s:%d: in func %s: strdup error out of memory!\n", file, line, func);
+ exit(1);
+ }
+ return ret;
+}
+void aFree_ (void *p, const char *file, int line, const char *func)
+{
+ // ShowMessage("%s:%d: in func %s: free %p\n",file,line,func,p);
+ if (p)
+ #ifndef MEMWATCH
+ FREE(p);
+ #else
+ mwFree(p, file, line);
+ #endif
+
+ p = NULL;
+}
+
+#ifdef GCOLLECT
+
+void* _bcallocA(size_t size, size_t cnt)
+{
+ void *ret = MALLOCA(size * cnt);
+ if (ret) memset(ret, 0, size * cnt);
+ return ret;
+}
+void* _bcalloc(size_t size, size_t cnt)
+{
+ void *ret = MALLOC(size * cnt);
+ if (ret) memset(ret, 0, size * cnt);
+ return ret;
+}
+char* _bstrdup(const char *chr)
+{
+ int len = strlen(chr);
+ char *ret = (char*)MALLOC(len + 1);
+ if (ret) memcpy(ret, chr, len + 1);
+ return ret;
+}
+
+#endif
+
+#ifdef USE_MEMMGR
+
+/* USE_MEMMGR */
+
+/*
+ * ƒƒ‚ƒŠƒ}ƒl[ƒWƒƒ
+ * malloc , free ‚̈—‚ðŒø—¦“I‚Éo—ˆ‚é‚悤‚É‚µ‚½‚à‚ÌB
+ * •¡ŽG‚Ȉ—‚ðs‚Á‚Ä‚¢‚é‚Ì‚ÅAŽáŠ±d‚­‚È‚é‚©‚à‚µ‚ê‚Ü‚¹‚ñB
+ *
+ * ƒf[ƒ^\‘¢‚È‚Çià–¾‰ºŽè‚Å‚·‚¢‚Ü‚¹‚ñ^^; j
+ * Eƒƒ‚ƒŠ‚ð•¡”‚ÌuƒuƒƒbƒNv‚É•ª‚¯‚ÄA‚³‚ç‚ɃuƒƒbƒN‚ð•¡”‚Ìuƒ†ƒjƒbƒgv
+ * ‚É•ª‚¯‚Ä‚¢‚Ü‚·Bƒ†ƒjƒbƒg‚̃TƒCƒY‚ÍA‚PƒuƒƒbƒN‚Ì—e—Ê‚ð•¡”ŒÂ‚É‹Ï“™”z•ª
+ * ‚µ‚½‚à‚Ì‚Å‚·B‚½‚Æ‚¦‚ÎA‚Pƒ†ƒjƒbƒg32KB‚Ìê‡AƒuƒƒbƒN‚P‚‚Í32Byte‚̃†
+ * ƒjƒbƒg‚ªA1024ŒÂW‚Ü‚Á‚Äo—ˆ‚Ä‚¢‚½‚èA64Byte‚̃†ƒjƒbƒg‚ª 512ŒÂW‚Ü‚Á‚Ä
+ * o—ˆ‚Ä‚¢‚½‚肵‚Ü‚·Bipadding,unit_head ‚𜂭j
+ *
+ * EƒuƒƒbƒN“¯Žm‚̓Šƒ“ƒNƒŠƒXƒg(block_prev,block_next) ‚ł‚Ȃª‚èA“¯‚¶ƒTƒC
+ * ƒY‚ðŽ‚ƒuƒƒbƒN“¯Žm‚àƒŠƒ“ƒNƒŠƒXƒg(samesize_prev,samesize_nect) ‚ł‚È
+ * ‚ª‚Á‚Ä‚¢‚Ü‚·B‚»‚ê‚É‚æ‚èA•s—v‚Æ‚È‚Á‚½ƒƒ‚ƒŠ‚ÌÄ—˜—p‚ªŒø—¦“I‚És‚¦‚Ü‚·B
+ */
+
+/* ƒuƒƒbƒN‚É“ü‚éƒf[ƒ^—Ê */
+#define BLOCK_DATA_SIZE 80*1024
+
+/* ˆê“x‚ÉŠm•Û‚·‚éƒuƒƒbƒN‚Ì”B */
+#define BLOCK_ALLOC 32
+
+/* ƒuƒƒbƒN‚̃Aƒ‰ƒCƒƒ“ƒg */
+#define BLOCK_ALIGNMENT 64
+
+/* ƒuƒƒbƒN */
+struct block {
+ int block_no; /* ƒuƒƒbƒN”Ô† */
+ struct block* block_prev; /* ‘O‚ÉŠm•Û‚µ‚½—̈æ */
+ struct block* block_next; /* ŽŸ‚ÉŠm•Û‚µ‚½—̈æ */
+ int samesize_no; /* “¯‚¶ƒTƒCƒY‚̔Ԇ */
+ struct block* samesize_prev; /* “¯‚¶ƒTƒCƒY‚Ì‘O‚̗̈æ */
+ struct block* samesize_next; /* “¯‚¶ƒTƒCƒY‚ÌŽŸ‚̗̈æ */
+ size_t unit_size; /* ƒ†ƒjƒbƒg‚̃oƒCƒg” 0=–¢Žg—p */
+ size_t unit_hash; /* ƒ†ƒjƒbƒg‚̃nƒbƒVƒ… */
+ int unit_count; /* ƒ†ƒjƒbƒg‚Ì” */
+ int unit_used; /* Žg—pς݃†ƒjƒbƒg */
+ char data[BLOCK_DATA_SIZE];
+};
+
+struct unit_head {
+ struct block* block;
+ size_t size;
+ const char* file;
+ int line;
+ unsigned int checksum;
+};
+
+struct chunk {
+ char *block;
+ struct chunk *next;
+};
+
+static struct block* block_first = NULL;
+static struct block* block_last = NULL;
+static struct block* block_unused = NULL;
+
+/* ƒ†ƒjƒbƒg‚ւ̃nƒbƒVƒ…B80KB/64Byte = 1280ŒÂ */
+static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* ʼn */
+static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* –„‚Ü‚Á‚Ä‚È‚¢ */
+static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* ÅŒã */
+
+/* ƒƒ‚ƒŠ‚ðŽg‚¢‰ñ‚¹‚È‚¢—̈æ—p‚̃f[ƒ^ */
+struct unit_head_large {
+ struct unit_head_large* prev;
+ struct unit_head_large* next;
+ struct unit_head unit_head;
+};
+static struct unit_head_large *unit_head_large_first = NULL;
+
+static struct chunk *chunk_first = NULL;
+
+static struct block* block_malloc(void);
+static void block_free(struct block* p);
+static void memmgr_info(void);
+static unsigned int memmgr_usage_bytes = 0;
+
+void* _mmalloc(size_t size, const char *file, int line, const char *func ) {
+ int i;
+ struct block *block;
+ size_t size_hash;
+
+ if (((long) size) < 0) {
+ printf("_mmalloc: %d\n", size);
+ return 0;
+ }
+
+ size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
+ if(size == 0) {
+ return NULL;
+ }
+ memmgr_usage_bytes += size;
+
+ /* ƒuƒƒbƒN’·‚ð’´‚¦‚é—̈æ‚ÌŠm•Û‚É‚ÍAmalloc() ‚ð—p‚¢‚é */
+ /* ‚»‚ÌÛAunit_head.block ‚É NULL ‚ð‘ã“ü‚µ‚Ä‹æ•Ê‚·‚é */
+ if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
+#ifdef MEMWATCH
+ struct unit_head_large* p = (struct unit_head_large*)mwMalloc(sizeof(struct unit_head_large) + size,file,line);
+#else
+ struct unit_head_large* p = (struct unit_head_large*) MALLOC (sizeof(struct unit_head_large) + size);
+#endif
+ if(p != NULL) {
+ p->unit_head.block = NULL;
+ p->unit_head.size = size;
+ p->unit_head.file = file;
+ p->unit_head.line = line;
+ p->prev = NULL;
+ if (unit_head_large_first == NULL)
+ p->next = NULL;
+ else {
+ unit_head_large_first->prev = p;
+ p->next = unit_head_large_first;
+ }
+ unit_head_large_first = p;
+ *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf;
+ return (char *)p + sizeof(struct unit_head_large) - sizeof(int);
+ } else {
+ ShowFatalError("Memory manager::memmgr_alloc failed.\n");
+ exit(1);
+ }
+ }
+
+ /* “¯ˆêƒTƒCƒY‚̃uƒƒbƒN‚ªŠm•Û‚³‚ê‚Ä‚¢‚È‚¢ŽžAV‚½‚ÉŠm•Û‚·‚é */
+ if(unit_unfill[size_hash] == NULL) {
+ block = block_malloc();
+ if(unit_first[size_hash] == NULL) {
+ /* ‰‰ñŠm•Û */
+ unit_first[size_hash] = block;
+ unit_last[size_hash] = block;
+ block->samesize_no = 0;
+ block->samesize_prev = NULL;
+ block->samesize_next = NULL;
+ } else {
+ /* ˜AŒ‹ì‹Æ */
+ unit_last[size_hash]->samesize_next = block;
+ block->samesize_no = unit_last[size_hash]->samesize_no + 1;
+ block->samesize_prev = unit_last[size_hash];
+ block->samesize_next = NULL;
+ unit_last[size_hash] = block;
+ }
+ unit_unfill[size_hash] = block;
+ block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head);
+ block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size);
+ block->unit_used = 0;
+ block->unit_hash = size_hash;
+ /* –¢Žg—pFlag‚𗧂Ăé */
+ for(i=0;i<block->unit_count;i++) {
+ ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL;
+ }
+ }
+ /* ƒ†ƒjƒbƒgŽg—pŒÂ”‰ÁŽZ */
+ block = unit_unfill[size_hash];
+ block->unit_used++;
+
+ /* ƒ†ƒjƒbƒg“à‚ð‘S‚ÄŽg‚¢‰Ê‚½‚µ‚½ */
+ if(block->unit_count == block->unit_used) {
+ do {
+ unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next;
+ } while(
+ unit_unfill[size_hash] != NULL &&
+ unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used
+ );
+ }
+
+ /* ƒuƒƒbƒN‚Ì’†‚̋󂫃†ƒjƒbƒg‘{õ */
+ for(i=0;i<block->unit_count;i++) {
+ struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
+ if(head->block == NULL) {
+ head->block = block;
+ head->size = size;
+ head->line = line;
+ head->file = file;
+ *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf;
+ return (char *)head + sizeof(struct unit_head) - sizeof(int);
+ }
+ }
+ // ‚±‚±‚É—ˆ‚Ä‚Í‚¢‚¯‚È‚¢B
+ ShowFatalError("Memory manager::memmgr_malloc() serious error.\n");
+ memmgr_info();
+ exit(1);
+ return NULL;
+};
+
+void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) {
+ void *p = _mmalloc(num * size,file,line,func);
+ memset(p,0,num * size);
+ return p;
+}
+
+void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) {
+ size_t old_size;
+ if(memblock == NULL) {
+ return _mmalloc(size,file,line,func);
+ }
+
+ old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size;
+ if(old_size > size) {
+ // ƒTƒCƒYk¬ -> ‚»‚Ì‚Ü‚Ü•Ô‚·iŽè”²‚«j
+ return memblock;
+ } else {
+ // ƒTƒCƒYŠg‘å
+ void *p = _mmalloc(size,file,line,func);
+ if(p != NULL) {
+ memcpy(p,memblock,old_size);
+ }
+ _mfree(memblock,file,line,func);
+ return p;
+ }
+}
+
+char* _mstrdup(const char *p, const char *file, int line, const char *func ) {
+ if(p == NULL) {
+ return NULL;
+ } else {
+ size_t len = strlen(p);
+ char *string = (char *)_mmalloc(len + 1,file,line,func);
+ memcpy(string,p,len+1);
+ return string;
+ }
+}
+
+void _mfree(void *ptr, const char *file, int line, const char *func ) {
+ struct unit_head *head;
+ size_t size_hash;
+
+ if (ptr == NULL)
+ return;
+
+ head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int));
+ size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT;
+
+ if(head->block == NULL) {
+ if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) {
+ /* malloc() ‚Å’¼‚ÉŠm•Û‚³‚ꂽ—̈æ */
+ struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int));
+ if(
+ *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size)
+ != 0xdeadbeaf)
+ {
+ ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
+ }
+ if(head_large->prev) {
+ head_large->prev->next = head_large->next;
+ } else {
+ unit_head_large_first = head_large->next;
+ }
+ if(head_large->next) {
+ head_large->next->prev = head_large->prev;
+ }
+ head->block = NULL;
+ memmgr_usage_bytes -= head->size;
+ FREE (head_large);
+ } else {
+ ShowError("Memory manager: args of aFree is freed pointer %s line %d\n", file, line);
+ }
+ ptr = NULL;
+ return;
+ } else {
+ /* ƒ†ƒjƒbƒg‰ð•ú */
+ struct block *block = head->block;
+ if((unsigned long)block % sizeof(struct block) != 0) {
+ ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line);
+ } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) {
+ ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line);
+ } else {
+ head->block = NULL;
+ memmgr_usage_bytes -= head->size;
+ if(--block->unit_used == 0) {
+ /* ƒuƒƒbƒN‚̉ð•ú */
+ if(unit_unfill[block->unit_hash] == block) {
+ /* ‹ó‚«ƒ†ƒjƒbƒg‚ÉŽw’肳‚ê‚Ä‚¢‚é */
+ do {
+ unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next;
+ } while(
+ unit_unfill[block->unit_hash] != NULL &&
+ unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used
+ );
+ }
+ if(block->samesize_prev == NULL && block->samesize_next == NULL) {
+ /* “Æ—§ƒuƒƒbƒN‚̉ð•ú */
+ unit_first[block->unit_hash] = NULL;
+ unit_last[block->unit_hash] = NULL;
+ unit_unfill[block->unit_hash] = NULL;
+ } else if(block->samesize_prev == NULL) {
+ /* 擪ƒuƒƒbƒN‚̉ð•ú */
+ unit_first[block->unit_hash] = block->samesize_next;
+ (block->samesize_next)->samesize_prev = NULL;
+ } else if(block->samesize_next == NULL) {
+ /* ––’[ƒuƒƒbƒN‚̉ð•ú */
+ unit_last[block->unit_hash] = block->samesize_prev;
+ (block->samesize_prev)->samesize_next = NULL;
+ } else {
+ /* ’†ŠÔƒuƒƒbƒN‚̉ð•ú */
+ (block->samesize_next)->samesize_prev = block->samesize_prev;
+ (block->samesize_prev)->samesize_next = block->samesize_next;
+ }
+ block_free(block);
+ } else {
+ /* ‹ó‚«ƒ†ƒjƒbƒg‚ÌÄÝ’è */
+ if(
+ unit_unfill[block->unit_hash] == NULL ||
+ unit_unfill[block->unit_hash]->samesize_no > block->samesize_no
+ ) {
+ unit_unfill[block->unit_hash] = block;
+ }
+ }
+ ptr = NULL;
+ }
+ }
+}
+
+/* Œ»Ý‚Ì󋵂ð•\Ž¦‚·‚é */
+static void memmgr_info(void) {
+ int i;
+ struct block *p;
+ ShowInfo("** Memory Manager Information **\n");
+ if(block_first == NULL) {
+ ShowMessage("Uninitialized.\n");
+ return;
+ }
+ ShowMessage(
+ "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n",
+ block_last->block_no+1,sizeof(struct block),
+ (block_last->block_no+1) * sizeof(struct block) / 1024
+ );
+ p = block_first;
+ for(i=0;i<=block_last->block_no;i++) {
+ ShowMessage(" Block #%04u : ",p->block_no);
+ if(p->unit_size == 0) {
+ ShowMessage("unused.\n");
+ } else {
+ ShowMessage(
+ "size: %05u byte. used: %04u/%04u prev:",
+ p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count
+ );
+ if(p->samesize_prev == NULL) {
+ ShowMessage("NULL");
+ } else {
+ ShowMessage("%04u",(p->samesize_prev)->block_no);
+ }
+ ShowMessage(" next:");
+ if(p->samesize_next == NULL) {
+ ShowMessage("NULL");
+ } else {
+ ShowMessage("%04u",(p->samesize_next)->block_no);
+ }
+ ShowMessage("\n");
+ }
+ p = p->block_next;
+ }
+}
+
+/* ƒuƒƒbƒN‚ðŠm•Û‚·‚é */
+static struct block* block_malloc(void) {
+ if(block_unused != NULL) {
+ /* ƒuƒƒbƒN—p‚̗̈æ‚ÍŠm•ÛÏ‚Ý */
+ struct block* ret = block_unused;
+ do {
+ block_unused = block_unused->block_next;
+ } while(block_unused != NULL && block_unused->unit_size != 0);
+ return ret;
+ } else {
+ /* ƒuƒƒbƒN—p‚̗̈æ‚ðV‚½‚ÉŠm•Û‚·‚é */
+ int i;
+ int block_no;
+ struct block* p;
+ struct chunk* chunk;
+ char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1);
+ if(pb == NULL) {
+ ShowFatalError("Memory manager::block_alloc failed.\n");
+ exit(1);
+ }
+
+ // store original block address in chunk
+ chunk = (struct chunk *) MALLOC (sizeof(struct chunk));
+ if (chunk == NULL) {
+ ShowFatalError("Memory manager::block_alloc failed.\n");
+ exit(1);
+ }
+ chunk->block = pb;
+ chunk->next = (chunk_first) ? chunk_first : NULL;
+ chunk_first = chunk;
+
+ // ƒuƒƒbƒN‚̃|ƒCƒ“ƒ^‚Ì擪‚ðsizeof(block) ƒAƒ‰ƒCƒƒ“ƒg‚É‘µ‚¦‚é
+ // ‚±‚̃AƒhƒŒƒX‚ðfree() ‚·‚邱‚Æ‚Í‚È‚¢‚Ì‚ÅA’¼Úƒ|ƒCƒ“ƒ^‚ð•ÏX‚µ‚Ä‚¢‚éB
+ pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
+ p = (struct block*)pb;
+ if(block_first == NULL) {
+ /* ‰‰ñŠm•Û */
+ block_no = 0;
+ block_first = p;
+ } else {
+ block_no = block_last->block_no + 1;
+ block_last->block_next = p;
+ p->block_prev = block_last;
+ }
+ block_last = &p[BLOCK_ALLOC - 1];
+ /* ƒuƒƒbƒN‚ð˜AŒ‹‚³‚¹‚é */
+ for(i=0;i<BLOCK_ALLOC;i++) {
+ if(i != 0) {
+ p[i].block_prev = &p[i-1];
+ }
+ if(i != BLOCK_ALLOC -1) {
+ p[i].block_next = &p[i+1];
+ }
+ p[i].block_no = block_no + i;
+ }
+
+ /* –¢Žg—pƒuƒƒbƒN‚ւ̃|ƒCƒ“ƒ^‚ðXV */
+ block_unused = &p[1];
+ p->unit_size = 1;
+ return p;
+ }
+}
+
+static void block_free(struct block* p) {
+ /* free() ‚¹‚¸‚ÉA–¢Žg—pƒtƒ‰ƒO‚ð•t‚¯‚邾‚¯ */
+ p->unit_size = 0;
+ /* –¢Žg—pƒ|ƒCƒ“ƒ^[‚ðXV‚·‚é */
+ if(block_unused == NULL) {
+ block_unused = p;
+ } else if(block_unused->block_no > p->block_no) {
+ block_unused = p;
+ }
+}
+
+unsigned int memmgr_usage (void)
+{
+ return memmgr_usage_bytes / 1024;
+}
+
+#ifdef LOG_MEMMGR
+static char memmer_logfile[128];
+static FILE *log_fp;
+
+static void memmgr_log (char *buf)
+{
+ if (!log_fp) {
+ log_fp = fopen(memmer_logfile,"w");
+ if (!log_fp) log_fp = stdout;
+ fprintf(log_fp, "Memory manager: Memory leaks found.\n");
+ }
+ fprintf(log_fp, buf);
+ return;
+}
+#endif
+
+static void memmgr_final (void)
+{
+ struct block *block = block_first;
+ struct chunk *chunk = chunk_first, *chunk2;
+ struct unit_head_large *large = unit_head_large_first, *large2;
+ int i;
+
+#ifdef LOG_MEMMGR
+ int count = 0;
+ char buf[128];
+#endif
+
+ while (block) {
+ if (block->unit_size) {
+ for (i = 0; i < block->unit_count; i++) {
+ struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]);
+ if(head->block != NULL)
+ {
+ #ifdef LOG_MEMMGR
+ sprintf (buf,
+ "%04d : %s line %d size %d\n", ++count,
+ head->file, head->line, head->size);
+ memmgr_log (buf);
+ #endif
+ // get block pointer and free it [celest]
+ _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK);
+ }
+ }
+ }
+ //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) {
+ // reached a new block array
+ //block = block->block_next;
+
+ /* Okay wise guys... this is how block2 was allocated: [Skotlex]
+ struct block* p;
+ char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1);
+ pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block));
+ p = (struct block*)pb;
+
+ The reason we get an invalid pointer is that we allocated pb, not p.
+ So how do you get pb when you only have p?
+ The answer is, you can't, because the original pointer was lost when
+ memory-aligning the block. So we either forget this FREE or use a
+ self-reference...
+ Since we are already quitting, it might be ok to just not free the block
+ as it is.
+ */
+ // didn't realise that before o.o -- block chunks are now freed below [celest]
+ // FREE(block2);
+ //block2 = block;
+ //continue;
+ //}
+ block = block->block_next;
+ }
+
+ // free the allocated block chunks
+ chunk = chunk_first;
+ while (chunk) {
+ chunk2 = chunk->next;
+ FREE(chunk->block);
+ FREE(chunk);
+ chunk = chunk2;
+ }
+
+ while(large) {
+ large2 = large->next;
+ #ifdef LOG_MEMMGR
+ sprintf (buf,
+ "%04d : %s line %d size %d\n", ++count,
+ large->unit_head.file, large->unit_head.line, large->unit_head.size);
+ memmgr_log (buf);
+ #endif
+ FREE (large);
+ large = large2;
+ }
+#ifdef LOG_MEMMGR
+ if(count == 0) {
+ ShowInfo("Memory manager: No memory leaks found.\n");
+ } else {
+ ShowWarning("Memory manager: Memory leaks found and fixed.\n");
+ fclose(log_fp);
+ }
+#endif
+ return;
+}
+
+static void memmgr_init (void)
+{
+ #ifdef LOG_MEMMGR
+ sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME);
+ ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile);
+ #endif
+ return;
+}
+#endif
+
+
+/*======================================
+ * Initialise
+ *--------------------------------------
+ */
+
+unsigned int malloc_usage (void)
+{
+#ifdef USE_MEMMGR
+ return memmgr_usage ();
+#else
+ return 0;
+#endif
+}
+
+void malloc_final (void)
+{
+#ifdef USE_MEMMGR
+ memmgr_final ();
+#endif
+ return;
+}
+
+void malloc_init (void)
+{
+#ifdef USE_MEMMGR
+ memmgr_init ();
+#endif
+ return;
+}
diff --git a/src/common/malloc.h b/src/common/malloc.h
new file mode 100644
index 000000000..e9dbb9d44
--- /dev/null
+++ b/src/common/malloc.h
@@ -0,0 +1,153 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MALLOC_H_
+#define _MALLOC_H_
+
+#ifndef __NETBSD__
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ ""
+# endif
+#endif
+#endif
+#define ALC_MARK __FILE__, __LINE__, __func__
+
+//////////////////////////////////////////////////////////////////////
+// Whether to use Athena's built-in Memory Manager (enabled by default)
+// To disable just comment the following line
+#if !defined(DMALLOC) && !defined(BCHECK)
+ #define USE_MEMMGR
+#endif
+// Whether to enable Memory Manager's logging
+#define LOG_MEMMGR
+
+#ifdef USE_MEMMGR
+
+# define aMalloc(n) _mmalloc(n,ALC_MARK)
+# define aMallocA(n) _mmalloc(n,ALC_MARK)
+# define aCalloc(m,n) _mcalloc(m,n,ALC_MARK)
+# define aCallocA(m,n) _mcalloc(m,n,ALC_MARK)
+# define aRealloc(p,n) _mrealloc(p,n,ALC_MARK)
+# define aStrdup(p) _mstrdup(p,ALC_MARK)
+# define aFree(p) _mfree(p,ALC_MARK)
+
+ void* _mmalloc (size_t, const char *, int, const char *);
+ void* _mcalloc (size_t, size_t, const char *, int, const char *);
+ void* _mrealloc (void *, size_t, const char *, int, const char *);
+ char* _mstrdup (const char *, const char *, int, const char *);
+ void _mfree (void *, const char *, int, const char *);
+
+#else
+
+# define aMalloc(n) aMalloc_(n,ALC_MARK)
+# define aMallocA(n) aMallocA_(n,ALC_MARK)
+# define aCalloc(m,n) aCalloc_(m,n,ALC_MARK)
+# define aCallocA(m,n) aCallocA_(m,n,ALC_MARK)
+# define aRealloc(p,n) aRealloc_(p,n,ALC_MARK)
+# define aStrdup(p) aStrdup_(p,ALC_MARK)
+# define aFree(p) aFree_(p,ALC_MARK)
+
+ void* aMalloc_ (size_t, const char *, int, const char *);
+ void* aMallocA_ (size_t, const char *, int, const char *);
+ void* aCalloc_ (size_t, size_t, const char *, int, const char *);
+ void* aCallocA_ (size_t, size_t, const char *, int, const char *);
+ void* aRealloc_ (void *, size_t, const char *, int, const char *);
+ char* aStrdup_ (const char *, const char *, int, const char *);
+ void aFree_ (void *, const char *, int, const char *);
+
+#endif
+
+////////////// Memory Managers //////////////////
+
+#ifdef MEMWATCH
+
+# include "memwatch.h"
+# define MALLOC(n) mwMalloc(n,__FILE__, __LINE__)
+# define MALLOCA(n) mwMalloc(n,__FILE__, __LINE__)
+# define CALLOC(m,n) mwCalloc(m,n,__FILE__, __LINE__)
+# define CALLOCA(m,n) mwCalloc(m,n,__FILE__, __LINE__)
+# define REALLOC(p,n) mwRealloc(p,n,__FILE__, __LINE__)
+# define STRDUP(p) mwStrdup(p,__FILE__, __LINE__)
+# define FREE(p) mwFree(p,__FILE__, __LINE__)
+
+#elif defined(DMALLOC)
+
+# include "dmalloc.h"
+# define MALLOC(n) dmalloc_malloc(__FILE__, __LINE__, (n), DMALLOC_FUNC_MALLOC, 0, 0)
+# define MALLOCA(n) dmalloc_malloc(__FILE__, __LINE__, (n), DMALLOC_FUNC_MALLOC, 0, 0)
+# define CALLOC(m,n) dmalloc_malloc(__FILE__, __LINE__, (m)*(n), DMALLOC_FUNC_CALLOC, 0, 0)
+# define CALLOCA(m,n) dmalloc_malloc(__FILE__, __LINE__, (m)*(n), DMALLOC_FUNC_CALLOC, 0, 0)
+# define REALLOC(p,n) dmalloc_realloc(__FILE__, __LINE__, (p), (n), DMALLOC_FUNC_REALLOC, 0)
+# define STRDUP(p) strdup(p)
+# define FREE(p) free(p)
+
+#elif defined(GCOLLECT)
+
+# include "gc.h"
+# define MALLOC(n) GC_MALLOC(n)
+# define MALLOCA(n) GC_MALLOC_ATOMIC(n)
+# define CALLOC(m,n) _bcalloc(m,n)
+# define CALLOCA(m,n) _bcallocA(m,n)
+# define REALLOC(p,n) GC_REALLOC(p,n)
+# define STRDUP(p) _bstrdup(p)
+# define FREE(p) GC_FREE(p)
+
+ void * _bcalloc(size_t, size_t);
+ void * _bcallocA(size_t, size_t);
+ char * _bstrdup(const char *);
+
+#elif defined(BCHECK)
+
+# define MALLOC(n) malloc(n)
+# define MALLOCA(n) malloc(n)
+# define CALLOC(m,n) calloc(m,n)
+# define CALLOCA(m,n) calloc(m,n)
+# define REALLOC(p,n) realloc(p,n)
+# define STRDUP(p) strdup(p)
+# define FREE(p) free(p)
+
+#else
+
+# define MALLOC(n) malloc(n)
+# define MALLOCA(n) malloc(n)
+# define CALLOC(m,n) calloc(m,n)
+# define CALLOCA(m,n) calloc(m,n)
+# define REALLOC(p,n) realloc(p,n)
+# define STRDUP(p) strdup(p)
+# define FREE(p) free(p)
+
+#endif
+
+/////////////// Buffer Creation /////////////////
+// Full credit for this goes to Shinomori [Ajarn]
+
+#ifdef __GNUC__ // GCC has variable length arrays
+
+ #define CREATE_BUFFER(name, type, size) type name[size]
+ #define DELETE_BUFFER(name)
+
+#else // others don't, so we emulate them
+
+ #define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type))
+ #define DELETE_BUFFER(name) aFree(name)
+
+#endif
+
+////////////// Others //////////////////////////
+// should be merged with any of above later
+#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type));
+
+#define CREATE_A(result, type, number) (result) = (type *) aCallocA ((number), sizeof(type));
+
+#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number));
+
+////////////////////////////////////////////////
+
+unsigned int malloc_usage (void);
+void malloc_init (void);
+void malloc_final (void);
+
+#endif
diff --git a/src/common/mapindex.c b/src/common/mapindex.c
new file mode 100644
index 000000000..e600b5ad7
--- /dev/null
+++ b/src/common/mapindex.c
@@ -0,0 +1,130 @@
+#include "mmo.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <showmsg.h>
+
+#define MAX_MAPINDEX 2000
+
+//Leave an extra char of space to hold the terminator, in case for the strncpy(mapindex_id2name()) calls.
+struct {
+ char name[MAP_NAME_LENGTH+1]; //Stores map name
+ int length; //Stores string length WITHOUT the extension for quick lookup.
+} indexes[MAX_MAPINDEX];
+
+static unsigned short max_index = 0;
+
+char mapindex_cfgfile[80] = "db/map_index.txt";
+
+unsigned short mapindex_name2id(char* name) {
+ //TODO: Perhaps use a db to speed this up? [Skotlex]
+ int i;
+ int length = strlen(name);
+ char *ext = strstr(name, ".");
+ if (ext)
+ length = ext-name; //Base map-name length without the extension.
+ for (i = 1; i < max_index; i++)
+ {
+ if (indexes[i].length == length && strncmp(indexes[i].name,name,length)==0)
+ return i;
+ }
+#ifdef MAPINDEX_AUTOADD
+ if (i < MAX_MAPINDEX) {
+ char map_name[MAP_NAME_LENGTH+5];
+ length = strlen(name);
+ if (length > MAP_NAME_LENGTH)
+ return;
+ memcpy(map_name, name, length+1);
+ if ((ext = strstr(map_name, ".")) != NULL) {
+ length = ext-map_name;
+ sprintf(ext, ".gat");
+ } else { //No extension?
+ length = strlen(map_name);
+ strcat(map_name, ".gat");
+ }
+ if (length > MAP_NAME_LENGTH - 4)
+ return 0; //Can't be added.
+ strncpy(indexes[i].name, map_name, MAP_NAME_LENGTH);
+ indexes[i].length = strlen(map_name);
+ ShowDebug("mapindex_name2id: Added map \"%s\" to position %d\n", indexes[i], i);
+ return i;
+ }
+#endif
+ ShowDebug("mapindex_name2id: Map \"%s\" not found in index list!\n", name);
+ return 0;
+}
+
+char* mapindex_id2name(unsigned short id) {
+ if (id > MAX_MAPINDEX || !indexes[id].length) {
+ ShowDebug("mapindex_id2name: Requested name for non-existant map index [%d] in cache.\n", id);
+ return indexes[0].name; //Theorically this should never happen, hence we return this string to prevent null pointer crashes.
+ }
+ return indexes[id].name;
+}
+
+void mapindex_init(void) {
+ FILE *fp;
+ char line[1024];
+ char *ext;
+ int last_index = -1;
+ int index, length;
+ char map_name[1024];
+
+ memset (&indexes, 0, sizeof (indexes));
+ fp=fopen(mapindex_cfgfile,"r");
+ if(fp==NULL){
+ ShowFatalError("Unable to read mapindex config file %s!\n", mapindex_cfgfile);
+ exit(1); //Server can't really run without this file.
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ switch (sscanf(line,"%1000s\t%d",map_name,&index)) {
+ case 1: //Map with no ID given, auto-assign
+ index = last_index+1;
+ case 2: //Map with ID given
+ if (index < 0 || index >= MAX_MAPINDEX) {
+ ShowError("(mapindex_init) Map index (%d) for \"%s\" out of range (max is %d)\n", index, map_name, MAX_MAPINDEX);
+ continue;
+ }
+ length = strlen(map_name);
+ if (length > MAP_NAME_LENGTH) {
+ ShowError("(mapindex_init) Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH);
+ continue;
+ }
+ if ((ext = strstr(map_name, ".gat")) != NULL) { //Gat map
+ length = ext-map_name;
+ } else if ((ext = strstr(map_name, ".afm")) != NULL || (ext = strstr(map_name, ".af2")) != NULL) { //afm map
+ length = ext-map_name;
+ sprintf(ext, ".gat"); //Change the extension to gat
+ } else if ((ext = strstr(map_name, ".")) != NULL) { //Generic extension?
+ length = ext-map_name;
+ sprintf(ext, ".gat");
+ } else { //No extension?
+ length = strlen(map_name);
+ strcat(map_name, ".gat");
+ }
+ if (length > MAP_NAME_LENGTH - 4) {
+ ShowError("(mapindex_init) Adjusted Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH);
+ continue;
+ }
+
+ if (indexes[index].length)
+ ShowWarning("(mapindex_init) Overriding index %d: map \"%s\" -> \"%s\"\n", indexes[index].name, map_name);
+
+ strncpy(indexes[index].name, map_name, MAP_NAME_LENGTH);
+ indexes[index].length = length;
+ if (max_index <= index)
+ max_index = index+1;
+ break;
+ default:
+ continue;
+ }
+ last_index = index;
+ }
+}
+
+void mapindex_final(void) {
+}
+
diff --git a/src/common/mapindex.h b/src/common/mapindex.h
new file mode 100644
index 000000000..7e2bbe289
--- /dev/null
+++ b/src/common/mapindex.h
@@ -0,0 +1,37 @@
+#ifndef _MAX_INDEX_H
+#define _MAX_INDEX_H
+//File in charge of assigning a numberic ID to each map in existance for space saving when passing map info between servers.
+extern char mapindex_cfgfile[80];
+
+//whether to enable auto-adding of maps during run. Not so secure as the map indexes will vary!
+#define MAPINDEX_AUTOADD
+
+//Some definitions for the mayor city maps.
+#define MAP_PRONTERA "prontera.gat"
+#define MAP_GEFFEN "geffen.gat"
+#define MAP_MORROC "morocc.gat"
+#define MAP_ALBERTA "alberta.gat"
+#define MAP_PAYON "payon.gat"
+#define MAP_IZLUDE "izlude.gat"
+#define MAP_ALDEBARAN "aldebaran.gat"
+#define MAP_LUTIE "xmas.gat"
+#define MAP_COMODO "comodo.gat"
+#define MAP_YUNO "yuno.gat"
+#define MAP_AMATSU "amatsu.gat"
+#define MAP_GONRYUN "gonryun.gat"
+#define MAP_UMBALA "umbala.gat"
+#define MAP_NIFLHEIM "niflheim.gat"
+#define MAP_LOUYANG "louyang.gat"
+#define MAP_JAWAII "jawaii.gat"
+#define MAP_AYOTHAYA "ayothaya.gat"
+#define MAP_EINBROCH "einbroch.gat"
+#define MAP_LIGHTHALZEN "lighthalzen.gat"
+#define MAP_EINBECH "einbech.gat"
+#define MAP_HUGEL "hugel.gat"
+#define MAP_JAIL "sec_pri.gat"
+unsigned short mapindex_name2id(char*);
+const char* mapindex_id2name(unsigned short);
+void mapindex_init(void);
+void mapindex_final(void);
+
+#endif
diff --git a/src/common/mmo.h b/src/common/mmo.h
new file mode 100644
index 000000000..d0d4685e2
--- /dev/null
+++ b/src/common/mmo.h
@@ -0,0 +1,403 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MMO_H_
+#define _MMO_H_
+
+#include <time.h>
+#include "utils.h" // _WIN32
+
+#if ! defined(Assert)
+#if defined(RELEASE)
+#define Assert(EX)
+#else
+// extern "C" {
+#include <assert.h>
+// }
+#ifndef DEFCPP
+#if defined(_WIN32) && !defined(MINGW)
+#include <crtdbg.h>
+#endif
+#endif
+#define Assert(EX) assert(EX)
+#endif
+#endif /* ! defined(Assert) */
+
+#ifdef CYGWIN
+// txt‚âlog‚È‚Ç‚Ì‘‚«o‚·ƒtƒ@ƒCƒ‹‚̉üsƒR[ƒh
+#define RETCODE "\r\n" // (CR/LFFWindowsŒn)
+#else
+#define RETCODE "\n" // (LFFUnixŒnj
+#endif
+
+#define RET RETCODE
+
+#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
+
+//Remove/Comment this line to disable sc_data saving. [Skotlex]
+#define ENABLE_SC_SAVING
+
+#define MAX_MAP_PER_SERVER 1024
+#define MAX_INVENTORY 100
+//Number of slots carded equipment can have. Never set to less than 4 as they are also used to keep the data of forged items/equipment. [Skotlex]
+//Note: The client seems unable to receive data for more than 4 slots due to all related packets having a fixed size.
+#define MAX_SLOTS 4
+#define MAX_AMOUNT 30000
+#define MAX_ZENY 1000000000
+#define MAX_FAME 1000000000
+#define MAX_CART 100
+#define MAX_SKILL 1100 // Bumped to 1100 for new quest skills, will need to further increase one day... [DracoRPG]
+#define GLOBAL_REG_NUM 96
+#define ACCOUNT_REG_NUM 32
+#define ACCOUNT_REG2_NUM 16
+//Should hold the max of GLOBAL/ACCOUNT/ACCOUNT2 (needed for some arrays that hold all three)
+#define MAX_REG_NUM 96
+#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 16+10*6 // increased max guild members +6 per 1 extension levels [Lupus]
+#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 15 // increased max guild skills because of new skills [Sara-chan]
+#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris]
+#define MAX_GUILDLEVEL 50
+#define MAX_GUARDIANS 8 //Local max per castle. [Skotlex]
+
+#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 WEDDING_RING_M 2634
+#define WEDDING_RING_F 2635
+
+//For character names, title names, guilds, maps, etc.
+//Includes null-terminator as it is the length of the array.
+#define NAME_LENGTH 24
+//For item names, which tend to have much longer names.
+#define ITEM_NAME_LENGTH 24
+//For Map Names, which the client considers to be 16 in length
+#define MAP_NAME_LENGTH 16
+
+#define MAX_FRIENDS 40
+#define MAX_MEMOPOINTS 10
+
+//These max values can be exceeded and the char/map servers will update them with no problems
+//These are just meant to minimize the updating needed between char/map servers as players login.
+//Room for initial 10K accounts
+#define DEFAULT_MAX_ACCOUNT_ID 2010000
+//Room for initial 100k characters
+#define DEFAULT_MAX_CHAR_ID 250000
+
+#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[MAX_SLOTS];
+};
+
+struct point{
+ unsigned short map;
+ short x,y;
+};
+
+struct skill {
+ unsigned short id,lv,flag;
+};
+
+struct global_reg {
+ char str[32];
+ char value[256]; // [zBuffer]
+};
+
+//For saving status changes across sessions. [Skotlex]
+struct status_change_data {
+ unsigned short type; //SC_type
+ int val1, val2, val3, val4, tick; //Remaining duration.
+};
+
+struct s_pet {
+ int account_id;
+ int char_id;
+ int pet_id;
+ short class_;
+ short level;
+ short egg_id;//pet egg id
+ short equip;//pet equip name_id
+ short intimate;//pet friendly
+ short hungry;//pet hungry
+ char name[NAME_LENGTH];
+ char rename_flag;
+ char incuvate;
+};
+
+struct friend {
+ int account_id;
+ int char_id;
+ char name[NAME_LENGTH];
+};
+
+struct mmo_charstatus {
+ int char_id;
+ int account_id;
+ int partner_id;
+ int father;
+ int mother;
+ int child;
+
+ int base_exp,job_exp,zeny;
+
+ short class_;
+ short status_point,skill_point;
+ int hp,max_hp,sp,max_sp;
+ short option,manner;
+ unsigned char karma;
+ short hair,hair_color,clothes_color;
+ int party_id,guild_id,pet_id;
+ int fame;
+
+ short weapon,shield;
+ short head_top,head_mid,head_bottom;
+
+ char name[NAME_LENGTH];
+ unsigned int 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[MAX_MEMOPOINTS];
+ struct item inventory[MAX_INVENTORY],cart[MAX_CART];
+ struct skill skill[MAX_SKILL];
+
+ struct friend friends[MAX_FRIENDS]; //New friend system [Skotlex]
+};
+
+struct registry {
+ int global_num;
+ struct global_reg global[GLOBAL_REG_NUM];
+ int account_num;
+ struct global_reg account[ACCOUNT_REG_NUM];
+ int account2_num;
+ struct global_reg account2[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;
+ int char_id;
+ char name[NAME_LENGTH];
+ struct map_session_data *sd;
+ unsigned short map;
+ unsigned short lv;
+ unsigned leader : 1,
+ online : 1;
+};
+
+struct party {
+ int party_id;
+ char name[NAME_LENGTH];
+ unsigned exp : 1,
+ item : 2; //&1: Party-Share (round-robin), &2: pickup style: shared.
+ short itemc; //For item sharing through round-robin, holds last item receiver.
+ struct party_member member[MAX_PARTY];
+};
+
+struct guild_member {
+ int account_id, char_id;
+ short hair,hair_color,gender,class_,lv;
+ int exp,exp_payper;
+ short online,position;
+ int rsv1,rsv2;
+ char name[NAME_LENGTH];
+ struct map_session_data *sd;
+};
+
+struct guild_position {
+ char name[NAME_LENGTH];
+ int mode;
+ int exp_mode;
+};
+
+struct guild_alliance {
+ int opposition;
+ int guild_id;
+ char name[NAME_LENGTH];
+};
+
+struct guild_explusion {
+ char name[NAME_LENGTH];
+ 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;
+#ifdef TXT_ONLY
+ //FIXME: Gotta remove this variable completely, but doing so screws up the format of the txt save file...
+ int castle_id;
+#endif
+ char name[NAME_LENGTH],master[NAME_LENGTH];
+ 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];
+#ifndef TXT_ONLY
+ unsigned char save_flag;
+#endif
+};
+
+struct guild_castle {
+ int castle_id;
+ char map_name[MAP_NAME_LENGTH];
+ char castle_name[NAME_LENGTH];
+ char castle_event[NAME_LENGTH];
+ int guild_id;
+ int economy;
+ int defense;
+ int triggerE;
+ int triggerD;
+ int nextTime;
+ int payTime;
+ int createTime;
+ int visibleC;
+ struct {
+ unsigned visible : 1;
+ int hp;
+ int id;
+ } guardian[MAX_GUARDIANS]; //New simplified structure. [Skotlex]
+};
+struct square {
+ int val1[5];
+ int val2[5];
+};
+
+struct fame_list {
+ int id;
+ int fame;
+ char name[NAME_LENGTH];
+};
+
+enum {
+ GBI_EXP =1, // ƒMƒ‹ƒh‚ÌEXP
+ GBI_GUILDLV, // ƒMƒ‹ƒh‚ÌLv
+ GBI_SKILLPOINT, // ƒMƒ‹ƒh‚̃XƒLƒ‹ƒ|ƒCƒ“ƒg
+ GBI_SKILLLV, // ƒMƒ‹ƒhƒXƒLƒ‹Lv
+};
+
+enum {
+ GMI_POSITION =0, // ƒƒ“ƒo[‚Ì–ðE•ÏX
+ GMI_EXP,
+ GMI_HAIR,
+ GMI_HAIR_COLOR,
+ GMI_GENDER,
+ GMI_CLASS,
+ GMI_LEVEL,
+};
+
+enum {
+ GD_SKILLBASE=10000,
+ GD_APPROVAL=10000,
+ GD_KAFRACONTRACT=10001,
+ GD_GUARDIANRESEARCH=10002,
+ GD_GUARDUP=10003,
+ GD_EXTENSION=10004,
+ GD_GLORYGUILD=10005,
+ GD_LEADERSHIP=10006,
+ GD_GLORYWOUNDS=10007,
+ GD_SOULCOLD=10008,
+ GD_HAWKEYES=10009,
+ GD_BATTLEORDER=10010,
+ GD_REGENERATION=10011,
+ GD_RESTORE=10012,
+ GD_EMERGENCYCALL=10013,
+ GD_DEVELOPMENT=10014,
+};
+
+#ifndef __WIN32
+ #ifndef strcmpi
+ #define strcmpi strcasecmp
+ #endif
+ #ifndef stricmp
+ #define stricmp strcasecmp
+ #endif
+ #ifndef strncmpi
+ #define strncmpi strncasecmp
+ #endif
+ #ifndef strnicmp
+ #define strnicmp strncasecmp
+ #endif
+#else
+ #define snprintf _snprintf
+ #define vsnprintf _vsnprintf
+ #ifndef strncmpi
+ #define strncmpi strnicmp
+ #endif
+#endif
+
+#endif // _MMO_H_
diff --git a/src/common/nullpo.c b/src/common/nullpo.c
new file mode 100644
index 000000000..8508f1333
--- /dev/null
+++ b/src/common/nullpo.c
@@ -0,0 +1,94 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "nullpo.h"
+#include "../common/showmsg.h"
+// #include "logs.h" // •z΂µ‚Ä‚Ý‚é
+
+static void nullpo_info_core(const char *file, int line, const char *func,
+ const char *fmt, va_list ap);
+
+/*======================================
+ * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—Í
+ *--------------------------------------
+ */
+int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (target != NULL)
+ return 0;
+
+ va_start(ap, fmt);
+ nullpo_info_core(file, line, func, fmt, ap);
+ va_end(ap);
+ return 1;
+}
+
+int nullpo_chk(const char *file, int line, const char *func, const void *target)
+{
+ if (target != NULL)
+ return 0;
+
+ nullpo_info_core(file, line, func, NULL, NULL);
+ return 1;
+}
+
+
+/*======================================
+ * nullpoî•ño—Í(ŠO•”ŒÄo‚µŒü‚¯ƒ‰ƒbƒp)
+ *--------------------------------------
+ */
+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);
+}
+
+
+/*======================================
+ * nullpoî•ño—Í(Main)
+ *--------------------------------------
+ */
+static void nullpo_info_core(const char *file, int line, const char *func,
+ const char *fmt, va_list ap)
+{
+ if (file == NULL)
+ file = "??";
+
+ func =
+ func == NULL ? "unknown":
+ func[0] == '\0' ? "unknown":
+ func;
+
+ ShowMessage("--- nullpo info --------------------------------------------\n");
+ ShowMessage("%s:%d: in func `%s'\n", file, line, func);
+ if (fmt != NULL)
+ {
+ if (fmt[0] != '\0')
+ {
+ vprintf(fmt, ap);
+
+ // ÅŒã‚ɉüs‚µ‚½‚©Šm”F
+ if (fmt[strlen(fmt)-1] != '\n')
+ ShowMessage("\n");
+ }
+ }
+ ShowMessage("--- end nullpo info ----------------------------------------\n");
+
+ // ‚±‚±‚ç‚ÅnullpoƒƒO‚ðƒtƒ@ƒCƒ‹‚É‘‚«o‚¹‚½‚ç
+ // ‚Ü‚Æ‚ß‚Ä’ño‚Å‚«‚é‚È‚ÆŽv‚Á‚Ä‚¢‚½‚èB
+}
diff --git a/src/common/nullpo.h b/src/common/nullpo.h
new file mode 100644
index 000000000..66d984224
--- /dev/null
+++ b/src/common/nullpo.h
@@ -0,0 +1,237 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _NULLPO_H_
+#define _NULLPO_H_
+
+
+#define NULLPO_CHECK 1
+ // ‘S‘̂̃XƒCƒbƒ`‚ð錾‚µ‚Ä‚¢‚éƒwƒbƒ_‚ª‚ ‚ê‚Î
+ // ‚»‚±‚Ɉړ®‚µ‚Ä‚¢‚½‚¾‚¯‚é‚Æ
+
+#ifndef __NETBSD__
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ ""
+# endif
+#endif
+#endif
+
+#ifdef _WIN32
+#define __attribute__(x) /* nothing */
+#endif
+
+
+#define NLP_MARK __FILE__, __LINE__, __func__
+
+/*----------------------------------------------------------------------------
+ * Macros
+ *----------------------------------------------------------------------------
+ */
+/*======================================
+ * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—ÍŒã return
+ *E“WŠJ‚·‚é‚Æif‚Æ‚©return“™‚ªo‚é‚Ì‚Å
+ * ˆês’P‘Ì‚ÅŽg‚Á‚Ä‚­‚¾‚³‚¢B
+ *Enullpo_ret(x = func());
+ * ‚̂悤‚ÈŽg—p–@‚à‘z’肵‚Ä‚¢‚Ü‚·B
+ *--------------------------------------
+ * nullpo_ret(t)
+ * –ß‚è’l 0ŒÅ’è
+ * [ˆø”]
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ *--------------------------------------
+ * nullpo_retv(t)
+ * –ß‚è’l ‚È‚µ
+ * [ˆø”]
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ *--------------------------------------
+ * nullpo_retr(ret, t)
+ * –ß‚è’l Žw’è
+ * [ˆø”]
+ * ret return(ret);
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ *--------------------------------------
+ * nullpo_ret_f(t, fmt, ...)
+ * Ú×î•ño—Í—p
+ * –ß‚è’l 0
+ * [ˆø”]
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ * fmt ... vprintf‚É“n‚³‚ê‚é
+ * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚È‚Ç‚É
+ *--------------------------------------
+ * nullpo_retv_f(t, fmt, ...)
+ * Ú×î•ño—Í—p
+ * –ß‚è’l ‚È‚µ
+ * [ˆø”]
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ * fmt ... vprintf‚É“n‚³‚ê‚é
+ * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚È‚Ç‚É
+ *--------------------------------------
+ * nullpo_retr_f(ret, t, fmt, ...)
+ * Ú×î•ño—Í—p
+ * –ß‚è’l Žw’è
+ * [ˆø”]
+ * ret return(ret);
+ * t ƒ`ƒFƒbƒN‘ÎÛ
+ * fmt ... vprintf‚É“n‚³‚ê‚é
+ * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚È‚Ç‚É
+ *--------------------------------------
+ */
+
+#if NULLPO_CHECK
+
+#define nullpo_ret(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);}
+
+#define nullpo_retv(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return;}
+
+#define nullpo_retr(ret, t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);}
+
+#define nullpo_retb(t) \
+ if (nullpo_chk(NLP_MARK, (void *)(t))) {break;}
+
+// ‰Â•Ïˆø”ƒ}ƒNƒ‚ÉŠÖ‚·‚éðŒƒRƒ“ƒpƒCƒ‹
+#if __STDC_VERSION__ >= 199901L
+/* C99‚ɑΉž */
+#define nullpo_ret_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);}
+
+#define nullpo_retv_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;}
+
+#define nullpo_retr_f(ret, t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);}
+
+#define nullpo_retb_f(t, fmt, ...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;}
+
+#elif __GNUC__ >= 2
+/* GCC—p */
+#define nullpo_ret_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);}
+
+#define nullpo_retv_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;}
+
+#define nullpo_retr_f(ret, t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);}
+
+#define nullpo_retb_f(t, fmt, args...) \
+ if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;}
+
+#else
+
+/* ‚»‚Ì‘¼‚Ìê‡EEE orz */
+
+#endif
+
+#else /* NULLPO_CHECK */
+/* No Nullpo check */
+
+// if((t)){;}
+// —Ç‚¢•û–@‚ªŽv‚¢‚‚©‚È‚©‚Á‚½‚Ì‚ÅEEE‹ê“÷‚Ìô‚Å‚·B
+// ˆê‰žƒ[ƒjƒ“ƒO‚Ío‚È‚¢‚Í‚¸
+
+#define nullpo_ret(t) if((t)){;}
+#define nullpo_retv(t) if((t)){;}
+#define nullpo_retr(ret, t) if((t)){;}
+#define nullpo_retb(t) if((t)){;}
+
+// ‰Â•Ïˆø”ƒ}ƒNƒ‚ÉŠÖ‚·‚éðŒƒRƒ“ƒpƒCƒ‹
+#if __STDC_VERSION__ >= 199901L
+/* C99‚ɑΉž */
+#define nullpo_ret_f(t, fmt, ...) if((t)){;}
+#define nullpo_retv_f(t, fmt, ...) if((t)){;}
+#define nullpo_retr_f(ret, t, fmt, ...) if((t)){;}
+#define nullpo_retb_f(t, fmt, ...) if((t)){;}
+
+#elif __GNUC__ >= 2
+/* GCC—p */
+#define nullpo_ret_f(t, fmt, args...) if((t)){;}
+#define nullpo_retv_f(t, fmt, args...) if((t)){;}
+#define nullpo_retr_f(ret, t, fmt, args...) if((t)){;}
+#define nullpo_retb_f(t, fmt, args...) if((t)){;}
+
+#else
+/* ‚»‚Ì‘¼‚Ìê‡EEE orz */
+#endif
+
+#endif /* NULLPO_CHECK */
+
+/*----------------------------------------------------------------------------
+ * Functions
+ *----------------------------------------------------------------------------
+ */
+/*======================================
+ * nullpo_chk
+ * Nullƒ`ƒFƒbƒN ‹y‚Ñ î•ño—Í
+ * [ˆø”]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (ŠÖ”–¼)
+ * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Æ‚æ‚¢
+ * target ƒ`ƒFƒbƒN‘ÎÛ
+ * [•Ô‚è’l]
+ * 0 OK
+ * 1 NULL
+ *--------------------------------------
+ */
+int nullpo_chk(const char *file, int line, const char *func, const void *target);
+
+
+/*======================================
+ * nullpo_chk_f
+ * Nullƒ`ƒFƒbƒN ‹y‚Ñ ÚׂÈî•ño—Í
+ * [ˆø”]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (ŠÖ”–¼)
+ * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Æ‚æ‚¢
+ * target ƒ`ƒFƒbƒN‘ÎÛ
+ * fmt ... vprintf‚É“n‚³‚ê‚é
+ * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚È‚Ç‚É
+ * [•Ô‚è’l]
+ * 0 OK
+ * 1 NULL
+ *--------------------------------------
+ */
+int nullpo_chk_f(const char *file, int line, const char *func, const void *target,
+ const char *fmt, ...)
+ __attribute__((format(printf,5,6)));
+
+
+/*======================================
+ * nullpo_info
+ * nullpoî•ño—Í
+ * [ˆø”]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (ŠÖ”–¼)
+ * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Æ‚æ‚¢
+ *--------------------------------------
+ */
+void nullpo_info(const char *file, int line, const char *func);
+
+
+/*======================================
+ * nullpo_info_f
+ * nullpoÚ×î•ño—Í
+ * [ˆø”]
+ * file __FILE__
+ * line __LINE__
+ * func __func__ (ŠÖ”–¼)
+ * ‚±‚ê‚ç‚É‚Í NLP_MARK ‚ðŽg‚¤‚Æ‚æ‚¢
+ * fmt ... vprintf‚É“n‚³‚ê‚é
+ * ”õl‚âŠÖŒW•Ï”‚Ì‘‚«o‚µ‚È‚Ç‚É
+ *--------------------------------------
+ */
+void nullpo_info_f(const char *file, int line, const char *func,
+ const char *fmt, ...)
+ __attribute__((format(printf,4,5)));
+
+
+#endif
diff --git a/src/common/plugin.h b/src/common/plugin.h
new file mode 100644
index 000000000..2ccefb6bd
--- /dev/null
+++ b/src/common/plugin.h
@@ -0,0 +1,40 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+////// Plugin functions ///////////////
+
+#define PLUGIN_VERSION "1.02"
+
+typedef struct _Plugin_Info {
+ char *name;
+ char type;
+ char *version;
+ char *req_version;
+ char *description;
+} Plugin_Info;
+
+typedef struct _Plugin_Event_Table {
+ char *func_name;
+ char *event_name;
+} Plugin_Event_Table;
+
+////// Plugin Export functions /////////////
+
+#define PLUGIN_ALL 0
+#define PLUGIN_LOGIN 1
+#define PLUGIN_CHAR 2
+#define PLUGIN_MAP 8
+#define PLUGIN_CORE 16
+
+#define IMPORT_SYMBOL(s,n) (s) = plugin_call_table[n]
+
+////// Global Plugin variables /////////////
+
+#define PLUGIN_INFO struct _Plugin_Info plugin_info
+#define PLUGIN_EVENTS_TABLE struct _Plugin_Event_Table plugin_event_table[]
+void **plugin_call_table;
+
+#endif // _PLUGIN_H_
diff --git a/src/common/plugins.c b/src/common/plugins.c
new file mode 100644
index 000000000..fbadca065
--- /dev/null
+++ b/src/common/plugins.c
@@ -0,0 +1,367 @@
+// 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>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "plugin.h"
+#include "plugins.h"
+#include "../common/mmo.h"
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/utils.h"
+#include "../common/socket.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/showmsg.h"
+
+//////////////////////////////////////////////
+
+typedef struct _Plugin_Event {
+ void (*func)(void);
+ struct _Plugin_Event *next;
+} Plugin_Event;
+
+typedef struct _Plugin_Event_List {
+ char *name;
+ struct _Plugin_Event_List *next;
+ struct _Plugin_Event *events;
+} Plugin_Event_List;
+
+static int auto_search = 1;
+static int load_priority = 0;
+Plugin_Event_List *event_head = NULL;
+Plugin *plugin_head = NULL;
+
+Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" };
+
+static size_t call_table_size = 0;
+static size_t max_call_table = 0;
+
+////// Plugin Events Functions //////////////////
+
+int register_plugin_func (char *name)
+{
+ Plugin_Event_List *evl;
+ if (name) {
+ evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List));
+ evl->name = (char *) aMalloc (strlen(name) + 1);
+
+ evl->next = event_head;
+ strcpy(evl->name, name);
+ evl->events = NULL;
+ event_head = evl;
+ }
+ return 0;
+}
+
+Plugin_Event_List *search_plugin_func (char *name)
+{
+ Plugin_Event_List *evl = event_head;
+ while (evl) {
+ if (strcmpi(evl->name, name) == 0)
+ return evl;
+ evl = evl->next;
+ }
+ return NULL;
+}
+
+int register_plugin_event (void (*func)(void), char* name)
+{
+ Plugin_Event_List *evl = search_plugin_func(name);
+ if (!evl) {
+ // register event if it doesn't exist already
+ register_plugin_func(name);
+ // relocate the new event list
+ evl = search_plugin_func(name);
+ }
+ if (evl) {
+ Plugin_Event *ev;
+
+ ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event));
+ ev->func = func;
+ ev->next = NULL;
+
+ if (evl->events == NULL)
+ evl->events = ev;
+ else {
+ Plugin_Event *ev2 = evl->events;
+ while (ev2) {
+ if (ev2->next == NULL) {
+ ev2->next = ev;
+ break;
+ }
+ ev2 = ev2->next;
+ }
+ }
+ }
+ return 0;
+}
+
+int plugin_event_trigger (char *name)
+{
+ int c = 0;
+ Plugin_Event_List *evl = search_plugin_func(name);
+ if (evl) {
+ Plugin_Event *ev = evl->events;
+ while (ev) {
+ ev->func();
+ ev = ev->next;
+ c++;
+ }
+ }
+ return c;
+}
+
+////// Plugins Call Table Functions /////////
+
+int export_symbol (void *var, int offset)
+{
+ //printf ("0x%x\n", var);
+
+ // add to the end of the list
+ if (offset < 0)
+ offset = call_table_size;
+
+ // realloc if not large enough
+ if ((size_t)offset >= max_call_table) {
+ max_call_table = 1 + offset;
+ plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*));
+
+ // clear the new alloced block
+ memset(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*));
+ }
+
+ // the new table size is delimited by the new element at the end
+ if ((size_t)offset >= call_table_size)
+ call_table_size = offset+1;
+
+ // put the pointer at the selected place
+ plugin_call_table[offset] = var;
+ return 0;
+}
+
+////// Plugins Core /////////////////////////
+
+Plugin *plugin_open (const char *filename)
+{
+ Plugin *plugin;
+ Plugin_Info *info;
+ Plugin_Event_Table *events;
+ void **procs;
+ int init_flag = 1;
+
+ //printf ("loading %s\n", filename);
+
+ // Check if the plugin has been loaded before
+ plugin = plugin_head;
+ while (plugin) {
+ // returns handle to the already loaded plugin
+ if (plugin->state && strcmpi(plugin->filename, filename) == 0) {
+ //printf ("not loaded (duplicate) : %s\n", filename);
+ return plugin;
+ }
+ plugin = plugin->next;
+ }
+
+ plugin = (Plugin *)aCalloc(1, sizeof(Plugin));
+ plugin->state = -1; // not loaded
+
+ plugin->dll = DLL_OPEN(filename);
+ if (!plugin->dll) {
+ //printf ("not loaded (invalid file) : %s\n", filename);
+ plugin_unload(plugin);
+ return NULL;
+ }
+
+ // Retrieve plugin information
+ plugin->state = 0; // initialising
+ DLL_SYM (info, plugin->dll, "plugin_info");
+ // For high priority plugins (those that are explicitly loaded from the conf file)
+ // we'll ignore them even (could be a 3rd party dll file)
+ if ((!info && load_priority == 0) ||
+ (info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code
+ (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server
+ (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP))))
+ {
+ //printf ("not loaded (incompatible) : %s\n", filename);
+ plugin_unload(plugin);
+ return NULL;
+ }
+ plugin->info = (info) ? info : &default_info;
+
+ plugin->filename = (char *) aMalloc (strlen(filename) + 1);
+ strcpy(plugin->filename, filename);
+
+ // Initialise plugin call table (For exporting procedures)
+ DLL_SYM (procs, plugin->dll, "plugin_call_table");
+ if (procs) *procs = plugin_call_table;
+
+ // Register plugin events
+ DLL_SYM (events, plugin->dll, "plugin_event_table");
+ if (events) {
+ int i = 0;
+ while (events[i].func_name) {
+ if (strcmpi(events[i].event_name, "Plugin_Test") == 0) {
+ int (*test_func)(void);
+ DLL_SYM (test_func, plugin->dll, events[i].func_name);
+ if (test_func && test_func() == 0) {
+ // plugin has failed test, disabling
+ //printf ("disabled (failed test) : %s\n", filename);
+ init_flag = 0;
+ }
+ } else {
+ void (*func)(void);
+ DLL_SYM (func, plugin->dll, events[i].func_name);
+ if (func) register_plugin_event (func, events[i].event_name);
+ }
+ i++;
+ }
+ }
+
+ plugin->next = plugin_head;
+ plugin_head = plugin;
+
+ plugin->state = init_flag; // fully loaded
+ ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename);
+
+ return plugin;
+}
+
+void plugin_load (const char *filename)
+{
+ plugin_open(filename);
+}
+
+void plugin_unload (Plugin *plugin)
+{
+ if (plugin == NULL)
+ return;
+ if (plugin->filename) aFree(plugin->filename);
+ if (plugin->dll) DLL_CLOSE(plugin->dll);
+ aFree(plugin);
+}
+
+#ifdef _WIN32
+char *DLL_ERROR(void)
+{
+ static char dllbuf[80];
+ DWORD dw = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL);
+ return dllbuf;
+}
+#endif
+
+////// Initialize/Finalize ////////////////////
+
+int plugins_config_read(const char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp = fopen(cfgName, "r");
+ if (fp == NULL) {
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while (fgets(line, 1020, fp)) {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+
+ if (strcmpi(w1, "auto_search") == 0) {
+ if(strcmpi(w2, "yes")==0)
+ auto_search = 1;
+ else if(strcmpi(w2, "no")==0)
+ auto_search = 0;
+ else auto_search = atoi(w2);
+ } else if (strcmpi(w1, "plugin") == 0) {
+ char filename[128];
+ sprintf (filename, "plugins/%s%s", w2, DLL_EXT);
+ plugin_load(filename);
+ } else if (strcmpi(w1, "import") == 0)
+ plugins_config_read(w2);
+ }
+ fclose(fp);
+ return 0;
+}
+
+void plugins_init (void)
+{
+ char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf";
+ register_plugin_func("Plugin_Init");
+ register_plugin_func("Plugin_Final");
+ register_plugin_func("Athena_Init");
+ register_plugin_func("Athena_Final");
+
+ // networking
+ export_symbol (func_parse_table, 18);
+ export_symbol (RFIFOSKIP, 17);
+ export_symbol (WFIFOSET, 16);
+ export_symbol (delete_session, 15);
+ export_symbol (session, 14);
+ export_symbol (&fd_max, 13);
+ export_symbol (addr_, 12);
+ // timers
+ export_symbol (get_uptime, 11);
+ export_symbol (delete_timer, 10);
+ export_symbol (add_timer_func_list, 9);
+ export_symbol (add_timer_interval, 8);
+ export_symbol (add_timer, 7);
+ export_symbol ((void *)get_svn_revision, 6);
+ export_symbol (gettick, 5);
+ // core
+ export_symbol (&runflag, 4);
+ export_symbol (arg_v, 3);
+ export_symbol (&arg_c, 2);
+ export_symbol (SERVER_NAME, 1);
+ export_symbol (&SERVER_TYPE, 0);
+
+ load_priority = 1;
+ plugins_config_read (PLUGIN_CONF_FILENAME);
+ load_priority = 0;
+
+ if (auto_search)
+ findfile("plugins", DLL_EXT, plugin_load);
+
+ plugin_event_trigger("Plugin_Init");
+
+ return;
+}
+
+void plugins_final (void)
+{
+ Plugin *plugin = plugin_head, *plugin2;
+ Plugin_Event_List *evl = event_head, *evl2;
+ Plugin_Event *ev, *ev2;
+
+ plugin_event_trigger("Plugin_Final");
+
+ while (plugin) {
+ plugin2 = plugin->next;
+ plugin_unload(plugin);
+ plugin = plugin2;
+ }
+
+ while (evl) {
+ ev = evl->events;
+ while (ev) {
+ ev2 = ev->next;
+ aFree(ev);
+ ev = ev2;
+ }
+ evl2 = evl->next;
+ aFree(evl->name);
+ aFree(evl);
+ evl = evl2;
+ }
+
+ aFree(plugin_call_table);
+
+ return;
+}
diff --git a/src/common/plugins.h b/src/common/plugins.h
new file mode 100644
index 000000000..d642b5965
--- /dev/null
+++ b/src/common/plugins.h
@@ -0,0 +1,61 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PLUGINS_H_
+#define _PLUGINS_H_
+
+////// Dynamic Link Library functions ///////////////
+
+#ifdef _WIN32
+
+ #include <windows.h>
+ #define DLL_OPEN(x) LoadLibrary(x)
+ #define DLL_SYM(x,y,z) (FARPROC)(x) = GetProcAddress(y,z)
+ #define DLL_CLOSE(x) FreeLibrary(x)
+ #define DLL_EXT ".dll"
+ #define DLL HINSTANCE
+ char *DLL_ERROR(void);
+
+#else
+
+ #include <dlfcn.h>
+ #define DLL_OPEN(x) dlopen(x,RTLD_NOW)
+ #define DLL_SYM(x,y,z) (x) = (void *)dlsym(y,z)
+ #define DLL_CLOSE(x) dlclose(x)
+ #define DLL_ERROR dlerror
+
+ #ifdef CYGWIN
+ #define DLL_EXT ".dll"
+ #else
+ #define DLL_EXT ".so"
+ #endif
+ #define DLL void *
+
+#endif
+
+////// Plugin Definitions ///////////////////
+
+typedef struct _Plugin {
+ DLL dll;
+ char state;
+ char *filename;
+ struct _Plugin_Info *info;
+ struct _Plugin *next;
+} Plugin;
+
+/////////////////////////////////////////////
+
+int register_plugin_func (char *);
+int register_plugin_event (void (*)(void), char *);
+int plugin_event_trigger (char *);
+
+int export_symbol (void *, int);
+#define EXPORT_SYMBOL(s) export_symbol((s), -1);
+
+Plugin *plugin_open (const char *);
+void plugin_load (const char *);
+void plugin_unload (Plugin *);
+void plugins_init (void);
+void plugins_final (void);
+
+#endif // _PLUGINS_H_
diff --git a/src/common/showmsg.c b/src/common/showmsg.c
new file mode 100644
index 000000000..7d940b189
--- /dev/null
+++ b/src/common/showmsg.c
@@ -0,0 +1,219 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include "showmsg.h"
+
+#ifdef _WIN32
+ #ifdef DEBUGLOGMAP
+ #define DEBUGLOGPATH "log\\map-server.log"
+ #else
+ #ifdef DEBUGLOGCHAR
+ #define DEBUGLOGPATH "log\\char-server.log"
+ #else
+ #ifdef DEBUGLOGLOGIN
+ #define DEBUGLOGPATH "log\\login-server.log"
+ #endif
+ #endif
+ #endif
+#else
+ #ifdef DEBUGLOGMAP
+ #define DEBUGLOGPATH "log/map-server.log"
+ #else
+ #ifdef DEBUGLOGCHAR
+ #define DEBUGLOGPATH "log/char-server.log"
+ #else
+ #ifdef DEBUGLOGLOGIN
+ #define DEBUGLOGPATH "log/login-server.log"
+ #endif
+ #endif
+ #endif
+#endif
+
+int msg_silent; //Specifies how silent the console is.
+char tmp_output[1024] = {"\0"};
+char timestamp_format[20] = ""; //For displaying Timestamps
+// by MC Cameri
+int _vShowMessage(enum msg_type flag, const char *string, va_list ap)
+{
+ // _ShowMessage MUST be used instead of printf as of 10/24/2004.
+ // Return: 0 = Successful, 1 = Failed.
+// int ret = 0;
+ char prefix[100];
+#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
+ FILE *fp;
+#endif
+
+ if (!string || strlen(string) <= 0) {
+ ShowError("Empty string passed to _vShowMessage().\n");
+ return 1;
+ }
+
+ if (timestamp_format)
+ { //Display time format. [Skotlex]
+ time_t t = time(NULL);
+ strftime(prefix, 80, timestamp_format, localtime(&t));
+ } else prefix[0]='\0';
+
+
+ switch (flag) {
+ case MSG_NONE: // direct printf replacement
+ break;
+ case MSG_STATUS: //Bright Green (To inform about good things)
+ strcat(prefix,CL_GREEN"[Status]"CL_RESET":");
+ break;
+ case MSG_SQL: //Bright Violet (For dumping out anything related with SQL) <- Actually, this is mostly used for SQL errors with the database, as successes can as well just be anything else... [Skotlex]
+ strcat(prefix,CL_MAGENTA"[SQL]"CL_RESET":");
+ break;
+ case MSG_INFORMATION: //Bright White (Variable information)
+ strcat(prefix,CL_WHITE"[Info]"CL_RESET":");
+ break;
+ case MSG_NOTICE: //Bright White (Less than a warning)
+ strcat(prefix,CL_WHITE"[Notice]"CL_RESET":");
+ break;
+ case MSG_WARNING: //Bright Yellow
+ strcat(prefix,CL_YELLOW"[Warning]"CL_RESET":");
+ break;
+ case MSG_DEBUG: //Bright Cyan, important stuff!
+ strcat(prefix,CL_CYAN"[Debug]"CL_RESET":");
+ break;
+ case MSG_ERROR: //Bright Red (Regular errors)
+ strcat(prefix,CL_RED"[Error]"CL_RESET":");
+ break;
+ case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible)
+ strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":");
+ break;
+ default:
+ ShowError("In function _vShowMessage() -> Invalid flag passed.\n");
+ return 1;
+ }
+
+ if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) ||
+ (flag == MSG_INFORMATION && msg_silent&1) ||
+ (flag == MSG_STATUS && msg_silent&2) ||
+ (flag == MSG_NOTICE && msg_silent&4) ||
+ (flag == MSG_WARNING && msg_silent&8) ||
+ (flag == MSG_ERROR && msg_silent&16) ||
+ (flag == MSG_SQL && msg_silent&16)
+ ) ; //Do not print it.
+ else {
+ if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL)
+ { //Send Errors to StdErr [Skotlex]
+ fprintf (stderr, "%s ", prefix);
+ vfprintf (stderr, string, ap);
+ fflush (stderr);
+ } else {
+ if (flag != MSG_NONE)
+ printf ("%s ", prefix);
+ vprintf (string, ap);
+ fflush (stdout);
+ }
+ }
+
+#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN)
+ if(strlen(DEBUGLOGPATH) > 0) {
+ fp=fopen(DEBUGLOGPATH,"a");
+ if (fp == NULL) {
+ printf(CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n",DEBUGLOGPATH);
+ fflush(stdout);
+ return 0;
+ }
+ fprintf(fp,"%s ", prefix);
+ vfprintf(fp,string,ap);
+ fclose(fp);
+ } else {
+ printf(CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n");
+ }
+#endif
+
+ va_end(ap);
+/*
+ if ((core_config.debug_output_level > -1) && (flag >= core_config.debug_output_level)) {
+ FILE *fp;
+ fp=fopen(OUTPUT_MESSAGES_LOG,"a");
+ if (fp == NULL) {
+ ShowError("Could not open '"CL_WHITE"%s"CL_RESET"', file not found.\n",OUTPUT_MESSAGES_LOG);
+ fflush(stdout);
+ return;
+ }
+ StripColor(output);
+ strcpy(output,"\r");
+ fwrite(output,strlen(output),1,fp);
+ fclose(fp);
+ }
+*/
+ return 0;
+}
+
+void ClearScreen(void)
+{
+#ifndef _WIN32
+ ShowMessage(CL_CLS); // to prevent empty string passed messages
+#endif
+}
+int _ShowMessage(enum msg_type flag, const char *string, ...)
+{
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(flag, string, ap);
+}
+
+// direct printf replacement
+int ShowMessage(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_NONE, string, ap);
+}
+int ShowStatus(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_STATUS, string, ap);
+}
+int ShowSQL(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_SQL, string, ap);
+}
+int ShowInfo(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_INFORMATION, string, ap);
+}
+int ShowNotice(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_NOTICE, string, ap);
+}
+int ShowWarning(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_WARNING, string, ap);
+}
+int ShowDebug(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_DEBUG, string, ap);
+}
+int ShowError(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_ERROR, string, ap);
+}
+int ShowFatalError(const char *string, ...) {
+ va_list ap;
+
+ va_start(ap, string);
+ return _vShowMessage(MSG_FATALERROR, string, ap);
+}
diff --git a/src/common/showmsg.h b/src/common/showmsg.h
new file mode 100644
index 000000000..af851de40
--- /dev/null
+++ b/src/common/showmsg.h
@@ -0,0 +1,88 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SHOWMSG_H_
+#define _SHOWMSG_H_
+
+#define SHOW_DEBUG_MSG 1
+
+// 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
+
+#ifdef _WIN32
+ #define CL_RESET ""
+ #define CL_CLS ""
+ #define CL_CLL ""
+ #define CL_BOLD ""
+ #define CL_NORMAL CL_RESET
+ #define CL_NONE CL_RESET
+ #define CL_WHITE ""
+ #define CL_GRAY ""
+ #define CL_RED ""
+ #define CL_GREEN ""
+ #define CL_YELLOW ""
+ #define CL_BLUE ""
+ #define CL_MAGENTA ""
+ #define CL_CYAN ""
+ #define CL_BT_YELLOW ""
+ #define CL_WTBL ""
+ #define CL_XXBL ""
+ #define CL_PASS ""
+#else
+ #define CL_RESET "\033[0;0m"
+ #define CL_CLS "\033[2J"
+ #define CL_CLL "\033[K"
+
+ // font settings
+ #define CL_BOLD "\033[1m"
+ #define CL_NORMAL CL_RESET
+ #define CL_NONE CL_RESET
+
+ #define CL_WHITE "\033[1;29m"
+ #define CL_GRAY "\033[1;30m"
+ #define CL_RED "\033[1;31m"
+ #define CL_GREEN "\033[1;32m"
+ #define CL_YELLOW "\033[1;33m"
+ #define CL_BLUE "\033[1;34m"
+ #define CL_MAGENTA "\033[1;35m"
+ #define CL_CYAN "\033[1;36m"
+
+ #define CL_BT_YELLOW "\033[1;33m"
+ #define CL_WTBL "\033[37;44m" // white on blue
+ #define CL_XXBL "\033[0;44m" // default on blue
+ #define CL_PASS "\033[0;32;42m" // green on green
+#endif
+
+extern int msg_silent; //Specifies how silent the console is. [Skotlex]
+extern char timestamp_format[20]; //For displaying Timestamps [Skotlex]
+extern char tmp_output[1024];
+
+enum msg_type {
+ MSG_NONE,
+ MSG_STATUS,
+ MSG_SQL,
+ MSG_INFORMATION,
+ MSG_NOTICE,
+ MSG_WARNING,
+ MSG_DEBUG,
+ MSG_ERROR,
+ MSG_FATALERROR
+};
+
+extern void ClearScreen(void);
+extern int ShowMessage(const char *, ...);
+extern int ShowStatus(const char *, ...);
+extern int ShowSQL(const char *, ...);
+extern int ShowInfo(const char *, ...);
+extern int ShowNotice(const char *, ...);
+extern int ShowWarning(const char *, ...);
+extern int ShowDebug(const char *, ...);
+extern int ShowError(const char *, ...);
+extern int ShowFatalError(const char *, ...);
+
+#endif
diff --git a/src/common/socket.c b/src/common/socket.c
new file mode 100644
index 000000000..2558e83ac
--- /dev/null
+++ b/src/common/socket.c
@@ -0,0 +1,1390 @@
+// 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 <sys/types.h>
+#include <errno.h>
+
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#include <io.h>
+typedef int socklen_t;
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifndef SIOCGIFCONF
+#include <sys/sockio.h> // SIOCGIFCONF on Solaris, maybe others? [Shinomori]
+#endif
+
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+
+#include "../common/socket.h"
+#include "../common/mmo.h" // [Valaris] thanks to fov
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+fd_set readfds;
+int fd_max;
+time_t last_tick;
+time_t stall_time = 60;
+int ip_rules = 1;
+
+// reuse port
+#ifndef SO_REUSEPORT
+ #define SO_REUSEPORT 15
+#endif
+
+// values derived from freya
+// a player that send more than 2k is probably a hacker without be parsed
+// biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes)
+size_t rfifo_size = (16*1024);
+size_t wfifo_size = (16*1024);
+
+#ifndef TCP_FRAME_LEN
+#define TCP_FRAME_LEN 1053
+#endif
+
+#define CONVIP(ip) ip&0xFF,(ip>>8)&0xFF,(ip>>16)&0xFF,ip>>24
+
+struct socket_data *session[FD_SETSIZE];
+
+static int null_parse(int fd);
+static int (*default_func_parse)(int) = null_parse;
+
+static int null_console_parse(char *buf);
+static int (*default_console_parse)(char*) = null_console_parse;
+#ifndef MINICORE
+static int connect_check(unsigned int ip);
+#else
+ #define connect_check(n) 1
+#endif
+
+/*======================================
+ * CORE : Set function
+ *--------------------------------------
+ */
+void set_defaultparse(int (*defaultparse)(int))
+{
+ default_func_parse = defaultparse;
+}
+
+void set_nonblocking(int fd, int yes) {
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+}
+
+static void setsocketopts(int fd)
+{
+ int yes = 1; // reuse fix
+ size_t buff;
+ size_t buff_size = sizeof (buff);
+
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes);
+#ifdef SO_REUSEPORT
+ setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes);
+#endif
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+
+#ifdef __WIN32
+{ //set SO_LINGER option (from Freya)
+ //(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/closesocket_2.asp)
+ struct linger opt;
+ opt.l_onoff = 1;
+ opt.l_linger = 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt)))
+ ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection %d!\n",fd);
+}
+#endif
+
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&wfifo_size , sizeof(wfifo_size ));
+ if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&buff, &buff_size) == 0)
+ {
+ if (buff < wfifo_size) //We are not going to complain if we get more, aight? [Skotlex]
+ ShowError("setsocketopts: Requested send buffer size failed (requested %d bytes buffer, received a buffer of size %d)\n", wfifo_size, buff);
+ }
+ else
+ perror("setsocketopts: getsockopt wfifo");
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &rfifo_size , sizeof(rfifo_size ));
+ if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &buff, &buff_size) == 0)
+ {
+ if (buff < rfifo_size)
+ ShowError("setsocketopts: Requested receive buffer size failed (requested %d bytes buffer, received a buffer of size %d)\n", rfifo_size, buff);
+ }
+ else
+ perror("setsocketopts: getsockopt rfifo");
+}
+
+/*======================================
+ * CORE : Socket Sub Function
+ *--------------------------------------
+ */
+static void set_eof(int fd)
+{ //Marks a connection eof and invokes the parse_function to disconnect it right away. [Skotlex]
+ if (session_isActive(fd))
+ session[fd]->eof=1;
+}
+
+static int recv_to_fifo(int fd)
+{
+ int len;
+
+ if( (fd<0) || (fd>=FD_SETSIZE) || (NULL==session[fd]) )
+ return -1;
+
+ if(session[fd]->eof)
+ return -1;
+
+#ifdef __WIN32
+ len=recv(fd,(char *)session[fd]->rdata+session[fd]->rdata_size, RFIFOSPACE(fd), 0);
+ if (len == SOCKET_ERROR) {
+ if (WSAGetLastError() == WSAECONNABORTED) {
+ ShowWarning("recv_to_fifo: Software caused connection abort on session #%d\n", fd);
+ FD_CLR(fd, &readfds); //Remove the socket so the select() won't hang on it.
+// exit(1); //Windows can't really recover from this one. [Skotlex]
+ }
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+// ShowDebug("recv_to_fifo: error %d, ending connection #%d\n", WSAGetLastError(), fd);
+ set_eof(fd);
+ }
+ return 0;
+ }
+#else
+ len=read(fd,session[fd]->rdata+session[fd]->rdata_size, RFIFOSPACE(fd));
+ if (len == -1)
+ {
+ if (errno == ECONNABORTED)
+ {
+ ShowFatalError("recv_to_fifo: Network broken (Software caused connection abort on session #%d)\n", fd);
+// exit(1); //Temporal debug, maybe this can be fixed.
+ }
+ if (errno != EAGAIN) { //Connection error.
+// perror("closing session: recv_to_fifo");
+ set_eof(fd);
+ }
+ return 0;
+ }
+#endif
+ if (len <= 0) { //Normal connection end.
+ set_eof(fd);
+ return 0;
+ }
+
+ session[fd]->rdata_size+=len;
+ session[fd]->rdata_tick = last_tick;
+ return 0;
+}
+
+static int send_from_fifo(int fd)
+{
+ int len;
+ if( !session_isValid(fd) )
+ return -1;
+
+// if (s->eof) // if we close connection, we can not send last information (you're been disconnected, etc...) [Yor]
+// return -1;
+/*
+ // clear write buffer if not connected <- I really like more the idea of sending the last information. [Skotlex]
+ if( session[fd]->eof )
+ {
+ session[fd]->wdata_size = 0;
+ return -1;
+ }
+*/
+
+ if (session[fd]->wdata_size == 0)
+ return 0;
+
+#ifdef __WIN32
+ len=send(fd, (const char *)session[fd]->wdata,session[fd]->wdata_size, 0);
+ if (len == SOCKET_ERROR) {
+ if (WSAGetLastError() == WSAECONNABORTED) {
+ ShowWarning("send_from_fifo: Software caused connection abort on session #%d\n", fd);
+ session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex]
+ set_eof(fd);
+ FD_CLR(fd, &readfds); //Remove the socket so the select() won't hang on it.
+ }
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+// ShowDebug("send_from_fifo: error %d, ending connection #%d\n", WSAGetLastError(), fd);
+ session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex]
+ set_eof(fd);
+ }
+ return 0;
+ }
+#else
+ len=write(fd,session[fd]->wdata,session[fd]->wdata_size);
+ if (len == -1)
+ {
+ if (errno == ECONNABORTED)
+ {
+ ShowWarning("send_from_fifo: Network broken (Software caused connection abort on session #%d)\n", fd);
+ session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex]
+ set_eof(fd);
+ }
+ if (errno != EAGAIN) {
+// perror("closing session: send_from_fifo");
+ session[fd]->wdata_size = 0; //Clear the send queue as we can't send anymore. [Skotlex]
+ set_eof(fd);
+ }
+ return 0;
+ }
+#endif
+
+ //{ int i; ShowMessage("send %d : ",fd); for(i=0;i<len;i++){ ShowMessage("%02x ",session[fd]->wdata[i]); } ShowMessage("\n");}
+ if(len>0){
+ if((unsigned int)len<session[fd]->wdata_size){
+ memmove(session[fd]->wdata,session[fd]->wdata+len,session[fd]->wdata_size-len);
+ session[fd]->wdata_size-=len;
+ } else {
+ session[fd]->wdata_size=0;
+ }
+ }
+ return 0;
+}
+
+void flush_fifo(int fd)
+{
+ if(session[fd] != NULL && session[fd]->func_send == send_from_fifo)
+ {
+ set_nonblocking(fd, 1);
+ send_from_fifo(fd);
+ set_nonblocking(fd, 0);
+ }
+}
+
+void flush_fifos(void)
+{
+ int i;
+ for(i=1;i<fd_max;i++)
+ if(session[i] != NULL &&
+ session[i]->func_send == send_from_fifo)
+ send_from_fifo(i);
+}
+
+static int null_parse(int fd)
+{
+ ShowMessage("null_parse : %d\n",fd);
+ session[fd]->rdata_pos = session[fd]->rdata_size; //RFIFOSKIP(fd, RFIFOREST(fd)); simplify calculation
+ return 0;
+}
+
+/*======================================
+ * CORE : Socket Function
+ *--------------------------------------
+ */
+
+static int connect_client(int listen_fd)
+{
+ int fd;
+ struct sockaddr_in client_address;
+#ifdef __WIN32
+ int len;
+#else
+ socklen_t len;
+#endif
+ //ShowMessage("connect_client : %d\n",listen_fd);
+
+ len=sizeof(client_address);
+
+ fd = accept(listen_fd,(struct sockaddr*)&client_address,&len);
+#ifdef __WIN32
+ if (fd == SOCKET_ERROR || fd == INVALID_SOCKET || fd < 0) {
+ ShowError("accept failed (code %d)!\n", fd, WSAGetLastError());
+ return -1;
+ }
+#else
+ if(fd==-1) {
+ perror("accept");
+ return -1;
+ }
+#endif
+
+ if(fd_max<=fd) fd_max=fd+1;
+
+ setsocketopts(fd);
+
+#ifdef __WIN32
+ {
+ unsigned long val = 1;
+ if (ioctlsocket(fd, FIONBIO, &val) != 0)
+ ShowError("Couldn't set the socket to non-blocking mode (code %d)!\n", WSAGetLastError());
+ }
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ perror("connect_client (set nonblock)");
+#endif
+
+ if (ip_rules && !connect_check(*(unsigned int*)(&client_address.sin_addr))) {
+ do_close(fd);
+ return -1;
+ } else
+ FD_SET(fd,&readfds);
+
+ CREATE(session[fd], struct socket_data, 1);
+ CREATE_A(session[fd]->rdata, unsigned char, rfifo_size);
+ CREATE_A(session[fd]->wdata, unsigned char, wfifo_size);
+
+ session[fd]->max_rdata = (int)rfifo_size;
+ session[fd]->max_wdata = (int)wfifo_size;
+ session[fd]->func_recv = recv_to_fifo;
+ session[fd]->func_send = send_from_fifo;
+ if(!session[listen_fd]->func_parse)
+ session[fd]->func_parse = default_func_parse;
+ else
+ session[fd]->func_parse = session[listen_fd]->func_parse;
+ session[fd]->client_addr = client_address;
+ session[fd]->rdata_tick = last_tick;
+ session[fd]->type = SESSION_UNKNOWN; // undefined type
+
+ //ShowMessage("new_session : %d %d\n",fd,session[fd]->eof);
+ return fd;
+}
+
+int make_listen_port(int port)
+{
+ struct sockaddr_in server_address;
+ int fd;
+ int result;
+
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+#ifdef __WIN32
+ if (fd == INVALID_SOCKET) {
+ ShowError("socket() creation failed (code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if (fd == -1) {
+ perror("make_listen_port:socket()");
+ exit(1);
+ }
+#endif
+
+#ifdef __WIN32
+ {
+ unsigned long val = 1;
+ if (ioctlsocket(fd, FIONBIO, &val) != 0)
+ ShowError("Couldn't set the socket to non-blocking mode (code %d)!\n", WSAGetLastError());
+ }
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ perror("make_listen_port (set nonblock)");
+#endif
+
+ setsocketopts(fd);
+
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = htonl( INADDR_ANY );
+ server_address.sin_port = htons((unsigned short)port);
+
+ result = bind(fd, (struct sockaddr*)&server_address, sizeof(server_address));
+#ifdef __WIN32
+ if( result == SOCKET_ERROR ) {
+ ShowError("bind failed (socket %d, code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if( result == -1 ) {
+ perror("bind");
+ exit(1);
+ }
+#endif
+ result = listen( fd, 5 );
+#ifdef __WIN32
+ if( result == SOCKET_ERROR ) {
+ ShowError("listen failed (socket %d, code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if( result != 0 ) { /* error */
+ perror("listen");
+ exit(1);
+ }
+#endif
+ if ( fd < 0 || fd > FD_SETSIZE )
+ { //Crazy error that can happen in Windows? (info from Freya)
+ ShowFatalError("listen() returned invalid fd %d!\n",fd);
+ exit(1);
+ }
+
+ if(fd_max<=fd) fd_max=fd+1;
+ FD_SET(fd, &readfds );
+
+ CREATE(session[fd], struct socket_data, 1);
+
+ memset(session[fd],0,sizeof(*session[fd]));
+ session[fd]->func_recv = connect_client;
+
+ return fd;
+}
+
+int make_listen_bind(long ip,int port)
+{
+ struct sockaddr_in server_address;
+ int fd;
+ int result;
+
+ fd = (int)socket( AF_INET, SOCK_STREAM, 0 );
+
+#ifdef __WIN32
+ if (fd == INVALID_SOCKET) {
+ ShowError("socket() creation failed (code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if (fd == -1) {
+ perror("make_listen_port:socket()");
+ exit(1);
+ }
+#endif
+
+#ifdef __WIN32
+ {
+ unsigned long val = 1;
+ if (ioctlsocket(fd, FIONBIO, &val) != 0)
+ ShowError("Couldn't set the socket to non-blocking mode (code %d)!\n", WSAGetLastError());
+ }
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ perror("make_listen_bind (set nonblock)");
+#endif
+
+ setsocketopts(fd);
+
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = ip;
+ server_address.sin_port = htons((unsigned short)port);
+
+ result = bind(fd, (struct sockaddr*)&server_address, sizeof(server_address));
+#ifdef __WIN32
+ if( result == SOCKET_ERROR ) {
+ ShowError("bind failed (socket %d, code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if( result == -1 ) {
+ perror("bind");
+ exit(1);
+ }
+#endif
+ result = listen( fd, 5 );
+#ifdef __WIN32
+ if( result == SOCKET_ERROR ) {
+ ShowError("listen failed (socket %d, code %d)!\n", fd, WSAGetLastError());
+ exit(1);
+ }
+#else
+ if( result != 0) { /* error */
+ perror("listen");
+ exit(1);
+ }
+#endif
+ if ( fd < 0 || fd > FD_SETSIZE )
+ { //Crazy error that can happen in Windows? (info from Freya)
+ ShowFatalError("listen() returned invalid fd %d!\n",fd);
+ exit(1);
+ }
+
+ if(fd_max<=fd) fd_max=fd+1;
+ FD_SET(fd, &readfds );
+
+ CREATE(session[fd], struct socket_data, 1);
+
+ memset(session[fd],0,sizeof(*session[fd]));
+ session[fd]->func_recv = connect_client;
+
+ ShowStatus("Open listen port on %d.%d.%d.%d:%i\n",
+ (ip)&0xFF,(ip>>8)&0xFF,(ip>>16)&0xFF,(ip>>24)&0xFF,port);
+
+ return fd;
+}
+
+// Console Reciever [Wizputer]
+int console_recieve(int i) {
+ int n;
+ char *buf;
+
+ CREATE_A(buf, char, 64);
+ memset(buf,0,sizeof(64));
+
+ n = read(0, buf , 64);
+ if ( n < 0 )
+ ShowError("Console input read error\n");
+ else
+ {
+ ShowNotice ("Sorry, the console is currently non-functional.\n");
+// session[0]->func_console(buf);
+ }
+
+ aFree(buf);
+ return 0;
+}
+
+void set_defaultconsoleparse(int (*defaultparse)(char*))
+{
+ default_console_parse = defaultparse;
+}
+
+static int null_console_parse(char *buf)
+{
+ ShowMessage("null_console_parse : %s\n",buf);
+ return 0;
+}
+
+// function parse table
+// To-do: -- use dynamic arrays
+// -- add a register_parse_func();
+struct func_parse_table func_parse_table[SESSION_MAX];
+
+int default_func_check (struct socket_data *sd) { return 1; }
+
+void func_parse_check (struct socket_data *sd)
+{
+ int i;
+ for (i = SESSION_HTTP; i < SESSION_MAX; i++) {
+ if (func_parse_table[i].func &&
+ func_parse_table[i].check &&
+ func_parse_table[i].check(sd) != 0)
+ {
+ sd->type = i;
+ sd->func_parse = func_parse_table[i].func;
+ return;
+ }
+ }
+
+ // undefined -- treat as raw socket (using default parse)
+ sd->type = SESSION_RAW;
+}
+
+// Console Input [Wizputer]
+int start_console(void) {
+
+ //Until a better plan is came up with... can't be using session[0] anymore! [Skotlex]
+ ShowNotice("The console is currently nonfunctional.\n");
+ return 0;
+
+ FD_SET(0,&readfds);
+
+ if (!session[0]) { // dummy socket already uses fd 0
+ CREATE(session[0], struct socket_data, 1);
+ }
+ memset(session[0],0,sizeof(*session[0]));
+
+ session[0]->func_recv = console_recieve;
+ session[0]->func_console = default_console_parse;
+
+ return 0;
+}
+
+int make_connection(long ip,int port)
+{
+ struct sockaddr_in server_address;
+ int fd;
+ int result;
+
+ fd = (int)socket( AF_INET, SOCK_STREAM, 0 );
+
+#ifdef __WIN32
+ if (fd == INVALID_SOCKET) {
+ ShowError("socket() creation failed (code %d)!\n", fd, WSAGetLastError());
+ return -1;
+ }
+#else
+ if (fd == -1) {
+ perror("make_connection:socket()");
+ return -1;
+ }
+#endif
+
+ setsocketopts(fd);
+
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = ip;
+ server_address.sin_port = htons((unsigned short)port);
+
+ ShowStatus("Connecting to %d.%d.%d.%d:%i\n",
+ (ip)&0xFF,(ip>>8)&0xFF,(ip>>16)&0xFF,(ip>>24)&0xFF,port);
+
+ result = connect(fd, (struct sockaddr *)(&server_address), sizeof(struct sockaddr_in));
+#ifdef __WIN32
+ if( result == SOCKET_ERROR ) {
+ ShowError("connect failed (socket %d, code %d)!\n", fd, WSAGetLastError());
+ do_close(fd);
+ return -1;
+ }
+#else
+ if (result < 0) { //This is only used when the map/char server try to connect to each other, so it can be handled. [Skotlex]
+ perror("make_connection");
+ do_close(fd);
+ return -1;
+ }
+#endif
+//Now the socket can be made non-blocking. [Skotlex]
+#ifdef __WIN32
+ {
+ unsigned long val = 1;
+ if (ioctlsocket(fd, FIONBIO, &val) != 0)
+ ShowError("Couldn't set the socket to non-blocking mode (code %d)!\n", WSAGetLastError());
+ }
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
+ perror("make_connection (set nonblock)");
+#endif
+
+ if (fd_max <= fd)
+ fd_max = fd + 1;
+ FD_SET(fd,&readfds);
+
+ CREATE(session[fd], struct socket_data, 1);
+ CREATE_A(session[fd]->rdata, unsigned char, rfifo_size);
+ CREATE_A(session[fd]->wdata, unsigned char, wfifo_size);
+
+ session[fd]->max_rdata = (int)rfifo_size;
+ session[fd]->max_wdata = (int)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]->rdata_tick = last_tick;
+
+ return fd;
+}
+
+int delete_session(int fd)
+{
+ if (fd <= 0 || fd >= FD_SETSIZE)
+ return -1;
+ FD_CLR(fd, &readfds);
+ if (session[fd]){
+ if (session[fd]->rdata)
+ aFree(session[fd]->rdata);
+ if (session[fd]->wdata)
+ aFree(session[fd]->wdata);
+ if (session[fd]->session_data)
+ aFree(session[fd]->session_data);
+ aFree(session[fd]);
+ session[fd] = NULL;
+ }
+ //ShowMessage("delete_session:%d\n",fd);
+ return 0;
+}
+
+int realloc_fifo(int fd,unsigned int rfifo_size,unsigned int wfifo_size)
+{
+ if( !session_isValid(fd) )
+ return 0;
+
+ if( session[fd]->max_rdata != rfifo_size && session[fd]->rdata_size < rfifo_size){
+ RECREATE(session[fd]->rdata, unsigned char, rfifo_size);
+ session[fd]->max_rdata = rfifo_size;
+ }
+
+ if( session[fd]->max_wdata != wfifo_size && session[fd]->wdata_size < wfifo_size){
+ RECREATE(session[fd]->wdata, unsigned char, wfifo_size);
+ session[fd]->max_wdata = wfifo_size;
+ }
+ return 0;
+}
+
+int realloc_writefifo(int fd, size_t addition)
+{
+ size_t newsize;
+
+ if( !session_isValid(fd) ) // might not happen
+ return 0;
+
+ if( session[fd]->wdata_size + (int)addition > session[fd]->max_wdata )
+ { // grow rule; grow in multiples of wfifo_size
+ newsize = wfifo_size;
+ while( session[fd]->wdata_size + addition > newsize ) newsize += newsize;
+ }
+ else if( session[fd]->max_wdata>=FIFOSIZE_SERVERLINK) {
+ //Inter-server adjust. [Skotlex]
+ if ((session[fd]->wdata_size+(int)addition)*4 < session[fd]->max_wdata)
+ newsize = session[fd]->max_wdata/2;
+ else
+ return 0; //No change
+ } else if( session[fd]->max_wdata>(int)wfifo_size &&
+ (session[fd]->wdata_size+(int)addition)*4 < session[fd]->max_wdata )
+ { // shrink rule, shrink by 2 when only a quater of the fifo is used, don't shrink below 4*addition
+ newsize = session[fd]->max_wdata/2;
+ }
+ else // no change
+ return 0;
+
+ RECREATE(session[fd]->wdata, unsigned char, newsize);
+ session[fd]->max_wdata = (int)newsize;
+
+ return 0;
+}
+
+int WFIFOSET(int fd,int len)
+{
+ size_t newreserve;
+ struct socket_data *s = session[fd];
+
+ if( !session_isValid(fd) || s->wdata == NULL )
+ return 0;
+
+ // we have written len bytes to the buffer already before calling WFIFOSET
+ if(s->wdata_size+len > s->max_wdata)
+ { // actually there was a buffer overflow already
+ unsigned char *sin_addr = (unsigned char *)&s->client_addr.sin_addr;
+ ShowFatalError("socket: Buffer Overflow. Connection %d (%d.%d.%d.%d) has written %d byteson a %d/%d bytes buffer.\n", fd,
+ sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3], len, s->wdata_size, s->max_wdata);
+ ShowDebug("Likely command that caused it: 0x%x\n", WFIFOW(fd,0));
+ // no other chance, make a better fifo model
+ exit(1);
+ }
+
+ s->wdata_size += len;
+ // always keep a wfifo_size reserve in the buffer
+ // For inter-server connections, let the reserve be 1/8th of the link size.
+ newreserve = s->wdata_size + (s->max_wdata>=FIFOSIZE_SERVERLINK?FIFOSIZE_SERVERLINK<<3:wfifo_size);
+
+ if (s->wdata_size > (TCP_FRAME_LEN))
+ send_from_fifo(fd);
+
+ // realloc after sending
+ // readfifo does not need to be realloced at all
+ // Even the inter-server buffer may need reallocating! [Skotlex]
+ realloc_writefifo(fd, newreserve);
+
+ return 0;
+}
+
+int do_sendrecv(int next)
+{
+ fd_set rfd,wfd,efd; //Added the Error Set so that such sockets can be made eof. They are the same as the rfd for now. [Skotlex]
+ struct timeval timeout;
+ int ret,i;
+
+ last_tick = time(0);
+
+ //memcpy(&rfd, &readfds, sizeof(rfd));
+ //memcpy(&efd, &readfds, sizeof(efd));
+ FD_ZERO(&wfd);
+
+ for (i = 1; i < fd_max; i++){ //Session 0 is never a valid session, so it's best to skip it. [Skotlex]
+ if(!session[i]) {
+ if (FD_ISSET(i, &readfds)){
+ ShowDebug("force clear fds %d\n", i);
+ FD_CLR(i, &readfds);
+ //FD_CLR(i, &rfd);
+ //FD_CLR(i, &efd);
+ }
+ continue;
+ }
+ if(session[i]->wdata_size)
+ FD_SET(i, &wfd);
+ }
+
+ timeout.tv_sec = next/1000;
+ timeout.tv_usec = next%1000*1000;
+ memcpy(&rfd, &readfds, sizeof(rfd));
+ memcpy(&efd, &readfds, sizeof(efd));
+ ret = select(fd_max, &rfd, &wfd, &efd, &timeout);
+
+#ifdef __WIN32
+ if (ret == SOCKET_ERROR) {
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ return 0; //Eh... try again later?
+ ShowError("do_sendrecv: select error (code %d)\n", WSAGetLastError());
+#else
+ if (ret < 0) {
+ perror("do_sendrecv");
+ if (errno == 11) //Isn't there a constantI can use instead of this hardcoded value? This should be "resource temporarily unavailable": ie: try again.
+ return 0;
+#endif
+
+ //if error, remove invalid connections
+ //Individual socket handling code shamelessly assimilated from Freya :3
+ // an error give invalid values in fd_set structures -> init them again
+ FD_ZERO(&rfd);
+ FD_ZERO(&wfd);
+ FD_ZERO(&efd);
+ for(i = 1; i < fd_max; i++) { //Session 0 is not parsed, it's a 'vacuum' for disconnected sessions. [Skotlex]
+ if (!session[i]) {
+#ifdef __WIN32
+ //Debug to locate runaway sockets in Windows [Skotlex]
+ if (FD_ISSET(i, &readfds)) {
+ FD_CLR(i, &readfds);
+ ShowDebug("Socket %d was set (read fifos) without a session, removed.\n", i);
+ }
+#endif
+ continue;
+ }
+ if (FD_ISSET(i, &readfds)){
+ FD_SET(i, &rfd);
+ FD_SET(i, &efd);
+ }
+ if (session[i]->wdata_size)
+ FD_SET(i, &wfd);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select(i + 1, &rfd, &wfd, &efd, &timeout) >= 0 && !FD_ISSET(i, &efd)) {
+ if (FD_ISSET(i, &wfd)) {
+ if (session[i]->func_send)
+ session[i]->func_send(i);
+ FD_CLR(i, &wfd);
+ }
+ if (FD_ISSET(i, &rfd)) {
+ if (session[i]->func_recv)
+ session[i]->func_recv(i);
+ FD_CLR(i, &rfd);
+ }
+ FD_CLR(i, &efd);
+ } else {
+ ShowDebug("do_sendrecv: Session #%d caused error in select(), disconnecting.\n", i);
+ set_eof(i); // set eof
+ // an error gives invalid values in fd_set structures -> init them again
+ FD_ZERO(&rfd);
+ FD_ZERO(&wfd);
+ FD_ZERO(&efd);
+ }
+ }
+ return 0;
+ }else if(ret > 0) {
+ for (i = 1; i < fd_max; i++){
+ if(!session[i])
+ continue;
+
+ if(FD_ISSET(i,&efd)){
+ //ShowMessage("error:%d\n",i);
+ ShowDebug("do_sendrecv: Connection error on Session %d.\n", i);
+ set_eof(i);
+ continue;
+ }
+
+ if (FD_ISSET(i, &wfd)) {
+ //ShowMessage("write:%d\n",i);
+ if(session[i]->func_send)
+ session[i]->func_send(i);
+ }
+
+ if(FD_ISSET(i,&rfd)){
+ //ShowMessage("read:%d\n",i);
+ if(session[i]->func_recv)
+ session[i]->func_recv(i);
+ }
+
+
+ if(session[i] && session[i]->eof) //The session check is for when the connection ended in func_parse
+ { //Finally, even if there is no data to parse, connections signalled eof should be closed, so we call parse_func [Skotlex]
+ if (session[i]->func_parse)
+ session[i]->func_parse(i); //This should close the session inmediately.
+ }
+ } // for (i = 0
+ }
+ return 0;
+}
+
+int do_parsepacket(void)
+{
+ int i;
+ struct socket_data *sd;
+ for(i = 1; i < fd_max; i++){
+ sd = session[i];
+ if(!sd)
+ continue;
+ if ((sd->rdata_tick != 0) && DIFF_TICK(last_tick,sd->rdata_tick) > stall_time) {
+ ShowInfo ("Session #%d timed out\n", i);
+ sd->eof = 1;
+ }
+ if(sd->rdata_size == 0 && sd->eof == 0)
+ continue;
+ if(sd->func_parse){
+ if(sd->type == SESSION_UNKNOWN)
+ func_parse_check(sd);
+ if(sd->type != SESSION_UNKNOWN)
+ sd->func_parse(i);
+ if(!session[i])
+ continue;
+ /* after parse, check client's RFIFO size to know if there is an invalid packet (too big and not parsed) */
+ if (session[i]->rdata_size == rfifo_size && session[i]->max_rdata == rfifo_size) {
+ session[i]->eof = 1;
+ continue;
+ }
+ }
+ RFIFOHEAD(i);
+ RFIFOFLUSH(i);
+ }
+ return 0;
+}
+
+/* DDoS UŒ‚‘Îô */
+#ifndef MINICORE
+enum {
+ ACO_DENY_ALLOW=0,
+ ACO_ALLOW_DENY,
+ ACO_MUTUAL_FAILTURE,
+};
+
+struct _access_control {
+ unsigned int ip;
+ unsigned int mask;
+};
+
+static struct _access_control *access_allow;
+static struct _access_control *access_deny;
+static int access_order=ACO_DENY_ALLOW;
+static int access_allownum=0;
+static int access_denynum=0;
+static int access_debug=0;
+static int ddos_count = 10;
+static int ddos_interval = 3000;
+static int ddos_autoreset = 600*1000;
+
+struct _connect_history {
+ struct _connect_history *next;
+ struct _connect_history *prev;
+ int status;
+ int count;
+ unsigned int ip;
+ unsigned int tick;
+};
+static struct _connect_history *connect_history[0x10000];
+static int connect_check_(unsigned int ip);
+
+// Ú‘±‚Å‚«‚é‚©‚Ç‚¤‚©‚ÌŠm”F
+// false : Ú‘±OK
+// true : Ú‘±NG
+static int connect_check(unsigned int ip) {
+ int result = connect_check_(ip);
+ if(access_debug) {
+ ShowMessage("connect_check: Connection from %d.%d.%d.%d %s\n",
+ CONVIP(ip),result ? "allowed." : "denied!");
+ }
+ return result;
+}
+
+static int connect_check_(unsigned int ip) {
+ struct _connect_history *hist = connect_history[ip & 0xFFFF];
+ struct _connect_history *hist_new;
+ int i,is_allowip = 0,is_denyip = 0,connect_ok = 0;
+
+ // allow , deny ƒŠƒXƒg‚É“ü‚Á‚Ä‚¢‚é‚©Šm”F
+ for(i = 0;i < access_allownum; i++) {
+ if((ip & access_allow[i].mask) == (access_allow[i].ip & access_allow[i].mask)) {
+ if(access_debug) {
+ ShowMessage("connect_check: Found match from allow list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n",
+ CONVIP(ip),
+ CONVIP(access_allow[i].ip),
+ CONVIP(access_allow[i].mask));
+ }
+ is_allowip = 1;
+ break;
+ }
+ }
+ for(i = 0;i < access_denynum; i++) {
+ if((ip & access_deny[i].mask) == (access_deny[i].ip & access_deny[i].mask)) {
+ if(access_debug) {
+ ShowMessage("connect_check: Found match from deny list:%d.%d.%d.%d IP:%d.%d.%d.%d Mask:%d.%d.%d.%d\n",
+ CONVIP(ip),
+ CONVIP(access_deny[i].ip),
+ CONVIP(access_deny[i].mask));
+ }
+ is_denyip = 1;
+ break;
+ }
+ }
+ // ƒRƒlƒNƒgo—ˆ‚é‚©‚Ç‚¤‚©Šm”F
+ // connect_ok
+ // 0 : –³ðŒ‚É‹‘”Û
+ // 1 : “c‘ã–Cƒ`ƒFƒbƒN‚ÌŒ‹‰ÊŽŸ‘æ
+ // 2 : –³ðŒ‚É‹–‰Â
+ switch(access_order) {
+ case ACO_DENY_ALLOW:
+ default:
+ if(is_allowip) {
+ connect_ok = 2;
+ } else if(is_denyip) {
+ connect_ok = 0;
+ } else {
+ connect_ok = 1;
+ }
+ break;
+ case ACO_ALLOW_DENY:
+ if(is_denyip) {
+ connect_ok = 0;
+ } else if(is_allowip) {
+ connect_ok = 2;
+ } else {
+ connect_ok = 1;
+ }
+ break;
+ case ACO_MUTUAL_FAILTURE:
+ if(is_allowip) {
+ connect_ok = 2;
+ } else {
+ connect_ok = 0;
+ }
+ break;
+ }
+
+ // Ú‘±—š—ð‚𒲂ׂé
+ while(hist) {
+ if(ip == hist->ip) {
+ // “¯‚¶IP”­Œ©
+ if(hist->status) {
+ // ban ƒtƒ‰ƒO‚ª—§‚Á‚Ä‚é
+ return (connect_ok == 2 ? 1 : 0);
+ } else if(DIFF_TICK(gettick(),hist->tick) < ddos_interval) {
+ // ddos_interval•bˆÈ“à‚ɃŠƒNƒGƒXƒg—L‚è
+ hist->tick = gettick();
+ if(hist->count++ >= ddos_count) {
+ // ddos UŒ‚‚ðŒŸo
+ hist->status = 1;
+ ShowWarning("connect_check: DDOS Attack detected from %d.%d.%d.%d!\n",
+ CONVIP(ip));
+ return (connect_ok == 2 ? 1 : 0);
+ } else {
+ return connect_ok;
+ }
+ } else {
+ // ddos_interval•bˆÈ“à‚ɃŠƒNƒGƒXƒg–³‚¢‚̂Ń^ƒCƒ}[ƒNƒŠƒA
+ hist->tick = gettick();
+ hist->count = 0;
+ return connect_ok;
+ }
+ }
+ hist = hist->next;
+ }
+ // IPƒŠƒXƒg‚É–³‚¢‚Ì‚ÅV‹Kì¬
+ hist_new = (struct _connect_history *) aCalloc(1,sizeof(struct _connect_history));
+ hist_new->ip = ip;
+ hist_new->tick = gettick();
+ if(connect_history[ip & 0xFFFF] != NULL) {
+ hist = connect_history[ip & 0xFFFF];
+ hist->prev = hist_new;
+ hist_new->next = hist;
+ }
+ connect_history[ip & 0xFFFF] = hist_new;
+ return connect_ok;
+}
+
+static int connect_check_clear(int tid,unsigned int tick,int id,int data) {
+ int i;
+ int clear = 0;
+ int list = 0;
+ struct _connect_history *hist , *hist2;
+ for(i = 0;i < 0x10000 ; i++) {
+ hist = connect_history[i];
+ while(hist) {
+ if ((DIFF_TICK(tick,hist->tick) > ddos_interval * 3 && !hist->status) ||
+ (DIFF_TICK(tick,hist->tick) > ddos_autoreset && hist->status)) {
+ // clear data
+ hist2 = hist->next;
+ if(hist->prev) {
+ hist->prev->next = hist->next;
+ } else {
+ connect_history[i] = hist->next;
+ }
+ if(hist->next) {
+ hist->next->prev = hist->prev;
+ }
+ aFree(hist);
+ hist = hist2;
+ clear++;
+ } else {
+ hist = hist->next;
+ }
+ list++;
+ }
+ }
+ if(access_debug) {
+ ShowMessage("connect_check_clear: Cleared %d of %d from IP list.\n", clear, list);
+ }
+ return list;
+}
+
+// IPƒ}ƒXƒNƒ`ƒFƒbƒN
+int access_ipmask(const char *str,struct _access_control* acc)
+{
+ unsigned int mask=0,i=0,m,ip, a0,a1,a2,a3;
+ if( !strcmp(str,"all") ) {
+ ip = 0;
+ mask = 0;
+ } else {
+ if( sscanf(str,"%d.%d.%d.%d%n",&a0,&a1,&a2,&a3,&i)!=4 || i==0) {
+ ShowError("access_ipmask: Unknown format %s!\n",str);
+ return 0;
+ }
+ ip = (a3 << 24) | (a2 << 16) | (a1 << 8) | a0;
+
+ if(sscanf(str+i,"/%d.%d.%d.%d",&a0,&a1,&a2,&a3)==4 ){
+ mask = (a3 << 24) | (a2 << 16) | (a1 << 8) | a0;
+ } else if(sscanf(str+i,"/%d",&m) == 1) {
+ for(i=0;i<m;i++) {
+ mask = (mask >> 1) | 0x80000000;
+ }
+ mask = ntohl(mask);
+ } else {
+ mask = 0xFFFFFFFF;
+ }
+ }
+ if(access_debug) {
+ ShowMessage("access_ipmask: Loaded IP:%d.%d.%d.%d mask:%d.%d.%d.%d\n",
+ CONVIP(ip), CONVIP(mask));
+ }
+ acc->ip = ip;
+ acc->mask = mask;
+ return 1;
+}
+#endif
+
+int socket_config_read(const char *cfgName) {
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+ fp=fopen(cfgName, "r");
+ if(fp==NULL){
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"stall_time")==0){
+ stall_time = atoi(w2);
+ #ifndef MINICORE
+ } else if(strcmpi(w1,"enable_ip_rules")==0){
+ if(strcmpi(w2,"yes")==0)
+ ip_rules = 1;
+ else if(strcmpi(w2,"no")==0)
+ ip_rules = 0;
+ else ip_rules = atoi(w2);
+ } else if(strcmpi(w1,"order")==0){
+ access_order=atoi(w2);
+ if(strcmpi(w2,"deny,allow")==0) access_order=ACO_DENY_ALLOW;
+ if(strcmpi(w2,"allow,deny")==0) access_order=ACO_ALLOW_DENY;
+ if(strcmpi(w2,"mutual-failure")==0) access_order=ACO_MUTUAL_FAILTURE;
+ } else if(strcmpi(w1,"allow")==0){
+ access_allow = (struct _access_control *) aRealloc(access_allow,(access_allownum+1)*sizeof(struct _access_control));
+ if(access_ipmask(w2,&access_allow[access_allownum])) {
+ access_allownum++;
+ }
+ } else if(strcmpi(w1,"deny")==0){
+ access_deny = (struct _access_control *) aRealloc(access_deny,(access_denynum+1)*sizeof(struct _access_control));
+ if(access_ipmask(w2,&access_deny[access_denynum])) {
+ access_denynum++;
+ }
+ } else if(!strcmpi(w1,"ddos_interval")){
+ ddos_interval = atoi(w2);
+ } else if(!strcmpi(w1,"ddos_count")){
+ ddos_count = atoi(w2);
+ } else if(!strcmpi(w1,"ddos_autoreset")){
+ ddos_autoreset = atoi(w2);
+ } else if(!strcmpi(w1,"debug")){
+ if(strcmpi(w2,"yes")==0)
+ access_debug = 1;
+ else if(strcmpi(w2,"no")==0)
+ access_debug = 0;
+ else access_debug = atoi(w2);
+ #endif
+ } else if (strcmpi(w1, "import") == 0)
+ socket_config_read(w2);
+ }
+ fclose(fp);
+ return 0;
+}
+
+int RFIFOSKIP(int fd,int len)
+{
+ struct socket_data *s;
+
+ if ( !session_isActive(fd) ) //Nullpo error here[Kevin]
+ return 0;
+
+ s = session[fd];
+
+ if (s->rdata_size-s->rdata_pos-len<0) {
+ //fprintf(stderr,"too many skip\n");
+ //exit(1);
+ //better than a COMPLETE program abort // TEST! :)
+ ShowError("too many skip (%d) now skipped: %d (FD: %d)\n", len, RFIFOREST(fd), fd);
+ len = RFIFOREST(fd);
+ }
+ s->rdata_pos = s->rdata_pos+len;
+ return 0;
+}
+
+
+unsigned int addr_[16]; // ip addresses of local host (host byte order)
+unsigned int naddr_ = 0; // # of ip addresses
+
+void socket_final (void)
+{
+ int i;
+#ifndef MINICORE
+ struct _connect_history *hist , *hist2;
+ for(i = 0; i < 0x10000; i++) {
+ hist = connect_history[i];
+ while(hist) {
+ hist2 = hist->next;
+ aFree(hist);
+ hist = hist2;
+ }
+ }
+ if (access_allow)
+ aFree(access_allow);
+ if (access_deny)
+ aFree(access_deny);
+#endif
+
+ for (i = 1; i < fd_max; i++) {
+ if(session[i])
+ delete_session(i);
+ }
+
+ // session[0] ‚̃_ƒ~[ƒf[ƒ^‚ðíœ
+ aFree(session[0]->rdata);
+ aFree(session[0]->wdata);
+ aFree(session[0]);
+}
+
+//Closes a socket.
+//Needed to simplify shutdown code as well as manage the subtle differences in socket management from Windows and *nix.
+void do_close(int fd)
+{
+//We don't really care if these closing functions return an error, we are just shutting down and not reusing this socket.
+#ifdef __WIN32
+// shutdown(fd, SD_BOTH); //FIXME: Shutdown requires winsock2.h! What would be the proper shutting down method for winsock1?
+ if (session[fd] && session[fd]->func_send == send_from_fifo)
+ session[fd]->func_send(fd); //Flush the data as it is gonna be closed down, but it may not succeed as it is a nonblocking socket! [Skotlex]
+ closesocket(fd);
+#else
+ if (close(fd))
+ perror("do_close: close");
+#endif
+ if (session[fd])
+ delete_session(fd);
+}
+
+void socket_init (void)
+{
+ char *SOCKET_CONF_FILENAME = "conf/packet_athena.conf";
+#ifdef __WIN32
+ char** a;
+ unsigned int i;
+ char fullhost[255];
+ struct hostent* hent;
+
+ /* Start up the windows networking */
+ WORD version_wanted = MAKEWORD(1, 1); //Demand at least WinSocket version 1.1 (from Freya)
+ WSADATA wsaData;
+
+ if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
+ ShowFatalError("SYSERR: WinSock not available!\n");
+ exit(1);
+ }
+
+ if(gethostname(fullhost, sizeof(fullhost)) == SOCKET_ERROR) {
+ ShowError("Ugg.. no hostname defined!\n");
+ return;
+ }
+
+ // XXX This should look up the local IP addresses in the registry
+ // instead of calling gethostbyname. However, the way IP addresses
+ // are stored in the registry is annoyingly complex, so I'll leave
+ // this as T.B.D.
+ hent = gethostbyname(fullhost);
+ if (hent == NULL) {
+ ShowError("Cannot resolve our own hostname to a IP address");
+ return;
+ }
+
+ a = hent->h_addr_list;
+ for(i = 0; a[i] != 0 && i < 16; ++i) {
+ unsigned long addr1 = ntohl(*(unsigned long*) a[i]);
+ addr_[i] = addr1;
+ }
+ naddr_ = i;
+#else
+ int pos;
+ int fdes = socket(AF_INET, SOCK_STREAM, 0);
+ char buf[16 * sizeof(struct ifreq)];
+ struct ifconf ic;
+
+ // The ioctl call will fail with Invalid Argument if there are more
+ // interfaces than will fit in the buffer
+ ic.ifc_len = sizeof(buf);
+ ic.ifc_buf = buf;
+ if(ioctl(fdes, SIOCGIFCONF, &ic) == -1) {
+ ShowError("SIOCGIFCONF failed!\n");
+ return;
+ }
+
+ for(pos = 0; pos < ic.ifc_len;)
+ {
+ struct ifreq * ir = (struct ifreq *) (ic.ifc_buf + pos);
+
+ struct sockaddr_in * a = (struct sockaddr_in *) &(ir->ifr_addr);
+
+ if(a->sin_family == AF_INET) {
+ u_long ad = ntohl(a->sin_addr.s_addr);
+ if(ad != INADDR_LOOPBACK) {
+ addr_[naddr_ ++] = ad;
+ if(naddr_ == 16)
+ break;
+ }
+ }
+
+ #if defined(_AIX) || defined(__APPLE__)
+ pos += ir->ifr_addr.sa_len; // For when we port athena to run on Mac's :)
+ pos += sizeof(ir->ifr_name);
+ #else
+ pos += sizeof(struct ifreq);
+ #endif
+ }
+#endif
+
+ FD_ZERO(&readfds);
+
+ socket_config_read(SOCKET_CONF_FILENAME);
+
+ // initialise last send-receive tick
+ last_tick = time(0);
+
+ // session[0] Was for the console (whatever that was?), but is now currently used for disconnected sessions of the map
+ // server, and as such, should hold enough buffer (it is a vacuum so to speak) as it is never flushed. [Skotlex]
+ CREATE(session[0], struct socket_data, 1);
+ CREATE_A(session[0]->rdata, unsigned char, 2*rfifo_size);
+ CREATE_A(session[0]->wdata, unsigned char, 2*wfifo_size);
+ session[0]->max_rdata = (int)2*rfifo_size;
+ session[0]->max_wdata = (int)2*wfifo_size;
+
+ memset (func_parse_table, 0, sizeof(func_parse_table));
+ func_parse_table[SESSION_RAW].check = default_func_check;
+ func_parse_table[SESSION_RAW].func = default_func_parse;
+
+#ifndef MINICORE
+ // ‚Æ‚è‚ ‚¦‚¸‚T•ª‚²‚Æ‚É•s—v‚ȃf[ƒ^‚ð휂·‚é
+ add_timer_func_list(connect_check_clear, "connect_check_clear");
+ add_timer_interval(gettick()+1000,connect_check_clear,0,0,300*1000);
+#endif
+}
+
+
+bool session_isValid(int fd)
+{ //End of Exam has pointed out that fd==0 is actually an unconnected session! [Skotlex]
+ //But this is not so true, it is used... for... something. The console uses it, would this not cause problems? [Skotlex]
+ return ( (fd>0) && (fd<FD_SETSIZE) && (NULL!=session[fd]) );
+}
+
+bool session_isActive(int fd)
+{
+ return ( session_isValid(fd) && !session[fd]->eof );
+}
diff --git a/src/common/socket.h b/src/common/socket.h
new file mode 100644
index 000000000..ba27e34a8
--- /dev/null
+++ b/src/common/socket.h
@@ -0,0 +1,189 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <stdio.h>
+
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+#include <time.h>
+#include "malloc.h"
+
+extern time_t last_tick;
+extern time_t stall_time;
+
+// define declaration
+
+#define RFIFOSPACE(fd) (session[fd]->max_rdata-session[fd]->rdata_size)
+#ifdef TURBO
+#define RFIFOHEAD(fd) char *rbPtr = session[fd]->rdata+session[fd]->rdata_pos
+#define RFIFOP(fd,pos) (&rbPtr[pos])
+#else
+//Make it a comment so it does not disrupts the rest of code.
+#define RFIFOHEAD(fd) //
+#define RFIFOP(fd,pos) (session[fd]->rdata+session[fd]->rdata_pos+(pos))
+#endif
+// use function instead of macro.
+#define RFIFOB(fd,pos) (*(unsigned char*)RFIFOP(fd,pos))
+#define RFIFOW(fd,pos) (*(unsigned short*)RFIFOP(fd,pos))
+#define RFIFOL(fd,pos) (*(unsigned long*)RFIFOP(fd,pos))
+#define RFIFOREST(fd) (session[fd]->rdata_size-session[fd]->rdata_pos)
+#define RFIFOFLUSH(fd) (memmove(session[fd]->rdata,RFIFOP(fd,0),RFIFOREST(fd)),session[fd]->rdata_size=RFIFOREST(fd),session[fd]->rdata_pos=0)
+//#define RFIFOSKIP(fd,len) ((session[fd]->rdata_size-session[fd]->rdata_pos-(len)<0) ? (fprintf(stderr,"too many skip\n"),exit(1)) : (session[fd]->rdata_pos+=(len)))
+
+#define RBUFP(p,pos) (((unsigned char*)(p))+(pos))
+#define RBUFB(p,pos) (*(unsigned char*)RBUFP((p),(pos)))
+#define RBUFW(p,pos) (*(unsigned short*)RBUFP((p),(pos)))
+#define RBUFL(p,pos) (*(unsigned long*)RBUFP((p),(pos)))
+
+#define WFIFOSPACE(fd) (session[fd]->max_wdata-session[fd]->wdata_size)
+#ifdef TURBO
+#define WFIFOHEAD(fd, x) char *wbPtr = session[fd]->wdata+session[fd]->wdata_size;
+#define WFIFOP(fd,pos) (&wbPtr[pos])
+#else
+#define WFIFOHEAD(fd, x) ;
+#define WFIFOP(fd,pos) (session[fd]->wdata+session[fd]->wdata_size+(pos))
+#endif
+#define WFIFOB(fd,pos) (*(unsigned char*)WFIFOP(fd,pos))
+#define WFIFOW(fd,pos) (*(unsigned short*)WFIFOP(fd,pos))
+#define WFIFOL(fd,pos) (*(unsigned long*)WFIFOP(fd,pos))
+// use function instead of macro.
+//#define WFIFOSET(fd,len) (session[fd]->wdata_size = (session[fd]->wdata_size + (len) + 2048 < session[fd]->max_wdata) ? session[fd]->wdata_size + len : session[fd]->wdata_size)
+#define WBUFP(p,pos) (((unsigned char*)(p)) + (pos))
+#define WBUFB(p,pos) (*(unsigned char*)((p) + (pos)))
+#define WBUFW(p,pos) (*(unsigned short*)((p) + (pos)))
+#define WBUFL(p,pos) (*(unsigned long*)((p) + (pos)))
+
+//FD_SETSIZE must be modified on the project files/Makefile, since a change here won't affect
+// dependant windows libraries.
+/*
+#ifdef __WIN32
+//The default FD_SETSIZE is kinda small for windows systems.
+ #ifdef FD_SETSIZE
+ #undef FD_SETSIZE
+ #endif
+#define FD_SETSIZE 4096
+#endif
+*/
+#ifdef __INTERIX
+#define FD_SETSIZE 4096
+#endif // __INTERIX
+
+/* Removed Cygwin FD_SETSIZE declarations, now are directly passed on to the compiler through Makefile [Valaris] */
+
+// Session type
+enum SessionType {
+ SESSION_UNKNOWN = -1,
+ SESSION_RAW = 0,
+ SESSION_HTTP = 1,
+//-----------------
+ SESSION_MAX = 2
+};
+
+// Struct declaration
+
+struct socket_data{
+ unsigned char eof;
+ unsigned char *rdata, *wdata;
+ unsigned int max_rdata, max_wdata;
+ unsigned int rdata_size, wdata_size;
+ int rdata_pos;
+ time_t rdata_tick;
+ struct sockaddr_in client_addr;
+ int (*func_recv)(int);
+ int (*func_send)(int);
+ int (*func_parse)(int);
+ int (*func_console)(char*);
+ void* session_data;
+ void* session_data2;
+ enum SessionType type;
+};
+
+// Parse functions table
+struct func_parse_table {
+ int (*func)(int);
+ int (*check)(struct socket_data *);
+};
+extern struct func_parse_table func_parse_table[SESSION_MAX];
+
+
+// Data prototype declaration
+
+extern struct socket_data *session[FD_SETSIZE];
+
+extern int fd_max;
+
+
+
+
+
+/////////////////////////////
+// for those still not building c++
+#ifndef __cplusplus
+//////////////////////////////
+
+// boolean types for C
+typedef int bool;
+#define false (1==0)
+#define true (1==1)
+
+//////////////////////////////
+#endif // not cplusplus
+//////////////////////////////
+
+
+
+//////////////////////////////////
+// some checking on sockets
+extern bool session_isValid(int fd);
+extern bool session_isActive(int fd);
+//////////////////////////////////
+
+
+
+
+
+
+
+
+
+
+// Function prototype declaration
+
+int make_listen_port(int);
+int make_listen_bind(long,int);
+int make_connection(long,int);
+int delete_session(int);
+int realloc_fifo(int fd,unsigned int rfifo_size,unsigned int wfifo_size);
+int realloc_writefifo(int fd, size_t addition);
+int WFIFOSET(int fd,int len);
+int RFIFOSKIP(int fd,int len);
+
+int do_sendrecv(int next);
+int do_parsepacket(void);
+void do_close(int fd);
+void socket_init(void);
+void socket_final(void);
+
+extern void flush_fifo(int fd);
+extern void flush_fifos(void);
+extern void set_nonblocking(int fd, int yes);
+
+int start_console(void);
+
+void set_defaultparse(int (*defaultparse)(int));
+void set_defaultconsoleparse(int (*defaultparse)(char*));
+
+extern unsigned int addr_[16]; // ip addresses of local host (host byte order)
+extern unsigned int naddr_; // # of ip addresses
+
+
+#endif // _SOCKET_H_
diff --git a/src/common/strlib.c b/src/common/strlib.c
new file mode 100644
index 000000000..12c34556f
--- /dev/null
+++ b/src/common/strlib.c
@@ -0,0 +1,133 @@
+// 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 "strlib.h"
+#include "utils.h"
+#include "malloc.h"
+
+//-----------------------------------------------
+// string lib.
+char* jstrescape (char* pt) {
+ //copy from here
+ char *ptr;
+ int i =0, j=0;
+
+ //copy string to temporary
+ CREATE_A(ptr, char, J_MAX_MALLOC_SIZE);
+ strcpy(ptr,pt);
+
+ while (ptr[i] != '\0') {
+ switch (ptr[i]) {
+ case '\'':
+ pt[j++] = '\\';
+ pt[j++] = ptr[i++];
+ break;
+ case '\\':
+ pt[j++] = '\\';
+ pt[j++] = ptr[i++];
+ break;
+ case '%':
+ pt[j++] = '_'; i++;
+ break;
+ default:
+ pt[j++] = ptr[i++];
+ }
+ }
+ pt[j++] = '\0';
+ aFree (ptr);
+ return &pt[0];
+}
+
+char* jstrescapecpy (char* pt,char* spt) {
+ //copy from here
+ //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time
+ //a escape character is found, the target's final length increases! [Skotlex]
+ int i =0, j=0;
+
+ while (spt[i] != '\0') {
+ switch (spt[i]) {
+ case '\'':
+ pt[j++] = '\\';
+ pt[j++] = spt[i++];
+ break;
+ case '\\':
+ pt[j++] = '\\';
+ pt[j++] = spt[i++];
+ break;
+ case '%':
+ pt[j++] = '_'; i++;
+ break;
+ default:
+ pt[j++] = spt[i++];
+ }
+ }
+ pt[j++] = '\0';
+ return &pt[0];
+}
+int jmemescapecpy (char* pt,char* spt, int size) {
+ //copy from here
+ int i =0, j=0;
+
+ while (i < size) {
+ switch (spt[i]) {
+ case '\'':
+ pt[j++] = '\\';
+ pt[j++] = spt[i++];
+ break;
+ case '\\':
+ pt[j++] = '\\';
+ pt[j++] = spt[i++];
+ break;
+ case '%':
+ pt[j++] = '_'; i++;
+ break;
+ default:
+ pt[j++] = spt[i++];
+ }
+ }
+ // copy size is 0 ~ (j-1)
+ return j;
+}
+
+//-----------------------------------------------------
+// Function to suppress control characters in a string.
+//-----------------------------------------------------
+//int remove_control_chars(char *str) {
+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;
+}
+
+//Trims a string, also removes illegal characters such as \t and reduces continous spaces to a single one. by [Foruken]
+char *trim(char *str, const char *delim)
+{
+ char *strp = strtok(str,delim);
+ char buf[1024];
+ char *bufp = buf;
+ memset(buf,0,sizeof buf);
+
+ while(strp) {
+ strcpy(bufp, strp);
+ bufp = bufp + strlen(strp);
+ strp = strtok(NULL, delim);
+ if (strp) {
+ strcpy(bufp," ");
+ bufp++;
+ }
+ }
+ strcpy(str,buf);
+ return str;
+}
diff --git a/src/common/strlib.h b/src/common/strlib.h
new file mode 100644
index 000000000..f4ee7074b
--- /dev/null
+++ b/src/common/strlib.h
@@ -0,0 +1,17 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _J_STR_LIB_H_
+#define _J_STR_LIB_H_
+#define J_MAX_MALLOC_SIZE 65535
+// String function library.
+// code by Jioh L. Jung (ziozzang@4wish.net)
+// This code is under license "BSD"
+char* jstrescape (char* pt);
+char* jstrescapecpy (char* pt,char* spt);
+int jmemescapecpy (char* pt,char* spt, int size);
+
+// custom functions
+int remove_control_chars(unsigned char *);
+char *trim(char *str, const char *delim);
+#endif
diff --git a/src/common/timer.c b/src/common/timer.c
new file mode 100644
index 000000000..7b7ac5e2c
--- /dev/null
+++ b/src/common/timer.c
@@ -0,0 +1,429 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+// Well, this won't last another 30++ years (where conversion will truncate).
+//#define _USE_32BIT_TIME_T // use 32 bit time variables on 64bit windows
+#include <windows.h>
+#else
+#include <sys/socket.h>
+#include <sys/time.h>
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "timer.h"
+#include "malloc.h"
+#include "showmsg.h"
+
+// ƒ^ƒCƒ}[ŠÔŠu‚ÌŬ’lBƒ‚ƒ“ƒXƒ^[‚Ì‘å—Ê¢ŠÒŽžA‘½”‚̃Nƒ‰ƒCƒAƒ“ƒgÚ‘±Žž‚É
+// ƒT[ƒo[‚ª”½‰ž‚µ‚È‚­‚È‚éꇂÍATIMER_MIN_INTERVAL ‚ð‘‚₵‚Ä‚­‚¾‚³‚¢B
+
+// If the server shows no reaction when processing thousands of monsters
+// or connected by many clients, please increase TIMER_MIN_INTERVAL.
+
+#define TIMER_MIN_INTERVAL 50
+
+static struct TimerData* timer_data = NULL;
+static int timer_data_max = 0;
+static int timer_data_num = 0;
+
+static int* free_timer_list = NULL;
+static int free_timer_list_max = 0;
+static int free_timer_list_pos = 0;
+
+static int timer_heap_num = 0;
+static int timer_heap_max = 0;
+static int* timer_heap = NULL;
+
+static int fix_heap_flag =0; //Flag for fixing the stack only once per tick loop. May not be the best way, but it's all I can think of currently :X [Skotlex]
+
+// for debug
+struct timer_func_list {
+ int (*func)(int,unsigned int,int,int);
+ struct timer_func_list* next;
+ char* name;
+};
+static struct timer_func_list* tfl_root;
+
+time_t start_time;
+
+#ifdef __WIN32
+/* Modified struct timezone to void - we pass NULL anyway */
+void gettimeofday (struct timeval *t, void *dummy)
+{
+ DWORD millisec = GetTickCount();
+
+ t->tv_sec = (int) (millisec / 1000);
+ t->tv_usec = (millisec % 1000) * 1000;
+}
+#endif
+
+//
+int add_timer_func_list(int (*func)(int,unsigned int,int,int), char* name)
+{
+ struct timer_func_list* tfl;
+
+ if (name) {
+ tfl = (struct timer_func_list*) aCalloc (sizeof(struct timer_func_list), 1);
+ tfl->name = (char *) aMalloc (strlen(name) + 1);
+
+ tfl->next = tfl_root;
+ tfl->func = func;
+ strcpy(tfl->name, name);
+ tfl_root = tfl;
+ }
+ return 0;
+}
+
+char* search_timer_func_list(int (*func)(int,unsigned int,int,int))
+{
+ struct timer_func_list* tfl = tfl_root;
+ while (tfl) {
+ if (func == tfl->func)
+ return tfl->name;
+ tfl = tfl->next;
+ }
+
+ return "unknown timer function";
+}
+
+/*----------------------------
+ * Get tick time
+ *----------------------------*/
+static unsigned int gettick_cache;
+static int gettick_count;
+
+unsigned int gettick_nocache(void)
+{
+ struct timeval tval;
+
+ gettimeofday(&tval, NULL);
+ gettick_count = 256;
+
+ return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000;
+}
+
+unsigned int gettick(void)
+{
+ gettick_count--;
+ if (gettick_count < 0)
+ return gettick_nocache();
+
+ return gettick_cache;
+}
+
+/*======================================
+ * CORE : Timer Heap
+ *--------------------------------------
+ */
+static void push_timer_heap(int index)
+{
+ int i, j;
+ int min, max, pivot; // for sorting
+
+ // check number of element
+ if (timer_heap_num >= timer_heap_max) {
+ if (timer_heap_max == 0) {
+ timer_heap_max = 256;
+ timer_heap = (int *) aCalloc( sizeof(int) , 256);
+ } else {
+ timer_heap_max += 256;
+ timer_heap = (int *) aRealloc( timer_heap, sizeof(int) * timer_heap_max);
+ memset(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256);
+ }
+ }
+
+ // do a sorting from higher to lower
+ j = timer_data[index].tick; // speed up
+ // with less than 4 values, it's speeder to use simple loop
+ if (timer_heap_num < 4) {
+ for(i = timer_heap_num; i > 0; i--)
+// if (j < timer_data[timer_heap[i - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[i - 1]].tick) < 0)
+ break;
+ else
+ timer_heap[i] = timer_heap[i - 1];
+ timer_heap[i] = index;
+ // searching by dichotomie
+ } else {
+ // if lower actual item is higher than new
+// if (j < timer_data[timer_heap[timer_heap_num - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0)
+ timer_heap[timer_heap_num] = index;
+ else {
+ // searching position
+ min = 0;
+ max = timer_heap_num - 1;
+ while (min < max) {
+ pivot = (min + max) / 2;
+// if (j < timer_data[timer_heap[pivot]].tick) //Plain comparisons break on bound looping timers. [Skotlex]
+ if (DIFF_TICK(j, timer_data[timer_heap[pivot]].tick) < 0)
+ min = pivot + 1;
+ else
+ max = pivot;
+ }
+ // move elements - do loop if there are a little number of elements to move
+ if (timer_heap_num - min < 5) {
+ for(i = timer_heap_num; i > min; i--)
+ timer_heap[i] = timer_heap[i - 1];
+ // move elements - else use memmove (speeder for a lot of elements)
+ } else
+ memmove(&timer_heap[min + 1], &timer_heap[min], sizeof(int) * (timer_heap_num - min));
+ // save new element
+ timer_heap[min] = index;
+ }
+ }
+
+ timer_heap_num++;
+}
+
+/*==========================
+ * Timer Management
+ *--------------------------
+ */
+
+int acquire_timer (void)
+{
+ int i;
+
+ if (free_timer_list_pos) {
+ do {
+ i = free_timer_list[--free_timer_list_pos];
+ } while(i >= timer_data_num && free_timer_list_pos > 0);
+ } else
+ i = timer_data_num;
+
+ 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;
+ timer_data = (struct TimerData*) aCalloc( sizeof(struct TimerData) , timer_data_max);
+ } else {
+ timer_data_max += 256;
+ timer_data = (struct TimerData *) aRealloc( timer_data, sizeof(struct TimerData) * timer_data_max);
+ memset(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256);
+ }
+ }
+
+ return i;
+}
+
+int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int), int id, int data)
+{
+ int tid = acquire_timer();
+
+ timer_data[tid].tick = tick;
+ timer_data[tid].func = func;
+ timer_data[tid].id = id;
+ timer_data[tid].data = data;
+ timer_data[tid].type = TIMER_ONCE_AUTODEL;
+ timer_data[tid].interval = 1000;
+ push_timer_heap(tid);
+
+ if (tid >= timer_data_num)
+ timer_data_num = tid + 1;
+
+ return tid;
+}
+
+int add_timer_interval(unsigned int tick, int (*func)(int,unsigned int,int,int), int id, int data, int interval)
+{
+ int tid = acquire_timer();
+
+ timer_data[tid].tick = tick;
+ timer_data[tid].func = func;
+ timer_data[tid].id = id;
+ timer_data[tid].data = data;
+ timer_data[tid].type = TIMER_INTERVAL;
+ timer_data[tid].interval = interval;
+ push_timer_heap(tid);
+
+ if (tid >= timer_data_num)
+ timer_data_num = tid + 1;
+
+ return tid;
+}
+
+int delete_timer(int id, int (*func)(int,unsigned int,int,int))
+{
+ if (id <= 0 || id >= timer_data_num) {
+ ShowError("delete_timer error : no such timer %d\n", id);
+ return -1;
+ }
+ if (timer_data[id].func != func) {
+ ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n",
+ (int)timer_data[id].func, search_timer_func_list(timer_data[id].func),
+ (int)func, search_timer_func_list(func));
+ return -2;
+ }
+ // ‚»‚Ì‚¤‚¿Á‚¦‚é‚É‚Ü‚©‚¹‚é
+ timer_data[id].func = NULL;
+ timer_data[id].type = TIMER_ONCE_AUTODEL;
+
+ return 0;
+}
+
+int addtick_timer(int tid, unsigned int tick)
+{
+ return timer_data[tid].tick += tick;
+}
+
+//Sets the tick at which the timer triggers directly (meant as a replacement of delete_timer + add_timer) [Skotlex]
+//FIXME: DON'T use this function yet, it is not correctly reorganizing the timer stack causing unexpected problems later on!
+int settick_timer(int tid, unsigned int tick)
+{
+ int i,j;
+ if (timer_data[tid].tick == tick)
+ return tick;
+
+ //FIXME: This search is not all that effective... there doesn't seems to be a better way to locate an element in the heap.
+ for(i = timer_heap_num-1; i >= 0 && timer_heap[i] != tid; i--);
+
+ if (i < 0)
+ return -1; //Sort of impossible, isn't it?
+ if (DIFF_TICK(timer_data[tid].tick, tick) > 0)
+ { //Timer is accelerated, shift timer near the end of the heap.
+ if (i == timer_heap_num-1) //Nothing to shift.
+ j = timer_heap_num-1;
+ else {
+ for (j = i+1; j < timer_heap_num && DIFF_TICK(timer_data[j].tick, tick) > 0; j++);
+ j--;
+ memmove(&timer_heap[i], &timer_heap[i+1], (j-i)*sizeof(int));
+ }
+ } else { //Timer is delayed, shift timer near the beginning of the heap.
+ if (i == 0) //Nothing to shift.
+ j = 0;
+ else {
+ for (j = i-1; j >= 0 && DIFF_TICK(timer_data[j].tick, tick) < 0; j--);
+ j++;
+ memmove(&timer_heap[j+1], &timer_heap[j], (i-j)*sizeof(int));
+ }
+ }
+ timer_heap[j] = tid;
+ timer_data[tid].tick = tick;
+ return tick;
+}
+
+struct TimerData* get_timer(int tid)
+{
+ return &timer_data[tid];
+}
+
+//Correcting the heap when the tick overflows is an idea taken from jA to
+//prevent timer problems. Thanks to [End of Exam] for providing the required data. [Skotlex]
+//This funtion will rearrange the heap and assign new tick values.
+static void fix_timer_heap(unsigned int tick)
+{
+ if (timer_heap_num >= 0 && tick < 0x00010000 && timer_data[timer_heap[0]].tick > 0xf0000000)
+ { //The last timer is way too far into the future, and the current tick is too close to 0, overflow was very likely
+ //(not perfect, but will work as long as the timer is not expected to happen 50 or so days into the future)
+ int i;
+ int *tmp_heap;
+ for (i=0; i < timer_heap_num && timer_data[timer_heap[i]].tick > 0xf0000000; i++)
+ { //All functions with high tick value should had been executed already...
+ timer_data[timer_heap[i]].tick = 0;
+ }
+ //Move elements to readjust the heap.
+ tmp_heap = aCalloc(sizeof(int), i);
+ memmove(&tmp_heap[0], &timer_heap[0], i*sizeof(int));
+ memmove(&timer_heap[0], &timer_heap[i], (timer_heap_num-i)*sizeof(int));
+ memmove(&timer_heap[timer_heap_num-i], &tmp_heap[0], i*sizeof(int));
+ aFree(tmp_heap);
+ }
+}
+
+int do_timer(unsigned int tick)
+{
+ int i, nextmin = 1000;
+
+ if (tick < 0x010000 && fix_heap_flag)
+ {
+ fix_timer_heap(tick);
+ fix_heap_flag = 0;
+ }
+
+ while(timer_heap_num) {
+ i = timer_heap[timer_heap_num - 1]; // next shorter element
+ if ((nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0)
+ break;
+ if (timer_heap_num > 0) // suppress the actual element from the table
+ timer_heap_num--;
+ timer_data[i].type |= TIMER_REMOVE_HEAP;
+ if (timer_data[i].func) {
+ if (nextmin < -1000) {
+ // 1•bˆÈã‚Ì‘å•‚È’x‰„‚ª”­¶‚µ‚Ä‚¢‚é‚Ì‚ÅA
+ // timerˆ—ƒ^ƒCƒ~ƒ“ƒO‚ðŒ»Ý’l‚Æ‚·‚鎖‚Å
+ // ŒÄ‚Ño‚µŽžƒ^ƒCƒ~ƒ“ƒO(ˆø”‚Ìtick)‘Š‘΂ň—‚µ‚Ä‚é
+ // timerŠÖ”‚ÌŽŸ‰ñˆ—ƒ^ƒCƒ~ƒ“ƒO‚ð’x‚点‚é
+ 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);
+ }
+ }
+ if (timer_data[i].type & TIMER_REMOVE_HEAP) {
+ switch(timer_data[i].type & ~TIMER_REMOVE_HEAP) {
+ case TIMER_ONCE_AUTODEL:
+ timer_data[i].type = 0;
+ if (free_timer_list_pos >= free_timer_list_max) {
+ free_timer_list_max += 256;
+ free_timer_list = (int *) aRealloc(free_timer_list, sizeof(int) * free_timer_list_max);
+ memset(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int));
+ }
+ 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;
+ }
+ timer_data[i].type &= ~TIMER_REMOVE_HEAP;
+ push_timer_heap(i);
+ break;
+ }
+ }
+ }
+
+ if (nextmin < TIMER_MIN_INTERVAL)
+ nextmin = TIMER_MIN_INTERVAL;
+
+ if ((unsigned int)(tick + nextmin) < tick) //Tick will loop, rearrange the heap on the next iteration.
+ fix_heap_flag = 1;
+ return nextmin;
+}
+
+unsigned long get_uptime (void)
+{
+ return (unsigned long) difftime (time(NULL), start_time);
+}
+
+void timer_init(void)
+{
+ time(&start_time);
+}
+
+void timer_final(void)
+{
+ struct timer_func_list* tfl = tfl_root, *tfl2;
+
+ while (tfl) {
+ tfl2 = tfl->next; // copy next pointer
+ aFree(tfl->name); // free structures
+ aFree(tfl);
+ tfl = tfl2; // use copied pointer for next cycle
+ }
+
+ if (timer_data) aFree(timer_data);
+ if (timer_heap) aFree(timer_heap);
+ if (free_timer_list) aFree(free_timer_list);
+}
+
diff --git a/src/common/timer.h b/src/common/timer.h
new file mode 100644
index 000000000..aafefd1e2
--- /dev/null
+++ b/src/common/timer.h
@@ -0,0 +1,60 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#ifdef __WIN32
+/* We need winsock lib to have timeval struct - windows is weirdo */
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#endif
+
+#define BASE_TICK 5
+
+#define TIMER_ONCE_AUTODEL 1
+#define TIMER_INTERVAL 2
+#define TIMER_REMOVE_HEAP 16
+
+#define DIFF_TICK(a,b) ((int)((a)-(b)))
+
+// Struct declaration
+
+struct TimerData {
+ unsigned int tick;
+ int (*func)(int,unsigned int,int,int);
+ int id;
+ int data;
+ int type;
+ int interval;
+ int heap_pos;
+};
+
+// Function prototype declaration
+
+#ifdef __WIN32
+void gettimeofday(struct timeval *t, void *dummy);
+#endif
+
+unsigned int gettick_nocache(void);
+unsigned int gettick(void);
+
+int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int);
+int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
+int delete_timer(int,int (*)(int,unsigned int,int,int));
+
+int addtick_timer(int tid,unsigned int tick);
+int settick_timer(int tid,unsigned int tick);
+struct TimerData *get_timer(int tid);
+
+int do_timer(unsigned int tick);
+
+int add_timer_func_list(int (*)(int,unsigned int,int,int),char*);
+char* search_timer_func_list(int (*)(int,unsigned int,int,int));
+
+unsigned long get_uptime(void);
+
+void timer_init(void);
+void timer_final(void);
+
+#endif // _TIMER_H_
diff --git a/src/common/utils.c b/src/common/utils.c
new file mode 100644
index 000000000..57dc1f480
--- /dev/null
+++ b/src/common/utils.c
@@ -0,0 +1,384 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+ #include <windows.h>
+ #define PATHSEP '\\'
+#else
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #define PATHSEP '/'
+#endif
+
+#include "utils.h"
+#include "../common/mmo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+void dump(unsigned char *buffer, int num)
+{
+ int icnt,jcnt;
+
+ printf(" Hex ASCII\n");
+ printf(" ----------------------------------------------- ----------------");
+
+ for (icnt=0;icnt<num;icnt+=16) {
+ printf("\n%p ",&buffer[icnt]);
+ for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
+ if (jcnt < num) {
+ printf("%02hX ",buffer[jcnt]);
+ } else
+ printf(" ");
+ }
+
+ printf(" | ");
+
+ for (jcnt=icnt;jcnt<icnt+16;++jcnt) {
+ if (jcnt < num) {
+ if (buffer[jcnt] > 31 && buffer[jcnt] < 127)
+ printf("%c",buffer[jcnt]);
+ else
+ printf(".");
+ } else
+ printf(" ");
+ }
+ }
+ printf("\n");
+}
+
+//NOTE: There is no need to use this function as the standard sqrt is plenty fast as it is. [Skotlex]
+int newt_sqrt(int input)
+{
+ int new_value, value = input/2, count = 0;
+ if (!value) //Division by zero fix, pointed out by Shinomori. [Skotlex]
+ return input;
+ do
+ {
+ new_value = (value + input/value)>>1;
+ if (abs(value - new_value) <= 1)
+ return new_value;
+ value = new_value;
+ }
+ while (count++ < 25);
+ return new_value;
+}
+
+#if defined(_WIN32) && !defined(MINGW)
+char *rindex(char *str, char c)
+{
+ char *sptr;
+
+ sptr = str;
+ while(*sptr)
+ ++sptr;
+ if (c == '\0')
+ return(sptr);
+ while(str != sptr)
+ if (*sptr-- == c)
+ return(++sptr);
+ return(NULL);
+}
+
+int strcasecmp(const char *arg1, const char *arg2)
+{
+ int chk, i;
+
+ if (arg1 == NULL || arg2 == NULL) {
+ ShowError("strcasecmp: received a NULL pointer, %p or %p.\n", arg1, arg2);
+ return (0);
+ }
+
+ for (i = 0; arg1[i] || arg2[i]; i++)
+ if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
+ return (chk); /* not equal */
+
+ return (0);
+}
+
+int strncasecmp(const char *arg1, const char *arg2, int n)
+{
+ int chk, i;
+
+ if (arg1 == NULL || arg2 == NULL) {
+ ShowError("strncasecmp(): received a NULL pointer, %p or %p.\n", arg1, arg2);
+ return (0);
+ }
+
+ for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--)
+ if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0)
+ return (chk); /* not equal */
+
+ return (0);
+}
+
+void str_upper(char *name)
+{
+
+ int len = (int)strlen(name);
+ while (len--) {
+ if (*name >= 'a' && *name <= 'z')
+ *name -= ('a' - 'A');
+ name++;
+ }
+}
+
+void str_lower(char *name)
+{
+ int len = (int)strlen(name);
+
+ while (len--) {
+ if (*name >= 'A' && *name <= 'Z')
+ *name += ('a' - 'A');
+ name++;
+ }
+}
+
+#endif
+
+// Allocate a StringBuf [MouseJstr]
+struct StringBuf * StringBuf_Malloc()
+{
+ struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf));
+ StringBuf_Init(ret);
+ return ret;
+}
+
+// Initialize a previously allocated StringBuf [MouseJstr]
+void StringBuf_Init(struct StringBuf * sbuf) {
+ sbuf->max_ = 1024;
+ sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1);
+}
+
+// printf into a StringBuf, moving the pointer [MouseJstr]
+int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...)
+{
+ va_list ap;
+ int n, size, off;
+
+ while (1) {
+ /* Try to print in the allocated space. */
+ va_start(ap, fmt);
+ size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_);
+ n = vsnprintf (sbuf->ptr_, size, fmt, ap);
+ va_end(ap);
+ /* If that worked, return the length. */
+ if (n > -1 && n < size) {
+ sbuf->ptr_ += n;
+ return (int)(sbuf->ptr_ - sbuf->buf_);
+ }
+ /* Else try again with more space. */
+ sbuf->max_ *= 2; // twice the old size
+ off = (int)(sbuf->ptr_ - sbuf->buf_);
+ sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1);
+ sbuf->ptr_ = sbuf->buf_ + off;
+ }
+}
+
+// Append buf2 onto the end of buf1 [MouseJstr]
+int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2)
+{
+ int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_);
+ int size2 = (int)(buf2->ptr_ - buf2->buf_);
+
+ if (size2 >= buf1_avail) {
+ int off = (int)(buf1->ptr_ - buf1->buf_);
+ buf1->max_ += size2;
+ buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1);
+ buf1->ptr_ = buf1->buf_ + off;
+ }
+
+ memcpy(buf1->ptr_, buf2->buf_, size2);
+ buf1->ptr_ += size2;
+ return (int)(buf1->ptr_ - buf1->buf_);
+}
+
+// Destroy a StringBuf [MouseJstr]
+void StringBuf_Destroy(struct StringBuf *sbuf)
+{
+ aFree(sbuf->buf_);
+ sbuf->ptr_ = sbuf->buf_ = 0;
+}
+
+// Free a StringBuf returned by StringBuf_Malloc [MouseJstr]
+void StringBuf_Free(struct StringBuf *sbuf)
+{
+ StringBuf_Destroy(sbuf);
+ aFree(sbuf);
+}
+
+// Return the built string from the StringBuf [MouseJstr]
+char * StringBuf_Value(struct StringBuf *sbuf)
+{
+ *sbuf->ptr_ = '\0';
+ return sbuf->buf_;
+}
+
+#ifdef WIN32
+
+char* checkpath(char *path, const char *srcpath)
+{ // just make sure the char*path is not const
+ char *p=path;
+ if(NULL!=path && NULL!=srcpath)
+ while(*srcpath) {
+ if (*srcpath=='/') {
+ *p++ = '\\';
+ srcpath++;
+ }
+ else
+ *p++ = *srcpath++;
+ }
+ *p = *srcpath; //EOS
+ return path;
+}
+
+void findfile(const char *p, const char *pat, void (func)(const char*))
+{
+ WIN32_FIND_DATA FindFileData;
+ HANDLE hFind;
+ char tmppath[MAX_PATH+1];
+
+ const char *path = (p ==NULL)? "." : p;
+ const char *pattern = (pat==NULL)? "" : pat;
+
+ checkpath(tmppath,path);
+ if( PATHSEP != tmppath[strlen(tmppath)-1])
+ strcat(tmppath, "\\*");
+ else
+ strcat(tmppath, "*");
+
+ hFind = FindFirstFile(tmppath, &FindFileData);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (strcmp(FindFileData.cFileName, ".") == 0)
+ continue;
+ if (strcmp(FindFileData.cFileName, "..") == 0)
+ continue;
+
+ sprintf(tmppath,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
+
+ if (FindFileData.cFileName && strstr(FindFileData.cFileName, pattern)) {
+ func( tmppath );
+ }
+
+
+ if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ findfile(tmppath, pat, func);
+ }
+ }while (FindNextFile(hFind, &FindFileData) != 0);
+ FindClose(hFind);
+ }
+ return;
+}
+#else
+
+#define MAX_DIR_PATH 2048
+
+char* checkpath(char *path, const char*srcpath)
+{ // just make sure the char*path is not const
+ char *p=path;
+ if(NULL!=path && NULL!=srcpath)
+ while(*srcpath) {
+ if (*srcpath=='\\') {
+ *p++ = '/';
+ srcpath++;
+ }
+ else
+ *p++ = *srcpath++;
+ }
+ *p = *srcpath; //EOS
+ return path;
+}
+
+void findfile(const char *p, const char *pat, void (func)(const char*))
+{
+ DIR* dir; // pointer to the scanned directory.
+ struct dirent* entry; // pointer to one directory entry.
+ struct stat dir_stat; // used by stat().
+ char tmppath[MAX_DIR_PATH+1];
+ char path[MAX_DIR_PATH+1]= ".";
+ const char *pattern = (pat==NULL)? "" : pat;
+ if(p!=NULL) strcpy(path,p);
+
+ // open the directory for reading
+ dir = opendir( checkpath(path, path) );
+ if (!dir) {
+ ShowError("Cannot read directory '%s'\n", path);
+ return;
+ }
+
+ // scan the directory, traversing each sub-directory
+ // matching the pattern for each file name.
+ while ((entry = readdir(dir))) {
+ // skip the "." and ".." entries.
+ if (strcmp(entry->d_name, ".") == 0)
+ continue;
+ if (strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name);
+
+ // check if the pattern matchs.
+ if (entry->d_name && strstr(entry->d_name, pattern)) {
+ func( tmppath );
+ }
+ // check if it is a directory.
+ if (stat(tmppath, &dir_stat) == -1) {
+ ShowError("stat error %s\n': ", tmppath);
+ continue;
+ }
+ // is this a directory?
+ if (S_ISDIR(dir_stat.st_mode)) {
+ // decent recursivly
+ findfile(tmppath, pat, func);
+ }
+ }//end while
+}
+#endif
+
+unsigned char GetByte(unsigned long val, size_t num)
+{
+ switch(num)
+ {
+ case 0:
+ return (unsigned char)((val & 0x000000FF) );
+ case 1:
+ return (unsigned char)((val & 0x0000FF00)>>0x08);
+ case 2:
+ return (unsigned char)((val & 0x00FF0000)>>0x10);
+ case 3:
+ return (unsigned char)((val & 0xFF000000)>>0x18);
+ default:
+ return 0; //better throw something here
+ }
+}
+unsigned short GetWord(unsigned long val, size_t num)
+{
+ switch(num)
+ {
+ case 0:
+ return (unsigned short)((val & 0x0000FFFF) );
+ case 1:
+ return (unsigned short)((val & 0xFFFF0000)>>0x10);
+ default:
+ return 0; //better throw something here
+ }
+}
+unsigned short MakeWord(unsigned char byte0, unsigned char byte1)
+{
+ return byte0 | (byte1<<0x08);
+}
+unsigned long MakeDWord(unsigned short word0, unsigned short word1)
+{
+ return ((unsigned long)word0)
+ | ((unsigned long)word1<<0x10);
+}
+
diff --git a/src/common/utils.h b/src/common/utils.h
new file mode 100644
index 000000000..9d2febe1b
--- /dev/null
+++ b/src/common/utils.h
@@ -0,0 +1,52 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef COMMON_UTILS_H
+#define COMMON_UTILS_H
+
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#define LOWER(c) (((c)>='A' && (c) <= 'Z') ? ((c)+('a'-'A')) : (c))
+#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c) )
+
+/* strcasecmp -> stricmp -> str_cmp */
+#if defined(_WIN32) && !defined(MINGW)
+ int strcasecmp(const char *arg1, const char *arg2);
+ int strncasecmp(const char *arg1, const char *arg2, int n);
+ void str_upper(char *name);
+ void str_lower(char *name);
+ char *rindex(char *str, char c);
+#endif
+
+void dump(unsigned char *buffer, int num);
+int newt_sqrt(int value); //Newton aproximation for getting a fast sqrt.
+
+struct StringBuf {
+ char *buf_;
+ char *ptr_;
+ unsigned int max_;
+};
+
+struct StringBuf * StringBuf_Malloc(void);
+void StringBuf_Init(struct StringBuf *);
+int StringBuf_Printf(struct StringBuf *,const char *,...);
+int StringBuf_Append(struct StringBuf *,const struct StringBuf *);
+char * StringBuf_Value(struct StringBuf *);
+void StringBuf_Destroy(struct StringBuf *);
+void StringBuf_Free(struct StringBuf *);
+
+void findfile(const char *p, const char *pat, void (func)(const char*));
+
+//////////////////////////////////////////////////////////////////////////
+// byte word dword access [Shinomori]
+//////////////////////////////////////////////////////////////////////////
+
+extern unsigned char GetByte(unsigned long val, size_t num);
+extern unsigned short GetWord(unsigned long val, size_t num);
+extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1);
+extern unsigned long MakeDWord(unsigned short word0, unsigned short word1);
+
+#endif
diff --git a/src/common/version.h b/src/common/version.h
new file mode 100644
index 000000000..1c7961ee1
--- /dev/null
+++ b/src/common/version.h
@@ -0,0 +1,30 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+#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
+
+#define ATHENA_SERVER_NONE 0 // not defined
+#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
+
+// ATHENA_MOD_VERSION‚̓pƒbƒ`”Ô†‚Å‚·B
+// ‚±‚ê‚Í–³—‚É•Ï‚¦‚È‚­‚Ä‚à‹C‚ªŒü‚¢‚½‚ç•Ï‚¦‚é’ö“x‚̈µ‚¢‚ÅB
+// i–ˆ‰ñƒAƒbƒvƒ[ƒh‚Ì“x‚É•ÏX‚·‚é‚Ì‚à–Ê“|‚ÆŽv‚í‚ê‚邵A‚»‚à‚»‚à
+// @‚±‚Ì€–Ú‚ðŽQÆ‚·‚él‚ª‚¢‚é‚©‚Ç‚¤‚©‚Å‹^–₾‚©‚çBj
+// ‚»‚Ì’ö“x‚̈µ‚¢‚È‚Ì‚ÅAƒT[ƒo[‚É–â‚¢‡‚킹‚鑤‚àA‚ ‚­‚Ü‚Å–ÚˆÀ’ö“x‚̈µ‚¢‚Å
+// ‚ ‚ñ‚Ü‚èM—p‚µ‚È‚¢‚±‚ÆB
+// ŽIsnapshot‚ÌŽž‚âA‘å‚«‚È•ÏX‚ª‚ ‚Á‚½ê‡‚Íݒ肵‚Ä‚Ù‚µ‚¢‚Å‚·B
+// CŒ¾Œê‚ÌŽd—lãAʼn‚É0‚ð•t‚¯‚é‚Æ8i”‚É‚È‚é‚̂ŊԈႦ‚È‚¢‚ʼnº‚³‚¢B
+#define ATHENA_MOD_VERSION 1249 // mod version (patch No.)
+
+#endif
diff --git a/src/ladmin/Makefile b/src/ladmin/Makefile
new file mode 100644
index 000000000..f669a6ef1
--- /dev/null
+++ b/src/ladmin/Makefile
@@ -0,0 +1,17 @@
+all txt sql: ladmin
+
+COMMON_OBJ = ../common/obj/minicore.o ../common/obj/minisocket.o ../common/obj/timer.o \
+ ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/strlib.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h \
+ ../common/version.h ../common/malloc.h ../common/showmsg.h ../common/strlib.h
+
+ladmin: ladmin.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ ladmin.o md5calc.o $(COMMON_OBJ) $(LIB_S)
+
+clean:
+ rm -f *.o ../../ladmin
+
+# DO NOT DELETE
+
+ladmin.o: ladmin.c ladmin.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h \ No newline at end of file
diff --git a/src/ladmin/ladmin.c b/src/ladmin/ladmin.c
new file mode 100644
index 000000000..8a3877dc4
--- /dev/null
+++ b/src/ladmin/ladmin.c
@@ -0,0 +1,4410 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+///////////////////////////////////////////////////////////////////////////
+// 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 <time.h>
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+void Gettimeofday(struct timeval *timenow)
+{
+ time_t t;
+ t = clock();
+ timenow->tv_usec = t;
+ timenow->tv_sec = t / CLK_TCK;
+ return;
+}
+#define gettimeofday(timenow, dummy) Gettimeofday(timenow)
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h> // gettimeofday
+#include <sys/ioctl.h>
+#include <unistd.h> // close
+#include <arpa/inet.h> // inet_addr
+#include <netdb.h> // gethostbyname
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h> // str*
+#include <stdarg.h> // valist
+#include <ctype.h> // tolower
+
+#include "../common/core.h"
+#include "../common/strlib.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "ladmin.h"
+#include "../common/version.h"
+#include "../common/mmo.h"
+
+#ifdef PASSWORDENC
+#include "md5calc.h"
+#endif
+
+//-------------------------------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(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, RETCODE);
+ else {
+ gettimeofday(&tv, NULL);
+ strftime(tmpstr, 24, date_format, localtime((const time_t*)&(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 return ordonal text of a number.
+//---------------------------------------------
+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)." RETCODE, 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)." RETCODE, 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." RETCODE);
+ } 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." RETCODE);
+ }
+ 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." RETCODE);
+ } 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." RETCODE);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+//---------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid).
+//---------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++) {
+ if (strchr(last_arobas, ch) != NULL) {
+ return 0;
+ break;
+ }
+ }
+
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
+ return 0;
+
+ // all correct
+ return 1;
+}
+
+//----------------------------------
+// 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." RETCODE);
+ } else {
+ ladmin_log("No password was given. Request to obtain a password." RETCODE);
+ }
+
+ 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." RETCODE);
+ ladmin_log(" Premier mot de passe: %s, second mot de passe: %s." RETCODE, password1, password2);
+ } else {
+ printf("Password verification failed. Please input same password.\n");
+ ladmin_log("Password verification failed. Please input same password." RETCODE);
+ ladmin_log(" First password: %s, second password: %s." RETCODE, password1, password2);
+ }
+ return 0;
+ }
+ if (defaultlanguage == 'F') {
+ ladmin_log("Mot de passe saisi: %s." RETCODE, password1);
+ } else {
+ ladmin_log("Typed password: %s." RETCODE, 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)." RETCODE, 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)." RETCODE, 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." RETCODE);
+ } 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." RETCODE);
+ }
+ 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." RETCODE);
+ } 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." RETCODE);
+ }
+ 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, "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." RETCODE);
+ } else {
+ ladmin_log("Displaying of the commands or a command." RETCODE);
+ }
+
+ 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, "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, "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(" 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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)." RETCODE, 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)." RETCODE, 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." RETCODE, sex);
+ } else {
+ printf("Illegal gender [%s]. Please input M or F.\n", sex);
+ ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to create an account." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the years (from -127 to 127).\n");
+ ladmin_log("Abnormal adjustement for the year ('banadd' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the months (from -255 to 255).\n");
+ ladmin_log("Abnormal adjustement for the month ('banadd' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the days (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the days ('banadd' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the hours ('banadd' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the minutes ('banadd' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the seconds ('banadd' command)." RETCODE);
+ }
+ return 137;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour modifier la date d'un bannissement." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to modify a ban date/time." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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 = mktime(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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ return 102;
+ }
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour fixer un ban." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to set a ban." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to check a password." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } else {
+ printf("Please input an account name.\n");
+ printf("<example> del testnametodelete\n");
+ ladmin_log("No name given to delete an account ('delete' command)." RETCODE);
+ }
+ 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')." RETCODE);
+ } else {
+ printf("Deletion canceled.\n");
+ ladmin_log("Deletion canceled by user ('delete' command)." RETCODE);
+ }
+ return 121;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour détruire un compte." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to delete an acount." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, email);
+ }
+ return 109;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour changer une email." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change an email." RETCODE);
+ }
+
+ 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." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to obtain the # of online players." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE, 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." RETCODE, GM_level);
+ }
+ return 103;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour changer un niveau de GM." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change a GM level." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to know an account id." RETCODE);
+ }
+
+ 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." RETCODE);
+ } else {
+ printf("Please input a positive value for the id.\n");
+ ladmin_log("Negative value was given to found the account." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to obtain information about an account (by its id)." RETCODE);
+ }
+
+ 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)')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE);
+ } else {
+ printf("Displaying language changed to English.\n");
+ ladmin_log("Displaying language changed to English." RETCODE);
+ }
+ } 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)." RETCODE);
+ } else {
+ printf("Undefined language (possible languages: Français or English).\n");
+ ladmin_log("Undefined language (must be Français or English)." RETCODE);
+ }
+ }
+
+ 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." RETCODE, list_first, list_last);
+ } else {
+ ladmin_log("Request to login-server to obtain the list of accounts from %d to %d." RETCODE, 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') {
+ printf(" id_compte GM nom_utilisateur sexe count statut\n");
+ } else {
+ printf("account_id GM user_name sex count state\n");
+ }
+ printf("-------------------------------------------------------------------------------\n");
+ list_count = 0;
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE, 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." RETCODE, strlen(memo));
+ }
+ return 102;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour changer un mémo." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change a memo." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } else {
+ printf("Please input a positive value for the id.\n");
+ ladmin_log("Negativ id given to search an account name ('name' command)." RETCODE);
+ }
+ return 136;
+ }
+
+ if (defaultlanguage == 'F')
+ ladmin_log("Envoi d'un requête au serveur de logins pour connaître le nom d'un compte." RETCODE);
+ else
+ ladmin_log("Request to login-server to know an account name." RETCODE);
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change a password." RETCODE);
+ }
+
+ 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." RETCODE);
+ 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." RETCODE);
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE, sex);
+ } else {
+ printf("Illegal gender [%s]. Please input M or F.\n", sex);
+ ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, sex);
+ }
+ return 103;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour changer un sexe." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change a sex." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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." RETCODE);
+ } 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." RETCODE);
+ }
+ 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." RETCODE);
+ } 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." RETCODE);
+ }
+ return 102;
+ }
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour changer un statut." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to change a state." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the years (from -127 to 127).\n");
+ ladmin_log("Abnormal adjustement for the year ('timeadd' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the months (from -255 to 255).\n");
+ ladmin_log("Abnormal adjustement for the month ('timeadd' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the days (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the days ('timeadd' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the hours ('timeadd' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the minutes ('timeadd' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n");
+ ladmin_log("Abnormal adjustement for the seconds ('timeadd' command)." RETCODE);
+ }
+ return 137;
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour modifier une date limite d'utilisation." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to modify a time limit." RETCODE);
+ }
+
+ 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')." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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)." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct value for the month (from 1 to 12).\n");
+ ladmin_log("Invalid month for the date ('timeset' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct value for the day (from 1 to 31).\n");
+ ladmin_log("Invalid day for the date ('timeset' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } 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)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct value for the hour (from 0 to 23).\n");
+ ladmin_log("Invalid hour for the time ('timeset' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct value for the minutes (from 0 to 59).\n");
+ ladmin_log("Invalid minute for the time ('timeset' command)." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ printf("Please give a correct value for the seconds (from 0 to 59).\n");
+ ladmin_log("Invalid second for the time ('timeset' command)." RETCODE);
+ }
+ 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 = mktime(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)." RETCODE);
+ } 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)." RETCODE);
+ }
+ return 102;
+ }
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Envoi d'un requête au serveur de logins pour fixer une date limite d'utilisation." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to set a time limit." RETCODE);
+ }
+
+ 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." RETCODE);
+ } else {
+ printf("Please input an account name.\n");
+ printf("<example> who testname\n");
+ ladmin_log("No name was given to found the account." RETCODE);
+ }
+ 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)." RETCODE);
+ } else {
+ ladmin_log("Request to login-server to obtain information about an account (by its name)." RETCODE);
+ }
+
+ 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." RETCODE);
+ else
+ ladmin_log("Request to login-server to obtain its version." RETCODE);
+
+ 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
+ printf("\n");
+ if (defaultlanguage == 'F')
+ printf("\033[32mPour afficher les commandes, tapez 'Entrée'.\033[0m\n");
+ else
+ printf("\033[32mTo list the commands, type 'enter'.\033[0m\n");
+ printf("\033[0;36mLadmin-> \033[0m");
+ printf("\033[1m");
+ fflush(stdout);
+
+ // get command and parameter
+ memset(buf, '\0', sizeof(buf));
+ fflush(stdin);
+ fgets(buf, 1023, stdin);
+ buf[1023] = '\0';
+
+ printf("\033[0m");
+ fflush(stdout);
+
+ // 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)" RETCODE, command, parameters);
+ } else {
+ ladmin_log("Command: '%s' (without parameters)" RETCODE, command, parameters);
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ ladmin_log("Commande: '%s', paramètres: '%s'" RETCODE, command, parameters);
+ } else {
+ ladmin_log("Command: '%s', parameters: '%s'" RETCODE, 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, "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]." RETCODE, buf);
+ } else {
+ printf("Unknown command [%s].\n", buf);
+ ladmin_log("Unknown command [%s]." RETCODE, buf);
+ }
+ }
+ }
+
+ return 0;
+}
+
+//-------------------------------------------------------------
+// Function: Parse receiving informations from the login-server
+//-------------------------------------------------------------
+int parse_fromlogin(int fd) {
+ struct char_session_data *sd;
+ int id;
+ 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] !" RETCODE, 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] !" RETCODE, 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 0;
+ 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." RETCODE);
+ } 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." RETCODE);
+ }
+ session[fd]->eof = 1;
+ //bytes_to_read = 1; // not stop at prompt
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Connexion établie.\n");
+ ladmin_log("Connexion établie." RETCODE);
+ printf("Lecture de la version du serveur de login...\n");
+ ladmin_log("Lecture de la version du serveur de login..." RETCODE);
+ } else {
+ printf("Established connection.\n");
+ ladmin_log("Established connection." RETCODE);
+ printf("Reading of the version of the login-server...\n");
+ ladmin_log("Reading of the version of the login-server..." RETCODE);
+ }
+ //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 0;
+ {
+ 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, (const char*)RFIFOP(fd,4), RFIFOW(fd,2) - 4);
+ strcat(md5str, loginserveradminpassword);
+ } else if (passenc == 2) {
+ strncpy(md5str, loginserveradminpassword, sizeof(loginserveradminpassword));
+ strcat(md5str, (const char*)RFIFOP(fd,4));
+ }
+ MD5_String2binary(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') {
+ printf("Réception de la clef MD5.\n");
+ ladmin_log("Réception de la clef MD5." RETCODE);
+ printf("Envoi du mot de passe crypté...\n");
+ ladmin_log("Envoi du mot de passe crypté..." RETCODE);
+ } else {
+ printf("Receiving of the MD5 key.\n");
+ ladmin_log("Receiving of the MD5 key." RETCODE);
+ printf("Sending of the encrypted password...\n");
+ ladmin_log("Sending of the encrypted password..." RETCODE);
+ }
+ }
+ 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 0;
+ printf(" Login-Server [%s:%d]\n", loginserverip, loginserverport);
+ if (((int)RFIFOB(login_fd,5)) == 0) {
+ printf(" eAthena version stable-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3));
+ } else {
+ printf(" eAthena version dev-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3));
+ }
+ if (((int)RFIFOB(login_fd,4)) == 0)
+ printf(" revision %d", (int)RFIFOB(login_fd,4));
+ if (((int)RFIFOB(login_fd,6)) == 0)
+ printf("%d.\n", RFIFOW(login_fd,8));
+ else
+ printf("-mod%d.\n", RFIFOW(login_fd,8));
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,10);
+ break;
+
+ case 0x7921: // Displaying of the list of accounts
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ if (RFIFOW(fd,2) < 5) {
+ if (defaultlanguage == 'F') {
+ ladmin_log(" Réception d'une liste des comptes vide." RETCODE);
+ 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." RETCODE);
+ if (list_count == 0)
+ printf("No account found.\n");
+ else if (list_count == 1)
+ printf("1 account found.\n");
+ else
+ printf("%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." RETCODE);
+ else
+ ladmin_log(" Receiving of a accounts list." RETCODE);
+ 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 ", (int)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 ", (int)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)." RETCODE, list_first, list_last);
+ else
+ ladmin_log("Request to login-server to obtain the list of accounts from %d to %d (complement)." RETCODE, 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 0;
+ id=RFIFOL(fd,2);
+ if (id == -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à." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Compte [%s] créé avec succès [id: %d].\n", RFIFOP(fd,6), id);
+ ladmin_log("Compte [%s] créé avec succès [id: %d]." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Account [%s] is successfully created [id: %d].\n", RFIFOP(fd,6), id);
+ ladmin_log("Account [%s] is successfully created [id: %d]." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x7933: // Answer of login-server about an account deletion
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ 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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Compte [%s][id: %d] SUPPRIME avec succès.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2));
+ ladmin_log("Compte [%s][id: %d] SUPPRIME avec succès." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2));
+ } else {
+ printf("Account [%s][id: %d] is successfully DELETED.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2));
+ ladmin_log("Account [%s][id: %d] is successfully DELETED." RETCODE, 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 0;
+ 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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Modification du mot de passe du compte [%s][id: %d] réussie.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2));
+ ladmin_log("Modification du mot de passe du compte [%s][id: %d] réussie." RETCODE, RFIFOP(fd,6), (int)RFIFOL(fd,2));
+ } else {
+ printf("Account [%s][id: %d] password successfully changed.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2));
+ ladmin_log("Account [%s][id: %d] password successfully changed." RETCODE, RFIFOP(fd,6), (int)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 0;
+ 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." RETCODE, 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." RETCODE, 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, RETCODE);
+ }
+ 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 0;
+ {
+ // Get length of the received packet
+ int i;
+ char name[20];
+ if (defaultlanguage == 'F') {
+ ladmin_log(" Réception du nombre de joueurs en ligne." RETCODE);
+ } else {
+ ladmin_log(" Receiving of the number of online players." RETCODE);
+ }
+ // 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 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Le mot de passe donné correspond bien au compte [%s][id: %d].\n", RFIFOP(fd,6), id);
+ ladmin_log("Le mot de passe donné correspond bien au compte [%s][id: %d]." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("The proposed password is correct for the account [%s][id: %d].\n", RFIFOP(fd,6), id);
+ ladmin_log("The proposed password is correct for the account [%s][id: %d]." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x793d: // answer of the change of an account sex
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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é." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Sexe du compte [%s][id: %d] changé avec succès.\n", RFIFOP(fd,6), id);
+ ladmin_log("Sexe du compte [%s][id: %d] changé avec succès." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Account [%s][id: %d] sex successfully changed.\n", RFIFOP(fd,6), id);
+ ladmin_log("Account [%s][id: %d] sex successfully changed." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x793f: // answer of the change of an account GM level
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Niveau de GM du compte [%s][id: %d] changé avec succès.\n", RFIFOP(fd,6), id);
+ ladmin_log("Niveau de GM du compte [%s][id: %d] changé avec succès." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Account [%s][id: %d] GM level successfully changed.\n", RFIFOP(fd,6), id);
+ ladmin_log("Account [%s][id: %d] GM level successfully changed." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x7941: // answer of the change of an account email
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Modification de l'e-mail du compte [%s][id: %d] réussie.\n", RFIFOP(fd,6), id);
+ ladmin_log("Modification de l'e-mail du compte [%s][id: %d] réussie." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Account [%s][id: %d] e-mail successfully changed.\n", RFIFOP(fd,6), id);
+ ladmin_log("Account [%s][id: %d] e-mail successfully changed." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x7943: // answer of the change of an account memo
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Mémo du compte [%s][id: %d] changé avec succès.\n", RFIFOP(fd,6), id);
+ ladmin_log("Mémo du compte [%s][id: %d] changé avec succès." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Account [%s][id: %d] memo successfully changed.\n", RFIFOP(fd,6), id);
+ ladmin_log("Account [%s][id: %d] memo successfully changed." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x7945: // answer of an account id search
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, RFIFOP(fd,6));
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Le compte [%s] a pour id: %d.\n", RFIFOP(fd,6), id);
+ ladmin_log("Le compte [%s] a pour id: %d." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("The account [%s] have the id: %d.\n", RFIFOP(fd,6), id);
+ ladmin_log("The account [%s] have the id: %d." RETCODE, RFIFOP(fd,6), id);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,30);
+ break;
+
+ case 0x7947: // answer of an account name search
+ if (RFIFOREST(fd) < 30)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (strcmp((const char*)RFIFOP(fd,6), "") == 0) {
+ if (defaultlanguage == 'F') {
+ printf("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", id);
+ ladmin_log("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas." RETCODE, id);
+ } else {
+ printf("Unable to find the account [%d] name. Account doesn't exist.\n", id);
+ ladmin_log("Unable to find the account [%d] name. Account doesn't exist." RETCODE, id);
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ printf("Le compte [id: %d] a pour nom: %s.\n", id, RFIFOP(fd,6));
+ ladmin_log("Le compte [id: %d] a pour nom: %s." RETCODE, id, RFIFOP(fd,6));
+ } else {
+ printf("The account [id: %d] have the name: %s.\n", id, RFIFOP(fd,6));
+ ladmin_log("The account [id: %d] have the name: %s." RETCODE, id, 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 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, 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), id);
+ ladmin_log("Limite de validité du compte [%s][id: %d] changée avec succès en [illimité]." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", RFIFOP(fd,6), id);
+ ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited]." RETCODE, RFIFOP(fd,6), id);
+ }
+ } else {
+ char tmpstr[128];
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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), id, tmpstr);
+ ladmin_log("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ } else {
+ printf("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr);
+ ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ }
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,34);
+ break;
+
+ case 0x794b: // answer of an account ban set
+ if (RFIFOREST(fd) < 34)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, 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), id);
+ ladmin_log("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie]." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id);
+ ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id);
+ }
+ } else {
+ char tmpstr[128];
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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), id, tmpstr);
+ ladmin_log("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ } else {
+ printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr);
+ ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ }
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,34);
+ break;
+
+ case 0x794d: // answer of an account ban date/time changing
+ if (RFIFOREST(fd) < 34)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, 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), id);
+ ladmin_log("Date finale de banissement du compte [%s][id: %d] changée avec succès en [dé-bannie]." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id);
+ ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id);
+ }
+ } else {
+ char tmpstr[128];
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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), id, tmpstr);
+ ladmin_log("Date finale de banissement du compte [%s][id: %d] changée avec succès pour être jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ } else {
+ printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr);
+ ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ }
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,34);
+ break;
+
+ case 0x794f: // answer of a broadcast
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ 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." RETCODE);
+ } else {
+ printf("Message sending failed. No online char-server.\n");
+ ladmin_log("Message sending failed. No online char-server." RETCODE);
+ }
+ } 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." RETCODE);
+ } else {
+ printf("Message successfully sended to login-server.\n");
+ ladmin_log("Message successfully sended to login-server." RETCODE);
+ }
+ }
+ bytes_to_read = 0;
+ RFIFOSKIP(fd,4);
+ break;
+
+ case 0x7951: // answer of an account validity limit changing
+ if (RFIFOREST(fd) < 34)
+ return 0;
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, 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), id);
+ 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." RETCODE, RFIFOP(fd,6), id);
+ } else {
+ printf("Validity limit of the account [%s][id: %d] unchanged.\n", RFIFOP(fd,6), id);
+ 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." RETCODE, RFIFOP(fd,6), id);
+ }
+ } else {
+ char tmpstr[128];
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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), id, tmpstr);
+ ladmin_log("Limite de validité du compte [%s][id: %d] changée avec succès pour être jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr);
+ } else {
+ printf("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr);
+ ladmin_log("Validity limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, 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 0;
+ {
+ 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, (const char*)RFIFOP(fd,150), RFIFOW(fd,148));
+ id = RFIFOL(fd,2);
+ if (id == -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." RETCODE, 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." RETCODE, 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." RETCODE, 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." RETCODE, parameters);
+ }
+ } else {
+ if (defaultlanguage == 'F') {
+ ladmin_log("Réception d'information concernant un compte." RETCODE);
+ printf("Le compte a les caractéristiques suivantes:\n");
+ } else {
+ ladmin_log("Receiving information about an account." RETCODE);
+ printf("The account is set with:\n");
+ }
+ if (RFIFOB(fd,6) == 0) {
+ printf(" Id: %d (non-GM)\n", id);
+ } else {
+ if (defaultlanguage == 'F') {
+ printf(" Id: %d (GM niveau %d)\n", id, (int)RFIFOB(fd,6));
+ } else {
+ printf(" Id: %d (GM level %d)\n", id, (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", (int)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", (int)RFIFOL(fd,32));
+ else
+ printf(" Compteur: %d connexion.\n", (int)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", (int)RFIFOL(fd,32));
+ else
+ printf(" Count: %d connection.\n", (int)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." RETCODE);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+
+ // if we don't wait new packets, do the prompt
+ prompt();
+
+ return 0;
+}
+
+//------------------------------------
+// Function to connect to login-server
+//------------------------------------
+int Connect_login_server(void) {
+ if (defaultlanguage == 'F') {
+ printf("Essai de connection au server de logins...\n");
+ ladmin_log("Essai de connection au server de logins..." RETCODE);
+ } else {
+ printf("Attempt to connect to login-server...\n");
+ ladmin_log("Attempt to connect to login-server..." RETCODE);
+ }
+
+ login_fd = make_connection(login_ip, loginserverport);
+ if (login_fd == -1)
+ { //Might not be the most elegant way to handle this, but I've never used ladmin so I dunno what else you could do. [Skotlex]
+ printf("Error: Failed to connect to Login Server\n");
+ exit(1);
+ }
+#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') {
+ printf("Envoi du mot de passe...\n");
+ ladmin_log("Envoi du mot de passe..." RETCODE);
+ } else {
+ printf("Sending of the password...\n");
+ ladmin_log("Sending of the password..." RETCODE);
+ }
+#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') {
+ printf("Demande de la clef MD5...\n");
+ ladmin_log("Demande de la clef MD5..." RETCODE);
+ } else {
+ printf("Request about the MD5 key...\n");
+ ladmin_log("Request about the MD5 key..." RETCODE);
+ }
+ }
+#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 (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+
+ return atoi(str);
+}
+
+//-----------------------------------
+// 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') {
+ printf("\033[0m---Début de lecture du fichier de configuration Ladmin (%s)\n", cfgName);
+ } else {
+ printf("\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((unsigned char *) w1);
+ remove_control_chars((unsigned char *) w2);
+
+ if(strcmpi(w1,"login_ip")==0){
+ struct hostent *h = gethostbyname (w2);
+ if (h != NULL) {
+ if (defaultlanguage == 'F') {
+ printf("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 {
+ 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(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 (strcmpi(w1, "login_port") == 0) {
+ loginserverport = atoi(w2);
+ } else if (strcmpi(w1, "admin_pass") == 0) {
+ strncpy(loginserveradminpassword, w2, sizeof(loginserveradminpassword));
+ loginserveradminpassword[sizeof(loginserveradminpassword)-1] = '\0';
+#ifdef PASSWORDENC
+ } else if (strcmpi(w1, "passenc") == 0) {
+ passenc = atoi(w2);
+ if (passenc < 0 || passenc > 2)
+ passenc = 0;
+#endif
+ } else if (strcmpi(w1, "defaultlanguage") == 0) {
+ if (w2[0] == 'F' || w2[0] == 'E')
+ defaultlanguage = w2[0];
+ } else if (strcmpi(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 (strcmpi(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 (strcmpi(w1, "import") == 0) {
+ ladmin_config_read(w2);
+ }
+ }
+ }
+ fclose(fp);
+
+ login_ip = inet_addr(loginserverip);
+
+ if (defaultlanguage == 'F') {
+ printf("---Lecture du fichier de configuration Ladmin terminée.\n");
+ } else {
+ printf("---End reading of Ladmin configuration file.\n");
+ }
+
+ return 0;
+}
+
+//--------------------------------------
+// Function called at exit of the server
+//--------------------------------------
+void do_final(void) {
+
+ if (already_exit_function == 0) {
+ delete_session(login_fd);
+
+ if (defaultlanguage == 'F') {
+ printf("\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)." RETCODE);
+ } else {
+ printf("\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)." RETCODE);
+ }
+
+ already_exit_function = 1;
+ }
+}
+
+//------------------------
+// Main function of ladmin
+//------------------------
+int do_init(int argc, char **argv)
+{
+ int next;
+ socket_init();
+
+ // read ladmin configuration
+ ladmin_config_read((argc > 1) ? argv[1] : LADMIN_CONF_NAME);
+
+ ladmin_log("");
+ if (defaultlanguage == 'F') {
+ ladmin_log("Fichier de configuration lu." RETCODE);
+ } else {
+ ladmin_log("Configuration file readed." RETCODE);
+ }
+
+ srand(time(NULL));
+
+ set_defaultparse(parse_fromlogin);
+
+ if (defaultlanguage == 'F') {
+ printf("Outil d'administration à distance de eAthena.\n");
+ printf("(pour eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION);
+ } else {
+ printf("EAthena login-server administration tool.\n");
+ printf("(for eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION);
+ }
+
+ if (defaultlanguage == 'F') {
+ ladmin_log("Ladmin est prêt." RETCODE);
+ printf("Ladmin est \033[1;32mprêt\033[0m.\n\n");
+ } else {
+ ladmin_log("Ladmin is ready." RETCODE);
+ printf("Ladmin is \033[1;32mready\033[0m.\n\n");
+ }
+
+ Connect_login_server();
+
+ // minimalist core doesn't have sockets parsing,
+ // so we have to do this ourselves
+ while (runflag) {
+ next = do_timer(gettick_nocache());
+ do_sendrecv(next);
+#ifndef TURBO
+ do_parsepacket();
+#endif
+ }
+
+ return 0;
+}
diff --git a/src/ladmin/ladmin.h b/src/ladmin/ladmin.h
new file mode 100644
index 000000000..5a1e8311a
--- /dev/null
+++ b/src/ladmin/ladmin.h
@@ -0,0 +1,13 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#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/md5calc.c b/src/ladmin/md5calc.c
new file mode 100644
index 000000000..f0acb4679
--- /dev/null
+++ b/src/ladmin/md5calc.c
@@ -0,0 +1,239 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+/***********************************************************
+ * md5 calculation algorithm
+ *
+ * The source code referred to the following URL.
+ * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html
+ *
+ ***********************************************************/
+
+#include "md5calc.h"
+#include <string.h>
+#include <stdio.h>
+
+#ifndef UINT_MAX
+#define UINT_MAX 4294967295U
+#endif
+
+// Global variable
+static unsigned int *pX;
+
+// Stirng Table
+static const unsigned int T[] = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60
+};
+
+// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC.
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+// The function used for other calculation
+static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Y) | (~X & Z);
+}
+static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Z) | (Y & ~Z);
+}
+static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return X ^ Y ^ Z;
+}
+static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return Y ^ (X | ~Z);
+}
+
+static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI,
+ unsigned int k, unsigned int s, unsigned int i)
+{
+ return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s);
+}
+
+static void Round1(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, F(b,c,d), k, s, i);
+}
+static void Round2(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, G(b,c,d), k, s, i);
+}
+static void Round3(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, H(b,c,d), k, s, i);
+}
+static void Round4(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, I(b,c,d), k, s, i);
+}
+
+static void MD5_Round_Calculate(const unsigned char *block,
+ unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2)
+{
+ //create X It is since it is required.
+ unsigned int X[16]; //512bit 64byte
+ int j,k;
+
+ //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D)
+ unsigned int A=*A2, B=*B2, C=*C2, D=*D2;
+ unsigned int AA = A,BB = B,CC = C,DD = D;
+
+ //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4
+ pX = X;
+
+ //Copy block(padding_message) i into X
+ for (j=0,k=0; j<64; j+=4,k++)
+ X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion
+ | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC
+ | ( ((unsigned int )block[j+2]) << 16 )
+ | ( ((unsigned int )block[j+3]) << 24 );
+
+
+ //Round 1
+ Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3);
+ Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7);
+ Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11);
+ Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15);
+
+ //Round 2
+ Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19);
+ Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23);
+ Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27);
+ Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31);
+
+ //Round 3
+ Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35);
+ Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39);
+ Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43);
+ Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47);
+
+ //Round 4
+ Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51);
+ Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55);
+ Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59);
+ Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63);
+
+ // Then perform the following additions. (let's add)
+ *A2 = A + AA;
+ *B2 = B + BB;
+ *C2 = C + CC;
+ *D2 = D + DD;
+
+ //The clearance of confidential information
+ memset(pX, 0, sizeof(X));
+}
+
+//-------------------------------------------------------------------
+// The function for the exteriors
+
+/** output is the coded binary in the character sequence which wants to code string. */
+void MD5_String2binary(const char * string, char * output)
+{
+//var
+ /*8bit*/
+ unsigned char padding_message[64]; //Extended message 512bit 64byte
+ unsigned char *pstring; //The position of string in the present scanning notes is held.
+
+// unsigned char digest[16];
+ /*32bit*/
+ unsigned int string_byte_len, //The byte chief of string is held.
+ string_bit_len, //The bit length of string is held.
+ copy_len, //The number of bytes which is used by 1-3 and which remained
+ msg_digest[4]; //Message digest 128bit 4byte
+ unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference)
+ *B = &msg_digest[1],
+ *C = &msg_digest[2],
+ *D = &msg_digest[3];
+ int i;
+
+//prog
+ //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head)
+ *A = 0x67452301;
+ *B = 0xefcdab89;
+ *C = 0x98badcfe;
+ *D = 0x10325476;
+
+ //Step 1.Append Padding Bits (extension of a mark bit)
+ //1-1
+ string_byte_len = strlen(string); //The byte chief of a character sequence is acquired.
+ pstring = (unsigned char *)string; //The position of the present character sequence is set.
+
+ //1-2 Repeat calculation until length becomes less than 64 bytes.
+ for (i=string_byte_len; 64<=i; i-=64,pstring+=64)
+ MD5_Round_Calculate(pstring, A,B,C,D);
+
+ //1-3
+ copy_len = string_byte_len % 64; //The number of bytes which remained is computed.
+ strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence.
+ memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length.
+ padding_message[copy_len] |= 0x80; //The next of a message is 1.
+
+ //1-4
+ //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes.
+ if (56 <= copy_len) {
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+ memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0.
+ }
+
+
+ //Step 2.Append Length (the information on length is added)
+ string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank)
+ memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set.
+
+ //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank.
+ if (UINT_MAX / 8 < string_byte_len) {
+ unsigned int high = (string_byte_len - UINT_MAX / 8) * 8;
+ memcpy(&padding_message[60], &high, 4);
+ } else
+ memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0.
+
+ //Step 4.Process Message in 16-Word Blocks (calculation of MD5)
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+
+
+ //Step 5.Output (output)
+ memcpy(output,msg_digest,16);
+// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC
+/* sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);*/
+}
+
+/** output is the coded character sequence in the character sequence which wants to code string. */
+void MD5_String(const char * string, char * output)
+{
+ unsigned char digest[16];
+
+ MD5_String2binary(string,(char*)digest);
+ sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+}
+
diff --git a/src/ladmin/md5calc.h b/src/ladmin/md5calc.h
new file mode 100644
index 000000000..1c42b16d9
--- /dev/null
+++ b/src/ladmin/md5calc.h
@@ -0,0 +1,10 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MD5CALC_H_
+#define _MD5CALC_H_
+
+void MD5_String(const char * string, char * output);
+void MD5_String2binary(const char * string, char * output);
+
+#endif
diff --git a/src/login/Makefile b/src/login/Makefile
new file mode 100644
index 000000000..549d1f170
--- /dev/null
+++ b/src/login/Makefile
@@ -0,0 +1,25 @@
+all txt: login-server
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/utils.o \
+ ../common/obj/strlib.o ../common/obj/graph.o ../common/obj/grfio.o \
+ ../common/obj/mapindex.o ../common/obj/ers.o ../zlib/unz.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h \
+ ../common/version.h ../common/db.h ../common/plugins.h ../common/lock.h \
+ ../common/malloc.h ../common/showmsg.h ../common/utils.h ../common/strlib.h \
+ ../common/graph.h ../common/grfio.h ../common/mapindex.h
+
+%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+login-server: login.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ login.o md5calc.o $(COMMON_OBJ) $(LIB_S)
+
+clean:
+ rm -f *.o ../../login-server
+
+# DO NOT DELETE
+
+login.o: login.c login.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
diff --git a/src/login/login.c b/src/login/login.c
new file mode 100644
index 000000000..f24e16499
--- /dev/null
+++ b/src/login/login.c
@@ -0,0 +1,4153 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// new version of the login-server by [Yor]
+
+#include <sys/types.h>
+#ifdef __WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <time.h>
+void Gettimeofday(struct timeval *timenow)
+{
+ time_t t;
+ t = clock();
+ timenow->tv_usec = (long)t;
+ timenow->tv_sec = (long)(t / CLK_TCK);
+ return;
+}
+#define gettimeofday(timenow, dummy) Gettimeofday(timenow)
+#define in_addr_t unsigned long
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h> // for stat/lstat/fstat
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "login.h"
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/version.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+#include "../common/malloc.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+
+#ifdef PASSWORDENC
+#include "md5calc.h"
+#endif
+
+int account_id_count = START_ACCOUNT_NUM;
+int server_num;
+int new_account_flag = 0;
+int bind_ip_set_ = 0;
+char bind_ip_str[128];
+int login_port = 6900;
+char lan_char_ip[16];
+int subneti[4];
+int subnetmaski[4];
+
+char account_filename[1024] = "save/account.txt";
+char GM_account_filename[1024] = "conf/GM_account.txt";
+char login_log_filename[1024] = "log/login.log";
+FILE *log_fp = NULL;
+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 log_login = 1;
+
+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 login_fd;
+
+static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex]
+//Account flood protection [Kevin]
+unsigned int new_reg_tick=0;
+int allowed_regs=1;
+int num_regs=0;
+int time_allowed=10; //Init this to 10 seconds. [Skotlex]
+
+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)
+
+int check_client_version = 0; //Client version check ON/OFF .. (sirius)
+int client_version_to_connect = 20; //Client version needed to connect ..(sirius)
+
+
+
+struct login_session_data {
+ unsigned 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 online_login_data {
+ int account_id;
+ short char_server;
+ short waiting_disconnect;
+};
+
+struct auth_dat {
+ int account_id, sex;
+ char userid[24], pass[33], lastlogin[24]; // 33 for 32 + NULL terminated
+ 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 = NULL;
+
+unsigned int auth_num = 0, auth_max = 0;
+
+// define the number of times that some players must authentify them before to save account file.
+// it's just about normal authentification. If an account is created or modified, save is immediatly done.
+// An authentification just change last connected IP and date. It already save in log file.
+// set minimum auth change before save:
+#define AUTH_BEFORE_SAVE_FILE 10
+// set divider of auth_num to found number of change before save
+#define AUTH_SAVE_FILE_DIVIDER 50
+int auth_before_save_file = 0; // Counter. First save when 1st char-server do connection.
+
+int admin_state = 0;
+char admin_pass[24] = "";
+unsigned int GM_num;
+unsigned int GM_max=256;
+char gm_pass[64] = "";
+int level_new_gm = 60;
+
+struct gm_account *gm_account_db;
+
+static struct dbt *online_db;
+
+int dynamic_pass_failure_ban = 1;
+int dynamic_pass_failure_ban_time = 5;
+int dynamic_pass_failure_ban_how_many = 3;
+int dynamic_pass_failure_ban_how_long = 1;
+
+int use_md5_passwds = 0;
+
+int console = 0;
+
+//------------------------------
+// Writing function of logs file
+//------------------------------
+int login_log(char *fmt, ...) {
+ if (log_login) {
+ va_list ap;
+ time_t raw_time;
+ char tmpstr[2048];
+
+ if(!log_fp)
+ log_fp = fopen(login_log_filename, "a");
+
+ if (log_fp) {
+ if (fmt[0] == '\0') // jump a line if no message
+ fprintf(log_fp, RETCODE);
+ else {
+ va_start(ap, fmt);
+ // Platform/Compiler dependant clock() for time check is removed. [Lance]
+ // clock() is originally used to track processing ticks on program execution.
+ time(&raw_time);
+ strftime(tmpstr, 24, date_format, localtime(&raw_time));
+ sprintf(tmpstr + strlen(tmpstr), ": %s", fmt);
+ vfprintf(log_fp, tmpstr, ap);
+ va_end(ap);
+ }
+ fflush(log_fp); // under cygwin or windows, if software is stopped, data are not written in the file -> fflush at every line
+ }
+ }
+
+ return 0;
+}
+
+static void* create_online_user(DBKey key, va_list args) {
+ struct online_login_data *p;
+ p = aCalloc(1, sizeof(struct online_login_data));
+ p->account_id = key.i;
+ p->char_server = -1;
+ return p;
+}
+//-----------------------------------------------------
+// Online User Database [Wizputer]
+//-----------------------------------------------------
+
+void add_online_user (int char_server, int account_id) {
+ struct online_login_data *p;
+ if (!online_check)
+ return;
+ p = idb_ensure(online_db, account_id, create_online_user);
+ p->char_server = char_server;
+ p->waiting_disconnect = 0;
+}
+int is_user_online (int account_id) {
+ return (idb_get(online_db, account_id) != NULL);
+}
+void remove_online_user (int account_id) {
+ if(!online_check)
+ return;
+ if (account_id == 99) { // reset all to offline
+ online_db->clear(online_db, NULL); // purge db
+ return;
+ }
+ idb_remove(online_db,account_id);
+}
+
+int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data)
+{
+ struct online_login_data *p;
+ if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect)
+ remove_online_user(p->account_id);
+ 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) {
+ unsigned int i;
+ for(i=0; i < GM_num; i++)
+ if(gm_account_db[i].account_id == account_id)
+ return gm_account_db[i].level;
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Adds a new GM using acc id and level
+//----------------------------------------------------------------------
+void addGM(int account_id, int level) {
+ unsigned int i;
+ int do_add = 0;
+ for(i = 0; i < auth_num; i++) {
+ if (auth_dat[i].account_id==account_id) {
+ do_add = 1;
+ break;
+ }
+ }
+ for(i = 0; i < GM_num; i++)
+ if (gm_account_db[i].account_id == account_id) {
+ if (gm_account_db[i].level == level)
+ ShowWarning("addGM: GM account %d defined twice (same level: %d).\n", account_id, level);
+ else {
+ ShowWarning("addGM: GM account %d defined twice (levels: %d and %d).\n", account_id, gm_account_db[i].level, level);
+ gm_account_db[i].level = level;
+ }
+ return;
+ }
+
+ // if new account
+ if (i == GM_num && do_add) {
+ if (GM_num >= GM_max) {
+ GM_max += 256;
+ gm_account_db = (struct gm_account*)aRealloc(gm_account_db, sizeof(struct gm_account) * GM_max);
+ memset(gm_account_db + (GM_max - 256), 0, sizeof(struct gm_account) * 256);
+ }
+ gm_account_db[GM_num].account_id = account_id;
+ gm_account_db[GM_num].level = level;
+ GM_num++;
+ if (GM_num >= 4000) {
+ ShowWarning("4000 GM accounts found. Next GM accounts are not read.\n");
+ login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not read." RETCODE);
+ }
+ }
+}
+
+//-------------------------------------------------------
+// Reading function of GM accounts file (and their level)
+//-------------------------------------------------------
+int read_gm_account(void) {
+ char line[512];
+ FILE *fp;
+ int account_id, level;
+ int line_counter;
+ struct stat file_stat;
+ int start_range = 0, end_range = 0, is_range = 0, current_id = 0;
+
+ if(gm_account_db) aFree(gm_account_db);
+ GM_num = 0;
+ if(GM_max < 0) GM_max = 256;
+ gm_account_db = (struct gm_account*)aCalloc(GM_max, sizeof(struct gm_account));
+
+ // 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 = (long)file_stat.st_mtime;
+
+ if ((fp = fopen(GM_account_filename, "r")) == NULL) {
+ ShowError("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename);
+ ShowError(" Actually, there is no GM accounts on the server.\n");
+ login_log("read_gm_account: GM accounts file [%s] not found." RETCODE, GM_account_filename);
+ login_log(" Actually, there is no GM accounts on the server." RETCODE);
+ return 1;
+ }
+
+ line_counter = 0;
+ // 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) && GM_num < 4000) {
+ line_counter++;
+ if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
+ continue;
+ is_range = (sscanf(line, "%d%*[-~]%d %d",&start_range,&end_range,&level)==3); // ID Range [MC Cameri]
+ if (!is_range && sscanf(line, "%d %d", &account_id, &level) != 2 && sscanf(line, "%d: %d", &account_id, &level) != 2)
+ ShowError("read_gm_account: file [%s], invalid 'acount_id|range level' format (line #%d).\n", GM_account_filename, line_counter);
+ else if (level <= 0)
+ ShowError("read_gm_account: file [%s] %dth account (line #%d) (invalid level [0 or negative]: %d).\n", GM_account_filename, GM_num+1, line_counter, level);
+ else {
+ if (level > 99) {
+ ShowNotice("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, GM_num+1, level);
+ level = 99;
+ }
+ if (is_range) {
+ if (start_range==end_range)
+ ShowError("read_gm_account: file [%s] invalid range, beginning of range is equal to end of range (line #%d).\n", GM_account_filename, line_counter);
+ else if (start_range>end_range)
+ ShowError("read_gm_account: file [%s] invalid range, beginning of range must be lower than end of range (line #%d).\n", GM_account_filename, line_counter);
+ else
+ for (current_id = start_range;current_id<=end_range;current_id++)
+ addGM(current_id,level);
+ } else {
+ addGM(account_id,level);
+ }
+ }
+ }
+ fclose(fp);
+
+ ShowStatus("read_gm_account: file '%s' read (%d GM accounts found).\n", GM_account_filename, GM_num);
+ login_log("read_gm_account: file '%s' read (%d GM accounts found)." RETCODE, GM_account_filename, GM_num);
+
+ 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((const char*)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((const char*)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((const char*)(str+i), "%d", &m) == 1 && m >= 0 && m <= 32) {
+ for(i = 0; i < m && i < 32; i++)
+ mask = (mask >> 1) | 0x80000000;
+ } else {
+ ShowError("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[16];
+ char * access_ip;
+ 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++) {
+ access_ip = access_allow + i * ACO_STRSIZE;
+ if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) {
+ if(access_order == ACO_ALLOW_DENY)
+ return 1; // With 'allow, deny' (deny if not allow), allow has priority
+ flag = ACF_ALLOW;
+ break;
+ }
+ }
+
+ for(i = 0; i < access_denynum; i++) {
+ access_ip = access_deny + i * ACO_STRSIZE;
+ if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) {
+ //flag = ACF_DENY; // not necessary to define flag
+ return 0; // At this point, if it's 'deny', we refuse connection.
+ }
+ }
+
+ 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[16];
+ char * access_ip;
+
+ 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++) {
+ access_ip = access_ladmin_allow + i * ACO_STRSIZE;
+ if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+//---------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid).
+//---------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++)
+ if (strchr(last_arobas, ch) != NULL)
+ return 0;
+
+ 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) {
+ unsigned int i, quantity;
+ int 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 (stricmp(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,
+ (long)p->connect_until_time, p->last_ip, p->memo, (long)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,%s ", 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;
+ unsigned int j;
+ char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048];
+ long ban_until_time;
+ long connect_until_time;
+ char str[2048];
+ char v[2048];
+ int GM_count = 0;
+ int server_count = 0;
+
+ auth_max = 256;
+ auth_dat = (struct auth_dat*)aCalloc(auth_max, sizeof(struct auth_dat));
+
+ if ((fp = fopen(account_filename, "r")) == NULL) {
+ // no account file -> no account -> no login, including char-server (ERROR)
+ ShowError(CL_RED"mmmo_auth_init: Accounts file [%s] not found."CL_RESET"\n", account_filename);
+ return 0;
+ }
+
+ while(fgets(line, sizeof(line)-1, fp) != NULL) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ line[sizeof(line)-1] = '\0';
+ // remove carriage return if exist
+ while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r'))
+ line[strlen(line)-1] = '\0';
+ p = line;
+
+ memset(userid, 0, sizeof(userid));
+ memset(pass, 0, sizeof(pass));
+ memset(lastlogin, 0, sizeof(lastlogin));
+ memset(email, 0, sizeof(email));
+ memset(error_message, 0, sizeof(error_message));
+ memset(last_ip, 0, sizeof(last_ip));
+ memset(memo, 0, sizeof(memo));
+
+ // 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) {
+ ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM);
+ ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id);
+ login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM);
+ login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id);
+ login_log("%s", line);
+ continue;
+ }
+ userid[23] = '\0';
+ remove_control_chars((unsigned char *)userid);
+ for(j = 0; j < auth_num; j++) {
+ if (auth_dat[j].account_id == account_id) {
+ ShowError(CL_RED"mmmo_auth_init: an account has an identical id to another.\n");
+ ShowError(" account id #%d -> new account not read (saved in log file)."CL_RED"\n", account_id);
+ login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE);
+ login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id);
+ login_log("%s", line);
+ break;
+ } else if (strcmp(auth_dat[j].userid, userid) == 0) {
+ ShowError(CL_RED"mmmo_auth_init: account name already exists.\n");
+ ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid); // 2 lines, account name can be long.
+ login_log("mmmo_auth_init: ******Error: an account has an identical name to another." RETCODE);
+ login_log(" account name '%s' -> new account not read (saved in next line):" RETCODE, userid);
+ login_log("%s", line);
+ break;
+ }
+ }
+ if (j != auth_num)
+ continue;
+
+ if (auth_num >= auth_max) {
+ auth_max += 256;
+ auth_dat = (struct auth_dat*)aRealloc(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((unsigned char *)pass);
+ strncpy(auth_dat[auth_num].pass, pass, 24);
+
+ lastlogin[23] = '\0';
+ remove_control_chars((unsigned char *)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) {
+ ShowNotice("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((unsigned char *)email);
+ strncpy(auth_dat[auth_num].email, email, 40);
+ }
+
+ error_message[19] = '\0';
+ remove_control_chars((unsigned char *)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 = (time_t)ban_until_time;
+ else
+ auth_dat[auth_num].ban_until_time = 0;
+
+ auth_dat[auth_num].connect_until_time = (time_t)connect_until_time;
+
+ last_ip[15] = '\0';
+ remove_control_chars((unsigned char *)last_ip);
+ strncpy(auth_dat[auth_num].last_ip, last_ip, 16);
+
+ memo[254] = '\0';
+ remove_control_chars((unsigned char *)memo);
+ strncpy(auth_dat[auth_num].memo, memo, 255);
+
+ for(j = 0; j < ACCOUNT_REG2_NUM; j++) {
+ p += n;
+ if (sscanf(p, "%[^\t,],%[^\t ] %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, ",%[^\t ] %n", v, &n) == 1) {
+ j--;
+ continue;
+ } else
+ break;
+ }
+ str[31] = '\0';
+ remove_control_chars((unsigned char *)str);
+ strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32);
+ strncpy(auth_dat[auth_num].account_reg2[j].value,v,256);
+ }
+ 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) {
+ ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM);
+ ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id);
+ login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM);
+ login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id);
+ login_log("%s", line);
+ continue;
+ }
+ userid[23] = '\0';
+ remove_control_chars((unsigned char *)userid);
+ for(j = 0; j < auth_num; j++) {
+ if (auth_dat[j].account_id == account_id) {
+ ShowError(CL_RED"mmo_auth_init: an account has an identical id to another.\n");
+ ShowError(" account id #%d -> new account not read (saved in log file)."CL_RESET"\n", account_id);
+ login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE);
+ login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id);
+ login_log("%s", line);
+ break;
+ } else if (strcmp(auth_dat[j].userid, userid) == 0) {
+ ShowError(CL_RED"mmo_auth_init: account name already exists.\n");
+ ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid);
+ login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE);
+ login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id);
+ login_log("%s", line);
+ break;
+ }
+ }
+ if (j != auth_num)
+ continue;
+
+ if (auth_num >= auth_max) {
+ auth_max += 256;
+ auth_dat = (struct auth_dat*)aRealloc(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((unsigned char *)pass);
+ strncpy(auth_dat[auth_num].pass, pass, 24);
+
+ lastlogin[23] = '\0';
+ remove_control_chars((unsigned char *)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,],%[^\t ] %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, ",%[^\t ] %n", v, &n) == 1) {
+ j--;
+ continue;
+ } else
+ break;
+ }
+ str[31] = '\0';
+ remove_control_chars((unsigned char *)str);
+ strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32);
+ strncpy(auth_dat[auth_num].account_reg2[j].value,v,256);
+ }
+ 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) {
+ ShowNotice("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) {
+ ShowStatus("mmo_auth_init: 1 account read in %s,\n", account_filename);
+ sprintf(line, "1 account read in %s,", account_filename);
+ } else {
+ ShowStatus("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) {
+ ShowStatus(" of which is no GM account, and ");
+ sprintf(str, "%s of which is no GM account and", line);
+ } else if (GM_count == 1) {
+ ShowStatus(" of which is 1 GM account, and ");
+ sprintf(str, "%s of which is 1 GM account and", line);
+ } else {
+ ShowStatus(" 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" RETCODE, line);
+
+ return 0;
+}
+
+//------------------------------------------
+// Writing of the accounts database file
+// (accounts are sorted by id before save)
+//------------------------------------------
+void mmo_auth_sync(void) {
+ FILE *fp;
+ unsigned int i, j, k;
+ int lock;
+ int account_id;
+ //int id[auth_num];
+ //int *id = (int *)aCalloc(auth_num, sizeof(int));
+ CREATE_BUFFER(id, int, auth_num);
+ char line[65536];
+
+ // Sorting before save
+ for(i = 0; i < auth_num; i++) {
+ id[i] = i;
+ account_id = auth_dat[i].account_id;
+ for(j = 0; j < i; j++) {
+ if (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
+ if ((fp = lock_fopen(account_filename, &lock)) == NULL) {
+ //if (id) aFree(id); // aFree, right?
+ DELETE_BUFFER(id);
+ 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" RETCODE, line);
+ }
+ fprintf(fp, "%d\t%%newid%%\n", account_id_count);
+
+ lock_fclose(fp, account_filename, &lock);
+
+ // set new counter to minimum number of auth before save
+ auth_before_save_file = auth_num / AUTH_SAVE_FILE_DIVIDER; // Re-initialise counter. We have save.
+ if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE)
+ auth_before_save_file = AUTH_BEFORE_SAVE_FILE;
+
+ //if (id) aFree(id);
+ DELETE_BUFFER(id);
+
+ return;
+}
+
+//-----------------------------------------------------
+// Check if we must save accounts file or not
+// every minute, we check if we must save because we
+// have do some authentifications without arrive to
+// the minimum of authentifications for the save.
+// Note: all other modification of accounts (deletion,
+// change of some informations excepted lastip/
+// lastlogintime, creation) are always save
+// immediatly and set the minimum of
+// authentifications to its initialization value.
+//-----------------------------------------------------
+int check_auth_sync(int tid, unsigned int tick, int id, int data) {
+ // we only save if necessary:
+ // we have do some authentifications without do saving
+ if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE ||
+ auth_before_save_file < (int)(auth_num / AUTH_SAVE_FILE_DIVIDER))
+ mmo_auth_sync();
+
+ return 0;
+}
+
+//--------------------------------------------------------------------
+// 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, fd;
+
+ for(i = 0, c = 0; i < MAX_SERVERS; i++) {
+ if ((fd = server_fd[i]) >= 0 && fd != sfd) {
+ WFIFOHEAD(fd, len);
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd, len);
+ c++;
+ }
+ }
+ return c;
+}
+
+//-----------------------------------------------------
+// Send GM accounts to all char-server
+//-----------------------------------------------------
+void send_GM_accounts(void) {
+ unsigned int i;
+ unsigned char buf[32767];
+ int len;
+
+ len = 4;
+ WBUFW(buf,0) = 0x2732;
+ for(i = 0; i < GM_num; i++)
+ // send only existing accounts. We can not create a GM account when server is online.
+ if (gm_account_db[i].level > 0) {
+ WBUFL(buf,len) = gm_account_db[i].account_id;
+ WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level;
+ len += 5;
+ }
+ WBUFW(buf,2) = len;
+ charif_sendallwos(-1, buf, len);
+
+ return;
+}
+
+//-----------------------------------------------------
+// Check if GM file account have been changed
+//-----------------------------------------------------
+int check_GM_file(int tid, unsigned int tick, int id, int data) {
+ struct stat file_stat;
+ long new_time;
+
+ // if we would not check
+ if (gm_account_filename_check_timer < 1)
+ return 0;
+
+ // get last modify time/date
+ if (stat(GM_account_filename, &file_stat))
+ new_time = 0; // error
+ else
+ new_time = (long)file_stat.st_mtime;
+
+ if (new_time != creation_time_GM_account_file) {
+ read_gm_account();
+ send_GM_accounts();
+ }
+
+ return 0;
+}
+
+//-------------------------------------
+// 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;
+ auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(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';
+
+ strncpy(auth_dat[i].pass, account->passwd, 32);
+ auth_dat[i].pass[23] = '\0';
+
+ memcpy(auth_dat[i].lastlogin, "-", 2);
+
+ auth_dat[i].sex = (sex == 'M' || 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 = localtime(&timestamp);
+ 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) {
+ unsigned int i;
+ time_t raw_time;
+ char tmpstr[256];
+ int len, newaccount = 0;
+#ifdef PASSWORDENC
+ struct login_session_data *ld;
+#endif
+ int encpasswdok;
+ char md5str[64], md5bin[32];
+ char ip[16];
+ unsigned char *sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr;
+ char user_password[256];
+
+ 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' ||
+ account->userid[len+1] == 'f' || account->userid[len+1] == 'm')
+ && new_account_flag && account_id_count <= END_ACCOUNT_NUM && len >= 4 && strlen(account->passwd) >= 4) {
+
+ //only continue if amount in this time limit is allowed (account registration flood protection)[Kevin]
+ if(gettick() <= new_reg_tick && num_regs >= allowed_regs) {
+ ShowNotice("Account registration denied (registration limit exceeded) to %s!\n", ip);
+ login_log("Notice: Account registration denied (registration limit exceeded) to %s!", ip);
+ return 3;
+ } else {
+ num_regs=0;
+ }
+
+ newaccount = 1;
+ account->userid[len] = '\0';
+ }
+
+ //EXE Version check [Sirius]
+ if (check_client_version == 1 && account->version != 0 &&
+ account->version != client_version_to_connect)
+ return 5;
+
+ // 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 && i == auth_num) {
+ i = search_account_index(account->userid);
+ if (i == -1)
+ i = auth_num;
+ else
+ memcpy(account->userid, auth_dat[i].userid, NAME_LENGTH); // for the possible tests/checks afterwards (copy correcte sensitive case).
+ }
+
+ if (i != auth_num) {
+ if (newaccount) {
+ login_log("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s, ip: %s)" RETCODE,
+ account->userid, account->userid[len+1], auth_dat[i].pass, account->passwd, ip);
+ return 1; // 1 = Incorrect Password
+ }
+ if(use_md5_passwds)
+ MD5_String(account->passwd, user_password);
+ else
+ memcpy(user_password, account->passwd, 25);
+ encpasswdok = 0;
+#ifdef PASSWORDENC
+ ld = (struct login_session_data*)session[fd]->session_data;
+ if (account->passwdenc > 0) {
+ int j = account->passwdenc;
+ if (!ld) {
+ login_log("Md5 key not created (account: %s, ip: %s)" RETCODE, account->userid, ip);
+ return 1; // 1 = Incorrect Password
+ }
+ if (j > 2)
+ j = 1;
+ do {
+ if (j == 1) {
+ sprintf(md5str, "%s%s", ld->md5key, auth_dat[i].pass); // 20 + 24
+ } else if (j == 2) {
+ sprintf(md5str, "%s%s", auth_dat[i].pass, ld->md5key); // 24 + 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 ((strcmp(account->passwd, auth_dat[i].pass) && !encpasswdok)) {
+ if (account->passwdenc == 0)
+ login_log("Invalid password (account: %s, pass: %s, received pass: %s, ip: %s)" RETCODE, account->userid, auth_dat[i].pass, account->passwd, ip);
+#ifdef PASSWORDENC
+ else {
+ char logbuf[512], *p = logbuf;
+ unsigned 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)" RETCODE, ip);
+ login_log(logbuf);
+ }
+#endif
+ return 1; // 1 = Incorrect Password
+ }
+
+ if (auth_dat[i].state) {
+ login_log("Connection refused (account: %s, pass: %s, state: %d, ip: %s)" RETCODE,
+ account->userid, account->passwd, 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 more accounts may be connected from this company
+ case 10: // 9 = MSI_REFUSE_BAN_BY_DBA
+ case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
+ case 12: // 11 = MSI_REFUSE_BAN_BY_GM
+ case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
+ case 14: // 13 = MSI_REFUSE_SELF_LOCK
+ case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ case 100: // 99 = This ID has been totally erased
+ case 101: // 100 = Login information remains at %s.
+ case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
+ case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation
+ case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being
+ case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being
+ return auth_dat[i].state - 1;
+ default:
+ return 99; // 99 = ID has been totally erased
+ }
+ }
+
+ if (online_check) {
+ unsigned char buf[8];
+ struct online_login_data* data = idb_get(online_db,auth_dat[i].account_id);
+ if (data && data->char_server > -1) {
+ //Request char servers to kick this account out. [Skotlex]
+ ShowWarning("User [%d] is already online - Rejected.\n",auth_dat[i].account_id);
+ WBUFW(buf,0) = 0x2734;
+ WBUFL(buf,2) = auth_dat[i].account_id;
+ charif_sendallwos(-1, buf, 6);
+ if (!data->waiting_disconnect)
+ add_timer(gettick()+30000, waiting_disconnect_timer,auth_dat[i].account_id, 0);
+ data->waiting_disconnect = 1;
+ return 3; // Rejected
+ }
+ }
+
+ if (auth_dat[i].ban_until_time != 0) { // if account is banned
+ strftime(tmpstr, 20, date_format, localtime(&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, pass: %s, banned until %s, ip: %s)" RETCODE,
+ account->userid, account->passwd, tmpstr, ip);
+ return 6; // 6 = Your are Prohibited to log in until %s
+ } else { // ban is finished
+ login_log("End of ban (account: %s, pass: %s, previously banned until %s -> not more banned, ip: %s)" RETCODE,
+ account->userid, account->passwd, 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, pass: %s, expired ID, ip: %s)" RETCODE,
+ account->userid, account->passwd, ip);
+ return 2; // 2 = This ID is expired
+ }
+
+ login_log("Authentification accepted (account: %s (id: %d), ip: %s)" RETCODE, account->userid, auth_dat[i].account_id, ip);
+ } else {
+ if (!newaccount) {
+ login_log("Unknown account (account: %s, received pass: %s, ip: %s)" RETCODE,
+ account->userid, account->passwd, 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), pass: %s, sex: %c, connection with _F/_M, ip: %s)" RETCODE,
+ account->userid, new_id, account->passwd, account->userid[len+1], ip);
+ auth_before_save_file = 0; // Creation of an account -> save accounts file immediatly
+
+ //restart ticker (account registration flood protection)[Kevin]
+ if(num_regs==0) {
+ new_reg_tick=gettick()+time_allowed*1000;
+ }
+ num_regs++;
+ }
+ }
+
+ // auth start : time seed
+ // Platform/Compiler dependant clock() for time check is removed. [Lance]
+ // clock() is originally used to track processing ticks on program execution.
+ time(&raw_time);
+ strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time));
+
+ account->account_id = auth_dat[i].account_id;
+ account->login_id1 = rand();
+ account->login_id2 = rand();
+ memcpy(account->lastlogin, auth_dat[i].lastlogin, 24);
+ memcpy(auth_dat[i].lastlogin, tmpstr, 24);
+ account->sex = auth_dat[i].sex;
+ if (account->sex != 2 && account->account_id < 700000)
+ ShowWarning("Account %s has account id %d! Account IDs must be over 700000 to work properly!\n", account->userid, account->account_id);
+
+ strncpy(auth_dat[i].last_ip, ip, 16);
+ auth_dat[i].logincount++;
+
+ // Save until for change ip/time of auth is not very useful => limited save for that
+ // Save there informations isnot necessary, because they are saved in log file.
+ if (--auth_before_save_file <= 0) // Reduce counter. 0 or less, we save
+ mmo_auth_sync();
+
+ return -1; // account OK
+}
+
+static int online_db_setoffline(DBKey key, void* data, va_list ap) {
+ struct online_login_data *p = (struct online_login_data *)data;
+ int server = va_arg(ap, int);
+ if (server == -1) {
+ p->char_server = -1;
+ p->waiting_disconnect = 0;
+ } else if (p->char_server == server)
+ p->char_server = -2; //Char server disconnected.
+ return 0;
+}
+
+//--------------------------------
+// Packet parsing for char-servers
+//--------------------------------
+int parse_fromchar(int fd) {
+ unsigned int i;
+ int j, id;
+ unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ char ip[16];
+ int acc;
+ RFIFOHEAD(fd);
+
+ 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 = 1;
+ if(session[fd]->eof) {
+ if (id < MAX_SERVERS) {
+ ShowWarning("Char-server '%s' has disconnected.\n", server[id].name);
+ login_log("Char-server '%s' has disconnected (ip: %s)." RETCODE,
+ server[id].name, ip);
+ server_fd[id] = -1;
+ memset(&server[id], 0, sizeof(struct mmo_char_server));
+ online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline.
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ 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)
+ ShowDebug("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)." RETCODE, 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 0;
+ {
+ 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) {
+ unsigned int k;
+ time_t connect_until_time = 0;
+ char email[40] = "";
+ auth_fifo[i].delflag = 1;
+ login_log("Char-server '%s': authentification of the account %d accepted (ip: %s)." RETCODE,
+ server[id].name, acc, ip);
+// printf("%d\n", i);
+ for(k = 0; k < auth_num; k++) {
+ if (auth_dat[k].account_id == acc) {
+ strcpy(email, auth_dat[k].email);
+ connect_until_time = auth_dat[k].connect_until_time;
+ break;
+ }
+ }
+ WFIFOW(fd,0) = 0x2713;
+ WFIFOL(fd,2) = acc;
+ WFIFOB(fd,6) = 0;
+ memcpy(WFIFOP(fd, 7), email, 40);
+ WFIFOL(fd,47) = (unsigned long)connect_until_time;
+ WFIFOSET(fd,51);
+ break;
+ }
+ }
+ // authentification not found
+ if (i == AUTH_FIFO_SIZE) {
+ login_log("Char-server '%s': authentification of the account %d REFUSED (ip: %s)." RETCODE,
+ server[id].name, acc, ip);
+ WFIFOHEAD(fd, 51);
+ 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 0;
+ //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);
+ // send some answer
+ WFIFOHEAD(fd, 2);
+ WFIFOW(fd,0) = 0x2718;
+ WFIFOSET(fd,2);
+
+ RFIFOSKIP(fd,6);
+ break;
+
+ // we receive a e-mail creation of an account with a default e-mail (no answer)
+ case 0x2715:
+ if (RFIFOREST(fd) < 46)
+ return 0;
+ {
+ char email[40];
+ acc = RFIFOL(fd,2); // speed up
+ memcpy(email, RFIFOP(fd,6), 40);
+ email[39] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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)." RETCODE,
+ server[id].name, acc, email, ip);
+ // Save
+ mmo_auth_sync();
+ 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)." RETCODE,
+ 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 0;
+ //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)." RETCODE,
+ 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)." RETCODE,
+ 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 0;
+ {
+ 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((char*)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];
+ time_t raw_time;
+ time(&raw_time);
+ strftime(tmpstr, 23, date_format, localtime(&raw_time));
+ fprintf(fp, RETCODE "// %s: @GM command on account %d" RETCODE "%d %d" RETCODE, tmpstr, acc, acc, level_new_gm);
+ fclose(fp);
+ WBUFL(buf,6) = level_new_gm;
+ read_gm_account();
+ send_GM_accounts();
+ ShowNotice("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)." RETCODE,
+ server[id].name, acc, level_new_gm, ip);
+ } else {
+ ShowError("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)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ } else {
+ ShowError("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)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ } else {
+ ShowError("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)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ } else {
+ ShowError("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)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ charif_sendallwos(-1, buf, 10);
+ }
+ RFIFOSKIP(fd, RFIFOW(fd,2));
+ return 0;
+
+ // 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 0;
+ {
+ 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((unsigned char *)actual_email);
+ memcpy(new_email, RFIFOP(fd,46), 40);
+ new_email[39] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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)" RETCODE,
+ server[id].name, acc, ip);
+ else if (strcmpi(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)" RETCODE,
+ server[id].name, acc, ip);
+ else {
+ for(i = 0; i < auth_num; i++) {
+ if (auth_dat[i].account_id == acc) {
+ if (strcmpi(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)." RETCODE,
+ server[id].name, acc, auth_dat[i].userid, new_email, ip);
+ // Save
+ mmo_auth_sync();
+ } 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)." RETCODE,
+ 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)." RETCODE,
+ 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 0;
+ {
+ 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)." RETCODE,
+ 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;
+ // Save
+ mmo_auth_sync();
+ } else
+ login_log("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s)." RETCODE,
+ 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)." RETCODE,
+ server[id].name, acc, statut, ip);
+ }
+ RFIFOSKIP(fd,10);
+ }
+ return 0;
+
+ case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor)
+ if (RFIFOREST(fd) < 18)
+ return 0;
+ {
+ 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 = localtime(&timestamp);
+ 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 = mktime(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, localtime(&timestamp));
+ login_log("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s)." RETCODE,
+ 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) = (unsigned int)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)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ auth_dat[i].ban_until_time = timestamp;
+ // Save
+ mmo_auth_sync();
+ } else {
+ login_log("Char-server '%s': Error of ban request (account: %d, no change for ban date, ip: %s)." RETCODE,
+ server[id].name, acc, ip);
+ }
+ } else {
+ login_log("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s)." RETCODE,
+ 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)." RETCODE,
+ server[id].name, acc, ip);
+ RFIFOSKIP(fd,18);
+ }
+ return 0;
+
+ case 0x2727: // Change of sex (sex is reversed)
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ {
+ int 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)." RETCODE,
+ 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)." RETCODE,
+ 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);
+ // Save
+ mmo_auth_sync();
+ }
+ break;
+ }
+ }
+ if (i == auth_num)
+ login_log("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s)." RETCODE,
+ server[id].name, acc, ip);
+ RFIFOSKIP(fd,6);
+ }
+ return 0;
+
+ case 0x2728: // We receive account_reg2 from a char-server, and we send them to other map-servers.
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+ int 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];
+ unsigned char *buf;
+ int len;
+ buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char));
+ login_log("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s)." RETCODE,
+ server[id].name, acc, ip);
+ for(j=0,p=13;j<ACCOUNT_REG2_NUM && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n",auth_dat[i].account_reg2[j].str,&len);
+ auth_dat[i].account_reg2[j].str[len]='\0';
+ p +=len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n",auth_dat[i].account_reg2[j].value,&len);
+ auth_dat[i].account_reg2[j].value[len]='\0';
+ p +=len+1;
+ remove_control_chars((unsigned char *)auth_dat[i].account_reg2[j].str);
+ remove_control_chars((unsigned char *)auth_dat[i].account_reg2[j].value);
+ }
+ 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));
+ // Save
+ mmo_auth_sync();
+// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (account id: %d).\n", acc);
+ if (buf) aFree(buf);
+ 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)." RETCODE,
+ 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 0;
+ {
+ 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)." RETCODE,
+ server[id].name, acc, ip);
+ } else {
+ login_log("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s)." RETCODE,
+ 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)." RETCODE,
+ server[id].name, acc, ip);
+ RFIFOSKIP(fd,6);
+ }
+ return 0;
+
+ case 0x272b: // Set account_id to online [Wizputer]
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ add_online_user(id, RFIFOL(fd,2));
+ RFIFOSKIP(fd,6);
+ break;
+
+ case 0x272c: // Set account_id to offline [Wizputer]
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ remove_online_user(RFIFOL(fd,2));
+ RFIFOSKIP(fd,6);
+ break;
+
+ case 0x272d: // Receive list of all online accounts. [Skotlex]
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ if (!online_check) {
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ {
+ struct online_login_data *p;
+ int aid, i, users;
+ online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first
+ users = RFIFOW(fd,4);
+ for (i = 0; i < users; i++) {
+ aid = RFIFOL(fd,6+i*4);
+ p = idb_ensure(online_db, aid, create_online_user);
+ p->char_server = id;
+ p->waiting_disconnect = 0;
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ case 0x272e: //Request account_reg2 for a character.
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ int account_id = RFIFOL(fd, 2);
+ int char_id = RFIFOL(fd, 6);
+ int p;
+ RFIFOSKIP(fd,10);
+ WFIFOW(fd,0) = 0x2729;
+ WFIFOL(fd,4) = account_id;
+ WFIFOL(fd,8) = char_id;
+ WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
+ for(i = 0; i < auth_num && auth_dat[i].account_id != account_id; i++);
+ if (i == auth_num) {
+ //Account not found? Send at least empty data, map servers need a reply!
+ WFIFOW(fd,2) = 13;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ break;
+ }
+ for(p = 13,j=0;j<auth_dat[i].account_reg2_num;j++){
+ if (auth_dat[i].account_reg2[j].str[0]) {
+ p+= sprintf(WFIFOP(fd,p), "%s", auth_dat[i].account_reg2[j].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(fd,p), "%s", auth_dat[i].account_reg2[j].value)+1;
+ }
+ }
+ WFIFOW(fd,2) = p;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ break;
+
+ case 0x3000: //change sex for chrif_changesex()
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ {
+ unsigned int sex,i = 0;
+ acc = RFIFOL(fd,4);
+ sex = RFIFOB(fd,8);
+ if (sex != 0 && sex != 1)
+ sex = 0;
+ for(i = 0; i < auth_num; i++) {
+ if (auth_dat[i].account_id == acc) {
+ unsigned char buf[16];
+ login_log("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s)." RETCODE,
+ server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip);
+ auth_fifo[i].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, suggested sex %c, ip: %s)." RETCODE,
+ server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip);
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ }
+ return 0;
+
+ default:
+ {
+ FILE *logfp;
+ char tmpstr[24];
+ time_t raw_time;
+ logfp = fopen(login_log_unknown_packets_filename, "a");
+ if (logfp) {
+ time(&raw_time);
+ strftime(tmpstr, 23, date_format, localtime(&raw_time));
+ fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr);
+ fprintf(logfp, "parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd));
+ fprintf(logfp, "Detail (in hex):" RETCODE);
+ fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE);
+ 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" RETCODE, 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" RETCODE, tmpstr);
+ }
+ fprintf(logfp, RETCODE);
+ fclose(logfp);
+ }
+ }
+ ShowWarning("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", RFIFOW(fd,0));
+ session[fd]->eof = 1;
+ ShowStatus("Char-server has been disconnected (unknown packet).\n");
+ return 0;
+ }
+ }
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ return 0;
+}
+
+//---------------------------------------
+// Packet parsing for administation login
+//---------------------------------------
+int parse_admin(int fd) {
+ unsigned int i, j;
+ unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ char* account_name;
+ char ip[16];
+ RFIFOHEAD(fd);
+
+ sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+
+ if (session[fd]->eof) {
+ do_close(fd);
+ ShowInfo("Remote administration has disconnected (session #%d).\n", fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2) {
+ if (display_parse_admin == 1) {
+
+ ShowDebug("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)" RETCODE, ip);
+ WFIFOHEAD(fd, 10);
+ WFIFOW(fd,0) = 0x7531;
+ WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
+ WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
+ WFIFOB(fd,4) = ATHENA_REVISION;
+ WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
+ WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
+ WFIFOB(fd,7) = ATHENA_SERVER_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)" RETCODE, ip);
+ RFIFOSKIP(fd,2);
+ session[fd]->eof = 1;
+ break;
+
+ case 0x7920: // Request of an accounts list
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ int st, ed, len;
+ //int id[auth_num];
+ //int *id=(int *)aCalloc(auth_num, sizeof(int));
+ CREATE_BUFFER(id, int, 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)" RETCODE, st, ed, ip);
+ // Sort before send
+ for(i = 0; i < auth_num; i++) {
+ unsigned 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);
+ //if (id) free(id);
+ DELETE_BUFFER(id);
+ }
+ break;
+
+ case 0x7930: // Request for an account creation
+ if (RFIFOREST(fd) < 91)
+ return 0;
+ {
+ struct mmo_account ma;
+ ma.userid = (char*)RFIFOP(fd, 2);
+ ma.userid[23] = '\0';
+ memcpy(ma.passwd, RFIFOP(fd, 26), 24);
+ ma.passwd[23] = '\0';
+ memcpy(ma.lastlogin, "-", 2);
+ ma.sex = RFIFOB(fd,50);
+ WFIFOW(fd,0) = 0x7931;
+ WFIFOL(fd,2) = 0xffffffff;
+ 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)" RETCODE,
+ 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)" RETCODE,
+ ip);
+ } else if (ma.sex != 'F' && ma.sex != 'M') {
+ login_log("'ladmin': Attempt to create an invalid account (account: %s, received pass: %s, invalid sex, ip: %s)" RETCODE,
+ ma.userid, ma.passwd, 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, pass: %s, sex: %c, ip: %s)" RETCODE,
+ ma.userid, ma.passwd, ma.sex, ip);
+ } else {
+ remove_control_chars((unsigned char *)ma.userid);
+ remove_control_chars((unsigned char *)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, pass: %s, received pass: %s, ip: %s)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].pass, ma.passwd, ip);
+ break;
+ }
+ }
+ if (i == auth_num) {
+ int new_id;
+ char email[40];
+ memcpy(email, RFIFOP(fd,51), 40);
+ email[39] = '\0';
+ remove_control_chars((unsigned char *)email);
+ new_id = mmo_auth_new(&ma, ma.sex, email);
+ login_log("'ladmin': Account creation (account: %s (id: %d), pass: %s, sex: %c, email: %s, ip: %s)" RETCODE,
+ ma.userid, new_id, ma.passwd, ma.sex, auth_dat[i].email, ip);
+ WFIFOL(fd,2) = new_id;
+ mmo_auth_sync();
+ }
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,91);
+ }
+ break;
+
+ case 0x7932: // Request for an account deletion
+ if (RFIFOREST(fd) < 26)
+ return 0;
+ WFIFOW(fd,0) = 0x7933;
+ WFIFOL(fd,2) = 0xFFFFFFFF;
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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:" RETCODE,
+ auth_dat[i].userid, auth_dat[i].account_id, ip);
+ mmo_auth_tostr((char*)buf, &auth_dat[i]);
+ login_log("%s" RETCODE, buf);
+ // delete account
+ memset(auth_dat[i].userid, '\0', sizeof(auth_dat[i].userid));
+ auth_dat[i].account_id = -1;
+ mmo_auth_sync();
+ } else {
+ memcpy(WFIFOP(fd,6), account_name, 24);
+ login_log("'ladmin': Attempt to delete an unknown account (account: %s, ip: %s)" RETCODE,
+ account_name, ip);
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,26);
+ break;
+
+ case 0x7934: // Request to change a password
+ if (RFIFOREST(fd) < 50)
+ return 0;
+ WFIFOW(fd,0) = 0x7935;
+ WFIFOL(fd,2) = 0xFFFFFFFF; /// WTF??? an unsigned being set to a -1
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)account_name);
+ i = search_account_index(account_name);
+ if (i != -1) {
+ memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24);
+ memcpy(auth_dat[i].pass, RFIFOP(fd,26), 24);
+ auth_dat[i].pass[23] = '\0';
+ remove_control_chars((unsigned char *)auth_dat[i].pass);
+ WFIFOL(fd,2) = auth_dat[i].account_id;
+ login_log("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].pass, ip);
+ mmo_auth_sync();
+ } else {
+ memcpy(WFIFOP(fd,6), account_name, 24);
+ login_log("'ladmin': Attempt to modify the password of an unknown account (account: %s, ip: %s)" RETCODE,
+ account_name, ip);
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,50);
+ break;
+
+ case 0x7936: // Request to modify a state
+ if (RFIFOREST(fd) < 50)
+ return 0;
+ {
+ char error_message[20];
+ int statut;
+ WFIFOW(fd,0) = 0x7937;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)account_name);
+ statut = RFIFOL(fd,26);
+ memcpy(error_message, RFIFOP(fd,30), 20);
+ error_message[19] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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)" RETCODE,
+ auth_dat[i].userid, statut, error_message, ip);
+ else
+ login_log("'ladmin': Modification of a state (account: %s, new state: %d, ip: %s)" RETCODE,
+ 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);
+ mmo_auth_sync();
+ }
+ } 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)" RETCODE,
+ 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)" RETCODE, 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].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 0;
+ WFIFOW(fd,0) = 0x793b;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)account_name);
+ i = search_account_index(account_name);
+ if (i != -1) {
+ char pass[25];
+ memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24);
+ memcpy(pass, RFIFOP(fd,26), 24);
+ pass[24] = '\0';
+ remove_control_chars((unsigned char *)pass);
+ if (strcmp(auth_dat[i].pass, pass) == 0) {
+ WFIFOL(fd,2) = auth_dat[i].account_id;
+ login_log("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].pass, ip);
+ } else {
+ login_log("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)" RETCODE,
+ 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)" RETCODE,
+ account_name, ip);
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,50);
+ break;
+
+ case 0x793c: // Request to modify sex
+ if (RFIFOREST(fd) < 27)
+ return 0;
+ WFIFOW(fd,0) = 0x793d;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ account_name, sex, ip);
+ else
+ login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: 'control char', ip: %s)" RETCODE,
+ 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)" RETCODE,
+ auth_dat[i].userid, sex, ip);
+ mmo_auth_sync();
+ // 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)" RETCODE,
+ 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)" RETCODE,
+ account_name, sex, ip);
+ }
+ }
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,27);
+ break;
+
+ case 0x793e: // Request to modify GM level
+ if (RFIFOREST(fd) < 27)
+ return 0;
+ WFIFOW(fd,0) = 0x793f;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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];
+ time_t raw_time;
+ if ((fp2 = lock_fopen(GM_account_filename, &lock)) != NULL) {
+ if ((fp = fopen(GM_account_filename, "r")) != NULL) {
+ time(&raw_time);
+ strftime(tmpstr, 23, date_format, localtime(&raw_time));
+ 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" RETCODE, line);
+ else {
+ if (sscanf(line, "%d %d", &GM_account, &GM_level) != 2 && sscanf(line, "%d: %d", &GM_account, &GM_level) != 2)
+ fprintf(fp2, "%s" RETCODE, line);
+ else if (GM_account != acc)
+ fprintf(fp2, "%s" RETCODE, line);
+ else if (new_gm_level < 1) {
+ fprintf(fp2, "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)" RETCODE "//%d %d" RETCODE, 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)" RETCODE "%d %d" RETCODE, 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)" RETCODE "%d %d" RETCODE, 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)" RETCODE,
+ auth_dat[i].userid, acc, (int)new_gm_level, ip);
+ }
+ if (lock_fclose(fp2, GM_account_filename, &lock) == 0) {
+ WFIFOL(fd,2) = acc;
+ login_log("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)" RETCODE,
+ 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)" RETCODE,
+ auth_dat[i].userid, acc, (int)new_gm_level, ip);
+ }
+ } 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)" RETCODE,
+ 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)" RETCODE,
+ 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)" RETCODE,
+ 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 0;
+ WFIFOW(fd,0) = 0x7941;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ account_name, ip);
+ } else {
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ auth_dat[i].userid, email, ip);
+ mmo_auth_sync();
+ } else {
+ login_log("'ladmin': Attempt to modify the e-mail of an unknown account (account: %s, received e-mail: %s, ip: %s)" RETCODE,
+ account_name, email, ip);
+ }
+ }
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,66);
+ break;
+
+ case 0x7942: // Request to modify memo field
+ if ((int)RFIFOREST(fd) < 28 || (int)RFIFOREST(fd) < (28 + RFIFOW(fd,26)))
+ return 0;
+ WFIFOW(fd,0) = 0x7943;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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((unsigned char *)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)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].memo, ip);
+ mmo_auth_sync();
+ } 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)" RETCODE,
+ 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 0;
+ WFIFOW(fd,0) = 0x7945;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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)" RETCODE,
+ account_name, ip);
+ }
+ WFIFOSET(fd,30);
+ RFIFOSKIP(fd,26);
+ break;
+
+ case 0x7946: // Request to found an account name
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ 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((char*)WFIFOP(fd,6), auth_dat[i].userid, 24);
+ login_log("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)" RETCODE,
+ 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)" RETCODE,
+ RFIFOL(fd,2), ip);
+ strncpy((char*)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 0;
+ {
+ time_t timestamp;
+ char tmpstr[2048];
+ WFIFOW(fd,0) = 0x7949;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)account_name);
+ timestamp = (time_t)RFIFOL(fd,26);
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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)" RETCODE,
+ 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;
+ mmo_auth_sync();
+ } 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)" RETCODE,
+ account_name, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip);
+ }
+ WFIFOL(fd,30) = (unsigned int)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 0;
+ {
+ time_t timestamp;
+ char tmpstr[2048];
+ WFIFOW(fd,0) = 0x794b;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)account_name);
+ timestamp = (time_t)RFIFOL(fd,26);
+ if (timestamp <= time(NULL))
+ timestamp = 0;
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ 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)" RETCODE,
+ 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) = (unsigned int)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;
+ mmo_auth_sync();
+ }
+ } 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)" RETCODE,
+ account_name, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip);
+ }
+ WFIFOL(fd,30) = (unsigned int)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 0;
+ {
+ time_t timestamp;
+ struct tm *tmtime;
+ char tmpstr[2048];
+ WFIFOW(fd,0) = 0x794d;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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 = localtime(&timestamp);
+ 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, localtime(&timestamp));
+ 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)" RETCODE,
+ 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) = (unsigned int)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;
+ mmo_auth_sync();
+ }
+ } else {
+ strftime(tmpstr, 24, date_format, localtime(&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)" RETCODE,
+ 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)" RETCODE,
+ 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 0;
+ WFIFOW(fd,0) = 0x794f;
+ WFIFOW(fd,2) = 0xFFFF; // WTF???
+ if (RFIFOL(fd,4) < 1) {
+ login_log("'ladmin': Receiving a message for broadcast, but message is void (ip: %s)" RETCODE,
+ 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)" RETCODE,
+ ip);
+ } else {
+ unsigned 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((unsigned char *)message);
+ if (RFIFOW(fd,2) == 0)
+ login_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)" RETCODE,
+ message, ip);
+ else
+ login_log("'ladmin': Receiving a message for broadcast (message (in blue): %s, ip: %s)" RETCODE,
+ 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 0;
+ {
+ time_t timestamp;
+ struct tm *tmtime;
+ char tmpstr[2048];
+ char tmpstr2[2048];
+ WFIFOW(fd,0) = 0x7951;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ auth_dat[i].userid, ip);
+ WFIFOL(fd,30) = 0;
+ } else {
+ if (timestamp == 0 || timestamp < time(NULL))
+ timestamp = time(NULL);
+ tmtime = localtime(&timestamp);
+ 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, localtime(&auth_dat[i].connect_until_time));
+ strftime(tmpstr2, 24, date_format, localtime(&timestamp));
+ 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)" RETCODE,
+ 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;
+ mmo_auth_sync();
+ WFIFOL(fd,30) = (unsigned long)auth_dat[i].connect_until_time;
+ } else {
+ strftime(tmpstr, 24, date_format, localtime(&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)" RETCODE,
+ 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)" RETCODE,
+ 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 0;
+ WFIFOW(fd,0) = 0x7953;
+ WFIFOL(fd,2) = 0xFFFFFFFF; // WTF???
+ account_name = (char*)RFIFOP(fd,2);
+ account_name[23] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE,
+ 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)" RETCODE,
+ 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 0;
+ 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)" RETCODE,
+ 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)" RETCODE,
+ RFIFOL(fd,2), ip);
+ strncpy((char*)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)." RETCODE, ip);
+ read_gm_account();
+ // send GM accounts to all char-servers
+ send_GM_accounts();
+ RFIFOSKIP(fd,2);
+ break;
+
+ default:
+ {
+ FILE *logfp;
+ char tmpstr[24];
+ time_t raw_time;
+ logfp = fopen(login_log_unknown_packets_filename, "a");
+ if (logfp) {
+ time(&raw_time);
+ strftime(tmpstr, 23, date_format, localtime(&raw_time));
+ fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr);
+ fprintf(logfp, "parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd));
+ fprintf(logfp, "Detail (in hex):" RETCODE);
+ fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE);
+ 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" RETCODE, 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" RETCODE, tmpstr);
+ }
+ fprintf(logfp, RETCODE);
+ fclose(logfp);
+ }
+ }
+ login_log("'ladmin': End of connection, unknown packet (ip: %s)" RETCODE, ip);
+ session[fd]->eof = 1;
+ ShowWarning("Remote administration has been disconnected (unknown packet).\n");
+ return 0;
+ }
+ //WFIFOW(fd,0) = 0x791f;
+ //WFIFOSET(fd,2);
+ }
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ return 0;
+}
+
+//--------------------------------------------
+// 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;
+ }
+ }
+ ShowMessage("LAN test (result): "CL_CYAN"%s source"CL_RESET".\n", (lancheck) ? "LAN" : "WAN");
+ return lancheck;
+}
+
+//----------------------------------------------------------------------------------------
+// Default packet parsing (normal players or administation/char-server connexion requests)
+//----------------------------------------------------------------------------------------
+int parse_login(int fd) {
+ struct mmo_account account;
+ int result, j;
+ unsigned int i;
+ unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ char ip[16];
+ RFIFOHEAD(fd);
+
+ sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+
+ memset(&account, 0, sizeof(account));
+
+ if (session[fd]->eof) {
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2) {
+ if (display_parse_login == 1) {
+ if (RFIFOW(fd,0) == 0x64 || RFIFOW(fd,0) == 0x01dd) {
+ if ((int)RFIFOREST(fd) >= ((RFIFOW(fd,0) == 0x64) ? 55 : 47))
+ ShowDebug("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)
+ ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,60));
+ } else
+ ShowDebug("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 0;
+ 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 0;
+ RFIFOSKIP(fd,18);
+ break;
+
+ case 0x64: // Ask connection of a client
+ case 0x01dd: // Ask connection of a client (encryption mode)
+ if ((int)RFIFOREST(fd) < ((RFIFOW(fd,0) == 0x64) ? 55 : 47))
+ return 0;
+
+ account.version = RFIFOL(fd, 2); //for exe version check [Sirius]
+ account.userid = (char*)RFIFOP(fd,6);
+ account.userid[23] = '\0';
+ remove_control_chars((unsigned char *)account.userid);
+ if (RFIFOW(fd,0) == 0x64) {
+ login_log("Request for connection (non encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip);
+ memcpy(account.passwd, RFIFOP(fd,30), NAME_LENGTH);
+ account.passwd[23] = '\0';
+ remove_control_chars((unsigned char *)account.passwd);
+ } else {
+ login_log("Request for connection (encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip);
+ // If remove control characters from received password encrypted by md5,
+ // there would be a wrong result and failed to authentication. [End_of_exam]
+ memcpy(account.passwd, RFIFOP(fd,30), 16);
+ account.passwd[16] = '\0';
+ }
+#ifdef PASSWORDENC
+ account.passwdenc = (RFIFOW(fd,0) == 0x64) ? 0 : PASSWORDENC;
+#else
+ account.passwdenc = 0;
+#endif
+
+ if (!check_ip(session[fd]->client_addr.sin_addr.s_addr)) {
+ login_log("Connection refused: IP isn't authorised (deny/allow, ip: %s)." RETCODE, ip);
+ WFIFOHEAD(fd, 23);
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 3; // 3 = Rejected from Server
+ WFIFOSET(fd,23);
+ 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)." RETCODE,
+ min_level_to_connect, account.userid, gm_level, ip);
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ } else {
+ if (gm_level)
+ ShowInfo("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid);
+ else
+ ShowInfo("Connection of the account '%s' accepted.\n", account.userid);
+ server_num = 0;
+ WFIFOHEAD(fd, 47+32*MAX_SERVERS);
+ 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].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)." RETCODE,
+ account.userid, ip);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ }
+ }
+ } else {
+ WFIFOHEAD(fd, 23);
+ 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, localtime(&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) {
+ ShowWarning("login: abnormal request of MD5 key (already opened session).\n");
+ session[fd]->eof = 1;
+ return 0;
+ }
+ ld = (struct login_session_data*)aCalloc(1, sizeof(struct login_session_data));
+ session[fd]->session_data = ld;
+ if (!ld) {
+ ShowFatalError("login: Request for md5 key: memory allocation failure (malloc)!\n");
+ session[fd]->eof = 1;
+ return 0;
+ }
+ if (RFIFOW(fd,0) == 0x01db)
+ login_log("Sending request of the coding key (ip: %s)" RETCODE, ip);
+ else
+ login_log("'ladmin': Sending request of the coding key (ip: %s)" RETCODE, 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);
+ WFIFOHEAD(fd, 4 + ld->md5keylen);
+ 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 0;
+ {
+ int GM_value, len;
+ char* server_name;
+ account.userid = (char*)RFIFOP(fd,2);
+ account.userid[23] = '\0';
+ remove_control_chars((unsigned char *)account.userid);
+ memcpy(account.passwd, RFIFOP(fd,26), 24);
+ account.passwd[23] = '\0';
+ remove_control_chars((unsigned char *)account.passwd);
+ account.passwdenc = 0;
+ server_name = (char*)RFIFOP(fd,60);
+ server_name[19] = '\0';
+ remove_control_chars((unsigned char *)server_name);
+ login_log("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)" RETCODE,
+ 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 && 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)" RETCODE,
+ server_name, account.userid, account.passwd, ip);
+ ShowStatus("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].new_ = RFIFOW(fd,84);
+ server_fd[account.account_id] = fd;
+ WFIFOHEAD(fd, 3);
+ 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 {
+ if (server_fd[account.account_id] != -1) {
+ ShowNotice("Connection of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)\n",
+ server_name, account.account_id, account.userid, account.passwd, ip);
+ login_log("Connexion of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)" RETCODE,
+ server_name, account.account_id, account.userid, account.passwd, ip);
+ } else {
+ ShowNotice("Connection of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s).\n", server_name, account.userid, account.passwd, ip);
+ login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE,
+ server_name, account.userid, account.passwd, ip);
+ }
+ WFIFOHEAD(fd, 3);
+ WFIFOW(fd,0) = 0x2711;
+ WFIFOB(fd,2) = 3;
+ WFIFOSET(fd,3);
+ }
+ }
+ RFIFOSKIP(fd,86);
+ return 0;
+
+ case 0x7530: // Request of the server version
+ login_log("Sending of the server version (ip: %s)" RETCODE, ip);
+ WFIFOHEAD(fd, 10);
+ WFIFOW(fd,0) = 0x7531;
+ WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
+ WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
+ WFIFOB(fd,4) = ATHENA_REVISION;
+ WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
+ WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
+ WFIFOB(fd,7) = ATHENA_SERVER_LOGIN;
+ WFIFOW(fd,8) = ATHENA_MOD_VERSION;
+ WFIFOSET(fd,10);
+ RFIFOSKIP(fd,2);
+ break;
+
+ case 0x7532: // Request to end connection
+ login_log("End of connection (ip: %s)" RETCODE, ip);
+ session[fd]->eof = 1;
+ return 0;
+
+ case 0x7918: // Request for administation login
+ if ((int)RFIFOREST(fd) < 4 || (int)RFIFOREST(fd) < ((RFIFOW(fd,2) == 0) ? 28 : 20))
+ return 0;
+ 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)." RETCODE, ip);
+ } else {
+ struct login_session_data *ld = (struct login_session_data*)session[fd]->session_data;
+ if (RFIFOW(fd,2) == 0) { // non encrypted password
+ char* password="";
+ memcpy(password, RFIFOP(fd,4), 24);
+ password[24] = '\0';
+ remove_control_chars((unsigned char *)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)" RETCODE, password, ip);
+ ShowNotice("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)" RETCODE, password, ip);
+ else
+ login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)" RETCODE, password, ip);
+ } else { // encrypted password
+ if (!ld)
+ ShowError("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n");
+ else {
+ char md5str[64] = "", md5bin[32];
+ if (RFIFOW(fd,2) == 1) {
+ sprintf(md5str, "%s%s", ld->md5key, admin_pass); // 20 24
+ } else if (RFIFOW(fd,2) == 2) {
+ sprintf(md5str, "%s%s", admin_pass, ld->md5key); // 24 20
+ }
+ MD5_String2binary(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)" RETCODE, ip);
+ ShowNotice("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)" RETCODE, ip);
+ else
+ login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)" RETCODE, ip);
+ }
+ }
+ }
+ WFIFOSET(fd,3);
+ RFIFOSKIP(fd, (RFIFOW(fd,2) == 0) ? 28 : 20);
+ break;
+
+ default:
+ if (save_unknown_packets) {
+ FILE *logfp;
+ char tmpstr[24];
+ time_t raw_time;
+ logfp = fopen(login_log_unknown_packets_filename, "a");
+ if (logfp) {
+ time(&raw_time);
+ strftime(tmpstr, 23, date_format, localtime(&raw_time));
+ fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr);
+ fprintf(logfp, "parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd));
+ fprintf(logfp, "Detail (in hex):" RETCODE);
+ fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE);
+ 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" RETCODE, 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" RETCODE, tmpstr);
+ }
+ fprintf(logfp, RETCODE);
+ fclose(logfp);
+ }
+ }
+ login_log("End of connection, unknown packet (ip: %s)" RETCODE, ip);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ return 0;
+}
+
+//-----------------------
+// Console Command Parser [Wizputer]
+//-----------------------
+int parse_console(char *buf) {
+ char command[256];
+
+ memset(command,0,sizeof(command));
+
+ sscanf(buf, "%[^\n]", command);
+
+ login_log("Console command :%s" RETCODE, command);
+
+ if(strcmpi("shutdown", command) == 0 ||
+ strcmpi("exit", command) == 0 ||
+ strcmpi("quit", command) == 0 ||
+ strcmpi("end", command) == 0)
+ runflag = 0;
+ else if(strcmpi("alive", command) == 0 ||
+ strcmpi("status", command) == 0)
+ ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
+ else if(strcmpi("help", command) == 0) {
+ printf(CL_BOLD"Help of commands:"CL_RESET"\n");
+ printf(" To shutdown the server:\n");
+ printf(" 'shutdown|exit|qui|end'\n");
+ printf(" To know if server is alive:\n");
+ printf(" 'alive|status'\n");
+ }
+
+ return 0;
+}
+
+static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
+{
+ struct online_login_data *character= (struct online_login_data*)data;
+ if (character->char_server == -2) //Unknown server.. set them offline
+ remove_online_user(character->account_id);
+ else if (character->char_server < 0)
+ //Free data from players that have not been online for a while.
+ db_remove(online_db, key);
+ return 0;
+}
+
+static int online_data_cleanup(int tid, unsigned int tick, int id, int data)
+{
+ online_db->foreach(online_db, online_data_cleanup_sub);
+ return 0;
+}
+//-------------------------------------------------
+// Return numerical value of a switch configuration
+// on/off, english, français, deutsch, español
+//-------------------------------------------------
+int config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+
+ return atoi(str);
+}
+
+//----------------------------------
+// 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) {
+ ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
+ return 1;
+ }
+
+ ShowInfo("Reading the configuration file %s...\n", lancfgName);
+
+ while(fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ line[sizeof(line)-1] = '\0';
+ memset(w2, 0, sizeof(w2));
+ if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2)
+ continue;
+
+ remove_control_chars((unsigned char *)w1);
+ remove_control_chars((unsigned char *)w2);
+ if (strcmpi(w1, "lan_char_ip") == 0) { // Read Char-Server Lan IP Address
+ memset(lan_char_ip, 0, sizeof(lan_char_ip));
+ 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';
+ }
+ ShowStatus("LAN IP of char-server: %s.\n", lan_char_ip);
+ } else if (strcmpi(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]);
+ }
+ ShowStatus("Sub-network of the char-server: %d.%d.%d.%d.\n", subneti[0], subneti[1], subneti[2], subneti[3]);
+ } else if (strcmpi(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]);
+ }
+ ShowStatus("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:" RETCODE);
+ login_log("- with LAN IP of char-server: %s." RETCODE, lan_char_ip);
+ login_log("- with the sub-network of the char-server: %d.%d.%d.%d/%d.%d.%d.%d." RETCODE,
+ 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;
+ ShowInfo("LAN test of LAN IP of the char-server: ");
+ if (lan_ip_check(p) == 0) {
+ ShowError(CL_RED" LAN IP of the char-server doesn't belong to the specified Sub-network"CL_RESET"\n");
+ login_log("***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network." RETCODE);
+ }
+ }
+
+ ShowInfo("Finished reading %s.\n", lancfgName);
+
+ return 0;
+}
+
+//-----------------------------------
+// Reading general configuration file
+//-----------------------------------
+int login_config_read(const char *cfgName) {
+ struct hostent *h = NULL;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("Configuration file (%s) not found.\n", cfgName);
+ return 1;
+ }
+
+ ShowInfo("Reading configuration file %s...\n", cfgName);
+ while(fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ line[sizeof(line)-1] = '\0';
+ memset(w2, 0, sizeof(w2));
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ remove_control_chars((unsigned char *)w1);
+ remove_control_chars((unsigned char *)w2);
+
+ if(strcmpi(w1,"timestamp_format") == 0) {
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "admin_state") == 0) {
+ admin_state = config_switch(w2);
+ } else if (strcmpi(w1, "admin_pass") == 0) {
+ memset(admin_pass, 0, sizeof(admin_pass));
+ strncpy(admin_pass, w2, sizeof(admin_pass));
+ admin_pass[sizeof(admin_pass)-1] = '\0';
+ } else if (strcmpi(w1, "ladminallowip") == 0) {
+ if (strcmpi(w2, "clear") == 0) {
+ if (access_ladmin_allow)
+ aFree(access_ladmin_allow);
+ access_ladmin_allow = NULL;
+ access_ladmin_allownum = 0;
+ } else {
+ if (strcmpi(w2, "all") == 0) {
+ // reset all previous values
+ if (access_ladmin_allow)
+ aFree(access_ladmin_allow);
+ // set to all
+ access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ access_ladmin_allownum = 1;
+ access_ladmin_allow[0] = '\0';
+ } else if (w2[0] && !(access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { // don't add IP if already 'all'
+ if (access_ladmin_allow)
+ access_ladmin_allow = (char*)aRealloc(access_ladmin_allow, (access_ladmin_allownum+1) * ACO_STRSIZE);
+ else
+ access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ strncpy(access_ladmin_allow + (access_ladmin_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE);
+ access_ladmin_allow[access_ladmin_allownum * ACO_STRSIZE - 1] = '\0';
+ }
+ }
+ } else if (strcmpi(w1, "gm_pass") == 0) {
+ memset(gm_pass, 0, sizeof(gm_pass));
+ strncpy(gm_pass, w2, sizeof(gm_pass));
+ gm_pass[sizeof(gm_pass)-1] = '\0';
+ } else if (strcmpi(w1, "level_new_gm") == 0) {
+ level_new_gm = atoi(w2);
+ } else if (strcmpi(w1, "new_account") == 0) {
+ new_account_flag = config_switch(w2);
+ } else if (strcmpi(w1, "bind_ip") == 0) {
+ bind_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowStatus("Login server binding 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(bind_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(bind_ip_str,w2,16);
+ } else if (strcmpi(w1, "login_port") == 0) {
+ login_port = atoi(w2);
+ } else if (strcmpi(w1, "account_filename") == 0) {
+ memset(account_filename, 0, sizeof(account_filename));
+ strncpy(account_filename, w2, sizeof(account_filename));
+ account_filename[sizeof(account_filename)-1] = '\0';
+ } else if (strcmpi(w1, "gm_account_filename") == 0) {
+ memset(GM_account_filename, 0, sizeof(GM_account_filename));
+ strncpy(GM_account_filename, w2, sizeof(GM_account_filename));
+ GM_account_filename[sizeof(GM_account_filename)-1] = '\0';
+ } else if (strcmpi(w1, "gm_account_filename_check_timer") == 0) {
+ gm_account_filename_check_timer = atoi(w2);
+ } else if (strcmpi(w1, "use_MD5_passwords") == 0) {
+ use_md5_passwds = config_switch(w2);
+ } else if (strcmpi(w1, "login_log_filename") == 0) {
+ memset(login_log_filename, 0, sizeof(login_log_filename));
+ strncpy(login_log_filename, w2, sizeof(login_log_filename));
+ login_log_filename[sizeof(login_log_filename)-1] = '\0';
+ } else if (strcmpi(w1, "log_login") == 0) {
+ log_login = atoi(w2);
+ } else if (strcmpi(w1, "login_log_unknown_packets_filename") == 0) {
+ memset(login_log_unknown_packets_filename, 0, sizeof(login_log_unknown_packets_filename));
+ 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 (strcmpi(w1, "save_unknown_packets") == 0) {
+ save_unknown_packets = config_switch(w2);
+ } else if (strcmpi(w1, "display_parse_login") == 0) {
+ display_parse_login = config_switch(w2); // 0: no, 1: yes
+ } else if (strcmpi(w1, "display_parse_admin") == 0) {
+ display_parse_admin = config_switch(w2); // 0: no, 1: yes
+ } else if (strcmpi(w1, "display_parse_fromchar") == 0) {
+ display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets
+ } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date!
+ memset(date_format, 0, sizeof(date_format));
+ 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 (strcmpi(w1, "min_level_to_connect") == 0) {
+ min_level_to_connect = atoi(w2);
+ } else if (strcmpi(w1, "add_to_unlimited_account") == 0) {
+ add_to_unlimited_account = config_switch(w2);
+ } else if (strcmpi(w1, "start_limited_time") == 0) {
+ start_limited_time = atoi(w2);
+ } else if (strcmpi(w1, "check_ip_flag") == 0) {
+ check_ip_flag = config_switch(w2);
+ } else if (strcmpi(w1, "order") == 0) {
+ access_order = atoi(w2);
+ if (strcmpi(w2, "deny,allow") == 0 ||
+ strcmpi(w2, "deny, allow") == 0) access_order = ACO_DENY_ALLOW;
+ if (strcmpi(w2, "allow,deny") == 0 ||
+ strcmpi(w2, "allow, deny") == 0) access_order = ACO_ALLOW_DENY;
+ if (strcmpi(w2, "mutual-failture") == 0 ||
+ strcmpi(w2, "mutual-failure") == 0) access_order = ACO_MUTUAL_FAILTURE;
+ } else if (strcmpi(w1, "allow") == 0) {
+ if (strcmpi(w2, "clear") == 0) {
+ if (access_allow)
+ aFree(access_allow);
+ access_allow = NULL;
+ access_allownum = 0;
+ } else {
+ if (strcmpi(w2, "all") == 0) {
+ // reset all previous values
+ if (access_allow)
+ aFree(access_allow);
+ // set to all
+ access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ access_allownum = 1;
+ access_allow[0] = '\0';
+ } else if (w2[0] && !(access_allownum == 1 && access_allow[0] == '\0')) { // don't add IP if already 'all'
+ if (access_allow)
+ access_allow = (char*)aRealloc(access_allow, (access_allownum+1) * ACO_STRSIZE);
+ else
+ access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ strncpy(access_allow + (access_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE);
+ access_allow[access_allownum * ACO_STRSIZE - 1] = '\0';
+ }
+ }
+ } else if (strcmpi(w1, "deny") == 0) {
+ if (strcmpi(w2, "clear") == 0) {
+ if (access_deny)
+ aFree(access_deny);
+ access_deny = NULL;
+ access_denynum = 0;
+ } else {
+ if (strcmpi(w2, "all") == 0) {
+ // reset all previous values
+ if (access_deny)
+ aFree(access_deny);
+ // set to all
+ access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ access_denynum = 1;
+ access_deny[0] = '\0';
+ } else if (w2[0] && !(access_denynum == 1 && access_deny[0] == '\0')) { // don't add IP if already 'all'
+ if (access_deny)
+ access_deny = (char*)aRealloc(access_deny, (access_denynum+1) * ACO_STRSIZE);
+ else
+ access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char));
+ strncpy(access_deny + (access_denynum++) * ACO_STRSIZE, w2, ACO_STRSIZE);
+ access_deny[access_denynum * ACO_STRSIZE - 1] = '\0';
+ }
+ }
+ // dynamic password error ban
+ } else if (strcmpi(w1, "dynamic_pass_failure_ban") == 0) {
+ dynamic_pass_failure_ban = config_switch(w2);
+ } else if (strcmpi(w1, "dynamic_pass_failure_ban_time") == 0) {
+ dynamic_pass_failure_ban_time = atoi(w2);
+ } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_many") == 0) {
+ dynamic_pass_failure_ban_how_many = atoi(w2);
+ } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_long") == 0) {
+ dynamic_pass_failure_ban_how_long = atoi(w2);
+ } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){
+ check_client_version = 1;
+ }
+ if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){
+ check_client_version = 0;
+ }
+ }else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check
+ client_version_to_connect = atoi(w2); //Added by Sirius for client version check
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin]
+ allowed_regs = atoi(w2);
+ } else if (strcmpi(w1, "time_allowed") == 0) {
+ time_allowed = atoi(w2);
+ } else if (strcmpi(w1, "online_check") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ online_check = 1;
+ else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 )
+ online_check = 0;
+ else
+ online_check = atoi(w2);
+ } else if (strcmpi(w1, "import") == 0) {
+ login_config_read(w2);
+ }
+ }
+ }
+ fclose(fp);
+
+ ShowInfo("Finished reading %s.\n", cfgName);
+
+ return 0;
+}
+
+//-------------------------------------
+// Displaying of configuration warnings
+//-------------------------------------
+void display_conf_warnings(void) {
+ if (admin_state != 0 && admin_state != 1) {
+ ShowWarning("Invalid value for admin_state parameter -> setting to 0 (no remote admin).\n");
+ admin_state = 0;
+ }
+
+ if (admin_state == 1) {
+ if (admin_pass[0] == '\0') {
+ ShowWarning("Administrator password is void (admin_pass).\n");
+ } else if (strcmp(admin_pass, "admin") == 0) {
+ ShowWarning("You are using the default administrator password (admin_pass).\n");
+ ShowWarning(" We highly recommend that you change it.\n");
+ }
+ }
+
+ if (gm_pass[0] == '\0') {
+ ShowWarning("'To GM become' password is void (gm_pass).\n");
+ ShowWarning(" We highly recommend that you set one password.\n");
+ } else if (strcmp(gm_pass, "gm") == 0) {
+ ShowWarning("You are using the default GM password (gm_pass).\n");
+ ShowWarning(" We highly recommend that you change it.\n");
+ }
+
+ if (level_new_gm < 0 || level_new_gm > 99) {
+ ShowWarning("Invalid value for level_new_gm parameter -> setting to 60 (default).\n");
+ level_new_gm = 60;
+ }
+
+ if (new_account_flag != 0 && new_account_flag != 1) {
+ ShowWarning("Invalid value for new_account parameter -> setting to 0 (no new account).\n");
+ new_account_flag = 0;
+ }
+
+ if (login_port < 1024 || login_port > 65535) {
+ ShowWarning("Invalid value for login_port parameter -> setting to 6900 (default).\n");
+ login_port = 6900;
+ }
+
+ if (gm_account_filename_check_timer < 0) {
+ ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 15 sec (default).\n");
+ gm_account_filename_check_timer = 15;
+ } else if (gm_account_filename_check_timer == 1) {
+ ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 2 sec (minimum value).\n");
+ gm_account_filename_check_timer = 2;
+ }
+
+ if (save_unknown_packets != 0 && save_unknown_packets != 1) {
+ ShowWarning("Invalid value for save_unknown_packets parameter -> setting to 0-no save.\n");
+ save_unknown_packets = 0;
+ }
+
+ if (display_parse_login != 0 && display_parse_login != 1) { // 0: no, 1: yes
+ ShowWarning("Invalid value for display_parse_login parameter -> setting to 0 (no display).\n");
+ display_parse_login = 0;
+ }
+
+ if (display_parse_admin != 0 && display_parse_admin != 1) { // 0: no, 1: yes
+ ShowWarning("Invalid value for display_parse_admin parameter -> setting 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
+ ShowWarning("Invalid value for display_parse_fromchar parameter -> setting 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
+ ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting 0 (any player).\n", min_level_to_connect);
+ min_level_to_connect = 0;
+ } else if (min_level_to_connect > 99) { // 0: all players, 1-99 at least gm level x
+ ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting to 99 (only GM level 99)\n", min_level_to_connect);
+ min_level_to_connect = 99;
+ }
+
+ if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) { // 0: no, 1: yes
+ ShowWarning("Invalid value for add_to_unlimited_account parameter\n");
+ ShowWarning(" -> setting 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
+ ShowWarning("Invalid value for start_limited_time parameter\n");
+ ShowWarning(" -> setting 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
+ ShowWarning("Invalid value for check_ip_flag parameter\n");
+ ShowWarning(" -> setting 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') {
+ ShowWarning("The IP security order is 'deny,allow' (allow if not deny) and you refuse ALL IP.\n");
+ }
+ } else if (access_order == ACO_ALLOW_DENY) {
+ if (access_allownum == 0) {
+ ShowWarning("The IP security order is 'allow,deny' (deny if not allow) but, NO IP IS AUTHORISED!\n");
+ }
+ } else { // ACO_MUTUAL_FAILTURE
+ if (access_allownum == 0) {
+ ShowWarning("The IP security order is 'mutual-failture'\n");
+ ShowWarning(" (allow if in the allow list and not in the deny list).\n");
+ ShowWarning(" But, NO IP IS AUTHORISED!\n");
+ } else if (access_denynum == 1 && access_deny[0] == '\0') {
+ ShowWarning("The IP security order is mutual-failture\n");
+ ShowWarning(" (allow if in the allow list and not in the deny list).\n");
+ ShowWarning(" But, you refuse ALL IP!\n");
+ }
+ }
+
+ if (dynamic_pass_failure_ban != 0) {
+ if (dynamic_pass_failure_ban_time < 1) {
+ ShowWarning("Invalid value for dynamic_pass_failure_ban_time (%d) parameter\n", dynamic_pass_failure_ban_time);
+ ShowWarning(" -> setting to 5 (5 minutes to look number of invalid passwords.\n");
+ dynamic_pass_failure_ban_time = 5;
+ }
+ if (dynamic_pass_failure_ban_how_many < 1) {
+ ShowWarning("Invalid value for dynamic_pass_failure_ban_how_many (%d) parameter\n", dynamic_pass_failure_ban_how_many);
+ ShowWarning(" -> setting to 3 (3 invalid passwords before to temporarily ban.\n");
+ dynamic_pass_failure_ban_how_many = 3;
+ }
+ if (dynamic_pass_failure_ban_how_long < 1) {
+ ShowWarning("Invalid value for dynamic_pass_failure_ban_how_long (%d) parameter\n", dynamic_pass_failure_ban_how_long);
+ ShowWarning(" -> setting to 1 (1 minute of temporarily ban.\n");
+ dynamic_pass_failure_ban_how_long = 1;
+ }
+ }
+
+ 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..." RETCODE);
+
+ // save configuration in log file
+ login_log("The configuration of the server is set:" RETCODE);
+
+ if (admin_state != 1)
+ login_log("- with no remote administration." RETCODE);
+ else if (admin_pass[0] == '\0')
+ login_log("- with a remote administration with a VOID password." RETCODE);
+ else if (strcmp(admin_pass, "admin") == 0)
+ login_log("- with a remote administration with the DEFAULT password." RETCODE);
+ else
+ login_log("- with a remote administration with the password of %d character(s)." RETCODE, 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" RETCODE);
+ } else {
+ login_log("- to accept following IP for remote administration:" RETCODE);
+ for(i = 0; i < access_ladmin_allownum; i++)
+ login_log(" %s" RETCODE, (char *)(access_ladmin_allow + i * ACO_STRSIZE));
+ }
+
+ if (gm_pass[0] == '\0')
+ login_log("- with a VOID 'To GM become' password (gm_pass)." RETCODE);
+ else if (strcmp(gm_pass, "gm") == 0)
+ login_log("- with the DEFAULT 'To GM become' password (gm_pass)." RETCODE);
+ else
+ login_log("- with a 'To GM become' password (gm_pass) of %d character(s)." RETCODE, strlen(gm_pass));
+ if (level_new_gm == 0)
+ login_log("- to refuse any creation of GM with @gm." RETCODE);
+ else
+ login_log("- to create GM with level '%d' when @gm is used." RETCODE, level_new_gm);
+
+ if (new_account_flag == 1)
+ login_log("- to ALLOW new users (with _F/_M)." RETCODE);
+ else
+ login_log("- to NOT ALLOW new users (with _F/_M)." RETCODE);
+ login_log("- with port: %d." RETCODE, login_port);
+ login_log("- with the accounts file name: '%s'." RETCODE, account_filename);
+ login_log("- with the GM accounts file name: '%s'." RETCODE, GM_account_filename);
+ if (gm_account_filename_check_timer == 0)
+ login_log("- to NOT check GM accounts file modifications." RETCODE);
+ else
+ login_log("- to check GM accounts file modifications every %d seconds." RETCODE, gm_account_filename_check_timer);
+
+ if (use_md5_passwds == 0)
+ login_log("- to save password in plain text." RETCODE);
+ else
+ login_log("- to save password with MD5 encrypting." RETCODE);
+
+ // not necessary to log the 'login_log_filename', we are inside :)
+
+ login_log("- with the unknown packets file name: '%s'." RETCODE, login_log_unknown_packets_filename);
+ if (save_unknown_packets)
+ login_log("- to SAVE all unkown packets." RETCODE);
+ else
+ login_log("- to SAVE only unkown packets sending by a char-server or a remote administration." RETCODE);
+ if (display_parse_login)
+ login_log("- to display normal parse packets on console." RETCODE);
+ else
+ login_log("- to NOT display normal parse packets on console." RETCODE);
+ if (display_parse_admin)
+ login_log("- to display administration parse packets on console." RETCODE);
+ else
+ login_log("- to NOT display administration parse packets on console." RETCODE);
+ if (display_parse_fromchar)
+ login_log("- to display char-server parse packets on console." RETCODE);
+ else
+ login_log("- to NOT display char-server parse packets on console." RETCODE);
+
+ if (min_level_to_connect == 0) // 0: all players, 1-99 at least gm level x
+ login_log("- with no minimum level for connection." RETCODE);
+ else if (min_level_to_connect == 99)
+ login_log("- to accept only GM with level 99." RETCODE);
+ else
+ login_log("- to accept only GM with level %d or more." RETCODE, min_level_to_connect);
+
+ if (add_to_unlimited_account)
+ login_log("- to authorize adjustment (with timeadd ladmin) on an unlimited account." RETCODE);
+ else
+ login_log("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before." RETCODE);
+
+ if (start_limited_time < 0)
+ login_log("- to create new accounts with an unlimited time." RETCODE);
+ else if (start_limited_time == 0)
+ login_log("- to create new accounts with a limited time: time of creation." RETCODE);
+ else
+ login_log("- to create new accounts with a limited time: time of creation + %d second(s)." RETCODE, start_limited_time);
+
+ if (check_ip_flag)
+ login_log("- with control of players IP between login-server and char-server." RETCODE);
+ else
+ login_log("- to not check players IP between login-server and char-server." RETCODE);
+
+ 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." RETCODE);
+ } 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." RETCODE);
+ } else {
+ login_log("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:" RETCODE);
+ for(i = 0; i < access_denynum; i++)
+ login_log(" %s" RETCODE, (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!" RETCODE);
+ } 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." RETCODE);
+ } else {
+ login_log("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:" RETCODE);
+ for(i = 0; i < access_allownum; i++)
+ login_log(" %s" RETCODE, (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)." RETCODE);
+ if (access_allownum == 0) {
+ login_log(" But, NO IP IS AUTHORISED!" RETCODE);
+ } else if (access_denynum == 1 && access_deny[0] == '\0') {
+ login_log(" But, you refuse ALL IP!" RETCODE);
+ } else {
+ if (access_allownum == 1 && access_allow[0] == '\0') {
+ login_log(" You authorise ALL IP." RETCODE);
+ } else {
+ login_log(" Authorised IP are:" RETCODE);
+ for(i = 0; i < access_allownum; i++)
+ login_log(" %s" RETCODE, (char *)(access_allow + i * ACO_STRSIZE));
+ }
+ login_log(" Refused IP are:" RETCODE);
+ for(i = 0; i < access_denynum; i++)
+ login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE));
+ }
+
+ // dynamic password error ban
+ if (dynamic_pass_failure_ban == 0)
+ login_log("- with NO dynamic password error ban." RETCODE);
+ else {
+ login_log("- with a dynamic password error ban:" RETCODE);
+ login_log(" After %d invalid password in %d minutes" RETCODE, dynamic_pass_failure_ban_how_many, dynamic_pass_failure_ban_time);
+ login_log(" IP is banned for %d minutes" RETCODE, dynamic_pass_failure_ban_how_long);
+ }
+ }
+}
+
+//--------------------------------------
+// Function called at exit of the server
+//--------------------------------------
+void do_final(void) {
+ int i, fd;
+ ShowInfo("Terminating...\n");
+ fflush(stdout);
+ mmo_auth_sync();
+ online_db->destroy(online_db, NULL);
+
+ if(auth_dat) aFree(auth_dat);
+ if(gm_account_db) aFree(gm_account_db);
+ if(access_ladmin_allow) aFree(access_ladmin_allow);
+ if(access_allow) aFree(access_allow);
+ if(access_deny) aFree(access_deny);
+ for (i = 0; i < MAX_SERVERS; i++) {
+ if ((fd = server_fd[i]) >= 0) {
+ server_fd[i] = -1;
+ memset(&server[i], 0, sizeof(struct mmo_char_server));
+ do_close(fd);
+ }
+ }
+ do_close(login_fd);
+
+ login_log("----End of login-server (normal end with closing of all files)." RETCODE);
+
+ if(log_fp)
+ fclose(log_fp);
+ ShowStatus("Finished.\n");
+}
+
+//------------------------------
+// Main function of login-server
+//------------------------------
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_LOGIN;
+}
+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);
+
+ srand((unsigned int)time(NULL));
+
+ 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 = NULL;
+ GM_num = 0;
+ GM_max = 0;
+ mmo_auth_init();
+ read_gm_account();
+ set_defaultparse(parse_login);
+ // Online user database init
+ online_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); // reinitialise
+ add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer");
+
+ if (bind_ip_set_)
+ login_fd = make_listen_bind(inet_addr(bind_ip_str),login_port);
+ else
+ login_fd = make_listen_bind(INADDR_ANY,login_port);
+
+ add_timer_func_list(check_auth_sync, "check_auth_sync");
+ add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save)
+
+ // 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");
+ add_timer_interval(gettick() + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed
+
+
+ add_timer_func_list(online_data_cleanup, "online_data_cleanup");
+ add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000); // every 10 minutes cleanup online account db.
+ if(console) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port);
+ ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (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
new file mode 100644
index 000000000..119a91595
--- /dev/null
+++ b/src/login/login.h
@@ -0,0 +1,44 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#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"
+#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
+
+extern int login_port;
+struct mmo_account {
+ int version; //Added for version check [Sirius]
+ char* userid;
+ char passwd[33];
+ 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[21];
+ long ip;
+ short port;
+ int users;
+ int maintenance;
+ int new_;
+};
+
+extern struct mmo_char_server server[MAX_SERVERS];
+extern int server_fd[MAX_SERVERS];
+#endif
diff --git a/src/login/md5calc.c b/src/login/md5calc.c
new file mode 100644
index 000000000..5c52670c7
--- /dev/null
+++ b/src/login/md5calc.c
@@ -0,0 +1,236 @@
+/***********************************************************
+ * md5 calculation algorithm
+ *
+ * The source code referred to the following URL.
+ * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html
+ *
+ ***********************************************************/
+
+#include "md5calc.h"
+#include <string.h>
+#include <stdio.h>
+
+#ifndef UINT_MAX
+#define UINT_MAX 4294967295U
+#endif
+
+// Global variable
+static unsigned int *pX;
+
+// String Table
+static const unsigned int T[] = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60
+};
+
+// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC.
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+// The function used for other calculation
+static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Y) | (~X & Z);
+}
+static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Z) | (Y & ~Z);
+}
+static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return X ^ Y ^ Z;
+}
+static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return Y ^ (X | ~Z);
+}
+
+static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI,
+ unsigned int k, unsigned int s, unsigned int i)
+{
+ return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s);
+}
+
+static void Round1(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, F(b,c,d), k, s, i);
+}
+static void Round2(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, G(b,c,d), k, s, i);
+}
+static void Round3(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, H(b,c,d), k, s, i);
+}
+static void Round4(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, I(b,c,d), k, s, i);
+}
+
+static void MD5_Round_Calculate(const unsigned char *block,
+ unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2)
+{
+ //create X It is since it is required.
+ unsigned int X[16]; //512bit 64byte
+ int j,k;
+
+ //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D)
+ unsigned int A=*A2, B=*B2, C=*C2, D=*D2;
+ unsigned int AA = A,BB = B,CC = C,DD = D;
+
+ //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4
+ pX = X;
+
+ //Copy block(padding_message) i into X
+ for (j=0,k=0; j<64; j+=4,k++)
+ X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion
+ | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC
+ | ( ((unsigned int )block[j+2]) << 16 )
+ | ( ((unsigned int )block[j+3]) << 24 );
+
+
+ //Round 1
+ Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3);
+ Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7);
+ Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11);
+ Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15);
+
+ //Round 2
+ Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19);
+ Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23);
+ Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27);
+ Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31);
+
+ //Round 3
+ Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35);
+ Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39);
+ Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43);
+ Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47);
+
+ //Round 4
+ Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51);
+ Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55);
+ Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59);
+ Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63);
+
+ // Then perform the following additions. (let's add)
+ *A2 = A + AA;
+ *B2 = B + BB;
+ *C2 = C + CC;
+ *D2 = D + DD;
+
+ //The clearance of confidential information
+ memset(pX, 0, sizeof(X));
+}
+
+//-------------------------------------------------------------------
+// The function for the exteriors
+
+/** output is the coded binary in the character sequence which wants to code string. */
+void MD5_String2binary(const char * string, char * output)
+{
+//var
+ /*8bit*/
+ unsigned char padding_message[64]; //Extended message 512bit 64byte
+ unsigned char *pstring; //The position of string in the present scanning notes is held.
+
+// unsigned char digest[16];
+ /*32bit*/
+ unsigned int string_byte_len, //The byte chief of string is held.
+ string_bit_len, //The bit length of string is held.
+ copy_len, //The number of bytes which is used by 1-3 and which remained
+ msg_digest[4]; //Message digest 128bit 4byte
+ unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference)
+ *B = &msg_digest[1],
+ *C = &msg_digest[2],
+ *D = &msg_digest[3];
+ int i;
+
+//prog
+ //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head)
+ *A = 0x67452301;
+ *B = 0xefcdab89;
+ *C = 0x98badcfe;
+ *D = 0x10325476;
+
+ //Step 1.Append Padding Bits (extension of a mark bit)
+ //1-1
+ string_byte_len = strlen(string); //The byte chief of a character sequence is acquired.
+ pstring = (unsigned char *)string; //The position of the present character sequence is set.
+
+ //1-2 Repeat calculation until length becomes less than 64 bytes.
+ for (i=string_byte_len; 64<=i; i-=64,pstring+=64)
+ MD5_Round_Calculate(pstring, A,B,C,D);
+
+ //1-3
+ copy_len = string_byte_len % 64; //The number of bytes which remained is computed.
+ strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence.
+ memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length.
+ padding_message[copy_len] |= 0x80; //The next of a message is 1.
+
+ //1-4
+ //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes.
+ if (56 <= copy_len) {
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+ memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0.
+ }
+
+
+ //Step 2.Append Length (the information on length is added)
+ string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank)
+ memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set.
+
+ //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank.
+ if (UINT_MAX / 8 < string_byte_len) {
+ unsigned int high = (string_byte_len - UINT_MAX / 8) * 8;
+ memcpy(&padding_message[60], &high, 4);
+ } else
+ memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0.
+
+ //Step 4.Process Message in 16-Word Blocks (calculation of MD5)
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+
+
+ //Step 5.Output (output)
+ memcpy(output,msg_digest,16);
+// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC
+/* sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);*/
+}
+
+/** output is the coded character sequence in the character sequence which wants to code string. */
+void MD5_String(const char * string, char * output)
+{
+ unsigned char digest[16];
+
+ MD5_String2binary(string,(char*)digest);
+ sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+}
+
diff --git a/src/login/md5calc.h b/src/login/md5calc.h
new file mode 100644
index 000000000..04fb2d8c5
--- /dev/null
+++ b/src/login/md5calc.h
@@ -0,0 +1,7 @@
+#ifndef _MD5CALC_H_
+#define _MD5CALC_H_
+
+void MD5_String(const char * string, char * output);
+void MD5_String2binary(const char * string, char * output);
+
+#endif
diff --git a/src/login_sql/Makefile b/src/login_sql/Makefile
new file mode 100644
index 000000000..2a54f680b
--- /dev/null
+++ b/src/login_sql/Makefile
@@ -0,0 +1,22 @@
+all sql: login-server_sql
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/utils.o \
+ ../common/obj/strlib.o ../common/obj/graph.o ../common/obj/grfio.o \
+ ../common/obj/mapindex.o ../common/obj/ers.o ../zlib/unz.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h \
+ ../common/version.h ../common/db.h ../common/plugins.h ../common/lock.h \
+ ../common/malloc.h ../common/showmsg.h ../common/utils.h ../common/strlib.h \
+ ../common/graph.h ../common/grfio.h ../common/mapindex.h
+
+login-server_sql: login.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIB_S)
+
+clean:
+ rm -f *.o ../../login-server_sql
+
+# DO NOT DELETE
+
+login.o: login.c login.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
diff --git a/src/login_sql/login.c b/src/login_sql/login.c
new file mode 100644
index 000000000..fe4248183
--- /dev/null
+++ b/src/login_sql/login.c
@@ -0,0 +1,2256 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+
+#ifdef LCCWIN32
+#include <winsock.h>
+#pragma lib <libmysql.lib>
+#else
+#ifdef __WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <time.h>
+void Gettimeofday(struct timeval *timenow)
+{
+ time_t t;
+ t = clock();
+ timenow->tv_usec = (long)t;
+ timenow->tv_sec = (long)(t / CLK_TCK);
+ return;
+}
+#define gettimeofday(timenow, dummy) Gettimeofday(timenow)
+#define in_addr_t unsigned long
+#pragma comment(lib,"libmysql.lib")
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h> // for stat/lstat/fstat
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+
+//add include for DBMS(mysql)
+#include <mysql.h>
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/malloc.h"
+#include "../common/db.h"
+#include "../common/timer.h"
+#include "../common/strlib.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/version.h"
+#include "login.h"
+
+#ifdef PASSWORDENC
+#include "md5calc.h"
+#endif
+
+#define J_MAX_MALLOC_SIZE 65535
+
+//-----------------------------------------------------
+// global variable
+//-----------------------------------------------------
+int account_id_count = START_ACCOUNT_NUM;
+int server_num;
+int new_account_flag = 0; //Set from config too XD [Sirius]
+int bind_ip_set_ = 0;
+char bind_ip_str[128];
+int login_port = 6900;
+char lan_char_ip[128]; // Lan char ip added by kashy
+int subnetmaski[4]; // Subnetmask added by kashy
+
+struct mmo_char_server server[MAX_SERVERS];
+int server_fd[MAX_SERVERS];
+
+int login_fd;
+
+//Account flood protection [Kevin]
+unsigned int new_reg_tick=0;
+int allowed_regs=1;
+int num_regs=0;
+int time_allowed=10; //Init this to 10 secs, not 10K secs [Skotlex]
+
+char date_format[32] = "%Y-%m-%d %H:%M:%S";
+unsigned int auth_num = 0, auth_max = 0;
+
+int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server
+int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system)
+int check_client_version = 0; //Client version check ON/OFF .. (sirius)
+int client_version_to_connect = 20; //Client version needed to connect ..(sirius)
+static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex]
+
+MYSQL mysql_handle;
+
+int ipban = 1;
+int dynamic_account_ban = 1;
+int dynamic_account_ban_class = 0;
+int dynamic_pass_failure_ban = 1;
+int dynamic_pass_failure_ban_time = 5;
+int dynamic_pass_failure_ban_how_many = 3;
+int dynamic_pass_failure_ban_how_long = 60;
+
+int login_server_port = 3306;
+char login_server_ip[32] = "127.0.0.1";
+char login_server_id[32] = "ragnarok";
+char login_server_pw[32] = "ragnarok";
+char login_server_db[32] = "ragnarok";
+char default_codepage[32] = ""; //Feature by irmin.
+int use_md5_passwds = 0;
+char login_db[256] = "login";
+int log_login=1; //Whether to log the logins or not. [Skotlex]
+char loginlog_db[256] = "loginlog";
+
+// added to help out custom login tables, without having to recompile
+// source so options are kept in the login_athena.conf or the inter_athena.conf
+char login_db_account_id[256] = "account_id";
+char login_db_userid[256] = "userid";
+char login_db_user_pass[256] = "user_pass";
+char login_db_level[256] = "level";
+
+char reg_db[256] = "global_reg_value";
+
+int lowest_gm_level;
+struct gm_account *gm_account_db;
+int GM_num;
+char tmpsql[65535], tmp_sql[65535];
+
+int console = 0;
+
+int case_sensitive = 1;
+
+//-----------------------------------------------------
+
+#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 online_login_data {
+ int account_id;
+ short char_server;
+ short waiting_disconnect;
+};
+
+//-----------------------------------------------------
+
+static char md5key[20], md5keylen = 16;
+
+struct dbt *online_db;
+
+static void* create_online_user(DBKey key, va_list args) {
+ struct online_login_data *p;
+ p = aCalloc(1, sizeof(struct online_login_data));
+ p->account_id = key.i;
+ p->char_server = -1;
+ return p;
+}
+
+//-----------------------------------------------------
+// Online User Database [Wizputer]
+//-----------------------------------------------------
+
+void add_online_user(int char_server, int account_id) {
+ struct online_login_data *p;
+ if (!online_check)
+ return;
+ p = idb_ensure(online_db, account_id, create_online_user);
+ p->char_server = char_server;
+ p->waiting_disconnect = 0;
+}
+
+int is_user_online(int account_id) {
+ return (idb_get(online_db, account_id) != NULL);
+}
+
+void remove_online_user(int account_id) {
+ if(!online_check)
+ return;
+ if (account_id == 99) { // reset all to offline
+ online_db->clear(online_db, NULL);
+ return;
+ }
+ idb_remove(online_db,account_id);
+}
+
+int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data)
+{
+ struct online_login_data *p;
+ if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect)
+ remove_online_user(id);
+ return 0;
+}
+
+//-----------------------------------------------------
+// Read GM accounts
+//-----------------------------------------------------
+void read_gm_account(void) {
+ MYSQL_RES* sql_res ;
+ MYSQL_ROW sql_row;
+
+ if (gm_account_db != NULL)
+ aFree(gm_account_db);
+ GM_num = 0;
+
+ sprintf(tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'",login_db_account_id,login_db_level,login_db,login_db_level,lowest_gm_level);
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ gm_account_db = (struct gm_account*)aCalloc((size_t)mysql_num_rows(sql_res), sizeof(struct gm_account));
+ while ((sql_row = mysql_fetch_row(sql_res))) {
+ gm_account_db[GM_num].account_id = atoi(sql_row[0]);
+ gm_account_db[GM_num].level = atoi(sql_row[1]);
+ GM_num++;
+ }
+ }
+
+ mysql_free_result(sql_res);
+}
+
+int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len);
+
+//-----------------------------------------------------
+// Send GM accounts to all char-server
+//-----------------------------------------------------
+void send_GM_accounts(int fd) {
+ int i;
+ unsigned char buf[32767];
+ int len;
+
+ len = 4;
+ WBUFW(buf,0) = 0x2732;
+ for(i = 0; i < GM_num; i++)
+ // send only existing accounts. We can not create a GM account when server is online.
+ if (gm_account_db[i].level > 0) {
+ WBUFL(buf,len) = gm_account_db[i].account_id;
+ WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level;
+ len += 5;
+ }
+ WBUFW(buf,2) = len;
+ if (fd == -1)
+ charif_sendallwos(-1, buf, len);
+ else
+ {
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ return;
+}
+
+//-----------------------------------------------------
+// check user level
+//-----------------------------------------------------
+/*
+int isGM(int account_id) {
+ int level;
+
+ MYSQL_RES* sql_res;
+ MYSQL_ROW sql_row;
+ level = 0;
+ sprintf(tmpsql,"SELECT `%s` FROM `%s` WHERE `%s`='%d'", login_db_level, login_db, login_db_account_id, account_id);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ level = atoi(sql_row[0]);
+ if (level > 99)
+ level = 99;
+ }
+
+ if (level == 0) {
+ return 0;
+ //not GM
+ }
+
+ mysql_free_result(sql_res);
+
+ return level;
+}
+*/
+
+//---------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid).
+//---------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++) {
+ if (strchr(last_arobas, ch) != NULL) {
+ return 0;
+ break;
+ }
+ }
+
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
+ return 0;
+
+ // all correct
+ return 1;
+}
+
+//-----------------------------------------------------
+// Read Account database - mysql db
+//-----------------------------------------------------
+int mmo_auth_sqldb_init(void) {
+
+ ShowStatus("Login server init....\n");
+
+ // memory initialize
+ ShowStatus("memory initialize....\n");
+
+ mysql_init(&mysql_handle);
+
+ // DB connection start
+ ShowStatus("Connect Login Database Server....\n");
+ if (!mysql_real_connect(&mysql_handle, login_server_ip, login_server_id, login_server_pw,
+ login_server_db, login_server_port, (char *)NULL, 0)) {
+ // pointer check
+ ShowFatalError("%s\n", mysql_error(&mysql_handle));
+ exit(1);
+ } else {
+ ShowStatus("Connect success!\n");
+ }
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmpsql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql);
+ }
+ }
+
+ if (log_login)
+ {
+ sprintf(tmpsql, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '', 'lserver', '100','login server started')", loginlog_db);
+
+ //query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ if (new_account_flag)
+ { //Check if the next new account will need to have it's ID set (to avoid bad DBs which would otherwise insert
+ //new accounts with account_ids of less than 2M [Skotlex]
+ sprintf(tmp_sql, "SELECT max(`%s`) from `%s`", login_db_account_id, login_db);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ } else {
+ MYSQL_RES* sql_res;
+ MYSQL_ROW sql_row;
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res)
+ {
+ if (mysql_num_rows(sql_res) > 0 &&
+ (sql_row = mysql_fetch_row(sql_res)) != NULL &&
+ sql_row[0] != NULL && atoi(sql_row[0]) >= account_id_count)
+ //Ok, chars already exist, no need to use this.
+ account_id_count = 0;
+ mysql_free_result(sql_res);
+ }
+ }
+ }
+ return 0;
+}
+
+//-----------------------------------------------------
+// DB server connect check
+//-----------------------------------------------------
+void mmo_auth_sqldb_sync(void) {
+ // db connect check? or close?
+ // ping pong DB server -if losted? then connect try. else crash.
+}
+
+//-----------------------------------------------------
+// close DB
+//-----------------------------------------------------
+void mmo_db_close(void) {
+ int i, fd;
+
+ //set log.
+ if (log_login)
+ {
+ sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '', 'lserver','100', 'login server shutdown')", loginlog_db);
+
+ //query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+/*
+ //delete all server status
+ sprintf(tmpsql,"DELETE FROM `sstatus`");
+ //query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ mysql_close(&mysql_handle);
+ ShowStatus("close DB connect....\n");
+*/
+
+ for (i = 0; i < MAX_SERVERS; i++) {
+ if ((fd = server_fd[i]) >= 0)
+ { //Clean only data related to servers we are connected to. [Skotlex]
+ sprintf(tmpsql,"DELETE FROM `sstatus` WHERE `index` = '%d'", i);
+ if (mysql_query(&mysql_handle, tmpsql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ delete_session(fd);
+ }
+ }
+ mysql_close(&mysql_handle);
+ ShowStatus("close DB connect....\n");
+ delete_session(login_fd);
+}
+
+//-----------------------------------------------------
+// Make new account
+//-----------------------------------------------------
+int mmo_auth_new(struct mmo_account* account, char sex)
+{
+ MYSQL_RES* sql_res;
+ unsigned int tick = gettick();
+ char user_password[256];
+ //Account Registration Flood Protection by [Kevin]
+ if(tick <= new_reg_tick && num_regs >= allowed_regs) {
+ ShowNotice("Account registration denied (registration limit exceeded)\n");
+ return 3;
+ }
+
+ //Check for preexisting account
+ sprintf(tmp_sql, "SELECT `%s` FROM `%s` WHERE `userid` = '%s'", login_db_userid, login_db, account->userid);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1; //Return Incorrect user/pass?
+ }
+
+ sql_res = mysql_store_result(&mysql_handle);
+ if(mysql_num_rows(sql_res) > 0){
+ mysql_free_result(sql_res);
+ return 1; //Already exists, return incorrect user/pass.
+ }
+ mysql_free_result(sql_res); //Only needed for the already-exists check...
+
+ mysql_real_escape_string(&mysql_handle, account->userid, account->userid, strlen(account->userid));
+ mysql_real_escape_string(&mysql_handle, account->passwd, account->passwd, strlen(account->passwd));
+
+ if (sex == 'f') sex = 'F';
+ else if (sex == 'm') sex = 'M';
+ if (use_md5_passwds)
+ MD5_String(account->passwd,user_password);
+ else
+ jstrescapecpy(user_password, account->passwd);
+
+ ShowInfo("New account: user: %s with passwd: %s sex: %c\n", account->userid, user_password, sex);
+
+ if (account_id_count) //Force new Account ID
+ sprintf(tmp_sql, "INSERT INTO `%s` (`%s`, `%s`, `%s`, `sex`, `email`) VALUES ('%d', '%s', '%s', '%c', '%s')", login_db, login_db_account_id, login_db_userid, login_db_user_pass, account_id_count, account->userid, user_password, sex, "a@a.com");
+ else
+ sprintf(tmp_sql, "INSERT INTO `%s` (`%s`, `%s`, `sex`, `email`) VALUES ('%s', '%s', '%c', '%s')", login_db, login_db_userid, login_db_user_pass, account->userid, user_password, sex, "a@a.com");
+
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ //Failed to insert new acc :/
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+
+ if (account_id_count) //Clear it or all new accounts will try to use the same id :P
+ account_id_count = 0;
+
+ if(tick > new_reg_tick)
+ { //Update the registration check.
+ num_regs=0;
+ new_reg_tick=gettick()+time_allowed*1000;
+ }
+ num_regs++;
+
+ return 0;
+}
+
+#ifdef LCCWIN32
+extern void gettimeofday(struct timeval *t, struct timezone *dummy);
+#endif
+
+// Send to char
+int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) {
+ int i, c;
+ int fd;
+
+ c = 0;
+ for(i = 0; i < MAX_SERVERS; i++) {
+ if ((fd = server_fd[i]) > 0 && fd != sfd) {
+ if (WFIFOSPACE(fd) < len) //Increase buffer size.
+ realloc_writefifo(fd, len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ c++;
+ }
+ }
+
+ return c;
+}
+
+//-----------------------------------------------------
+// Auth
+//-----------------------------------------------------
+int mmo_auth( struct mmo_account* account , int fd){
+ time_t ban_until_time, raw_time;
+ char tmpstr[256];
+ char t_uid[256], t_pass[256];
+ char user_password[256];
+
+ //added for account creation _M _F
+ int len;
+
+ MYSQL_RES* sql_res;
+ MYSQL_ROW sql_row;
+ //int sql_fields, sql_cnt;
+ char md5str[64], md5bin[32];
+
+ 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]);
+ ShowInfo("auth start for %s...\n", ip);
+
+ //accountreg with _M/_F .. [Sirius]
+ len = strlen(account->userid) -2;
+
+ if (account->passwdenc == 0 && account->userid[len] == '_' &&
+ (account->userid[len+1] == 'F' || account->userid[len+1] == 'M' ||
+ account->userid[len+1] == 'f' || account->userid[len+1] == 'm') &&
+ new_account_flag == 1 &&
+ len >= 4 && strlen(account->passwd) >= 4)
+ {
+ int result;
+ account->userid[len] = '\0'; //Terminating the name.
+ if ((result = mmo_auth_new(account, account->userid[len+1])))
+ return result; //Failed to make account. [Skotlex].
+ }
+
+ // auth start : time seed
+ // Platform/Compiler dependant clock() for time check is removed. [Lance]
+ // clock() is originally used to track processing ticks on program execution.
+ time(&raw_time);
+ strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time));
+
+ jstrescapecpy(t_uid,account->userid);
+ jstrescapecpy(t_pass, account->passwd);
+
+
+ // make query
+ sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`,`%s`"
+ " FROM `%s` WHERE %s `%s`='%s'", login_db_account_id, login_db_userid, login_db_user_pass, login_db_level, login_db, case_sensitive ? "BINARY" : "", login_db_userid, t_uid);
+ //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state/10-level}
+
+ // query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+ if (!sql_row) {
+ //there's no id.
+ ShowNotice("auth failed: no such account %s %s %s\n", tmpstr, account->userid, account->passwd);
+ mysql_free_result(sql_res);
+ return 0;
+ }
+ } else {
+ ShowError("mmo_auth DB result error ! \n");
+ return 0;
+ }
+
+ //Client Version check[Sirius]
+ if(check_client_version == 1 && account->version != 0){
+ if(account->version != client_version_to_connect){
+ mysql_free_result(sql_res);
+ return 5;
+ }
+ }
+
+ // Documented by CLOWNISIUS || LLRO || Gunstar lead this one with me
+ // IF changed to diferent returns~ you get diferent responses from your msgstringtable.txt
+ //Ireturn 2 == line 9
+ //Ireturn 5 == line 311
+ //Ireturn 6 == line 450
+ //Ireturn 7 == line 440
+ //Ireturn 8 == line 682
+ //Ireturn 9 == line 704
+ //Ireturn 10 == line 705
+ //Ireturn 11 == line 706
+ //Ireturn 12 == line 707
+ //Ireturn 13 == line 708
+ //Ireturn 14 == line 709
+ //Ireturn 15 == line 710
+ //Ireturn -1 == line 010
+ // Check status
+ {
+ int encpasswdok = 0;
+
+ if (atoi(sql_row[9]) == -3) {
+ //id is banned
+ mysql_free_result(sql_res);
+ return -3;
+ } else if (atoi(sql_row[9]) == -2) { //dynamic ban
+ //id is banned
+ mysql_free_result(sql_res);
+ //add IP list.
+ return -2;
+ }
+
+ if (use_md5_passwds) {
+ MD5_String(account->passwd,user_password);
+ } else {
+ jstrescapecpy(user_password, account->passwd);
+ }
+ ShowInfo("account id ok encval:%d\n",account->passwdenc);
+#ifdef PASSWORDENC
+ if (account->passwdenc > 0) {
+ int j = account->passwdenc;
+ ShowInfo("start md5calc..\n");
+ if (j > 2)
+ j = 1;
+ do {
+ if (j == 1) {
+ sprintf(md5str, "%s%s", md5key,sql_row[2]);
+ } else if (j == 2) {
+ sprintf(md5str, "%s%s", sql_row[2], md5key);
+ } else
+ md5str[0] = 0;
+ ShowDebug("j:%d mdstr:%s\n", j, md5str);
+ MD5_String2binary(md5str, md5bin);
+ encpasswdok = (memcmp(user_password, md5bin, 16) == 0);
+ } while (j < 2 && !encpasswdok && (j++) != account->passwdenc);
+ //printf("key[%s] md5 [%s] ", md5key, md5);
+ ShowInfo("client [%s] accountpass [%s]\n", user_password, sql_row[2]);
+ ShowInfo("end md5calc..\n");
+ }
+#endif
+ if ((strcmp(user_password, sql_row[2]) && !encpasswdok)) {
+ if (account->passwdenc == 0) {
+ ShowNotice("auth failed pass error %s %s %s" RETCODE, tmpstr, account->userid, user_password);
+#ifdef PASSWORDENC
+ } else {
+ char logbuf[1024], *p = logbuf;
+ int j;
+ p += sprintf(p, "auth failed pass error %s %s recv-md5[", tmpstr, account->userid);
+ for(j = 0; j < 16; j++)
+ p += sprintf(p, "%02x", ((unsigned char *)user_password)[j]);
+ p += sprintf(p, "] calc-md5[");
+ for(j = 0; j < 16; j++)
+ p += sprintf(p, "%02x", ((unsigned char *)md5bin)[j]);
+ p += sprintf(p, "] md5key[");
+ for(j = 0; j < md5keylen; j++)
+ p += sprintf(p, "%02x", ((unsigned char *)md5key)[j]);
+ p += sprintf(p, "]" RETCODE);
+ ShowNotice("%s\n", p);
+#endif
+ }
+ return 1;
+ }
+ ShowInfo("auth ok %s %s" RETCODE, tmpstr, account->userid);
+ }
+
+/*
+// do not remove this section. this is meant for future, and current forums usage
+// as a login manager and CP for login server. [CLOWNISIUS]
+ if (atoi(sql_row[10]) == 1) {
+ return 4;
+ }
+
+ if (atoi(sql_row[10]) >= 5) {
+ switch(atoi(sql_row[10])) {
+ case 5:
+ return 5;
+ break;
+ case 6:
+ return 7;
+ break;
+ case 7:
+ return 9;
+ break;
+ case 8:
+ return 10;
+ break;
+ case 9:
+ return 11;
+ break;
+ default:
+ return 10;
+ break;
+ }
+ }
+*/
+ ban_until_time = atol(sql_row[8]);
+
+ //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state}
+ if (ban_until_time != 0) { // if account is banned
+ strftime(tmpstr, 20, date_format, localtime(&ban_until_time));
+ tmpstr[19] = '\0';
+ if (ban_until_time > time(NULL)) { // always banned
+ return 6; // 6 = Your are Prohibited to log in until %s
+ } else { // ban is finished
+ // reset the ban time
+ if (atoi(sql_row[9])==7) {//it was a temp ban - so we set STATE to 0
+ sprintf(tmpsql, "UPDATE `%s` SET `ban_until`='0', `state`='0' WHERE %s `%s`='%s'", login_db, case_sensitive ? "BINARY" : "", login_db_userid, t_uid);
+ strcpy(sql_row[9],"0"); //we clear STATE
+ } else //it was a permanent ban + temp ban. So we leave STATE = 5, but clear the temp ban
+ sprintf(tmpsql, "UPDATE `%s` SET `ban_until`='0' WHERE %s `%s`='%s'", login_db, case_sensitive ? "BINARY" : "", login_db_userid, t_uid);
+
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ if (atoi(sql_row[9])) {
+ switch(atoi(sql_row[9])) { // 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 more accounts may be connected from this company
+ case 10: // 9 = MSI_REFUSE_BAN_BY_DBA
+ case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
+ case 12: // 11 = MSI_REFUSE_BAN_BY_GM
+ case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
+ case 14: // 13 = MSI_REFUSE_SELF_LOCK
+ case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ case 100: // 99 = This ID has been totally erased
+ case 101: // 100 = Login information remains at %s.
+ case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
+ case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation
+ case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being
+ case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being
+ ShowNotice("Auth Error #%d\n", atoi(sql_row[9]));
+ return atoi(sql_row[9]) - 1;
+ break;
+ default:
+ return 99; // 99 = ID has been totally erased
+ break;
+ }
+ }
+
+ if (atol(sql_row[6]) != 0 && atol(sql_row[6]) < time(NULL)) {
+ return 2; // 2 = This ID is expired
+ }
+
+ if (online_check) {
+ struct online_login_data* data = idb_get(online_db,atoi(sql_row[0]));
+ unsigned char buf[8];
+ if (data && data->char_server > -1) {
+ //Request char servers to kick this account out. [Skotlex]
+ ShowWarning("User [%s] is already online - Rejected.\n",sql_row[1]);
+ WBUFW(buf,0) = 0x2734;
+ WBUFL(buf,2) = atol(sql_row[0]);
+ charif_sendallwos(-1, buf, 6);
+ if (!data->waiting_disconnect)
+ add_timer(gettick()+30000, waiting_disconnect_timer, atol(sql_row[0]), 0);
+ data->waiting_disconnect = 1;
+ return 3; // Rejected
+ }
+ }
+
+ account->account_id = atoi(sql_row[0]);
+ account->login_id1 = rand();
+ account->login_id2 = rand();
+ memcpy(tmpstr, sql_row[3], 19);
+ memcpy(account->lastlogin, tmpstr, 24);
+ account->sex = sql_row[5][0] == 'S' ? 2 : sql_row[5][0]=='M';
+ account->level = atoi(sql_row[10]) > 99 ? 99 : atoi(sql_row[10]); // as was in isGM() [zzo]
+
+ if (account->sex != 2 && account->account_id < 700000)
+ ShowWarning("Account %s has account id %d! Account IDs must be over 700000 to work properly!\n", account->userid, account->account_id);
+ sprintf(tmpsql, "UPDATE `%s` SET `lastlogin` = NOW(), `logincount`=`logincount` +1, `last_ip`='%s' WHERE %s `%s` = '%s'",
+ login_db, ip, case_sensitive ? "BINARY" : "", login_db_userid, sql_row[1]);
+ mysql_free_result(sql_res) ; //resource free
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return -1;
+}
+
+static int online_db_setoffline(DBKey key, void* data, va_list ap) {
+ struct online_login_data *p = (struct online_login_data *)data;
+ int server = va_arg(ap, int);
+ if (server == -1) {
+ p->char_server = -1;
+ p->waiting_disconnect = 0;
+ } else if (p->char_server == server)
+ p->char_server = -2; //Char server disconnected.
+ return 0;
+}
+
+//-----------------------------------------------------
+// char-server packet parse
+//-----------------------------------------------------
+int parse_fromchar(int fd){
+ int i, id;
+ MYSQL_RES* sql_res;
+ MYSQL_ROW sql_row = NULL;
+
+ 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 = 1;
+ if(session[fd]->eof) {
+ if (id < MAX_SERVERS) {
+ ShowStatus("Char-server '%s' has disconnected.\n", server[id].name);
+ server_fd[id] = -1;
+ memset(&server[id], 0, sizeof(struct mmo_char_server));
+ online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline.
+ // server delete
+ sprintf(tmpsql, "DELETE FROM `sstatus` WHERE `index`='%d'", id);
+ // query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd) >= 2) {
+// printf("char_parse: %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0));
+
+ switch (RFIFOW(fd,0)) {
+ case 0x2709:
+ if (log_login)
+ {
+ sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%d.%d.%d.%d', '%s','%s', 'GM reload request')", loginlog_db, p[0], p[1], p[2], p[3], server[id].name, RETCODE);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ read_gm_account();
+ // send GM accounts to all char-servers
+ send_GM_accounts(-1);
+ RFIFOSKIP(fd,2);
+ break;
+
+ case 0x2712:
+ if (RFIFOREST(fd) < 19)
+ return 0;
+ {
+ int account_id;
+ account_id = RFIFOL(fd,2); // speed up
+ for(i=0;i<AUTH_FIFO_SIZE;i++){
+ if (auth_fifo[i].account_id == account_id &&
+ 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) &&
+#if CMP_AUTHFIFO_IP != 0
+ auth_fifo[i].ip == RFIFOL(fd,15) &&
+#endif
+ !auth_fifo[i].delflag) {
+ auth_fifo[i].delflag = 1;
+ ShowDebug("auth -> %d\n", i);
+ break;
+ }
+ }
+
+ if (i != AUTH_FIFO_SIZE && account_id > 0) { // send ack
+ time_t connect_until_time = 0;
+ char email[40] = "";
+ account_id=RFIFOL(fd,2);
+ sprintf(tmpsql, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ connect_until_time = atol(sql_row[1]);
+ strcpy(email, sql_row[0]);
+ mysql_free_result(sql_res);
+ }
+ WFIFOW(fd,0) = 0x2713;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 0;
+ memcpy(WFIFOP(fd, 7), email, 40);
+ WFIFOL(fd,47) = (unsigned long) connect_until_time;
+ WFIFOSET(fd,51);
+ } else {
+ WFIFOW(fd,0) = 0x2713;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 1;
+ WFIFOSET(fd,51);
+ }
+ }
+ RFIFOSKIP(fd,19);
+ break;
+
+ case 0x2714:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ // how many users on world? (update)
+ if (server[id].users != RFIFOL(fd,2))
+ {
+ ShowStatus("set users %s : %d\n", server[id].name, RFIFOL(fd,2));
+
+ server[id].users = RFIFOL(fd,2);
+ sprintf(tmpsql,"UPDATE `sstatus` SET `user` = '%d' WHERE `index` = '%d'", server[id].users, id);
+ // query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ // send some answer
+ WFIFOW(fd,0) = 0x2718;
+ WFIFOSET(fd,2);
+
+ RFIFOSKIP(fd,6);
+ 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 0;
+ {
+ int account_id;
+ time_t connect_until_time = 0;
+ char email[40] = "";
+ account_id=RFIFOL(fd,2);
+ sprintf(tmpsql,"SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'",login_db, login_db_account_id, account_id);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res);
+ connect_until_time = atol(sql_row[1]);
+ strcpy(email, sql_row[0]);
+ }
+ mysql_free_result(sql_res);
+ //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2));
+ WFIFOW(fd,0) = 0x2717;
+ WFIFOL(fd,2) = RFIFOL(fd,2);
+ memcpy(WFIFOP(fd, 6), email, 40);
+ WFIFOL(fd,46) = (unsigned long) connect_until_time;
+ WFIFOSET(fd,50);
+ }
+ RFIFOSKIP(fd,6);
+ break;
+
+ case 0x2720: // GM
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ if (RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ //oldacc = RFIFOL(fd,4);
+ ShowWarning("change GM isn't supported in this login server version.\n");
+ ShowError("change GM error 0 %s\n", RFIFOP(fd, 8));
+
+ RFIFOSKIP(fd, RFIFOW(fd, 2));
+ WFIFOW(fd, 0) = 0x2721;
+ WFIFOL(fd, 2) = RFIFOL(fd,4); // oldacc;
+ WFIFOL(fd, 6) = 0; // newacc;
+ WFIFOSET(fd, 10);
+ return 0;
+
+ // 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 0;
+ {
+ int acc;
+ char actual_email[40], new_email[40];
+ acc = RFIFOL(fd,2);
+ memcpy(actual_email, RFIFOP(fd,6), 40);
+ memcpy(new_email, RFIFOP(fd,46), 40);
+ if (e_mail_check(actual_email) == 0)
+ ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" RETCODE,
+ server[id].name, acc, ip);
+ else if (e_mail_check(new_email) == 0)
+ ShowWarning("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)" RETCODE,
+ server[id].name, acc, ip);
+ else if (strcmpi(new_email, "a@a.com") == 0)
+ ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)" RETCODE,
+ server[id].name, acc, ip);
+ else {
+ sprintf(tmpsql, "SELECT `%s`,`email` FROM `%s` WHERE `%s` = '%d'", login_db_userid, login_db, login_db_account_id, acc);
+ if (mysql_query(&mysql_handle, tmpsql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+
+ if (strcmpi(sql_row[1], actual_email) == 0) {
+ sprintf(tmpsql, "UPDATE `%s` SET `email` = '%s' WHERE `%s` = '%d'", login_db, new_email, login_db_account_id, acc);
+ // query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ ShowInfo("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s)." RETCODE,
+ server[id].name, acc, sql_row[0], actual_email, ip);
+ }
+ }
+
+ }
+ }
+ RFIFOSKIP(fd, 86);
+ break;
+
+ case 0x2724: // Receiving of map-server via char-server a status change resquest (by Yor)
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ int acc, statut;
+ acc = RFIFOL(fd,2);
+ statut = RFIFOL(fd,6);
+ sprintf(tmpsql, "SELECT `state` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, acc);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); // row fetching
+ }
+ if (atoi(sql_row[0]) != statut && 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);
+ }
+ sprintf(tmpsql,"UPDATE `%s` SET `state` = '%d' WHERE `%s` = '%d'", login_db, statut,login_db_account_id,acc);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ RFIFOSKIP(fd,10);
+ }
+ return 0;
+
+ case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor)
+ if (RFIFOREST(fd) < 18)
+ return 0;
+ {
+ int acc;
+ struct tm *tmtime;
+ time_t timestamp, tmptime;
+ acc = RFIFOL(fd,2);
+ sprintf(tmpsql, "SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); // row fetching
+ }
+ tmptime = atol(sql_row[0]);
+ if (tmptime == 0 || tmptime < time(NULL))
+ timestamp = time(NULL);
+ else
+ timestamp = tmptime;
+ tmtime = localtime(&timestamp);
+ 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 = mktime(tmtime);
+ if (timestamp != -1) {
+ if (timestamp <= time(NULL))
+ timestamp = 0;
+ if (tmptime != timestamp) {
+ if (timestamp != 0) {
+ unsigned char buf[16];
+ WBUFW(buf,0) = 0x2731;
+ WBUFL(buf,2) = acc;
+ WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
+ WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment
+ charif_sendallwos(-1, buf, 11);
+ }
+ ShowNotice("Account: %d Banned until: %ld\n", acc, timestamp);
+ sprintf(tmpsql, "UPDATE `%s` SET `ban_until` = '%ld', `state`='7' WHERE `%s` = '%d'", login_db, (unsigned long)timestamp, login_db_account_id, acc);
+ // query
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ RFIFOSKIP(fd,18);
+ break;
+ }
+ return 0;
+
+ case 0x2727:
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ {
+ int acc,sex;
+ unsigned char buf[16];
+ acc=RFIFOL(fd,2);
+ sprintf(tmpsql,"SELECT `sex` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc);
+
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ sql_res = mysql_store_result(&mysql_handle) ;
+
+ if (sql_res) {
+ if (mysql_num_rows(sql_res) == 0) {
+ mysql_free_result(sql_res);
+ return 0;
+ }
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+ }
+
+ if (strcmpi(sql_row[0], "M") == 0)
+ sex = 1;
+ else
+ sex = 0;
+ sprintf(tmpsql,"UPDATE `%s` SET `sex` = '%c' WHERE `%s` = '%d'", login_db, (sex==0?'M':'F'), login_db_account_id, acc);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ WBUFW(buf,0) = 0x2723;
+ WBUFL(buf,2) = acc;
+ WBUFB(buf,6) = sex;
+ charif_sendallwos(-1, buf, 7);
+ RFIFOSKIP(fd,6);
+ }
+ return 0;
+
+ case 0x2728: // save account_reg2
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ if (RFIFOL(fd,4) > 0) {
+ int acc,p,j,len;
+ char str[32];
+ char temp_str[64]; //Needs twice as much space as the original string.
+ char temp_str2[512];
+ char value[256];
+ unsigned char *buf;
+ acc=RFIFOL(fd,4);
+ buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char));
+ //Delete all global account variables....
+ sprintf(tmpsql,"DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d';",reg_db,acc);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ //Proceed to insert them....
+ for(j=0,p=13;j<ACCOUNT_REG2_NUM && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n",str,&len);
+ str[len]='\0';
+ p +=len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n",value,&len);
+ value[len]='\0';
+ p +=len+1;
+
+ sprintf(tmpsql,"INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , '%s' , '%s');", reg_db, acc, jstrescapecpy(temp_str,str), jstrescapecpy(temp_str2,value));
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ // Send to char
+ memcpy(WBUFP(buf,0),RFIFOP(fd,0),RFIFOW(fd,2));
+ WBUFW(buf,0)=0x2729;
+ charif_sendallwos(fd,buf,WBUFW(buf,2));
+ if (buf) aFree(buf);
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ //printf("login: save account_reg (from char)\n");
+ break;
+
+ case 0x272a: // Receiving of map-server via char-server a unban resquest (by Yor)
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ {
+ int acc;
+ acc = RFIFOL(fd,2);
+ sprintf(tmpsql,"SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+ }
+ if (atol(sql_row[0]) != 0) {
+ sprintf(tmpsql,"UPDATE `%s` SET `ban_until` = '0', `state`='0' WHERE `%s` = '%d'", login_db,login_db_account_id,acc);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ break;
+ }
+ RFIFOSKIP(fd,6);
+ }
+ return 0;
+
+ case 0x272b: // Set account_id to online [Wizputer]
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ add_online_user(id, RFIFOL(fd,2));
+ RFIFOSKIP(fd,6);
+ break;
+
+ case 0x272c: // Set account_id to offline [Wizputer]
+ if (RFIFOREST(fd) < 6)
+ return 0;
+ remove_online_user(RFIFOL(fd,2));
+ RFIFOSKIP(fd,6);
+ break;
+ case 0x272d: // Receive list of all online accounts. [Skotlex]
+ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+ return 0;
+ if (!online_check) {
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ {
+ struct online_login_data *p;
+ int aid, i, users;
+ online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first
+ users = RFIFOW(fd,4);
+ for (i = 0; i < users; i++) {
+ aid = RFIFOL(fd,6+i*4);
+ p = idb_ensure(online_db, aid, create_online_user);
+ p->char_server = id;
+ }
+ RFIFOSKIP(fd,RFIFOW(fd,2));
+ break;
+ }
+ case 0x272e: //Request account_reg2 for a character.
+ if (RFIFOREST(fd) < 10)
+ return 0;
+ {
+ int account_id = RFIFOL(fd, 2);
+ int char_id = RFIFOL(fd, 6);
+ int p;
+ RFIFOSKIP(fd,10);
+ sprintf(tmpsql, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'",reg_db, account_id);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ break;
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (!sql_res) {
+ break;
+ }
+ WFIFOW(fd,0) = 0x2729;
+ WFIFOL(fd,4) = account_id;
+ WFIFOL(fd,8) = char_id;
+ WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
+ for(p = 13; (sql_row = mysql_fetch_row(sql_res));){
+ if (sql_row[0][0]) {
+ p+= sprintf(WFIFOP(fd,p), "%s", sql_row[0])+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(fd,p), "%s", sql_row[1])+1;
+ }
+ }
+ WFIFOW(fd,2) = p;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ mysql_free_result(sql_res);
+ }
+ break;
+ default:
+ ShowError("login: unknown packet %x! (from char).\n", RFIFOW(fd,0));
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ return 0;
+}
+
+//Lan ip check added by Kashy
+int lan_ip_check(unsigned char *p) {
+ int y;
+ int lancheck = 1;
+ int lancharip[4];
+
+ unsigned int k0, k1, k2, k3;
+ sscanf(lan_char_ip, "%d.%d.%d.%d", &k0, &k1, &k2, &k3);
+ lancharip[0] = k0; lancharip[1] = k1; lancharip[2] = k2; lancharip[3] = k3;
+
+ for(y = 0; y < 4; y++) {
+ if ((lancharip[y] & subnetmaski[y])!= (p[y]))
+ lancheck = 0;
+ break; }
+
+ ShowInfo("LAN check: "CL_CYAN"%s"CL_RESET".\n", (lancheck) ? "LAN" : "WAN");
+ return lancheck;
+}
+
+//----------------------------------------------------------------------------------------
+// Default packet parsing (normal players or administation/char-server connection requests)
+//----------------------------------------------------------------------------------------
+int parse_login(int fd) {
+ //int len;
+
+ MYSQL_RES* sql_res ;
+ MYSQL_ROW sql_row = NULL;
+
+ char t_uid[100];
+ //int sql_fields, sql_cnt;
+ struct mmo_account account;
+
+ int result, i;
+ 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]);
+
+ memset(&account, 0, sizeof(account));
+
+ if (ipban > 0) {
+ //ip ban
+ //p[0], p[1], p[2], p[3]
+ //request DB connection
+ //check
+ sprintf(tmpsql, "SELECT count(*) FROM `ipbanlist` WHERE `list` = '%d.*.*.*' OR `list` = '%d.%d.*.*' OR `list` = '%d.%d.%d.*' OR `list` = '%d.%d.%d.%d'",
+ p[0], p[0], p[1], p[0], p[1], p[2], p[0], p[1], p[2], p[3]);
+ if (mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ // close connection because we can't verify their connectivity.
+ session[fd]->eof = 1;
+ } else { //Avoid entering as it causes a crash.
+ sql_res = mysql_store_result(&mysql_handle) ;
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+
+ if (atoi(sql_row[0]) >0) {
+ // ip ban ok.
+ ShowWarning("packet from banned ip : %d.%d.%d.%d\n" RETCODE, p[0], p[1], p[2], p[3]);
+ if (log_login)
+ {
+ sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%d.%d.%d.%d', 'unknown','-3', 'ip banned')", loginlog_db, p[0], p[1], p[2], p[3]);
+
+ // query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo ("close session connection...\n");
+
+ // close connection
+ session[fd]->eof = 1;
+
+ } else {
+ ShowInfo ("packet from ip (ban check ok) : %d.%d.%d.%d" RETCODE, p[0], p[1], p[2], p[3]);
+ }
+ mysql_free_result(sql_res);
+ }
+ }
+ if (session[fd]->eof) {
+ for(i = 0; i < MAX_SERVERS; i++)
+ if (server_fd[i] == fd)
+ server_fd[i] = -1;
+ do_close(fd);
+ return 0;
+ }
+
+ while(RFIFOREST(fd)>=2){
+ ShowDebug("parse_login : %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd,0));
+
+ 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 0;
+ 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 0;
+ RFIFOSKIP(fd,18);
+ break;
+
+ case 0x64: // request client login
+ case 0x01dd: // request client login with encrypt
+ if((int)RFIFOREST(fd)< ((RFIFOW(fd, 0) ==0x64)?55:47))
+ return 0;
+
+ ShowInfo("client connection request %s from %d.%d.%d.%d\n", RFIFOP(fd, 6), p[0], p[1], p[2], p[3]);
+ account.version = RFIFOL(fd, 2);
+ account.userid = (char*)RFIFOP(fd, 6);
+ account.userid[23] = '\0';
+ account.passwd = (char*)RFIFOP(fd, 30);
+ account.passwd[23] = '\0';
+#ifdef PASSWORDENC
+ account.passwdenc= (RFIFOW(fd,0)==0x64)?0:PASSWORDENC;
+#else
+ account.passwdenc=0;
+#endif
+ result=mmo_auth(&account, fd);
+
+
+ jstrescapecpy(t_uid,(char*)RFIFOP(fd, 6));
+ if(result==-1){
+ // as we have queried account level earlier in mmo_auth anyway, no need to do this again [zzo]
+// int gm_level = isGM(account.account_id); // removed by [zzo]
+
+ if (min_level_to_connect > account.level) {
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ } else {
+
+ if (p[0] != 127 && log_login) {
+ sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%d.%d.%d.%d', '%s','100', 'login ok')", loginlog_db, p[0], p[1], p[2], p[3], t_uid);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ if (account.level)
+ ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account.level, account.userid);
+ else
+ ShowStatus("Connection of the account '%s' accepted.\n", account.userid);
+ server_num=0;
+ for(i = 0; i < MAX_SERVERS; i++) {
+ if (server_fd[i] >= 0) {
+ //Lan check added by Kashy
+ 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].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;
+ memcpy(WFIFOP(fd,20),account.lastlogin,24);
+ 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++;
+ } else {
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3);
+ }
+ }
+ } else {
+ char tmp_sql[512];
+ char error[64];
+ if (log_login)
+ {
+ sprintf(tmp_sql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%d.%d.%d.%d', '%s', '%d','login failed : %%s')", loginlog_db, p[0], p[1], p[2], p[3], t_uid, result);
+ switch((result + 1)) {
+ case -2: //-3 = Account Banned
+ sprintf(tmpsql,tmp_sql,"Account banned.");
+ sprintf(error,"Account banned.");
+ break;
+ case -1: //-2 = Dynamic Ban
+ sprintf(tmpsql,tmp_sql,"dynamic ban (ip and account).");
+ sprintf(error,"dynamic ban (ip and account).");
+ break;
+ case 1: // 0 = Unregistered ID
+ sprintf(tmpsql,tmp_sql,"Unregisterd ID.");
+ sprintf(error,"Unregisterd ID.");
+ break;
+ case 2: // 1 = Incorrect Password
+ sprintf(tmpsql,tmp_sql,"Incorrect Password.");
+ sprintf(error,"Incorrect Password.");
+ break;
+ case 3: // 2 = This ID is expired
+ sprintf(tmpsql,tmp_sql,"Account Expired.");
+ sprintf(error,"Account Expired.");
+ break;
+ case 4: // 3 = Rejected from Server
+ sprintf(tmpsql,tmp_sql,"Rejected from server.");
+ sprintf(error,"Rejected from server.");
+ break;
+ case 5: // 4 = You have been blocked by the GM Team
+ sprintf(tmpsql,tmp_sql,"Blocked by GM.");
+ sprintf(error,"Blocked by GM.");
+ break;
+ case 6: // 5 = Your Game's EXE file is not the latest version
+ sprintf(tmpsql,tmp_sql,"Not latest game EXE.");
+ sprintf(error,"Not latest game EXE.");
+ break;
+ case 7: // 6 = Your are Prohibited to log in until %s
+ sprintf(tmpsql,tmp_sql,"Banned.");
+ sprintf(error,"Banned.");
+ break;
+ case 8: // 7 = Server is jammed due to over populated
+ sprintf(tmpsql,tmp_sql,"Server Over-population.");
+ sprintf(error,"Server Over-population.");
+ break;
+ case 9: // 8 = No more accounts may be connected from this company
+ sprintf(tmpsql,tmp_sql,"Account limit from company");
+ sprintf(error,"Account limit from company");
+ break;
+ case 10: // 9 = MSI_REFUSE_BAN_BY_DBA
+ sprintf(tmpsql,tmp_sql,"Ban by DBA");
+ sprintf(error,"Ban by DBA");
+ break;
+ case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
+ sprintf(tmpsql,tmp_sql,"Email not confirmed");
+ sprintf(error,"Email not confirmed");
+ break;
+ case 12: // 11 = MSI_REFUSE_BAN_BY_GM
+ sprintf(tmpsql,tmp_sql,"Ban by GM");
+ sprintf(error,"Ban by GM");
+ break;
+ case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
+ sprintf(tmpsql,tmp_sql,"Working in DB");
+ sprintf(error,"Working in DB");
+ break;
+ case 14: // 13 = MSI_REFUSE_SELF_LOCK
+ sprintf(tmpsql,tmp_sql,"Self Lock");
+ sprintf(error,"Self Lock");
+ break;
+ case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ sprintf(tmpsql,tmp_sql,"Not Permitted Group");
+ sprintf(error,"Not Permitted Group");
+ break;
+ case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP
+ sprintf(tmpsql,tmp_sql,"Not Permitted Group");
+ sprintf(error,"Not Permitted Group");
+ break;
+ case 100: // 99 = This ID has been totally erased
+ sprintf(tmpsql,tmp_sql,"Account gone.");
+ sprintf(error,"Account gone.");
+ break;
+ case 101: // 100 = Login information remains at %s
+ sprintf(tmpsql,tmp_sql,"Login info remains.");
+ sprintf(error,"Login info remains.");
+ break;
+ case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
+ sprintf(tmpsql,tmp_sql,"Hacking investigation.");
+ sprintf(error,"Hacking investigation.");
+ break;
+ case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation
+ sprintf(tmpsql,tmp_sql,"Bug investigation.");
+ sprintf(error,"Bug investigation.");
+ break;
+ case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being
+ sprintf(tmpsql,tmp_sql,"Deleting char.");
+ sprintf(error,"Deleting char.");
+ break;
+ case 105: // 104 = This character is being deleted. Login is temporarily unavailable for the time being
+ sprintf(tmpsql,tmp_sql,"Deleting spouse char.");
+ sprintf(error,"Deleting spouse char.");
+ break;
+ default:
+ sprintf(tmpsql,tmp_sql,"Uknown Error.");
+ sprintf(error,"Uknown Error.");
+ break;
+ }
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } //End login log of error.
+ if ((result == 1) && (dynamic_pass_failure_ban != 0) && log_login){ // failed password
+ sprintf(tmpsql,"SELECT count(*) FROM `%s` WHERE `ip` = '%d.%d.%d.%d' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE",
+ loginlog_db, p[0], p[1], p[2], p[3], dynamic_pass_failure_ban_time); //how many times filed account? in one ip.
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ //check query result
+ sql_res = mysql_store_result(&mysql_handle) ;
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+
+ if (atoi(sql_row[0]) >= dynamic_pass_failure_ban_how_many ) {
+ sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban: %s')", p[0], p[1], p[2], dynamic_pass_failure_ban_how_long, t_uid);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+ else if (result == -2){ //dynamic banned - add ip to ban list.
+ sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL 1 MONTH ,'Dynamic banned user id : %s')", p[0], p[1], p[2], t_uid);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ result = -3;
+ }else if(result == 6){ //not lastet version ..
+ //result = 5;
+ }
+
+ sprintf(tmpsql,"SELECT `ban_until` FROM `%s` WHERE %s `%s` = '%s'",login_db, case_sensitive ? "BINARY" : "",login_db_userid, t_uid);
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res) {
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+ }
+ //cannot connect login failed
+ 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
+ if (atol(sql_row[0]) != 0) { // if account is banned, we send ban timestamp
+ char tmpstr[256];
+ time_t ban_until_time;
+ ban_until_time = atol(sql_row[0]);
+ strftime(tmpstr, 20, date_format, localtime(&ban_until_time));
+ tmpstr[19] = '\0';
+ memcpy(WFIFOP(fd,3), tmpstr, 20);
+ } else { // we send error message
+ memcpy(WFIFOP(fd,3), error, 20);
+ }
+ }
+ WFIFOSET(fd,23);
+ }
+ RFIFOSKIP(fd,(RFIFOW(fd,0)==0x64)?55:47);
+ break;
+
+ case 0x01db: // request password key
+ if (session[fd]->session_data) {
+ ShowWarning("login: abnormal request of MD5 key (already opened session).\n");
+ session[fd]->eof = 1;
+ return 0;
+ }
+ ShowDebug("Request Password key -%s\n",md5key);
+ RFIFOSKIP(fd,2);
+ WFIFOW(fd,0)=0x01dc;
+ WFIFOW(fd,2)=4+md5keylen;
+ memcpy(WFIFOP(fd,4),md5key,md5keylen);
+ WFIFOSET(fd,WFIFOW(fd,2));
+ break;
+
+ case 0x2710: // request Char-server connection
+ if(RFIFOREST(fd)<86)
+ return 0;
+ {
+ unsigned char* server_name;
+ if (log_login)
+ {
+ sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%d.%d.%d.%d', '%s@%s','100', 'charserver - %s@%d.%d.%d.%d:%d')", loginlog_db, p[0], p[1], p[2], p[3], RFIFOP(fd, 2),RFIFOP(fd, 60),RFIFOP(fd, 60), RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), RFIFOW(fd, 58));
+
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo("server connection request %s @ %d.%d.%d.%d:%d (%d.%d.%d.%d)\n",
+ RFIFOP(fd, 60), RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), RFIFOW(fd, 58),
+ p[0], p[1], p[2], p[3]);
+ account.userid = (char*)RFIFOP(fd, 2);
+ account.userid[23] = '\0';
+ account.passwd = (char*)RFIFOP(fd, 26);
+ account.passwd[23] = '\0';
+ account.passwdenc = 0;
+ server_name = RFIFOP(fd,60);
+ result = mmo_auth(&account, fd);
+ //printf("Result: %d - Sex: %d - Account ID: %d\n",result,account.sex,(int) account.account_id);
+
+ if(result == -1 && account.sex==2 && account.account_id<MAX_SERVERS && server_fd[account.account_id]==-1){
+ ShowStatus("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,RFIFOP(fd,60),20);
+ server[account.account_id].users=0;
+ server[account.account_id].maintenance=RFIFOW(fd,82);
+ server[account.account_id].new_=RFIFOW(fd,84);
+ server_fd[account.account_id]=fd;
+ sprintf(tmpsql,"DELETE FROM `sstatus` WHERE `index`='%ld'", account.account_id);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ jstrescapecpy(t_uid,server[account.account_id].name);
+ sprintf(tmpsql,"INSERT INTO `sstatus`(`index`,`name`,`user`) VALUES ( '%ld', '%s', '%d')",
+ account.account_id, server[account.account_id].name,0);
+ //query
+ if(mysql_query(&mysql_handle, tmpsql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ 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
+ send_GM_accounts(fd);
+ } else {
+ WFIFOW(fd, 0) =0x2711;
+ WFIFOB(fd, 2)=3;
+ WFIFOSET(fd, 3);
+ }
+ }
+ RFIFOSKIP(fd, 86);
+ return 0;
+
+ case 0x7530: // request Athena information
+ 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);
+ ShowInfo ("Athena version check...\n");
+ break;
+
+ case 0x7532:
+ default:
+ ShowStatus ("End of connection (ip: %s)" RETCODE, ip);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ }
+
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ return 0;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)aMalloc(64);
+ command = (char *)aMalloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ ShowInfo("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ ShowInfo("Type of command: %s || Command: %s \n",type,command);
+
+ if(buf) aFree(buf);
+ if(type) aFree(type);
+ if(command) aFree(command);
+
+ return 0;
+}
+
+static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
+{
+ struct online_login_data *character= (struct online_login_data*)data;
+ if (character->char_server == -2) //Unknown server.. set them offline
+ remove_online_user(character->account_id);
+ else if (character->char_server < 0)
+ //Free data from players that have not been online for a while.
+ db_remove(online_db, key);
+ return 0;
+}
+
+static int online_data_cleanup(int tid, unsigned int tick, int id, int data)
+{
+ online_db->foreach(online_db, online_data_cleanup_sub);
+ return 0;
+}
+
+//-------------------------------------------------
+// Return numerical value of a switch configuration
+// on/off, english, français, deutsch, español
+//-------------------------------------------------
+int config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+
+ return atoi(str);
+}
+
+
+//Lan Support conf reading added by Kashy
+int login_lan_config_read(const char *lancfgName){
+ int i;
+ char subnetmask[128];
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp=fopen(lancfgName, "r");
+
+ if (fp == NULL) {
+ ShowError("file not found: %s\n", lancfgName);
+ return 1;
+ }
+ ShowInfo("reading configuration file %s...\n", lancfgName);
+ while(fgets(line, sizeof(line)-1, fp)){
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ i = sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+
+ else if(strcmpi(w1,"lan_char_ip")==0){
+ strcpy(lan_char_ip, w2);
+ ShowStatus("set Lan_Char_IP : %s\n",w2);
+ }
+
+ else if(strcmpi(w1,"subnetmask")==0){
+ unsigned int k0, k1, k2, k3;
+
+ strcpy(subnetmask, w2);
+ sscanf(subnetmask, "%d.%d.%d.%d", &k0, &k1, &k2, &k3);
+ subnetmaski[0] = k0; subnetmaski[1] = k1; subnetmaski[2] = k2; subnetmaski[3] = k3;
+ ShowStatus("set subnetmask : %s\n",w2);
+ }
+ }
+ fclose(fp);
+
+ {
+ 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;
+ ShowInfo("LAN test of LAN IP of the char-server:\n");
+ if (lan_ip_check(p) == 0) {
+ ShowError(CL_RED" LAN IP of the char-server doesn't belong to the specified Sub-network"CL_RESET"\n");
+ }
+ }
+
+ ShowInfo("Finished reading %s.\n",lancfgName);
+
+ return 0;
+}
+
+//-----------------------------------------------------
+//BANNED IP CHECK.
+//-----------------------------------------------------
+int ip_ban_check(int tid, unsigned int tick, int id, int data){
+
+ //query
+ if(mysql_query(&mysql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()")) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------
+// reading configuration
+//-----------------------------------------------------
+int login_config_read(const char *cfgName){
+ int i;
+ struct hostent *h = NULL;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ fp=fopen(cfgName,"r");
+
+ if(fp==NULL){
+ ShowError("Configuration file (%s) not found.\n", cfgName);
+ return 1;
+ }
+ ShowInfo("reading configuration file %s...\n", cfgName);
+ while(fgets(line, sizeof(line)-1, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+
+ remove_control_chars((unsigned char *) w1);
+ remove_control_chars((unsigned char *) w2);
+ if(strcmpi(w1,"timestamp_format") == 0) {
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "bind_ip") == 0) {
+ bind_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowStatus("Login server binding 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(bind_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(bind_ip_str,w2,16);
+ } else if(strcmpi(w1,"login_port")==0){
+ login_port=atoi(w2);
+ ShowStatus("set login_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"ipban")==0){
+ ipban=atoi(w2);
+ ShowStatus("set ipban : %d\n",ipban);
+ }
+ //account ban -> ip ban
+ else if(strcmpi(w1,"dynamic_account_ban")==0){
+ dynamic_account_ban=atoi(w2);
+ ShowStatus("set dynamic_account_ban : %d\n",dynamic_account_ban);
+ }
+ else if(strcmpi(w1,"dynamic_account_ban_class")==0){
+ dynamic_account_ban_class=atoi(w2);
+ ShowStatus("set dynamic_account_ban_class : %d\n",dynamic_account_ban_class);
+ }
+ //dynamic password error ban
+ else if(strcmpi(w1,"dynamic_pass_failure_ban")==0){
+ dynamic_pass_failure_ban=atoi(w2);
+ ShowStatus("set dynamic_pass_failure_ban : %d\n",dynamic_pass_failure_ban);
+ }
+ else if(strcmpi(w1,"dynamic_pass_failure_ban_time")==0){
+ dynamic_pass_failure_ban_time=atoi(w2);
+ ShowStatus("set dynamic_pass_failure_ban_time : %d\n",dynamic_pass_failure_ban_time);
+ }
+ else if(strcmpi(w1,"dynamic_pass_failure_ban_how_many")==0){
+ dynamic_pass_failure_ban_how_many=atoi(w2);
+ ShowStatus("set dynamic_pass_failure_ban_how_many : %d\n",dynamic_pass_failure_ban_how_many);
+ }
+ else if(strcmpi(w1,"dynamic_pass_failure_ban_how_long")==0){
+ dynamic_pass_failure_ban_how_long=atoi(w2);
+ ShowStatus("set dynamic_pass_failure_ban_how_long : %d\n",dynamic_pass_failure_ban_how_long);
+ } else if(strcmpi(w1, "new_account") == 0){ //Added by Sirius for new account _M/_F
+ new_account_flag = atoi(w2); //Added by Sirius for new account _M/_F
+ } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check
+ //check_client_version = config_switch(w2); //Added by Sirius for client version check
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){
+ check_client_version = 1;
+ } else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){
+ check_client_version = 0;
+ }
+ } else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check
+ client_version_to_connect = atoi(w2); //Added by SIrius for client version check
+ } else if(strcmpi(w1,"use_MD5_passwords")==0){
+ if (!strcmpi(w2,"yes")) {
+ use_md5_passwds=1;
+ } else if (!strcmpi(w2,"no")){
+ use_md5_passwds=0;
+ }
+ ShowStatus("Using MD5 Passwords: %s \n",w2);
+ }
+ else if (strcmpi(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 (strcmpi(w1, "min_level_to_connect") == 0) {
+ min_level_to_connect = atoi(w2);
+ }
+ else if (strcmpi(w1, "check_ip_flag") == 0) {
+ check_ip_flag = config_switch(w2);
+ }
+ else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ }
+ else if (strcmpi(w1, "case_sensitive") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ case_sensitive = 1;
+ if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 )
+ case_sensitive = 0;
+ else
+ case_sensitive = atoi(w2);
+ } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin]
+ allowed_regs = atoi(w2);
+ } else if (strcmpi(w1, "time_allowed") == 0) {
+ time_allowed = atoi(w2);
+ } else if (strcmpi(w1, "online_check") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ online_check = 1;
+ else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 )
+ online_check = 0;
+ else
+ online_check = atoi(w2);
+ } else if (strcmpi(w1, "log_login") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ log_login = 1;
+ else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 )
+ log_login = 0;
+ else
+ log_login = atoi(w2);
+ } else if (strcmpi(w1, "import") == 0) {
+ login_config_read(w2);
+ }
+ }
+ fclose(fp);
+ ShowInfo("done reading %s.\n", cfgName);
+ return 0;
+}
+
+void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowFatalError("file not found: %s\n",cfgName);
+ exit(1);
+ }
+ ShowInfo("reading configuration file %s...\n", cfgName);
+ while(fgets(line, sizeof(line)-1, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+ if (strcmpi(w1, "login_db") == 0) {
+ strcpy(login_db, w2);
+ }
+ //add for DB connection
+ else if(strcmpi(w1,"login_server_ip")==0){
+ strcpy(login_server_ip, w2);
+ ShowStatus ("set login_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_port")==0){
+ login_server_port=atoi(w2);
+ ShowStatus ("set login_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_id")==0){
+ strcpy(login_server_id, w2);
+ ShowStatus ("set login_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_pw")==0){
+ strcpy(login_server_pw, w2);
+ ShowStatus ("set login_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"login_server_db")==0){
+ strcpy(login_server_db, w2);
+ ShowStatus ("set login_server_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"default_codepage")==0){
+ strcpy(default_codepage, w2);
+ ShowStatus ("set default_codepage : %s\n",w2);
+ }
+ //added for custom column names for custom login table
+ else if(strcmpi(w1,"login_db_account_id")==0){
+ strcpy(login_db_account_id, w2);
+ }
+ else if(strcmpi(w1,"login_db_userid")==0){
+ strcpy(login_db_userid, w2);
+ }
+ else if(strcmpi(w1,"login_db_user_pass")==0){
+ strcpy(login_db_user_pass, w2);
+ }
+ else if(strcmpi(w1,"login_db_level")==0){
+ strcpy(login_db_level, w2);
+ }
+ else if (strcmpi(w1, "loginlog_db") == 0) {
+ strcpy(loginlog_db, w2);
+ }
+ else if (strcmpi(w1, "lowest_gm_level") == 0) {
+ lowest_gm_level = atoi(w2);
+ }
+ else if (strcmpi(w1, "reg_db") == 0) {
+ strcpy(reg_db, w2);
+ }
+ //support the import command, just like any other config
+ else if(strcmpi(w1,"import")==0){
+ sql_config_read(w2);
+ }
+ }
+ fclose(fp);
+ ShowInfo("done reading %s.\n", cfgName);
+}
+
+//--------------------------------------
+// Function called at exit of the server
+//--------------------------------------
+void do_final(void) {
+ //sync account when terminating.
+ //but no need when you using DBMS (mysql)
+ mmo_db_close();
+ online_db->destroy(online_db, NULL);
+ if (gm_account_db)
+ aFree(gm_account_db);
+}
+
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_LOGIN;
+}
+int do_init(int argc,char **argv){
+ //initialize login server
+ int i;
+
+ //read login configue
+ login_config_read( (argc>1)?argv[1]:LOGIN_CONF_NAME );
+ sql_config_read(SQL_CONF_NAME);
+ login_lan_config_read((argc > 1) ? argv[1] : LAN_CONF_NAME);
+ //Generate Passworded Key.
+ ShowInfo("Initializing md5key...\n");
+ memset(md5key, 0, sizeof(md5key));
+ md5keylen=rand()%4+12;
+ for(i=0;i<md5keylen;i++)
+ md5key[i]=rand()%255+1;
+ ShowInfo("md5key setup complete\n");
+
+
+ ShowInfo("set FIFO Size\n");
+ for(i=0;i<AUTH_FIFO_SIZE;i++)
+ auth_fifo[i].delflag=1;
+ ShowInfo("set FIFO Size complete\n");
+
+ ShowInfo("set max servers\n");
+ for(i=0;i<MAX_SERVERS;i++)
+ server_fd[i]=-1;
+ ShowInfo("set max servers complete\n");
+ //server port open & binding
+
+ // Online user database init
+ online_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); // reinitialise
+ add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer");
+
+ //login_fd=make_listen_port(login_port);
+ if (bind_ip_set_)
+ login_fd = make_listen_bind(inet_addr(bind_ip_str),login_port);
+ else
+ login_fd = make_listen_bind(INADDR_ANY,login_port);
+
+ //Auth start
+ ShowInfo("Running mmo_auth_sqldb_init()\n");
+ mmo_auth_sqldb_init();
+ ShowInfo("finished mmo_auth_sqldb_init()\n");
+
+ //Read account information.
+ read_gm_account();
+
+ //set default parser as parse_login function
+ set_defaultparse(parse_login);
+
+ // ban deleter timer - 1 minute term
+ ShowStatus("add interval tic (ip_ban_check)....\n");
+ add_timer_func_list(ip_ban_check,"ip_ban_check");
+ add_timer_interval(gettick()+10, ip_ban_check,0,0,60*1000);
+
+ add_timer_func_list(online_data_cleanup, "online_data_cleanup");
+ add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000); // every 10 minutes cleanup online account db.
+
+ if (console) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", login_port);
+
+ return 0;
+}
diff --git a/src/login_sql/login.h b/src/login_sql/login.h
new file mode 100644
index 000000000..c031b26bf
--- /dev/null
+++ b/src/login_sql/login.h
@@ -0,0 +1,57 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOGIN_H_
+#define _LOGIN_H_
+
+#define MAX_SERVERS 30
+
+#define LOGIN_CONF_NAME "conf/login_athena.conf"
+#define SQL_CONF_NAME "conf/inter_athena.conf"
+#define LAN_CONF_NAME "conf/lan_support.conf"
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+#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 {
+ int version; //Added by sirius for versioncheck
+ char* userid;
+ char* passwd;
+ int passwdenc;
+
+
+ long account_id;
+ long login_id1;
+ long login_id2;
+ long char_id;
+ char lastlogin[24];
+ int sex;
+ int level; // added [zzo]
+};
+
+struct mmo_char_server {
+ char name[20];
+ long ip;
+ short port;
+ int users;
+ int maintenance;
+ int new_;
+};
+
+
+#endif
diff --git a/src/login_sql/make.sh b/src/login_sql/make.sh
new file mode 100644
index 000000000..3e43cd11f
--- /dev/null
+++ b/src/login_sql/make.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+ rsqlt=`rm -rf *.o`
+ gcc -c login.c -I/usr/local/include/mysql/
+ gcc -c md5calc.c -I/usr/local/include/mysql/
+ gcc -c strlib.c
+ gcc -o login-server login.o strlib.o md5calc.o ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o -L/usr/local/lib/mysql/ -lmysqlclient -lz
diff --git a/src/login_sql/md5calc.c b/src/login_sql/md5calc.c
new file mode 100644
index 000000000..704a94fd4
--- /dev/null
+++ b/src/login_sql/md5calc.c
@@ -0,0 +1,239 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+/***********************************************************
+ * md5 calculation algorithm
+ *
+ * The source code referred to the following URL.
+ * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html
+ *
+ ***********************************************************/
+
+#include "md5calc.h"
+#include <string.h>
+#include <stdio.h>
+
+#ifndef UINT_MAX
+#define UINT_MAX 4294967295U
+#endif
+
+// Global variable
+static unsigned int *pX;
+
+// Stirng Table
+static const unsigned int T[] = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60
+};
+
+// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC.
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+// The function used for other calculation
+static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Y) | (~X & Z);
+}
+static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return (X & Z) | (Y & ~Z);
+}
+static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return X ^ Y ^ Z;
+}
+static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
+{
+ return Y ^ (X | ~Z);
+}
+
+static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI,
+ unsigned int k, unsigned int s, unsigned int i)
+{
+ return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s);
+}
+
+static void Round1(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, F(b,c,d), k, s, i);
+}
+static void Round2(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, G(b,c,d), k, s, i);
+}
+static void Round3(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, H(b,c,d), k, s, i);
+}
+static void Round4(unsigned int *a, unsigned int b, unsigned int c,
+ unsigned int d,unsigned int k, unsigned int s, unsigned int i)
+{
+ *a = Round(*a, b, I(b,c,d), k, s, i);
+}
+
+static void MD5_Round_Calculate(const unsigned char *block,
+ unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2)
+{
+ //create X It is since it is required.
+ unsigned int X[16]; //512bit 64byte
+ int j,k;
+
+ //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D)
+ unsigned int A=*A2, B=*B2, C=*C2, D=*D2;
+ unsigned int AA = A,BB = B,CC = C,DD = D;
+
+ //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4
+ pX = X;
+
+ //Copy block(padding_message) i into X
+ for (j=0,k=0; j<64; j+=4,k++)
+ X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion
+ | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC
+ | ( ((unsigned int )block[j+2]) << 16 )
+ | ( ((unsigned int )block[j+3]) << 24 );
+
+
+ //Round 1
+ Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3);
+ Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7);
+ Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11);
+ Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15);
+
+ //Round 2
+ Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19);
+ Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23);
+ Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27);
+ Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31);
+
+ //Round 3
+ Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35);
+ Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39);
+ Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43);
+ Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47);
+
+ //Round 4
+ Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51);
+ Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55);
+ Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59);
+ Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63);
+
+ // Then perform the following additions. (let's add)
+ *A2 = A + AA;
+ *B2 = B + BB;
+ *C2 = C + CC;
+ *D2 = D + DD;
+
+ //The clearance of confidential information
+ memset(pX, 0, sizeof(X));
+}
+
+//-------------------------------------------------------------------
+// The function for the exteriors
+
+/** output is the coded binary in the character sequence which wants to code string. */
+void MD5_String2binary(const char * string, char * output)
+{
+//var
+ /*8bit*/
+ unsigned char padding_message[64]; //Extended message 512bit 64byte
+ unsigned char *pstring; //The position of string in the present scanning notes is held.
+
+// unsigned char digest[16];
+ /*32bit*/
+ unsigned int string_byte_len, //The byte chief of string is held.
+ string_bit_len, //The bit length of string is held.
+ copy_len, //The number of bytes which is used by 1-3 and which remained
+ msg_digest[4]; //Message digest 128bit 4byte
+ unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference)
+ *B = &msg_digest[1],
+ *C = &msg_digest[2],
+ *D = &msg_digest[3];
+ int i;
+
+//prog
+ //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head)
+ *A = 0x67452301;
+ *B = 0xefcdab89;
+ *C = 0x98badcfe;
+ *D = 0x10325476;
+
+ //Step 1.Append Padding Bits (extension of a mark bit)
+ //1-1
+ string_byte_len = strlen(string); //The byte chief of a character sequence is acquired.
+ pstring = (unsigned char *)string; //The position of the present character sequence is set.
+
+ //1-2 Repeat calculation until length becomes less than 64 bytes.
+ for (i=string_byte_len; 64<=i; i-=64,pstring+=64)
+ MD5_Round_Calculate(pstring, A,B,C,D);
+
+ //1-3
+ copy_len = string_byte_len % 64; //The number of bytes which remained is computed.
+ strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence.
+ memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length.
+ padding_message[copy_len] |= 0x80; //The next of a message is 1.
+
+ //1-4
+ //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes.
+ if (56 <= copy_len) {
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+ memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0.
+ }
+
+
+ //Step 2.Append Length (the information on length is added)
+ string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank)
+ memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set.
+
+ //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank.
+ if (UINT_MAX / 8 < string_byte_len) {
+ unsigned int high = (string_byte_len - UINT_MAX / 8) * 8;
+ memcpy(&padding_message[60], &high, 4);
+ } else
+ memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0.
+
+ //Step 4.Process Message in 16-Word Blocks (calculation of MD5)
+ MD5_Round_Calculate(padding_message, A,B,C,D);
+
+
+ //Step 5.Output (output)
+ memcpy(output,msg_digest,16);
+// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC
+/* sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);*/
+}
+
+/** output is the coded character sequence in the character sequence which wants to code string. */
+void MD5_String(const char * string, char * output)
+{
+ unsigned char digest[16];
+
+ MD5_String2binary(string,(char*)digest);
+ sprintf(output,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[ 0], digest[ 1], digest[ 2], digest[ 3],
+ digest[ 4], digest[ 5], digest[ 6], digest[ 7],
+ digest[ 8], digest[ 9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+}
+
diff --git a/src/login_sql/md5calc.h b/src/login_sql/md5calc.h
new file mode 100644
index 000000000..b3735788c
--- /dev/null
+++ b/src/login_sql/md5calc.h
@@ -0,0 +1,10 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MD5CALC_H_
+#define _MD5CALC_H_
+
+void MD5_String(const char * string, char * output);
+void MD5_String2binary(const char * string, char * output);
+
+#endif
diff --git a/src/map/Makefile b/src/map/Makefile
new file mode 100644
index 000000000..f3073d476
--- /dev/null
+++ b/src/map/Makefile
@@ -0,0 +1,99 @@
+all txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+COMMON_OBJ = ../common/obj/core.o ../common/obj/socket.o ../common/obj/timer.o \
+ ../common/obj/db.o ../common/obj/plugins.o ../common/obj/lock.o \
+ ../common/obj/nullpo.o ../common/obj/malloc.o ../common/obj/showmsg.o \
+ ../common/obj/utils.o ../common/obj/strlib.o ../common/obj/grfio.o \
+ ../common/obj/graph.o ../common/obj/mapindex.o ../common/obj/ers.o \
+ ../zlib/unz.o
+
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h \
+ ../common/plugins.h ../common/lock.h ../common/nullpo.h ../common/malloc.h \
+ ../common/showmsg.h ../common/utils.h ../common/strlib.h ../common/grfio.h \
+ ../common/graph.h ../common/mapindex.h
+
+OBJECTS = obj/map.o obj/chrif.o obj/clif.o obj/pc.o obj/status.o obj/npc.o \
+ obj/npc_chat.o obj/chat.o obj/path.o obj/itemdb.o obj/mob.o obj/script.o \
+ obj/storage.o obj/skill.o obj/atcommand.o obj/charcommand.o obj/battle.o \
+ obj/intif.o obj/trade.o obj/party.o obj/vending.o obj/guild.o obj/pet.o \
+ obj/log.o obj/mail.o obj/charsave.o obj/date.o $(COMMON_OBJ)
+
+map-server: $(OBJECTS:obj/%=txtobj/%)
+ $(CC) -o ../../$@ $> $(LIBS) $(LIB_S)
+
+map-server_sql: $(OBJECTS:obj/%=sqlobj/%)
+ $(CC) -o ../../$@ $> $(LIB_S)
+
+txtobj/%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+sqlobj/%.o: %.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
+
+# DO NOT DELETE
+
+txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h $(COMMON_H)
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h $(COMMON_H)
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h charcommand.h $(COMMON_H)
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h $(COMMON_H)
+txtobj/status.o: status.c pc.h map.h clif.h status.h mob.h itemdb.h battle.h skill.h script.h pet.h guild.h $(COMMON_H)
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+txtobj/npc_chat.o: npc_chat.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h $(COMMON_H)
+txtobj/path.o: path.c map.h battle.h $(COMMON_H)
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h $(COMMON_H)
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h date.h $(COMMON_H)
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h $(COMMON_H)
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h $(COMMON_H)
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h date.h $(COMMON_H)
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h $(COMMON_H)
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h $(COMMON_H)
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h $(COMMON_H)
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h $(COMMON_H)
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h $(COMMON_H)
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h $(COMMON_H)
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h $(COMMON_H)
+txtobj/log.o: log.c log.h map.h $(COMMON_H)
+txtobj/charcommand.o: charcommand.c charcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+txtobj/date.o: date.c date.h $(COMMON_H)
+
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h $(COMMON_H)
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h $(COMMON_H)
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h charcommand.h $(COMMON_H)
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h $(COMMON_H)
+sqlobj/status.o: status.c pc.h map.h clif.h status.h mob.h itemdb.h battle.h skill.h script.h pet.h guild.h $(COMMON_H)
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+sqlobj/npc_chat.o: npc_chat.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h $(COMMON_H)
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h $(COMMON_H)
+sqlobj/path.o: path.c map.h battle.h $(COMMON_H)
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h $(COMMON_H)
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h date.h $(COMMON_H)
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h $(COMMON_H)
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h $(COMMON_H)
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h date.h $(COMMON_H)
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h $(COMMON_H)
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h $(COMMON_H)
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h $(COMMON_H)
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h $(COMMON_H)
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h $(COMMON_H)
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h $(COMMON_H)
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h $(COMMON_H)
+sqlobj/mail.o: mail.c mail.h $(COMMON_H)
+sqlobj/log.o: log.c log.h map.h $(COMMON_H)
+sqlobj/charcommand.o: charcommand.c charcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h $(COMMON_H)
+sqlobj/charsave.o: charsave.c charsave.h $(COMMON_H)
+sqlobj/date.o: date.c date.h $(COMMON_H)
diff --git a/src/map/Makefile.win32 b/src/map/Makefile.win32
new file mode 100644
index 000000000..4e1be4cc5
--- /dev/null
+++ b/src/map/Makefile.win32
@@ -0,0 +1,99 @@
+# grab a copy of http://www.winimage.com/zLibDll/zlib122.zip
+# and put safely into a subdirectory someplace
+# then point ZLIBDIR at whereever you put it
+#
+
+all: txt sql
+
+txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+ZLIBDIR = ../zlib
+PACKETDEF = -DPACKETVER=6 -DNEW_006b -D__WIN32 -DLOCALZLIB
+# OPT = /MDd /D_DEBUG
+OPT =
+LINKOPT = /debug /SUBSYSTEM:CONSOLE
+# OPT = /O2
+CFLAGS = $(OPT) /nologo /I../common /I$(ZLIBDIR) $(PACKETDEF) /D_WIN32
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/grfio.o ../common/db.o ../common/lock.o ../common/nullpo.o ../common/malloc.o ../common/showmsg.o ../common/strlib.o ../common/utils.o
+
+LIBS = "WSOCK32.LIB"
+
+# "WSOCK32.LIB" "USER32.LIB" "ADVAPI32.LIB" "MSVCRT.LIB" "OLDNAMES.LIB" "KERNEL32.LIB"
+
+TXTOBJS = txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/status.o txtobj/npc.o txtobj/npc_chat.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/charcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o txtobj/log.o txtobj/date.o $(COMMON_OBJ) $(ZLIBDIR)/inflate.o $(ZLIBDIR)/deflate.o $(ZLIBDIR)/trees.o $(ZLIBDIR)/adler32.o $(ZLIBDIR)/compress.o $(ZLIBDIR)/crc32.o $(ZLIBDIR)/inftrees.o $(ZLIBDIR)/zutil.o $(ZLIBDIR)/inffast.o
+
+SQLOBJS = sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/status.o sqlobj/npc.o sqlobj/npc_chat.o sqlobj/chat.o sqlobj/path.o sqlobj/itemdb.o sqlobj/mob.o sqlobj/script.o sqlobj/storage.o sqlobj/skill.o sqlobj/atcommand.o sqlobj/charcommand.o sqlobj/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/log.o sqlobj/date.o sqlobj/charsave.o $(COMMON_OBJ) $(ZLIBDIR)/inflate.o $(ZLIBDIR)/adler32.o $(ZLIBDIR)/crc32.o $(ZLIBDIR)/inftrees.o $(ZLIBDIR)/zutil.o $(ZLIBDIR)/inffast.o
+
+map-server: $(TXTOBJS)
+ link $(LINKOPT) /out:../../$@.exe $(TXTOBJS) $(LIBS)
+
+map-server_sql: $(SQLOBJS)
+ link $(LINKOPT) /out:../../$@.exe $> $(LIBS)
+
+txtobj/%.o: %.c
+ Cl /c $(CFLAGS) -DTXT_ONLY /Fo$@ $<
+
+sqlobj/%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h date.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h date.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.h ../common/showmsg.h
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.h ../common/showmsg.h
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/date.o: date.c date.h ../common/timer.h
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h date.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h date.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h log.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/date.o: date.c date.h ../common/timer.h
+sqlobj/mail.o: mail.c mail.h ../common/showmsg.h ../common/strlib.h ../common/utils.h
+sqlobj/log.o: log.c log.h map.h ../common/nullpo.h
+sqlobj/charsave.o: charsave.c charsave.h $(COMMON_H)
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
new file mode 100644
index 000000000..6d2d3cc2b
--- /dev/null
+++ b/src/map/atcommand.c
@@ -0,0 +1,10039 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/core.h"
+#include "../common/showmsg.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "script.h"
+#include "npc.h"
+#include "trade.h"
+
+#ifndef TXT_ONLY
+#include "mail.h"
+#endif
+
+static char command_symbol = '@'; // first char of the commands (by [Yor])
+
+char *msg_table[MAX_MSG]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+#define ACMD_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
+ACMD_FUNC(broadcast);
+ACMD_FUNC(localbroadcast);
+ACMD_FUNC(rura);
+ACMD_FUNC(where);
+ACMD_FUNC(jumpto);
+ACMD_FUNC(jump);
+ACMD_FUNC(who);
+ACMD_FUNC(who2);
+ACMD_FUNC(who3);
+ACMD_FUNC(whomap);
+ACMD_FUNC(whomap2);
+ACMD_FUNC(whomap3);
+ACMD_FUNC(whogm); // by Yor
+ACMD_FUNC(whozeny); // [Valaris]
+ACMD_FUNC(happyhappyjoyjoy); // [Valaris]
+ACMD_FUNC(save);
+ACMD_FUNC(load);
+ACMD_FUNC(speed);
+ACMD_FUNC(storage);
+ACMD_FUNC(guildstorage);
+ACMD_FUNC(option);
+ACMD_FUNC(hide);
+ACMD_FUNC(jobchange);
+ACMD_FUNC(die);
+ACMD_FUNC(kill);
+ACMD_FUNC(alive);
+ACMD_FUNC(kami);
+ACMD_FUNC(heal);
+ACMD_FUNC(item);
+ACMD_FUNC(item2);
+ACMD_FUNC(itemreset);
+ACMD_FUNC(itemcheck);
+ACMD_FUNC(baselevelup);
+ACMD_FUNC(joblevelup);
+ACMD_FUNC(help);
+ACMD_FUNC(help2);
+ACMD_FUNC(gm);
+ACMD_FUNC(pvpoff);
+ACMD_FUNC(pvpon);
+ACMD_FUNC(gvgoff);
+ACMD_FUNC(gvgon);
+ACMD_FUNC(model);
+ACMD_FUNC(go);
+ACMD_FUNC(monster);
+ACMD_FUNC(monstersmall);
+ACMD_FUNC(monsterbig);
+ACMD_FUNC(spawn);
+ACMD_FUNC(killmonster);
+ACMD_FUNC(killmonster2);
+ACMD_FUNC(refine);
+ACMD_FUNC(produce);
+ACMD_FUNC(memo);
+ACMD_FUNC(gat);
+ACMD_FUNC(packet);
+ACMD_FUNC(waterlevel);
+ACMD_FUNC(statuspoint);
+ACMD_FUNC(skillpoint);
+ACMD_FUNC(zeny);
+ACMD_FUNC(param);
+ACMD_FUNC(guildlevelup);
+ACMD_FUNC(makeegg);
+ACMD_FUNC(hatch);
+ACMD_FUNC(petfriendly);
+ACMD_FUNC(pethungry);
+ACMD_FUNC(petrename);
+ACMD_FUNC(recall);
+ACMD_FUNC(recallall);
+ACMD_FUNC(revive);
+ACMD_FUNC(night);
+ACMD_FUNC(day);
+ACMD_FUNC(doom);
+ACMD_FUNC(doommap);
+ACMD_FUNC(raise);
+ACMD_FUNC(raisemap);
+ACMD_FUNC(kick);
+ACMD_FUNC(kickall);
+ACMD_FUNC(allskill);
+ACMD_FUNC(questskill);
+ACMD_FUNC(lostskill);
+ACMD_FUNC(spiritball);
+ACMD_FUNC(party);
+ACMD_FUNC(guild);
+ACMD_FUNC(agitstart);
+ACMD_FUNC(agitend);
+ACMD_FUNC(reloaditemdb);
+ACMD_FUNC(reloadmobdb);
+ACMD_FUNC(reloadskilldb);
+ACMD_FUNC(reloadscript);
+ACMD_FUNC(reloadgmdb); // by Yor
+ACMD_FUNC(reloadatcommand);
+ACMD_FUNC(reloadbattleconf);
+ACMD_FUNC(reloadstatusdb);
+ACMD_FUNC(reloadpcdb);
+ACMD_FUNC(reloadmotd); // [Valaris]
+ACMD_FUNC(mapexit);
+ACMD_FUNC(idsearch);
+ACMD_FUNC(mapinfo);
+ACMD_FUNC(dye); //** by fritz
+ACMD_FUNC(hair_style); //** by fritz
+ACMD_FUNC(hair_color); //** by fritz
+ACMD_FUNC(stat_all); //** by fritz
+ACMD_FUNC(char_block); // by Yor
+ACMD_FUNC(char_ban); // by Yor
+ACMD_FUNC(char_unblock); // by Yor
+ACMD_FUNC(char_unban); // by Yor
+ACMD_FUNC(mount_peco); // by Valaris
+ACMD_FUNC(char_mount_peco); // by Yor
+ACMD_FUNC(guildspy); // [Syrus22]
+ACMD_FUNC(partyspy); // [Syrus22]
+ACMD_FUNC(repairall); // [Valaris]
+ACMD_FUNC(guildrecall); // by Yor
+ACMD_FUNC(partyrecall); // by Yor
+ACMD_FUNC(nuke); // [Valaris]
+ACMD_FUNC(shownpc);
+ACMD_FUNC(hidenpc);
+ACMD_FUNC(loadnpc);
+ACMD_FUNC(unloadnpc);
+ACMD_FUNC(servertime); // by Yor
+ACMD_FUNC(chardelitem); // by Yor
+ACMD_FUNC(jail); // by Yor
+ACMD_FUNC(unjail); // by Yor
+ACMD_FUNC(disguise); // [Valaris]
+ACMD_FUNC(undisguise); // by Yor
+ACMD_FUNC(chardisguise); // Kalaspuff
+ACMD_FUNC(charundisguise); // Kalaspuff
+ACMD_FUNC(email); // by Yor
+ACMD_FUNC(effect);//by Apple
+ACMD_FUNC(character_cart_list); // by Yor
+ACMD_FUNC(addwarp); // by MouseJstr
+ACMD_FUNC(follow); // by MouseJstr
+ACMD_FUNC(skillon); // by MouseJstr
+ACMD_FUNC(skilloff); // by MouseJstr
+ACMD_FUNC(killer); // by MouseJstr
+ACMD_FUNC(npcmove); // by MouseJstr
+ACMD_FUNC(killable); // by MouseJstr
+ACMD_FUNC(charkillable); // by MouseJstr
+ACMD_FUNC(dropall); // by MouseJstr
+ACMD_FUNC(chardropall); // by MouseJstr
+ACMD_FUNC(storeall); // by MouseJstr
+ACMD_FUNC(charstoreall); // by MouseJstr
+ACMD_FUNC(skillid); // by MouseJstr
+ACMD_FUNC(useskill); // by MouseJstr
+ACMD_FUNC(summon);
+ACMD_FUNC(rain);
+ACMD_FUNC(snow);
+ACMD_FUNC(sakura);
+ACMD_FUNC(clouds);
+ACMD_FUNC(clouds2); // [Valaris]
+ACMD_FUNC(fog);
+ACMD_FUNC(fireworks);
+ACMD_FUNC(leaves);
+ACMD_FUNC(adjgmlvl); // by MouseJstr
+ACMD_FUNC(adjcmdlvl); // by MouseJstr
+ACMD_FUNC(trade); // by MouseJstr
+ACMD_FUNC(send); // by davidsiaw
+ACMD_FUNC(setbattleflag); // by MouseJstr
+ACMD_FUNC(unmute); // [Valaris]
+ACMD_FUNC(clearweather); // Dexity
+ACMD_FUNC(uptime); // by MC Cameri
+ACMD_FUNC(changesex); // by MC Cameri
+ACMD_FUNC(mute); // celest
+ACMD_FUNC(refresh); // by MC Cameri
+ACMD_FUNC(petid); // by MC Cameri
+ACMD_FUNC(identify); // by MC Cameri
+ACMD_FUNC(gmotd); // Added by MC Cameri, created by davidsiaw
+ACMD_FUNC(misceffect); // by MC Cameri
+ACMD_FUNC(mobsearch);
+ACMD_FUNC(cleanmap);
+ACMD_FUNC(npctalk);
+ACMD_FUNC(pettalk);
+ACMD_FUNC(users);
+ACMD_FUNC(autoloot); // Improved version imported from Freya.
+
+#ifndef TXT_ONLY
+ACMD_FUNC(checkmail); // [Valaris]
+ACMD_FUNC(listmail); // [Valaris]
+ACMD_FUNC(listnewmail); // [Valaris]
+ACMD_FUNC(readmail); // [Valaris]
+ACMD_FUNC(sendmail); // [Valaris]
+ACMD_FUNC(sendprioritymail); // [Valaris]
+ACMD_FUNC(deletemail); // [Valaris]
+ACMD_FUNC(refreshonline); // [Valaris]
+#endif /* TXT_ONLY */
+
+ACMD_FUNC(skilltree); // by MouseJstr
+
+ACMD_FUNC(marry); // by MouseJstr
+ACMD_FUNC(divorce); // by MouseJstr
+
+ACMD_FUNC(grind); // by MouseJstr
+ACMD_FUNC(grind2); // by MouseJstr
+
+#ifdef DMALLOC
+ACMD_FUNC(dmstart); // by MouseJstr
+ACMD_FUNC(dmtick); // by MouseJstr
+#endif
+
+ACMD_FUNC(jumptoid); // by Dino9021
+ACMD_FUNC(jumptoid2); // by Dino9021
+ACMD_FUNC(recallid); // by Dino9021
+ACMD_FUNC(recallid2); // by Dino9021
+ACMD_FUNC(kickid); // by Dino9021
+ACMD_FUNC(kickid2); // by Dino9021
+ACMD_FUNC(reviveid); // by Dino9021
+ACMD_FUNC(reviveid2); // by Dino9021
+ACMD_FUNC(killid); // by Dino9021
+ACMD_FUNC(killid2); // by Dino9021
+ACMD_FUNC(charkillableid); // by Dino9021
+ACMD_FUNC(charkillableid2); // by Dino9021
+ACMD_FUNC(sound);
+ACMD_FUNC(undisguiseall);
+ACMD_FUNC(disguiseall);
+ACMD_FUNC(changelook);
+ACMD_FUNC(mobinfo); //by Lupus
+ACMD_FUNC(adopt); // by Veider
+
+ACMD_FUNC(version); // by Ancyker
+
+ACMD_FUNC(mutearea); // by MouseJstr
+ACMD_FUNC(shuffle); // by MouseJstr
+ACMD_FUNC(rates); // by MouseJstr
+
+ACMD_FUNC(iteminfo); // Lupus
+ACMD_FUNC(mapflag); // Lupus
+ACMD_FUNC(me); //added by massdriller, code by lordalfa
+ACMD_FUNC(monsterignore); // [Valaris]
+ACMD_FUNC(fakename); //[Valaris]
+ACMD_FUNC(size); //[Valaris]
+ACMD_FUNC(showexp); //moved from charcommand [Kevin]
+ACMD_FUNC(showzeny);
+ACMD_FUNC(showdelay); //moved from charcommand [Kevin]
+ACMD_FUNC(autotrade);// durf
+ACMD_FUNC(changeleader);// [Skotlex]
+ACMD_FUNC(changegm);// durf
+
+// Duel [LuzZza]
+ACMD_FUNC(invite);
+ACMD_FUNC(duel);
+ACMD_FUNC(leave);
+ACMD_FUNC(accept);
+ACMD_FUNC(reject);
+
+ACMD_FUNC(away); // LuzZza
+ACMD_FUNC(main); // LuzZza
+
+ACMD_FUNC(clone); // [Valaris]
+
+/*==========================================
+ *AtCommandInfo atcommand_info[]\‘¢‘Ì‚Ì’è‹`
+ *------------------------------------------
+ */
+
+// First char of commands is configured in atcommand_athena.conf. Leave @ in this list for default value.
+// to set default level, read atcommand_athena.conf first please.
+static AtCommandInfo atcommand_info[] = {
+ { AtCommand_Rura, "@rura", 40, atcommand_rura },
+ { AtCommand_Warp, "@warp", 40, atcommand_rura },
+ { AtCommand_Where, "@where", 1, atcommand_where },
+ { AtCommand_JumpTo, "@jumpto", 20, atcommand_jumpto }, // + /shift
+ { AtCommand_JumpTo, "@warpto", 20, atcommand_jumpto },
+ { AtCommand_JumpTo, "@goto", 20, atcommand_jumpto },
+ { AtCommand_Jump, "@jump", 40, atcommand_jump },
+ { AtCommand_Who, "@who", 20, atcommand_who },
+ { AtCommand_Who, "@whois", 20, atcommand_who },
+ { AtCommand_Who, "@w", 20, atcommand_who },
+ { AtCommand_Who2, "@who2", 20, atcommand_who2 },
+ { AtCommand_Who3, "@who3", 20, atcommand_who3 },
+ { AtCommand_WhoMap, "@whomap", 20, atcommand_whomap },
+ { AtCommand_WhoMap2, "@whomap2", 20, atcommand_whomap2 },
+ { AtCommand_WhoMap3, "@whomap3", 20, atcommand_whomap3 },
+ { AtCommand_WhoGM, "@whogm", 20, atcommand_whogm }, // by Yor
+ { AtCommand_Save, "@save", 40, atcommand_save },
+ { AtCommand_Load, "@return", 40, atcommand_load },
+ { AtCommand_Load, "@load", 40, atcommand_load },
+ { AtCommand_Speed, "@speed", 40, atcommand_speed },
+ { AtCommand_Storage, "@storage", 1, atcommand_storage },
+ { AtCommand_GuildStorage, "@gstorage", 50, atcommand_guildstorage },
+ { AtCommand_Option, "@option", 40, atcommand_option },
+ { AtCommand_Hide, "@hide", 40, atcommand_hide }, // + /hide
+ { AtCommand_JobChange, "@jobchange", 40, atcommand_jobchange },
+ { AtCommand_JobChange, "@job", 40, atcommand_jobchange },
+ { AtCommand_Die, "@die", 1, atcommand_die },
+ { AtCommand_Kill, "@kill", 60, atcommand_kill },
+ { AtCommand_Alive, "@alive", 60, atcommand_alive },
+ { AtCommand_Kami, "@kami", 40, atcommand_kami },
+ { AtCommand_KamiB, "@kamib", 40, atcommand_kami },
+ { AtCommand_KamiC, "@kamic", 40, atcommand_kami }, //[LuzZza]
+ { AtCommand_Heal, "@heal", 40, atcommand_heal },
+ { AtCommand_Item, "@item", 60, atcommand_item },
+ { AtCommand_Item2, "@item2", 60, atcommand_item2 },
+ { AtCommand_ItemReset, "@itemreset", 40, atcommand_itemreset },
+ { AtCommand_ItemCheck, "@itemcheck", 60, atcommand_itemcheck },
+ { AtCommand_BaseLevelUp, "@lvup", 60, atcommand_baselevelup },
+ { AtCommand_BaseLevelUp, "@blevel", 60, atcommand_baselevelup },
+ { AtCommand_BaseLevelUp, "@baselvlup", 60, atcommand_baselevelup },
+ { AtCommand_JobLevelUp, "@jlevel", 60, atcommand_joblevelup },
+ { AtCommand_JobLevelUp, "@joblvup", 60, atcommand_joblevelup },
+ { AtCommand_JobLevelUp, "@joblvlup", 60, atcommand_joblevelup },
+ { AtCommand_H, "@h", 20, atcommand_help },
+ { AtCommand_Help, "@help", 20, atcommand_help },
+ { AtCommand_H2, "@h2", 20, atcommand_help2 },
+ { AtCommand_Help2, "@help2", 20, atcommand_help2 },
+ { AtCommand_GM, "@gm", 100,atcommand_gm },
+ { AtCommand_PvPOff, "@pvpoff", 40, atcommand_pvpoff },
+ { AtCommand_PvPOn, "@pvpon", 40, atcommand_pvpon },
+ { AtCommand_GvGOff, "@gvgoff", 40, atcommand_gvgoff },
+ { AtCommand_GvGOff, "@gpvpoff", 40, atcommand_gvgoff },
+ { AtCommand_GvGOn, "@gvgon", 40, atcommand_gvgon },
+ { AtCommand_GvGOn, "@gpvpon", 40, atcommand_gvgon },
+ { AtCommand_Model, "@model", 20, atcommand_model },
+ { AtCommand_Go, "@go", 10, atcommand_go },
+ { AtCommand_Spawn, "@monster", 50, atcommand_monster },
+ { AtCommand_Spawn, "@spawn", 50, atcommand_monster },
+ { AtCommand_MonsterSmall, "@monstersmall", 50, atcommand_monstersmall },
+ { AtCommand_MonsterBig, "@monsterbig", 50, atcommand_monsterbig },
+ { AtCommand_KillMonster, "@killmonster", 60, atcommand_killmonster },
+ { AtCommand_KillMonster2, "@killmonster2", 40, atcommand_killmonster2 },
+ { AtCommand_Refine, "@refine", 60, atcommand_refine },
+ { AtCommand_Produce, "@produce", 60, atcommand_produce },
+ { AtCommand_Memo, "@memo", 40, atcommand_memo },
+ { AtCommand_GAT, "@gat", 99, atcommand_gat }, // debug function
+ { AtCommand_Packet, "@packet", 99, atcommand_packet }, // debug function
+ { AtCommand_Packet, "@packetmode", 99, atcommand_packet }, // debug function
+ { AtCommand_WaterLevel, "@waterlevel", 99, atcommand_waterlevel }, // debug function
+ { AtCommand_StatusPoint, "@stpoint", 60, atcommand_statuspoint },
+ { AtCommand_SkillPoint, "@skpoint", 60, atcommand_skillpoint },
+ { AtCommand_Zeny, "@zeny", 60, atcommand_zeny },
+ { AtCommand_Strength, "@str", 60, atcommand_param },
+ { AtCommand_Agility, "@agi", 60, atcommand_param },
+ { AtCommand_Vitality, "@vit", 60, atcommand_param },
+ { AtCommand_Intelligence, "@int", 60, atcommand_param },
+ { AtCommand_Dexterity, "@dex", 60, atcommand_param },
+ { AtCommand_Luck, "@luk", 60, atcommand_param },
+ { AtCommand_GuildLevelUp, "@guildlvup", 60, atcommand_guildlevelup },
+ { AtCommand_GuildLevelUp, "@guildlvlup", 60, atcommand_guildlevelup },
+ { AtCommand_MakeEgg, "@makeegg", 60, atcommand_makeegg },
+ { AtCommand_Hatch, "@hatch", 60, atcommand_hatch },
+ { AtCommand_PetFriendly, "@petfriendly", 40, atcommand_petfriendly },
+ { AtCommand_PetHungry, "@pethungry", 40, atcommand_pethungry },
+ { AtCommand_PetRename, "@petrename", 1, atcommand_petrename },
+ { AtCommand_Recall, "@recall", 60, atcommand_recall }, // + /recall
+ { AtCommand_Revive, "@revive", 60, atcommand_revive },
+ { AtCommand_Night, "@night", 80, atcommand_night },
+ { AtCommand_Day, "@day", 80, atcommand_day },
+ { AtCommand_Doom, "@doom", 80, atcommand_doom },
+ { AtCommand_DoomMap, "@doommap", 80, atcommand_doommap },
+ { AtCommand_Raise, "@raise", 80, atcommand_raise },
+ { AtCommand_RaiseMap, "@raisemap", 80, atcommand_raisemap },
+ { AtCommand_Kick, "@kick", 20, atcommand_kick }, // + right click menu for GM "(name) force to quit"
+ { AtCommand_KickAll, "@kickall", 99, atcommand_kickall },
+ { AtCommand_AllSkill, "@allskill", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@allskills", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@skillall", 60, atcommand_allskill },
+ { AtCommand_AllSkill, "@skillsall", 60, atcommand_allskill },
+ { AtCommand_QuestSkill, "@questskill", 40, atcommand_questskill },
+ { AtCommand_LostSkill, "@lostskill", 40, atcommand_lostskill },
+ { AtCommand_SpiritBall, "@spiritball", 40, atcommand_spiritball },
+ { AtCommand_Party, "@party", 1, atcommand_party },
+ { AtCommand_Guild, "@guild", 50, atcommand_guild },
+ { AtCommand_AgitStart, "@agitstart", 60, atcommand_agitstart },
+ { AtCommand_AgitEnd, "@agitend", 60, atcommand_agitend },
+ { AtCommand_MapExit, "@mapexit", 99, atcommand_mapexit },
+ { AtCommand_IDSearch, "@idsearch", 60, atcommand_idsearch },
+ { AtCommand_MapMove, "@mapmove", 40, atcommand_rura }, // /mm command
+ { AtCommand_Broadcast, "@broadcast", 40, atcommand_broadcast }, // /b and /nb command
+ { AtCommand_LocalBroadcast, "@localbroadcast", 40, atcommand_localbroadcast }, // /lb and /nlb command
+ { AtCommand_RecallAll, "@recallall", 80, atcommand_recallall },
+ { AtCommand_ReloadItemDB, "@reloaditemdb", 99, atcommand_reloaditemdb }, // admin command
+ { AtCommand_ReloadMobDB, "@reloadmobdb", 99, atcommand_reloadmobdb }, // admin command
+ { AtCommand_ReloadSkillDB, "@reloadskilldb", 99, atcommand_reloadskilldb }, // admin command
+ { AtCommand_ReloadScript, "@reloadscript", 99, atcommand_reloadscript }, // admin command
+ { AtCommand_ReloadGMDB, "@reloadgmdb", 99, atcommand_reloadgmdb }, // admin command
+ { AtCommand_ReloadAtcommand, "@reloadatcommand", 99, atcommand_reloadatcommand },
+ { AtCommand_ReloadBattleConf, "@reloadbattleconf", 99, atcommand_reloadbattleconf },
+ { AtCommand_ReloadStatusDB, "@reloadstatusdb", 99, atcommand_reloadstatusdb },
+ { AtCommand_ReloadPcDB, "@reloadpcdb", 99, atcommand_reloadpcdb },
+ { AtCommand_ReloadMOTD, "@reloadmotd", 99, atcommand_reloadmotd }, // [Valaris]
+ { AtCommand_MapInfo, "@mapinfo", 99, atcommand_mapinfo },
+ { AtCommand_Dye, "@dye", 40, atcommand_dye }, // by fritz
+ { AtCommand_Dye, "@ccolor", 40, atcommand_dye }, // by fritz
+ { AtCommand_Hstyle, "@hairstyle", 40, atcommand_hair_style }, // by fritz
+ { AtCommand_Hstyle, "@hstyle", 40, atcommand_hair_style }, // by fritz
+ { AtCommand_Hcolor, "@haircolor", 40, atcommand_hair_color }, // by fritz
+ { AtCommand_Hcolor, "@hcolor", 40, atcommand_hair_color }, // by fritz
+ { AtCommand_StatAll, "@statall", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_StatAll, "@statsall", 60, atcommand_stat_all },
+ { AtCommand_StatAll, "@allstats", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_StatAll, "@allstat", 60, atcommand_stat_all }, // by fritz
+ { AtCommand_CharBlock, "@block", 60, atcommand_char_block }, // by Yor
+ { AtCommand_CharBlock, "@charblock", 60, atcommand_char_block }, // by Yor
+ { AtCommand_CharBan, "@ban", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@banish", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@charban", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharBan, "@charbanish", 60, atcommand_char_ban }, // by Yor
+ { AtCommand_CharUnBlock, "@unblock", 60, atcommand_char_unblock }, // by Yor
+ { AtCommand_CharUnBlock, "@charunblock", 60, atcommand_char_unblock }, // by Yor
+ { AtCommand_CharUnBan, "@unban", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@unbanish", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@charunban", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_CharUnBan, "@charunbanish", 60, atcommand_char_unban }, // by Yor
+ { AtCommand_MountPeco, "@mountpeco", 20, atcommand_mount_peco }, // by Valaris
+ { AtCommand_CharMountPeco, "@charmountpeco", 50, atcommand_char_mount_peco }, // by Yor
+ { AtCommand_GuildSpy, "@guildspy", 60, atcommand_guildspy }, // [Syrus22]
+ { AtCommand_PartySpy, "@partyspy", 60, atcommand_partyspy }, // [Syrus22]
+ { AtCommand_RepairAll, "@repairall", 60, atcommand_repairall }, // [Valaris]
+ { AtCommand_GuildRecall, "@guildrecall", 60, atcommand_guildrecall }, // by Yor
+ { AtCommand_PartyRecall, "@partyrecall", 60, atcommand_partyrecall }, // by Yor
+ { AtCommand_Nuke, "@nuke", 60, atcommand_nuke }, // [Valaris]
+ { AtCommand_Shownpc, "@shownpc", 80, atcommand_shownpc }, // []
+ { AtCommand_Hidenpc, "@hidenpc", 80, atcommand_hidenpc }, // []
+ { AtCommand_Loadnpc, "@loadnpc", 80, atcommand_loadnpc }, // []
+ { AtCommand_Unloadnpc, "@unloadnpc", 80, atcommand_unloadnpc }, // []
+ { AtCommand_ServerTime, "@time", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@date", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@server_date", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@serverdate", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@server_time", 1, atcommand_servertime }, // by Yor
+ { AtCommand_ServerTime, "@servertime", 1, atcommand_servertime }, // by Yor
+ { AtCommand_CharDelItem, "@chardelitem", 60, atcommand_chardelitem }, // by Yor
+ { AtCommand_Jail, "@jail", 60, atcommand_jail }, // by Yor
+ { AtCommand_UnJail, "@unjail", 60, atcommand_unjail }, // by Yor
+ { AtCommand_UnJail, "@discharge", 60, atcommand_unjail }, // by Yor
+ { AtCommand_Disguise, "@disguise", 20, atcommand_disguise }, // [Valaris]
+ { AtCommand_UnDisguise, "@undisguise", 20, atcommand_undisguise }, // by Yor
+ { AtCommand_CharDisguise, "@chardisguise", 60, atcommand_chardisguise }, // Kalaspuff
+ { AtCommand_CharUnDisguise, "@charundisguise", 60, atcommand_charundisguise }, // Kalaspuff
+ { AtCommand_EMail, "@email", 1, atcommand_email }, // by Yor
+ { AtCommand_Effect, "@effect", 40, atcommand_effect }, // by Apple
+ { AtCommand_Char_Cart_List, "@charcartlist", 40, atcommand_character_cart_list }, // by Yor
+ { AtCommand_Follow, "@follow", 20, atcommand_follow }, // by MouseJstr
+ { AtCommand_AddWarp, "@addwarp", 60, atcommand_addwarp }, // by MouseJstr
+ { AtCommand_SkillOn, "@skillon", 80, atcommand_skillon }, // by MouseJstr
+ { AtCommand_SkillOff, "@skilloff", 80, atcommand_skilloff }, // by MouseJstr
+ { AtCommand_Killer, "@killer", 60, atcommand_killer }, // by MouseJstr
+ { AtCommand_NpcMove, "@npcmove", 20, atcommand_npcmove }, // by MouseJstr
+ { AtCommand_Killable, "@killable", 40, atcommand_killable }, // by MouseJstr
+ { AtCommand_CharKillable, "@charkillable", 40, atcommand_charkillable }, // by MouseJstr
+ { AtCommand_Dropall, "@dropall", 40, atcommand_dropall }, // MouseJstr
+ { AtCommand_Chardropall, "@chardropall", 40, atcommand_chardropall }, // MouseJstr
+ { AtCommand_Storeall, "@storeall", 40, atcommand_storeall }, // MouseJstr
+ { AtCommand_Charstoreall, "@charstoreall", 40, atcommand_charstoreall }, // MouseJstr
+ { AtCommand_Skillid, "@skillid", 40, atcommand_skillid }, // MouseJstr
+ { AtCommand_Useskill, "@useskill", 40, atcommand_useskill }, // MouseJstr
+ { AtCommand_Rain, "@rain", 99, atcommand_rain },
+ { AtCommand_Snow, "@snow", 99, atcommand_snow },
+ { AtCommand_Sakura, "@sakura", 99, atcommand_sakura },
+ { AtCommand_Clouds, "@clouds", 99, atcommand_clouds },
+ { AtCommand_Clouds2, "@clouds2", 99, atcommand_clouds2 },
+ { AtCommand_Fog, "@fog", 99, atcommand_fog },
+ { AtCommand_Fireworks, "@fireworks", 99, atcommand_fireworks },
+ { AtCommand_Leaves, "@leaves", 99, atcommand_leaves },
+ { AtCommand_Summon, "@summon", 60, atcommand_summon },
+ { AtCommand_AdjGmLvl, "@adjgmlvl", 99, atcommand_adjgmlvl },
+ { AtCommand_AdjCmdLvl, "@adjcmdlvl", 99, atcommand_adjcmdlvl },
+ { AtCommand_Trade, "@trade", 60, atcommand_trade },
+ { AtCommand_Send, "@send", 60, atcommand_send },
+ { AtCommand_SetBattleFlag, "@setbattleflag", 99, atcommand_setbattleflag },
+ { AtCommand_UnMute, "@unmute", 60, atcommand_unmute }, // [Valaris]
+ { AtCommand_Clearweather, "@clearweather", 99, atcommand_clearweather }, // Dexity
+ { AtCommand_UpTime, "@uptime", 1, atcommand_uptime }, // by MC Cameri
+// { AtCommand_ChangeSex, "@changesex", 1, atcommand_changesex }, // by MC Cameri <- do we still need this? [Foruken]
+ { AtCommand_Mute, "@mute", 99, atcommand_mute }, // [celest]
+ { AtCommand_Mute, "@red", 99, atcommand_mute }, // [celest]
+ { AtCommand_WhoZeny, "@whozeny", 20, atcommand_whozeny }, // [Valaris]
+ { AtCommand_HappyHappyJoyJoy, "@happyhappyjoyjoy", 40, atcommand_happyhappyjoyjoy }, // [Valaris]
+ { AtCommand_Refresh, "@refresh", 1, atcommand_refresh }, // by MC Cameri
+ { AtCommand_PetId, "@petid", 40, atcommand_petid }, // by MC Cameri
+ { AtCommand_Identify, "@identify", 40, atcommand_identify }, // by MC Cameri
+ { AtCommand_Gmotd, "@gmotd", 20, atcommand_gmotd }, // Added by MC Cameri, created by davidsiaw
+ { AtCommand_MiscEffect, "@misceffect", 50, atcommand_misceffect }, // by MC Cameri
+ { AtCommand_MobSearch, "@mobsearch", 10, atcommand_mobsearch },
+ { AtCommand_CleanMap, "@cleanmap", 40, atcommand_cleanmap },
+ { AtCommand_NpcTalk, "@npctalk", 20, atcommand_npctalk },
+ { AtCommand_PetTalk, "@pettalk", 10, atcommand_pettalk },
+ { AtCommand_Users, "@users", 40, atcommand_users },
+ { AtCommand_ResetState, "/reset", 40, NULL },
+
+#ifndef TXT_ONLY // sql-only commands
+ { AtCommand_CheckMail, "@checkmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ListMail, "@listmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ListNewMail, "@listnewmail", 1, atcommand_listmail }, // [Valaris]
+ { AtCommand_ReadMail, "@readmail", 1, atcommand_readmail }, // [Valaris]
+ { AtCommand_DeleteMail, "@deletemail", 1, atcommand_readmail }, // [Valaris]
+ { AtCommand_SendMail, "@sendmail", 1, atcommand_sendmail }, // [Valaris]
+ { AtCommand_SendPriorityMail, "@sendprioritymail", 80, atcommand_sendmail }, // [Valaris]
+ { AtCommand_RefreshOnline, "@refreshonline", 99, atcommand_refreshonline }, // [Valaris]
+
+#endif /* TXT_ONLY */
+ { AtCommand_SkillTree, "@skilltree", 40, atcommand_skilltree }, // [MouseJstr]
+ { AtCommand_Marry, "@marry", 40, atcommand_marry }, // [MouseJstr]
+ { AtCommand_Divorce, "@divorce", 40, atcommand_divorce }, // [MouseJstr]
+ { AtCommand_Grind, "@grind", 99, atcommand_grind }, // [MouseJstr]
+ { AtCommand_Grind2, "@grind2", 99, atcommand_grind2 }, // [MouseJstr]
+
+#ifdef DMALLOC
+ { AtCommand_DMStart, "@dmstart", 99, atcommand_dmstart }, // [MouseJstr]
+ { AtCommand_DMTick, "@dmtick", 99, atcommand_dmtick }, // [MouseJstr]
+#endif
+
+ { AtCommand_JumpToId, "@jumptoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId, "@warptoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId, "@gotoid", 20, atcommand_jumptoid }, // [Dino9021]
+ { AtCommand_JumpToId2, "@jumptoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_JumpToId2, "@warptoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_JumpToId2, "@gotoid2", 20, atcommand_jumptoid2 }, // [Dino9021]
+ { AtCommand_RecallId, "@recallid", 60, atcommand_recallid }, // [Dino9021]
+ { AtCommand_RecallId2, "@recallid2", 60, atcommand_recallid2 }, // [Dino9021]
+ { AtCommand_KickId, "@kickid", 99, atcommand_kickid }, // [Dino9021]
+ { AtCommand_KickId2, "@kickid2", 99, atcommand_kickid2 }, // [Dino9021]
+ { AtCommand_ReviveId, "@reviveid", 60, atcommand_reviveid }, // [Dino9021]
+ { AtCommand_ReviveId2, "@reviveid2", 60, atcommand_reviveid2 }, // [Dino9021]
+ { AtCommand_KillId, "@killid", 60, atcommand_killid }, // [Dino9021]
+ { AtCommand_KillId2, "@killid2", 60, atcommand_killid2 }, // [Dino9021]
+ { AtCommand_CharKillableId, "@charkillableid", 40, atcommand_charkillableid }, // [Dino9021]
+ { AtCommand_CharKillableId2, "@charkillableid2", 40, atcommand_charkillableid2 }, // [Dino9021]
+ { AtCommand_Sound, "@sound", 40, atcommand_sound },
+ { AtCommand_UndisguiseAll, "@undisguiseall", 99, atcommand_undisguiseall },
+ { AtCommand_DisguiseAll, "@disguiseall", 99, atcommand_disguiseall },
+ { AtCommand_ChangeLook, "@changelook", 99, atcommand_changelook },
+ { AtCommand_AutoLoot, "@autoloot", 10, atcommand_autoloot }, // Upa-Kun
+ { AtCommand_MobInfo, "@mobinfo", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_MobInfo, "@monsterinfo", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_MobInfo, "@mi", 1, atcommand_mobinfo }, // [Lupus]
+ { AtCommand_Adopt, "@adopt", 40, atcommand_adopt }, // [Veider]
+ { AtCommand_Version, "@version", 1, atcommand_version },
+
+ { AtCommand_MuteArea, "@mutearea", 99, atcommand_mutearea }, // MouseJstr
+ { AtCommand_MuteArea, "@stfu", 99, atcommand_mutearea }, // MouseJstr
+ { AtCommand_Shuffle, "@shuffle", 40, atcommand_shuffle }, // MouseJstr
+ { AtCommand_Rates, "@rates", 1, atcommand_rates }, // MouseJstr
+
+ { AtCommand_ItemInfo, "@iteminfo", 1, atcommand_iteminfo }, // [Lupus]
+ { AtCommand_ItemInfo, "@ii", 1, atcommand_iteminfo }, // [Lupus]
+ { AtCommand_MapFlag, "@mapflag", 99, atcommand_mapflag }, // [Lupus]
+
+ { AtCommand_Me, "@me", 20, atcommand_me }, //added by massdriller, code by lordalfa
+ { AtCommand_MonsterIgnore, "@monsterignore", 99, atcommand_monsterignore }, // [Valaris]
+ { AtCommand_FakeName, "@fakename", 20, atcommand_fakename }, // [Valaris]
+ { AtCommand_Size, "@size", 20, atcommand_size },
+ { AtCommand_ShowExp, "@showexp", 10, atcommand_showexp},
+ { AtCommand_ShowZeny, "@showzeny", 10, atcommand_showzeny},
+ { AtCommand_ShowDelay, "@showdelay", 1, atcommand_showdelay},
+ { AtCommand_AutoTrade, "@autotrade", 10, atcommand_autotrade }, // durf
+ { AtCommand_AutoTrade, "@at", 10, atcommand_autotrade },
+ { AtCommand_ChangeGM, "@changegm", 10, atcommand_changegm }, // durf
+ { AtCommand_ChangeLeader, "@changeleader", 10, atcommand_changeleader }, // durf
+ { AtCommand_Invite, "@invite", 1, atcommand_invite }, // By LuzZza
+ { AtCommand_Duel, "@duel", 1, atcommand_duel }, // By LuzZza
+ { AtCommand_Leave, "@leave", 1, atcommand_leave }, // By LuzZza
+ { AtCommand_Accept, "@accept", 1, atcommand_accept }, // By LuzZza
+ { AtCommand_Reject, "@reject", 1, atcommand_reject }, // By LuzZza
+ { AtCommand_Away, "@away", 1, atcommand_away }, // [LuzZza]
+ { AtCommand_Away, "@aw", 1, atcommand_away }, // [LuzZza]
+ { AtCommand_Main, "@main", 1, atcommand_main }, // [LuzZza]
+ { AtCommand_Clone, "@clone", 50, atcommand_clone },
+ { AtCommand_Clone, "@slaveclone", 50, atcommand_clone },
+ { AtCommand_Clone, "@evilclone", 50, atcommand_clone }, // [Valaris]
+
+// add new commands before this line
+ { AtCommand_Unknown, NULL, 1, NULL }
+};
+
+/*=========================================
+ * Generic variables
+ *-----------------------------------------
+ */
+char atcmd_output[200];
+char atcmd_player_name[100];
+char atcmd_temp[100];
+
+/*==========================================
+ * estr_lower (replace strlwr, non ANSI function that doesn't exist in all C compilator)
+ *------------------------------------------
+ */
+char *estr_lower(char *str)
+{
+ int i;
+
+ for (i=0; str[i]; i++)
+ if ((str[i] >= 65) && (str[i] <= 90))
+ str[i] += 32;
+ return str;
+}
+
+// compare function for sorting high to lowest
+int hightolow_compare (const void * a, const void * b)
+{
+ return ( *(int*)b - *(int*)a );
+}
+
+// compare function for sorting lowest to highest
+int lowtohigh_compare (const void * a, const void * b)
+{
+ return ( *(int*)a - *(int*)b );
+}
+
+//-----------------------------------------------------------
+// Return the message string of the specified number by [Yor]
+//-----------------------------------------------------------
+char * msg_txt(int msg_number) {
+ if (msg_number >= 0 && msg_number < MAX_MSG &&
+ msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0')
+ return msg_table[msg_number];
+
+ return "??";
+}
+
+//-----------------------------------------------------------
+// Returns Players title (from msg_athena.conf) [Lupus]
+//-----------------------------------------------------------
+char * player_title_txt(int level) {
+ if (level < battle_config.title_lvl1)
+ return ""; //w/o any titles
+
+ if (level >= battle_config.title_lvl8)
+ sprintf(atcmd_temp, msg_table[332], level);
+ else
+ if (level >= battle_config.title_lvl7)
+ sprintf(atcmd_temp, msg_table[331], level);
+ else
+ if (level >= battle_config.title_lvl6)
+ sprintf(atcmd_temp, msg_table[330], level);
+ else
+ if (level >= battle_config.title_lvl5)
+ sprintf(atcmd_temp, msg_table[329], level);
+ else
+ if (level >= battle_config.title_lvl4)
+ sprintf(atcmd_temp, msg_table[328], level);
+ else
+ if (level >= battle_config.title_lvl3)
+ sprintf(atcmd_temp, msg_table[327], level);
+ else
+ if (level >= battle_config.title_lvl2)
+ sprintf(atcmd_temp, msg_table[326], level);
+ else
+ sprintf(atcmd_temp, msg_table[325], level); //lvl1
+ return atcmd_temp;
+}
+
+//------------------------------------------------------------
+// E-mail check: return 0 (not correct) or 1 (valid). by [Yor]
+//------------------------------------------------------------
+int e_mail_check(char *email) {
+ char ch;
+ char* last_arobas;
+
+ // athena limits
+ if (strlen(email) < 3 || strlen(email) > 39)
+ return 0;
+
+ // part of RFC limits (official reference of e-mail description)
+ if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@')
+ return 0;
+
+ if (email[strlen(email)-1] == '.')
+ return 0;
+
+ last_arobas = strrchr(email, '@');
+
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
+ return 0;
+
+ for(ch = 1; ch < 32; ch++) {
+ if (strchr(last_arobas, ch) != NULL) {
+ return 0;
+ break;
+ }
+ }
+
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
+ return 0;
+
+ // all correct
+ return 1;
+}
+
+/*==========================================
+ * get_atcommand_level @ƒRƒ}ƒ“ƒh‚Ì•K—vƒŒƒxƒ‹‚ðŽæ“¾
+ *------------------------------------------
+ */
+int get_atcommand_level(const AtCommandType type) {
+ int i;
+
+ for (i = 0; atcommand_info[i].type != AtCommand_None; i++)
+ if (atcommand_info[i].type == type)
+ return atcommand_info[i].level;
+
+ return 100; // 100: command can not be used
+}
+
+/*==========================================
+ *is_atcommand @ƒRƒ}ƒ“ƒh‚É‘¶Ý‚·‚é‚©‚Ç‚¤‚©Šm”F‚·‚é
+ *------------------------------------------
+ */
+AtCommandType
+is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl) {
+ const char* str = message;
+ int s_flag = 0;
+ AtCommandInfo info;
+ AtCommandType type;
+
+ nullpo_retr(AtCommand_None, sd);
+
+ if (!battle_config.allow_atcommand_when_mute &&
+ sd->sc_count && sd->sc_data[SC_NOCHAT].timer != -1) {
+ return AtCommand_Unknown;
+ }
+
+ if (!message || !*message)
+ return AtCommand_None;
+
+ memset(&info, 0, sizeof(info));
+ str += strlen(sd->status.name);
+ while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
+ if (*str == ':')
+ s_flag = 1;
+ str++;
+ }
+ if (!*str)
+ return AtCommand_None;
+
+ type = atcommand(sd, gmlvl > 0 ? gmlvl : pc_isGM(sd), str, &info);
+ if (type != AtCommand_None) {
+ char command[100];
+ const char* p = str;
+ memset(command, '\0', sizeof(command));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ while (*p && !isspace(*p))
+ p++;
+ if (p - str >= sizeof(command)) // too long
+ return AtCommand_Unknown;
+ strncpy(command, str, p - str);
+ while (isspace(*p))
+ p++;
+
+ if (type == AtCommand_Unknown || info.proc == NULL) {
+ sprintf(atcmd_output, msg_table[153], command); // %s is Unknown Command.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ if (info.proc(fd, sd, command, p) != 0) {
+ // Command can not be executed
+ sprintf(atcmd_output, msg_table[154], command); // %s failed.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+
+ return info.type;
+ }
+
+ return AtCommand_None;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+AtCommandType atcommand(struct map_session_data* sd, const int level, const char* message, struct AtCommandInfo* info) {
+ char* p = (char *)message; // it's 'char' and not 'const char' to have possibility to modify the first character if necessary
+
+ if (!info)
+ return AtCommand_None;
+ if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
+ return AtCommand_None;
+ if (!p || !*p) {
+ ShowError("at command message is empty\n");
+ return AtCommand_None;
+ }
+
+ if (*p == command_symbol) { // check first char.
+ char command[101];
+ int i = 0;
+ memset(info, 0, sizeof(AtCommandInfo));
+ sscanf(p, "%100s", command);
+ command[sizeof(command)-1] = '\0';
+
+ while (atcommand_info[i].type != AtCommand_Unknown) {
+ if (strcmpi(command+1, atcommand_info[i].command+1) == 0 && level >= atcommand_info[i].level) {
+ p[0] = atcommand_info[i].command[0]; // set correct first symbol for after.
+ break;
+ }
+ i++;
+ }
+
+ if (atcommand_info[i].type == AtCommand_Unknown) {
+ // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
+ if (level == 0)
+ return AtCommand_None;
+ else
+ return AtCommand_Unknown;
+ } else if((log_config.gm) && (atcommand_info[i].level >= log_config.gm)) {
+ log_atcommand(sd, message);
+ }
+ memcpy(info, &atcommand_info[i], sizeof atcommand_info[i]);
+ } else {
+ return AtCommand_None;
+ }
+
+ return info->type;
+}
+
+/*==========================================
+ * Read Message Data
+ *------------------------------------------
+ */
+int msg_config_read(const char *cfgName) {
+ int msg_number;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ static int called = 1;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("Messages file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ if ((--called) == 0)
+ memset(&msg_table[0], 0, sizeof(msg_table[0]) * MAX_MSG);
+ while(fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if (strcmpi(w1, "import") == 0) {
+ msg_config_read(w2);
+ } else {
+ msg_number = atoi(w1);
+ if (msg_number >= 0 && msg_number < MAX_MSG) {
+ if (msg_table[msg_number] != NULL)
+ aFree(msg_table[msg_number]);
+ msg_table[msg_number] = (char *)aCalloc(strlen(w2) + 1, sizeof (char));
+ strcpy(msg_table[msg_number],w2);
+ // printf("message #%d: '%s'.\n", msg_number, msg_table[msg_number]);
+ }
+ }
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * Cleanup Message Data
+ *------------------------------------------
+ */
+void do_final_msg (void) {
+ int i;
+ for (i = 0; i < MAX_MSG; i++)
+ aFree(msg_table[i]);
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static AtCommandInfo* get_atcommandinfo_byname(const char* name) {
+ int i;
+
+ for (i = 0; atcommand_info[i].type != AtCommand_Unknown; i++)
+ if (strcmpi(atcommand_info[i].command + 1, name) == 0)
+ return &atcommand_info[i];
+
+ return NULL;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ AtCommandInfo* p;
+ FILE* fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("At commands configuration file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
+ continue;
+ p = get_atcommandinfo_byname(w1);
+ if (p != NULL) {
+ p->level = atoi(w2);
+ if (p->level > 100)
+ p->level = 100;
+ else if (p->level < 0)
+ p->level = 0;
+ }
+
+ if (strcmpi(w1, "import") == 0)
+ atcommand_config_read(w2);
+ else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
+ w2[0] != '/' && // symbol of standard ragnarok GM commands
+ w2[0] != '%' && // symbol of party chat speaking
+ w2[0] != '$' && // symbol of guild chat
+ w2[0] != '#') // symbol of charcommand
+ command_symbol = w2[0];
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * Duel organizing functions [LuzZza]
+ *------------------------------------------
+ */
+void duel_msg_foreach_sameduel_wos(
+ const unsigned int did, struct map_session_data* sd, char *output)
+{
+ int i;
+ struct map_session_data* msg_sd;
+
+ for(i=0; i<fd_max; i++)
+ if(session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_group == did && msg_sd != sd)
+
+ clif_disp_onlyself(msg_sd, output, strlen(output));
+
+ return;
+}
+
+void duel_savetime(struct map_session_data* sd) {
+
+ time_t timer;
+ struct tm *t;
+
+ time(&timer);
+ t = localtime(&timer);
+
+ pc_setglobalreg(sd, "PC_LAST_DUEL_TIME",
+ t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min);
+ return;
+}
+
+int duel_checktime(struct map_session_data* sd) {
+
+ int diff;
+ time_t timer;
+ struct tm *t;
+
+ time(&timer);
+ t = localtime(&timer);
+
+ diff = t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min -
+ pc_readglobalreg(sd, "PC_LAST_DUEL_TIME");
+
+ return !(diff >= 0 && diff < battle_config.duel_time_interval);
+}
+
+int duel_showinfo(
+ const unsigned int did, struct map_session_data* sd)
+{
+ int i, p=0;
+ char output[256];
+ struct map_session_data* msg_sd;
+
+ if(duel_list[did].max_players_limit > 0)
+ sprintf(output, msg_txt(370), //" -- Duels: %d/%d, Members: %d/%d, Max players: %d --"
+ did, duel_count,
+ duel_list[did].members_count,
+ duel_list[did].members_count + duel_list[did].invites_count,
+ duel_list[did].max_players_limit);
+ else
+ sprintf(output, msg_txt(371), //" -- Duels: %d/%d, Members: %d/%d --"
+ did, duel_count,
+ duel_list[did].members_count,
+ duel_list[did].members_count + duel_list[did].invites_count);
+
+ clif_disp_onlyself(sd, output, strlen(output));
+
+ for(i=0; i<fd_max; i++)
+ if (session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_group == did) {
+
+ sprintf(output, " %d. %s", ++p, (unsigned char *) msg_sd->status.name);
+ clif_disp_onlyself(sd, output, strlen(output));
+ }
+
+ return 0;
+}
+
+int duel_create(
+ struct map_session_data* sd, const unsigned int maxpl)
+{
+ int i=1;
+ char output[256];
+
+ while(duel_list[i].members_count > 0 && i < MAX_DUEL) i++;
+ if(i == MAX_DUEL) return 0;
+
+ duel_count++;
+ sd->duel_group = i;
+ duel_list[i].members_count++;
+ duel_list[i].invites_count = 0;
+ duel_list[i].max_players_limit = maxpl;
+
+ strcpy(output, msg_txt(372)); // " -- Duel has been created (@invite/@leave) --"
+ clif_disp_onlyself(sd, output, strlen(output));
+
+ clif_set0199(sd->fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ return i;
+}
+
+int duel_invite(
+ const unsigned int did, struct map_session_data* sd,
+ struct map_session_data* target_sd)
+{
+ char output[256];
+
+ sprintf(output, msg_txt(373), // " -- Player %s invites %s to duel --"
+ (unsigned char *)sd->status.name, (unsigned char *)target_sd->status.name);
+
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ target_sd->duel_invite = did;
+ duel_list[did].invites_count++;
+
+ // "Blue -- Player %s invites you to PVP duel (@accept/@reject) --"
+ sprintf(output, msg_txt(374), (unsigned char *)sd->status.name);
+ clif_GMmessage((struct block_list *)target_sd, output, strlen(output)+1, 3);
+ return 0;
+}
+
+int duel_leave(
+ const unsigned int did, struct map_session_data* sd)
+{
+ int i;
+ char output[256];
+ struct map_session_data* msg_sd;
+
+ // " <- Player %s has left duel --"
+ sprintf(output, msg_txt(375), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].members_count--;
+
+ if(duel_list[did].members_count == 0) {
+ for (i=0; i<fd_max; i++)
+ if (session[i] && (msg_sd = (struct map_session_data *) session[i]->session_data)
+ && msg_sd->state.auth && msg_sd->duel_invite == did && msg_sd != sd) {
+
+ msg_sd->duel_invite = 0;
+ }
+
+ duel_count--;
+ }
+
+ sd->duel_group = 0;
+ duel_savetime(sd);
+ clif_set0199(sd->fd, 0);
+ return 0;
+}
+
+int duel_accept(
+ const unsigned int did, struct map_session_data* sd)
+{
+ char output[256];
+
+ // " -> Player %s has accepted duel --"
+ sprintf(output, msg_txt(376), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].members_count++;
+ sd->duel_group = sd->duel_invite;
+ duel_list[did].invites_count--;
+ sd->duel_invite = 0;
+
+ clif_set0199(sd->fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ return 0;
+}
+
+int duel_reject(
+ const unsigned int did, struct map_session_data* sd)
+{
+ char output[256];
+
+ // " -- Player %s has rejected duel --"
+ sprintf(output, msg_txt(377), (unsigned char *)sd->status.name);
+ duel_msg_foreach_sameduel_wos(did, sd, output);
+
+ duel_list[did].invites_count--;
+ sd->duel_invite = 0;
+ return 0;
+}
+
+/*==========================================
+// @ command processing functions
+ *------------------------------------------
+ */
+
+/*==========================================
+ * @send (used for testing packet sends from the client)
+ *------------------------------------------
+ */
+int atcommand_send(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i,type=0;
+ int info[20];
+
+ if (!message || !*message || sscanf(message, "%x %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &type, &info[1], &info[2], &info[3], &info[4], &info[5], &info[6], &info[7], &info[8], &info[9], &info[10], &info[11], &info[12], &info[13], &info[14], &info[15], &info[16], &info[17], &info[18], &info[19], &info[20]) < 1) {
+ clif_displaymessage(fd, "Please enter a packet number, and - if required - up to 20 additional values.");
+ return -1;
+ }
+
+ if (type > 0 && type < MAX_PACKET_DB) {
+
+ switch (type)
+ {
+ case 0x209: {
+ WFIFOHEAD(fd, packet_db[sd->packet_ver][type].len);
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = 2;
+ memcpy(WFIFOP(fd, 12), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_db[sd->packet_ver][type].len);
+ break;
+ }
+ case 0x1b1:
+ case 0x1c2:
+ //case xxx:
+ // add others here
+ // break;
+ default: {
+ WFIFOHEAD(fd, packet_db[sd->packet_ver][type].len);
+ WFIFOW(fd,0) = type;
+ for(i=1;i<=sizeof(info);i++)
+ if(info[i])
+ WFIFOW(fd,i) = info[i];
+ WFIFOSET(fd, packet_db[sd->packet_ver][type].len);
+ break;
+ }
+ }
+
+ sprintf (atcmd_output, msg_table[258], type, type);
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[259]);
+ }
+
+ return 0;
+}
+
+// @rura
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_rura(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ unsigned short mapindex;
+ int x = 0, y = 0;
+ int m = -1;
+
+ nullpo_retr(-1, sd);
+
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d", map_name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Please, enter a map (usage: @warp/@rura/@mapmove <mapname> <x> <y>).");
+ return -1;
+ }
+
+ if (x <= 0)
+ x = rand() % 399 + 1;
+ if (y <= 0)
+ y = rand() % 399 + 1;
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ mapindex = mapindex_name2id(map_name);
+ if (mapindex)
+ m = map_mapindex2mapid(mapindex);
+
+ if (!mapindex || m < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+
+ if (x > 0 && x < 400 && y > 0 && y < 400) {
+ if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, mapindex, x, y, 3) == 0)
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Displays where a character is. Corrected version by Silent. [Skotlex]
+ *------------------------------------------
+ */
+int atcommand_where(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ int GM_level, pl_GM_level;
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @where <char name>).");
+ return -1;
+ }
+ pl_sd = map_nick2sd(atcmd_player_name);
+ nullpo_retr(-1, sd);
+
+ if (pl_sd == NULL)
+ return -1;
+
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0)
+ return -1;
+
+ GM_level = pc_isGM(sd);//also hide gms depending on settings in battle_athena.conf, show if they are aid [Kevin]
+ pl_GM_level = pc_isGM(pl_sd);
+
+ if (battle_config.hide_GM_session) {
+ if(!(GM_level >= pl_GM_level)) {
+ if (!(battle_config.who_display_aid > 0 && pc_isGM(sd) >= battle_config.who_display_aid)) {
+ return -1;
+ }
+ }
+ }
+
+ snprintf(atcmd_output, sizeof atcmd_output, "%s %s %d %d",
+ atcmd_player_name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_jumpto(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @jumpto/@warpto/@goto <char name>).");
+ return -1;
+ }
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+ if (sscanf(message, "%23[^\n]", atcmd_player_name) < 1)
+ return -1;
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0) //Yourself mate? Tsk tsk tsk.
+ return -1;
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], atcmd_player_name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_jump(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x = 0, y = 0;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ sscanf(message, "%d %d", &x, &y);
+
+ if (x <= 0) //If coordinates are 'wrong', random jump.
+ x = -1;
+ if (y <= 0)
+ y = -1;
+ if (sd->bl.m >= 0 && (map[sd->bl.m].flag.nowarp || map[sd->bl.m].flag.nowarpto) && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, sd->mapindex, x, y, 3);
+ sprintf(atcmd_output, msg_table[5], sd->bl.x, sd->bl.y); // Jump to %d %d
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+}
+
+/*==========================================
+ * @who3 = Player name, his location
+ *------------------------------------------
+ */
+int atcommand_who3(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+
+ if (battle_config.who_display_aid > 0 && pc_isGM(sd) >= battle_config.who_display_aid) {
+ sprintf(atcmd_output, "(CID:%d/AID:%d) ", pl_sd->status.char_id, pl_sd->status.account_id);
+ } else {
+ atcmd_output[0]=0;
+ }
+ //Player name
+ sprintf(temp0, msg_txt(333), pl_sd->status.name);
+ strcat(atcmd_output,temp0);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Location: map x y
+ sprintf(temp0, msg_txt(338), mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ strcat(atcmd_output,temp0);
+
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Player name, BLevel, Job,
+ *------------------------------------------
+ */
+int atcommand_who2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ //Players Name
+ //sprintf(atcmd_output, "Name: %s ", pl_sd->status.name);
+ sprintf(atcmd_output, msg_txt(333), pl_sd->status.name);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Base Level / Job name
+ //sprintf(temp0, "| L:%d/%d | Job: %s", pl_sd->status.base_level, pl_sd->status.job_level, job_name(pl_sd->status.class_) );
+ sprintf(temp0, msg_txt(337), pl_sd->status.base_level, pl_sd->status.job_level, job_name(pl_sd->status.class_) );
+ strcat(atcmd_output,temp0);
+
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_txt(28)); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_txt(29)); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_txt(30), count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Player name, Playrs Party / Guild name
+ *------------------------------------------
+ */
+int atcommand_who(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ g = guild_search(pl_sd->status.guild_id);
+ p = party_search(pl_sd->status.party_id);
+ //Players Name
+ //sprintf(atcmd_output, "Name: %s ", pl_sd->status.name);
+ sprintf(atcmd_output, msg_txt(333), pl_sd->status.name);
+ //Player title, if exists
+ if (pl_GM_level > 0) {
+ //sprintf(temp0, "(%s) ", player_title_txt(pl_GM_level) );
+ sprintf(temp0, msg_txt(334), player_title_txt(pl_GM_level) );
+ strcat(atcmd_output,temp0);
+ }
+ //Players Party if exists
+ if (p != NULL) {
+ //sprintf(temp0," | Party: '%s'", p->name);
+ sprintf(temp0, msg_txt(335), p->name);
+ strcat(atcmd_output,temp0);
+ }
+ //Players Guild if exists
+ if (g != NULL) {
+ //sprintf(temp0," | Guild: '%s'", g->name);
+ sprintf(temp0, msg_txt(336), g->name);
+ strcat(atcmd_output,temp0);
+ }
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_txt(28)); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_txt(29)); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_txt(30), count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap3(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id;
+ char map_name[MAP_NAME_LENGTH];
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ else
+ sprintf(atcmd_output, "Name: %s | Location: %s %d %d", pl_sd->status.name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_txt(54), map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_txt(55), map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_txt(56), count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id = 0;
+ char map_name[MAP_NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_GM_level, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ else
+ sprintf(atcmd_output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_table[54], map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_table[55], map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whomap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ char temp1[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, count, users;
+ int pl_GM_level, GM_level;
+ int map_id = 0;
+ char map_name[MAP_NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(temp1, '\0', sizeof(temp1));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message)
+ map_id = sd->bl.m;
+ else {
+ sscanf(message, "%15s", map_name);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ if (pl_sd->bl.m == map_id) {
+ g = guild_search(pl_sd->status.guild_id);
+ if (g == NULL)
+ sprintf(temp1, "None");
+ else
+ sprintf(temp1, "%s", g->name);
+ p = party_search(pl_sd->status.party_id);
+ if (p == NULL)
+ sprintf(temp0, "None");
+ else
+ sprintf(temp0, "%s", p->name);
+ if (pl_GM_level > 0)
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_GM_level, temp0, temp1);
+ else
+ sprintf(atcmd_output, "Name: %s | Party: '%s' | Guild: '%s'", pl_sd->status.name, temp0, temp1);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ sprintf(atcmd_output, msg_table[54], map[map_id].name); // No player found in map '%s'.
+ else if (count == 1)
+ sprintf(atcmd_output, msg_table[55], map[map_id].name); // 1 player found in map '%s'.
+ else {
+ sprintf(atcmd_output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'.
+ }
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_whogm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char temp0[100];
+ char temp1[100];
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count, users;
+ int pl_GM_level, GM_level;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ struct guild *g;
+ struct party *p;
+
+ nullpo_retr(-1, sd);
+
+ memset(temp0, '\0', sizeof(temp0));
+ memset(temp1, '\0', sizeof(temp1));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ GM_level = pc_isGM(sd);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ pl_GM_level = pc_isGM(pl_sd);
+ if (pl_GM_level > 0) {
+ if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_INVISIBLE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ sprintf(atcmd_output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level);
+ clif_displaymessage(fd, atcmd_output);
+ g = guild_search(pl_sd->status.guild_id);
+ if (g == NULL)
+ sprintf(temp1, "None");
+ else
+ sprintf(temp1, "%s", g->name);
+ p = party_search(pl_sd->status.party_id);
+ if (p == NULL)
+ sprintf(temp0, "None");
+ else
+ sprintf(temp0, "%s", p->name);
+ sprintf(atcmd_output, " Party: '%s' | Guild: '%s'", temp0, temp1);
+ clif_displaymessage(fd, atcmd_output);
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[150]); // No GM found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[151]); // 1 GM found.
+ else {
+ sprintf(atcmd_output, msg_table[152], count); // %d GMs found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+int atcommand_whozeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, j, count,c, users;
+ char match_text[100];
+ char player_name[NAME_LENGTH];
+ int *zeny;
+ int *counted;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(match_text, '\0', sizeof(match_text));
+ memset(player_name, '\0', sizeof(player_name));
+
+ if (sscanf(message, "%99[^\n]", match_text) < 1)
+ strcpy(match_text, "");
+ for (j = 0; match_text[j]; j++)
+ match_text[j] = tolower(match_text[j]);
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ if (users < 1)
+ {
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ return 0;
+ }
+ zeny = (int *)aCallocA(users, sizeof(int));
+ counted = (int *)aCallocA(users, sizeof(int));
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
+ for (j = 0; player_name[j]; j++)
+ player_name[j] = tolower(player_name[j]);
+ if (strstr(player_name, match_text) != NULL) { // search with no case sensitive
+ zeny[count]=pl_sd->status.zeny;
+ counted[i]=0;
+ count++;
+ }
+ }
+ }
+
+ qsort(zeny, count, sizeof(int), hightolow_compare);
+ for (c = 0; c < count && c < 50; c++) {
+ if(!zeny[c])
+ continue;
+ for (i = 0; i < users; i++) {
+ if(!zeny[c])
+ continue;
+ if ((pl_sd = pl_allsd[i]) && counted[i]==0) {
+ if(pl_sd->status.zeny==zeny[c]) {
+ sprintf(atcmd_output, "Name: %s | Zeny: %d", pl_sd->status.name, pl_sd->status.zeny);
+ clif_displaymessage(fd, atcmd_output);
+ zeny[c]=0;
+ counted[i]=1;
+ }
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(atcmd_output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ aFree(zeny);
+ aFree(counted);
+
+ return 0;
+}
+
+
+// cause random emote on all online players [Valaris]
+int atcommand_happyhappyjoyjoy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i,e, users;
+
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ e=rand()%40;
+ if(e==34)
+ e = 0;
+ clif_emotion(&pl_sd->bl,e);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_save(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ pc_setsavepoint(sd, sd->mapindex, sd->bl.x, sd->bl.y);
+ if (sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id, &sd->pet);
+
+ chrif_save(sd,0);
+
+ clif_displaymessage(fd, msg_table[6]); // Character data respawn point saved.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_load(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int m;
+
+ nullpo_retr(-1, sd);
+
+ m = map_mapindex2mapid(sd->status.save_point.map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[249]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0);
+ clif_displaymessage(fd, msg_table[7]); // Warping to respawn point.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_speed(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int speed;
+
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ sprintf(atcmd_output, "Please, enter a speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ speed = atoi(message);
+ if (speed >= MIN_WALK_SPEED && speed <= MAX_WALK_SPEED) {
+ sd->speed = speed;
+ //sd->walktimer = x;
+ //‚±‚Ì•¶‚ð’ljÁ by ‚ê‚
+ clif_updatestatus(sd, SP_SPEED);
+ clif_displaymessage(fd, msg_table[8]); // Speed changed.
+ } else {
+ sprintf(atcmd_output, "Please, enter a valid speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_storage(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor; //changes from Freya/Yor
+ nullpo_retr(-1, sd);
+
+ if (sd->state.storage_flag) {
+ clif_displaymessage(fd, msg_table[250]);
+ return -1;
+ }
+
+ if ((stor = account2storage2(sd->status.account_id)) != NULL && stor->storage_status == 1) {
+ clif_displaymessage(fd, msg_table[250]);
+ return -1;
+ }
+
+ storage_storageopen(sd);
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guildstorage(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor; //changes from Freya/Yor
+ nullpo_retr(-1, sd);
+
+ if (sd->status.guild_id > 0) {
+ if (sd->state.storage_flag) {
+ clif_displaymessage(fd, msg_table[251]);
+ return -1;
+ }
+ if ((stor = account2storage2(sd->status.account_id)) != NULL && stor->storage_status == 1) {
+ clif_displaymessage(fd, msg_table[251]);
+ return -1;
+ }
+ storage_guild_storageopen(sd);
+ } else {
+ clif_displaymessage(fd, msg_table[252]);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_option(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int param1 = 0, param2 = 0, param3 = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d %d", &param1, &param2, &param3) < 1 || param1 < 0 || param2 < 0 || param3 < 0) {
+ clif_displaymessage(fd, "Please, enter at least a option (usage: @option <param1:0+> <param2:0+> <param3:0+>).");
+ return -1;
+ }
+
+ sd->opt1 = param1;
+ sd->opt2 = param2;
+ if (!(sd->status.option & CART_MASK) && param3 & CART_MASK) {
+ if (sd->status.class_ == JOB_BABY_MERCHANT)
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd, SP_CARTINFO);
+ }
+ pc_setoption(sd, param3);
+
+ clif_displaymessage(fd, msg_table[9]); // Options changed.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hide(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.option & OPTION_INVISIBLE) {
+ sd->status.option &= ~OPTION_INVISIBLE;
+ clif_displaymessage(fd, msg_table[10]); // Invisible: Off
+ } else {
+ sd->status.option |= OPTION_INVISIBLE;
+ clif_displaymessage(fd, msg_table[11]); // Invisible: On
+ }
+ clif_changeoption(&sd->bl);
+
+ return 0;
+}
+
+/*==========================================
+ * “]E‚·‚é upper‚ðŽw’è‚·‚é‚Æ“]¶‚â—{Žq‚É‚à‚È‚ê‚é
+ *------------------------------------------
+ */
+int atcommand_jobchange(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int job = 0, upper = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d", &job, &upper) < 1) {
+
+ int i, found = 0;
+ const struct { char name[16]; int id; } jobs[] = {
+ { "novice", 0 },
+ { "swordsman", 1 },
+ { "mage", 2 },
+ { "archer", 3 },
+ { "acolyte", 4 },
+ { "merchant", 5 },
+ { "thief", 6 },
+ { "knight", 7 },
+ { "priest", 8 },
+ { "priestess", 8 },
+ { "wizard", 9 },
+ { "blacksmith", 10 },
+ { "hunter", 11 },
+ { "assassin", 12 },
+ { "crusader", 14 },
+ { "monk", 15 },
+ { "sage", 16 },
+ { "rogue", 17 },
+ { "alchemist", 18 },
+ { "bard", 19 },
+ { "dancer", 20 },
+ { "super novice", 23 },
+ { "supernovice", 23 },
+ { "high novice", 4001 },
+ { "swordsman high", 4002 },
+ { "mage high", 4003 },
+ { "archer high", 4004 },
+ { "acolyte high", 4005 },
+ { "merchant high", 4006 },
+ { "thief high", 4007 },
+ { "lord knight", 4008 },
+ { "high priest", 4009 },
+ { "high priestess", 4009 },
+ { "high wizard", 4010 },
+ { "whitesmith", 4011 },
+ { "sniper", 4012 },
+ { "assassin cross", 4013 },
+ { "paladin", 4015 },
+ { "champion", 4016 },
+ { "professor", 4017 },
+ { "stalker", 4018 },
+ { "creator", 4019 },
+ { "clown", 4020 },
+ { "gypsy", 4021 },
+ { "baby novice", 4023 },
+ { "baby swordsman", 4024 },
+ { "baby mage", 4025 },
+ { "baby archer", 4026 },
+ { "baby acolyte", 4027 },
+ { "baby merchant", 4028 },
+ { "baby thief", 4029 },
+ { "baby knight", 4030 },
+ { "baby priest", 4031 },
+ { "baby priestess", 4031 },
+ { "baby wizard", 4032 },
+ { "baby blacksmith",4033 },
+ { "baby hunter", 4034 },
+ { "baby assassin", 4035 },
+ { "baby crusader", 4037 },
+ { "baby monk", 4038 },
+ { "baby sage", 4039 },
+ { "baby rogue", 4040 },
+ { "baby alchemist", 4041 },
+ { "baby bard", 4042 },
+ { "baby dancer", 4043 },
+ { "super baby", 4045 },
+ { "taekwon", 4046 },
+ { "taekwon boy", 4046 },
+ { "taekwon girl", 4046 },
+ { "star gladiator", 4047 },
+ { "soul linker", 4049 },
+ };
+
+ for (i=0; i < (int)(sizeof(jobs) / sizeof(jobs[0])); i++) {
+ if (strncmpi(message, jobs[i].name, 16) == 0) {
+ job = jobs[i].id;
+ upper = 0;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ clif_displaymessage(fd, "Please, enter job ID (usage: @job/@jobchange <job ID>).");
+ return -1;
+ }
+ }
+
+ if (job == 37 ||job == 45)
+ return 0;
+
+ if ((job >= 0 && job < MAX_PC_CLASS))
+ {
+ int j;
+
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(sd->status.inventory[j].nameid>0 && sd->status.inventory[j].equip!=0)
+ pc_unequipitem(sd, j, 3);
+ }
+ if (pc_jobchange(sd, job, upper) == 0)
+ clif_displaymessage(fd, msg_table[12]); // Your job has been changed.
+ else {
+ clif_displaymessage(fd, msg_table[155]); // Impossible to change your job.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, "Please, enter a valid job ID (usage: @job/@jobchange <job ID>).");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_die(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,1);
+ pc_damage(NULL, sd, sd->status.hp);
+ clif_displaymessage(fd, msg_table[13]); // A pity! You've died.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @kill <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_alive(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (pc_isdead(sd)) {
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_setstand(sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ clif_displaymessage(fd, msg_table[16]); // You've been revived! It's a miracle!
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * +kamic [LuzZza]
+ *------------------------------------------
+ */
+int atcommand_kami(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+
+ unsigned long color=0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if(*(command + 5) != 'c') {
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @kami <message>).");
+ return -1;
+ }
+
+ sscanf(message, "%199[^\n]", atcmd_output);
+ intif_GMmessage(atcmd_output, strlen(atcmd_output) + 1, (*(command + 5) == 'b') ? 0x10 : 0);
+
+ } else {
+
+ if(!message || !*message || (sscanf(message, "%lx %199[^\n]", &color, atcmd_output) < 2)) {
+ clif_displaymessage(fd, "Please, enter color and message (usage: @kamic <color> <message>).");
+ return -1;
+ }
+
+ if(color < 0 || color > 0xFFFFFF) {
+ clif_displaymessage(fd, "Invalid color.");
+ return -1;
+ }
+
+ intif_announce(atcmd_output, strlen(atcmd_output) + 1, color, 0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_heal(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hp = 0, sp = 0; // [Valaris] thanks to fov
+ nullpo_retr(-1, sd);
+
+ sscanf(message, "%d %d", &hp, &sp);
+
+ if (hp == 0 && sp == 0) {
+ hp = sd->status.max_hp - sd->status.hp;
+ sp = sd->status.max_sp - sd->status.sp;
+ } else {
+ if (hp > 0 && (hp > sd->status.max_hp || hp > (sd->status.max_hp - sd->status.hp))) // fix positiv overflow
+ hp = sd->status.max_hp - sd->status.hp;
+ else if (hp < 0 && (hp < -sd->status.max_hp || hp < (1 - sd->status.hp))) // fix negativ overflow
+ hp = 1 - sd->status.hp;
+ if (sp > 0 && (sp > sd->status.max_sp || sp > (sd->status.max_sp - sd->status.sp))) // fix positiv overflow
+ sp = sd->status.max_sp - sd->status.sp;
+ else if (sp < 0 && (sp < -sd->status.max_sp || sp < (1 - sd->status.sp))) // fix negativ overflow
+ sp = 1 - sd->status.sp;
+ }
+
+ if (hp > 0) // display like heal
+ clif_heal(fd, SP_HP, hp);
+ else if (hp < 0) // display like damage
+ clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0 , 4, 0);
+ if (sp > 0) // no display when we lost SP
+ clif_heal(fd, SP_SP, sp);
+
+ if (hp != 0 || sp != 0) {
+ pc_heal(sd, hp, sp);
+ if (hp >= 0 && sp >= 0)
+ clif_displaymessage(fd, msg_table[17]); // HP, SP recovered.
+ else
+ clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified.
+ } else {
+ clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @item command (usage: @item <name/id_of_item> <quantity>) (modified by [Yor] for pet_egg)
+ *------------------------------------------
+ */
+int atcommand_item(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ int number = 0, item_id, flag;
+ struct item item_tmp;
+ struct item_data *item_data;
+ int get_count, i, pet_id;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d", item_name, &number) < 1) {
+ clif_displaymessage(fd, "Please, enter an item name/id (usage: @item <item name or ID> [quantity]).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+ get_count = number;
+ // check pet egg
+ pet_id = search_petDB_index(item_id, PET_EGG);
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ get_count = 1;
+ }
+ for (i = 0; i < number; i += get_count) {
+ // if pet egg
+ if (pet_id >= 0) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ // if not pet egg
+ } else {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem((struct map_session_data*)sd, &item_tmp, get_count)))
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ }
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_id, number, NULL);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_item2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct item item_tmp;
+ struct item_data *item_data;
+ char item_name[100];
+ int item_id, number = 0;
+ int identify = 0, refine = 0, attr = 0;
+ int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+ int flag;
+ int loop, get_count, i;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9) {
+ clif_displaymessage(fd, "Please, enter all informations (usage: @item2 <item name or ID> <quantity>");
+ clif_displaymessage(fd, " <Identify_flag> <refine> <attribut> <Card1> <Card2> <Card3> <Card4>).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id > 500) {
+ loop = 1;
+ get_count = number;
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ loop = number;
+ get_count = 1;
+ if (item_data->type == 7) {
+ identify = 1;
+ refine = 0;
+ }
+ if (item_data->type == 8)
+ refine = 0;
+ if (refine > 10)
+ refine = 10;
+ } else {
+ identify = 1;
+ refine = attr = 0;
+ }
+ for (i = 0; i < loop; i++) {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = identify;
+ item_tmp.refine = refine;
+ item_tmp.attribute = attr;
+ item_tmp.card[0] = c1;
+ item_tmp.card[1] = c2;
+ item_tmp.card[2] = c3;
+ item_tmp.card[3] = c4;
+ if ((flag = pc_additem(sd, &item_tmp, get_count)))
+ clif_additem(sd, 0, 0, flag);
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_itemreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount && sd->status.inventory[i].equip == 0) {
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0);
+ }
+ }
+ clif_displaymessage(fd, msg_table[20]); // All of your items have been removed.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_itemcheck(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ pc_checkitem(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * Atcommand @lvlup
+ *------------------------------------------
+ */
+int atcommand_baselevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int level=0, i=0;
+ nullpo_retr(-1, sd);
+ level = atoi(message);
+
+ if (!message || !*message || !level) {
+ clif_displaymessage(fd, "Please, enter a level adjustement (usage: @lvup/@blevel/@baselvlup <number of levels>).");
+ return -1;
+ }
+
+ if (level > 0) {
+ if (sd->status.base_level == battle_config.max_base_level) { /* check for max level by Valaris */
+ clif_displaymessage(fd, msg_table[47]); /* Base level can't go any higher. */
+ return -1;
+ } /* End Addition */
+ if ((unsigned int)level > battle_config.max_base_level || (unsigned int)level > (battle_config.max_base_level - sd->status.base_level)) // fix positiv overflow
+ level = battle_config.max_base_level - sd->status.base_level;
+ for (i = 1; i <= level; i++)
+ sd->status.status_point += (sd->status.base_level + i + 14) / 5;
+ sd->status.base_level += level;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ status_calc_pc(sd, 0);
+ pc_heal(sd, sd->status.max_hp, sd->status.max_sp);
+ clif_misceffect(&sd->bl, 0);
+ clif_displaymessage(fd, msg_table[21]); /* Base level raised. */
+ } else {
+ if (sd->status.base_level == 1) {
+ clif_displaymessage(fd, msg_table[158]); /* Base level can't go any lower. */
+ return -1;
+ }
+ if (level < -(int)battle_config.max_base_level || level < (1 - (int)sd->status.base_level)) /* fix negativ overflow */
+ level = 1 - sd->status.base_level;
+ if (sd->status.status_point > 0) {
+ for (i = 0; i > level; i--)
+ sd->status.status_point -= (sd->status.base_level + i + 14) / 5;
+ if (sd->status.status_point < 0)
+ sd->status.status_point = 0;
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ } /* to add: remove status points from stats */
+ sd->status.base_level += level;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ pc_resetskill(sd); /* Skills are reset */
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[22]); /* Base level lowered. */
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_joblevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned int up_level = battle_config.max_job_level;
+ int level=0;
+ nullpo_retr(-1, sd);
+
+ level = atoi(message);
+
+ if (!message || !*message || !level) {
+ clif_displaymessage(fd, "Please, enter a level adjustement (usage: @joblvup/@jlevel/@joblvlup <number of levels>).");
+ return -1;
+ }
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE) //Novice
+ up_level = 10;
+ else if ((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE) //S. Novice
+ up_level = battle_config.max_sn_level;
+ else if (sd->class_&JOBL_UPPER && sd->class_&JOBL_2)
+ up_level = battle_config.max_adv_level; //2nd Adv Class
+
+ if (level > 0) {
+ if (sd->status.job_level == up_level) {
+ clif_displaymessage(fd, msg_table[23]); // Job level can't go any higher.
+ return -1;
+ }
+ if ((unsigned int)level > up_level || (unsigned int)level > (up_level - sd->status.job_level)) // fix positiv overflow
+ level = up_level - sd->status.job_level;
+ sd->status.job_level += level;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ sd->status.skill_point += level;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ status_calc_pc(sd, 0);
+ clif_misceffect(&sd->bl, 1);
+ clif_displaymessage(fd, msg_table[24]); // Job level raised.
+ } else {
+ if (sd->status.job_level == 1) {
+ clif_displaymessage(fd, msg_table[159]); // Job level can't go any lower.
+ return -1;
+ }
+ if (level < -(int)up_level || level < (1 - (int)sd->status.job_level)) // fix negativ overflow
+ level = 1 - sd->status.job_level;
+ sd->status.job_level += level;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ if (sd->status.skill_point > 0) {
+ sd->status.skill_point += level;
+ if (sd->status.skill_point < 0)
+ sd->status.skill_point = 0;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ } // to add: remove status points from skills
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[25]); // Job level lowered.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @help
+ *------------------------------------------
+ */
+int atcommand_help(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(help_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @help2 - Char commands [Kayla]
+ *------------------------------------------
+ */
+int atcommand_help2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(help2_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * @gm
+ *------------------------------------------
+ */
+int atcommand_gm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char password[100];
+ nullpo_retr(-1, sd);
+
+ memset(password, '\0', sizeof(password));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", password) < 1) {
+ clif_displaymessage(fd, "Please, enter a password (usage: @gm <password>).");
+ return -1;
+ }
+
+ if (pc_isGM(sd)) { /* a GM can not use this function. only a normal player (become gm is not for gm!) */
+ clif_displaymessage(fd, msg_table[50]); /* You already have some GM powers. */
+ return -1;
+ } else
+ chrif_changegm(sd->status.account_id, password, strlen(password) + 1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pvpoff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris]
+ clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode.
+ return -1;
+ }
+
+ if (map[sd->bl.m].flag.pvp) {
+ map[sd->bl.m].flag.pvp = 0;
+ clif_send0199(sd->bl.m, 0);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) { //l”•ªƒ‹[ƒv
+ if ((pl_sd = pl_allsd[i]) && sd->bl.m == pl_sd->bl.m) {
+ clif_pvpset(pl_sd, 0, 0, 2);
+ if (pl_sd->pvp_timer != -1) {
+ delete_timer(pl_sd->pvp_timer, pc_calc_pvprank_timer);
+ pl_sd->pvp_timer = -1;
+ }
+ }
+ }
+ clif_displaymessage(fd, msg_table[31]); // PvP: Off.
+ } else {
+ clif_displaymessage(fd, msg_table[160]); // PvP is already Off.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pvpon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris]
+ clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode.
+ return -1;
+ }
+
+ if (!map[sd->bl.m].flag.pvp && !map[sd->bl.m].flag.nopvp) {
+ map[sd->bl.m].flag.pvp = 1;
+ clif_send0199(sd->bl.m, 1);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->bl.m == pl_sd->bl.m && pl_sd->pvp_timer == -1) {
+ pl_sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, pl_sd->bl.id, 0);
+ pl_sd->pvp_rank = 0;
+ pl_sd->pvp_lastusers = 0;
+ pl_sd->pvp_point = 5;
+ pl_sd->pvp_won = 0;
+ pl_sd->pvp_lost = 0;
+ }
+ }
+ clif_displaymessage(fd, msg_table[32]); // PvP: On.
+ } else {
+ clif_displaymessage(fd, msg_table[161]); // PvP is already On.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gvgoff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.gvg) {
+ map[sd->bl.m].flag.gvg = 0;
+ clif_send0199(sd->bl.m, 0);
+ clif_displaymessage(fd, msg_table[33]); // GvG: Off.
+ } else {
+ clif_displaymessage(fd, msg_table[162]); // GvG is already Off.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gvgon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (!map[sd->bl.m].flag.gvg) {
+ map[sd->bl.m].flag.gvg = 1;
+ clif_send0199(sd->bl.m, 3);
+ clif_displaymessage(fd, msg_table[34]); // GvG: On.
+ } else {
+ clif_displaymessage(fd, msg_table[163]); // GvG is already On.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_model(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hair_style = 0, hair_color = 0, cloth_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d %d %d", &hair_style, &hair_color, &cloth_color) < 1) {
+ sprintf(atcmd_output, "Please, enter at least a value (usage: @model <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d>).",
+ MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
+ hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
+ cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ /* Removed because this check is TOO strange. [Skotlex]
+ //•bÌF•ÏX
+ if (cloth_color != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) {
+ //The hell? Why Rogue/Assassins can't... change their option if they have clothes colors and are males? o.O [Skotlex]
+ //•bÌF–¢ŽÀ‘•E‚Ì”»’è
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR, hair_style);
+ pc_changelook(sd, LOOK_HAIR_COLOR, hair_color);
+ pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @dye && @ccolor
+ *------------------------------------------
+ */
+int atcommand_dye(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int cloth_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &cloth_color) < 1) {
+ sprintf(atcmd_output, "Please, enter a clothes color (usage: @dye/@ccolor <clothes color: %d-%d>).", MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @hairstyle && @hstyle
+ *------------------------------------------
+ */
+int atcommand_hair_style(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int hair_style = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &hair_style) < 1) {
+ sprintf(atcmd_output, "Please, enter a hair style (usage: @hairstyle/@hstyle <hair ID: %d-%d>).", MIN_HAIR_STYLE, MAX_HAIR_STYLE);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) {
+ /* Removed because this check is TOO strange. [Skotlex]
+ if (hair_style != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) { //???
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR, hair_style);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charhairstyle by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_charhairstyle(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ return 0;
+}
+
+/*==========================================
+ * @haircolor && @hcolor
+ *------------------------------------------
+ */
+int atcommand_hair_color(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ int hair_color = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &hair_color) < 1) {
+ sprintf(atcmd_output, "Please, enter a hair color (usage: @haircolor/@hcolor <hair color: %d-%d>).", MIN_HAIR_COLOR, MAX_HAIR_COLOR);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) {
+ /* Removed for being such a strange check. [Skotlex]
+ if (hair_color != 0 && sd->status.sex == 1 && (sd->status.class_ == JOB_ASSASSIN || sd->status.class_ == JOB_ROGUE)) {
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(sd, LOOK_HAIR_COLOR, hair_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @go [city_number or city_name] - Updated by Harbin
+ *------------------------------------------
+ */
+int atcommand_go(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ int town;
+ char map_name[MAP_NAME_LENGTH];
+ int m;
+
+ const struct { char map[MAP_NAME_LENGTH]; int x, y; } data[] = {
+ { MAP_PRONTERA, 156, 191 }, // 0=Prontera
+ { MAP_MORROC, 156, 93 }, // 1=Morroc
+ { MAP_GEFFEN, 119, 59 }, // 2=Geffen
+ { MAP_PAYON, 162, 233 }, // 3=Payon
+ { MAP_ALBERTA, 192, 147 }, // 4=Alberta
+ { MAP_IZLUDE, 128, 114 }, // 5=Izlude
+ { MAP_ALDEBARAN, 140, 131 }, // 6=Al de Baran
+ { MAP_LUTIE, 147, 134 }, // 7=Lutie
+ { MAP_COMODO, 209, 143 }, // 8=Comodo
+ { MAP_YUNO, 157, 51 }, // 9=Yuno
+ { MAP_AMATSU, 198, 84 }, // 10=Amatsu
+ { MAP_GONRYUN, 160, 120 }, // 11=Gon Ryun
+ { MAP_UMBALA, 89, 157 }, // 12=Umbala
+ { MAP_NIFLHEIM, 21, 153 }, // 13=Niflheim
+ { MAP_LOUYANG, 217, 40 }, // 14=Lou Yang
+ { "new_1-1.gat", 53, 111 }, // 15=Training Grounds
+ { MAP_JAIL, 23, 61 }, // 16=Prison
+ { MAP_JAWAII, 249, 127 }, // 17=Jawaii
+ { MAP_AYOTHAYA, 151, 117 }, // 18=Ayothaya
+ { MAP_EINBROCH, 64, 200 }, // 19=Einbroch
+ { MAP_LIGHTHALZEN, 158, 92 }, // 20=Lighthalzen
+ { MAP_EINBECH, 70, 95 }, // 21=Einbech
+ { MAP_HUGEL, 96, 145 }, // 22=Hugel
+ };
+
+ nullpo_retr(-1, sd);
+
+ if(map[sd->bl.m].flag.nogo) {
+ clif_displaymessage(sd->fd,"You can not use @go on this map.");
+ return 0;
+ }
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ // get the number
+ town = atoi(message);
+
+ // if no value, display all value
+ if (!message || !*message || sscanf(message, "%15s", map_name) < 1 || town < -3 || town >= (int)(sizeof(data) / sizeof(data[0]))) {
+ clif_displaymessage(fd, msg_table[38]); // Invalid location number or name.
+ clif_displaymessage(fd, msg_table[82]); // Please, use one of this number/name:
+ clif_displaymessage(fd, " 0=Prontera 1=Morroc 2=Geffen");
+ clif_displaymessage(fd, " 3=Payon 4=Alberta 5=Izlude");
+ clif_displaymessage(fd, " 6=Al De Baran 7=Lutie 8=Comodo");
+ clif_displaymessage(fd, " 9=Yuno 10=Amatsu 11=Gon Ryun");
+ clif_displaymessage(fd, " 12=Umbala 13=Niflheim 14=Lou Yang");
+ clif_displaymessage(fd, " 15=Novice Grounds 16=Prison 17=Jawaii");
+ clif_displaymessage(fd, " 18=Ayothaya 19=Einbroch 20=Lighthalzen");
+ clif_displaymessage(fd, " 21=Einbech 22=Hugel");
+ return -1;
+ } else {
+ // get possible name of the city and add .gat if not in the name
+ map_name[MAP_NAME_LENGTH-1] = '\0';
+ for (i = 0; map_name[i]; i++)
+ map_name[i] = tolower(map_name[i]);
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ // try to see if it's a name, and not a number (try a lot of possibilities, write errors and abbreviations too)
+ if (strncmp(map_name, "prontera.gat", 3) == 0) { // 3 first characters
+ town = 0;
+ } else if (strncmp(map_name, "morocc.gat", 3) == 0) { // 3 first characters
+ town = 1;
+ } else if (strncmp(map_name, "geffen.gat", 3) == 0) { // 3 first characters
+ town = 2;
+ } else if (strncmp(map_name, "payon.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "paion.gat", 3) == 0) { // writing error (3 first characters)
+ town = 3;
+ } else if (strncmp(map_name, "alberta.gat", 3) == 0) { // 3 first characters
+ town = 4;
+ } else if (strncmp(map_name, "izlude.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "islude.gat", 3) == 0) { // writing error (3 first characters)
+ town = 5;
+ } else if (strncmp(map_name, "aldebaran.gat", 3) == 0 || // 3 first characters
+ strcmp(map_name, "al.gat") == 0) { // al (de baran)
+ town = 6;
+ } else if (strncmp(map_name, "lutie.gat", 3) == 0 || // name of the city, not name of the map (3 first characters)
+ strcmp(map_name, "christmas.gat") == 0 || // name of the symbol
+ strncmp(map_name, "xmas.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "x-mas.gat", 3) == 0) { // writing error (3 first characters)
+ town = 7;
+ } else if (strncmp(map_name, "comodo.gat", 3) == 0) { // 3 first characters
+ town = 8;
+ } else if (strncmp(map_name, "yuno.gat", 3) == 0) { // 3 first characters
+ town = 9;
+ } else if (strncmp(map_name, "amatsu.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "ammatsu.gat", 3) == 0) { // writing error (3 first characters)
+ town = 10;
+ } else if (strncmp(map_name, "gonryun.gat", 3) == 0) { // 3 first characters
+ town = 11;
+ } else if (strncmp(map_name, "umbala.gat", 3) == 0) { // 3 first characters
+ town = 12;
+ } else if (strncmp(map_name, "niflheim.gat", 3) == 0) { // 3 first characters
+ town = 13;
+ } else if (strncmp(map_name, "louyang.gat", 3) == 0) { // 3 first characters
+ town = 14;
+ } else if (strncmp(map_name, "new_1-1.gat", 3) == 0 || // 3 first characters (or "newbies")
+ strncmp(map_name, "startpoint.gat", 3) == 0 || // name of the position (3 first characters)
+ strncmp(map_name, "begining.gat", 3) == 0) { // name of the position (3 first characters)
+ town = 15;
+ } else if (strncmp(map_name, "sec_pri.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "prison.gat", 3) == 0 || // name of the position (3 first characters)
+ strncmp(map_name, "jails.gat", 3) == 0) { // name of the position
+ town = 16;
+ } else if (strncmp(map_name, "jawaii.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "jawai.gat", 3) == 0) { // writing error (3 first characters)
+ town = 17;
+ } else if (strncmp(map_name, "ayothaya.gat", 2) == 0 || // 2 first characters
+ strncmp(map_name, "ayotaya.gat", 2) == 0) { // writing error (2 first characters)
+ town = 18;
+ } else if (strncmp(map_name, "einbroch.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "ainbroch.gat", 3) == 0) { // writing error (3 first characters)
+ town = 19;
+ } else if (strncmp(map_name, "lighthalzen.gat", 3) == 0 || // 3 first characters
+ strncmp(map_name, "reichthalzen.gat", 3) == 0) { // 'alternative' name (3 first characters)
+ town = 20;
+ } else if (strncmp(map_name, "einbech.gat", 5) == 0) { // 5 first characters
+ town = 21;
+ } else if (strncmp(map_name, "hugel.gat", 3) == 0) { // 3 first characters
+ town = 22;
+ }
+
+ if (town >= -3 && town <= -1) {
+ if (sd->status.memo_point[-town-1].map) {
+ m = map_mapindex2mapid(sd->status.memo_point[-town-1].map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, sd->status.memo_point[-town-1].map, sd->status.memo_point[-town-1].x, sd->status.memo_point[-town-1].y, 3) == 0) {
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ sprintf(atcmd_output, msg_table[164], -town-1); // Your memo point #%d doesn't exist.
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+ } else if (town >= 0 && town < (int)(sizeof(data) / sizeof(data[0]))) {
+ m = map_mapname2mapid((char *)data[town].map);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ if (pc_setpos(sd, mapindex_name2id((char *)data[town].map), data[town].x, data[town].y, 3) == 0) {
+ clif_displaymessage(fd, msg_table[0]); // Warped.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else { // if you arrive here, you have an error in town variable when reading of names
+ clif_displaymessage(fd, msg_table[38]); // Invalid location number or name.
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_monster(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH];
+ char monster[NAME_LENGTH];
+ int mob_id;
+ int number = 0;
+ int x = 0, y = 0;
+ int count;
+ int i, j, k;
+ int mx, my, range;
+ nullpo_retr(-1, sd);
+
+ memset(name, '\0', sizeof(name));
+ memset(monster, '\0', sizeof(monster));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, msg_table[80]); // Give a display name and monster name/id please.
+ return -1;
+ }
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) > 1 ||
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) > 1) {
+ //All data can be left as it is.
+ } else if ((count=sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y)) > 1) {
+ //Here, it is possible name was not given and we are using monster for it.
+ if (count < 3) //Blank mob's name.
+ name[0] = '\0';
+ } else if (sscanf(message, "%23s %23s %d %d %d", name, monster, &number, &x, &y) > 1) {
+ //All data can be left as it is.
+ } else if (sscanf(message, "%23s", monster) > 0) {
+ //As before, name may be already filled.
+ name[0] = '\0';
+ } else {
+ clif_displaymessage(fd, msg_table[80]); // Give a display name and monster name/id please.
+ return -1;
+ }
+
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = mobdb_checkid(atoi(monster));
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]); // Cannot spawn emperium.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ if (battle_config.etc_log)
+ ShowInfo("%s monster='%s' name='%s' id=%d count=%d (%d,%d)\n", command, monster, name, mob_id, number, x, y);
+
+ count = 0;
+ range = (int)sqrt(number) / 2;
+ range = range * 2 + 5; // calculation of an odd number (+ 4 area around)
+ for (i = 0; i < number; i++) {
+ j = 0;
+ k = 0;
+ while(j++ < 8 && k == 0) { // try 8 times to spawn the monster (needed for close area)
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % range - (range / 2));
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % range - (range / 2));
+ else
+ my = y;
+ k = mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id, 1, "");
+ }
+ count += (k != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ if (number == count)
+ clif_displaymessage(fd, msg_table[39]); // All monster summoned!
+ else {
+ sprintf(atcmd_output, msg_table[240], count); // %d monster(s) summoned!
+ clif_displaymessage(fd, atcmd_output);
+ }
+ else {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+// small monster spawning [Valaris]
+int atcommand_monstersmall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message) {
+ char name[NAME_LENGTH] = "";
+ char monster[NAME_LENGTH] = "";
+ int mob_id = 0;
+ int number = 0;
+ int x = 0;
+ int y = 0;
+ int count;
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(monster);
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]);
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]);
+ return -1;
+ }
+
+ if (mobdb_checkid(mob_id) == 0) {
+ clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ count = 0;
+ for (i = 0; i < number; i++) {
+ int mx, my;
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % 11 - 5);
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % 11 - 5);
+ else
+ my = y;
+ count += (mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id+MAX_MOB_DB, 1, "") != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ clif_displaymessage(fd, msg_table[39]); // Monster Summoned!!
+ else
+ clif_displaymessage(fd, msg_table[40]); // Invalid Monster ID.
+
+ return 0;
+}
+// big monster spawning [Valaris]
+int atcommand_monsterbig(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message) {
+ char name[NAME_LENGTH] = "";
+ char monster[NAME_LENGTH] = "";
+ int mob_id = 0;
+ int number = 0;
+ int x = 0;
+ int y = 0;
+ int count;
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ if (sscanf(message, "\"%23[^\"]\" %23s %d %d %d", name, monster, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s \"%23[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 &&
+ sscanf(message, "%23s %d %23s %d %d", monster, &number, name, &x, &y) < 1) {
+ clif_displaymessage(fd, "Give a monster name/id please.");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(monster);
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]);
+ return -1;
+ }
+
+ if (mob_id == MOBID_EMPERIUM) {
+ clif_displaymessage(fd, msg_table[83]);
+ return -1;
+ }
+
+ if (mobdb_checkid(mob_id) == 0) {
+ clif_displaymessage(fd, "Invalid monster ID"); // Invalid Monster ID.
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ if (strlen(name) < 1)
+ strcpy(name, "--ja--");
+
+ // If value of atcommand_spawn_quantity_limit directive is greater than or equal to 1 and quantity of monsters is greater than value of the directive
+ if (battle_config.atc_spawn_quantity_limit >= 1 && number > battle_config.atc_spawn_quantity_limit)
+ number = battle_config.atc_spawn_quantity_limit;
+
+ count = 0;
+ for (i = 0; i < number; i++) {
+ int mx, my;
+ if (x <= 0)
+ mx = sd->bl.x + (rand() % 11 - 5);
+ else
+ mx = x;
+ if (y <= 0)
+ my = sd->bl.y + (rand() % 11 - 5);
+ else
+ my = y;
+ count += (mob_once_spawn((struct map_session_data*)sd, "this", mx, my, name, mob_id+2*MAX_MOB_DB, 1, "") != 0) ? 1 : 0;
+ }
+
+ if (count != 0)
+ clif_displaymessage(fd, msg_table[39]); // Monster Summoned!!
+ else
+ clif_displaymessage(fd, msg_table[40]); // Invalid Monster ID.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int atkillmonster_sub(struct block_list *bl, va_list ap) {
+ struct mob_data *md;
+ int flag;
+
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data *)bl);
+ flag = va_arg(ap, int);
+
+ if (flag)
+ mob_damage(NULL, md, md->hp, 2);
+ else
+ mob_delete(md);
+
+ return 0;
+}
+void atcommand_killmonster_sub(
+ const int fd, struct map_session_data* sd, const char* message,
+ const int drop)
+{
+ int map_id;
+ char map_name[MAP_NAME_LENGTH];
+
+ if (!sd) return;
+
+ memset(map_name, '\0', sizeof(map_name));
+
+ if (!message || !*message || sscanf(message, "%15s", map_name) < 1)
+ map_id = sd->bl.m;
+ else {
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+ if ((map_id = map_mapname2mapid(map_name)) < 0)
+ map_id = sd->bl.m;
+ }
+
+ map_foreachinmap(atkillmonster_sub, map_id, BL_MOB, drop);
+
+ clif_displaymessage(fd, msg_table[165]); // All monsters killed!
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_killmonster(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (!sd) return 0;
+ atcommand_killmonster_sub(fd, sd, message, 1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_killmonster2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (!sd) return 0;
+ atcommand_killmonster_sub(fd, sd, message, 0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_refine(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, position = 0, refine = 0, current_position, final_refine;
+ int count;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d %d", &position, &refine) < 2) {
+ clif_displaymessage(fd, "Please, enter a position and a amount (usage: @refine <equip position> <+/- amount>).");
+ return -1;
+ }
+
+ if (refine < -10)
+ refine = -10;
+ else if (refine > 10)
+ refine = 10;
+ else if (refine == 0)
+ refine = 1;
+
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && // ŠY“–ŒÂŠ‚Ì‘•”õ‚ð¸˜B‚·‚é
+ (sd->status.inventory[i].equip & position ||
+ (sd->status.inventory[i].equip && !position))) {
+ final_refine = sd->status.inventory[i].refine + refine;
+ if (final_refine > 10)
+ final_refine = 10;
+ else if (final_refine < 0)
+ final_refine = 0;
+ if (sd->status.inventory[i].refine != final_refine) {
+ sd->status.inventory[i].refine = final_refine;
+ current_position = sd->status.inventory[i].equip;
+ pc_unequipitem(sd, i, 3);
+ clif_refine(fd, sd, 0, i, sd->status.inventory[i].refine);
+ clif_delitem(sd, i, 1);
+ clif_additem(sd, i, 1, 0);
+ pc_equipitem(sd, i, current_position);
+ clif_misceffect((struct block_list*)&sd->bl, 3);
+ count++;
+ }
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[166]); // No item has been refined!
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[167]); // 1 item has been refined!
+ else {
+ sprintf(atcmd_output, msg_table[168], count); // %d items have been refined!
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_produce(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ int item_id, attribute = 0, star = 0;
+ int flag = 0;
+ struct item_data *item_data;
+ struct item tmp_item;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %d", item_name, &attribute, &star) < 1) {
+ clif_displaymessage(fd, "Please, enter at least an item name/id (usage: @produce <equip name or equip ID> <element> <# of very's>).");
+ return -1;
+ }
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (itemdb_exists(item_id) &&
+ (item_id <= 500 || item_id > 1099) &&
+ (item_id < 4001 || item_id > 4148) &&
+ (item_id < 7001 || item_id > 10019) &&
+ itemdb_isequip(item_id)) {
+ if (attribute < MIN_ATTRIBUTE || attribute > MAX_ATTRIBUTE)
+ attribute = ATTRIBUTE_NORMAL;
+ if (star < MIN_STAR || star > MAX_STAR)
+ star = 0;
+ memset(&tmp_item, 0, sizeof tmp_item);
+ tmp_item.nameid = item_id;
+ tmp_item.amount = 1;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = 0x00ff;
+ tmp_item.card[1] = ((star * 5) << 8) + attribute;
+ tmp_item.card[2] = GetWord(sd->char_id, 0);
+ tmp_item.card[3] = GetWord(sd->char_id, 1);
+ clif_produceeffect(sd, 0, item_id); // »‘¢ƒGƒtƒFƒNƒgƒpƒPƒbƒg
+ clif_misceffect(&sd->bl, 3); // ‘¼l‚ɂଌ÷‚ð’Ê’m
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, tmp_item.nameid, 1, &tmp_item);
+ }
+ //Logs
+
+ if ((flag = pc_additem(sd, &tmp_item, 1)))
+ clif_additem(sd, 0, 0, flag);
+ } else {
+ if (battle_config.error_log)
+ ShowError("@produce NOT WEAPON [%d]\n", item_id);
+ if (item_id != 0 && itemdb_exists(item_id))
+ sprintf(atcmd_output, msg_table[169], item_id, item_data->name); // This item (%d: '%s') is not an equipment.
+ else
+ sprintf(atcmd_output, msg_table[170]); // This item is not an equipment.
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Sub-function to display actual memo points
+ *------------------------------------------
+ */
+void atcommand_memo_sub(struct map_session_data* sd) {
+ int i;
+
+ if (!sd) return;
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ clif_displaymessage(sd->fd, "Your actual memo positions are (except respawn point):");
+ for (i = MIN_PORTAL_MEMO; i <= MAX_PORTAL_MEMO; i++) {
+ if (sd->status.memo_point[i].map)
+ sprintf(atcmd_output, "%d - %s (%d,%d)", i, mapindex_id2name(sd->status.memo_point[i].map), sd->status.memo_point[i].x, sd->status.memo_point[i].y);
+ else
+ sprintf(atcmd_output, msg_table[171], i); // %d - void
+ clif_displaymessage(sd->fd, atcmd_output);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_memo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int position = 0;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &position) < 1)
+ atcommand_memo_sub(sd);
+ else {
+ if (position >= MIN_PORTAL_MEMO && position <= MAX_PORTAL_MEMO) {
+ if (sd->bl.m >= 0 && (map[sd->bl.m].flag.nowarpto || map[sd->bl.m].flag.nomemo) && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[253]);
+ return -1;
+ }
+ if (sd->status.memo_point[position].map) {
+ sprintf(atcmd_output, msg_table[172], position, mapindex_id2name(sd->status.memo_point[position].map), sd->status.memo_point[position].x, sd->status.memo_point[position].y); // You replace previous memo position %d - %s (%d,%d).
+ clif_displaymessage(fd, atcmd_output);
+ }
+ sd->status.memo_point[position].map = map[sd->bl.m].index;
+ sd->status.memo_point[position].x = sd->bl.x;
+ sd->status.memo_point[position].y = sd->bl.y;
+ clif_skill_memo(sd, 0);
+ if (pc_checkskill(sd, AL_WARP) <= (position + 1))
+ clif_displaymessage(fd, msg_table[173]); // Note: you don't have the 'Warp' skill level to use it.
+ atcommand_memo_sub(sd);
+ } else {
+ sprintf(atcmd_output, "Please, enter a valid position (usage: @memo <memo_position:%d-%d>).", MIN_PORTAL_MEMO, MAX_PORTAL_MEMO);
+ clif_displaymessage(fd, atcmd_output);
+ atcommand_memo_sub(sd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_gat(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int y;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ for (y = 2; y >= -2; y--) {
+ sprintf(atcmd_output, "%s (x= %d, y= %d) %02X %02X %02X %02X %02X",
+ map[sd->bl.m].name, sd->bl.x - 2, sd->bl.y + y,
+ map_getcell(sd->bl.m, sd->bl.x - 2, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x - 1, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x + 1, sd->bl.y + y, CELL_GETTYPE),
+ map_getcell(sd->bl.m, sd->bl.x + 2, sd->bl.y + y, CELL_GETTYPE));
+
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_packet(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ static int packet_mode = 0;
+ int i, x = 0, y = 0;
+ nullpo_retr(-1, sd);
+
+ if (strstr(command, "packetmode")) {
+ packet_mode = atoi(message);
+ clif_displaymessage(fd, "Packet mode changed.");
+ return 0;
+ }
+
+ if (!message || !*message || (i = sscanf(message, "%d %d", &x, &y)) < 1) {
+ clif_displaymessage(fd, "Please, enter a status type/flag (usage: @packet <status type> <flag>).");
+ return -1;
+ }
+ if (i == 1) y = 1;
+
+ switch (packet_mode)
+ {
+ case 0:
+ clif_status_change(&sd->bl, x, y);
+ break;
+ case 1:
+ sd->status.skill[sd->cloneskill_id].id=0;
+ sd->status.skill[sd->cloneskill_id].lv=0;
+ sd->status.skill[sd->cloneskill_id].flag=0;
+ sd->cloneskill_id = x;
+ sd->status.skill[x].id = x;
+ sd->status.skill[x].lv = skill_get_max(x);
+ sd->status.skill[x].flag = 13;//cloneskill flag
+ clif_skillinfoblock(sd);
+ break;
+ case 2:
+ clif_skill_nodamage(&sd->bl,&sd->bl,x,y,1);
+ case 3:
+ clif_skill_poseffect(&sd->bl,x,y,sd->bl.x,sd->bl.y,gettick());
+ default:
+ break;
+ //added later
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @waterlevel [Skotlex]
+ *------------------------------------------
+ */
+
+int atcommand_waterlevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int newlevel;
+ if (!message || !*message || sscanf(message, "%d", &newlevel) < 1) {
+ sprintf(atcmd_output, "%s's current water level: %d", map[sd->bl.m].name, map_waterheight(map[sd->bl.m].name));
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+
+ if (map_setwaterheight(sd->bl.m, map[sd->bl.m].name, newlevel)) {
+ if (newlevel > 0)
+ sprintf(atcmd_output, "%s's water level changed to: %d", map[sd->bl.m].name, newlevel);
+ else
+ sprintf(atcmd_output, "Removed %s's water level information.", map[sd->bl.m].name);
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sprintf(atcmd_output, "Failed to change %s's water level.", map[sd->bl.m].name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ return 0;
+}
+
+/*==========================================
+ * @stpoint (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_statuspoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int point, new_status_point;
+
+ if (!message || !*message || (point = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter a number (usage: @stpoint <number of points>).");
+ return -1;
+ }
+
+ new_status_point = (int)sd->status.status_point + point;
+ if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow
+ new_status_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow
+ new_status_point = 0;
+
+ if (new_status_point != (int)sd->status.status_point) {
+ sd->status.status_point = (short)new_status_point;
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ clif_displaymessage(fd, msg_table[174]); // Number of status points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @skpoint (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_skillpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int point, new_skill_point;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (point = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter a number (usage: @skpoint <number of points>).");
+ return -1;
+ }
+
+ new_skill_point = (int)sd->status.skill_point + point;
+ if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow
+ new_skill_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow
+ new_skill_point = 0;
+
+ if (new_skill_point != (int)sd->status.skill_point) {
+ sd->status.skill_point = (short)new_skill_point;
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ clif_displaymessage(fd, msg_table[175]); // Number of skill points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @zeny (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int atcommand_zeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int zeny, new_zeny;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (zeny = atoi(message)) == 0) {
+ clif_displaymessage(fd, "Please, enter an amount (usage: @zeny <amount>).");
+ return -1;
+ }
+
+ new_zeny = sd->status.zeny + zeny;
+ if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
+ new_zeny = MAX_ZENY;
+ else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
+ new_zeny = 0;
+
+ if (new_zeny != sd->status.zeny) {
+ sd->status.zeny = new_zeny;
+ clif_updatestatus(sd, SP_ZENY);
+ clif_displaymessage(fd, msg_table[176]); // Number of zenys changed!
+ } else {
+ if (zeny < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_param(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, index, value = 0, new_value;
+ const char* param[] = { "@str", "@agi", "@vit", "@int", "@dex", "@luk", NULL };
+ short* status[] = {
+ &sd->status.str, &sd->status.agi, &sd->status.vit,
+ &sd->status.int_, &sd->status.dex, &sd->status.luk
+ };
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0) {
+ sprintf(atcmd_output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>).");
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ index = -1;
+ for (i = 0; param[i] != NULL; i++) {
+ if (strcmpi(command, param[i]) == 0) {
+ index = i;
+ break;
+ }
+ }
+ if (index < 0 || index > MAX_STATUS_TYPE) { // normaly impossible...
+ sprintf(atcmd_output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>).");
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ }
+
+ new_value = (int)*status[index] + value;
+ if (value > 0 && (value > pc_maxparameter(sd) || new_value > pc_maxparameter(sd))) // fix positiv overflow
+ new_value = pc_maxparameter(sd);
+ else if (value < 0 && (value < -(int)pc_maxparameter(sd) || new_value < 1)) // fix negativ overflow
+ new_value = 1;
+
+ if (new_value != (int)*status[index]) {
+ *status[index] = new_value;
+ clif_updatestatus(sd, SP_STR + index);
+ clif_updatestatus(sd, SP_USTR + index);
+ status_calc_pc(sd, 0);
+ clif_displaymessage(fd, msg_table[42]); // Stat changed.
+ } else {
+ if (value < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** Stat all by fritz (rewritten by [Yor])
+int atcommand_stat_all(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int index, count, value = 0, new_value;
+ short* status[] = {
+ &sd->status.str, &sd->status.agi, &sd->status.vit,
+ &sd->status.int_, &sd->status.dex, &sd->status.luk
+ };
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0)
+ value = pc_maxparameter(sd);
+
+ count = 0;
+ for (index = 0; index < (int)(sizeof(status) / sizeof(status[0])); index++) {
+
+ new_value = (int)*status[index] + value;
+ if (value > 0 && (value > pc_maxparameter(sd) || new_value > pc_maxparameter(sd))) // fix positiv overflow
+ new_value = pc_maxparameter(sd);
+ else if (value < 0 && (value < -(int)pc_maxparameter(sd) || new_value < 1)) // fix negative overflow
+ new_value = 1;
+
+ if (new_value != (int)*status[index]) {
+ *status[index] = new_value;
+ clif_updatestatus(sd, SP_STR + index);
+ clif_updatestatus(sd, SP_USTR + index);
+ status_calc_pc(sd, 0);
+ count++;
+ }
+ }
+
+ if (count > 0) // if at least 1 stat modified
+ clif_displaymessage(fd, msg_table[84]); // All stats changed!
+ else {
+ if (value < 0)
+ clif_displaymessage(fd, msg_table[177]); // Impossible to decrease a stat.
+ else
+ clif_displaymessage(fd, msg_table[178]); // Impossible to increase a stat.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guildlevelup(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int level = 0;
+ short added_level;
+ struct guild *guild_info;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d", &level) < 1 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a valid level (usage: @guildlvup/@guildlvlup <# of levels>).");
+ return -1;
+ }
+
+ if (sd->status.guild_id <= 0 || (guild_info = guild_search(sd->status.guild_id)) == NULL) {
+ clif_displaymessage(fd, msg_table[43]); // You're not in a guild.
+ return -1;
+ }
+ if (strcmp(sd->status.name, guild_info->master) != 0) {
+ clif_displaymessage(fd, msg_table[44]); // You're not the master of your guild.
+ return -1;
+ }
+
+ added_level = (short)level;
+ if (level > 0 && (level > MAX_GUILDLEVEL || added_level > ((short)MAX_GUILDLEVEL - guild_info->guild_lv))) // fix positiv overflow
+ added_level = (short)MAX_GUILDLEVEL - guild_info->guild_lv;
+ else if (level < 0 && (level < -MAX_GUILDLEVEL || added_level < (1 - guild_info->guild_lv))) // fix negativ overflow
+ added_level = 1 - guild_info->guild_lv;
+
+ if (added_level != 0) {
+ intif_guild_change_basicinfo(guild_info->guild_id, GBI_GUILDLV, &added_level, 2);
+ clif_displaymessage(fd, msg_table[179]); // Guild level changed.
+ } else {
+ clif_displaymessage(fd, msg_table[45]); // Guild level change failed.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_makeegg(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct item_data *item_data;
+ int id, pet_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a monter/egg name/id (usage: @makeegg <pet_id>).");
+ return -1;
+ }
+
+ if ((item_data = itemdb_searchname(message)) != NULL) // for egg name
+ id = item_data->nameid;
+ else if ((id = mobdb_searchname(message)) == 0) // for monster name
+ id = atoi(message);
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ } else {
+ clif_displaymessage(fd, msg_table[180]); // The monter/egg name/id doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hatch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.pet_id <= 0)
+ clif_sendegg(sd);
+ else {
+ clif_displaymessage(fd, msg_table[181]); // You already have a pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_petfriendly(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int friendly;
+ int t;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (friendly = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a valid value (usage: @petfriendly <0-1000>).");
+ return -1;
+ }
+
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (friendly >= 0 && friendly <= 1000) {
+ if (friendly != sd->pet.intimate) {
+ t = sd->pet.intimate;
+ sd->pet.intimate = friendly;
+ clif_send_petstatus(sd);
+ if (battle_config.pet_status_support) {
+ if ((sd->pet.intimate > 0 && t <= 0) ||
+ (sd->pet.intimate <= 0 && t > 0)) {
+ if (sd->bl.prev != NULL)
+ status_calc_pc(sd, 0);
+ else
+ status_calc_pc(sd, 2);
+ }
+ }
+ clif_displaymessage(fd, msg_table[182]); // Pet friendly value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_pethungry(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hungry;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (hungry = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a valid number (usage: @pethungry <0-100>).");
+ return -1;
+ }
+
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (hungry >= 0 && hungry <= 100) {
+ if (hungry != sd->pet.hungry) {
+ sd->pet.hungry = hungry;
+ clif_send_petstatus(sd);
+ clif_displaymessage(fd, msg_table[185]); // Pet hungry value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[186]); // Pet hungry is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_petrename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->status.pet_id > 0 && sd->pd) {
+ if (sd->pet.rename_flag != 0) {
+ sd->pet.rename_flag = 0;
+ intif_save_petdata(sd->status.account_id, &sd->pet);
+ clif_send_petstatus(sd);
+ clif_displaymessage(fd, msg_table[187]); // You can now rename your pet.
+ } else {
+ clif_displaymessage(fd, msg_table[188]); // You can already rename your pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[184]); // Sorry, but you have no pet.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int
+atcommand_recall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @recall <char name>).");
+ return -1;
+ }
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+ if(sscanf(message, "%23[^\n]", atcmd_player_name) < 1)
+ return -1;
+ if(strncmp(sd->status.name,atcmd_player_name,NAME_LENGTH)==0)
+ return -1;
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorized to warp this player from its actual map.");
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], atcmd_player_name); // %s recalled!
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_revive(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @revive <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isdead(pl_sd)) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(pl_sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ return 0;
+ }
+ return -1;
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charblock command (usage: charblock <player_name>)
+ * This command do a definitiv ban on a player
+ *------------------------------------------
+ */
+int atcommand_char_block(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charblock/@block <name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charban command (usage: charban <time> <player_name>)
+ * This command do a limited ban on a player
+ * Time is done as follows:
+ * Adjustment value (-1, 1, +1, etc...)
+ * Modified element:
+ * a or y: year
+ * m: month
+ * j or d: day
+ * h: hour
+ * mn: minute
+ * s: second
+ * <example> @ban +1m-2mn1s-6y test_player
+ * this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time.
+ *------------------------------------------
+ */
+int atcommand_char_ban(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char * modif_p;
+ int year, month, day, hour, minute, second, value;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%s %99[^\n]", atcmd_output, atcmd_player_name) < 2) {
+ clif_displaymessage(fd, "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish <time> <name>).");
+ return -1;
+ }
+
+ atcmd_output[sizeof(atcmd_output)-1] = '\0';
+
+ modif_p = atcmd_output;
+ year = month = day = hour = minute = second = 0;
+ while (modif_p[0] != '\0') {
+ value = atoi(modif_p);
+ if (value == 0)
+ modif_p++;
+ else {
+ if (modif_p[0] == '-' || modif_p[0] == '+')
+ modif_p++;
+ while (modif_p[0] >= '0' && modif_p[0] <= '9')
+ modif_p++;
+ if (modif_p[0] == 's') {
+ second = value;
+ modif_p++;
+ } else if (modif_p[0] == 'm' && modif_p[1] == 'n') {
+ minute = value;
+ modif_p = modif_p + 2;
+ } else if (modif_p[0] == 'h') {
+ hour = value;
+ modif_p++;
+ } else if (modif_p[0] == 'd' || modif_p[0] == 'j') {
+ day = value;
+ modif_p++;
+ } else if (modif_p[0] == 'm') {
+ month = value;
+ modif_p++;
+ } else if (modif_p[0] == 'y' || modif_p[0] == 'a') {
+ year = value;
+ modif_p++;
+ } else if (modif_p[0] != '\0') {
+ modif_p++;
+ }
+ }
+ }
+ if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) {
+ clif_displaymessage(fd, msg_table[85]); // Invalid time for ban command.
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 2, year, month, day, hour, minute, second); // type: 2 - ban
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charunblock command (usage: charunblock <player_name>)
+ *------------------------------------------
+ */
+int atcommand_char_unblock(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charunblock <player_name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ // send answer to login server via char-server
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 3, 0, 0, 0, 0, 0, 0); // type: 3 - unblock
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charunban command (usage: charunban <player_name>)
+ *------------------------------------------
+ */
+int atcommand_char_unban(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charunban <player_name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(atcmd_player_name) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(atcmd_player_name) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ // send answer to login server via char-server
+ chrif_char_ask_name(sd->status.account_id, atcmd_player_name, 4, 0, 0, 0, 0, 0, 0); // type: 4 - unban
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_night(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (night_flag != 1) {
+ map_night_timer(night_timer_tid, 0, 0, 1);
+ } else {
+ clif_displaymessage(fd, msg_table[89]); // Sorry, it's already the night. Impossible to execute the command.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_day(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (night_flag != 0) {
+ map_day_timer(day_timer_tid, 0, 0, 1);
+ } else {
+ clif_displaymessage(fd, msg_table[90]); // Sorry, it's already the day. Impossible to execute the command.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_doom(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,2);
+ pl_allsd = map_getallusers(&users);
+ for(i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can doom only lower or same gm level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement.
+ }
+ }
+ clif_displaymessage(fd, msg_table[62]); // Judgement was made.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_doommap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+ clif_specialeffect(&sd->bl,450,3);
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && sd->bl.m == pl_sd->bl.m &&
+ pc_isGM(sd) >= pc_isGM(pl_sd)) // you can doom only lower or same gm level
+ {
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+// clif_specialeffect(&pl_sd->bl,450,1);
+ clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement.
+ }
+ }
+ clif_displaymessage(fd, msg_table[62]); // Judgement was made.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static void atcommand_raise_sub(struct map_session_data* sd)
+{
+ if (sd && sd->state.auth && pc_isdead(sd)) {
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ pc_setstand(sd);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_displaymessage(sd->fd, msg_table[63]); // Mercy has been shown.
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_raise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, users;
+ struct map_session_data **all_sd;
+
+ nullpo_retr(-1, sd);
+
+ all_sd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ atcommand_raise_sub(all_sd[i]);
+ }
+ clif_displaymessage(fd, msg_table[64]); // Mercy has been granted.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_raisemap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data **pl_allsd;
+ int i, users;
+
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if (sd->bl.m == pl_allsd[i]->bl.m)
+ atcommand_raise_sub(pl_allsd[i]);
+ }
+ clif_displaymessage(fd, msg_table[64]); // Mercy has been granted.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kick(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @kick <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_kickall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kick only lower or same gm level
+ if (sd->status.account_id != pl_sd->status.account_id)
+ clif_GM_kick(sd, pl_sd, 0);
+ }
+ }
+
+ clif_displaymessage(fd, msg_table[195]); // All players have been kicked!
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_allskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ pc_allskillup(sd); // all skills
+ sd->status.skill_point = 0; // 0 skill points
+ clif_updatestatus(sd, SP_SKILLPOINT); // update
+ clif_displaymessage(fd, msg_table[76]); // You have received all skills.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_questskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skill_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (skill_id = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number (usage: @questskill <#:0+>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if (pc_checkskill(sd, skill_id) == 0) {
+ pc_skill(sd, skill_id, 1, 0);
+ clif_displaymessage(fd, msg_table[70]); // You have learned the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[196]); // You already have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_lostskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skill_id;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (skill_id = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number (usage: @lostskill <#:0+>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if (pc_checkskill(sd, skill_id) > 0) {
+ sd->status.skill[skill_id].lv = 0;
+ sd->status.skill[skill_id].flag = 0;
+ clif_skillinfoblock(sd);
+ clif_displaymessage(fd, msg_table[71]); // You have forgotten the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[201]); // You don't have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_spiritball(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int number;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || (number = atoi(message)) < 0) {
+ clif_displaymessage(fd, "Please, enter a spirit ball number (usage: @spiritball <number: 0-1000>).");
+ return -1;
+ }
+
+ // set max number to avoid server/client crash (500 create big balls of several balls: no visial difference with more)
+ if (number > 500)
+ number = 500;
+
+ if (number >= 0 && number <= 0x7FFF) {
+ if (sd->spiritball != number || number > 499) {
+ if (sd->spiritball > 0)
+ pc_delspiritball(sd, sd->spiritball, 1);
+ sd->spiritball = number;
+ clif_spiritball(sd);
+ // no message, player can look the difference
+ if (number > 1000)
+ clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
+ } else {
+ clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_party(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char party[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(party, '\0', sizeof(party));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name (usage: @party <party_name>).");
+ return -1;
+ }
+
+ party_create(sd, party, 0, 0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_guild(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char guild[NAME_LENGTH];
+ int prev;
+ nullpo_retr(-1, sd);
+
+ memset(guild, '\0', sizeof(guild));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name (usage: @guild <guild_name>).");
+ return -1;
+ }
+
+ prev = battle_config.guild_emperium_check;
+ battle_config.guild_emperium_check = 0;
+ guild_create(sd, guild);
+ battle_config.guild_emperium_check = prev;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_agitstart(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (agit_flag == 1) {
+ clif_displaymessage(fd, msg_table[73]); // Already it has started siege warfare.
+ return -1;
+ }
+
+ agit_flag = 1;
+ guild_agit_start();
+ clif_displaymessage(fd, msg_table[72]); // Guild siege warfare start!
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_agitend(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (agit_flag == 0) {
+ clif_displaymessage(fd, msg_table[75]); // Siege warfare hasn't started yet.
+ return -1;
+ }
+
+ agit_flag = 0;
+ guild_agit_end();
+ clif_displaymessage(fd, msg_table[74]); // Guild siege warfare end!
+
+ return 0;
+}
+
+/*==========================================
+ * @mapexit‚Ń}ƒbƒvƒT[ƒo[‚ðI—¹‚³‚¹‚é
+ *------------------------------------------
+ */
+int atcommand_mapexit(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id)
+ clif_GM_kick(sd, pl_sd, 0);
+ }
+ clif_GM_kick(sd, sd, 0);
+
+ flush_fifos();
+
+ runflag = 0;
+
+ return 0;
+}
+
+/*==========================================
+ * idsearch <part_of_name>: revrited by [Yor]
+ *------------------------------------------
+ */
+int atcommand_idsearch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ unsigned int i, match;
+ struct item_data *item;
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%99s", item_name) < 0) {
+ clif_displaymessage(fd, "Please, enter a part of item name (usage: @idsearch <part_of_item_name>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, msg_table[77], item_name); // The reference result of '%s' (name: id):
+ clif_displaymessage(fd, atcmd_output);
+ match = 0;
+ for(i = 0; i < 20000; i++) {
+ if ((item = itemdb_exists(i)) != NULL && strstr(item->jname, item_name) != NULL) {
+ match++;
+ sprintf(atcmd_output, msg_table[78], item->jname, item->nameid); // %s: %d
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ sprintf(atcmd_output, msg_table[79], match); // It is %d affair above.
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall All Characters Online To Your Location
+ *------------------------------------------
+ */
+int atcommand_recallall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i;
+ int count, users;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else {
+ if (pc_isdead(pl_sd)) { //Wake them up
+ pc_setstand(pl_sd);
+ pc_setrestartvalue(pl_sd,1);
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ }
+
+ clif_displaymessage(fd, msg_table[92]); // All characters recalled!
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Recall online characters of a guild to your location
+ *------------------------------------------
+ */
+int atcommand_guildrecall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users, count;
+ char guild_name[NAME_LENGTH];
+ struct guild *g;
+ nullpo_retr(-1, sd);
+
+ memset(guild_name, '\0', sizeof(guild_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildrecall <guild_name/id>).");
+ return -1;
+ }
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number
+ (g = guild_search(atoi(message))) != NULL) {
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pl_sd->status.guild_id == g->guild_id) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ sprintf(atcmd_output, msg_table[93], g->name); // All online characters of the %s guild are near you.
+ clif_displaymessage(fd, atcmd_output);
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Recall online characters of a party to your location
+ *------------------------------------------
+ */
+int atcommand_partyrecall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd, **pl_allsd;
+ char party_name[NAME_LENGTH];
+ struct party *p;
+ int count, users;
+ nullpo_retr(-1, sd);
+
+ memset(party_name, '\0', sizeof(party_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyrecall <party_name/id>).");
+ return -1;
+ }
+
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map.");
+ return -1;
+ }
+
+ if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number
+ (p = party_search(atoi(message))) != NULL) {
+ count = 0;
+
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && sd->status.account_id != pl_sd->status.account_id &&
+ pl_sd->status.party_id == p->party_id) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd))
+ count++;
+ else
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ }
+ }
+ sprintf(atcmd_output, msg_table[95], p->name); // All online characters of the %s party are near you.
+ clif_displaymessage(fd, atcmd_output);
+ if (count) {
+ sprintf(atcmd_output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloaditemdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ itemdb_reload();
+ clif_displaymessage(fd, msg_table[97]); // Item database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadmobdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ mob_reload();
+ do_final_pet();
+ read_petdb();
+ clif_displaymessage(fd, msg_table[98]); // Monster database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadskilldb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ skill_reload();
+ clif_displaymessage(fd, msg_table[99]); // Skill database reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ * @reloadatcommand
+ * atcommand_athena.conf ‚̃Šƒ[ƒh
+ *------------------------------------------
+ */
+int
+atcommand_reloadatcommand(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ atcommand_config_read(ATCOMMAND_CONF_FILENAME);
+ clif_displaymessage(fd, msg_table[254]);
+ return 0;
+}
+/*==========================================
+ * @reloadbattleconf
+ * battle_athena.conf ‚̃Šƒ[ƒh
+ *------------------------------------------
+ */
+int
+atcommand_reloadbattleconf(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ battle_config_read(BATTLE_CONF_FILENAME);
+ mob_reload(); //Needed as well so rate changes take effect.
+ clif_displaymessage(fd, msg_table[255]);
+ return 0;
+}
+/*==========================================
+ * @reloadstatusdb
+ * job_db1.txt job_db2.txt job_db2-2.txt
+ * refine_db.txt size_fix.txt
+ * ‚̃Šƒ[ƒh
+ *------------------------------------------
+ */
+int
+atcommand_reloadstatusdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ status_readdb();
+ clif_displaymessage(fd, msg_table[256]);
+ return 0;
+}
+/*==========================================
+ * @reloadpcdb
+ * exp.txt skill_tree.txt attr_fix.txt
+ * ‚̃Šƒ[ƒh
+ *------------------------------------------
+ */
+int
+atcommand_reloadpcdb(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ pc_readdb();
+ clif_displaymessage(fd, msg_table[257]);
+ return 0;
+}
+
+/*==========================================
+ * @reloadmotd [Valaris]
+ * Reloads motd.txt
+ *------------------------------------------
+ */
+int
+atcommand_reloadmotd(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ pc_read_motd();
+ clif_displaymessage(fd, msg_table[268]);
+ return 0;
+}
+
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadscript(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ atcommand_broadcast( fd, sd, "@broadcast", "eAthena Server is Rehashing..." );
+ atcommand_broadcast( fd, sd, "@broadcast", "You will feel a bit of lag at this point !" );
+ atcommand_broadcast( fd, sd, "@broadcast", "Reloading NPCs..." );
+ flush_fifos();
+
+ //do_init_npc();
+ script_reload();
+ npc_reload();
+ npc_event_do_oninit();
+
+ clif_displaymessage(fd, msg_table[100]); // Scripts reloaded.
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_reloadgmdb( // by [Yor]
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ chrif_reloadGMdb();
+
+ clif_displaymessage(fd, msg_table[101]); // Login-server asked to reload GM accounts and their level.
+
+ return 0;
+}
+
+/*==========================================
+ * @mapinfo <map name> [0-3] by MC_Cameri
+ * => Shows information about the map [map name]
+ * 0 = no additional information
+ * 1 = Show users in that map and their location
+ * 2 = Shows NPCs in that map
+ * 3 = Shows the shops/chats in that map (not implemented)
+ *------------------------------------------
+ */
+int atcommand_mapinfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ struct npc_data *nd = NULL;
+ struct chat_data *cd = NULL;
+ char direction[12];
+ int m_id, i, chat_num, users, list = 0;
+ unsigned short m_index;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(direction, '\0', sizeof(direction));
+
+ sscanf(message, "%d %23[^\n]", &list, atcmd_player_name);
+
+ if (list < 0 || list > 3) {
+ clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map]).");
+ return -1;
+ }
+
+ if (atcmd_player_name[0] == '\0') {
+ memcpy(atcmd_player_name, mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH);
+ atcmd_player_name[MAP_NAME_LENGTH] = '\0';
+ m_id = map_mapindex2mapid(sd->mapindex);
+ } else {
+ if (strstr(atcmd_player_name, ".gat") == NULL && strstr(atcmd_player_name, ".afm") == NULL && strlen(atcmd_player_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(atcmd_player_name, ".gat");
+ m_id = map_mapname2mapid(atcmd_player_name);
+ }
+ if (m_id < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ m_index = mapindex_name2id(atcmd_player_name); //This one shouldn't fail since the previous seek did not.
+
+ clif_displaymessage(fd, "------ Map Info ------");
+ chat_num = 0;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && (cd = (struct chat_data*)map_id2bl(pl_sd->chatID))) {
+ chat_num++;
+ }
+ }
+ sprintf(atcmd_output, "Map Name: %s | Players In Map: %d | NPCs In Map: %d | Chats In Map: %d", atcmd_player_name, map[m_id].users, map[m_id].npc_num, chat_num);
+ clif_displaymessage(fd, atcmd_output);
+ if (map[m_id].flag.alias)
+ strcat(atcmd_output, "This map is an alias (a named clone of some other map).");
+ clif_displaymessage(fd, "------ Map Flags ------");
+ strcpy(atcmd_output,"PvP Flags: ");
+ if (map[m_id].flag.pvp)
+ strcat(atcmd_output, "Pvp ON | ");
+ if (map[m_id].flag.nopvp)
+ strcat(atcmd_output, "NoPvp | ");
+ if (map[m_id].flag.pvp_noguild)
+ strcat(atcmd_output, "NoGuild | ");
+ if (map[m_id].flag.pvp_noparty)
+ strcat(atcmd_output, "NoParty | ");
+ if (map[m_id].flag.pvp_nightmaredrop)
+ strcat(atcmd_output, "NightmareDrop | ");
+ if (map[m_id].flag.pvp_nocalcrank)
+ strcat(atcmd_output, "NoCalcRank | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"GvG Flags: ");
+ if (map[m_id].flag.gvg)
+ strcat(atcmd_output, "GvG ON | ");
+ if (map[m_id].flag.gvg_dungeon)
+ strcat(atcmd_output, "GvG Dungeon | ");
+ if (map[m_id].flag.gvg_castle)
+ strcat(atcmd_output, "GvG Castle | ");
+ if (map[m_id].flag.gvg_noparty)
+ strcat(atcmd_output, "NoParty | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Teleport Flags: ");
+ if (map[m_id].flag.noteleport)
+ strcat(atcmd_output, "NoTeleport | ");
+ if (map[m_id].flag.monster_noteleport)
+ strcat(atcmd_output, "Monster NoTeleport | ");
+ if (map[m_id].flag.nowarp)
+ strcat(atcmd_output, "NoWarp | ");
+ if (map[m_id].flag.nowarpto)
+ strcat(atcmd_output, "NoWarpTo | ");
+ if (map[m_id].flag.noreturn)
+ strcat(atcmd_output, "NoReturn | ");
+ if (map[m_id].flag.nogo)
+ strcat(atcmd_output, "NoGo | ");
+ if (map[m_id].flag.nomemo)
+ strcat(atcmd_output, "NoMemo | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ sprintf(atcmd_output, "No Penalty: %s | No Zeny Penalty: %s", (map[m_id].flag.nopenalty) ? "On" : "Off", (map[m_id].flag.nozenypenalty) ? "On" : "Off");
+ clif_displaymessage(fd, atcmd_output);
+
+ if (map[m_id].flag.nosave) {
+ if (map[m_id].save.x == -1 || map[m_id].save.y == -1 )
+ sprintf(atcmd_output, "No Save, Save Point: %s,Random",mapindex_id2name(map[m_id].save.map));
+ else
+ sprintf(atcmd_output, "No Save, Save Point: %s,%d,%d",
+ mapindex_id2name(map[m_id].save.map),map[m_id].save.x,map[m_id].save.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ strcpy(atcmd_output,"Weather Flags: ");
+ if (map[m_id].flag.snow)
+ strcat(atcmd_output, "Snow | ");
+ if (map[m_id].flag.fog)
+ strcat(atcmd_output, "Fog | ");
+ if (map[m_id].flag.sakura)
+ strcat(atcmd_output, "Sakura | ");
+ if (map[m_id].flag.clouds)
+ strcat(atcmd_output, "Clouds | ");
+ if (map[m_id].flag.clouds2)
+ strcat(atcmd_output, "Clouds2 | ");
+ if (map[m_id].flag.fireworks)
+ strcat(atcmd_output, "Fireworks | ");
+ if (map[m_id].flag.leaves)
+ strcat(atcmd_output, "Leaves | ");
+ if (map[m_id].flag.rain)
+ strcat(atcmd_output, "Rain | ");
+ if (map[m_id].flag.indoors)
+ strcat(atcmd_output, "Indoors | ");
+ if (map[m_id].flag.nightenabled)
+ strcat(atcmd_output, "Displays Night | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Other Flags: ");
+ if (map[m_id].flag.nobranch)
+ strcat(atcmd_output, "NoBranch | ");
+ if (map[m_id].flag.notrade)
+ strcat(atcmd_output, "NoTrade | ");
+ if (map[m_id].flag.noskill)
+ strcat(atcmd_output, "NoSkill | ");
+ if (map[m_id].flag.noicewall)
+ strcat(atcmd_output, "NoIcewall | ");
+ clif_displaymessage(fd, atcmd_output);
+
+ strcpy(atcmd_output,"Other Flags: ");
+ if (map[m_id].flag.nobaseexp)
+ strcat(atcmd_output, "NoBaseEXP | ");
+ if (map[m_id].flag.nojobexp)
+ strcat(atcmd_output, "NoJobEXP | ");
+ if (map[m_id].flag.nomobloot)
+ strcat(atcmd_output, "NoMobLoot | ");
+ if (map[m_id].flag.nomvploot)
+ strcat(atcmd_output, "NoMVPLoot | ");
+ clif_displaymessage(fd, atcmd_output);
+
+
+ switch (list) {
+ case 0:
+ // Do nothing. It's list 0, no additional display.
+ break;
+ case 1:
+ clif_displaymessage(fd, "----- Players in Map -----");
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->mapindex == m_index) {
+ sprintf(atcmd_output, "Player '%s' (session #%d) | Location: %d,%d",
+ pl_sd->status.name, pl_sd->fd, pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ break;
+ case 2:
+ clif_displaymessage(fd, "----- NPCs in Map -----");
+ for (i = 0; i < map[m_id].npc_num;) {
+ nd = map[m_id].npc[i];
+ switch(nd->dir) {
+ case 0: strcpy(direction, "North"); break;
+ case 1: strcpy(direction, "North West"); break;
+ case 2: strcpy(direction, "West"); break;
+ case 3: strcpy(direction, "South West"); break;
+ case 4: strcpy(direction, "South"); break;
+ case 5: strcpy(direction, "South East"); break;
+ case 6: strcpy(direction, "East"); break;
+ case 7: strcpy(direction, "North East"); break;
+ case 9: strcpy(direction, "North"); break;
+ default: strcpy(direction, "Unknown"); break;
+ }
+ sprintf(atcmd_output, "NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d",
+ ++i, nd->name, direction, nd->class_, nd->bl.x, nd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ break;
+ case 3:
+ clif_displaymessage(fd, "----- Chats in Map -----");
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]) && (cd = (struct chat_data*)map_id2bl(pl_sd->chatID)) &&
+ pl_sd->mapindex == m_index &&
+ cd->usersd[0] == pl_sd) {
+ sprintf(atcmd_output, "Chat %d: %s | Player: %s | Location: %d %d",
+ i, cd->title, pl_sd->status.name, cd->bl.x, cd->bl.y);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " Users: %d/%d | Password: %s | Public: %s",
+ cd->users, cd->limit, cd->pass, (cd->pub) ? "Yes" : "No");
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ break;
+ default: // normally impossible to arrive here
+ clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map]).");
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_mount_peco(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (!pc_isriding(sd)) { // if actually no peco
+ if (pc_checkskill(sd, KN_RIDING)) {
+ pc_setoption(sd, sd->status.option | 0x0020);
+ clif_displaymessage(fd, msg_table[102]); // Mounted Peco.
+ } else {
+ clif_displaymessage(fd, msg_table[213]); // You can not mount a peco with your job.
+ return -1;
+ }
+ } else { //Dismount
+ pc_setoption(sd, sd->status.option & ~0x0020);
+ clif_displaymessage(fd, msg_table[214]); // Unmounted Peco.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_char_mount_peco(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charmountpeco <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+
+ if (!pc_isriding(pl_sd)) { // if actually no peco
+ if (pc_checkskill(pl_sd, KN_RIDING)) {
+ pc_setoption(pl_sd, pl_sd->status.option | 0x0020);
+ clif_displaymessage(fd, msg_table[216]); // Mounted Peco.
+ } else {
+ clif_displaymessage(fd, msg_table[217]); // You can not mount a peco with your job.
+ return -1;
+ }
+ } else { //Dismount
+ pc_setoption(pl_sd, pl_sd->status.option & ~0x0020);
+ clif_displaymessage(fd, msg_table[218]); // Unmounted Peco.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *Spy Commands by Syrus22
+ *------------------------------------------
+ */
+int atcommand_guildspy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char guild_name[NAME_LENGTH];
+ struct guild *g;
+ nullpo_retr(-1, sd);
+
+ memset(guild_name, '\0', sizeof(guild_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!enable_spy)
+ {
+ clif_displaymessage(fd, "The mapserver has spy command support disabled.");
+ return -1;
+ }
+ if (!message || !*message || sscanf(message, "%23[^\n]", guild_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildspy <guild_name/id>).");
+ return -1;
+ }
+
+ if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number
+ (g = guild_search(atoi(message))) != NULL) {
+ if (sd->guildspy == g->guild_id) {
+ sd->guildspy = 0;
+ sprintf(atcmd_output, msg_table[103], g->name); // No longer spying on the %s guild.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sd->guildspy = g->guild_id;
+ sprintf(atcmd_output, msg_table[104], g->name); // Spying on the %s guild.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_partyspy(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char party_name[NAME_LENGTH];
+ struct party *p;
+ nullpo_retr(-1, sd);
+
+ memset(party_name, '\0', sizeof(party_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!enable_spy)
+ {
+ clif_displaymessage(fd, "The mapserver has spy command support disabled.");
+ return -1;
+ }
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", party_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyspy <party_name/id>).");
+ return -1;
+ }
+
+ if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number
+ (p = party_search(atoi(message))) != NULL) {
+ if (sd->partyspy == p->party_id) {
+ sd->partyspy = 0;
+ sprintf(atcmd_output, msg_table[105], p->name); // No longer spying on the %s party.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ sd->partyspy = p->party_id;
+ sprintf(atcmd_output, msg_table[106], p->name); // Spying on the %s party.
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @repairall [Valaris]
+ *------------------------------------------
+ */
+int atcommand_repairall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int count, i;
+ nullpo_retr(-1, sd);
+
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && sd->status.inventory[i].attribute == 1) {
+ sd->status.inventory[i].attribute = 0;
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ count++;
+ }
+ }
+
+ if (count > 0) {
+ clif_misceffect(&sd->bl, 3);
+ clif_equiplist(sd);
+ clif_displaymessage(fd, msg_table[107]); // All items have been repaired.
+ } else {
+ clif_displaymessage(fd, msg_table[108]); // No item need to be repaired.
+ return -1;
+ }
+
+ return 0;
+}
+
+// Removed @nuke for now in favor of alchemist marine sphere skill [Valaris]
+int atcommand_nuke(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @nuke <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same GM level
+ skill_castend_damage_id(&pl_sd->bl, &pl_sd->bl, NPC_SELFDESTRUCTION, 99, gettick(), 0);
+ clif_displaymessage(fd, msg_table[109]); // Player has been nuked!
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_shownpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @enablenpc <NPC_name>).");
+ return -1;
+ }
+
+ if (npc_name2id(NPCname) != NULL) {
+ npc_enable(NPCname, 1);
+ clif_displaymessage(fd, msg_table[110]); // Npc Enabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int atcommand_hidenpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcoff <NPC_name>).");
+ return -1;
+ }
+
+ if (npc_name2id(NPCname) != NULL) {
+ npc_enable(NPCname, 0);
+ clif_displaymessage(fd, msg_table[112]); // Npc Disabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+int atcommand_loadnpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ FILE *fp;
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a script file name (usage: @loadnpc <file name>).");
+ return -1;
+ }
+
+ // check if script file exists
+ if ((fp = fopen(message, "r")) == NULL) {
+ clif_displaymessage(fd, msg_table[261]);
+ return -1;
+ }
+ fclose(fp);
+
+ // add to list of script sources and run it
+ npc_addsrcfile((char *)message);
+ npc_parsesrcfile((char *)message);
+
+ clif_displaymessage(fd, msg_table[262]);
+
+ return 0;
+}
+
+int atcommand_unloadnpc(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct npc_data *nd;
+ char NPCname[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ memset(NPCname, '\0', sizeof(NPCname));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", NPCname) < 1) {
+ clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcoff <NPC_name>).");
+ return -1;
+ }
+
+ if ((nd = npc_name2id(NPCname)) != NULL) {
+ npc_unload(nd);
+ clif_displaymessage(fd, msg_table[112]); // Npc Disabled.
+ } else {
+ clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * time in txt for time command (by [Yor])
+ *------------------------------------------
+ */
+char * txt_time(unsigned int duration) {
+ int days, hours, minutes, seconds;
+ char temp[256];
+ static char temp1[256];
+
+ memset(temp, '\0', sizeof(temp));
+ memset(temp1, '\0', sizeof(temp1));
+
+ if (duration < 0)
+ duration = 0;
+
+ days = duration / (60 * 60 * 24);
+ duration = duration - (60 * 60 * 24 * days);
+ hours = duration / (60 * 60);
+ duration = duration - (60 * 60 * hours);
+ minutes = duration / 60;
+ seconds = duration - (60 * minutes);
+
+ if (days < 2)
+ sprintf(temp, msg_table[219], days); // %d day
+ else
+ sprintf(temp, msg_table[220], days); // %d days
+ if (hours < 2)
+ sprintf(temp1, msg_table[221], temp, hours); // %s %d hour
+ else
+ sprintf(temp1, msg_table[222], temp, hours); // %s %d hours
+ if (minutes < 2)
+ sprintf(temp, msg_table[223], temp1, minutes); // %s %d minute
+ else
+ sprintf(temp, msg_table[224], temp1, minutes); // %s %d minutes
+ if (seconds < 2)
+ sprintf(temp1, msg_table[225], temp, seconds); // %s and %d second
+ else
+ sprintf(temp1, msg_table[226], temp, seconds); // %s and %d seconds
+
+ return temp1;
+}
+
+/*==========================================
+ * @time/@date/@server_date/@serverdate/@server_time/@servertime: Display the date/time of the server (by [Yor]
+ * Calculation management of GM modification (@day/@night GM commands) is done
+ *------------------------------------------
+ */
+int atcommand_servertime(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct TimerData * timer_data;
+ struct TimerData * timer_data2;
+ time_t time_server; // variable for number of seconds (used with time() function)
+ struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ...
+ char temp[256];
+ nullpo_retr(-1, sd);
+
+ memset(temp, '\0', sizeof(temp));
+
+ time(&time_server); // get time in seconds since 1/1/1970
+ datetime = localtime(&time_server); // convert seconds in structure
+ // like sprintf, but only for date/time (Sunday, November 02 2003 15:12:52)
+ strftime(temp, sizeof(temp)-1, msg_table[230], datetime); // Server time (normal time): %A, %B %d %Y %X.
+ clif_displaymessage(fd, temp);
+
+ if (battle_config.night_duration == 0 && battle_config.day_duration == 0) {
+ if (night_flag == 0)
+ clif_displaymessage(fd, msg_table[231]); // Game time: The game is in permanent daylight.
+ else
+ clif_displaymessage(fd, msg_table[232]); // Game time: The game is in permanent night.
+ } else if (battle_config.night_duration == 0)
+ if (night_flag == 1) { // we start with night
+ timer_data = get_timer(day_timer_tid);
+ sprintf(temp, msg_table[233], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in night for %s.
+ clif_displaymessage(fd, temp);
+ clif_displaymessage(fd, msg_table[234]); // Game time: After, the game will be in permanent daylight.
+ } else
+ clif_displaymessage(fd, msg_table[231]); // Game time: The game is in permanent daylight.
+ else if (battle_config.day_duration == 0)
+ if (night_flag == 0) { // we start with day
+ timer_data = get_timer(night_timer_tid);
+ sprintf(temp, msg_table[235], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in daylight for %s.
+ clif_displaymessage(fd, temp);
+ clif_displaymessage(fd, msg_table[236]); // Game time: After, the game will be in permanent night.
+ } else
+ clif_displaymessage(fd, msg_table[232]); // Game time: The game is in permanent night.
+ else {
+ if (night_flag == 0) {
+ timer_data = get_timer(night_timer_tid);
+ timer_data2 = get_timer(day_timer_tid);
+ sprintf(temp, msg_table[235], txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is actualy in daylight for %s.
+ clif_displaymessage(fd, temp);
+ if (DIFF_TICK(timer_data->tick, timer_data2->tick) > 0)
+ sprintf(temp, msg_table[237], txt_time(DIFF_TICK(timer_data->interval,DIFF_TICK(timer_data->tick,timer_data2->tick)) / 1000)); // Game time: After, the game will be in night for %s.
+ else
+ sprintf(temp, msg_table[237], txt_time(DIFF_TICK(timer_data2->tick,timer_data->tick)/1000)); // Game time: After, the game will be in night for %s.
+ clif_displaymessage(fd, temp);
+ sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s.
+ clif_displaymessage(fd, temp);
+ } else {
+ timer_data = get_timer(day_timer_tid);
+ timer_data2 = get_timer(night_timer_tid);
+ sprintf(temp, msg_table[233], txt_time(DIFF_TICK(timer_data->tick,gettick()) / 1000)); // Game time: The game is actualy in night for %s.
+ clif_displaymessage(fd, temp);
+ if (DIFF_TICK(timer_data->tick,timer_data2->tick) > 0)
+ sprintf(temp, msg_table[239], txt_time((timer_data->interval - DIFF_TICK(timer_data->tick, timer_data2->tick)) / 1000)); // Game time: After, the game will be in daylight for %s.
+ else
+ sprintf(temp, msg_table[239], txt_time(DIFF_TICK(timer_data2->tick, timer_data->tick) / 1000)); // Game time: After, the game will be in daylight for %s.
+ clif_displaymessage(fd, temp);
+ sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s.
+ clif_displaymessage(fd, temp);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @chardelitem <item_name_or_ID> <quantity> <player> (by [Yor]
+ * removes <quantity> item from a character
+ * item can be equiped or not.
+ * Inspired from a old command created by RoVeRT
+ *------------------------------------------
+ */
+int atcommand_chardelitem(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char item_name[100];
+ int i, number = 0, item_id, item_position, count;
+ struct item_data *item_data;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message || sscanf(message, "%s %d %99[^\n]", item_name, &number, atcmd_player_name) < 3 || number < 1) {
+ clif_displaymessage(fd, "Please, enter an item name/id, a quantity and a player name (usage: @chardelitem <item_name_or_ID> <quantity> <player>).");
+ return -1;
+ }
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id > 500) {
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ item_position = pc_search_inventory(pl_sd, item_id);
+ if (item_position >= 0) {
+ count = 0;
+ for(i = 0; i < number && item_position >= 0; i++) {
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(pl_sd, "A", 0, pl_sd->status.inventory[item_position].nameid, -1, &pl_sd->status.inventory[item_position]);
+ }
+ //Logs
+
+ pc_delitem(pl_sd, item_position, 1, 0);
+ count++;
+ item_position = pc_search_inventory(pl_sd, item_id); // for next loop
+ }
+ sprintf(atcmd_output, msg_table[113], count); // %d item(s) removed by a GM.
+ clif_displaymessage(pl_sd->fd, atcmd_output);
+ if (number == count)
+ sprintf(atcmd_output, msg_table[114], count); // %d item(s) removed from the player.
+ else
+ sprintf(atcmd_output, msg_table[115], count, count, number); // %d item(s) removed. Player had only %d on %d items.
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[116]); // Character does not have the item.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @jail <char_name> by [Yor]
+ * Special warp! No check with nowarp and nowarpto flag
+ *------------------------------------------
+ */
+int atcommand_jail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int x, y;
+ unsigned short m_index;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @jail <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM
+ switch(rand() % 2) {
+ case 0:
+ x = 24;
+ y = 75;
+ break;
+ default:
+ x = 49;
+ y = 75;
+ break;
+ }
+ m_index = mapindex_name2id(MAP_JAIL);
+ if (pc_setpos(pl_sd, m_index, x, y, 3) == 0) {
+ pc_setsavepoint(pl_sd, m_index, x, y); // Save Char Respawn Point in the jail room [Lupus]
+ clif_displaymessage(pl_sd->fd, msg_table[117]); // GM has send you in jails.
+ clif_displaymessage(fd, msg_table[118]); // Player warped in jails.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @unjail/@discharge <char_name> by [Yor]
+ * Special warp! No check with nowarp and nowarpto flag
+ *------------------------------------------
+ */
+int atcommand_unjail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ unsigned short m_index;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @unjail/@discharge <char_name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ m_index = mapindex_name2id(MAP_PRONTERA);
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM
+ if (pl_sd->bl.m != map_mapname2mapid(MAP_JAIL)) {
+ clif_displaymessage(fd, msg_table[119]); // This player is not in jails.
+ return -1;
+ } else if (pc_setpos(pl_sd, m_index, 0, 0, 3) == 0) { //old coords: 156,191
+ pc_setsavepoint(pl_sd, m_index, 0, 0); // Save char respawn point in Prontera
+ clif_displaymessage(pl_sd->fd, msg_table[120]); // GM has discharge you.
+ clif_displaymessage(fd, msg_table[121]); // Player warped to Prontera.
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @disguise <mob_id> by [Valaris] (simplified by [Yor])
+ *------------------------------------------
+ */
+int atcommand_disguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id (usage: @disguise <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ if ((id = atoi(message)) > 0)
+ { //Acquired an ID
+ if (!mobdb_checkid(id) && !npcdb_checkid(id))
+ id = 0; //Invalid id for either mobs or npcs.
+ } else { //Acquired a Name
+ if ((id = mobdb_searchname(message)) == 0)
+ {
+ struct npc_data* nd = npc_name2id(message);
+ if (nd != NULL)
+ id = nd->n;
+ }
+ }
+
+ if (id == 0)
+ { // Monster/NPC name/id hasn't been found.
+ clif_displaymessage(fd, msg_table[123]);
+ return -1;
+ }
+
+ /* The previous way....
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(message);
+
+ if ((mob_id >= 46 && mob_id <= 125) || (mob_id >= 700 && mob_id <= 718) || // NPC
+ (mob_id >= 721 && mob_id <= 755) || (mob_id >= 757 && mob_id <= 811) || // NPC
+ (mob_id >= 813 && mob_id <= 858) || // NPC
+ (mob_id > 1000 && mob_id < 1582)) { // monsters
+ */
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = id;
+ sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ clif_displaymessage(fd, msg_table[122]); // Disguise applied.
+
+ return 0;
+}
+
+/*==========================================
+ * DisguiseAll
+ *------------------------------------------
+ */
+
+int atcommand_disguiseall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int mob_id=0, i=0, users;
+ struct map_session_data *pl_sd, **pl_allsd;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id (usage: @disguiseall <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = atoi(message);
+
+ if (mobdb_checkid(mob_id) || npcdb_checkid(mob_id)) { //if mob or npc...
+ pl_allsd = map_getallusers(&users);
+ for(i=0; i < users; i++) {
+ if((pl_sd = pl_allsd[i])) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = mob_id;
+ pl_sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ }
+ }
+ clif_displaymessage(fd, msg_table[122]); // Disguise applied.
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @undisguise by [Yor]
+ *------------------------------------------
+ */
+int atcommand_undisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = 0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ clif_displaymessage(fd, msg_table[124]); // Undisguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[125]); // You're not disguised.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * UndisguiseAll
+ *------------------------------------------
+ */
+int atcommand_undisguiseall(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int i, users;
+ nullpo_retr(-1, sd);
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0; i < users; i++) {
+ if((pl_sd = pl_allsd[i]) && pl_sd->disguise) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = 0;
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ }
+ }
+ clif_displaymessage(fd, msg_table[124]); // Undisguise applied.
+
+ return 0;
+}
+
+/*==========================================
+ * @broadcast by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_broadcast(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @broadcast <message>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, "%s : %s", sd->status.name, message);
+ intif_GMmessage(atcmd_output, strlen(atcmd_output) + 1, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * @localbroadcast by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_localbroadcast(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @localbroadcast <message>).");
+ return -1;
+ }
+
+ sprintf(atcmd_output, "%s : %s", sd->status.name, message);
+
+ clif_GMmessage(&sd->bl, atcmd_output, strlen(atcmd_output) + 1, 1); // 1: ALL_SAMEMAP
+
+ return 0;
+}
+
+/*==========================================
+ * @chardisguise <mob_id> <character> by Kalaspuff (based off Valaris' and Yor's work)
+ *------------------------------------------
+ */
+int atcommand_chardisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int mob_id;
+ char mob_name[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(mob_name, '\0', sizeof(mob_name));
+
+ if (!message || !*message || sscanf(message, "%s %23[^\n]", mob_name, atcmd_player_name) < 2) {
+ clif_displaymessage(fd, "Please, enter a Monster/NPC name/id and a player name (usage: @chardisguise <monster_name_or_monster_ID> <char name>).");
+ return -1;
+ }
+
+ if ((mob_id = atoi(mob_name)) > 0)
+ { //Acquired an ID
+ if (!mobdb_checkid(mob_id) && !npcdb_checkid(mob_id))
+ mob_id = 0; //Invalid id for either mobs or npcs.
+ } else { //Acquired a Name
+ if ((mob_id = mobdb_searchname(mob_name)) == 0)
+ {
+ struct npc_data* nd = npc_name2id(mob_name);
+ if (nd != NULL)
+ mob_id = nd->n;
+ }
+ }
+
+ if (mob_id == 0)
+ {
+ clif_displaymessage(fd, msg_table[123]); // Monster/NPC name/id hasn't been found.
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can disguise only lower or same level
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = mob_id;
+ pl_sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ clif_displaymessage(fd, msg_table[140]); // Character's disguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charundisguise <character> by Kalaspuff (based off Yor's work)
+ *------------------------------------------
+ */
+int atcommand_charundisguise(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data* pl_sd;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charundisguise <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can undisguise only lower or same level
+ if (pl_sd->disguise) {
+ pc_stop_walking(pl_sd,0);
+ clif_clearchar(&pl_sd->bl, 0);
+ pl_sd->disguise = 0;
+ clif_changeoption(&pl_sd->bl);
+ clif_spawnpc(pl_sd);
+ clif_displaymessage(fd, msg_table[141]); // Character's undisguise applied.
+ } else {
+ clif_displaymessage(fd, msg_table[142]); // Character is not disguised.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @email <actual@email> <new@email> by [Yor]
+ *------------------------------------------
+ */
+int atcommand_email(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char actual_email[100];
+ char new_email[100];
+ nullpo_retr(-1, sd);
+
+ memset(actual_email, '\0', sizeof(actual_email));
+ memset(new_email, '\0', sizeof(new_email));
+
+ if (!message || !*message || sscanf(message, "%99s %99s", actual_email, new_email) < 2) {
+ clif_displaymessage(fd, "Please enter 2 emails (usage: @email <actual@email> <new@email>).");
+ return -1;
+ }
+
+ if (e_mail_check(actual_email) == 0) {
+ clif_displaymessage(fd, msg_table[144]); // Invalid actual email. If you have default e-mail, give a@a.com.
+ return -1;
+ } else if (e_mail_check(new_email) == 0) {
+ clif_displaymessage(fd, msg_table[145]); // Invalid new email. Please enter a real e-mail.
+ return -1;
+ } else if (strcmpi(new_email, "a@a.com") == 0) {
+ clif_displaymessage(fd, msg_table[146]); // New email must be a real e-mail.
+ return -1;
+ } else if (strcmpi(actual_email, new_email) == 0) {
+ clif_displaymessage(fd, msg_table[147]); // New email must be different of the actual e-mail.
+ return -1;
+ } else {
+ chrif_changeemail(sd->status.account_id, actual_email, new_email);
+ clif_displaymessage(fd, msg_table[148]); // Information sended to login-server via char-server.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *@effect
+ *------------------------------------------
+ */
+int atcommand_effect(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd, **pl_allsd;
+ int type = 0, flag = 0, i, users;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d", &type,&flag) < 2) {
+ clif_displaymessage(fd, "Please, enter at least a option (usage: @effect <type+>).");
+ return -1;
+ }
+ if(flag <=0){
+ clif_specialeffect(&sd->bl, type, flag);
+ clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
+ }
+ else{
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ clif_specialeffect(&pl_sd->bl, type, flag);
+ clif_displaymessage(pl_sd->fd, msg_table[229]); // Your effect has changed.
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @charcartlist <character>: Displays the items list of a player's cart.
+ *------------------------------------------
+ */
+int
+atcommand_character_cart_list(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char outputtmp[200];
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, count, counter, counter2;
+ nullpo_retr(-1, sd);
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charitemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_CART; i++) {
+ if (pl_sd->status.cart[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.cart[i].nameid)) != NULL) {
+ counter = counter + pl_sd->status.cart[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(atcmd_output, "------ Cart items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ if (pl_sd->status.cart[i].refine)
+ sprintf(atcmd_output, "%d %s %+d (%s %+d, id: %d)", pl_sd->status.cart[i].amount, item_data->name, pl_sd->status.cart[i].refine, item_data->jname, pl_sd->status.cart[i].refine, pl_sd->status.cart[i].nameid);
+ else
+ sprintf(atcmd_output, "%d %s (%s, id: %d)", pl_sd->status.cart[i].amount, item_data->name, item_data->jname, pl_sd->status.cart[i].nameid);
+ clif_displaymessage(fd, atcmd_output);
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (pl_sd->status.cart[i].card[j]) {
+ if ( (item_temp = itemdb_search(pl_sd->status.cart[i].card[j])) != NULL) {
+ if (atcmd_output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(atcmd_output, outputtmp);
+ }
+ }
+ }
+ if (atcmd_output[0] != '\0') {
+ atcmd_output[strlen(atcmd_output) - 2] = ')';
+ atcmd_output[strlen(atcmd_output) - 1] = '\0';
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found in the cart of this player.");
+ else {
+ sprintf(atcmd_output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @killer by MouseJstr
+ * enable killing players even when not in pvp
+ *------------------------------------------
+ */
+int
+atcommand_killer(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ sd->special_state.killer = !sd->special_state.killer;
+
+ if(sd->special_state.killer)
+ clif_displaymessage(fd, msg_table[241]);
+ else
+ clif_displaymessage(fd, msg_table[242]);
+
+ return 0;
+}
+
+/*==========================================
+ * @killable by MouseJstr
+ * enable other people killing you
+ *------------------------------------------
+ */
+int
+atcommand_killable(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ sd->special_state.killable = !sd->special_state.killable;
+
+ if(sd->special_state.killable)
+ clif_displaymessage(fd, msg_table[242]);
+ else
+ clif_displaymessage(fd, msg_table[241]);
+
+ return 0;
+}
+
+/*==========================================
+ * @charkillable by MouseJstr
+ * enable another player to be killed
+ *------------------------------------------
+ */
+int
+atcommand_charkillable(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+
+ return 0;
+}
+
+
+/*==========================================
+ * @skillon by MouseJstr
+ * turn skills on for the map
+ *------------------------------------------
+ */
+int
+atcommand_skillon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.noskill = 0;
+ clif_displaymessage(fd, msg_table[244]);
+ return 0;
+}
+
+/*==========================================
+ * @skilloff by MouseJstr
+ * Turn skills off on the map
+ *------------------------------------------
+ */
+int
+atcommand_skilloff(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.noskill = 1;
+ clif_displaymessage(fd, msg_table[243]);
+ return 0;
+}
+
+/*==========================================
+ * @npcmove by MouseJstr
+ *
+ * move a npc
+ *------------------------------------------
+ */
+int
+atcommand_npcmove(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x = 0, y = 0;
+ struct npc_data *nd = 0;
+ nullpo_retr(-1, sd);
+
+
+ if (!message || !*message)
+ return -1;
+
+ memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
+
+ if (sscanf(message, "%d %d %23[^\n]", &x, &y, atcmd_player_name) < 3) {
+ clif_displaymessage(fd, "Usage: @npcmove <X> <Y> <npc_name>");
+ return -1;
+ }
+
+ //Who's idea was this? nullpo's are meant to check pointers that SHOULDN'T be null unless there's a bug... [Skotlex]
+// nullpo_retr(-1, (nd = npc_name2id(atcmd_player_name)));
+
+ if ((nd = npc_name2id(atcmd_player_name)) == NULL)
+ return -1;
+
+ npc_enable(atcmd_player_name, 0);
+ nd->bl.x = x;
+ nd->bl.y = y;
+ npc_enable(atcmd_player_name, 1);
+
+ return 0;
+}
+
+/*==========================================
+ * @addwarp by MouseJstr
+ *
+ * Create a new static warp point.
+ *------------------------------------------
+ */
+int
+atcommand_addwarp(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char w1[64], w3[64], w4[64];
+ int x,y,ret;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message, "%99s %d %d[^\n]", atcmd_player_name, &x, &y ) < 3)
+ return -1;
+
+ sprintf(w1,"%s,%d,%d", mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
+ sprintf(w3,"%s%d%d%d%d", atcmd_player_name,sd->bl.x, sd->bl.y, x, y);
+ sprintf(w4,"1,1,%s.gat,%d,%d", atcmd_player_name, x, y);
+
+ ret = npc_parse_warp(w1, "warp", w3, w4);
+
+ sprintf(atcmd_output, "New warp NPC => %s",w3);
+
+ clif_displaymessage(fd, atcmd_output);
+
+ return ret;
+}
+
+/*==========================================
+ * @follow by [MouseJstr]
+ *
+ * Follow a player .. staying no more then 5 spaces away
+ *------------------------------------------
+ */
+int
+atcommand_follow(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ if (sd->followtarget == -1)
+ return -1;
+
+ pc_stop_following (sd);
+ clif_displaymessage(fd, "Stop following");
+ return 0;
+ }
+ if ((pl_sd = map_nick2sd((char *) message)) != NULL) {
+ if (sd->followtarget == pl_sd->bl.id)
+ pc_stop_following (sd);
+ else
+ pc_follow(sd, pl_sd->bl.id);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*==========================================
+ * @dropall by [MouseJstr]
+ *
+ * Drop all your possession on the ground
+ *------------------------------------------
+ */
+int
+atcommand_dropall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ if(sd->status.inventory[i].equip != 0)
+ pc_unequipitem(sd, i, 3);
+ pc_dropitem(sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * @chardropall by [MouseJstr]
+ *
+ * Throw all the characters possessions on the ground. Normally
+ * done in response to them being disrespectful of a GM
+ *------------------------------------------
+ */
+int
+atcommand_chardropall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].amount) {
+ if(pl_sd->status.inventory[i].equip != 0)
+ pc_unequipitem(pl_sd, i, 3);
+ pc_dropitem(pl_sd, i, pl_sd->status.inventory[i].amount);
+ }
+ }
+
+ clif_displaymessage(pl_sd->fd, "Ever play 52 card pickup?");
+ clif_displaymessage(fd, "It is done");
+ //clif_displaymessage(fd, "It is offical.. your a jerk");
+
+ return 0;
+}
+/*==========================================
+ * @storeall by [MouseJstr]
+ *
+ * Put everything into storage to simplify your inventory to make
+ * debugging easie
+ *------------------------------------------
+ */
+int
+atcommand_storeall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ nullpo_retr(-1, sd);
+ if (storage_storageopen(sd) == 1) {
+ clif_displaymessage(fd, "run this command again..");
+ return 0;
+ }
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ if(sd->status.inventory[i].equip != 0)
+ pc_unequipitem(sd, i, 3);
+ storage_storageadd(sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ storage_storageclose(sd);
+
+ clif_displaymessage(fd, "It is done");
+ return 0;
+}
+/*==========================================
+ * @charstoreall by [MouseJstr]
+ *
+ * A way to screw with players who piss you off
+ *------------------------------------------
+ */
+int
+atcommand_charstoreall(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i;
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) == NULL)
+ return -1;
+
+ if (storage_storageopen(pl_sd) == 1) {
+ clif_displaymessage(fd, "Had to open the characters storage window...");
+ clif_displaymessage(fd, "run this command again..");
+ return 0;
+ }
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].amount) {
+ if(pl_sd->status.inventory[i].equip != 0)
+ pc_unequipitem(pl_sd, i, 3);
+ storage_storageadd(pl_sd, i, sd->status.inventory[i].amount);
+ }
+ }
+ storage_storageclose(pl_sd);
+
+ clif_displaymessage(pl_sd->fd, "Everything you own has been put away for safe keeping.");
+ clif_displaymessage(pl_sd->fd, "go to the nearest kafka to retrieve it..");
+ clif_displaymessage(pl_sd->fd, " -- the management");
+
+ clif_displaymessage(fd, "It is done");
+
+ return 0;
+}
+/*==========================================
+ * @skillid by [MouseJstr]
+ *
+ * lookup a skill by name
+ *------------------------------------------
+ */
+int
+atcommand_skillid(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int skillen, idx;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ skillen = strlen(message);
+
+ for (idx = 0; idx < MAX_SKILL_DB; idx++) {
+ if ((skill_db[idx].name != NULL && strnicmp(skill_db[idx].name, message, skillen) == 0) ||
+ (skill_db[idx].desc != NULL && strnicmp(skill_db[idx].desc, message, skillen) == 0))
+ {
+ sprintf(atcmd_output, "skill %d: %s", idx, skill_db[idx].desc);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @useskill by [MouseJstr]
+ *
+ * A way of using skills without having to find them in the skills menu
+ *------------------------------------------
+ */
+int
+atcommand_useskill(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum;
+ int skilllv;
+ char target[255];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if(sscanf(message, "%d %d %99[^\n]", &skillnum, &skilllv, target) != 3) {
+ clif_displaymessage(fd, "Usage: @useskill <skillnum> <skillv> <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL) {
+ return -1;
+ }
+
+ if (skill_get_inf(skillnum) & INF_GROUND_SKILL)
+ skill_use_pos(sd, pl_sd->bl.x, pl_sd->bl.y, skillnum, skilllv);
+ else
+ skill_use_id(sd, pl_sd->bl.id, skillnum, skilllv);
+
+ return 0;
+}
+
+/*==========================================
+ * @skilltree by [MouseJstr]
+ *
+ * prints the skill tree for a player required to get to a skill
+ *------------------------------------------
+ */
+int
+atcommand_skilltree(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum, skillidx = -1;
+ int meets = 1, j, c=0;
+ char target[NAME_LENGTH], *tbl;
+ struct skill_tree_entry *ent;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if(sscanf(message, "%d %23[^\r\n]", &skillnum, target) != 2) {
+ clif_displaymessage(fd, "Usage: @skilltree <skillnum> <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL)
+ return -1;
+
+ c = pc_calc_skilltree_normalize_job(pl_sd);
+
+ tbl = job_name(c);
+
+ sprintf(atcmd_output, "Player is using %s skill tree (%d basic points)",
+ tbl, pc_checkskill(pl_sd, 1));
+ clif_displaymessage(fd, atcmd_output);
+
+ for (j = 0; skill_tree[c][j].id != 0; j++) {
+ if (skill_tree[c][j].id == skillnum) {
+ skillidx = j;
+ break;
+ }
+ }
+
+ if (skillidx == -1) {
+ sprintf(atcmd_output, "I do not believe the player can use that skill");
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+
+ ent = &skill_tree[c][skillidx];
+
+ for(j=0;j<5;j++)
+ if( ent->need[j].id &&
+ pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv)
+ {
+ char *desc = (skill_db[ ent->need[j].id ].desc) ? skill_db[ ent->need[j].id ].desc : "Unknown skill";
+ sprintf(atcmd_output, "player requires level %d of skill %s",
+ ent->need[j].lv, desc);
+ clif_displaymessage(fd, atcmd_output);
+ meets = 0;
+ }
+
+ if (meets == 1) {
+ sprintf(atcmd_output, "I believe the player meets all the requirements for that skill");
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+// Hand a ring with partners name on it to this char
+void getring (struct map_session_data *sd)
+{
+ int flag,item_id = 0;
+ struct item item_tmp;
+ if(sd->status.sex==0)
+ item_id = 2635;
+ else
+ item_id = 2634;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=item_id;
+ item_tmp.identify=1;
+ item_tmp.card[0]=255;
+ item_tmp.card[2]=sd->status.partner_id;
+ item_tmp.card[3]=sd->status.partner_id >> 16;
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_id, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag = pc_additem(sd,&item_tmp,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+}
+
+
+/*==========================================
+ * @marry by [MouseJstr], fixed by Lupus
+ *
+ * Marry two players
+ *------------------------------------------
+ */
+int
+atcommand_marry(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd1 = NULL;
+ struct map_session_data *pl_sd2 = NULL;
+ char player1[128], player2[128]; //Length used for return error msgs
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^,],%23[^\r\n]", player1, player2) != 2) {
+ clif_displaymessage(fd, "Usage: @marry <player1>,<player2>");
+ return -1;
+ }
+
+ if((pl_sd1=map_nick2sd((char *) player1)) == NULL) {
+ sprintf(player2, "Cannot find player '%s' online", player1);
+ clif_displaymessage(fd, player2);
+ return -1;
+ }
+
+ if((pl_sd2=map_nick2sd((char *) player2)) == NULL) {
+ sprintf(player1, "Cannot find player '%s' online", player2);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if (pc_marriage(pl_sd1, pl_sd2) == 0) {
+ clif_displaymessage(fd, "They are married.. wish them well");
+ clif_wedding_effect(&sd->bl); //wedding effect and music [Lupus]
+ // Auto-give named rings (Aru)
+ getring (pl_sd1);
+ getring (pl_sd2);
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * @divorce by [MouseJstr], fixed by [Lupus]
+ *
+ * divorce two players
+ *------------------------------------------
+ */
+int
+atcommand_divorce(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\r\n]", atcmd_player_name) != 1) {
+ clif_displaymessage(fd, "Usage: @divorce <player>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) atcmd_player_name)) != NULL) {
+ if (pc_divorce(pl_sd) != 0) {
+ sprintf(atcmd_output, "The divorce has failed.. Cannot find player '%s' or his(her) partner online.", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+ } else {
+ sprintf(atcmd_output, "'%s' and his(her) partner are now divorced.", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+ }
+ sprintf(atcmd_output, "Cannot find player '%s' online", atcmd_player_name);
+ clif_displaymessage(fd, atcmd_output);
+ return -1;
+}
+
+
+#ifdef DMALLOC
+unsigned long dmark_;
+int
+atcommand_dmstart(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ dmark_ = dmalloc_mark();
+
+ clif_displaymessage(fd, "debug mark set");
+
+ return 0;
+}
+
+int
+atcommand_dmtick(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ dmalloc_log_changed ( dmark_, 1, 0, 1 ) ;
+ dmark_ = dmalloc_mark();
+ clif_displaymessage(fd, "malloc changes logged");
+
+ return 0;
+}
+#endif
+
+/*==========================================
+ * @grind by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_grind(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int skillnum;
+ int inf;
+ char target[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if(sscanf(message, "%23s", target) != 1) {
+ clif_displaymessage(fd, "Usage: @grind <target>");
+ return -1;
+ }
+ if((pl_sd=map_nick2sd(target)) == NULL)
+ return -1;
+
+ for (skillnum = 1; skillnum < 500; skillnum++) {
+ sd->status.sp = sd->status.max_sp;
+ atcommand_alive(fd, sd, command, message);
+
+ inf = skill_get_inf(skillnum);
+
+ if (inf & INF_GROUND_SKILL)
+ skill_use_pos(sd, pl_sd->bl.x+5, pl_sd->bl.y+5, skillnum, 1);
+ else if (!(inf & INF_SUPPORT_SKILL))
+ skill_use_id(sd, pl_sd->bl.id, skillnum, 1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @grind2 by [MouseJstr]
+ *------------------------------------------
+ */
+int
+atcommand_grind2(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, x, y, id;
+
+ for (i = 1000; i <2000; i++) {
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+ id = mob_once_spawn(sd, "this", x, y, "--ja--", i, 1, "");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @changelook by [Celest]
+ *------------------------------------------
+ */
+int
+atcommand_changelook(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, j = 0, k = 0;
+ int pos[6] = { LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HEAD_BOTTOM,LOOK_WEAPON,LOOK_SHIELD,LOOK_SHOES };
+
+ if((i = sscanf(message, "%d %d", &j, &k)) < 1) {
+ clif_displaymessage(fd, "Usage: @changelook [<position>] <view id> -- [] = optional");
+ clif_displaymessage(fd, "Position: 1-Top 2-Middle 3-Bottom 4-Weapon 5-Shield");
+ return -1;
+ } else if (i == 2) {
+ if (j < 1) j = 1;
+ else if (j > 6) j = 6; // 6 = Shoes - for beta clients only perhaps
+ j = pos[j - 1];
+ } else if (i == 1) { // position not defined, use HEAD_TOP as default
+ k = j; // swap
+ j = LOOK_HEAD_TOP;
+ }
+
+ clif_changelook(&sd->bl,j,k);
+
+ return 0;
+}
+
+/*==========================================
+ *Turns on/off Autotrade for a specific player
+ *------------------------------------------
+ *by durf (changed by Lupus)
+ */
+int
+atcommand_autotrade(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (sd->vender_id) //check if player's vending
+ {
+ sd->state.autotrade = 1;
+ clif_authfail_fd(fd, 15);
+ }
+ else
+ {
+ //"You should be vending to use @Autotrade."
+ clif_displaymessage(fd, msg_txt(549));
+ }
+ return 0;
+}
+
+
+/*==========================================
+ * Changes Master of your Guild to a specified guild member
+ *------------------------------------------
+ *by durf (changed by Lupus)
+ */
+int
+atcommand_changegm(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct guild *g;
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL || strcmp(g->master,sd->status.name))
+ {
+ clif_displaymessage(fd, "You need to be a Guild Master to use this command.");
+ return -1;
+ }
+ if (strlen(message)==0)
+ {
+ clif_displaymessage(fd, "Command usage: @changegm <guildmember name>");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) {
+ clif_displaymessage(fd, "Target character must be online and be a guildmate.");
+ return -1;
+ }
+
+ guild_gm_change(sd->status.guild_id, pl_sd);
+ return 0;
+}
+
+/*==========================================
+ *Changes the leader of a party.
+ *------------------------------------------
+ *by Skotlex
+ */
+int
+atcommand_changeleader(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct party *p;
+ struct map_session_data *pl_sd;
+ int mi, pl_mi;
+ nullpo_retr(-1, sd);
+
+ if (sd->status.party_id == 0 || (p = party_search(sd->status.party_id)) == NULL)
+ {
+ clif_displaymessage(fd, "You need to be a party's leader to use this command.");
+ return -1;
+ }
+
+ for (mi = 0; mi < MAX_PARTY && p->member[mi].sd != sd; mi++);
+
+ if (mi == MAX_PARTY)
+ return -1; //Shouldn't happen
+
+ if (!p->member[mi].leader)
+ {
+ clif_displaymessage(fd, "You need to be the party's leader to use this command.");
+ return -1;
+ }
+
+ if (strlen(message)==0)
+ {
+ clif_displaymessage(fd, "Command usage: @changeleader <party member name>");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) message)) == NULL || pl_sd->status.party_id != sd->status.party_id) {
+ clif_displaymessage(fd, "Target character must be online and be in your party.");
+ return -1;
+ }
+
+ for (pl_mi = 0; pl_mi < MAX_PARTY && p->member[pl_mi].sd != pl_sd; pl_mi++);
+
+ if (pl_mi == MAX_PARTY)
+ return -1; //Shouldn't happen
+
+ //Change leadership.
+ p->member[mi].leader = 0;
+ if (p->member[mi].sd->fd)
+ clif_displaymessage(p->member[mi].sd->fd, "Leadership transferred.");
+ p->member[pl_mi].leader = 1;
+ if (p->member[pl_mi].sd->fd)
+ clif_displaymessage(p->member[pl_mi].sd->fd, "You've become the party leader.");
+
+ intif_party_leaderchange(p->party_id,p->member[pl_mi].account_id,p->member[pl_mi].char_id);
+ //Update info.
+ clif_party_main_info(p,-1);
+ clif_party_info(p,-1);
+
+ return 0;
+}
+
+/*==========================================
+ *Turns on/off AutoLoot for a specific player
+ *------------------------------------------
+ *by Upa-Kun
+ */
+int
+atcommand_autoloot(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int rate;
+ double drate;
+ nullpo_retr(-1, sd);
+ if (!message || !*message) {
+ if (sd->state.autoloot) {
+ sd->state.autoloot = 0;
+ clif_displaymessage(fd, "Autoloot is now off.");
+ return 0;
+ } else {
+ clif_displaymessage(fd, "Usage: autoloot <max drop-rate to loot>.");
+ return -1;
+ }
+ }
+ drate = atof(message);
+ rate = (int)(drate*100);
+ if (rate > 10000) rate = 10000;
+ else if (rate < 0) rate = 0;
+
+ sd->state.autoloot = rate;
+ if (sd->state.autoloot) {
+ snprintf(atcmd_output, sizeof atcmd_output, "Autolooting items with drop rates of %0.02f%% and below.",((double)sd->state.autoloot)/100.);
+ clif_displaymessage(fd, atcmd_output);
+ }else
+ clif_displaymessage(fd, "Autoloot is now off.");
+ return 0;
+}
+
+
+/*==========================================
+ * It is made to rain.
+ *------------------------------------------
+ */
+int
+atcommand_rain(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.rain) {
+ map[sd->bl.m].flag.rain=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The rain has stopped.");
+ } else {
+ map[sd->bl.m].flag.rain=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "It is made to rain.");
+ }
+ return 0;
+}
+/*==========================================
+ * It is made to snow.
+ *------------------------------------------
+ */
+int
+atcommand_snow(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.snow) {
+ map[sd->bl.m].flag.snow=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Snow has stopped falling.");
+ } else {
+ map[sd->bl.m].flag.snow=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "It is made to snow.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Cherry tree snowstorm is made to fall. (Sakura)
+ *------------------------------------------
+ */
+int
+atcommand_sakura(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.sakura) {
+ map[sd->bl.m].flag.sakura=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Cherry tree leaves no longer fall.");
+ } else {
+ map[sd->bl.m].flag.sakura=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Cherry tree leaves is made to fall.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Clouds appear.
+ *------------------------------------------
+ */
+int
+atcommand_clouds(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.clouds) {
+ map[sd->bl.m].flag.clouds=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The clouds has disappear.");
+ } else {
+ map[sd->bl.m].flag.clouds=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Clouds appear.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Different type of clouds using effect 516
+ *------------------------------------------
+ */
+int
+atcommand_clouds2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.clouds2) {
+ map[sd->bl.m].flag.clouds2=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The alternative clouds disappear.");
+ } else {
+ map[sd->bl.m].flag.clouds2=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Alternative clouds appear.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Fog hangs over.
+ *------------------------------------------
+ */
+int
+atcommand_fog(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.fog) {
+ map[sd->bl.m].flag.fog=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "The fog has gone.");
+ } else {
+ map[sd->bl.m].flag.fog=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fog hangs over.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Fallen leaves fall.
+ *------------------------------------------
+ */
+int
+atcommand_leaves(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.leaves) {
+ map[sd->bl.m].flag.leaves=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Leaves no longer fall.");
+ } else {
+ map[sd->bl.m].flag.leaves=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fallen leaves fall.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Fireworks appear.
+ *------------------------------------------
+ */
+int
+atcommand_fireworks(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ if (map[sd->bl.m].flag.fireworks) {
+ map[sd->bl.m].flag.fireworks=0;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fireworks are ended.");
+ } else {
+ map[sd->bl.m].flag.fireworks=1;
+ clif_weather(sd->bl.m);
+ clif_displaymessage(fd, "Fireworks are launched.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Clearing Weather Effects by Dexity
+ *------------------------------------------
+ */
+int
+atcommand_clearweather(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ map[sd->bl.m].flag.rain=0;
+ map[sd->bl.m].flag.snow=0;
+ map[sd->bl.m].flag.sakura=0;
+ map[sd->bl.m].flag.clouds=0;
+ map[sd->bl.m].flag.clouds2=0;
+ map[sd->bl.m].flag.fog=0;
+ map[sd->bl.m].flag.fireworks=0;
+ map[sd->bl.m].flag.leaves=0;
+ clif_weather(sd->bl.m);
+
+ return 0;
+}
+
+/*===============================================================
+ * Sound Command - plays a sound for everyone! [Codemaster]
+ *---------------------------------------------------------------
+ */
+int
+atcommand_sound(
+ const int fd, struct map_session_data *sd,
+ const char *command, const char *message)
+{
+ char sound_file[100];
+
+ if(!message || !*message || sscanf(message, "%99[^\n]", sound_file) < 1) {
+ clif_displaymessage(fd, "Please, enter a sound filename. (usage: @sound <filename>)");
+ return -1;
+ }
+
+ memset(sound_file, '\0', sizeof(sound_file));
+ if(sscanf(message, "%99[^\n]", sound_file) < 1)
+ return -1;
+
+ if(strstr(sound_file, ".wav") == NULL)
+ strcat(sound_file, ".wav");
+
+ clif_soundeffectall(&sd->bl, sound_file,0);
+
+ return 0;
+}
+
+/*==========================================
+ * MOB Search
+ *------------------------------------------
+ */
+static int atmobsearch_sub(struct block_list *bl,va_list ap)
+{
+ int mob_id,fd;
+ static int number=0;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+
+ if(!ap){
+ number=0;
+ return 0;
+ }
+ mob_id = va_arg(ap,int);
+ fd = va_arg(ap,int);
+
+ md = (struct mob_data *)bl;
+
+ if(md && fd && (mob_id==-1 || (md->class_==mob_id))){
+ snprintf(atcmd_output, sizeof atcmd_output, "%2d[%3d:%3d] %s",
+ ++number,bl->x, bl->y,md->name);
+ clif_displaymessage(fd, atcmd_output);
+ }
+ return 0;
+}
+int atcommand_mobsearch(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char mob_name[100];
+ int mob_id,map_id = 0;
+
+ nullpo_retr(-1, sd);
+
+ if (sscanf(message, "%99[^\n]", mob_name) < 0)
+ return -1;
+
+ if ((mob_id = atoi(mob_name)) == 0)
+ mob_id = mobdb_searchname(mob_name);
+ if(mob_id > 0 && mobdb_checkid(mob_id) == 0){
+ snprintf(atcmd_output, sizeof atcmd_output, "Invalid mob id %s!",mob_name);
+ clif_displaymessage(fd, atcmd_output);
+ return 0;
+ }
+ if(mob_id == atoi(mob_name) && mob_db(mob_id)->jname)
+ strcpy(mob_name,mob_db(mob_id)->jname); // --ja--
+// strcpy(mob_name,mob_db(mob_id)->name); // --en--
+
+ map_id = sd->bl.m;
+
+ snprintf(atcmd_output, sizeof atcmd_output, "Mob Search... %s %s",
+ mob_name, mapindex_id2name(sd->mapindex));
+ clif_displaymessage(fd, atcmd_output);
+
+ map_foreachinmap(atmobsearch_sub, map_id, BL_MOB, mob_id, fd);
+
+ atmobsearch_sub(&sd->bl,0); // ”Ô†ƒŠƒZƒbƒg
+
+ return 0;
+}
+/*==========================================
+ * ƒhƒƒbƒvƒAƒCƒeƒ€‚Ì‘|œ
+ *------------------------------------------
+ */
+/*==========================================
+ * cleanmap
+ *------------------------------------------
+ */
+static int atcommand_cleanmap_sub(struct block_list *bl, va_list ap)
+{
+ nullpo_retr(0, bl);
+ map_clearflooritem(bl->id);
+
+ return 0;
+}
+
+int
+atcommand_cleanmap(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ map_foreachinarea(atcommand_cleanmap_sub, sd->bl.m,
+ sd->bl.x-AREA_SIZE*2, sd->bl.y-AREA_SIZE*2,
+ sd->bl.x+AREA_SIZE*2, sd->bl.y+AREA_SIZE*2,
+ BL_ITEM);
+ clif_displaymessage(fd, "All dropped items have been cleaned up.");
+ return 0;
+}
+
+/*==========================================
+ * NPC/PET‚ɘb‚³‚¹‚é
+ *------------------------------------------
+ */
+int
+atcommand_npctalk(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH],mes[100],temp[100];
+ struct npc_data *nd;
+
+ if (sscanf(message, "%23[^,],%99[^\n]", name, mes) < 2)
+ return -1;
+
+ if (!(nd = npc_name2id(name)))
+ return -1;
+
+ snprintf(temp, sizeof temp ,"%s : %s",name,mes);
+ clif_message(&nd->bl, temp);
+
+ return 0;
+}
+int
+atcommand_pettalk(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char mes[100],temp[100];
+ struct pet_data *pd;
+
+ nullpo_retr(-1, sd);
+
+ if(!sd->status.pet_id || !(pd=sd->pd))
+ return -1;
+
+ if (sscanf(message, "%99[^\n]", mes) < 1)
+ return -1;
+
+ snprintf(temp, sizeof temp ,"%s : %s",sd->pet.name,mes);
+ clif_message(&pd->bl, temp);
+
+ return 0;
+}
+
+/*==========================================
+ * @users
+ * ƒT[ƒo[“à‚Ìl”ƒ}ƒbƒv‚ð•\Ž¦‚³‚¹‚é
+ * Žè”²‚«‚Ì‚½‚߉˜‚­‚È‚Á‚Ä‚¢‚é‚Ì‚ÍŽd—l‚Å‚·B
+ *------------------------------------------
+ */
+
+static struct dbt *users_db = NULL;
+static int users_all;
+
+static int atcommand_users_sub1(struct map_session_data* sd,va_list va) {
+ int users = (int)(uidb_get(users_db,(unsigned int)sd->mapindex)) + 1;
+ users_all++;
+ uidb_put(users_db,(unsigned int)sd->mapindex,(void *)users);
+ return 0;
+}
+
+static int atcommand_users_sub2(DBKey key,void* val,va_list va) {
+ char buf[256];
+ struct map_session_data* sd = va_arg(va,struct map_session_data*);
+ sprintf(buf,"%s : %d (%d%%)",mapindex_id2name(key.i),(int)val,(int)val * 100 / users_all);
+ clif_displaymessage(sd->fd,buf);
+ return 0;
+}
+
+int
+atcommand_users(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[256];
+ users_all = 0;
+
+ users_db->clear(users_db, NULL);
+ clif_foreachclient(atcommand_users_sub1);
+ users_db->foreach(users_db,atcommand_users_sub2,sd);
+ sprintf(buf,"all : %d",users_all);
+ clif_displaymessage(fd,buf);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int
+atcommand_summon(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH];
+ int mob_id = 0;
+ int x = 0;
+ int y = 0;
+ int id = 0;
+ int duration = 0;
+ struct mob_data *md;
+ unsigned int tick=gettick();
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%23s %d", name, &duration) < 1)
+ return -1;
+
+ if (duration < 1)
+ duration =1;
+ else if (duration > 60)
+ duration =60;
+
+ if ((mob_id = atoi(name)) == 0)
+ mob_id = mobdb_searchname(name);
+ if(mob_id == 0 || mobdb_checkid(mob_id) == 0)
+ return -1;
+
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+
+ id = mob_once_spawn(sd,"this", x, y, "--ja--", mob_id, 1, "");
+ if((md=(struct mob_data *)map_id2bl(id))){
+ md->master_id=sd->bl.id;
+ md->special_state.ai=1;
+ md->mode=md->db->mode|MD_AGGRESSIVE;
+ md->deletetimer=add_timer(tick+(duration*60000),mob_timer_delete,id,0);
+ clif_misceffect2(&md->bl,344);
+ }
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,x,y,tick);
+
+ return 0;
+}
+
+
+/*==========================================
+ * @adjcmdlvl by [MouseJstr]
+ *
+ * Temp adjust the GM level required to use a GM command
+ *
+ * Used during beta testing to allow players to use GM commands
+ * for short periods of time
+ *------------------------------------------
+ */
+int
+atcommand_adjcmdlvl(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i, newlev;
+ char cmd[100];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %s", &newlev, cmd) != 2) {
+ clif_displaymessage(fd, "Usage: @adjcmdlvl <lvl> <command>.");
+ return -1;
+ }
+
+ for (i = 0; (atcommand_info[i].command) && atcommand_info[i].type != AtCommand_None; i++)
+ if (strcmpi(cmd, atcommand_info[i].command+1) == 0) {
+ atcommand_info[i].level = newlev;
+ clif_displaymessage(fd, "@command level changed.");
+ return 0;
+ }
+
+ clif_displaymessage(fd, "@command not found.");
+ return -1;
+}
+
+/*==========================================
+ * @adjgmlvl by [MouseJstr]
+ *
+ * Create a temp GM
+ *
+ * Used during beta testing to allow players to use GM commands
+ * for short periods of time
+ *------------------------------------------
+ */
+int
+atcommand_adjgmlvl(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int newlev;
+ char user[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\r\n]", &newlev, user) != 2) {
+ clif_displaymessage(fd, "Usage: @adjgmlvl <lvl> <user>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) user)) == NULL)
+ return -1;
+
+ pc_set_gm_level(pl_sd->status.account_id, newlev);
+
+ return 0;
+}
+
+
+/*==========================================
+ * @trade by [MouseJstr]
+ *
+ * Open a trade window with a remote player
+ *
+ * If I have to jump to a remote player one more time, I am
+ * gonna scream!
+ *------------------------------------------
+ */
+int
+atcommand_trade(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) != NULL) {
+ trade_traderequest(sd, pl_sd->bl.id);
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * @setbattleflag by [MouseJstr]
+ *
+ * set a battle_config flag without having to reboot
+ */
+int
+atcommand_setbattleflag(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char flag[128], value[128];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%127s %127s", flag, value) != 2) {
+ clif_displaymessage(fd, "Usage: @setbattleflag <flag> <value>.");
+ return -1;
+ }
+
+ if (battle_set_value(flag, value) == 0)
+ clif_displaymessage(fd, "unknown battle_config flag");
+ else
+ clif_displaymessage(fd, "battle_config set as requested");
+
+ return 0;
+}
+
+
+/*===========================
+ * @unmute [Valaris]
+ *===========================
+*/
+int atcommand_unmute(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ nullpo_retr(-1, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ if (!message || !*message)
+ return -1;
+
+ if((pl_sd=map_nick2sd((char *) message)) != NULL) {
+ if(pl_sd->sc_data[SC_NOCHAT].timer!=-1) {
+ pl_sd->status.manner = 0; // have to set to 0 first [celest]
+ status_change_end(&pl_sd->bl,SC_NOCHAT,-1);
+ clif_displaymessage(sd->fd,"Player unmuted");
+ }
+ else
+ clif_displaymessage(sd->fd,"Player is not muted");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @uptime by MC Cameri
+ *------------------------------------------
+ */
+int
+atcommand_uptime(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned long seconds = 0, day = 24*60*60, hour = 60*60,
+ minute = 60, days = 0, hours = 0, minutes = 0;
+ nullpo_retr(-1, sd);
+
+ seconds = get_uptime();
+ days = seconds/day;
+ seconds -= (seconds/day>0)?(seconds/day)*day:0;
+ hours = seconds/hour;
+ seconds -= (seconds/hour>0)?(seconds/hour)*hour:0;
+ minutes = seconds/minute;
+ seconds -= (seconds/minute>0)?(seconds/minute)*minute:0;
+
+ snprintf(atcmd_output, sizeof(atcmd_output), msg_table[245], days, hours, minutes, seconds);
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+}
+
+/*==========================================
+ * @changesex <sex>
+ * => Changes one's sex. Argument sex can be
+ * 0 or 1, m or f, male or female.
+ *------------------------------------------
+ */
+int
+atcommand_changesex(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ chrif_char_ask_name(sd->status.account_id,sd->status.name, 5,0,0,0,0,0,0);
+ return 0;
+}
+
+/*================================================
+ * @mute - Mutes a player for a set amount of time
+ *------------------------------------------------
+ */
+int atcommand_mute(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int manner;
+ nullpo_retr(-1, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &manner, atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Usage: @mute <time> <character name>.");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) {
+ clif_GM_silence(sd, pl_sd, 0);
+ pl_sd->status.manner -= manner;
+ if(pl_sd->status.manner < 0)
+ status_change_start(&pl_sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+ }
+ else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @refresh (like @jumpto <<yourself>>)
+ *------------------------------------------
+ */
+int atcommand_refresh(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+ clif_refresh(sd);
+ return 0;
+}
+
+/*==========================================
+ * @petid <part of pet name>
+ * => Displays a list of matching pets.
+ *------------------------------------------
+ */
+int
+atcommand_petid(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char searchtext[100];
+ char temp0[100];
+ char temp1[100];
+ int cnt = 0, i = 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%99s", searchtext) < 1)
+ return -1;
+ estr_lower(searchtext);
+ snprintf(temp0, sizeof(temp0), "Search results for: %s", searchtext);
+ clif_displaymessage(fd,temp0);
+ while (i < MAX_PET_DB) {
+ strcpy(temp1,pet_db[i].name);
+ strcpy(temp1, estr_lower(temp1));
+ strcpy(temp0,pet_db[i].jname);
+ strcpy(temp0, estr_lower(temp1));
+ if (strstr(temp1, searchtext) || strstr(temp0, searchtext) ) {
+ snprintf(temp0, sizeof(temp0), "ID: %i -- Name: %s", pet_db[i].class_,
+ pet_db[i].jname);
+ if (cnt >= 100) { // Only if there are custom pets
+ clif_displaymessage(fd, "Be more specific, can't send more than"
+ " 100 results.");
+ } else {
+ clif_displaymessage(fd, temp0);
+ }
+ cnt++;
+ }
+ i++;
+ }
+ snprintf(temp0, sizeof(temp0),"%i pets have '%s' in their name.", cnt, searchtext);
+ clif_displaymessage(fd, temp0);
+ return 0;
+}
+
+/*==========================================
+ * @identify
+ * => GM's magnifier.
+ *------------------------------------------
+ */
+int
+atcommand_identify(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int i,num;
+
+ nullpo_retr(-1, sd);
+
+ for(i=num=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){
+ num++;
+ }
+ }
+ if (num > 0) {
+ clif_item_identify_list(sd);
+ } else {
+ clif_displaymessage(fd,"There are no items to appraise.");
+ }
+ return 0;
+}
+
+/*==========================================
+ * @gmotd (Global MOTD)
+ * by davidsiaw :P
+ *------------------------------------------
+ */
+int
+atcommand_gmotd(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[256];
+ FILE *fp;
+ nullpo_retr(-1, sd);
+ if( (fp = fopen(motd_txt, "r"))!=NULL){
+ while (fgets(buf, 250, fp) != NULL){
+ int i;
+ for( i=0; buf[i]; i++){
+ if( buf[i]=='\r' || buf[i]=='\n'){
+ buf[i]=0;
+ break;
+ }
+ }
+ intif_GMmessage(buf,strlen(buf)+1,8);
+ }
+ fclose(fp);
+ }
+ return 0;
+}
+
+int atcommand_misceffect(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int effect = 0;
+ nullpo_retr(-1, sd);
+ if (!message || !*message)
+ return -1;
+ if (sscanf(message, "%d", &effect) < 1)
+ return -1;
+ clif_misceffect(&sd->bl,effect);
+
+ return 0;
+}
+
+int charid2sessionid(int charid)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ pl_sd = map_charid2sd(charid);
+ if (pl_sd) return pl_sd->fd;
+
+ return 0;
+}
+
+int accountid2sessionid(int accountid)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ pl_sd = map_id2sd(accountid);
+ if (pl_sd) return pl_sd->fd;
+
+ return 0;
+}
+
+
+/*==========================================
+ * Jump to a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+
+int atcommand_jumptoid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @jumptoid/@warptoid/@gotoid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Jump to a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+
+int atcommand_jumptoid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @jumptoid/@warptoid/@gotoid <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ sprintf(atcmd_output, msg_table[4], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_recallid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @recallid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Recall a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_recallid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @recallid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[247]);
+ return -1;
+ }
+ if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, msg_table[248]);
+ return -1;
+ }
+ pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2);
+ sprintf(atcmd_output, msg_table[46], pl_sd->status.name); // Jump to %s
+ clif_displaymessage(fd, atcmd_output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[154]); // Character not found.
+ return -1;
+ }
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kick a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_kickid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int cid=0;
+ int session_id=0;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @kickid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kick a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_kickid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ int aid=0;
+ int session_id=0;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @kickid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level
+ clif_GM_kick(sd, pl_sd, 1);
+ else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Revive a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_reviveid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @reviveid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+
+ return 0;
+}
+
+/*==========================================
+ * Revive a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_reviveid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @reviveid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ pl_sd->status.hp = pl_sd->status.max_hp;
+ pc_setstand(pl_sd);
+ if (battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ clif_updatestatus(pl_sd, SP_HP);
+ clif_updatestatus(pl_sd, SP_SP);
+ clif_resurrection(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[51]); // Character revived.
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+
+ return 0;
+}
+
+/*==========================================
+ * Kill a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_killid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int cid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (cid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player CID (usage: @killid <char id>).");
+ return -1;
+ }
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+
+ return 0;
+}
+
+/*==========================================
+ * Kill a player by PID number
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int atcommand_killid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int aid=0;
+ int session_id=0;
+ struct map_session_data *pl_sd;
+
+ memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
+
+ if (!message || (aid = atoi(message)) == 0 || !*message || sscanf(message, "%99[^\n]", atcmd_player_name) < 1) {
+ clif_displaymessage(fd, "Please, enter a player AID (usage: @killid2 <account id>).");
+ return -1;
+ }
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level
+ pc_damage(NULL, pl_sd, pl_sd->status.hp);
+ clif_displaymessage(fd, msg_table[14]); // Character killed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+
+ return 0;
+}
+
+/*==========================================
+ * Make a player killable, by PID
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int
+atcommand_charkillableid(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int cid=0;
+ int session_id=0;
+
+ if (!message || (cid = atoi(message)) == 0 || !*message)
+ return -1;
+
+ cid=atoi(message);
+
+ if ((session_id=charid2sessionid(cid))!=0)
+ {
+ if((pl_sd= (struct map_session_data *) session[session_id]->session_data) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, cid = %d\n",session_id,cid);
+ return 0;
+}
+
+
+/*==========================================
+ * Make a player killable, by PID
+ * Original by Dino9021
+ * Added in by nsstrunks
+ *------------------------------------------
+ */
+int
+atcommand_charkillableid2(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ int aid=0;
+ int session_id=0;
+
+ if (!message || (aid = atoi(message)) == 0 || !*message)
+ return -1;
+
+ aid=atoi(message);
+
+ if ((session_id=accountid2sessionid(aid))!=0)
+ {
+ if((pl_sd= (struct map_session_data *) session[session_id]->session_data) == NULL)
+ return -1;
+
+ pl_sd->special_state.killable = !pl_sd->special_state.killable;
+
+ if(pl_sd->special_state.killable)
+ clif_displaymessage(fd, "The player is now killable");
+ else
+ clif_displaymessage(fd, "The player is no longer killable");
+ }
+ else
+ {
+ clif_displaymessage(fd,msg_table[3]);
+ }
+ //printf("Session_id = %d, aid = %d\n",session_id,aid);
+ return 0;
+}
+
+#ifndef TXT_ONLY /* Begin SQL-Only commands */
+
+/*==========================================
+ * Mail System commands by [Valaris]
+ *------------------------------------------
+ */
+int atcommand_listmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if(strlen(command)==12)
+ mail_check(sd,3);
+ else if(strlen(command)==9)
+ mail_check(sd,2);
+ else
+ mail_check(sd,1);
+ return 0;
+}
+
+int atcommand_readmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int index;
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must specify a message number.");
+ return 0;
+ }
+
+ index = atoi(message);
+ if (index < 1) {
+ clif_displaymessage(sd->fd,"Message number cannot be negative or zero.");
+ return 0;
+ }
+
+ if(strlen(command)==11)
+ mail_delete(sd,index);
+ else
+ mail_read(sd,index);
+
+ return 0;
+}
+
+int atcommand_sendmail(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char name[NAME_LENGTH],text[80];
+
+ if(!mail_server_enable)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must specify a recipient and a message.");
+ return 0;
+ }
+
+ if ((sscanf(message, "\"%23[^\"]\" %79[^\n]", name, text) < 2) &&
+ (sscanf(message, "%23s %79[^\n]", name, text) < 2)) {
+ clif_displaymessage(sd->fd,"You must specify a recipient and a message.");
+ return 0;
+ }
+
+ if(strlen(command)==17)
+ mail_send(sd,name,text,1);
+ else
+ mail_send(sd,name,text,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Refresh online command for SQL [Valaris]
+ * Will refresh and check online column of
+ * players and set correctly.
+ *------------------------------------------
+ */
+int atcommand_refreshonline(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ send_users_tochar(-1, gettick(), 0, 0);
+ return 0;
+}
+
+#endif /* end sql only */
+
+/*==========================================
+ * Show Monster DB Info v 1.0
+ * originally by [Lupus] eAthena
+ *------------------------------------------
+ */
+int atcommand_mobinfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned char msize[3][7] = {"Small", "Medium", "Large"};
+ unsigned char mrace[12][11] = {"Formless", "Undead", "Beast", "Plant", "Insect", "Fish", "Demon", "Demi-Human", "Angel", "Dragon", "Boss", "Non-Boss"};
+ unsigned char melement[11][8] = {"None", "Neutral", "Water", "Earth", "Fire", "Wind", "Poison", "Holy", "Dark", "Ghost", "Undead"};
+ char atcmd_output2[200];
+ struct item_data *item_data;
+ struct mob_db *mob;
+ int mob_id;
+ int i, j;
+
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+ memset(atcmd_output2, '\0', sizeof(atcmd_output2));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a Monster/ID (usage: @mobinfo <monster_name_or_monster_ID>).");
+ return -1;
+ }
+
+ // If monster identifier/name argument is a name
+ if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number)
+ mob_id = mobdb_checkid(atoi(message));
+
+ if (mob_id == 0) {
+ clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name.
+ return -1;
+ }
+
+ mob = mob_db(mob_id);
+
+ // stats
+ if (mob->mexp)
+ sprintf(atcmd_output, "MVP Monster: '%s'/'%s' (%d)", mob->name, mob->jname, mob_id);
+ else
+ sprintf(atcmd_output, "Monster: '%s'/'%s' (%d)", mob->name, mob->jname, mob_id);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " Level:%d HP:%d SP:%d Base EXP:%d Job EXP:%d", mob->lv, mob->max_hp, mob->max_sp, mob->base_exp, mob->job_exp);
+ clif_displaymessage(fd, atcmd_output);
+ sprintf(atcmd_output, " DEF:%d MDEF:%d STR:%d AGI:%d VIT:%d INT:%d DEX:%d LUK:%d", mob->def, mob->mdef, mob->str, mob->agi, mob->vit, mob->int_, mob->dex, mob->luk);
+ clif_displaymessage(fd, atcmd_output);
+ if (mob->element < 20) {
+ //Element - None, Level 0
+ i = 0;
+ j = 0;
+ } else {
+ i = mob->element % 20 + 1;
+ j = mob->element / 20;
+ }
+ sprintf(atcmd_output, " ATK:%d~%d Range:%d~%d~%d Size:%s Race: %s Element: %s (Lv:%d)", mob->atk1, mob->atk2, mob->range, mob->range2 , mob->range3, msize[mob->size], mrace[mob->race], melement[i], j);
+ clif_displaymessage(fd, atcmd_output);
+ // drops
+ clif_displaymessage(fd, " Drops:");
+ strcpy(atcmd_output, " ");
+ j = 0;
+ for (i = 0; i < 10; i++) {
+ if (mob->dropitem[i].nameid <= 0 || (item_data = itemdb_search(mob->dropitem[i].nameid)) == NULL)
+ continue;
+ if (mob->dropitem[i].p > 0) {
+ sprintf(atcmd_output2, " - %s %02.02f%%", item_data->name, (float)mob->dropitem[i].p / 100);
+ strcat(atcmd_output, atcmd_output2);
+ if (++j % 3 == 0) {
+ clif_displaymessage(fd, atcmd_output);
+ strcpy(atcmd_output, " ");
+ }
+ }
+ }
+ if (j == 0)
+ clif_displaymessage(fd, "This monster has no drops.");
+ else if (j % 3 != 0)
+ clif_displaymessage(fd, atcmd_output);
+ // mvp
+ if (mob->mexp) {
+ sprintf(atcmd_output, " MVP Bonus EXP:%d %02.02f%%", mob->mexp, (float)mob->mexpper / 100);
+ clif_displaymessage(fd, atcmd_output);
+ strcpy(atcmd_output, " MVP Items:");
+ j = 0;
+ for (i = 0; i < 3; i++) {
+ if (mob->mvpitem[i].nameid <= 0 || (item_data = itemdb_search(mob->mvpitem[i].nameid)) == NULL)
+ continue;
+ if (mob->mvpitem[i].p > 0) {
+ j++;
+ if (j == 1)
+ sprintf(atcmd_output2, " %s %02.02f%%", item_data->name, (float)mob->mvpitem[i].p / 100);
+ else
+ sprintf(atcmd_output2, " - %s %02.02f%%", item_data->name, (float)mob->mvpitem[i].p / 100);
+ strcat(atcmd_output, atcmd_output2);
+ }
+ }
+ if (j == 0)
+ clif_displaymessage(fd, "This monster has no MVP prizes.");
+ else
+ clif_displaymessage(fd, atcmd_output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Show Items DB Info v 1.0
+ * originally by [Lupus] eAthena
+ *------------------------------------------
+ */
+int atcommand_iteminfo(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char *itype[12] = {"Potion/Food", "BUG!", "Usable", "Etc", "Weapon", "Protection", "Card", "Egg", "Pet Acessory", "BUG!", "Arrow"};
+ //, "Lure/Scroll"}; No need, type 11 items are converted to type 2 upon loading [Skotlex]
+
+ struct item_data *item_data;
+ int item_id=0;
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter Item name or its ID (usage: @iteminfo <item_name_or_ID>).");
+ return -1;
+ }
+
+ if ((item_data = itemdb_searchname(message)) != NULL ||
+ (item_data = itemdb_exists(atoi(message))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+
+ sprintf(atcmd_output, "Item: '%s'/'%s'[%d] (%d) Type: %s | Extra Effect: %s",
+ item_data->name,item_data->jname,item_data->slot,item_id,
+ item_data->type < 12 ? itype[item_data->type] : "BUG!",
+ (item_data->script==NULL)? "None" : "With script"
+ );
+ clif_displaymessage(fd, atcmd_output);
+
+ sprintf(atcmd_output, "NPC Buy:%dz%s, Sell:%dz%s | Weight: %d ", item_data->value_buy, item_data->flag.value_notdc ? "(No Discount!)":"", item_data->value_sell, item_data->flag.value_notoc ? "(No Overcharge!)":"", item_data->weight );
+ clif_displaymessage(fd, atcmd_output);
+
+ if (item_data->maxchance == 10000)
+ strcpy(atcmd_output, " - Available in the shops only");
+ else if (item_data->maxchance)
+ sprintf(atcmd_output, " - Maximal monsters drop chance: %02.02f%%", (float)item_data->maxchance / 100 );
+ else
+ strcpy(atcmd_output, " - Monsters don't drop this item");
+ clif_displaymessage(fd, atcmd_output);
+
+ return 0;
+ }
+
+ clif_displaymessage(fd, "Item not found.");
+ return -1;
+}
+
+/*==========================================
+ * @adopt by [Veider]
+ *
+ * adopt a novice
+ *------------------------------------------
+ */
+int
+atcommand_adopt(const int fd, struct map_session_data* sd,
+const char* command, const char* message)
+{
+ struct map_session_data *pl_sd1 = NULL;
+ struct map_session_data *pl_sd2 = NULL;
+ struct map_session_data *pl_sd3 = NULL;
+ char player1[NAME_LENGTH], player2[NAME_LENGTH], player3[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message, "%23[^,],%23[^,],%23[^\r\n]", player1, player2, player3) != 3) {
+ clif_displaymessage(fd, "usage: @adopt <player1>,<player2>,<player3>.");
+ return -1;
+ }
+
+ if (battle_config.etc_log)
+ printf("Adopting: --%s--%s--%s--\n",player1,player2,player3);
+
+ if((pl_sd1=map_nick2sd((char *) player1)) == NULL) {
+ sprintf(player2, "Cannot find player %s online", player1);
+ clif_displaymessage(fd, player2);
+ return -1;
+ }
+
+ if((pl_sd2=map_nick2sd((char *) player2)) == NULL) {
+ sprintf(player1, "Cannot find player %s online", player2);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if((pl_sd3=map_nick2sd((char *) player3)) == NULL) {
+ sprintf(player1, "Cannot find player %s online", player3);
+ clif_displaymessage(fd, player1);
+ return -1;
+ }
+
+ if((pl_sd1->status.base_level < 70) || (pl_sd2->status.base_level < 70)){
+ clif_displaymessage(fd, "They are too young to be parents!");
+ return -1;
+ }
+
+ if (pc_adoption(pl_sd1, pl_sd2, pl_sd3) == 0) {
+ clif_displaymessage(fd, "They are family.. wish them luck");
+ return 0;
+ }
+ else
+ return -1;
+}
+
+int atcommand_version(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ const char * revision;
+
+ if ((revision = get_svn_revision()) != 0) {
+ sprintf(atcmd_output,"eAthena Version SVN r%s",revision);
+ clif_displaymessage(fd,atcmd_output);
+ } else
+ clif_displaymessage(fd,"Cannot determine SVN revision");
+
+ return 0;
+}
+
+
+static int atcommand_mutearea_sub(struct block_list *bl,va_list ap)
+{
+
+ int time, id;
+ struct map_session_data *pl_sd = (struct map_session_data *)bl;
+ if (pl_sd == NULL)
+ return 0;
+
+ id = va_arg(ap, int);
+ time = va_arg(ap, int);
+
+ if (id != bl->id && !pc_isGM(pl_sd)) {
+ pl_sd->status.manner -= time;
+ if (pl_sd->status.manner < 0)
+ status_change_start(&pl_sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * @mutearea by MouseJstr
+ *------------------------------------------
+ */
+int atcommand_mutearea(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int time;
+ nullpo_retr(0, sd);
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Please enable the muting system before using it.");
+ return 0;
+ }
+
+ time = atoi(message);
+ if (time <= 0)
+ time = 15; // 15 minutes default
+ map_foreachinarea(atcommand_mutearea_sub,sd->bl.m,
+ sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE,
+ sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd->bl.id, time);
+
+ return 0;
+}
+
+static int atcommand_shuffle_sub(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *pl_sd = (struct map_session_data *) bl;
+ if (bl == NULL)
+ return 0;
+
+ if (!pc_isGM(pl_sd))
+ pc_setpos(pl_sd, pl_sd->mapindex, rand() % 399 + 1, rand() % 399 + 1, 3);
+
+ return 0;
+}
+
+/*==========================================
+ * @shuffle by MouseJstr
+ *------------------------------------------
+ */
+int atcommand_shuffle(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(0, sd);
+
+ if (strcmp(message, "area")== 0) {
+ map_foreachinarea(atcommand_shuffle_sub,sd->bl.m,
+ sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,
+ sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC);
+ } else if (strcmp(message, "map")== 0) {
+ map_foreachinmap(atcommand_shuffle_sub,sd->bl.m, BL_PC);
+ } else if (strcmp(message, "world") == 0) {
+ struct map_session_data **pl_allsd;
+ int i, users;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++)
+ atcommand_shuffle_sub(&pl_allsd[i]->bl, 0);
+ } else
+ clif_displaymessage(fd, "options are area, map, or world");
+
+ return 0;
+}
+
+int atcommand_rates(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[255];
+
+ nullpo_retr(0, sd);
+
+ sprintf(buf, "Experience rates: Base %.1fx / Job %.1fx",
+ battle_config.base_exp_rate/100., battle_config.job_exp_rate/100.);
+
+ clif_displaymessage(fd, buf);
+
+ return 0;
+}
+
+/*==========================================
+ * @me by lordalfa
+ * => Displays the OUTPUT string on top of
+ * the Visible players Heads.
+ *------------------------------------------
+ */
+
+int atcommand_me(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char tempmes[200];
+ nullpo_retr(-1, sd);
+
+ memset(tempmes, '\0', sizeof(tempmes));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a message (usage: @me <message>).");
+ return -1;
+ }
+
+ sscanf(message, "%199[^\n]", tempmes);
+ sprintf(atcmd_output, "* %s %s *", sd->status.name, tempmes);
+ clif_disp_overhead(sd, atcmd_output);
+
+ return 0;
+
+}
+
+/*==========================================
+ * @size
+ * => Resize your character sprite. [Valaris]
+ *------------------------------------------
+ */
+int atcommand_size(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int size=0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+
+ if (sscanf(message,"%d", &size) < 1)
+ return -1;
+
+ if(sd->state.size) {
+ sd->state.size=0;
+ pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, 3);
+ }
+
+ if(size==1) {
+ sd->state.size=1;
+ clif_specialeffect(&sd->bl,420,0);
+ } else if(size==2) {
+ sd->state.size=2;
+ clif_specialeffect(&sd->bl,422,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * @monsterignore
+ * => Makes monsters ignore you. [Valaris]
+ *------------------------------------------
+ */
+
+int atcommand_monsterignore(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ nullpo_retr(-1, sd);
+
+ if (!sd->state.monster_ignore) {
+ sd->state.monster_ignore = 1;
+ clif_displaymessage(sd->fd, "Monsters will now ignore you.");
+ } else {
+ sd->state.monster_ignore = 0;
+ clif_displaymessage(sd->fd, "Monsters are no longer ignoring you.");
+ }
+
+ return 0;
+}
+/*==========================================
+ * @fakename
+ * => Gives your character a fake name. [Valaris]
+ *------------------------------------------
+ */
+int atcommand_fakename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+
+ char name[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if((!message || !*message) && strlen(sd->fakename) > 1) {
+ sd->fakename[0]='\0';
+ pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Returned to real name.");
+ return 0;
+ }
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must enter a name.");
+ return 0;
+ }
+
+
+ if (sscanf(message, "%23[^\n]", name) < 1) {
+ return 0;
+ }
+
+ if(strlen(name) < 2) {
+ clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
+ return 0;
+ }
+
+ memcpy(sd->fakename,name,NAME_LENGTH-1);
+ clif_charnameack(0, &sd->bl);
+ clif_displaymessage(sd->fd,"Fake name enabled.");
+
+ return 0;
+}
+/*==========================================
+ * @mapflag [flagap name] [1|0|on|off] [map name] by Lupus
+ * => Shows information about the map flags [map name]
+ * Also set flags
+ *------------------------------------------
+ */
+int atcommand_mapflag(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+// WIP
+ return 0;
+}
+
+/*===================================
+ * Remove some messages
+ *-----------------------------------
+ */
+int atcommand_showexp(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showexp) {
+ sd->state.showexp = 0;
+ clif_displaymessage(fd, "Gained exp will not be shown.");
+ return 0;
+ }
+
+ sd->state.showexp = 1;
+ clif_displaymessage(fd, "Gained exp is now shown");
+ return 0;
+}
+
+int atcommand_showzeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showzeny) {
+ sd->state.showzeny = 0;
+ clif_displaymessage(fd, "Gained zeny will not be shown.");
+ return 0;
+ }
+
+ sd->state.showzeny = 1;
+ clif_displaymessage(fd, "Gained zeny is now shown");
+ return 0;
+}
+
+int atcommand_showdelay(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if (sd->state.showdelay) {
+ sd->state.showdelay = 0;
+ clif_displaymessage(fd, "Skill delay failures won't be shown.");
+ return 0;
+ }
+
+ sd->state.showdelay = 1;
+ clif_displaymessage(fd, "Skill delay failures are shown now.");
+ return 0;
+}
+
+/*==========================================
+ * Duel organizing functions [LuzZza]
+ *
+ * @duel [limit|nick] - create a duel
+ * @invite <nick> - invite player
+ * @accept - accept invitation
+ * @reject - reject invitation
+ * @leave - leave duel
+ *------------------------------------------
+ */
+int atcommand_invite(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ unsigned int did = sd->duel_group;
+ struct map_session_data *target_sd = map_nick2sd((char *)message);
+
+ if(did <= 0) {
+ // "Duel: @invite without @duel."
+ clif_displaymessage(fd, msg_txt(350));
+ return 0;
+ }
+
+ if(duel_list[did].max_players_limit > 0 &&
+ duel_list[did].members_count >= duel_list[did].max_players_limit) {
+
+ // "Duel: Limit of players is reached."
+ clif_displaymessage(fd, msg_txt(351));
+ return 0;
+ }
+
+ if(target_sd == NULL) {
+ // "Duel: Player not found."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+
+ if(target_sd->duel_group > 0 || target_sd->duel_invite > 0) {
+ // "Duel: Player already in duel."
+ clif_displaymessage(fd, msg_txt(353));
+ return 0;
+ }
+
+ duel_invite(did, sd, target_sd);
+ // "Duel: Invitation has been sent."
+ clif_displaymessage(fd, msg_txt(354));
+ return 0;
+}
+
+int atcommand_duel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char output[256];
+ unsigned int maxpl=0, newduel;
+ struct map_session_data *target_sd;
+
+ /* // Commnted because it can be disabled in at-comms conf.
+ if(!battle_config.duel_enable) {
+ clif_displaymessage(fd, "Duel: duel is disable.");
+ return 0;
+ }
+ */
+
+ if(sd->duel_group > 0) {
+ duel_showinfo(sd->duel_group, sd);
+ return 0;
+ }
+
+ if(sd->duel_invite > 0) {
+ // "Duel: @duel without @reject."
+ clif_displaymessage(fd, msg_txt(355));
+ return 0;
+ }
+
+ if(!duel_checktime(sd)) {
+ // "Duel: You can take part in duel only one time per %d minutes."
+ sprintf(output, msg_txt(356), battle_config.duel_time_interval);
+ clif_displaymessage(fd, output);
+ return 0;
+ }
+
+ if(strlen(message) > 0) {
+ if(sscanf(message, "%d", &maxpl) >= 1) {
+ if(maxpl < 2 || maxpl > 65535) {
+ clif_displaymessage(fd, msg_txt(357)); // "Duel: Invalid value."
+ return 0;
+ }
+ duel_create(sd, maxpl);
+ } else {
+ target_sd = map_nick2sd((char *)message);
+ if(target_sd != NULL) {
+ if((newduel = duel_create(sd, 2)) != -1) {
+ if(target_sd->duel_group > 0 || target_sd->duel_invite > 0) {
+ clif_displaymessage(fd, msg_txt(353)); // "Duel: Player already in duel."
+ return 0;
+ }
+ duel_invite(newduel, sd, target_sd);
+ clif_displaymessage(fd, msg_txt(354)); // "Duel: Invitation has been sent."
+ }
+ } else {
+ // "Duel: Player not found."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+ }
+ } else
+ duel_create(sd, 0);
+
+ return 0;
+}
+
+
+int atcommand_leave(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(sd->duel_group <= 0) {
+ // "Duel: @leave without @duel."
+ clif_displaymessage(fd, msg_txt(358));
+ return 0;
+ }
+
+ duel_leave(sd->duel_group, sd);
+ clif_displaymessage(fd, msg_txt(359)); // "Duel: You left the duel."
+ return 0;
+}
+
+int atcommand_accept(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char output[256];
+
+ if(!duel_checktime(sd)) {
+ // "Duel: You can take part in duel only one time per %d minutes."
+ sprintf(output, msg_txt(356), battle_config.duel_time_interval);
+ clif_displaymessage(fd, output);
+ return 0;
+ }
+
+ if(sd->duel_invite <= 0) {
+ // "Duel: @accept without invititation."
+ clif_displaymessage(fd, msg_txt(360));
+ return 0;
+ }
+
+ duel_accept(sd->duel_invite, sd);
+ // "Duel: Invitation has been accepted."
+ clif_displaymessage(fd, msg_txt(361));
+ return 0;
+}
+
+int atcommand_reject(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(sd->duel_invite <= 0) {
+ // "Duel: @reject without invititation."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+ }
+
+ duel_reject(sd->duel_invite, sd);
+ // "Duel: Invitation has been rejected."
+ clif_displaymessage(fd, msg_txt(352));
+ return 0;
+}
+
+/*===================================
+ * Away message (@away, @aw) [LuzZza]
+ *-----------------------------------
+ */
+int atcommand_away(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(strlen(message) > 0) {
+ if(strlen(message) > 128)
+ return -1;
+ strcpy(sd->away_message, message);
+ //"Away automessage has been activated."
+ clif_displaymessage(fd, msg_txt(546));
+ } else {
+ if(strlen(sd->away_message) > 0) {
+ sd->away_message[0] = 0;
+ //"Away automessage has been disabled."
+ clif_displaymessage(fd, msg_txt(547));
+ return 0;
+ }
+ //"Usage: @away,@aw <message>. Enter empty message for disable it."
+ clif_displaymessage(fd, msg_txt(548));
+ }
+ return 0;
+}
+
+// @clone/@slaveclone/@evilclone <playername> [Valaris]
+int atcommand_clone(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int x=0,y=0,flag=0,master=0,i=0;
+ struct map_session_data *pl_sd=NULL;
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must enter a name or character ID.");
+ return 0;
+ }
+
+ if((pl_sd=map_nick2sd((char *)message)) == NULL && (pl_sd=map_charid2sd(atoi(message))) == NULL) {
+ clif_displaymessage(fd, "Player not found.");
+ return 0;
+ }
+
+ if(pc_isGM(pl_sd) > pc_isGM(sd)) {
+ clif_displaymessage(fd, "Cannot clone a player of higher GM level than yourself.");
+ return 0;
+ }
+
+ if (strcmpi(command, "@clone") == 0)
+ flag = 1;
+ else if (strcmpi(command, "@slaveclone") == 0) {
+ flag = 2;
+ master = sd->bl.id;
+ if (battle_config.atc_slave_clone_limit
+ && mob_countslave(&sd->bl) >= battle_config.atc_slave_clone_limit) {
+ clif_displaymessage(fd, "You've reached your slave clones limit.");
+ return 0;
+ }
+ }
+
+ do {
+ x = sd->bl.x + (rand() % 10 - 5);
+ y = sd->bl.y + (rand() % 10 - 5);
+ } while (map_getcell(sd->bl.m,x,y,CELL_CHKNOPASS) && i++ < 10);
+
+ if (i >= 10) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+
+
+ if((x = mob_clone_spawn(pl_sd, (char*)mapindex_id2name(sd->mapindex), x, y, "", master, 0, flag?1:0, 0)) > 0) {
+ clif_displaymessage(fd, msg_txt(126+flag*2));
+ return 0;
+ }
+ clif_displaymessage(fd, msg_txt(127+flag*2));
+ return 0;
+}
+
+/*===================================
+ * Main chat [LuzZza]
+ * Usage: @main <on|off|message>
+ *-----------------------------------
+ */
+int atcommand_main(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ if(strlen(message) > 0) {
+
+ if(strlen(message) > 128)
+ return -1;
+
+ if(strcmpi(message, "on") == 0) {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ } else {
+ clif_displaymessage(fd, msg_txt(381)); // Main chat already activated.
+ }
+ } else if(strcmpi(message, "off") == 0) {
+ if(sd->state.mainchat) {
+ sd->state.mainchat = 0;
+ clif_displaymessage(fd, msg_txt(382)); // Main chat has been disabled.
+ } else {
+ clif_displaymessage(fd, msg_txt(383)); // Main chat already disabled.
+ }
+ } else {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ }
+ if (sd->sc_data[SC_NOCHAT].timer != -1) {
+ clif_displaymessage(fd, msg_txt(387));
+ return -1;
+ }
+ sprintf(atcmd_output, msg_txt(386), sd->status.name, message);
+ // I use 0xFE000000 color for signalizing that this message is
+ // main chat message. 0xFE000000 is invalid color, same using
+ // 0xFF000000 for simple (not colored) GM messages. [LuzZza]
+ intif_announce(atcmd_output, strlen(atcmd_output) + 1, 0xFE000000, 0);
+ }
+
+ } else {
+
+ if(sd->state.mainchat)
+ // Main chat currently enabled. Usage: @main <on|off>, @main <message>.
+ clif_displaymessage(fd, msg_txt(384));
+ else
+ // Main chat currently disabled. Usage: @main <on|off>, @main <message>.
+ clif_displaymessage(fd, msg_txt(385));
+ }
+ return 0;
+}
+
+void do_init_atcommand() {
+ users_db = db_alloc(__FILE__,__LINE__,DB_UINT,DB_OPT_BASE,sizeof(int));
+ duel_count = 0;
+ memset(&duel_list[0], 0, sizeof(duel_list));
+ return;
+}
+
+void do_final_atcommand() {
+ users_db->destroy(users_db,NULL);
+}
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
new file mode 100644
index 000000000..5c24778fa
--- /dev/null
+++ b/src/map/atcommand.h
@@ -0,0 +1,319 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ATCOMMAND_H_
+#define _ATCOMMAND_H_
+
+enum AtCommandType {
+ AtCommand_None = -1,
+ AtCommand_Broadcast = 0,
+ AtCommand_LocalBroadcast,
+ AtCommand_MapMove,
+ AtCommand_ResetState,
+ AtCommand_RuraP,
+ AtCommand_Rura,
+ AtCommand_Warp,
+ AtCommand_Where,
+ AtCommand_JumpTo,
+ AtCommand_Jump,
+ AtCommand_Who,
+ AtCommand_Who2,
+ AtCommand_Who3,
+ AtCommand_WhoMap,
+ AtCommand_WhoMap2,
+ AtCommand_WhoMap3,
+ AtCommand_WhoGM,
+ AtCommand_Save,
+ AtCommand_Load,
+ AtCommand_Speed,
+ AtCommand_Storage,
+ AtCommand_GuildStorage,
+ AtCommand_Option,
+ AtCommand_Hide,
+ AtCommand_JobChange,
+ AtCommand_JobChange2,
+ AtCommand_JobChange3,
+ AtCommand_Die,
+ AtCommand_Kill,
+ AtCommand_Alive,
+ AtCommand_Kami,
+ AtCommand_KamiB,
+ AtCommand_KamiC, //LuzZza
+ AtCommand_Heal,
+ AtCommand_Item,
+ AtCommand_Item2,
+ AtCommand_ItemReset,
+ AtCommand_ItemCheck,
+ AtCommand_BaseLevelUp,
+ AtCommand_JobLevelUp,
+ AtCommand_H,
+ AtCommand_Help,
+ AtCommand_H2,
+ AtCommand_Help2,
+ AtCommand_GM,
+ AtCommand_PvPOff,
+ AtCommand_PvPOn,
+ AtCommand_GvGOff,
+ AtCommand_GvGOn,
+ AtCommand_Model,
+ AtCommand_Go,
+ AtCommand_Spawn,
+ AtCommand_Monster,
+ AtCommand_MonsterSmall,
+ AtCommand_MonsterBig,
+ AtCommand_KillMonster,
+ AtCommand_KillMonster2,
+ AtCommand_Refine,
+ AtCommand_Produce,
+ AtCommand_Memo,
+ AtCommand_GAT,
+ AtCommand_Packet,
+ AtCommand_WaterLevel,
+ AtCommand_StatusPoint,
+ AtCommand_SkillPoint,
+ AtCommand_Zeny,
+ AtCommand_Param,
+ AtCommand_Strength,
+ AtCommand_Agility,
+ AtCommand_Vitality,
+ AtCommand_Intelligence,
+ AtCommand_Dexterity,
+ AtCommand_Luck,
+ AtCommand_GuildLevelUp,
+ AtCommand_MakeEgg,
+ AtCommand_PetFriendly,
+ AtCommand_PetHungry,
+ AtCommand_PetRename,
+ AtCommand_Recall,
+ AtCommand_Revive,
+ AtCommand_CharacterStatsAll,
+ AtCommand_CharacterLoad,
+ AtCommand_Night,
+ AtCommand_Day,
+ AtCommand_Doom,
+ AtCommand_DoomMap,
+ AtCommand_Raise,
+ AtCommand_RaiseMap,
+ AtCommand_Kick,
+ AtCommand_KickAll,
+ AtCommand_AllSkill,
+ AtCommand_QuestSkill,
+ AtCommand_LostSkill,
+ AtCommand_SpiritBall,
+ AtCommand_Party,
+ AtCommand_Guild,
+ AtCommand_AgitStart,
+ AtCommand_AgitEnd,
+ AtCommand_MapExit,
+ AtCommand_IDSearch,
+ AtCommand_RecallAll,
+ AtCommand_ReloadItemDB,
+ AtCommand_ReloadMobDB,
+ AtCommand_ReloadSkillDB,
+ AtCommand_ReloadScript,
+ AtCommand_ReloadGMDB,
+ AtCommand_ReloadAtcommand,
+ AtCommand_ReloadBattleConf,
+ AtCommand_ReloadStatusDB,
+ AtCommand_ReloadPcDB,
+ AtCommand_ReloadMOTD, // [Valaris]
+ AtCommand_MapInfo,
+ AtCommand_Dye,
+ AtCommand_Hstyle,
+ AtCommand_Hcolor,
+ AtCommand_StatAll,
+ AtCommand_CharBlock, // by Yor
+ AtCommand_CharBan, // by Yor
+ AtCommand_CharUnBlock, // by Yor
+ AtCommand_CharUnBan, // by Yor
+ AtCommand_MountPeco, // by Valaris
+ AtCommand_CharMountPeco, // by Yor
+ AtCommand_GuildSpy, // [Syrus22]
+ AtCommand_PartySpy, // [Syrus22]
+ AtCommand_RepairAll, // [Valaris]
+ AtCommand_GuildRecall, // by Yor
+ AtCommand_PartyRecall, // by Yor
+ AtCommand_Nuke, // [Valaris]
+ AtCommand_Shownpc,
+ AtCommand_Hidenpc,
+ AtCommand_Loadnpc,
+ AtCommand_Unloadnpc,
+ AtCommand_ServerTime, // by Yor
+ AtCommand_CharDelItem, // by Yor
+ AtCommand_Jail, // by Yor
+ AtCommand_UnJail, // by Yor
+ AtCommand_Disguise, // [Valaris]
+ AtCommand_UnDisguise, // by Yor
+ AtCommand_CharDisguise, // Kalaspuff
+ AtCommand_CharUnDisguise, // Kalaspuff
+ AtCommand_EMail, // by Yor
+ AtCommand_Hatch,
+ AtCommand_Effect, // by Apple
+ AtCommand_Char_Cart_List, // by Yor
+ AtCommand_AddWarp, // by MouseJstr
+ AtCommand_Follow, // by MouseJstr
+ AtCommand_SkillOn, // by MouseJstr
+ AtCommand_SkillOff, // by MouseJstr
+ AtCommand_Killer, // by MouseJstr
+ AtCommand_NpcMove, // by MouseJstr
+ AtCommand_Killable, // by MouseJstr
+ AtCommand_CharKillable, // by MouseJstr
+ AtCommand_Dropall, // by MouseJstr
+ AtCommand_Chardropall, // by MouseJstr
+ AtCommand_Storeall, // by MouseJstr
+ AtCommand_Charstoreall, // by MouseJstr
+ AtCommand_Skillid, // by MouseJstr
+ AtCommand_Useskill, // by MouseJstr
+ AtCommand_Summon,
+ AtCommand_Rain,
+ AtCommand_Snow,
+ AtCommand_Sakura,
+ AtCommand_Clouds,
+ AtCommand_Clouds2, // [Valaris]
+ AtCommand_Fog,
+ AtCommand_Fireworks,
+ AtCommand_Leaves,
+ AtCommand_AdjGmLvl, // MouseJstr
+ AtCommand_AdjCmdLvl, // MouseJstr
+ AtCommand_Trade, // MouseJstr
+ AtCommand_Send,
+ AtCommand_SetBattleFlag,
+ AtCommand_UnMute,
+ AtCommand_Clearweather, // by Dexity
+ AtCommand_UpTime, // by MC Cameri
+ AtCommand_ChangeSex, // by MC Cameri
+ AtCommand_Mute, // [celest]
+ AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it.
+ AtCommand_HappyHappyJoyJoy, // [Valaris]
+ AtCommand_Refresh, // by MC Cameri
+ AtCommand_PetId, // by MC Cameri
+ AtCommand_Identify, // by MC Cameri
+ AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw
+ AtCommand_MiscEffect, // by MC Cameri
+ AtCommand_MobSearch,
+ AtCommand_CleanMap,
+ AtCommand_NpcTalk,
+ AtCommand_PetTalk,
+ AtCommand_Users,
+ // SQL-only commands start
+#ifndef TXT_ONLY
+ AtCommand_CheckMail, // [Valaris]
+ AtCommand_ListMail, // [Valaris]
+ AtCommand_ListNewMail, // [Valaris]
+ AtCommand_ReadMail, // [Valaris]
+ AtCommand_SendMail, // [Valaris]
+ AtCommand_DeleteMail, // [Valaris]
+ AtCommand_SendPriorityMail, // [Valaris]
+ AtCommand_RefreshOnline, // [Valaris]
+ // SQL-only commands end
+#endif
+ AtCommand_SkillTree, // by MouseJstr
+ AtCommand_Marry, // by MouseJstr
+ AtCommand_Divorce, // by MouseJstr
+ AtCommand_Grind, // by MouseJstr
+ AtCommand_Grind2, // by MouseJstr
+
+ AtCommand_Me, //added by massdriller, code by lordalfa
+
+ AtCommand_DMStart, // by MouseJstr
+ AtCommand_DMTick, // by MouseJstr
+
+ AtCommand_JumpToId, // by Dino9021
+ AtCommand_JumpToId2, // by Dino9021
+ AtCommand_RecallId, // by Dino9021
+ AtCommand_RecallId2, // by Dino9021
+ AtCommand_KickId, // by Dino9021
+ AtCommand_KickId2, // by Dino9021
+ AtCommand_ReviveId, // by Dino9021
+ AtCommand_ReviveId2, // by Dino9021
+ AtCommand_KillId, // by Dino9021
+ AtCommand_KillId2, // by Dino9021
+ AtCommand_CharKillableId, // by Dino9021
+ AtCommand_CharKillableId2, // by Dino9021
+ AtCommand_Sound,
+ AtCommand_UndisguiseAll,
+ AtCommand_DisguiseAll,
+ AtCommand_ChangeLook,
+ AtCommand_AutoLoot, //by Upa-Kun
+ AtCommand_MobInfo, //by Lupus
+ AtCommand_Adopt, // by Veider
+ AtCommand_Version, // by Ancyker
+
+ AtCommand_MuteArea, // MouseJstr
+ AtCommand_Shuffle, // MouseJstr
+ AtCommand_Rates, // MouseJstr
+
+ AtCommand_ItemInfo, // Lupus
+ AtCommand_MapFlag, // Lupus
+ AtCommand_MonsterIgnore, // [Valaris]
+ AtCommand_FakeName, // [Valaris]
+ AtCommand_Size, // [Valaris]
+ AtCommand_ShowDelay,
+ AtCommand_ShowExp,
+ AtCommand_ShowZeny,
+ AtCommand_AutoTrade,//durf
+ AtCommand_ChangeGM,//durf
+ AtCommand_ChangeLeader,
+
+ AtCommand_Invite, // By LuzZza
+ AtCommand_Duel, // By LuzZza
+ AtCommand_Leave, // By LuzZza
+ AtCommand_Accept, // By LuzZza
+ AtCommand_Reject, // By LuzZza
+
+ AtCommand_Away, // LuzZza
+ AtCommand_Main, // LuzZza
+
+ AtCommand_Clone, // [Valaris]
+
+ // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex]
+ AtCommand_Unknown,
+ AtCommand_MAX
+};
+
+typedef enum AtCommandType AtCommandType;
+
+typedef struct AtCommandInfo {
+ AtCommandType type;
+ const char* command;
+ int level;
+ int (*proc)(const int, struct map_session_data*,
+ const char* command, const char* message);
+} AtCommandInfo;
+
+AtCommandType
+is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl);
+
+AtCommandType atcommand(
+ struct map_session_data *sd,
+ const int level, const char* message, AtCommandInfo* info);
+int get_atcommand_level(const AtCommandType type);
+
+char * msg_txt(int msg_number); // [Yor]
+char * player_title_txt(int level); // [Lupus]
+
+void do_init_atcommand(void);
+void do_final_atcommand(void);
+
+int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris]
+int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor]
+int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message);
+
+int duel_leave(const unsigned int did, struct map_session_data* sd); // [LuzZza]
+int duel_reject(const unsigned int did, struct map_session_data* sd); // [LuzZza]
+
+int atcommand_config_read(const char *cfgName);
+int msg_config_read(const char *cfgName);
+void do_final_msg(void);
+
+char *estr_lower(char *str);
+
+int e_mail_check(char *email);
+
+#define MAX_MSG 1000
+extern char *msg_table[MAX_MSG];
+
+#endif
+
diff --git a/src/map/battle.c b/src/map/battle.c
new file mode 100644
index 000000000..91563d939
--- /dev/null
+++ b/src/map/battle.c
@@ -0,0 +1,4412 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "battle.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "itemdb.h"
+#include "clif.h"
+#include "pet.h"
+#include "guild.h"
+#include "party.h"
+
+#define is_boss(bl) status_get_mexp(bl) // Can refine later [Aru]
+
+int attr_fix_table[4][10][10];
+
+struct Battle_Config battle_config;
+
+/*==========================================
+ * Ž©•ª‚ðƒ?ƒbƒN‚µ‚Ä‚¢‚éMOB‚Ì?‚ð?‚¦‚é(foreachclient)
+ *------------------------------------------
+ */
+static int battle_counttargeted_sub(struct block_list *bl, va_list ap)
+{
+ int id, *c, target_lv;
+ struct block_list *src;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ id = va_arg(ap,int);
+ nullpo_retr(0, c = va_arg(ap, int *));
+ src = va_arg(ap,struct block_list *);
+ target_lv = va_arg(ap,int);
+
+ if (id == bl->id || (src && id == src->id))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd && sd->attacktarget == id && sd->attacktimer != -1 && sd->attacktarget_lv >= target_lv)
+ (*c)++;
+ }
+ else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md && md->target_id == id && md->timer != -1 && md->state.state == MS_ATTACK && md->target_lv >= target_lv)
+ (*c)++;
+ //printf("md->target_lv:%d, target_lv:%d\n", md->target_lv, target_lv);
+ }
+ else if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data *)bl;
+ if (pd && pd->target_id == id && pd->timer != -1 && pd->state.state == MS_ATTACK && pd->target_lv >= target_lv)
+ (*c)++;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Ž©•ª‚ðƒ?ƒbƒN‚µ‚Ä‚¢‚é‘Î?Û‚Ì?”‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í?®?”‚Å0ˆÈ?ã
+ *------------------------------------------
+ */
+int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv)
+{
+ int c = 0;
+ nullpo_retr(0, bl);
+
+ map_foreachinarea(battle_counttargeted_sub, bl->m,
+ bl->x-AREA_SIZE, bl->y-AREA_SIZE,
+ bl->x+AREA_SIZE, bl->y+AREA_SIZE, BL_CHAR,
+ bl->id, &c, src, target_lv);
+
+ return c;
+}
+
+/*==========================================
+ * Get random targetting enemy
+ *------------------------------------------
+ */
+static int battle_gettargeted_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list **bl_list;
+ struct block_list *target;
+ int *c;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ bl_list = va_arg(ap, struct block_list **);
+ nullpo_retr(0, c = va_arg(ap, int *));
+ nullpo_retr(0, target = va_arg(ap, struct block_list *));
+
+ if (bl->id == target->id)
+ return 0;
+ if (*c >= 24)
+ return 0;
+
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (!sd || sd->attacktarget != target->id || sd->attacktimer == -1)
+ return 0;
+ } else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (!md || md->target_id != target->id || md->timer == -1 || md->state.state != MS_ATTACK)
+ return 0;
+ } else if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data *)bl;
+ if (!pd || pd->target_id != target->id || pd->timer == -1 || pd->state.state != MS_ATTACK)
+ return 0;
+ }
+
+ bl_list[(*c)++] = bl;
+ return 0;
+}
+
+int battle_getcurrentskill(struct block_list *bl)
+{ //Returns the current/last skill in use by this bl.
+ switch (bl->type)
+ {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->skillid;
+ case BL_MOB:
+ return ((struct mob_data*)bl)->skillid;
+ case BL_PET:
+ return 0; //Skill data is not stored for pets...
+ break;
+ case BL_SKILL:
+ {
+ struct skill_unit * su = (struct skill_unit*)bl;
+ if (su->group)
+ return su->group->skill_id;
+ }
+ break;
+ }
+ return 0;
+}
+
+//Returns the id of the current targetted character of the passed bl. [Skotlex]
+int battle_gettarget(struct block_list *bl)
+{
+ switch (bl->type)
+ {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->attacktarget;
+ case BL_MOB:
+ return ((struct mob_data*)bl)->target_id;
+ case BL_PET:
+ return ((struct pet_data*)bl)->target_id;
+ }
+ return 0;
+}
+
+struct block_list* battle_gettargeted(struct block_list *target)
+{
+ struct block_list *bl_list[24];
+ int c = 0;
+ nullpo_retr(NULL, target);
+
+ memset(bl_list, 0, sizeof(bl_list));
+ map_foreachinarea(battle_gettargeted_sub, target->m,
+ target->x-AREA_SIZE, target->y-AREA_SIZE,
+ target->x+AREA_SIZE, target->y+AREA_SIZE, BL_CHAR,
+ bl_list, &c, target);
+ if (c == 0 || c > 24)
+ return NULL;
+ return bl_list[rand()%c];
+}
+
+// ƒ_ƒ??[ƒW‚Ì’x‰„
+struct delay_damage {
+ struct block_list *src;
+ int target;
+ int damage;
+ unsigned short distance;
+ unsigned short skill_lv;
+ unsigned short skill_id;
+ unsigned short dmg_lv;
+ unsigned short flag;
+ unsigned char attack_type;
+};
+
+int battle_delay_damage_sub (int tid, unsigned int tick, int id, int data)
+{
+ struct delay_damage *dat = (struct delay_damage *)data;
+ struct block_list *target = map_id2bl(dat->target);
+ if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL && !status_isdead(target) &&
+ target->m == dat->src->m && check_distance_bl(dat->src, target, dat->distance)) //Check to see if you haven't teleported. [Skotlex]
+ {
+ battle_damage(dat->src, target, dat->damage, dat->flag);
+ if (!status_isdead(target) && (dat->dmg_lv == ATK_DEF || dat->damage > 0) && dat->attack_type)
+ skill_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type, tick);
+ }
+ aFree(dat);
+ return 0;
+}
+
+int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int flag)
+{
+ struct delay_damage *dat;
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (!battle_config.delay_battle_damage) {
+ battle_damage(src, target, damage, flag);
+ if (!status_isdead(target) && (damage > 0 || dmg_lv == ATK_DEF) && attack_type)
+ skill_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick());
+ return 0;
+ }
+ dat = (struct delay_damage *)aCalloc(1, sizeof(struct delay_damage));
+ dat->src = src;
+ dat->target = target->id;
+ dat->skill_id = skill_id;
+ dat->skill_lv = skill_lv;
+ dat->attack_type = attack_type;
+ dat->damage = damage;
+ dat->dmg_lv = dmg_lv;
+ dat->flag = flag;
+ dat->distance = distance_bl(src, target)+10; //Attack should connect regardless unless you teleported.
+ add_timer(tick, battle_delay_damage_sub, src->id, (int)dat);
+
+ return 0;
+}
+
+// ŽÀ?Û‚ÉHP‚ð‘€?ì
+int battle_damage(struct block_list *bl,struct block_list *target,int damage, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change *sc_data;
+ short *sc_count;
+
+ nullpo_retr(0, target); //bl‚ÍNULL‚ŌĂ΂ê‚邱‚Æ‚ª‚ ‚é‚Ì‚Å‘¼‚Ń`ƒFƒbƒN
+
+ sc_data = status_get_sc_data(target);
+ sc_count = status_get_sc_count(target);
+
+ if (damage == 0 ||
+ target->prev == NULL ||
+ target->type == BL_PET)
+ return 0;
+
+ if (bl) {
+ if (bl->prev == NULL)
+ return 0;
+ if (bl->type == BL_PC) {
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+ }
+ }
+
+ if (damage < 0)
+ return battle_heal(bl,target,-damage,0,flag);
+
+ if (!flag && sc_count && *sc_count > 0) {
+ // “€Œ‹?A?Ή»?A?‡–°‚ð?Á‹Ž
+ if (sc_data[SC_FREEZE].timer != -1)
+ status_change_end(target,SC_FREEZE,-1);
+ if (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2 == 0)
+ status_change_end(target,SC_STONE,-1);
+ if (sc_data[SC_SLEEP].timer != -1)
+ status_change_end(target,SC_SLEEP,-1);
+ if (sc_data[SC_WINKCHARM].timer != -1)
+ status_change_end(target,SC_WINKCHARM,-1);
+ }
+
+ if (target->type == BL_MOB) { // MOB
+ struct mob_data *md = (struct mob_data *)target;
+ if (md && md->skilltimer != -1 && md->state.skillcastcancel) // ‰r?¥–WŠQ
+ skill_castcancel(target,0);
+ return mob_damage(bl,md,damage,0);
+ } else if (target->type == BL_PC) { // PC
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ if (!tsd)
+ return 0;
+ if (sc_data[SC_DEVOTION].val1 && bl && battle_getcurrentskill(bl) != PA_PRESSURE)
+ { //Devotion only works on attacks from a source (prevent it from absorbing coma) [Skotlex]
+ struct map_session_data *sd2 = map_id2sd(tsd->sc_data[SC_DEVOTION].val1);
+ if (sd2 && sd2->devotion[sc_data[SC_DEVOTION].val2] == target->id)
+ {
+ clif_damage(bl, &sd2->bl, gettick(), 0, 0, damage, 0, 0, 0);
+ pc_damage(&sd2->bl, sd2, damage);
+ return 0;
+ } else
+ status_change_end(target, SC_DEVOTION, -1);
+ }
+
+ if (tsd->skilltimer != -1) { // ‰r?¥–WŠQ
+ // ƒtƒFƒ“ƒJ?[ƒh‚â–WŠQ‚³‚ê‚È‚¢ƒXƒLƒ‹‚©‚ÌŒŸ?¸
+ if ((!tsd->special_state.no_castcancel || map_flag_gvg(target->m)) && tsd->state.skillcastcancel &&
+ !tsd->special_state.no_castcancel2)
+ skill_castcancel(target,0);
+ }
+ return pc_damage(bl,tsd,damage);
+ } else if (target->type == BL_SKILL)
+ return skill_unit_ondamaged((struct skill_unit *)target, bl, damage, gettick());
+ return 0;
+}
+
+int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag)
+{
+ nullpo_retr(0, target); //bl‚ÍNULL‚ŌĂ΂ê‚邱‚Æ‚ª‚ ‚é‚Ì‚Å‘¼‚Ń`ƒFƒbƒN
+
+ if (target->type == BL_PET)
+ return 0;
+ if (target->type == BL_PC && pc_isdead((struct map_session_data *)target) )
+ return 0;
+ if (hp == 0 && sp == 0)
+ return 0;
+
+ if (hp < 0)
+ return battle_damage(bl,target,-hp,flag);
+
+ if (target->type == BL_MOB)
+ return mob_heal((struct mob_data *)target,hp);
+ else if (target->type == BL_PC)
+ return pc_heal((struct map_session_data *)target,hp,sp);
+ return 0;
+}
+
+// ?UŒ‚’âŽ~
+int battle_stopattack(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if (bl->type == BL_MOB)
+ return mob_stopattack((struct mob_data*)bl);
+ else if (bl->type == BL_PC)
+ return pc_stopattack((struct map_session_data*)bl);
+ else if (bl->type == BL_PET)
+ return pet_stopattack((struct pet_data*)bl);
+ return 0;
+}
+
+// Returns whether the given object is moving or not.
+int battle_iswalking(struct block_list *bl) {
+ switch (bl->type)
+ {
+ case BL_PC:
+ return (((struct map_session_data*)bl)->walktimer != -1);
+ case BL_MOB:
+ return (((struct mob_data*)bl)->state.state == MS_WALK);
+ case BL_PET:
+ return (((struct pet_data*)bl)->state.state == MS_WALK);
+ default:
+ return 0;
+ }
+}
+// ˆÚ“®’âŽ~
+int battle_stopwalking(struct block_list *bl,int type)
+{
+ nullpo_retr(0, bl);
+ if (bl->type == BL_MOB)
+ return mob_stop_walking((struct mob_data*)bl,type);
+ else if (bl->type == BL_PC)
+ return pc_stop_walking((struct map_session_data*)bl,type);
+ else if (bl->type == BL_PET)
+ return pet_stop_walking((struct pet_data*)bl,type);
+ return 0;
+}
+
+
+/*==========================================
+ * Does attribute fix modifiers.
+ * Added passing of the chars so that the status changes can affect it. [Skotlex]
+ * Note: Passing src/target == NULL is perfectly valid, it skips SC_ checks.
+ *------------------------------------------
+ */
+int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_elem)
+{
+ int def_type = def_elem % 10, def_lv = def_elem / 10 / 2;
+ struct status_change *sc_data=NULL, *tsc_data=NULL;
+ int ratio;
+
+ if (src) sc_data = status_get_sc_data(src);
+ if (target) tsc_data = status_get_sc_data(target);
+
+ if (atk_elem < 0 || atk_elem > 9)
+ atk_elem = rand()%9; //•?Ší‘®?«ƒ‰ƒ“ƒ_ƒ€‚Å•t‰Á
+
+ if (def_type < 0 || def_type > 9 ||
+ def_lv < 1 || def_lv > 4) { // ‘® ?«’l‚ª‚¨‚©‚µ‚¢‚Ì‚Å‚Æ‚è‚ ‚¦‚¸‚»‚Ì‚Ü‚Ü•Ô‚·
+ if (battle_config.error_log)
+ ShowError("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv);
+ //TODO: Remove this debug case once the cause is resolved. [Skotlex]
+ if (src) switch (src->type) {
+ case BL_MOB:
+ ShowDebug("src: Mob %s-%d\n", ((struct mob_data*)src)->name, ((struct mob_data*)src)->class_);
+ break;
+ case BL_PC:
+ ShowDebug("src: Player %s-%d\n", ((struct map_session_data*)src)->status.name, ((struct map_session_data*)src)->bl.id);
+ break;
+ case BL_PET:
+ ShowDebug("src: Pet %s-%d\n", ((struct pet_data*)src)->name, ((struct pet_data*)src)->bl.id);
+ break;
+ case BL_SKILL:
+ ShowDebug("src: Ground Skill id: %d\n", ((struct skill_unit*)src)->group->skill_id);
+ break;
+ default:
+ ShowDebug("unknown source type %d.\n", src->type);
+ }
+ if (target) switch (target->type) {
+ case BL_MOB:
+ ShowDebug("target: Mob %s-%d\n", ((struct mob_data*)target)->name, ((struct mob_data*)target)->class_);
+ break;
+ case BL_PC:
+ ShowDebug("target: Player %s-%d\n", ((struct map_session_data*)target)->status.name, ((struct map_session_data*)target)->bl.id);
+ break;
+ case BL_PET:
+ ShowDebug("target: Pet %s-%d\n", ((struct pet_data*)target)->name, ((struct pet_data*)target)->bl.id);
+ break;
+ case BL_SKILL:
+ ShowDebug("target: Ground Skill id: %d\n", ((struct skill_unit*)target)->group->skill_id);
+ break;
+ default:
+ ShowDebug("unknown target type %d.\n", target->type);
+ }
+ return damage;
+ }
+
+ ratio = attr_fix_table[def_lv-1][atk_elem][def_type];
+ if (sc_data)
+ {
+ if(sc_data[SC_VOLCANO].timer!=-1 && atk_elem == 3)
+ ratio += enchant_eff[sc_data[SC_VOLCANO].val1-1];
+ if(sc_data[SC_VIOLENTGALE].timer!=-1 && atk_elem == 4)
+ ratio += enchant_eff[sc_data[SC_VIOLENTGALE].val1-1];
+ if(sc_data[SC_DELUGE].timer!=-1 && atk_elem == 1)
+ ratio += enchant_eff[sc_data[SC_DELUGE].val1-1];
+ }
+ if (tsc_data)
+ {
+ if(tsc_data[SC_ARMOR_ELEMENT].timer!=-1)
+ {
+ if (tsc_data[SC_ARMOR_ELEMENT].val1 == atk_elem)
+ ratio -= tsc_data[SC_ARMOR_ELEMENT].val2;
+ else
+ if (tsc_data[SC_ARMOR_ELEMENT].val3 == atk_elem)
+ ratio -= tsc_data[SC_ARMOR_ELEMENT].val4;
+ }
+ }
+ return damage*ratio/100;
+}
+
+static int battle_walkdelay_sub(int tid, unsigned int tick, int id, int data)
+{
+ struct block_list *bl = map_id2bl(id);
+ if (!bl || status_isdead(bl))
+ return 0;
+
+ switch (bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ if (sd->walktimer != -1)
+ pc_stop_walking (sd,3);
+ if (sd->canmove_tick < tick)
+ sd->canmove_tick = tick + data;
+ }
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data*)bl;
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,3);
+ if (md->canmove_tick < tick)
+ md->canmove_tick = tick + data;
+ }
+ break;
+ }
+ return 0;
+}
+/*==========================================
+ * Applies walk delay based on attack type. [Skotlex]
+ *------------------------------------------
+ */
+int battle_walkdelay(struct block_list *bl, unsigned int tick, int adelay, int delay, int div_) {
+
+ if (status_isdead(bl))
+ return 0;
+
+ if (bl->type == BL_PC) {
+ if (battle_config.pc_walk_delay_rate != 100)
+ delay = delay*battle_config.pc_walk_delay_rate/100;
+ } else
+ if (battle_config.walk_delay_rate != 100)
+ delay = delay*battle_config.walk_delay_rate/100;
+
+ if (div_ > 1) //Multi-hit skills mean higher delays.
+ delay += battle_config.multihit_delay*(div_-1);
+
+ if (delay <= 0)
+ return 0;
+
+ //See if it makes sense to set this trigger.
+ switch (bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ if (DIFF_TICK(sd->canmove_tick, tick+adelay) > 0)
+ return 0;
+ if (!adelay) //No need of timer.
+ sd->canmove_tick = tick + delay;
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data*)bl;
+ if (DIFF_TICK(md->canmove_tick, tick+adelay) > 0)
+ return 0;
+ if (!adelay) //No need of timer.
+ md->canmove_tick = tick + delay;
+ break;
+ }
+ default:
+ return 0;
+ }
+ if (adelay > 0)
+ add_timer(tick+adelay, battle_walkdelay_sub, bl->id, delay);
+ return 1;
+}
+
+/*==========================================
+ * ƒ_ƒ??[ƒW?Å?IŒvŽZ
+ *------------------------------------------
+ */
+int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct mob_data *md = NULL;
+ struct status_change *sc_data, *sc;
+ short *sc_count;
+ int class_;
+
+ nullpo_retr(0, bl);
+
+ if (damage <= 0)
+ return 0;
+
+ class_ = status_get_class(bl);
+
+ if (bl->type == BL_MOB) {
+ md=(struct mob_data *)bl;
+ } else if (bl->type == BL_PC) {
+ sd=(struct map_session_data *)bl;
+ }
+
+ sc_data = status_get_sc_data(bl);
+ sc_count = status_get_sc_count(bl);
+
+ if(flag&BF_LONG && map_getcell(bl->m, bl->x, bl->y, CELL_CHKPNEUMA) &&
+ ((flag&BF_WEAPON && skill_num != NPC_GUIDEDATTACK) ||
+ (flag&BF_MISC && skill_num != PA_PRESSURE) ||
+ (flag&BF_MAGIC && skill_num == ASC_BREAKER))){ // It should block only physical part of Breaker! [Lupus], on the contrary, players all over the boards say it completely blocks Breaker x.x' [Skotlex]
+ return 0;
+ }
+
+ if (sc_count && *sc_count > 0) {
+ //First, sc_*'s that reduce damage to 0.
+ if (sc_data[SC_SAFETYWALL].timer!=-1 && flag&BF_SHORT && (skill_num != NPC_GUIDEDATTACK && skill_num != AM_DEMONSTRATION)
+ ) {
+ // ƒZ?[ƒtƒeƒBƒEƒH?[ƒ‹
+ struct skill_unit_group *group = (struct skill_unit_group *)sc_data[SC_SAFETYWALL].val3;
+ if (group) {
+ if (--group->val2<=0)
+ skill_delunitgroup(group);
+ return 0;
+ } else {
+ status_change_end(bl,SC_SAFETYWALL,-1);
+ }
+ }
+
+ if(sc_data[SC_LANDPROTECTOR].timer!=-1 && flag&BF_MAGIC)
+ return 0;
+
+ /* Moved to battle_calc_weapon_attack for now.
+ if(sc_data[SC_KAUPE].timer != -1 && damage > 0 && !skill_num) {
+ if(rand()%100 < sc_data[SC_KAUPE].val2) {
+ clif_skill_nodamage(bl,bl,SL_KAUPE,sc_data[SC_KAUPE].val1,1);
+ if (--sc_data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
+ status_change_end(bl, SC_KAUPE, -1);
+ return 0;
+ }
+ }
+ */
+
+ if(sc_data[SC_AUTOGUARD].timer != -1 && flag&BF_WEAPON &&
+ rand()%100 < sc_data[SC_AUTOGUARD].val2) {
+ int delay;
+ clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sc_data[SC_AUTOGUARD].val1,1);
+ // different delay depending on skill level [celest]
+ if (sc_data[SC_AUTOGUARD].val1 <= 5)
+ delay = 300;
+ else if (sc_data[SC_AUTOGUARD].val1 > 5 && sc_data[SC_AUTOGUARD].val1 <= 9)
+ delay = 200;
+ else
+ delay = 100;
+ if(sd)
+ sd->canmove_tick = gettick() + delay;
+ else if(md)
+ md->canmove_tick = gettick() + delay;
+
+ if(sc_data[SC_SHRINK].timer != -1 && rand()%100<5*sc_data[SC_AUTOGUARD].val1)
+ skill_blown(bl,src,skill_get_blewcount(CR_SHRINK,1));
+ return 0;
+ }
+
+// -- moonsoul (chance to block attacks with new Lord Knight skill parrying)
+//
+ if(sc_data[SC_PARRYING].timer != -1 && flag&BF_WEAPON &&
+ rand()%100 < sc_data[SC_PARRYING].val2) {
+ clif_skill_nodamage(bl,bl,LK_PARRYING,sc_data[SC_PARRYING].val1,1);
+ return 0;
+ }
+
+ if(sc_data[SC_DODGE].timer != -1 && (flag&BF_LONG || (sc_data[SC_SPURT].timer != -1 && flag&BF_WEAPON))
+ && rand()%100 < 20) {
+ clif_skill_nodamage(bl,bl,TK_DODGE,1,1);
+ if (sc_data[SC_COMBO].timer == -1)
+ status_change_start(bl, SC_COMBO, TK_JUMPKICK, src->id, 0, 0, 2000, 0);
+ return 0;
+ }
+
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&BF_MAGIC
+ && rand()%100 < 75 && !(skill_get_inf(skill_num)&INF_GROUND_SKILL))
+ return 0;
+
+ //Now damage increasing effects
+ if(sc_data[SC_AETERNA].timer!=-1 && skill_num != PA_PRESSURE){
+ damage<<=1;
+ if (skill_num != ASC_BREAKER || flag & BF_MAGIC) //Only end it on the second attack of breaker. [Skotlex]
+ status_change_end( bl,SC_AETERNA,-1 );
+ }
+
+ if(sc_data[SC_SPIDERWEB].timer!=-1) // [Celest]
+ if ((flag&BF_SKILL && skill_get_pl(skill_num)==3) ||
+ (!flag&BF_SKILL && status_get_attack_element(src)==3)) {
+ damage<<=1;
+ status_change_end(bl, SC_SPIDERWEB, -1);
+ }
+
+ //Finally damage reductions....
+ if(sc_data[SC_ASSUMPTIO].timer != -1){
+ if(map_flag_vs(bl->m))
+ damage=damage*2/3; //Receive 66% damage
+ else
+ damage>>=1; //Receive 50% damage
+ }
+
+ if(sc_data[SC_DEFENDER].timer != -1 && flag&BF_LONG && flag&BF_WEAPON)
+ damage=damage*(100-sc_data[SC_DEFENDER].val2)/100;
+
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&BF_LONG && flag&BF_WEAPON)
+ damage >>=1;
+
+ if(sc_data[SC_ENERGYCOAT].timer!=-1 && flag&BF_WEAPON){
+ if(sd){
+ if(sd->status.sp>0){
+ int per = sd->status.sp * 5 / (sd->status.max_sp + 1);
+ sd->status.sp -= sd->status.sp * (per * 5 + 10) / 1000;
+ if( sd->status.sp < 0 ) sd->status.sp = 0;
+ damage -= damage * ((per+1) * 6) / 100;
+ clif_updatestatus(sd,SP_SP);
+ }
+ if(sd->status.sp<=0)
+ status_change_end( bl,SC_ENERGYCOAT,-1 );
+ }
+ else
+ damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100;
+ }
+
+ if(sc_data[SC_REJECTSWORD].timer!=-1 && flag&BF_WEAPON &&
+ // Fixed the condition check [Aalye]
+ (src->type==BL_MOB || (src->type==BL_PC && (((struct map_session_data *)src)->status.weapon == 1 ||
+ ((struct map_session_data *)src)->status.weapon == 2 ||
+ ((struct map_session_data *)src)->status.weapon == 3)))){
+ if(rand()%100 < (15*sc_data[SC_REJECTSWORD].val1)){
+ damage = damage*50/100;
+ clif_damage(bl,src,gettick(),0,0,damage,0,0,0);
+ battle_damage(bl,src,damage,0);
+ clif_skill_nodamage(bl,bl,ST_REJECTSWORD,sc_data[SC_REJECTSWORD].val1,1);
+ if((--sc_data[SC_REJECTSWORD].val2)<=0)
+ status_change_end(bl, SC_REJECTSWORD, -1);
+ }
+ }
+
+ //Finally Kyrie because it may, or not, reduce damage to 0.
+ if(sc_data[SC_KYRIE].timer!=-1){
+ sc=&sc_data[SC_KYRIE];
+ sc->val2-=damage;
+ if(flag&BF_WEAPON || skill_num == TF_THROWSTONE){
+ if(sc->val2>=0)
+ damage=0;
+ else
+ damage=-sc->val2;
+ }
+ if((--sc->val3)<=0 || (sc->val2<=0) || skill_num == AL_HOLYLIGHT)
+ status_change_end(bl, SC_KYRIE, -1);
+ }
+ if (damage <= 0) return 0;
+ }
+
+ //SC effects from caster side.
+ sc_data = status_get_sc_data(src);
+ sc_count = status_get_sc_count(src);
+ if (sc_count && *sc_count > 0) {
+ if(sc_data[SC_FOGWALL].timer != -1 && flag&(BF_LONG|BF_MAGIC)) {
+ if (flag&BF_MAGIC) {
+ if(!(skill_get_inf(skill_num)&INF_GROUND_SKILL) && rand()%100 < 75)
+ return 0;
+ } else
+ damage /=2;
+ }
+ }
+
+ if(md && md->guardian_data) {
+ if(class_ == MOBID_EMPERIUM && (flag&BF_SKILL && //Only a few skills can hit the Emperium.
+ skill_num != PA_PRESSURE && skill_num != MO_TRIPLEATTACK && skill_num != HW_GRAVITATION))
+ return 0;
+ if(src->type == BL_PC) {
+ struct guild *g=guild_search(((struct map_session_data *)src)->status.guild_id);
+ if (g && class_ == MOBID_EMPERIUM && guild_checkskill(g,GD_APPROVAL) <= 0)
+ return 0;
+ if (g && battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
+ return 0; // [MouseJstr]
+ }
+ }
+
+ if (skill_num != PA_PRESSURE) { // Gloria Domini ignores WoE damage reductions
+ if (map_flag_gvg(bl->m)) { //GvG
+ if (md && md->guardian_data)
+ damage -= damage * (md->guardian_data->castle->defense/100) * (battle_config.castle_defense_rate/100);
+
+ if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex]
+ if (flag&BF_WEAPON)
+ damage = damage * battle_config.gvg_weapon_damage_rate/100;
+ if (flag&BF_MAGIC)
+ damage = damage * battle_config.gvg_magic_damage_rate/100;
+ if (flag&BF_MISC)
+ damage = damage * battle_config.gvg_misc_damage_rate/100;
+ } else { //Normal attacks get reductions based on range.
+ if (flag & BF_SHORT)
+ damage = damage * battle_config.gvg_short_damage_rate/100;
+ if (flag & BF_LONG)
+ damage = damage * battle_config.gvg_long_damage_rate/100;
+ }
+ } else if (battle_config.pk_mode && bl->type == BL_PC) {
+ if (flag & BF_WEAPON) {
+ if (flag & BF_SHORT)
+ damage = damage * 80/100;
+ if (flag & BF_LONG)
+ damage = damage * 70/100;
+ }
+ if (flag & BF_MAGIC)
+ damage = damage * 60/100;
+ if(flag & BF_MISC)
+ damage = damage * 60/100;
+ }
+ if(damage < 1) damage = 1;
+ }
+
+ if(battle_config.skill_min_damage && damage > 0 && damage < div_)
+ {
+ if ((flag&BF_WEAPON && battle_config.skill_min_damage&1)
+ || (flag&BF_MAGIC && battle_config.skill_min_damage&2)
+ || (flag&BF_MISC && battle_config.skill_min_damage&4)
+ )
+ damage = div_;
+ }
+
+ if( md!=NULL && md->hp>0 && damage > 0 ) // ”½Œ‚‚È‚Ç‚ÌMOBƒXƒLƒ‹”»’è
+ mobskill_event(md,flag);
+
+ return damage;
+}
+
+/*==========================================
+ * HP/SP‹zŽû‚ÌŒvŽZ
+ *------------------------------------------
+ */
+int battle_calc_drain(int damage, int rate, int per, int val)
+{
+ int diff = 0;
+
+ if (damage <= 0)
+ return 0;
+
+ if (per && rand()%1000 < rate) {
+ diff = (damage * per) / 100;
+ if (diff == 0) {
+ if (per > 0)
+ diff = 1;
+ else
+ diff = -1;
+ }
+ }
+
+ if (val /*&& rand()%1000 < rate*/) { //Absolute leech/penalties have 100% chance. [Skotlex]
+ diff += val;
+ }
+ return diff;
+}
+
+/*==========================================
+ * ?C—ûƒ_ƒ??[ƒW
+ *------------------------------------------
+ */
+int battle_addmastery(struct map_session_data *sd,struct block_list *target,int dmg,int type)
+{
+ int damage,skill;
+ int race=status_get_race(target);
+ int weapon;
+ damage = dmg;
+
+ nullpo_retr(0, sd);
+
+ // ƒf?[ƒ‚ƒ“ƒxƒCƒ“(+3 ?` +30) vs •sŽ€ or ˆ«–‚ (Ž€?l‚ÍŠÜ‚ß‚È‚¢?H)
+ if((skill = pc_checkskill(sd,AL_DEMONBANE)) > 0 && (battle_check_undead(race,status_get_elem_type(target)) || race==6) )
+ damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn
+ //damage += (skill * 3);
+
+ // ƒr?[ƒXƒgƒxƒCƒ“(+4 ?` +40) vs “®•¨ or ?©’Ž
+ if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (race==2 || race==4) ) {
+ damage += (skill * 4);
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_HUNTER)
+ damage += sd->status.str;
+ }
+
+ if(type == 0)
+ weapon = sd->weapontype1;
+ else
+ weapon = sd->weapontype2;
+ switch(weapon)
+ {
+ case 0x01: // Knife
+ case 0x02: // 1HS
+ {
+ // Œ•?C—û(+4 ?` +40) •ÐŽèŒ• ’ZŒ•ŠÜ‚Þ
+ if((skill = pc_checkskill(sd,SM_SWORD)) > 0) {
+ damage += (skill * 4);
+ }
+ break;
+ }
+ case 0x03: // 2HS
+ {
+ // —¼ŽèŒ•?C—û(+4 ?` +40) —¼ŽèŒ•
+ if((skill = pc_checkskill(sd,SM_TWOHAND)) > 0) {
+ damage += (skill * 4);
+ }
+ break;
+ }
+ case 0x04: // 1HL
+ case 0x05: // 2HL
+ {
+ // ‘„?C—û(+4 ?` +40,+5 ?` +50) ‘„
+ if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) {
+ if(!pc_isriding(sd))
+ damage += (skill * 4); // ƒyƒR‚É?æ‚Á‚Ä‚È‚¢
+ else
+ damage += (skill * 5); // ƒyƒR‚É?æ‚Á‚Ä‚é
+ }
+ break;
+ }
+ case 0x06: // 1H Axe
+ case 0x07: // 2H Axe by Tato
+ {
+ if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x08: // Maces
+ {
+ if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x09: // ‚È‚µ?
+ break;
+ case 0x0a: // Staffs
+ break;
+ case 0x0b: // Bows
+ break;
+ case 0x00: // Bare Hands
+ case 0x0c: // Knuckles
+ {
+ if((skill = pc_checkskill(sd,MO_IRONHAND)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0d: // Musical Instrument
+ {
+ // ŠyŠí‚Ì—û?K(+3 ?` +30) ŠyŠí
+ if((skill = pc_checkskill(sd,BA_MUSICALLESSON)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0e: // Whips
+ {
+ // Dance Lesson Skill Effect(+3 damage for every lvl = +30) •Ú
+ if((skill = pc_checkskill(sd,DC_DANCINGLESSON)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x0f: // Book
+ {
+ // Advance Book Skill Effect(+3 damage for every lvl = +30) {
+ if((skill = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0) {
+ damage += (skill * 3);
+ }
+ break;
+ }
+ case 0x10: // Katars
+ {
+ if((skill = pc_checkskill(sd,ASC_KATAR)) > 0) {
+ //Advanced Katar Research by zanetheinsane
+ damage += damage*(10 +skill * 2)/100;
+ }
+ // ƒJƒ^?[ƒ‹?C—û(+3 ?` +30) ƒJƒ^?[ƒ‹
+ if((skill = pc_checkskill(sd,AS_KATAR)) > 0) {
+ //ƒ\ƒjƒbƒNƒuƒ??[Žž‚Í•Ê?ˆ—??i1Œ‚‚É•t‚«1/8“K‰ž)
+ damage += (skill * 3);
+ }
+ break;
+ }
+ }
+ return (damage);
+}
+/*==========================================
+ * Calculates the standard damage of a normal attack assuming it hits,
+ * it calculates nothing extra fancy, is needed for magnum break's WATK_ELEMENT bonus. [Skotlex]
+ *------------------------------------------
+ * Pass damage2 as NULL to not calc it.
+ * Flag values:
+ * &1: Critical hit
+ * &2: Arrow attack
+ * &4: Skill is Magic Crasher
+ * &8: Skip target size adjustment (Extremity Fist?)
+ */
+static void battle_calc_base_damage(struct block_list *src, struct block_list *target, int* damage1, int* damage2, int flag)
+{
+ unsigned short baseatk=0, baseatk_=0, atkmin=0, atkmax=0, atkmin_=0, atkmax_=0;
+ struct map_session_data *sd;
+ struct status_change *sc_data = status_get_sc_data(src);
+ int t_size = status_get_size(target);
+
+ if (src->type == BL_PC)
+ sd = (struct map_session_data*)src;
+ else
+ sd = NULL;
+
+ if (!sd)
+ { //Mobs/Pets
+ if ((target->type==BL_MOB && battle_config.enemy_str) ||
+ (target->type==BL_PET && battle_config.pet_str))
+ baseatk = status_get_batk(src);
+
+ if(flag&4)
+ {
+ if (!(flag&1))
+ atkmin = status_get_matk2(src);
+ atkmax = status_get_matk1(src);
+ } else {
+ if (!(flag&1))
+ atkmin = status_get_atk(src);
+ atkmax = status_get_atk2(src);
+ }
+ if (atkmin > atkmax)
+ atkmin = atkmax;
+ } else { //PCs
+ if(flag&4)
+ {
+ baseatk = status_get_matk2(src);
+ if (damage2) baseatk_ = baseatk;
+ } else {
+ baseatk = status_get_batk(src);
+ if (damage2) baseatk_ = baseatk;
+ }
+ //rodatazone says that Overrefine bonuses are part of baseatk
+ if(sd->right_weapon.overrefine>0)
+ baseatk+= rand()%sd->right_weapon.overrefine+1;
+ if (damage2 && sd->left_weapon.overrefine>0)
+ baseatk_+= rand()%sd->left_weapon.overrefine+1;
+
+ atkmax = status_get_atk(src);
+ if (damage2)
+ atkmax_ = status_get_atk(src);
+
+ if (!(flag&1) || (flag&2))
+ { //Normal attacks
+ atkmin = atkmin_ = status_get_dex(src);
+
+ if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]])
+ atkmin = atkmin*(80 + sd->inventory_data[sd->equip_index[9]]->wlv*20)/100;
+
+ if (atkmin > atkmax)
+ atkmin = atkmax;
+
+ if(damage2)
+ {
+ if (sd->equip_index[8] >= 0 && sd->inventory_data[sd->equip_index[8]])
+ atkmin_ = atkmin_*(80 + sd->inventory_data[sd->equip_index[8]]->wlv*20)/100;
+
+ if (atkmin_ > atkmax_)
+ atkmin_ = atkmax_;
+ }
+
+ if(flag&2)
+ { //Bows
+ atkmin = atkmin*atkmax/100;
+ if (atkmin > atkmax)
+ atkmax = atkmin;
+ }
+ }
+ }
+
+ if (sc_data && sc_data[SC_MAXIMIZEPOWER].timer!=-1)
+ {
+ atkmin = atkmax;
+ atkmin_ = atkmax_;
+ }
+
+ //Weapon Damage calculation
+ if (!(flag&1))
+ {
+ (*damage1) += (atkmax>atkmin? rand()%(atkmax-atkmin):0)+atkmin;
+ if (damage2)
+ (*damage2) += (atkmax_>atkmin_? rand()%(atkmax_-atkmin_) :0) +atkmin_;
+ } else {
+ (*damage1) += atkmax;
+ if (damage2)
+ (*damage2) += atkmax_;
+ }
+
+ if (sd)
+ {
+ //rodatazone says the range is 0~arrow_atk-1 for non crit
+ if (flag&2 && sd->arrow_atk)
+ (*damage1) += ((flag&1)?sd->arrow_atk:rand()%sd->arrow_atk);
+
+ //SizeFix only for players
+ if (!(
+ sd->special_state.no_sizefix ||
+ (sc_data && sc_data[SC_WEAPONPERFECTION].timer!=-1) ||
+ (pc_isriding(sd) && (sd->status.weapon==4 || sd->status.weapon==5) && t_size==1) ||
+ (flag&8)
+ ))
+ {
+ (*damage1) = (*damage1)*(sd->right_weapon.atkmods[t_size])/100;
+ if (damage2)
+ (*damage2) = (*damage2)*(sd->left_weapon.atkmods[t_size])/100;
+ }
+ }
+
+ //Finally, add baseatk
+ (*damage1) += baseatk;
+ if (damage2)
+ (*damage2) += baseatk_;
+ return;
+}
+/*==========================================
+ * battle_calc_weapon_attack (by Skotlex)
+ *------------------------------------------
+ */
+static struct Damage battle_calc_weapon_attack(
+ struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag)
+{
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ struct mob_data *md=NULL, *tmd=NULL;
+ struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets)
+ struct Damage wd;
+ short skill=0;
+ unsigned short skillratio = 100; //Skill dmg modifiers.
+
+ short i;
+ short t_mode = status_get_mode(target), t_size = status_get_size(target);
+ short t_race=0, t_ele=0, s_race=0; //Set to 0 because the compiler does not notices they are NOT gonna be used uninitialized
+ short s_ele, s_ele_;
+ short def1, def2;
+ struct status_change *sc_data = status_get_sc_data(src);
+ struct status_change *t_sc_data = status_get_sc_data(target);
+ struct {
+ unsigned hit : 1; //the attack Hit? (not a miss)
+ unsigned cri : 1; //Critical hit
+ unsigned idef : 1; //Ignore defense
+ unsigned idef2 : 1; //Ignore defense (left weapon)
+ unsigned infdef : 1; //Infinite defense (plants)
+ unsigned arrow : 1; //Attack is arrow-based
+ unsigned rh : 1; //Attack considers right hand (wd.damage)
+ unsigned lh : 1; //Attack considers left hand (wd.damage2)
+ unsigned weapon : 1; //It's a weapon attack (consider VVS, and all that)
+ unsigned cardfix : 1;
+ } flag;
+
+ memset(&wd,0,sizeof(wd));
+ memset(&flag,0,sizeof(flag));
+
+ if(src==NULL || target==NULL)
+ {
+ nullpo_info(NLP_MARK);
+ return wd;
+ }
+ //Initial flag
+ flag.rh=1;
+ flag.weapon=1;
+ flag.cardfix=1;
+ flag.infdef=(t_mode&MD_PLANT?1:0);
+
+ //Initial Values
+ wd.type=0; //Normal attack
+ wd.div_=skill_num?skill_get_num(skill_num,skill_lv):1;
+ wd.amotion=(skill_num && skill_get_inf(skill_num)&INF_GROUND_SKILL)?0:status_get_amotion(src); //Amotion should be 0 for ground skills.
+ if(skill_num == KN_AUTOCOUNTER)
+ wd.amotion >>= 1;
+ wd.dmotion=status_get_dmotion(target);
+ wd.blewcount=skill_get_blewcount(skill_num,skill_lv);
+ wd.flag=BF_SHORT|BF_WEAPON|BF_NORMAL; //Initial Flag
+ wd.dmg_lv=ATK_DEF; //This assumption simplifies the assignation later
+
+ switch (src->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)src;
+ break;
+ }
+ switch (target->type)
+ {
+ case BL_PC:
+ tsd=(struct map_session_data *)target;
+ if (pd) { //Pets can't target players
+ memset(&wd,0,sizeof(wd));
+ return wd;
+ }
+ break;
+ case BL_MOB:
+ tmd=(struct mob_data *)target;
+ break;
+ case BL_PET://Cannot target pets
+ memset(&wd,0,sizeof(wd));
+ return wd;
+ }
+
+ if(sd) {
+ sd->state.attack_type = BF_WEAPON;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ for (i = 0; i < 5 && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < 5 && sd->skillblown[i].id == skill_num)
+ wd.blewcount += sd->skillblown[i].val;
+ }
+ }
+ //Set miscellaneous data that needs be filled regardless of hit/miss
+ if(sd && sd->status.weapon == 11) {
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 1;
+ } else if (status_get_range(src) > 3)
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+
+
+ if(skill_num){
+ wd.flag=(wd.flag&~BF_SKILLMASK)|BF_SKILL;
+ switch(skill_num)
+ {
+ case AC_DOUBLE:
+ case AC_SHOWER:
+ case AC_CHARGEARROW:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case CG_ARROWVULCAN:
+ case AS_VENOMKNIFE:
+ case HT_POWER:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 1;
+ break;
+
+ case HT_PHANTASMIC:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ flag.arrow = 0;
+ break;
+
+ case MO_FINGEROFFENSIVE:
+ if(sd && battle_config.finger_offensive_type == 0)
+ wd.div_ = sd->spiritball_old;
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case CR_SHIELDBOOMERANG:
+ case PA_SHIELDCHAIN:
+ flag.weapon = 0;
+ case AS_GRIMTOOTH:
+ case KN_SPEARBOOMERANG:
+ case NPC_RANGEATTACK:
+ case LK_SPIRALPIERCE:
+ case ASC_BREAKER:
+ case AM_ACIDTERROR:
+ case ITM_TOMAHAWK: //Tomahawk is a ranged attack! [Skotlex]
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case KN_PIERCE:
+ wd.div_= t_size+1;
+ break;
+
+ case TF_DOUBLE: //For NPC used skill.
+ wd.type = 0x08;
+ break;
+
+ case KN_SPEARSTAB:
+ case KN_BOWLINGBASH:
+ case MO_BALKYOUNG:
+ case TK_TURNKICK:
+ wd.blewcount=0;
+ break;
+
+ case CR_SHIELDCHARGE:
+ flag.weapon = 0;
+ case NPC_PIERCINGATT:
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT;
+ break;
+
+ case KN_AUTOCOUNTER:
+ wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL;
+ break;
+
+
+ }
+ }
+
+ if (skill_num && battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((sd && battle_config.skillrange_by_distance&1)
+ || (md && battle_config.skillrange_by_distance&2)
+ || (pd && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(src, target, 3))
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ if(is_boss(target)) //Bosses can't be knocked-back
+ wd.blewcount = 0;
+
+ if (sd)
+ { //Arrow consumption
+ sd->state.arrow_atk = flag.arrow;
+ }
+
+ //Check for counter
+ if(!skill_num)
+ {
+ if(t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1)
+ //If it got here and you had autocounter active, then the direction/range does not matches: critical
+ flag.cri = 1;
+ } //End counter-check
+
+ if (!skill_num && (tsd || battle_config.enemy_perfect_flee))
+ { //Check for Lucky Dodge
+ short flee2 = status_get_flee2(target);
+ if (rand()%1000 < flee2)
+ {
+ wd.type=0x0b;
+ wd.dmg_lv=ATK_LUCKY;
+ return wd;
+ }
+ }
+
+ //Initialize variables that will be used afterwards
+ t_race = status_get_race(target);
+ t_ele = status_get_elem_type(target);
+
+ s_race = status_get_race(src);
+ s_ele = s_ele_ = skill_get_pl(skill_num);
+ if (!skill_num || s_ele == -1) { //Take weapon's element
+ s_ele = status_get_attack_element(src);
+ s_ele_ = status_get_attack_element2(src);
+ if (flag.arrow && sd && sd->arrow_ele)
+ s_ele = sd->arrow_ele;
+ } else if (s_ele == -2) { //Use enchantment's element
+ s_ele = s_ele_ = status_get_attack_sc_element(src);
+ }
+
+ if (sd)
+ { //Set whether damage1 or damage2 (or both) will be used
+ if(sd->weapontype1 == 0 && sd->weapontype2 > 0)
+ {
+ flag.rh=0;
+ flag.lh=1;
+ }
+ if(sd->status.weapon > 16)
+ flag.rh = flag.lh = 1;
+ }
+
+ //Check for critical
+ if(!flag.cri &&
+ (sd || battle_config.enemy_critical_rate) &&
+ (!skill_num || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING))
+ {
+ short cri = status_get_critical(src);
+ if (sd)
+ {
+ cri+= sd->critaddrace[t_race];
+ if(flag.arrow)
+ cri += sd->arrow_cri;
+ if(sd->status.weapon == 16)
+ cri <<=1;
+ }
+ //The official equation is *2, but that only applies when sd's do critical.
+ //Therefore, we use the old value 3 on cases when an sd gets attacked by a mob
+ cri -= status_get_luk(target) * (md&&tsd?3:2);
+
+ if(t_sc_data)
+ {
+ if (t_sc_data[SC_SLEEP].timer!=-1 )
+ cri <<=1;
+ if(t_sc_data[SC_JOINTBEAT].timer != -1 &&
+ t_sc_data[SC_JOINTBEAT].val2 == 6) // Always take crits with Neck broken by Joint Beat [DracoRPG]
+ flag.cri=1;
+ }
+ switch (skill_num)
+ {
+ case KN_AUTOCOUNTER:
+ if(!(battle_config.pc_auto_counter_type&1))
+ flag.cri = 1;
+ else
+ cri <<= 1;
+ break;
+ case SN_SHARPSHOOTING:
+ cri += 200;
+ break;
+ }
+ if(tsd && tsd->critical_def)
+ cri = cri*(100-tsd->critical_def)/100;
+ if (rand()%1000 < cri)
+ flag.cri= 1;
+ }
+ if (flag.cri)
+ {
+ wd.type = 0x0a;
+ flag.idef = flag.idef2 = flag.hit = 1;
+ } else { //Check for Perfect Hit
+ if(sd && sd->perfect_hit > 0 && rand()%100 < sd->perfect_hit)
+ flag.hit = 1;
+ if (sc_data && sc_data[SC_FUSION].timer != -1) {
+ flag.hit = 1; //SG_FUSION always hit [Komurka]
+ flag.idef = flag.idef2 = 1; //def ignore [Komurka]
+ }
+ if (skill_num && !flag.hit)
+ switch(skill_num)
+ {
+ case NPC_GUIDEDATTACK:
+ case RG_BACKSTAP:
+ case AM_ACIDTERROR:
+ case MO_INVESTIGATE:
+ case MO_EXTREMITYFIST:
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ case PA_SACRIFICE:
+ case TK_COUNTER:
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ flag.hit = 1;
+ break;
+ case CR_SHIELDBOOMERANG:
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ flag.hit = 1;
+ break;
+ }
+ if ((t_sc_data && !flag.hit) &&
+ (t_sc_data[SC_SLEEP].timer!=-1 ||
+ t_sc_data[SC_STAN].timer!=-1 ||
+ t_sc_data[SC_FREEZE].timer!=-1 ||
+ (t_sc_data[SC_STONE].timer!=-1 && t_sc_data[SC_STONE].val2==0))
+ )
+ flag.hit = 1;
+ }
+
+ if (!flag.hit)
+ { //Hit/Flee calculation
+ short
+ flee = status_get_flee(target),
+ hitrate=80; //Default hitrate
+
+ if(battle_config.agi_penalty_type)
+ {
+ unsigned char target_count; //256 max targets should be a sane max
+ target_count = 1+battle_counttargeted(target,src,battle_config.agi_penalty_count_lv);
+ if(target_count >= battle_config.agi_penalty_count)
+ {
+ if (battle_config.agi_penalty_type == 1)
+ flee = (flee * (100 - (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num))/100;
+ else //asume type 2: absolute reduction
+ flee -= (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num;
+ if(flee < 1) flee = 1;
+ }
+ }
+
+ hitrate+= status_get_hit(src) - flee;
+
+ if(sd && flag.arrow)
+ hitrate += sd->arrow_hit;
+ if(skill_num)
+ switch(skill_num)
+ { //Hit skill modifiers
+ case SM_BASH:
+ hitrate += 5*skill_lv;
+ break;
+ case SM_MAGNUM:
+ hitrate += 10*skill_lv;
+ break;
+ case KN_AUTOCOUNTER:
+ hitrate += 20;
+ break;
+ case KN_PIERCE:
+ hitrate += hitrate*(5*skill_lv)/100;
+ break;
+ case PA_SHIELDCHAIN:
+ hitrate += 20;
+ break;
+ case AS_SONICBLOW:
+ if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
+ hitrate += 50;
+ break;
+ }
+
+ // Weaponry Research hidden bonus
+ if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ hitrate += hitrate*(2*skill)/100;
+
+ if (hitrate > battle_config.max_hitrate)
+ hitrate = battle_config.max_hitrate;
+ else if (hitrate < battle_config.min_hitrate)
+ hitrate = battle_config.min_hitrate;
+
+ if(rand()%100 >= hitrate)
+ wd.dmg_lv = ATK_FLEE;
+ else if (t_sc_data && t_sc_data[SC_KAUPE].timer != -1 && rand()%100 < t_sc_data[SC_KAUPE].val2) {
+ if (--t_sc_data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
+ status_change_end(target, SC_KAUPE, -1);
+ wd.dmg_lv = ATK_FLEE;
+ } else
+ flag.hit =1;
+ } //End hit/miss calculation
+
+ if(tsd && tsd->special_state.no_weapon_damage)
+ return wd;
+
+ if (flag.hit && !flag.infdef) //No need to do the math for plants
+ { //Hitting attack
+
+//Assuming that 99% of the cases we will not need to check for the flag.rh... we don't.
+//ATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc
+#define ATK_RATE( a ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(a)/100; }
+#define ATK_RATE2( a , b ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(b)/100; }
+//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
+#define ATK_ADDRATE( a ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(a)/100; }
+#define ATK_ADDRATE2( a , b ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(b)/100; }
+//Adds an absolute value to damage. 100 = +100 damage
+#define ATK_ADD( a ) { wd.damage+= a; if (flag.lh) wd.damage2+= a; }
+#define ATK_ADD2( a , b ) { wd.damage+= a; if (flag.lh) wd.damage2+= b; }
+
+ def1 = status_get_def(target);
+ def2 = status_get_def2(target);
+
+ switch (skill_num)
+ { //Calc base damage according to skill
+ case PA_SACRIFICE:
+ {
+ int hp_dmg = status_get_max_hp(src)* 9/100;
+ battle_damage(src, src, hp_dmg, 0); //Damage to self is always 9%
+ clif_damage(src,src, gettick(), 0, 0, hp_dmg, 0 , 0, 0);
+
+ wd.damage = hp_dmg;
+ wd.damage2 = 0;
+
+ if (sc_data && sc_data[SC_SACRIFICE].timer != -1)
+ {
+ if (--sc_data[SC_SACRIFICE].val2 <= 0)
+ status_change_end(src, SC_SACRIFICE,-1);
+ }
+ break;
+ }
+ case LK_SPIRALPIERCE:
+ if (sd) {
+ short index = sd->equip_index[9];
+
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 4)
+ wd.damage = sd->inventory_data[index]->weight*8/100; //80% of weight
+
+ ATK_ADDRATE(50*skill_lv); //Skill modifier applies to weight only.
+ index = status_get_str(src)/10;
+ index = index*index;
+ ATK_ADD(index); //Add str bonus.
+
+ switch (t_size) { //Size-fix. Is this modified by weapon perfection?
+ case 0: //Small: 125%
+ ATK_RATE(125);
+ break;
+ //case 1: //Medium: 100%
+ case 2: //Large: 75%
+ ATK_RATE(75);
+ break;
+ }
+ ATK_RATE(wd.div_*100); //Increase overall damage by number of this
+ //FIXME: (shouldn't something like this apply to ALL weapon skills?) [Skotlex]
+ break;
+ }
+ case CR_SHIELDBOOMERANG:
+ case PA_SHIELDCHAIN:
+ if (sd) {
+ short index = sd->equip_index[8];
+
+ wd.damage = status_get_batk(src);
+ if (flag.lh) wd.damage2 = status_get_batk(src);
+
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 5)
+ ATK_ADD(sd->inventory_data[index]->weight/10);
+ break;
+ }
+ default:
+ {
+ battle_calc_base_damage(src, target, &wd.damage, flag.lh?&wd.damage2:NULL,
+ (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0));
+ //Add any bonuses that modify the base baseatk+watk (pre-skills)
+ if(sd)
+ {
+ if (sd->status.weapon < 16 && (sd->atk_rate != 100 || sd->weapon_atk_rate[sd->status.weapon] != 0))
+ ATK_RATE(sd->atk_rate + sd->weapon_atk_rate[sd->status.weapon]);
+
+ if(flag.cri && sd->crit_atk_rate)
+ ATK_ADDRATE(sd->crit_atk_rate);
+
+ if(sd->status.party_id && (skill=pc_checkskill(sd,TK_POWER)) > 0){
+ i = 0;
+ party_foreachsamemap(party_sub_count, sd, 0, &i);
+ ATK_ADDRATE(2*skill*i);
+ }
+ }
+ break;
+ } //End default case
+ } //End switch(skill_num)
+
+ //Skill damage modifiers that stack linearly
+ if(sc_data && skill_num != PA_SACRIFICE)
+ {
+ if(sc_data[SC_OVERTHRUST].timer != -1)
+ skillratio += 5*sc_data[SC_OVERTHRUST].val1;
+ if(sc_data[SC_MAXOVERTHRUST].timer != -1)
+ skillratio += 20*sc_data[SC_MAXOVERTHRUST].val1;
+ if(sc_data[SC_BERSERK].timer != -1)
+ skillratio += 100;
+ }
+ if (!skill_num)
+ {
+ // Random chance to deal multiplied damage - Consider it as part of skill-based-damage
+ if(sd &&
+ sd->random_attack_increase_add > 0 &&
+ sd->random_attack_increase_per &&
+ rand()%100 < sd->random_attack_increase_per
+ )
+ skillratio += sd->random_attack_increase_add;
+
+ ATK_RATE(skillratio);
+ } else { //Skills
+ switch( skill_num )
+ {
+ case SM_BASH:
+ skillratio += 30*skill_lv;
+ break;
+ case SM_MAGNUM:
+ skillratio += 20*skill_lv;
+ break;
+ case MC_MAMMONITE:
+ skillratio += 50*skill_lv;
+ break;
+ case HT_POWER: //FIXME: How exactly is the STR based damage supposed to be done? [Skotlex]
+ skillratio += 10*status_get_str(src);
+ break;
+ case TF_DOUBLE: //This is the mob-used Double Attack. [Skotlex]
+ skillratio += 100;
+ break;
+ case AC_DOUBLE:
+ skillratio += 80+20*skill_lv;
+ break;
+ case AC_SHOWER:
+ skillratio += 5*skill_lv-25;
+ break;
+ case AC_CHARGEARROW:
+ skillratio += 50;
+ break;
+ case KN_PIERCE:
+ skillratio += wd.div_*(100+10*skill_lv)-100;
+ break;
+ case KN_SPEARSTAB:
+ skillratio += 15*skill_lv;
+ break;
+ case KN_SPEARBOOMERANG:
+ skillratio += 50*skill_lv;
+ break;
+ case KN_BRANDISHSPEAR:
+ {
+ int ratio = 100+20*skill_lv;
+ skillratio += ratio-100;
+ if(skill_lv>3 && wflag==1) skillratio += ratio/2;
+ if(skill_lv>6 && wflag==1) skillratio += ratio/4;
+ if(skill_lv>9 && wflag==1) skillratio += ratio/8;
+ if(skill_lv>6 && wflag==2) skillratio += ratio/2;
+ if(skill_lv>9 && wflag==2) skillratio += ratio/4;
+ if(skill_lv>9 && wflag==3) skillratio += ratio/2;
+ break;
+ }
+ case KN_BOWLINGBASH:
+ skillratio+= 40*skill_lv;
+ break;
+ case KN_AUTOCOUNTER:
+ case LK_SPIRALPIERCE:
+ case NPC_CRITICALSLASH:
+ flag.idef= flag.idef2= 1;
+ break;
+ case AS_GRIMTOOTH:
+ skillratio += 20*skill_lv;
+ break;
+ case AS_POISONREACT:
+ skillratio += 30*skill_lv;
+ break;
+ case AS_SONICBLOW:
+ skillratio += 200+50*skill_lv;
+ break;
+ case TF_SPRINKLESAND:
+ skillratio += 30;
+ break;
+ case MC_CARTREVOLUTION:
+ skillratio += 50;
+ if(sd && sd->cart_max_weight > 0 && sd->cart_weight > 0)
+ skillratio += 100*sd->cart_weight/sd->cart_max_weight; // +1% every 1% weight
+ else if (!sd)
+ skillratio += 150; //Max damage for non players.
+ break;
+ case NPC_COMBOATTACK:
+ skillratio += 100*wd.div_ -100;
+ break;
+ case NPC_RANDOMATTACK:
+ skillratio += rand()%150-50;
+ break;
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_TELEKINESISATTACK:
+ skillratio += 25*skill_lv;
+ break;
+ case NPC_GUIDEDATTACK:
+ case NPC_RANGEATTACK:
+ case NPC_PIERCINGATT:
+ break;
+ case RG_BACKSTAP:
+ if(sd && sd->status.weapon == 11 && battle_config.backstab_bow_penalty)
+ skillratio += (200+40*skill_lv)/2;
+ else
+ skillratio += 200+40*skill_lv;
+ break;
+ case RG_RAID:
+ skillratio += 40*skill_lv;
+ break;
+ case RG_INTIMIDATE:
+ skillratio += 30*skill_lv;
+ break;
+ case CR_SHIELDCHARGE:
+ skillratio += 20*skill_lv;
+ break;
+ case CR_SHIELDBOOMERANG:
+ skillratio += 30*skill_lv;
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ skillratio += 100;
+ break;
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS:
+ skillratio += 35*skill_lv;
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ flag.cardfix = 0;
+ break;
+ case AM_DEMONSTRATION:
+ skillratio += 20*skill_lv;
+ flag.cardfix = 0;
+ break;
+ case AM_ACIDTERROR:
+ skillratio += 40*skill_lv;
+ flag.cardfix = 0;
+ break;
+ case MO_FINGEROFFENSIVE:
+ if(battle_config.finger_offensive_type == 0)
+ skillratio+= wd.div_ * (100 + 50*skill_lv) -100;
+ else
+ skillratio+= 50 * skill_lv;
+ break;
+ case MO_INVESTIGATE:
+ skillratio += 75*skill_lv;
+ ATK_RATE(2*(def1 + def2));
+ flag.idef= flag.idef2= 1;
+ break;
+ case MO_EXTREMITYFIST:
+ if (sd)
+ { //Overflow check. [Skotlex]
+ unsigned int ratio = skillratio + 100*(8 + ((sd->status.sp)/10));
+ //You'd need something like 6K SP to reach this max, so should be fine for most purposes.
+ if (ratio > 60000) ratio = 60000; //We leave some room here in case skillratio gets further increased.
+ skillratio = (unsigned short)ratio;
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_SP);
+ }
+ flag.idef= flag.idef2= 1;
+ break;
+ case MO_TRIPLEATTACK:
+ skillratio += 20*skill_lv;
+ break;
+ case MO_CHAINCOMBO:
+ skillratio += 50+50*skill_lv;
+ break;
+ case MO_COMBOFINISH:
+ skillratio += 140+60*skill_lv;
+ break;
+ case BA_MUSICALSTRIKE:
+ skillratio += 40*skill_lv-40;
+ break;
+ case DC_THROWARROW:
+ skillratio += 50*skill_lv;
+ break;
+ case CH_TIGERFIST:
+ skillratio += 100*skill_lv-60;
+ break;
+ case CH_CHAINCRUSH:
+ skillratio += 300+100*skill_lv;
+ break;
+ case CH_PALMSTRIKE:
+ skillratio += 100+100*skill_lv;
+ break;
+ case LK_HEADCRUSH:
+ skillratio += 40*skill_lv;
+ break;
+ case LK_JOINTBEAT:
+ skillratio += 10*skill_lv-50;
+ break;
+ case ASC_METEORASSAULT:
+ skillratio += 40*skill_lv-60;
+ flag.cardfix = 0;
+ break;
+ case SN_SHARPSHOOTING:
+ skillratio += 50*skill_lv;
+ break;
+ case CG_ARROWVULCAN:
+ skillratio += 100+100*skill_lv;
+ break;
+ case AS_SPLASHER:
+ skillratio += 100+20*skill_lv;
+ if (sd)
+ skillratio += 20*pc_checkskill(sd,AS_POISONREACT);
+ if(wflag>1) //FIXME: Splash damage... is this the correct method? [Skotlex]
+ skillratio /= wflag;
+ flag.cardfix = 0;
+ break;
+ case ASC_BREAKER:
+ skillratio += 100*skill_lv-100;
+ flag.cardfix = 0;
+ break;
+ case PA_SACRIFICE:
+ //40% less effective on siege maps. [Skotlex]
+ skillratio += 10*skill_lv-10;
+ flag.idef = flag.idef2 = 1;
+ break;
+ case PA_SHIELDCHAIN:
+ skillratio += wd.div_*(100+30*skill_lv)-100;
+ break;
+ case WS_CARTTERMINATION:
+ if(sd && sd->cart_weight > 0)
+ skillratio += sd->cart_weight / (10 * (16 - skill_lv)) - 100;
+ else if (!sd)
+ skillratio += battle_config.max_cart_weight / (10 * (16 - skill_lv));
+ flag.cardfix = 0;
+ break;
+ case TK_DOWNKICK:
+ skillratio += 60 + 20*skill_lv;
+ break;
+ case TK_STORMKICK:
+ skillratio += 60 + 20*skill_lv;
+ break;
+ case TK_TURNKICK:
+ skillratio += 90 + 30*skill_lv;
+ break;
+ case TK_COUNTER:
+ skillratio += 90 + 30*skill_lv;
+ break;
+ case TK_JUMPKICK:
+ skillratio += -70 + 10*skill_lv;
+ if (sc_data && sc_data[SC_COMBO].timer != -1 && sc_data[SC_COMBO].val1 == skill_num)
+ skillratio += 10*status_get_lv(src)/3;
+ break;
+ case KN_CHARGEATK:
+ skillratio += wflag*15; //FIXME: How much is the actual bonus? [Skotlex]
+ break;
+ case HT_PHANTASMIC:
+ skillratio += 50;
+ break;
+ case MO_BALKYOUNG:
+ skillratio += 200;
+ break;
+ }
+
+ ATK_RATE(skillratio);
+
+ //Constant/misc additions from skills
+ switch (skill_num) {
+ case MO_EXTREMITYFIST:
+ ATK_ADD(250 + 150*skill_lv);
+ break;
+ case TK_DOWNKICK:
+ case TK_STORMKICK:
+ case TK_TURNKICK:
+ case TK_COUNTER:
+ case TK_JUMPKICK:
+ //TK_RUN kick damage bonus.
+ if(sd && sd->weapontype1 == 0 && sd->weapontype2 == 0)
+ ATK_ADD(10*pc_checkskill(sd, TK_RUN));
+ break;
+ }
+ }
+ //Here comes a second pass for skills that stack to the previously defined % damage. [Skotlex]
+ skillratio = 100;
+ //Skill damage modifiers that affect linearly stacked damage.
+ if (sc_data && skill_num != PA_SACRIFICE) {
+ if(sc_data[SC_TRUESIGHT].timer != -1)
+ skillratio += 2*sc_data[SC_TRUESIGHT].val1;
+ // It is still not quite decided whether it works on bosses or not...
+ if(sc_data[SC_EDP].timer != -1 /*&& !(t_mode&MD_BOSS)*/ && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT)
+ skillratio += 50 +50*sc_data[SC_EDP].val1;
+ }
+ switch (skill_num) {
+ case AS_SONICBLOW:
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_ASSASIN)
+ skillratio += (map_flag_gvg(src->m))?25:100; //+25% dmg on woe/+100% dmg on nonwoe
+ if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
+ skillratio += 10;
+ break;
+ }
+ if (sd && sd->skillatk[0].id != 0)
+ {
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ //May seem wrong as it also applies on top of other modifiers, but adding, say, 10%
+ //to 800% dmg -> 810% would make the bonus a little lame. [Skotlex]
+ skillratio += sd->skillatk[i].val;
+ }
+ if (skillratio != 100)
+ ATK_RATE(skillratio);
+ if(sd)
+ {
+ if (skill_num != PA_SACRIFICE && skill_num != MO_INVESTIGATE && !flag.cri)
+ { //Elemental/Racial adjustments
+ char raceele_flag=0, raceele_flag_=0;
+ if(sd->right_weapon.def_ratio_atk_ele & (1<<t_ele) ||
+ sd->right_weapon.def_ratio_atk_race & (1<<t_race) ||
+ sd->right_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11)
+ )
+ raceele_flag = flag.idef = 1;
+
+ if(sd->left_weapon.def_ratio_atk_ele & (1<<t_ele) ||
+ sd->left_weapon.def_ratio_atk_race & (1<<t_race) ||
+ sd->left_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11)
+ ) { //Pass effect onto right hand if configured so. [Skotlex]
+ if (battle_config.left_cardfix_to_right && flag.rh)
+ raceele_flag = flag.idef = 1;
+ else
+ raceele_flag_ = flag.idef2 = 1;
+ }
+
+ if (raceele_flag || raceele_flag_)
+ ATK_RATE2(raceele_flag?(def1 + def2):100, raceele_flag_?(def1 + def2):100);
+ }
+
+ //Ignore Defense?
+ if (!flag.idef && (
+ (tmd && sd->right_weapon.ignore_def_mob & (is_boss(target)?2:1)) ||
+ sd->right_weapon.ignore_def_ele & (1<<t_ele) ||
+ sd->right_weapon.ignore_def_race & (1<<t_race) ||
+ sd->right_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11)
+ ))
+ flag.idef = 1;
+
+ if (!flag.idef2 && (
+ (tmd && sd->left_weapon.ignore_def_mob & (is_boss(target)?2:1)) ||
+ sd->left_weapon.ignore_def_ele & (1<<t_ele) ||
+ sd->left_weapon.ignore_def_race & (1<<t_race) ||
+ sd->left_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11)
+ )) {
+ if(battle_config.left_cardfix_to_right && flag.rh) //Move effect to right hand. [Skotlex]
+ flag.idef = 1;
+ else
+ flag.idef2 = 1;
+ }
+ }
+
+ if (!flag.idef || !flag.idef2)
+ { //Defense reduction
+ short vit_def;
+ if(battle_config.vit_penalty_type)
+ {
+ unsigned char target_count; //256 max targets should be a sane max
+ target_count = 1 + battle_counttargeted(target,src,battle_config.vit_penalty_count_lv);
+ if(target_count >= battle_config.vit_penalty_count) {
+ if(battle_config.vit_penalty_type == 1) {
+ def1 = (def1 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100;
+ def2 = (def2 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100;
+ } else { //Assume type 2
+ def1 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num;
+ def2 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num;
+ }
+ }
+ if(def1 < 0 || skill_num == AM_ACIDTERROR) def1 = 0; //Acid Terror ignores only armor defense. [Skotlex]
+ if(def2 < 1) def2 = 1;
+ }
+ //Vitality reduction from rodatazone: http://rodatazone.simgaming.net/mechanics/substats.php#def
+ if (tsd) //Sd vit-eq
+ { //[VIT*0.5] + rnd([VIT*0.3], max([VIT*0.3],[VIT^2/150]-1))
+ vit_def = def2*(def2-15)/150;
+ vit_def = def2/2 + (vit_def>0?rand()%vit_def:0);
+
+ if((battle_check_undead(s_race,status_get_elem_type(src)) || s_race==6) &&
+ (skill=pc_checkskill(tsd,AL_DP)) >0)
+ vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn
+ } else { //Mob-Pet vit-eq
+ //VIT + rnd(0,[VIT/20]^2-1)
+ vit_def = (def2/20)*(def2/20);
+ vit_def = def2 + (vit_def>0?rand()%vit_def:0);
+ }
+
+ if ((sd && battle_config.player_defense_type)
+ || (md && battle_config.monster_defense_type)
+ || (pd && battle_config.pet_defense_type)
+ )
+ vit_def += def1*battle_config.player_defense_type;
+ else
+ ATK_RATE2(flag.idef?100:100-def1, flag.idef2?100:100-def1);
+ ATK_ADD2(flag.idef?0:-vit_def, flag.idef2?0:-vit_def);
+ }
+
+ //Post skill/vit reduction damage increases
+ if (sc_data && skill_num != LK_SPIRALPIERCE)
+ { //SC skill damages
+ if(sc_data[SC_AURABLADE].timer!=-1)
+ ATK_ADD(20*sc_data[SC_AURABLADE].val1);
+ }
+
+ //Refine bonus
+ if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) {
+ if (skill_num == MO_FINGEROFFENSIVE) //Counts refine bonus multiple times
+ {
+ ATK_ADD2(wd.div_*status_get_atk2(src), wd.div_*status_get_atk_2(src));
+ } else {
+ ATK_ADD2(status_get_atk2(src), status_get_atk_2(src));
+ }
+ }
+
+ //Set to min of 1
+ if (flag.rh && wd.damage < 1) wd.damage = 1;
+ if (flag.lh && wd.damage2 < 1) wd.damage2 = 1;
+
+ if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST
+ && skill_num != CR_GRANDCROSS)
+ { //Add mastery damage
+ wd.damage = battle_addmastery(sd,target,wd.damage,0);
+ if (flag.lh) wd.damage2 = battle_addmastery(sd,target,wd.damage2,1);
+
+ if (pc_checkskill(sd,SG_SUN_ANGER) || pc_checkskill(sd,SG_MOON_ANGER) || pc_checkskill(sd,SG_STAR_ANGER))
+ { //SG Anger bonus - ATK_ADDRATE [Komurka]
+ static int type[] = { SG_SUN_ANGER, SG_MOON_ANGER, SG_STAR_ANGER };
+ short t_class = status_get_class(target);
+ for (i = 0; i < 2; i++)
+ {
+ if (t_class == sd->hate_mob[i] && (skill = pc_checkskill(sd,type[i])))
+ {
+ skillratio = (sd->status.base_level + (i==2?status_get_str(src):0) + status_get_dex(src)+ status_get_luk(src))/(skill<4?12-3*skill:1);
+ ATK_ADDRATE(skillratio);
+ break;
+ }
+ }
+ }
+ }
+ } //Here ends flag.hit section, the rest of the function applies to both hitting and missing attacks
+
+ if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS)
+ return wd; //Enough, rest is not needed.
+
+ if(sd && (skill=pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ ATK_ADD(skill*2);
+
+ if(skill_num==TF_POISON)
+ ATK_ADD(15*skill_lv);
+
+ if ((sd && (skill_num || !battle_config.pc_attack_attr_none)) ||
+ (md && (skill_num || !battle_config.mob_attack_attr_none)) ||
+ (pd && (skill_num || !battle_config.pet_attack_attr_none)))
+ { //Elemental attribute fix
+ short t_element = status_get_element(target);
+ if (!(!sd && tsd && battle_config.mob_ghostring_fix && t_ele==8))
+ {
+ if (wd.damage > 0)
+ {
+ wd.damage=battle_attr_fix(src,target,wd.damage,s_ele,t_element);
+ if(skill_num==MC_CARTREVOLUTION) //Cart Revolution applies the element fix once more with neutral element
+ wd.damage = battle_attr_fix(src,target,wd.damage,0,t_element);
+ }
+ if (flag.lh && wd.damage2 > 0)
+ wd.damage2 = battle_attr_fix(src,target,wd.damage2,s_ele_,t_element);
+ }
+ if(sc_data && sc_data[SC_WATK_ELEMENT].timer != -1)
+ { //Descriptions indicate this means adding a percent of a normal attack in another element. [Skotlex]
+ int damage=0;
+ battle_calc_base_damage(src, target, &damage, NULL, (flag.arrow?2:0));
+ damage = damage*sc_data[SC_WATK_ELEMENT].val2/100;
+ damage = battle_attr_fix(src,target,damage,sc_data[SC_WATK_ELEMENT].val1,t_element);
+ ATK_ADD(damage);
+ }
+ }
+
+
+ if ((!flag.rh || wd.damage == 0) && (!flag.lh || wd.damage2 == 0))
+ flag.cardfix = 0; //When the attack does no damage, avoid doing %bonuses
+
+ if (sd)
+ {
+ if (skill_num != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus.
+ ATK_ADD2(wd.div_*sd->right_weapon.star, wd.div_*sd->left_weapon.star);
+ if (skill_num==MO_FINGEROFFENSIVE) { //The finger offensive spheres on moment of attack do count. [Skotlex]
+ ATK_ADD(wd.div_*sd->spiritball_old*3);
+ } else {
+ ATK_ADD(wd.div_*sd->spiritball*3);
+ }
+
+ //Card Fix, sd side
+ if (flag.cardfix)
+ {
+ short cardfix = 1000, cardfix_ = 1000;
+ short t_class = status_get_class(target);
+ short t_race2 = status_get_race2(target);
+ if(sd->state.arrow_atk)
+ {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->arrow_addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->arrow_addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->arrow_addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->arrow_addrace[is_boss(target)?10:11])/100;
+ } else { //Melee attack
+ if(!battle_config.left_cardfix_to_right)
+ {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11])/100;
+
+ if (flag.lh)
+ {
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace[t_race])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addele[t_ele])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addsize[t_size])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace2[t_race2])/100;
+ cardfix_=cardfix_*(100+sd->left_weapon.addrace[is_boss(target)?10:11])/100;
+ }
+ } else {
+ cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->left_weapon.addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->left_weapon.addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->left_weapon.addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2]+sd->left_weapon.addrace2[t_race2])/100;
+ cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->left_weapon.addrace[is_boss(target)?10:11])/100;
+ }
+ }
+
+ for(i=0;i<sd->right_weapon.add_damage_class_count;i++) {
+ if(sd->right_weapon.add_damage_classid[i] == t_class) {
+ cardfix=cardfix*(100+sd->right_weapon.add_damage_classrate[i])/100;
+ break;
+ }
+ }
+
+ if (flag.lh)
+ {
+ for(i=0;i<sd->left_weapon.add_damage_class_count;i++) {
+ if(sd->left_weapon.add_damage_classid[i] == t_class) {
+ cardfix_=cardfix_*(100+sd->left_weapon.add_damage_classrate[i])/100;
+ break;
+ }
+ }
+ }
+
+ if(wd.flag&BF_LONG)
+ cardfix=cardfix*(100+sd->long_attack_atk_rate)/100;
+
+ if (cardfix != 1000 || cardfix_ != 1000)
+ ATK_RATE2(cardfix/10, cardfix_/10); //What happens if you use right-to-left and there's no right weapon, only left?
+ }
+
+ if (skill_num == CR_SHIELDBOOMERANG || skill_num == PA_SHIELDCHAIN) { //Refine bonus applies after cards and elements.
+ short index= sd->equip_index[8];
+ if (index >= 0 &&
+ sd->inventory_data[index] &&
+ sd->inventory_data[index]->type == 5)
+ ATK_ADD(10*sd->status.inventory[index].refine);
+ }
+ } //if (sd)
+
+ //Card Fix, tsd side - Cards always apply on the target. [Skotlex]
+ if (tsd) {
+ short s_size,s_race2,s_class;
+ short cardfix=1000;
+
+ s_size = status_get_size(src);
+ s_race2 = status_get_race2(src);
+ s_class = status_get_class(src);
+
+ cardfix=cardfix*(100-tsd->subele[s_ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[s_size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[s_race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[s_race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100;
+
+ for(i=0;i<tsd->add_dmg_count;i++) {
+ if(tsd->add_dmg[i].class_ == s_class) {
+ cardfix=cardfix*(100+tsd->add_dmg[i].rate)/100;
+ break;
+ }
+ }
+
+ if(wd.flag&BF_SHORT)
+ cardfix=cardfix*(100-tsd->near_attack_def_rate)/100;
+ else // BF_LONG (there's no other choice)
+ cardfix=cardfix*(100-tsd->long_attack_def_rate)/100;
+
+ if (cardfix != 1000)
+ ATK_RATE(cardfix/10);
+ }
+
+ if(flag.infdef)
+ { //Plants receive 1 damage when hit
+ if (flag.rh && (flag.hit || wd.damage>0))
+ wd.damage = 1;
+ if (flag.lh && (flag.hit || wd.damage2>0))
+ wd.damage2 = 1;
+ if (!(battle_config.skill_min_damage&1)) //Do not return if you are supposed to deal greater damage to plants than 1. [Skotlex]
+ return wd;
+ }
+
+ if(sd && !skill_num && !flag.cri)
+ { //Check for double attack.
+ if(( (skill_lv = 5*pc_checkskill(sd,TF_DOUBLE)) > 0 && sd->weapontype1 == 0x01) ||
+ sd->double_rate > 0) //Success chance is not added, the higher one is used? [Skotlex]
+ if (rand()%100 < (skill_lv>sd->double_rate?skill_lv:sd->double_rate))
+ {
+ wd.damage *=2;
+ wd.div_=skill_get_num(TF_DOUBLE,skill_lv?skill_lv:1);
+ wd.type = 0x08;
+ }
+ }
+
+ if(!flag.rh || wd.damage<1)
+ wd.damage=0;
+
+ if(!flag.lh || wd.damage2<1)
+ wd.damage2=0;
+
+ if (sd)
+ {
+ if (!flag.rh && flag.lh)
+ { //Move lh damage to the rh
+ wd.damage = wd.damage2;
+ wd.damage2 = 0;
+ flag.rh=1;
+ flag.lh=0;
+ } else if(sd->status.weapon > 16)
+ { //Dual-wield
+ if (wd.damage > 0)
+ {
+ skill = pc_checkskill(sd,AS_RIGHT);
+ wd.damage = wd.damage * (50 + (skill * 10))/100;
+ if(wd.damage < 1) wd.damage = 1;
+ }
+ if (wd.damage2 > 0)
+ {
+ skill = pc_checkskill(sd,AS_LEFT);
+ wd.damage2 = wd.damage2 * (30 + (skill * 10))/100;
+ if(wd.damage2 < 1) wd.damage2 = 1;
+ }
+ } else if(sd->status.weapon == 16)
+ { //Katars
+ skill = pc_checkskill(sd,TF_DOUBLE);
+ wd.damage2 = wd.damage * (1 + (skill * 2))/100;
+
+ if(wd.damage > 0 && wd.damage2 < 1) wd.damage2 = 1;
+ flag.lh = 1;
+ }
+ }
+
+ if(wd.damage > 0 || wd.damage2 > 0)
+ {
+ if(wd.damage2<1)
+ wd.damage=battle_calc_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag);
+ else if(wd.damage<1)
+ wd.damage2=battle_calc_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag);
+ else
+ {
+ int d1=wd.damage+wd.damage2,d2=wd.damage2;
+ wd.damage=battle_calc_damage(src,target,d1,wd.div_,skill_num,skill_lv,wd.flag);
+ wd.damage2=(d2*100/d1)*wd.damage/100;
+ if(wd.damage > 1 && wd.damage2 < 1) wd.damage2=1;
+ wd.damage-=wd.damage2;
+ }
+ }
+
+ if(sd && sd->classchange && tmd && !(t_mode&MD_BOSS) && !tmd->guardian_data && (tmd->class_ < 1324 || tmd->class_ > 1363) && (rand()%10000 < sd->classchange))
+ { //Classchange:
+ struct mob_db *mob;
+ int k, class_;
+ i = 0;
+ do {
+ do {
+ class_ = rand() % MAX_MOB_DB;
+ } while (!mobdb_checkid(class_));
+
+ k = rand() % 1000000;
+ mob = mob_db(class_);
+ } while ((mob->mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= k) && (i++) < 2000);
+ if (i< 2000)
+ mob_class_change(((struct mob_data *)target),class_);
+ }
+
+ if (sd && (battle_config.equip_self_break_rate || battle_config.equip_skill_break_rate) &&
+ (wd.damage > 0 || wd.damage2 > 0)) {
+ if (battle_config.equip_self_break_rate) { // Self weapon breaking
+ int breakrate = battle_config.equip_natural_break_rate;
+ if (sd->sc_count) {
+ if(sd->sc_data[SC_OVERTHRUST].timer!=-1)
+ breakrate += 10;
+ if(sd->sc_data[SC_MAXOVERTHRUST].timer!=-1)
+ breakrate += 10;
+ }
+ if(rand() % 10000 < breakrate * battle_config.equip_self_break_rate / 100 || breakrate >= 10000)
+ pc_breakweapon(sd);
+ }
+ if (battle_config.equip_skill_break_rate) { // Target equipment breaking
+ int breakrate[2] = {0,0}; // weapon = 0, armor = 1
+ int breaktime = 5000;
+
+ breakrate[0] += sd->break_weapon_rate; // Break rate from equipment
+ breakrate[1] += sd->break_armor_rate;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MELTDOWN].timer!=-1) {
+ breakrate[0] += 100*sd->sc_data[SC_MELTDOWN].val1;
+ breakrate[1] += 70*sd->sc_data[SC_MELTDOWN].val1;
+ breaktime = skill_get_time2(WS_MELTDOWN,1);
+ }
+ }
+ if(rand() % 10000 < breakrate[0] * battle_config.equip_skill_break_rate / 100 || breakrate[0] >= 10000) {
+ if (target->type == BL_PC)
+ pc_breakweapon((struct map_session_data *)target);
+ else
+ status_change_start(target,SC_STRIPWEAPON,1,75,0,0,breaktime,0);
+ }
+ if(rand() % 10000 < breakrate[1] * battle_config.equip_skill_break_rate/100 || breakrate[1] >= 10000) {
+ if (target->type == BL_PC) {
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ pc_breakarmor(tsd);
+ } else
+ status_change_start(target,SC_STRIPSHIELD,1,75,0,0,breaktime,0);
+ }
+ }
+ }
+ return wd;
+}
+
+/*==========================================
+ * battle_calc_magic_attack [DracoRPG]
+ *------------------------------------------
+ */
+struct Damage battle_calc_magic_attack(
+ struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag)
+ {
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ struct mob_data *md=NULL, *tmd=NULL;
+ struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets)
+ struct Damage ad;
+ unsigned short skillratio = 100; //Skill dmg modifiers.
+
+ short i;
+ short t_mode = status_get_mode(target);
+ short t_race, t_size, t_ele, s_race, s_size, s_ele;
+ struct {
+ unsigned imdef : 1;
+ unsigned infdef : 1;
+ unsigned elefix : 1;
+ unsigned cardfix : 1;
+ } flag;
+
+ memset(&ad,0,sizeof(ad));
+ memset(&flag,0,sizeof(flag));
+
+ if(src==NULL || target==NULL)
+ {
+ nullpo_info(NLP_MARK);
+ return ad;
+ }
+ //Initial flag
+ flag.elefix=1;
+ flag.cardfix=1;
+
+ //Initial Values
+ ad.damage = 1;
+ ad.div_=skill_get_num(skill_num,skill_lv);
+ ad.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(src); //Amotion should be 0 for ground skills.
+ ad.dmotion=status_get_dmotion(target);
+ ad.blewcount = skill_get_blewcount(skill_num,skill_lv);
+ ad.flag=BF_MAGIC|BF_LONG|BF_SKILL;
+ ad.dmg_lv=ATK_DEF;
+
+ switch (src->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)src;
+ break;
+ }
+ switch (target->type)
+ {
+ case BL_PC:
+ tsd=(struct map_session_data *)target;
+ if (pd) { //Pets can't target players
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ }
+ break;
+ case BL_MOB:
+ tmd=(struct mob_data *)target;
+ break;
+ case BL_PET://Cannot target pets
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ }
+
+ //Initialize variables that will be used afterwards
+ t_race = status_get_race(target);
+ t_size = status_get_size(target);
+ t_ele = status_get_elem_type(target);
+
+ s_race = status_get_race(src);
+ s_size = status_get_size(src);
+ s_ele = skill_get_pl(skill_num);
+
+ if (s_ele == -1) // pl=-1 : the skill takes the weapon's element
+ s_ele = status_get_attack_element(src);
+ else if (s_ele == -2) //Use status element
+ s_ele = status_get_attack_sc_element(src);
+
+ if (skill_num == ASC_BREAKER) // Soul Breaker's magical part is neutral, although pl=-1 for the physical part to take weapon element
+ s_ele = 0;
+
+ //Set miscellaneous data that needs be filled
+ if(sd) {
+ sd->state.attack_type = BF_MAGIC;
+ sd->state.arrow_atk = 0;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num)
+ ad.blewcount += sd->skillblown[i].val;
+ }
+ }
+
+ if (battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((sd && battle_config.skillrange_by_distance&1)
+ || (md && battle_config.skillrange_by_distance&2)
+ || (pd && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(src, target, 3))
+ ad.flag=(ad.flag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ ad.flag=(ad.flag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ flag.infdef=(t_mode&MD_PLANT?1:0);
+
+ switch(skill_num)
+ {
+ case MG_FIREWALL:
+ if(mflag) { //mflag has a value when it was checked it works against an undead in skill.c [Skotlex]
+ ad.div_ = mflag; //mflag contains the number of hits against undead.
+ ad.blewcount = 0; //No knockback
+ ad.dmotion = 0; //No flinch animation.
+ } else
+ ad.blewcount |= 0x10000;
+ break;
+ case PR_SANCTUARY:
+ ad.blewcount|=0x10000;
+ case AL_HEAL:
+ case WZ_FIREPILLAR:
+ flag.imdef = 1;
+ break;
+ case PR_ASPERSIO:
+ case PF_SOULBURN:
+ case HW_GRAVITATION:
+ case ASC_BREAKER:
+ flag.imdef = 1;
+ flag.elefix = 0;
+ flag.cardfix = 0;
+ break;
+ case PR_TURNUNDEAD:
+ flag.imdef = 1;
+ flag.cardfix = 0;
+ break;
+ case NPC_GRANDDARKNESS:
+ case CR_GRANDCROSS:
+ flag.cardfix = 0;
+ break;
+ }
+
+ if(is_boss(target)) //Bosses can't be knocked-back
+ ad.blewcount = 0;
+
+ if (!flag.infdef) //No need to do the math for plants
+ {
+
+//MATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc
+#define MATK_RATE( a ) { ad.damage= ad.damage*(a)/100; }
+//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage
+#define MATK_ADDRATE( a ) { ad.damage+= ad.damage*(a)/100; }
+//Adds an absolute value to damage. 100 = +100 damage
+#define MATK_ADD( a ) { ad.damage+= a; }
+
+ switch (skill_num)
+ { //Calc base damage according to skill
+ case AL_HEAL:
+ case PR_BENEDICTIO:
+ ad.damage = skill_calc_heal(src,skill_lv)/2;
+ if (sd)
+ ad.damage += ad.damage * pc_checkskill(sd, HP_MEDITATIO) * 2 / 100;
+ break;
+ case PR_ASPERSIO:
+ ad.damage = 40;
+ break;
+ case PR_SANCTUARY:
+ ad.damage = (skill_lv>6)?388:skill_lv*50;
+ break;
+ case ALL_RESURRECTION:
+ case PR_TURNUNDEAD:
+ if(!tsd && battle_check_undead(t_race,t_ele)){
+ int hp, mhp, thres;
+ hp = status_get_hp(target);
+ mhp = status_get_max_hp(target);
+ thres = (skill_lv * 20) + status_get_luk(src) + status_get_int(src) + status_get_lv(src) + ((200 - hp * 200 / mhp));
+ if(thres > 700) thres = 700;
+ if(rand()%1000 < thres && !(t_mode&MD_BOSS))
+ ad.damage = hp;
+ else
+ ad.damage = status_get_lv(src) + status_get_int(src) + skill_lv * 10;
+ }
+ break;
+ case PF_SOULBURN:
+ if (!tsd) {
+ memset(&ad,0,sizeof(ad));
+ return ad;
+ } else
+ ad.damage = tsd->status.sp * 2;
+ break;
+ case ASC_BREAKER:
+ ad.damage = rand()%500 + 500 + skill_lv * status_get_int(src) * 5;
+ break;
+ case HW_GRAVITATION:
+ ad.damage = 200+200*skill_lv;
+ break;
+ default:
+ {
+ unsigned short matkmin,matkmax;
+
+ matkmin = status_get_matk2(src);
+ matkmax = status_get_matk1(src);
+
+ MATK_ADD(matkmin+(matkmax>matkmin?rand()%(matkmax-matkmin+1):0));
+
+ if(skill_num == MG_NAPALMBEAT || skill_num == HW_NAPALMVULCAN){ // Divide MATK in case of multiple targets skill
+ if(mflag>0)
+ ad.damage/= mflag;
+ else if(battle_config.error_log)
+ ShowError("0 enemies targeted by Napalm Beat/Vulcan, divide per 0 avoided!\n");
+ }
+
+ switch(skill_num){
+ case MG_NAPALMBEAT:
+ skillratio += skill_lv*10-30;
+ break;
+ case MG_SOULSTRIKE:
+ if (battle_check_undead(t_race,t_ele))
+ skillratio += 5*skill_lv;
+ break;
+ case MG_FIREBALL:
+ if(mflag>2)
+ ad.damage = 0;
+ else {
+ int drate[]={100,90,70};
+ MATK_RATE(drate[mflag]);
+ skillratio += 70+10*skill_lv;
+ }
+ break;
+ case MG_FIREWALL:
+ skillratio -= 50;
+ break;
+ case MG_THUNDERSTORM:
+ skillratio -= 20;
+ break;
+ case MG_FROSTDIVER:
+ skillratio += 10*skill_lv;
+ break;
+ case AL_HOLYLIGHT:
+ skillratio += 25;
+ if (sd && sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_PRIEST)
+ skillratio *= 5; //Does 5x damage include bonuses from other skills?
+ break;
+ case AL_RUWACH:
+ skillratio += 45;
+ break;
+ case WZ_FROSTNOVA:
+ skillratio += (100+skill_lv*10)*2/3-100;
+ break;
+ case WZ_FIREPILLAR:
+ skillratio -= 80;
+ break;
+ case WZ_SIGHTRASHER:
+ skillratio += 20*skill_lv;
+ break;
+ case WZ_VERMILION:
+ skillratio += 20*skill_lv-20;
+ break;
+ case WZ_WATERBALL:
+ skillratio += 30*skill_lv;
+ break;
+ case WZ_STORMGUST:
+ skillratio += 40*skill_lv;
+ break;
+ case HW_NAPALMVULCAN:
+ skillratio += 10*skill_lv-30;
+ break;
+ case SL_STIN:
+ skillratio += (t_size?-99:10*skill_lv); //target size must be small (0) for full damage.
+ break;
+ case SL_STUN:
+ skillratio += (t_size!=2?5*skill_lv:-99); //Full damage is dealt on small/medium targets
+ break;
+ case SL_SMA:
+ skillratio += -60 + status_get_lv(src); //Base damage is 40% + lv%
+ break;
+ }
+
+ if (sd && sd->skillatk[0].id != 0)
+ {
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++)
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ //If we apply skillatk[] as ATK_RATE, it will also affect other skills,
+ //unfortunately this way ignores a skill's constant modifiers...
+ skillratio += sd->skillatk[i].val;
+ }
+
+ MATK_RATE(skillratio);
+
+ //Constant/misc additions from skills
+ if (skill_num == WZ_FIREPILLAR)
+ MATK_ADD(50);
+ }
+ }
+
+ if(sd) {
+ //Ignore Defense?
+ if (!flag.imdef && (
+ sd->ignore_mdef_ele & (1<<t_ele) ||
+ sd->ignore_mdef_race & (1<<t_race) ||
+ sd->ignore_mdef_race & (is_boss(target)?1<<10:1<<11)
+ ))
+ flag.imdef = 1;
+ }
+
+ if(!flag.imdef){
+ if(battle_config.magic_defense_type)
+ ad.damage = ad.damage - (status_get_mdef(target)*battle_config.magic_defense_type) - status_get_mdef2(target);
+ else
+ ad.damage = ad.damage * (100-status_get_mdef(target))/100 - status_get_mdef2(target);
+ }
+
+ if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS)
+ { //Apply the physical part of the skill's damage. [Skotlex]
+ struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag);
+ ad.damage = (wd.damage + ad.damage) * (100 + 40*skill_lv)/100;
+ if(src==target)
+ {
+ if (src->type == BL_PC)
+ ad.damage = ad.damage/2;
+ else
+ ad.damage = 0;
+ }
+ }
+
+ if(ad.damage<1)
+ ad.damage=1;
+
+ if (flag.elefix)
+ ad.damage=battle_attr_fix(src, target, ad.damage, s_ele, status_get_element(target));
+
+ if (sd && flag.cardfix) {
+ short t_class = status_get_class(target);
+ short cardfix=100;
+
+ cardfix=cardfix*(100+sd->magic_addrace[t_race])/100;
+ cardfix=cardfix*(100+sd->magic_addele[t_ele])/100;
+ cardfix=cardfix*(100+sd->magic_addsize[t_size])/100;
+ cardfix=cardfix*(100+sd->magic_addrace[is_boss(target)?10:11])/100;
+ for(i=0;i<sd->add_mdmg_count;i++) {
+ if(sd->add_mdmg[i].class_ == t_class) {
+ cardfix=cardfix*(100+sd->add_mdmg[i].rate)/100;
+ continue;
+ }
+ }
+
+ MATK_RATE(cardfix);
+ }
+
+ if (tsd && skill_num != HW_GRAVITATION) { //Card fixes always apply on the target side. [Skotlex]
+ short s_race2=status_get_race2(src);
+ short s_class= status_get_class(src);
+ short cardfix=100;
+
+ cardfix=cardfix*(100-tsd->subele[s_ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[s_size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[s_race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[s_race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100;
+ for(i=0;i<tsd->add_mdef_count;i++) {
+ if(tsd->add_mdef[i].class_ == s_class) {
+ cardfix=cardfix*(100-tsd->add_mdef[i].rate)/100;
+ continue;
+ }
+ }
+ cardfix=cardfix*(100-tsd->magic_def_rate)/100;
+
+ MATK_RATE(cardfix);
+ }
+ }
+
+ if(!flag.infdef && ad.div_>1 && skill_num != WZ_VERMILION)
+ ad.damage *= ad.div_;
+
+ if (tsd && status_isimmune(target)) {
+ if (sd && battle_config.gtb_pvp_only) { // [MouseJstr]
+ MATK_RATE(100 - battle_config.gtb_pvp_only);
+ } else ad.damage = 0;
+ }
+
+ ad.damage=battle_calc_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
+ if (ad.damage == 0) //Magic attack blocked? Consider it a miss so it doesn't invokes additional effects. [Skotlex]
+ ad.dmg_lv = ATK_FLEE;
+ return ad;
+}
+
+/*==========================================
+ * ‚»‚Ì‘¼ƒ_ƒ??[ƒWŒvŽZ
+ *------------------------------------------
+ */
+struct Damage battle_calc_misc_attack(
+ struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag)
+{
+ int int_=status_get_int(bl);
+ int dex=status_get_dex(bl);
+ int skill,ele,race,size,cardfix,race2,t_mode;
+ struct map_session_data *sd=NULL,*tsd=NULL;
+ int damage=0,div_=1,blewcount=skill_get_blewcount(skill_num,skill_lv);
+ struct Damage md;
+ int damagefix=1;
+
+ int aflag=BF_MISC|BF_SHORT|BF_SKILL;
+
+ if( bl == NULL || target == NULL ){
+ nullpo_info(NLP_MARK);
+ memset(&md,0,sizeof(md));
+ return md;
+ }
+
+ if(target->type == BL_PET) {
+ memset(&md,0,sizeof(md));
+ return md;
+ }
+
+ //Some initial values
+ md.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(bl);
+ md.dmotion=status_get_dmotion(target);
+ md.damage2=0;
+ md.type=0;
+ md.dmg_lv=ATK_DEF;
+
+ if( bl->type == BL_PC && (sd=(struct map_session_data *)bl) ) {
+ sd->state.attack_type = BF_MISC;
+ sd->state.arrow_atk = 0;
+ if (sd->skillblown[0].id != 0)
+ { //Apply the bonus blewcount. [Skotlex]
+ int i;
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num)
+ blewcount += sd->skillblown[i].val;
+ }
+ }
+
+ if( target->type==BL_PC )
+ tsd=(struct map_session_data *)target;
+
+ t_mode = status_get_mode(target);
+ ele = skill_get_pl(skill_num);
+ if (ele == -1) //Attack that takes weapon's element for misc attacks? Make it neutral [Skotlex]
+ ele = 0;
+ race = status_get_race(bl);
+ size = status_get_size(bl);
+ race2 = status_get_race(bl);
+
+ switch(skill_num){
+
+ case HT_LANDMINE: // ƒ‰ƒ“ƒhƒ}ƒCƒ“
+ damage=skill_lv*(dex+75)*(100+int_)/100;
+ break;
+
+ case HT_BLASTMINE: // ƒuƒ‰ƒXƒgƒ}ƒCƒ“
+ damage=skill_lv*(dex/2+50)*(100+int_)/100;
+ break;
+
+ case HT_CLAYMORETRAP: // ƒNƒŒƒCƒ‚ƒA?[ƒgƒ‰ƒbƒv
+ damage=skill_lv*(dex/2+75)*(100+int_)/100;
+ break;
+
+ case HT_BLITZBEAT: // ƒuƒŠƒbƒcƒr?[ƒg
+ if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0)
+ skill=0;
+ damage=(dex/10+int_/2+skill*3+40)*2;
+ if(flag > 1)
+ damage /= flag;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case TF_THROWSTONE: // ?ΓŠ‚°
+ damage=50;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case BA_DISSONANCE: // •s‹¦˜a‰¹
+ damage=30+skill_lv*10+pc_checkskill(sd,BA_MUSICALLESSON)*3;
+ break;
+
+ case NPC_SELFDESTRUCTION: // Ž©”š
+ damage = status_get_hp(bl);
+ damagefix = 0;
+ break;
+
+ case NPC_SMOKING: // ƒ^ƒoƒR‚ð‹z‚¤
+ damage=3;
+ damagefix=0;
+ break;
+
+ case NPC_DARKBREATH:
+ {
+ struct status_change *sc_data = status_get_sc_data(target);
+ int hitrate=status_get_hit(bl) - status_get_flee(target) + 80;
+ hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) );
+ if(sc_data && (sc_data[SC_SLEEP].timer!=-1 || sc_data[SC_STAN].timer!=-1 ||
+ sc_data[SC_FREEZE].timer!=-1 || (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) ) )
+ hitrate = 1000000;
+ if(rand()%100 < hitrate) {
+ damage = 500 + (skill_lv-1)*1000 + rand()%1000;
+ if(damage > 9999) damage = 9999;
+ } else
+ md.dmg_lv=ATK_FLEE;
+ }
+ break;
+
+ case SN_FALCONASSAULT: /* ƒtƒ@ƒ‹ƒRƒ“ƒAƒTƒ‹ƒg */
+ if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0)
+ skill=0;
+ damage=(dex/10+int_/2+skill*3+40)*2; //Blitz Beat Damage
+ damage=damage*(150+70*skill_lv)/100; //Falcon Assault Modifier
+ if(flag > 1)
+ damage /= flag;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case PA_PRESSURE:
+ damage=500+300*skill_lv;
+ damagefix=0;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ //This equation is not official, but it's the closest to the official one
+ //that Viccious Pucca and the other folks at the forums could come up with. [Skotlex]
+ damage = int_ * (int)(sqrt(100*status_get_vit(target))) / 3;
+ if (tsd) damage/=2;
+ aflag = (aflag&~BF_RANGEMASK)|BF_LONG;
+ break;
+ }
+
+ if(damagefix){
+ if(damage<1 && skill_num != NPC_DARKBREATH)
+ damage=1;
+
+ if( tsd ){
+ cardfix=100;
+ cardfix=cardfix*(100-tsd->subele[ele])/100;
+ cardfix=cardfix*(100-tsd->subsize[size])/100;
+ cardfix=cardfix*(100-tsd->subrace2[race2])/100;
+ cardfix=cardfix*(100-tsd->subrace[race])/100;
+ cardfix=cardfix*(100-tsd->subrace[is_boss(bl)?10:11])/100;
+ cardfix=cardfix*(100-tsd->misc_def_rate)/100;
+ if(aflag&BF_SHORT)
+ cardfix=cardfix*(100-tsd->near_attack_def_rate)/100;
+ else // BF_LONG (there's no other choice)
+ cardfix=cardfix*(100-tsd->long_attack_def_rate)/100;
+
+ damage=damage*cardfix/100;
+ }
+ if (sd && skill_num > 0 && sd->skillatk[0].id != 0)
+ {
+ int i;
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++);
+ if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num)
+ damage += damage*sd->skillatk[i].val/100;
+ }
+
+ if(damage < 0) damage = 0;
+ damage=battle_attr_fix(bl, target, damage, ele, status_get_element(target) ); // ‘®?«?C?³
+ }
+
+ div_=skill_get_num( skill_num,skill_lv );
+ if(div_>1)
+ damage*=div_;
+
+ if(damage > 0 && t_mode&MD_PLANT && skill_num != PA_PRESSURE) //Pressure can vaporize plants.
+ damage = 1;
+
+ if(is_boss(target))
+ blewcount = 0;
+
+ if (battle_config.skillrange_by_distance)
+ { //Skill range based on distance between src/target [Skotlex]
+ if ((bl->type == BL_PC && battle_config.skillrange_by_distance&1)
+ || (bl->type == BL_MOB && battle_config.skillrange_by_distance&2)
+ || (bl->type == BL_PET && battle_config.skillrange_by_distance&4)
+ ) {
+ if (check_distance_bl(bl, target, 3))
+ aflag=(aflag&~BF_RANGEMASK)|BF_SHORT;
+ else
+ aflag=(aflag&~BF_RANGEMASK)|BF_LONG;
+ }
+ }
+
+ if (skill_num != PA_PRESSURE) //Pressure ignores all these things...
+ damage=battle_calc_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); // ÅIC³
+
+ md.damage=damage;
+ md.div_=div_;
+ md.blewcount=blewcount;
+ md.flag=aflag;
+ return md;
+}
+/*==========================================
+ * ƒ_ƒ??[ƒWŒvŽZˆêŠ‡?ˆ—?—p
+ *------------------------------------------
+ */
+struct Damage battle_calc_attack( int attack_type,
+ struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag)
+{
+ struct Damage d;
+ switch(attack_type){
+ case BF_WEAPON:
+ d = battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ case BF_MAGIC:
+ d = battle_calc_magic_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ case BF_MISC:
+ d = battle_calc_misc_attack(bl,target,skill_num,skill_lv,flag);
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("battle_calc_attack: unknown attack type! %d\n",attack_type);
+ memset(&d,0,sizeof(d));
+ break;
+ }
+ return d;
+}
+/*==========================================
+ * ’Ê?í?UŒ‚?ˆ—?‚Ü‚Æ‚ß
+ *------------------------------------------
+ */
+int battle_weapon_attack( struct block_list *src,struct block_list *target,
+ unsigned int tick,int flag)
+{
+ struct map_session_data *sd = NULL, *tsd = NULL;
+ struct status_change *sc_data, *tsc_data;
+ int race, ele, damage, rdamage = 0;
+ struct Damage wd;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (src->prev == NULL || target->prev == NULL)
+ return 0;
+
+ if(src->type == BL_PC)
+ sd = (struct map_session_data *)src;
+
+ if (target->type == BL_PC)
+ tsd = (struct map_session_data *)target;
+
+ sc_data = status_get_sc_data(src);
+ tsc_data = status_get_sc_data(target);
+
+ race = status_get_race(target);
+ ele = status_get_elem_type(target);
+
+ if(sd && sd->status.weapon == 11) {
+ if(sd->equip_index[10] >= 0) {
+ if(battle_config.arrow_decrement)
+ pc_delitem(sd,sd->equip_index[10],1,0);
+ }
+ else {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ }
+
+ //Check for counter attacks that block your attack. [Skotlex]
+ if(tsc_data)
+ {
+ if(tsc_data[SC_AUTOCOUNTER].timer != -1 &&
+ (!sc_data || sc_data[SC_AUTOCOUNTER].timer == -1) &&
+ status_check_skilluse(target, src, KN_AUTOCOUNTER, 0)
+ ) {
+ int dir = map_calc_dir(target,src->x,src->y);
+ int t_dir = status_get_dir(target);
+ int dist = distance_bl(src, target);
+ if(dist <= 0 || (!map_check_dir(dir,t_dir) && dist <= status_get_range(target)+1))
+ {
+ int skilllv = tsc_data[SC_AUTOCOUNTER].val1;
+ clif_skillcastcancel(target); //Remove the casting bar. [Skotlex]
+ clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS.
+ status_change_end(target,SC_AUTOCOUNTER,-1);
+ skill_attack(BF_WEAPON,target,target,src,KN_AUTOCOUNTER,skilllv,tick,0);
+ return 0;
+ }
+ }
+ if (tsc_data[SC_BLADESTOP_WAIT].timer != -1 && !is_boss(src)) {
+ int skilllv = tsc_data[SC_BLADESTOP_WAIT].val1;
+ int duration = skill_get_time2(MO_BLADESTOP,skilllv);
+ status_change_end(target, SC_BLADESTOP_WAIT, -1);
+ clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS.
+ status_change_start(target, SC_BLADESTOP, skilllv, 2, (int)target, (int)src, duration, 0);
+ skilllv = sd?pc_checkskill(sd, MO_BLADESTOP):1;
+ status_change_start(src, SC_BLADESTOP, skilllv, 1, (int)src, (int)target, duration, 0);
+ return 0;
+ }
+
+ }
+ //Recycled the rdamage variable rather than use a new one... [Skotlex]
+ if(sd && (rdamage = pc_checkskill(sd,MO_TRIPLEATTACK)) > 0 && sd->status.weapon <= 16) // triple blow works with bows ^^ [celest]
+ {
+ int triple_rate= 30 - rdamage; //Base Rate
+ if (sc_data && sc_data[SC_SKILLRATE_UP].timer!=-1 && sc_data[SC_SKILLRATE_UP].val1 == MO_TRIPLEATTACK)
+ {
+ triple_rate+= triple_rate*(sc_data[SC_SKILLRATE_UP].val2)/100;
+ status_change_end(src,SC_SKILLRATE_UP,-1);
+ }
+ if (rand()%100 < triple_rate) return skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,rdamage,tick,0);
+ }
+ else if (sc_data && sc_data[SC_SACRIFICE].timer != -1)
+ return skill_attack(BF_WEAPON,src,src,target,PA_SACRIFICE,sc_data[SC_SACRIFICE].val1,tick,0);
+
+ wd = battle_calc_weapon_attack(src,target, 0, 0,0);
+
+ if ((damage = wd.damage + wd.damage2) > 0 && src != target) {
+ rdamage = 0;
+ if (wd.flag & BF_SHORT) {
+ if (tsd && tsd->short_weapon_damage_return)
+ rdamage += damage * tsd->short_weapon_damage_return / 100;
+ if (tsc_data && tsc_data[SC_REFLECTSHIELD].timer != -1) {
+ rdamage += damage * tsc_data[SC_REFLECTSHIELD].val2 / 100;
+ if (rdamage < 1) rdamage = 1;
+ }
+ } else if (wd.flag & BF_LONG) {
+ if (tsd && tsd->long_weapon_damage_return)
+ rdamage += damage * tsd->long_weapon_damage_return / 100;
+ }
+ if (rdamage > 0)
+ clif_damage(src, src, tick, wd.amotion, wd.dmotion, rdamage, 1, 4, 0);
+ }
+
+
+ clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , wd.type, wd.damage2);
+ //“ñ“?—¬?¶Žè‚ƃJƒ^?[ƒ‹’ÇŒ‚‚̃~ƒX•\Ž¦(–³—?‚â‚è?`)
+ if(sd && sd->status.weapon >= 16 && wd.damage2 == 0)
+ clif_damage(src, target, tick+10, wd.amotion, wd.dmotion,0, 1, 0, 0);
+
+ if (sd && sd->splash_range > 0 && (wd.damage > 0 || wd.damage2 > 0))
+ skill_castend_damage_id(src, target, 0, -1, tick, 0);
+
+ map_freeblock_lock();
+
+ battle_delay_damage(tick+wd.amotion, src, target, BF_WEAPON, 0, 0, (wd.damage+wd.damage2), wd.dmg_lv, 0);
+
+ if (wd.dmg_lv == ATK_DEF || wd.damage > 0 || wd.damage2 > 0) //Added counter effect [Skotlex]
+ skill_counter_additional_effect(src, target, 0, 0, BF_WEAPON, tick);
+ if (!status_isdead(target) && (wd.damage > 0 || wd.damage2 > 0)) {
+ if (sd) {
+ int boss = status_get_mode(target)&MD_BOSS;
+ if (
+ (sd->weapon_coma_ele[ele] > 0 && rand()%10000 < sd->weapon_coma_ele[ele]) ||
+ (sd->weapon_coma_race[race] > 0 && rand()%10000 < sd->weapon_coma_race[race]) ||
+ (sd->weapon_coma_race[boss?10:11] > 0 && rand()%10000 < sd->weapon_coma_race[boss?10:11])
+ )
+ status_change_start(target, SC_COMA, 0, 0, 0, 0, 0, 0);
+ }
+ }
+
+ if (sc_data && sc_data[SC_AUTOSPELL].timer != -1 && rand()%100 < sc_data[SC_AUTOSPELL].val4) {
+ int sp = 0, f = 0;
+ int skillid = sc_data[SC_AUTOSPELL].val2;
+ int skilllv = sc_data[SC_AUTOSPELL].val3;
+ int i = rand()%100;
+ if (sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_SAGE)
+ i = 0; //Max chance, no skilllv reduction. [Skotlex]
+ if (i >= 50) skilllv -= 2;
+ else if (i >= 15) skilllv--;
+ if (skilllv < 1) skilllv = 1;
+ if (sd) sp = skill_get_sp(skillid,skilllv) * 2 / 3;
+
+ if ((sd && sd->status.sp >= sp) || !sd) {
+ if (skill_get_inf(skillid) & INF_GROUND_SKILL)
+ f = skill_castend_pos2(src, target->x, target->y, skillid, skilllv, tick, flag);
+ else {
+ switch(skill_get_nk(skillid)) {
+ case NK_NO_DAMAGE:/* Žx‰‡Œn */
+ if((skillid == AL_HEAL || (skillid == ALL_RESURRECTION && !tsd)) && battle_check_undead(race,ele))
+ f = skill_castend_damage_id(src, target, skillid, skilllv, tick, flag);
+ else
+ f = skill_castend_nodamage_id(src, target, skillid, skilllv, tick, flag);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ f = skill_castend_damage_id(src, target, skillid, skilllv, tick, flag);
+ break;
+ }
+ }
+ if (sd && !f) { pc_heal(sd, 0, -sp); }
+ }
+ }
+ if (sd) {
+ if (wd.flag & BF_WEAPON && src != target && (wd.damage > 0 || wd.damage2 > 0)) {
+ int hp = 0, sp = 0;
+ if (!battle_config.left_cardfix_to_right) { // “ñ“?—¬?¶ŽèƒJ?[ƒh‚Ì‹zŽûŒnŒø‰Ê‚ð‰EŽè‚ɒljÁ‚µ‚È‚¢?ê?‡
+ hp += battle_calc_drain(wd.damage, sd->right_weapon.hp_drain_rate, sd->right_weapon.hp_drain_per, sd->right_weapon.hp_drain_value);
+ hp += battle_calc_drain(wd.damage2, sd->left_weapon.hp_drain_rate, sd->left_weapon.hp_drain_per, sd->left_weapon.hp_drain_value);
+ sp += battle_calc_drain(wd.damage, sd->right_weapon.sp_drain_rate, sd->right_weapon.sp_drain_per, sd->right_weapon.sp_drain_value);
+ sp += battle_calc_drain(wd.damage2, sd->left_weapon.sp_drain_rate, sd->left_weapon.sp_drain_per, sd->left_weapon.sp_drain_value);
+ } else { // “ñ“?—¬?¶ŽèƒJ?[ƒh‚Ì‹zŽûŒnŒø‰Ê‚ð‰EŽè‚ɒljÁ‚·‚é?ê?‡
+ int hp_drain_rate = sd->right_weapon.hp_drain_rate + sd->left_weapon.hp_drain_rate;
+ int hp_drain_per = sd->right_weapon.hp_drain_per + sd->left_weapon.hp_drain_per;
+ int hp_drain_value = sd->right_weapon.hp_drain_value + sd->left_weapon.hp_drain_value;
+ int sp_drain_rate = sd->right_weapon.sp_drain_rate + sd->left_weapon.sp_drain_rate;
+ int sp_drain_per = sd->right_weapon.sp_drain_per + sd->left_weapon.sp_drain_per;
+ int sp_drain_value = sd->right_weapon.sp_drain_value + sd->left_weapon.sp_drain_value;
+ hp += battle_calc_drain(wd.damage, hp_drain_rate, hp_drain_per, hp_drain_value);
+ sp += battle_calc_drain(wd.damage, sp_drain_rate, sp_drain_per, sp_drain_value);
+ }
+ if (hp && hp + sd->status.hp > sd->status.max_hp)
+ hp = sd->status.max_hp - sd->status.hp;
+ if (sp && sp + sd->status.sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+
+ if (hp || sp)
+ pc_heal(sd, hp, sp);
+
+ if (battle_config.show_hp_sp_drain)
+ { //Display gained values only when they are positive [Skotlex]
+ if (hp > 0)
+ clif_heal(sd->fd, SP_HP, hp);
+ if (sp > 0)
+ clif_heal(sd->fd, SP_SP, sp);
+ }
+
+ if (tsd && sd->sp_drain_type)
+ pc_heal(tsd, 0, -sp);
+ }
+ }
+ if (rdamage > 0) //By sending attack type "none" skill_additional_effect won't be invoked. [Skotlex]
+ battle_delay_damage(tick+wd.amotion, target, src, 0, 0, 0, rdamage, ATK_DEF, 0);
+
+ if (tsc_data) {
+ if (tsc_data[SC_POISONREACT].timer != -1 &&
+ check_distance_bl(src, target, status_get_range(target)+1) &&
+ status_check_skilluse(target, src, TF_POISON, 0)
+ ) { //Poison React
+ if (status_get_elem_type(src) == 5) {
+ tsc_data[SC_POISONREACT].val2 = 0;
+ skill_attack(BF_WEAPON,target,target,src,AS_POISONREACT,sc_data[SC_POISONREACT].val1,tick,0);
+ } else {
+ skill_attack(BF_WEAPON,target,target,src,TF_POISON, 5, tick, flag);
+ --tsc_data[SC_POISONREACT].val2;
+ }
+ if (tsc_data[SC_POISONREACT].val2 <= 0)
+ status_change_end(target, SC_POISONREACT, -1);
+ }
+ if (tsc_data[SC_SPLASHER].timer != -1) //‰£‚Á‚½‚Ì‚Å‘Î?ۂ̃xƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ?[?ó‘Ô‚ð‰ð?œ
+ status_change_end(target, SC_SPLASHER, -1);
+ }
+
+ //SG_FUSION hp penalty [Komurka]
+ if (sd && sc_data && sc_data[SC_FUSION].timer!=-1)
+ {
+ int hp=0;
+ if(target->type == BL_PC)
+ {
+ hp = sd->status.max_hp * 8 / 100;
+ if((sd->status.hp * 100/sd->status.max_hp) <= 20)
+ hp = sd->status.hp;
+ }else
+ hp = sd->status.max_hp * 5 / 1000;
+ pc_heal(sd,-hp,0);
+ }
+
+ map_freeblock_unlock();
+ return wd.dmg_lv;
+}
+
+int battle_check_undead(int race,int element)
+{
+ if(battle_config.undead_detect_type == 0) {
+ if(element == 9)
+ return 1;
+ }
+ else if(battle_config.undead_detect_type == 1) {
+ if(race == 1)
+ return 1;
+ }
+ else {
+ if(element == 9 || race == 1)
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks the state between two targets (rewritten by Skotlex)
+ * (enemy, friend, party, guild, etc)
+ * See battle.h for possible values/combinations
+ * to be used here (BCT_* constants)
+ * Return value is:
+ * 1: flag holds true (is enemy, party, etc)
+ * -1: flag fails
+ * 0: Invalid target (non-targetable ever)
+ *------------------------------------------
+ */
+int battle_check_target( struct block_list *src, struct block_list *target,int flag)
+{
+ int m,state = 0; //Initial state none
+ int strip_enemy = 1; //Flag which marks whether to remove the BCT_ENEMY status if it's also friend/ally.
+ struct block_list *s_bl= src, *t_bl= target;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ m = target->m;
+ if (flag&BCT_ENEMY && !map_flag_gvg(m) && !(status_get_mode(src)&MD_BOSS))
+ { //No offensive stuff while in Basilica.
+ if (map_getcell(m,src->x,src->y,CELL_CHKBASILICA) ||
+ map_getcell(m,target->x,target->y,CELL_CHKBASILICA))
+ return -1;
+ }
+
+ if (target->type == BL_SKILL) //Needed out of the switch in case the ownership needs to be passed skill->mob->master
+ {
+ struct skill_unit *su = (struct skill_unit *)target;
+ if (!su->group)
+ return 0;
+ if (skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ { //Only a few skills can target traps...
+ switch (battle_getcurrentskill(src))
+ {
+ case HT_REMOVETRAP:
+ case AC_SHOWER:
+ case WZ_HEAVENDRIVE:
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ break;
+ default:
+ return 0;
+ }
+ } else if (su->group->skill_id==WZ_ICEWALL)
+ { //Icewall can be hit by anything except skills.
+ if (src->type == BL_SKILL)
+ return 0;
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ } else //Excepting traps and icewall, you should not be able to target skills.
+ return 0;
+ if ((t_bl = map_id2bl(su->group->src_id)) == NULL)
+ t_bl = target; //Fallback on the trap itself, otherwise consider this a "versus caster" scenario.
+ }
+
+ switch (t_bl->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *)t_bl;
+ if (sd->invincible_timer != -1 || pc_isinvisible(sd))
+ return -1; //Cannot be targeted yet.
+ if (sd->state.monster_ignore && src->type == BL_MOB)
+ return 0; //option to have monsters ignore GMs [Valaris]
+ if (sd->special_state.killable)
+ {
+ state |= BCT_ENEMY; //Universal Victim
+ strip_enemy = 0;
+ }
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)t_bl;
+ if (!agit_flag && md->guardian_data && md->guardian_data->guild_id)
+ return 0; //Disable guardians/emperiums owned by Guilds on non-woe times.
+ if (md->special_state.ai == 2)
+ { //Mines are sort of universal enemies.
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ }
+ if (md->master_id && (t_bl = map_id2bl(md->master_id)) == NULL)
+ t_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "versus master" scenario.
+ break;
+ }
+ case BL_PET:
+ {
+ return 0; //Pets cannot be targetted.
+ }
+ case BL_SKILL: //Skill with no owner? Kinda odd... but.. let it through.
+ break;
+ default: //Invalid target
+ return 0;
+ }
+
+ if (src->type == BL_SKILL)
+ {
+ struct skill_unit *su = (struct skill_unit *)src;
+ if (!su->group)
+ return 0;
+
+ if (su->group->src_id == target->id)
+ {
+ int inf2;
+ inf2 = skill_get_inf2(su->group->skill_id);
+ if (inf2&INF2_NO_TARGET_SELF)
+ return -1;
+ if (inf2&INF2_TARGET_SELF)
+ return 1;
+ }
+ if ((s_bl = map_id2bl(su->group->src_id)) == NULL)
+ s_bl = src; //Fallback on the trap itself, otherwise consider this a "caster versus enemy" scenario.
+ }
+
+ switch (s_bl->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *) s_bl;
+ if (sd->special_state.killer)
+ {
+ state |= BCT_ENEMY; //Is on a killing rampage :O
+ strip_enemy = 0;
+ } else
+ if (sd->duel_group && // Duel [LuzZza]
+ !((!battle_config.duel_allow_pvp && map[m].flag.pvp) ||
+ (!battle_config.duel_allow_gvg && map_flag_gvg(m)))) {
+ if (t_bl->type == BL_PC && t_bl != s_bl &&
+ (sd->duel_group == ((struct map_session_data *)t_bl)->duel_group))
+ {
+ state |= BCT_ENEMY;
+ strip_enemy = 0;
+ } else if (t_bl != s_bl) {
+ // You can't target anything out of your duel
+ return 0;
+ }
+ }
+ if (map_flag_gvg(m) && !sd->status.guild_id &&
+ t_bl->type == BL_MOB && ((struct mob_data *)t_bl)->guardian_data)
+ return 0; //If you don't belong to a guild, can't target guardians/emperium.
+ if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Natural enemy.
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)s_bl;
+ if (!agit_flag && md->guardian_data && md->guardian_data->guild_id)
+ return 0; //Disable guardians/emperium owned by Guilds on non-woe times.
+ if (!md->special_state.ai) { //Normal mobs.
+ if (t_bl->type == BL_MOB && !((struct mob_data*)t_bl)->special_state.ai)
+ state |= BCT_PARTY; //Normal mobs with no ai are friends.
+ else
+ state |= BCT_ENEMY; //However, all else are enemies.
+ } else if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Natural enemy for AI mobs are nonplayers.
+ if (md->master_id && (s_bl = map_id2bl(md->master_id)) == NULL)
+ s_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "from master" scenario.
+ break;
+ }
+ case BL_PET:
+ {
+ struct pet_data *pd = (struct pet_data *)s_bl;
+ if (t_bl->type != BL_MOB && flag&BCT_ENEMY)
+ return 0; //Pet may not attack non-mobs/items.
+ if (t_bl->type == BL_MOB && ((struct mob_data *)t_bl)->guardian_data && flag&BCT_ENEMY)
+ return 0; //pet may not attack Guardians/Emperium
+ if (t_bl->type != BL_PC)
+ state |= BCT_ENEMY; //Stock enemy type.
+ if (pd->msd)
+ s_bl = &pd->msd->bl; //"My master's enemies are my enemies..."
+ break;
+ }
+ case BL_SKILL: //Skill with no owner? Fishy, but let it through.
+ break;
+ default: //Invalid source of attack?
+ return 0;
+ }
+
+ if ((flag&BCT_ALL) == BCT_ALL) { //All actually stands for all players/mobs
+ if (target->type == BL_MOB || target->type == BL_PC)
+ return 1;
+ else
+ return -1;
+ } else if (flag == BCT_NOONE) //Why would someone use this? no clue.
+ return -1;
+
+ if (t_bl == s_bl)
+ { //No need for further testing.
+ state |= BCT_SELF|BCT_PARTY|BCT_GUILD;
+ if (state&BCT_ENEMY && strip_enemy)
+ state&=~BCT_ENEMY;
+ return (flag&state)?1:-1;
+ }
+
+ if (map_flag_vs(m)) { //Check rivalry settings.
+ if (flag&(BCT_PARTY|BCT_ENEMY)) {
+ int s_party = status_get_party_id(s_bl);
+ if (
+ !(map[m].flag.pvp && map[m].flag.pvp_noparty) &&
+ !(map_flag_gvg(m) && map[m].flag.gvg_noparty) &&
+ s_party && s_party == status_get_party_id(t_bl)
+ )
+ state |= BCT_PARTY;
+ else
+ state |= BCT_ENEMY;
+ }
+ if (flag&(BCT_GUILD|BCT_ENEMY)) {
+ int s_guild = status_get_guild_id(s_bl);
+ int t_guild = status_get_guild_id(t_bl);
+ if (
+ !(map[m].flag.pvp && map[m].flag.pvp_noguild) &&
+ s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild))
+ )
+ state |= BCT_GUILD;
+ else
+ state |= BCT_ENEMY;
+ }
+ if (state&BCT_ENEMY && battle_config.pk_mode && !map_flag_gvg(m) && s_bl->type == BL_PC && t_bl->type == BL_PC)
+ { //Prevent novice engagement on pk_mode (feature by Valaris)
+ struct map_session_data* sd;
+ if ((sd = (struct map_session_data*)s_bl) &&
+ ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || sd->status.base_level < battle_config.pk_min_level))
+ state&=~BCT_ENEMY;
+ else
+ if ((sd = (struct map_session_data*)t_bl) &&
+ ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || sd->status.base_level < battle_config.pk_min_level))
+ state&=~BCT_ENEMY;
+ }
+ } else { //Non pvp/gvg, check party/guild settings.
+ if (flag&BCT_PARTY || state&BCT_ENEMY) {
+ int s_party = status_get_party_id(s_bl);
+ if (s_party && s_party ==status_get_party_id(t_bl))
+ state |= BCT_PARTY;
+ }
+ if (flag&BCT_GUILD || state&BCT_ENEMY) {
+ int s_guild = status_get_guild_id(s_bl);
+ int t_guild = status_get_guild_id(t_bl);
+ if (s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild)))
+ state |= BCT_GUILD;
+ }
+ }
+
+ if (!state) //If not an enemy, nor a guild, nor party, nor yourself, it's neutral.
+ state = BCT_NEUTRAL;
+ //Alliance state takes precedence over enemy one.
+ else if (state&BCT_ENEMY && strip_enemy && state&(BCT_SELF|BCT_PARTY|BCT_GUILD))
+ state&=~BCT_ENEMY;
+
+ return (flag&state)?1:-1;
+}
+/*==========================================
+ * ŽË’ö”»’è
+ *------------------------------------------
+ */
+int battle_check_range(struct block_list *src,struct block_list *bl,int range)
+{
+ int arange;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(src->m != bl->m) // ˆá‚¤ƒ}ƒbƒv
+ return 0;
+
+ arange = distance_bl(src, bl);
+ if( range>0 && range < arange)
+ return 0;
+
+ if( arange<2 ) //No need for path checking.
+ return 1;
+
+ // ?áŠQ•¨”»’è
+ return path_search_long(NULL,src->m,src->x,src->y,bl->x,bl->y);
+}
+
+/*==========================================
+ * Return numerical value of a switch configuration (modified by [Yor])
+ * on/off, english, français, deutsch, español
+ *------------------------------------------
+ */
+int battle_config_switch(const char *str) {
+ if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
+ return 1;
+ if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
+ return 0;
+ return atoi(str);
+}
+
+static const struct battle_data_short {
+ const char *str;
+ unsigned short *val;
+} battle_data_short[] = { //List here battle_athena options which are type unsigned short!
+ { "warp_point_debug", &battle_config.warp_point_debug },
+ { "enemy_critical_rate", &battle_config.enemy_critical_rate },
+ { "enemy_str", &battle_config.enemy_str },
+ { "enemy_perfect_flee", &battle_config.enemy_perfect_flee },
+ { "casting_rate", &battle_config.cast_rate },
+ { "delay_rate", &battle_config.delay_rate },
+ { "delay_dependon_dex", &battle_config.delay_dependon_dex },
+ { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable },
+ { "left_cardfix_to_right", &battle_config.left_cardfix_to_right },
+ { "player_skill_add_range", &battle_config.pc_skill_add_range },
+ { "skill_out_range_consume", &battle_config.skill_out_range_consume },
+ { "monster_skill_add_range", &battle_config.mob_skill_add_range },
+ { "skillrange_by_distance", &battle_config.skillrange_by_distance },
+ { "skillrange_from_weapon", &battle_config.use_weapon_skill_range },
+ { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate },
+ { "defunit_not_enemy", &battle_config.defnotenemy },
+ { "gvg_traps_target_all", &battle_config.vs_traps_bctall },
+ { "random_monster_checklv", &battle_config.random_monster_checklv },
+ { "attribute_recover", &battle_config.attr_recover },
+ { "flooritem_lifetime", &battle_config.flooritem_lifetime },
+ { "item_auto_get", &battle_config.item_auto_get },
+ { "drop_rate0item", &battle_config.drop_rate0item },
+ { "pvp_exp", &battle_config.pvp_exp },
+ { "gtb_pvp_only", &battle_config.gtb_pvp_only },
+ { "guild_max_castles", &battle_config.guild_max_castles },
+ { "death_penalty_type", &battle_config.death_penalty_type },
+ { "death_penalty_base", &battle_config.death_penalty_base },
+ { "death_penalty_job", &battle_config.death_penalty_job },
+ { "restart_hp_rate", &battle_config.restart_hp_rate },
+ { "restart_sp_rate", &battle_config.restart_sp_rate },
+ { "mvp_hp_rate", &battle_config.mvp_hp_rate },
+ { "monster_hp_rate", &battle_config.monster_hp_rate },
+ { "monster_max_aspd", &battle_config.monster_max_aspd },
+ { "atcommand_gm_only", &battle_config.atc_gmonly },
+ { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit },
+ { "atcommand_slave_clone_limit", &battle_config.atc_slave_clone_limit},
+ { "gm_all_skill", &battle_config.gm_allskill },
+ { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra },
+ { "gm_all_equipment", &battle_config.gm_allequip },
+ { "gm_skill_unconditional", &battle_config.gm_skilluncond },
+ { "gm_join_chat", &battle_config.gm_join_chat },
+ { "gm_kick_chat", &battle_config.gm_kick_chat },
+ { "player_skillfree", &battle_config.skillfree },
+ { "player_skillup_limit", &battle_config.skillup_limit },
+ { "weapon_produce_rate", &battle_config.wp_rate },
+ { "potion_produce_rate", &battle_config.pp_rate },
+ { "monster_active_enable", &battle_config.monster_active_enable },
+ { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate},
+ { "monster_loot_type", &battle_config.monster_loot_type },
+// { "mob_skill_use", &battle_config.mob_skill_use }, //Deprecated
+ { "mob_skill_rate", &battle_config.mob_skill_rate },
+ { "mob_skill_delay", &battle_config.mob_skill_delay },
+ { "mob_count_rate", &battle_config.mob_count_rate },
+ { "mob_spawn_delay", &battle_config.mob_spawn_delay },
+ { "no_spawn_on_player", &battle_config.no_spawn_on_player },
+ { "plant_spawn_delay", &battle_config.plant_spawn_delay },
+ { "boss_spawn_delay", &battle_config.boss_spawn_delay },
+ { "slaves_inherit_speed", &battle_config.slaves_inherit_speed },
+ { "summons_inherit_effects", &battle_config.summons_inherit_effects },
+ { "pc_damage_walk_delay_rate", &battle_config.pc_walk_delay_rate },
+ { "damage_walk_delay_rate", &battle_config.walk_delay_rate },
+ { "multihit_delay", &battle_config.multihit_delay },
+ { "quest_skill_learn", &battle_config.quest_skill_learn },
+ { "quest_skill_reset", &battle_config.quest_skill_reset },
+ { "basic_skill_check", &battle_config.basic_skill_check },
+ { "guild_emperium_check", &battle_config.guild_emperium_check },
+ { "guild_exp_rate", &battle_config.guild_exp_rate },
+ { "guild_exp_limit", &battle_config.guild_exp_limit },
+ { "player_invincible_time", &battle_config.pc_invincible_time },
+ { "pet_catch_rate", &battle_config.pet_catch_rate },
+ { "pet_rename", &battle_config.pet_rename },
+ { "pet_friendly_rate", &battle_config.pet_friendly_rate },
+ { "pet_hungry_delay_rate", &battle_config.pet_hungry_delay_rate },
+ { "pet_hungry_friendly_decrease", &battle_config.pet_hungry_friendly_decrease},
+ { "pet_str", &battle_config.pet_str },
+ { "pet_status_support", &battle_config.pet_status_support },
+ { "pet_attack_support", &battle_config.pet_attack_support },
+ { "pet_damage_support", &battle_config.pet_damage_support },
+ { "pet_support_min_friendly", &battle_config.pet_support_min_friendly },
+ { "pet_support_rate", &battle_config.pet_support_rate },
+ { "pet_attack_exp_to_master", &battle_config.pet_attack_exp_to_master },
+ { "pet_attack_exp_rate", &battle_config.pet_attack_exp_rate },
+ { "pet_lv_rate", &battle_config.pet_lv_rate }, //Skotlex
+ { "pet_max_stats", &battle_config.pet_max_stats }, //Skotlex
+ { "pet_max_atk1", &battle_config.pet_max_atk1 }, //Skotlex
+ { "pet_max_atk2", &battle_config.pet_max_atk2 }, //Skotlex
+ { "pet_disable_in_gvg", &battle_config.pet_no_gvg }, //Skotlex
+ { "skill_min_damage", &battle_config.skill_min_damage },
+ { "finger_offensive_type", &battle_config.finger_offensive_type },
+ { "heal_exp", &battle_config.heal_exp },
+ { "resurrection_exp", &battle_config.resurrection_exp },
+ { "shop_exp", &battle_config.shop_exp },
+ { "combo_delay_rate", &battle_config.combo_delay_rate },
+ { "item_check", &battle_config.item_check },
+ { "item_use_interval", &battle_config.item_use_interval },
+ { "wedding_modifydisplay", &battle_config.wedding_modifydisplay },
+ { "wedding_ignorepalette", &battle_config.wedding_ignorepalette }, //[Skotlex]
+ { "xmas_ignorepalette", &battle_config.xmas_ignorepalette }, // [Valaris]
+ { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate },
+ { "item_name_override_grffile", &battle_config.item_name_override_grffile},
+ { "item_equip_override_grffile", &battle_config.item_equip_override_grffile}, // [Celest]
+ { "item_slots_override_grffile", &battle_config.item_slots_override_grffile}, // [Celest]
+ { "indoors_override_grffile", &battle_config.indoors_override_grffile}, // [Celest]
+ { "skill_sp_override_grffile", &battle_config.skill_sp_override_grffile}, // [Celest]
+ { "cardillust_read_grffile", &battle_config.cardillust_read_grffile}, // [Celest]
+ { "arrow_decrement", &battle_config.arrow_decrement },
+ { "max_aspd", &battle_config.max_aspd },
+ { "max_walk_speed", &battle_config.max_walk_speed },
+ { "max_lv", &battle_config.max_lv },
+ { "aura_lv", &battle_config.aura_lv },
+ { "max_parameter", &battle_config.max_parameter },
+ { "max_baby_parameter", &battle_config.max_baby_parameter },
+ { "max_def", &battle_config.max_def },
+ { "over_def_bonus", &battle_config.over_def_bonus },
+ { "player_skill_log", &battle_config.pc_skill_log },
+ { "monster_skill_log", &battle_config.mob_skill_log },
+ { "battle_log", &battle_config.battle_log },
+ { "save_log", &battle_config.save_log },
+ { "error_log", &battle_config.error_log },
+ { "etc_log", &battle_config.etc_log },
+ { "save_clothcolor", &battle_config.save_clothcolor },
+ { "undead_detect_type", &battle_config.undead_detect_type },
+ { "player_auto_counter_type", &battle_config.pc_auto_counter_type },
+ { "monster_auto_counter_type", &battle_config.monster_auto_counter_type},
+ { "min_hitrate", &battle_config.min_hitrate },
+ { "max_hitrate", &battle_config.max_hitrate },
+ { "agi_penalty_type", &battle_config.agi_penalty_type },
+ { "agi_penalty_count", &battle_config.agi_penalty_count },
+ { "agi_penalty_num", &battle_config.agi_penalty_num },
+ { "agi_penalty_count_lv", &battle_config.agi_penalty_count_lv },
+ { "vit_penalty_type", &battle_config.vit_penalty_type },
+ { "vit_penalty_count", &battle_config.vit_penalty_count },
+ { "vit_penalty_num", &battle_config.vit_penalty_num },
+ { "vit_penalty_count_lv", &battle_config.vit_penalty_count_lv },
+ { "player_defense_type", &battle_config.player_defense_type },
+ { "monster_defense_type", &battle_config.monster_defense_type },
+ { "pet_defense_type", &battle_config.pet_defense_type },
+ { "magic_defense_type", &battle_config.magic_defense_type },
+ { "player_skill_reiteration", &battle_config.pc_skill_reiteration },
+ { "monster_skill_reiteration", &battle_config.monster_skill_reiteration},
+ { "player_skill_nofootset", &battle_config.pc_skill_nofootset },
+ { "monster_skill_nofootset", &battle_config.monster_skill_nofootset },
+ { "player_cloak_check_type", &battle_config.pc_cloak_check_type },
+ { "monster_cloak_check_type", &battle_config.monster_cloak_check_type },
+ { "sense_type", &battle_config.estimation_type },
+ { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate },
+ { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate },
+ { "gvg_weapon_attack_damage_rate", &battle_config.gvg_weapon_damage_rate },
+ { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate },
+ { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate },
+ { "gvg_flee_penalty", &battle_config.gvg_flee_penalty },
+ { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill},
+ { "player_attack_direction_change", &battle_config.pc_attack_direction_change },
+ { "monster_attack_direction_change", &battle_config.monster_attack_direction_change },
+ { "player_land_skill_limit", &battle_config.pc_land_skill_limit },
+ { "monster_land_skill_limit", &battle_config.monster_land_skill_limit},
+ { "party_skill_penalty", &battle_config.party_skill_penalty },
+ { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover },
+ { "produce_item_name_input", &battle_config.produce_item_name_input },
+ { "produce_potion_name_input", &battle_config.produce_potion_name_input},
+ { "making_arrow_name_input", &battle_config.making_arrow_name_input },
+ { "holywater_name_input", &battle_config.holywater_name_input },
+ { "cdp_name_input", &battle_config.cdp_name_input },
+ { "display_delay_skill_fail", &battle_config.display_delay_skill_fail },
+ { "display_snatcher_skill_fail", &battle_config.display_snatcher_skill_fail },
+ { "chat_warpportal", &battle_config.chat_warpportal },
+ { "mob_warpportal", &battle_config.mob_warpportal },
+ { "dead_branch_active", &battle_config.dead_branch_active },
+ { "show_steal_in_same_party", &battle_config.show_steal_in_same_party },
+ { "show_party_share_picker", &battle_config.party_show_share_picker },
+ { "party_item_share_type", &battle_config.party_share_type },
+ { "pet_attack_attr_none", &battle_config.pet_attack_attr_none },
+ { "mob_attack_attr_none", &battle_config.mob_attack_attr_none },
+ { "mob_ghostring_fix", &battle_config.mob_ghostring_fix },
+ { "pc_attack_attr_none", &battle_config.pc_attack_attr_none },
+ { "gx_allhit", &battle_config.gx_allhit },
+ { "gx_disptype", &battle_config.gx_disptype },
+ { "devotion_level_difference", &battle_config.devotion_level_difference },
+ { "player_skill_partner_check", &battle_config.player_skill_partner_check},
+ { "hide_GM_session", &battle_config.hide_GM_session },
+ { "invite_request_check", &battle_config.invite_request_check },
+ { "skill_removetrap_type", &battle_config.skill_removetrap_type },
+ { "disp_experience", &battle_config.disp_experience },
+ { "disp_zeny", &battle_config.disp_zeny },
+ { "castle_defense_rate", &battle_config.castle_defense_rate },
+ { "hp_rate", &battle_config.hp_rate },
+ { "sp_rate", &battle_config.sp_rate },
+ { "gm_cant_drop_min_lv", &battle_config.gm_cant_drop_min_lv },
+ { "gm_cant_drop_max_lv", &battle_config.gm_cant_drop_max_lv },
+ { "disp_hpmeter", &battle_config.disp_hpmeter },
+ { "bone_drop", &battle_config.bone_drop },
+ { "buyer_name", &battle_config.buyer_name },
+ { "skill_wall_check", &battle_config.skill_wall_check },
+ { "cell_stack_limit", &battle_config.cell_stack_limit },
+// eAthena additions
+ { "item_logarithmic_drops", &battle_config.logarithmic_drops },
+ { "item_drop_common_min", &battle_config.item_drop_common_min }, // Added by TyrNemesis^
+ { "item_drop_common_max", &battle_config.item_drop_common_max },
+ { "item_drop_equip_min", &battle_config.item_drop_equip_min },
+ { "item_drop_equip_max", &battle_config.item_drop_equip_max },
+ { "item_drop_card_min", &battle_config.item_drop_card_min },
+ { "item_drop_card_max", &battle_config.item_drop_card_max },
+ { "item_drop_mvp_min", &battle_config.item_drop_mvp_min },
+ { "item_drop_mvp_max", &battle_config.item_drop_mvp_max }, // End Addition
+ { "item_drop_heal_min", &battle_config.item_drop_heal_min },
+ { "item_drop_heal_max", &battle_config.item_drop_heal_max },
+ { "item_drop_use_min", &battle_config.item_drop_use_min },
+ { "item_drop_use_max", &battle_config.item_drop_use_max },
+ { "item_drop_treasure_min", &battle_config.item_drop_treasure_min },
+ { "item_drop_treasure_max", &battle_config.item_drop_treasure_max },
+ { "prevent_logout", &battle_config.prevent_logout }, // Added by RoVeRT
+ { "alchemist_summon_reward", &battle_config.alchemist_summon_reward }, // [Valaris]
+ { "max_base_level", &battle_config.max_base_level }, // [Valaris]
+ { "max_job_level", &battle_config.max_job_level },
+ { "max_advanced_job_level", &battle_config.max_adv_level },
+ { "max_super_novice_level", &battle_config.max_sn_level },
+ { "drops_by_luk", &battle_config.drops_by_luk }, // [Valaris]
+ { "drops_by_luk2", &battle_config.drops_by_luk2 }, // [Skotlex]
+ { "equip_natural_break_rate", &battle_config.equip_natural_break_rate },
+ { "equip_self_break_rate", &battle_config.equip_self_break_rate },
+ { "equip_skill_break_rate", &battle_config.equip_skill_break_rate },
+ { "pk_mode", &battle_config.pk_mode }, // [Valaris]
+ { "manner_system", &battle_config.manner_system }, // [Komurka]
+ { "pet_equip_required", &battle_config.pet_equip_required }, // [Valaris]
+ { "multi_level_up", &battle_config.multi_level_up }, // [Valaris]
+ { "backstab_bow_penalty", &battle_config.backstab_bow_penalty },
+ { "night_at_start", &battle_config.night_at_start }, // added by [Yor]
+ { "show_mob_hp", &battle_config.show_mob_hp }, // [Valaris]
+ { "ban_spoof_namer", &battle_config.ban_spoof_namer }, // added by [Yor]
+ { "hack_info_GM_level", &battle_config.hack_info_GM_level }, // added by [Yor]
+ { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level }, // added by [Yor]
+ { "packet_ver_flag", &battle_config.packet_ver_flag }, // added by [Yor]
+ { "min_hair_style", &battle_config.min_hair_style }, // added by [MouseJstr]
+ { "max_hair_style", &battle_config.max_hair_style }, // added by [MouseJstr]
+ { "min_hair_color", &battle_config.min_hair_color }, // added by [MouseJstr]
+ { "max_hair_color", &battle_config.max_hair_color }, // added by [MouseJstr]
+ { "min_cloth_color", &battle_config.min_cloth_color }, // added by [MouseJstr]
+ { "max_cloth_color", &battle_config.max_cloth_color }, // added by [MouseJstr]
+ { "pet_hair_style", &battle_config.pet_hair_style }, // added by [Skotlex]
+ { "castrate_dex_scale", &battle_config.castrate_dex_scale }, // added by [MouseJstr]
+ { "area_size", &battle_config.area_size }, // added by [MouseJstr]
+ { "muting_players", &battle_config.muting_players}, // added by [Apple]
+ { "zeny_from_mobs", &battle_config.zeny_from_mobs}, // [Valaris]
+ { "mobs_level_up", &battle_config.mobs_level_up}, // [Valaris]
+ { "mobs_level_up_exp_rate", &battle_config.mobs_level_up_exp_rate}, // [Valaris]
+ { "pk_min_level", &battle_config.pk_min_level}, // [celest]
+ { "skill_steal_type", &battle_config.skill_steal_type}, // [celest]
+ { "skill_steal_rate", &battle_config.skill_steal_rate}, // [celest]
+// { "night_darkness_level", &battle_config.night_darkness_level}, // [celest]
+ { "motd_type", &battle_config.motd_type}, // [celest]
+ { "allow_atcommand_when_mute", &battle_config.allow_atcommand_when_mute}, // [celest]
+ { "finding_ore_rate", &battle_config.finding_ore_rate}, // [celest]
+ { "exp_calc_type", &battle_config.exp_calc_type}, // [celest]
+ { "min_skill_delay_limit", &battle_config.min_skill_delay_limit}, // [celest]
+ { "require_glory_guild", &battle_config.require_glory_guild}, // [celest]
+ { "idle_no_share", &battle_config.idle_no_share}, // [celest], for a feature by [MouseJstr]
+ { "party_even_share_bonus", &battle_config.party_even_share_bonus},
+ { "delay_battle_damage", &battle_config.delay_battle_damage}, // [celest]
+ { "display_version", &battle_config.display_version}, // [Ancyker], for a feature by...?
+ { "who_display_aid", &battle_config.who_display_aid}, // [Ancyker], for a feature by...?
+ { "display_hallucination", &battle_config.display_hallucination}, // [Skotlex]
+ { "use_statpoint_table", &battle_config.use_statpoint_table}, // [Skotlex]
+ { "ignore_items_gender", &battle_config.ignore_items_gender}, // [Lupus]
+ { "copyskill_restrict", &battle_config.copyskill_restrict}, // [Aru]
+ { "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs}, // [Aru]
+ { "monster_ai", &battle_config.mob_ai},
+ { "dynamic_mobs", &battle_config.dynamic_mobs},
+ { "mob_remove_damaged", &battle_config.mob_remove_damaged},
+ { "show_hp_sp_drain", &battle_config.show_hp_sp_drain}, // [Skotlex]
+ { "show_hp_sp_gain", &battle_config.show_hp_sp_gain}, // [Skotlex]
+ { "mob_npc_event_type", &battle_config.mob_npc_event_type},
+ { "mob_clear_delay", &battle_config.mob_clear_delay}, // [Valaris]
+ { "character_size", &battle_config.character_size}, // [Lupus]
+ { "mob_max_skilllvl", &battle_config.mob_max_skilllvl}, // [Lupus]
+ { "retaliate_to_master", &battle_config.retaliate_to_master}, // [Skotlex]
+ { "rare_drop_announce", &battle_config.rare_drop_announce}, // [Lupus]
+ { "firewall_hits_on_undead", &battle_config.firewall_hits_on_undead}, // [Skotlex]
+ { "title_lvl1", &battle_config.title_lvl1}, // [Lupus]
+ { "title_lvl2", &battle_config.title_lvl2}, // [Lupus]
+ { "title_lvl3", &battle_config.title_lvl3}, // [Lupus]
+ { "title_lvl4", &battle_config.title_lvl4}, // [Lupus]
+ { "title_lvl5", &battle_config.title_lvl5}, // [Lupus]
+ { "title_lvl6", &battle_config.title_lvl6}, // [Lupus]
+ { "title_lvl7", &battle_config.title_lvl7}, // [Lupus]
+ { "title_lvl8", &battle_config.title_lvl8}, // [Lupus]
+
+ { "duel_enable", &battle_config.duel_enable}, // [LuzZza]
+ { "duel_allow_pvp", &battle_config.duel_allow_pvp}, // [LuzZza]
+ { "duel_allow_gvg", &battle_config.duel_allow_gvg}, // [LuzZza]
+ { "duel_allow_teleport", &battle_config.duel_allow_teleport}, // [LuzZza]
+ { "duel_autoleave_when_die", &battle_config.duel_autoleave_when_die}, //[LuzZza]
+ { "duel_time_interval", &battle_config.duel_time_interval}, // [LuzZza]
+
+ { "skip_teleport_lv1_menu", &battle_config.skip_teleport_lv1_menu}, // [LuzZza]
+ { "allow_skill_without_day", &battle_config.allow_skill_without_day}, // [Komurka]
+};
+
+static const struct battle_data_int {
+ const char *str;
+ int *val;
+} battle_data_int[] = { //List here battle_athena options which are type int!
+ { "item_first_get_time", &battle_config.item_first_get_time },
+ { "item_second_get_time", &battle_config.item_second_get_time },
+ { "item_third_get_time", &battle_config.item_third_get_time },
+ { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time },
+ { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time },
+ { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time },
+ { "base_exp_rate", &battle_config.base_exp_rate },
+ { "job_exp_rate", &battle_config.job_exp_rate },
+ { "zeny_penalty", &battle_config.zeny_penalty },
+ { "mvp_exp_rate", &battle_config.mvp_exp_rate },
+ { "natural_healhp_interval", &battle_config.natural_healhp_interval },
+ { "natural_healsp_interval", &battle_config.natural_healsp_interval },
+ { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval},
+ { "max_hp", &battle_config.max_hp },
+ { "max_sp", &battle_config.max_sp },
+ { "max_cart_weight", &battle_config.max_cart_weight },
+ { "gvg_eliminate_time", &battle_config.gvg_eliminate_time },
+ { "vending_max_value", &battle_config.vending_max_value },
+// eAthena additions
+ { "item_rate_mvp", &battle_config.item_rate_mvp },
+ { "item_rate_common", &battle_config.item_rate_common }, // Added by RoVeRT
+ { "item_rate_equip", &battle_config.item_rate_equip },
+ { "item_rate_card", &battle_config.item_rate_card }, // End Addition
+ { "item_rate_heal", &battle_config.item_rate_heal }, // Added by Valaris
+ { "item_rate_use", &battle_config.item_rate_use }, // End
+ { "item_rate_treasure", &battle_config.item_rate_treasure }, // End
+ { "day_duration", &battle_config.day_duration }, // added by [Yor]
+ { "night_duration", &battle_config.night_duration }, // added by [Yor]
+ { "mob_remove_delay", &battle_config.mob_remove_delay },
+};
+
+int battle_set_value(char *w1, char *w2) {
+ int i;
+ for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++)
+ if (strcmpi(w1, battle_data_short[i].str) == 0) {
+ * battle_data_short[i].val = battle_config_switch(w2);
+ return 1;
+ }
+ for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++)
+ if (strcmpi(w1, battle_data_int[i].str) == 0) {
+ *battle_data_int[i].val = battle_config_switch(w2);
+ return 1;
+ }
+/*
+ int val = battle_config_switch(w2);
+ switch(battle_data[i].size) {
+ case 1:
+ *((unsigned char *) battle_data[i].val) = val;
+ break;
+ case 2:
+ *((unsigned short *) battle_data[i].val) = val;
+ break;
+ case 4:
+ *((unsigned int *) battle_data[i].val) = val;
+ break;
+ }
+ return 1;
+ }
+*/
+ return 0;
+}
+
+void battle_set_defaults() {
+ battle_config.warp_point_debug=0;
+ battle_config.enemy_critical_rate=0;
+ battle_config.enemy_str=1;
+ battle_config.enemy_perfect_flee=0;
+ battle_config.cast_rate=100;
+ battle_config.delay_rate=100;
+ battle_config.delay_dependon_dex=0;
+ battle_config.sdelay_attack_enable=0;
+ battle_config.left_cardfix_to_right=0;
+ battle_config.pc_skill_add_range=0;
+ battle_config.skill_out_range_consume=1;
+ battle_config.mob_skill_add_range=0;
+ battle_config.skillrange_by_distance=6;
+ battle_config.use_weapon_skill_range=0;
+ battle_config.pc_damage_delay_rate=100;
+ battle_config.defnotenemy=0;
+ battle_config.vs_traps_bctall=1;
+ battle_config.random_monster_checklv=1;
+ battle_config.attr_recover=1;
+ battle_config.flooritem_lifetime=LIFETIME_FLOORITEM*1000;
+ battle_config.item_auto_get=0;
+ battle_config.item_first_get_time=3000;
+ battle_config.item_second_get_time=1000;
+ battle_config.item_third_get_time=1000;
+ battle_config.mvp_item_first_get_time=10000;
+ battle_config.mvp_item_second_get_time=10000;
+ battle_config.mvp_item_third_get_time=2000;
+
+ battle_config.drop_rate0item=0;
+ battle_config.base_exp_rate=100;
+ battle_config.job_exp_rate=100;
+ battle_config.pvp_exp=1;
+ battle_config.gtb_pvp_only=0;
+ battle_config.death_penalty_type=0;
+ battle_config.death_penalty_base=0;
+ battle_config.death_penalty_job=0;
+ battle_config.zeny_penalty=0;
+ battle_config.restart_hp_rate=0;
+ battle_config.restart_sp_rate=0;
+ battle_config.mvp_exp_rate=100;
+ battle_config.mvp_hp_rate=100;
+ battle_config.monster_hp_rate=100;
+ battle_config.monster_max_aspd=199;
+ battle_config.atc_gmonly=0;
+ battle_config.atc_spawn_quantity_limit=0;
+ battle_config.atc_slave_clone_limit=0;
+ battle_config.gm_allskill=0;
+ battle_config.gm_allequip=0;
+ battle_config.gm_skilluncond=0;
+ battle_config.gm_join_chat=0;
+ battle_config.gm_kick_chat=0;
+ battle_config.guild_max_castles=0;
+ battle_config.skillfree = 0;
+ battle_config.skillup_limit = 0;
+ battle_config.wp_rate=100;
+ battle_config.pp_rate=100;
+ battle_config.monster_active_enable=1;
+ battle_config.monster_damage_delay_rate=100;
+ battle_config.monster_loot_type=0;
+// battle_config.mob_skill_use=1;
+ battle_config.mob_skill_rate=100;
+ battle_config.mob_skill_delay=100;
+ battle_config.mob_count_rate=100;
+ battle_config.mob_spawn_delay=100;
+ battle_config.no_spawn_on_player=0;
+ battle_config.plant_spawn_delay=100;
+ battle_config.boss_spawn_delay=100;
+ battle_config.slaves_inherit_speed=1;
+ battle_config.summons_inherit_effects=1;
+ battle_config.pc_walk_delay_rate=20;
+ battle_config.walk_delay_rate=100;
+ battle_config.multihit_delay=230;
+ battle_config.quest_skill_learn=0;
+ battle_config.quest_skill_reset=1;
+ battle_config.basic_skill_check=1;
+ battle_config.guild_emperium_check=1;
+ battle_config.guild_exp_limit=50;
+ battle_config.guild_exp_rate=100;
+ battle_config.pc_invincible_time = 5000;
+ battle_config.pet_catch_rate=100;
+ battle_config.pet_rename=0;
+ battle_config.pet_friendly_rate=100;
+ battle_config.pet_hungry_delay_rate=100;
+ battle_config.pet_hungry_friendly_decrease=5;
+ battle_config.pet_str=1;
+ battle_config.pet_status_support=0;
+ battle_config.pet_attack_support=0;
+ battle_config.pet_damage_support=0;
+ battle_config.pet_support_min_friendly=900;
+ battle_config.pet_support_rate=100;
+ battle_config.pet_attack_exp_to_master=0;
+ battle_config.pet_attack_exp_rate=100;
+ battle_config.pet_lv_rate=0; //Skotlex
+ battle_config.pet_max_stats=99; //Skotlex
+ battle_config.pet_max_atk1=750; //Skotlex
+ battle_config.pet_max_atk2=1000; //Skotlex
+ battle_config.pet_no_gvg=0; //Skotlex
+ battle_config.skill_min_damage=6; //Ishizu claims that magic and misc attacks always do at least div_ damage. [Skotlex]
+ battle_config.finger_offensive_type=0;
+ battle_config.heal_exp=0;
+ battle_config.resurrection_exp=0;
+ battle_config.shop_exp=0;
+ battle_config.combo_delay_rate=100;
+ battle_config.item_check=1;
+ battle_config.item_use_interval=500;
+ battle_config.wedding_modifydisplay=0;
+ battle_config.wedding_ignorepalette=0;
+ battle_config.xmas_ignorepalette=0; // [Valaris]
+ battle_config.natural_healhp_interval=6000;
+ battle_config.natural_healsp_interval=8000;
+ battle_config.natural_heal_skill_interval=10000;
+ battle_config.natural_heal_weight_rate=50;
+ battle_config.item_name_override_grffile=1;
+ battle_config.item_equip_override_grffile=0; // [Celest]
+ battle_config.item_slots_override_grffile=0; // [Celest]
+ battle_config.indoors_override_grffile=0; // [Celest]
+ battle_config.skill_sp_override_grffile=0; // [Celest]
+ battle_config.cardillust_read_grffile=0; // [Celest]
+ battle_config.arrow_decrement=1;
+ battle_config.max_aspd = 199;
+ battle_config.max_walk_speed = 300;
+ battle_config.max_hp = 32500;
+ battle_config.max_sp = 32500;
+ battle_config.max_lv = 99; // [MouseJstr]
+ battle_config.aura_lv = 99; // [Skotlex]
+ battle_config.max_parameter = 99;
+ battle_config.max_baby_parameter = 80;
+ battle_config.max_cart_weight = 8000;
+ battle_config.max_def = 99; // [Skotlex]
+ battle_config.over_def_bonus = 0; // [Skotlex]
+ battle_config.pc_skill_log = 0;
+ battle_config.mob_skill_log = 0;
+ battle_config.battle_log = 0;
+ battle_config.save_log = 0;
+ battle_config.error_log = 1;
+ battle_config.etc_log = 1;
+ battle_config.save_clothcolor = 0;
+ battle_config.undead_detect_type = 0;
+ battle_config.pc_auto_counter_type = 1;
+ battle_config.monster_auto_counter_type = 1;
+ battle_config.min_hitrate = 5;
+ battle_config.max_hitrate = 95;
+ battle_config.agi_penalty_type = 1;
+ battle_config.agi_penalty_count = 3;
+ battle_config.agi_penalty_num = 10;
+ battle_config.agi_penalty_count_lv = ATK_FLEE;
+ battle_config.vit_penalty_type = 1;
+ battle_config.vit_penalty_count = 3;
+ battle_config.vit_penalty_num = 5;
+ battle_config.vit_penalty_count_lv = ATK_DEF;
+ battle_config.player_defense_type = 0;
+ battle_config.monster_defense_type = 0;
+ battle_config.pet_defense_type = 0;
+ battle_config.magic_defense_type = 0;
+ battle_config.pc_skill_reiteration = 0;
+ battle_config.monster_skill_reiteration = 0;
+ battle_config.pc_skill_nofootset = 0;
+ battle_config.monster_skill_nofootset = 0;
+ battle_config.pc_cloak_check_type = 1;
+ battle_config.monster_cloak_check_type = 0;
+ battle_config.estimation_type = 3;
+ battle_config.gvg_short_damage_rate = 100;
+ battle_config.gvg_long_damage_rate = 75;
+ battle_config.gvg_weapon_damage_rate = 60;
+ battle_config.gvg_magic_damage_rate = 50;
+ battle_config.gvg_misc_damage_rate = 60;
+ battle_config.gvg_flee_penalty = 20;
+ battle_config.gvg_eliminate_time = 7000;
+ battle_config.mob_changetarget_byskill = 0;
+ battle_config.pc_attack_direction_change = 1;
+ battle_config.monster_attack_direction_change = 1;
+ battle_config.pc_land_skill_limit = 1;
+ battle_config.monster_land_skill_limit = 1;
+ battle_config.party_skill_penalty = 1;
+ battle_config.monster_class_change_full_recover = 0;
+ battle_config.produce_item_name_input = 1;
+ battle_config.produce_potion_name_input = 1;
+ battle_config.making_arrow_name_input = 1;
+ battle_config.holywater_name_input = 1;
+ battle_config.cdp_name_input = 1;
+ battle_config.display_delay_skill_fail = 1;
+ battle_config.display_snatcher_skill_fail = 1;
+ battle_config.chat_warpportal = 0;
+ battle_config.mob_warpportal = 0;
+ battle_config.dead_branch_active = 0;
+ battle_config.vending_max_value = 10000000;
+ battle_config.show_steal_in_same_party = 0;
+ battle_config.party_share_type = 0;
+ battle_config.party_show_share_picker = 0;
+ battle_config.pet_attack_attr_none = 0;
+ battle_config.pc_attack_attr_none = 0;
+ battle_config.mob_attack_attr_none = 0;
+ battle_config.mob_ghostring_fix = 1;
+ battle_config.gx_allhit = 1;
+ battle_config.gx_disptype = 1;
+ battle_config.devotion_level_difference = 10;
+ battle_config.player_skill_partner_check = 1;
+ battle_config.hide_GM_session = 0;
+ battle_config.invite_request_check = 1;
+ battle_config.skill_removetrap_type = 0;
+ battle_config.disp_experience = 0;
+ battle_config.disp_zeny = 0;
+ battle_config.castle_defense_rate = 100;
+ battle_config.hp_rate = 100;
+ battle_config.sp_rate = 100;
+ battle_config.gm_cant_drop_min_lv = 1;
+ battle_config.gm_cant_drop_max_lv = 0;
+ battle_config.disp_hpmeter = 60;
+ battle_config.skill_wall_check = 0;
+ battle_config.cell_stack_limit = 1;
+ battle_config.bone_drop = 0;
+ battle_config.buyer_name = 1;
+
+// eAthena additions
+ battle_config.item_rate_mvp=100;
+ battle_config.item_rate_common = 100;
+ battle_config.item_rate_equip = 100;
+ battle_config.item_rate_card = 100;
+ battle_config.item_rate_heal = 100; // Added by Valaris
+ battle_config.item_rate_use = 100; // End
+ battle_config.item_rate_treasure = 100;
+ battle_config.logarithmic_drops = 0;
+ battle_config.item_drop_common_min=1; // Added by TyrNemesis^
+ battle_config.item_drop_common_max=10000;
+ battle_config.item_drop_equip_min=1;
+ battle_config.item_drop_equip_max=10000;
+ battle_config.item_drop_card_min=1;
+ battle_config.item_drop_card_max=10000;
+ battle_config.item_drop_mvp_min=1;
+ battle_config.item_drop_mvp_max=10000; // End Addition
+ battle_config.item_drop_heal_min=1; // Added by Valaris
+ battle_config.item_drop_heal_max=10000;
+ battle_config.item_drop_use_min=1;
+ battle_config.item_drop_use_max=10000; // End
+ battle_config.item_drop_treasure_min=1;
+ battle_config.item_drop_treasure_max=10000;
+ battle_config.prevent_logout = 10000; // Added by RoVeRT
+ battle_config.max_base_level = 255; // Added by Valaris
+ battle_config.max_job_level = 50;
+ battle_config.max_adv_level = 70;
+ battle_config.max_sn_level = 99;
+ battle_config.drops_by_luk = 0; // [Valaris]
+ battle_config.drops_by_luk2 = 0;
+ battle_config.equip_natural_break_rate = 1;
+ battle_config.equip_self_break_rate = 100; // [Valaris], adapted by [Skotlex]
+ battle_config.equip_skill_break_rate = 100; // [Valaris], adapted by [Skotlex]
+ battle_config.pk_mode = 0; // [Valaris]
+ battle_config.manner_system = 1; // [Valaris]
+ battle_config.pet_equip_required = 0; // [Valaris]
+ battle_config.multi_level_up = 0; // [Valaris]
+ battle_config.backstab_bow_penalty = 0; // Akaru
+ battle_config.night_at_start = 0; // added by [Yor]
+ battle_config.day_duration = 2*60*60*1000; // added by [Yor] (2 hours)
+ battle_config.night_duration = 30*60*1000; // added by [Yor] (30 minutes)
+ battle_config.show_mob_hp = 0; // [Valaris]
+ battle_config.ban_spoof_namer = 5; // added by [Yor] (default: 5 minutes)
+ battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level)
+ battle_config.any_warp_GM_min_level = 20; // added by [Yor]
+ battle_config.packet_ver_flag = 1023; // added by [Yor]
+ battle_config.min_hair_style = 0;
+ battle_config.max_hair_style = 23;
+ battle_config.min_hair_color = 0;
+ battle_config.max_hair_color = 9;
+ battle_config.min_cloth_color = 0;
+ battle_config.max_cloth_color = 4;
+ battle_config.pet_hair_style = 100;
+ battle_config.zeny_from_mobs = 0;
+ battle_config.mobs_level_up = 0; // [Valaris]
+ battle_config.mobs_level_up_exp_rate = 1; // [Valaris]
+ battle_config.pk_min_level = 55;
+ battle_config.skill_steal_type = 1;
+ battle_config.skill_steal_rate = 100;
+// battle_config.night_darkness_level = 9;
+ battle_config.motd_type = 0;
+ battle_config.allow_atcommand_when_mute = 0;
+ battle_config.finding_ore_rate = 100;
+ battle_config.castrate_dex_scale = 150;
+ battle_config.area_size = 14;
+ battle_config.exp_calc_type = 1;
+ battle_config.min_skill_delay_limit = 100;
+ battle_config.require_glory_guild = 0;
+ battle_config.idle_no_share = 0;
+ battle_config.party_even_share_bonus = 0;
+ battle_config.delay_battle_damage = 1;
+ battle_config.display_version = 1;
+ battle_config.who_display_aid = 0;
+ battle_config.display_hallucination = 1;
+ battle_config.ignore_items_gender = 1;
+ battle_config.use_statpoint_table = 1;
+ battle_config.mob_ai = 0;
+ battle_config.dynamic_mobs = 1; // use Dynamic Mobs [Wizputer]
+ battle_config.mob_remove_damaged = 1; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
+ battle_config.mob_remove_delay = 60000;
+ battle_config.show_hp_sp_drain = 0; //Display drained hp/sp from attacks
+ battle_config.show_hp_sp_gain = 1; //Display gained hp/sp from mob-kills
+ battle_config.mob_npc_event_type = 1; //Execute npc-event on player that delivered final blow.
+ battle_config.mob_clear_delay = 0;
+ battle_config.character_size = 3; //3: Peco riders Size=2, Baby Class Riders Size=1 [Lupus]
+ battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; //max possible level of monsters skills [Lupus]
+ battle_config.retaliate_to_master = 1; //Make mobs retaliate against the master rather than the mob that attacked them. [Skotlex]
+ battle_config.rare_drop_announce = 1; //show global announces for rare items drops (<= 0.01% chance) [Lupus]
+ battle_config.firewall_hits_on_undead = 1;
+ battle_config.title_lvl1 = 1; //Players Titles for @who, etc commands [Lupus]
+ battle_config.title_lvl2 = 10;
+ battle_config.title_lvl3 = 20;
+ battle_config.title_lvl4 = 40;
+ battle_config.title_lvl5 = 50;
+ battle_config.title_lvl6 = 60;
+ battle_config.title_lvl7 = 80;
+ battle_config.title_lvl8 = 99;
+
+ battle_config.duel_enable = 1;
+ battle_config.duel_allow_pvp = 0;
+ battle_config.duel_allow_pvp = 0;
+ battle_config.duel_allow_teleport = 0;
+ battle_config.duel_autoleave_when_die = 1;
+ battle_config.duel_time_interval = 60;
+
+ battle_config.skip_teleport_lv1_menu = 0;
+ battle_config.allow_skill_without_day = 0;
+}
+
+void battle_validate_conf() {
+ if(battle_config.flooritem_lifetime < 1000)
+ battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000;
+/* if(battle_config.restart_hp_rate < 0)
+ battle_config.restart_hp_rate = 0;
+ else*/ if(battle_config.restart_hp_rate > 100)
+ battle_config.restart_hp_rate = 100;
+/* if(battle_config.restart_sp_rate < 0)
+ battle_config.restart_sp_rate = 0;
+ else*/ if(battle_config.restart_sp_rate > 100)
+ battle_config.restart_sp_rate = 100;
+ if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_weight_rate < 50)
+ battle_config.natural_heal_weight_rate = 50;
+ if(battle_config.natural_heal_weight_rate > 101)
+ battle_config.natural_heal_weight_rate = 101;
+ battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10;
+ if(battle_config.monster_max_aspd < 10)
+ battle_config.monster_max_aspd = 10;
+ if(battle_config.monster_max_aspd > 1000)
+ battle_config.monster_max_aspd = 1000;
+ battle_config.max_aspd = 2000 - battle_config.max_aspd*10;
+ if(battle_config.max_aspd < 10)
+ battle_config.max_aspd = 10;
+ if(battle_config.max_aspd > 1000)
+ battle_config.max_aspd = 1000;
+
+ if (battle_config.max_walk_speed < 100)
+ battle_config.max_walk_speed = 100;
+ battle_config.max_walk_speed = 100*DEFAULT_WALK_SPEED/battle_config.max_walk_speed;
+ if (battle_config.max_walk_speed < 1)
+ battle_config.max_walk_speed = 1;
+
+ if(battle_config.hp_rate < 1)
+ battle_config.hp_rate = 1;
+ if(battle_config.sp_rate < 1)
+ battle_config.sp_rate = 1;
+ if(battle_config.max_hp > 1000000000)
+ battle_config.max_hp = 1000000000;
+ if(battle_config.max_hp < 100)
+ battle_config.max_hp = 100;
+ if(battle_config.max_sp > 1000000000)
+ battle_config.max_sp = 1000000000;
+ if(battle_config.max_sp < 100)
+ battle_config.max_sp = 100;
+ if(battle_config.max_parameter < 10)
+ battle_config.max_parameter = 10;
+ if(battle_config.max_parameter > 10000)
+ battle_config.max_parameter = 10000;
+ if(battle_config.max_baby_parameter < 10)
+ battle_config.max_baby_parameter = 10;
+ if(battle_config.max_baby_parameter > 10000)
+ battle_config.max_baby_parameter = 10000;
+ if(battle_config.max_cart_weight > 1000000)
+ battle_config.max_cart_weight = 1000000;
+ if(battle_config.max_cart_weight < 100)
+ battle_config.max_cart_weight = 100;
+ battle_config.max_cart_weight *= 10;
+
+ if(battle_config.max_def > 100 && !battle_config.player_defense_type) // added by [Skotlex]
+ battle_config.max_def = 100;
+ if(battle_config.over_def_bonus > 1000)
+ battle_config.over_def_bonus = 1000;
+
+ if(battle_config.min_hitrate > battle_config.max_hitrate)
+ battle_config.min_hitrate = battle_config.max_hitrate;
+
+ if(battle_config.agi_penalty_count < 2)
+ battle_config.agi_penalty_count = 2;
+ if(battle_config.vit_penalty_count < 2)
+ battle_config.vit_penalty_count = 2;
+
+ if(battle_config.guild_exp_limit > 99)
+ battle_config.guild_exp_limit = 99;
+/* if(battle_config.guild_exp_limit < 0)
+ battle_config.guild_exp_limit = 0;*/
+
+ if(battle_config.pet_support_min_friendly > 950) //Capped to 950/1000 [Skotlex]
+ battle_config.pet_support_min_friendly = 950;
+
+ if(battle_config.pet_max_atk1 > battle_config.pet_max_atk2) //Skotlex
+ battle_config.pet_max_atk1 = battle_config.pet_max_atk2;
+
+// if(battle_config.castle_defense_rate < 0)
+// battle_config.castle_defense_rate = 0;
+ if(battle_config.castle_defense_rate > 100)
+ battle_config.castle_defense_rate = 100;
+ if(battle_config.item_drop_common_min < 1) // Added by TyrNemesis^
+ battle_config.item_drop_common_min = 1;
+ if(battle_config.item_drop_common_max > 10000)
+ battle_config.item_drop_common_max = 10000;
+ if(battle_config.item_drop_equip_min < 1)
+ battle_config.item_drop_equip_min = 1;
+ if(battle_config.item_drop_equip_max > 10000)
+ battle_config.item_drop_equip_max = 10000;
+ if(battle_config.item_drop_card_min < 1)
+ battle_config.item_drop_card_min = 1;
+ if(battle_config.item_drop_card_max > 10000)
+ battle_config.item_drop_card_max = 10000;
+ if(battle_config.item_drop_mvp_min < 1)
+ battle_config.item_drop_mvp_min = 1;
+ if(battle_config.item_drop_mvp_max > 10000)
+ battle_config.item_drop_mvp_max = 10000; // End Addition
+
+/* if (battle_config.night_at_start < 0) // added by [Yor]
+ battle_config.night_at_start = 0;
+ else if (battle_config.night_at_start > 1) // added by [Yor]
+ battle_config.night_at_start = 1; */
+ if (battle_config.day_duration != 0 && battle_config.day_duration < 60000) // added by [Yor]
+ battle_config.day_duration = 60000;
+ if (battle_config.night_duration != 0 && battle_config.night_duration < 60000) // added by [Yor]
+ battle_config.night_duration = 60000;
+
+/* if (battle_config.ban_spoof_namer < 0) // added by [Yor]
+ battle_config.ban_spoof_namer = 0;
+ else*/ if (battle_config.ban_spoof_namer > 32767)
+ battle_config.ban_spoof_namer = 32767;
+
+/* if (battle_config.hack_info_GM_level < 0) // added by [Yor]
+ battle_config.hack_info_GM_level = 0;
+ else*/ if (battle_config.hack_info_GM_level > 100)
+ battle_config.hack_info_GM_level = 100;
+
+/* if (battle_config.any_warp_GM_min_level < 0) // added by [Yor]
+ battle_config.any_warp_GM_min_level = 0;
+ else*/ if (battle_config.any_warp_GM_min_level > 100)
+ battle_config.any_warp_GM_min_level = 100;
+
+/* //This is a hassle to keep updated each time there's a new limit to packet_ver_flag.... [Skotlex]
+ // at least 1 client must be accepted
+ if ((battle_config.packet_ver_flag & 255) == 0) // added by [Yor]
+ battle_config.packet_ver_flag = 255; // accept all clients
+*/
+/* Deprecated by dynamix's new night system (using SI_NIGHT)
+ if (battle_config.night_darkness_level <= 0)
+ battle_config.night_darkness_level = 9;
+ else if (battle_config.night_darkness_level > 10) // Celest
+ battle_config.night_darkness_level = 10;
+*/
+/* if (battle_config.motd_type < 0)
+ battle_config.motd_type = 0;
+ else if (battle_config.motd_type > 1)
+ battle_config.motd_type = 1;
+*/
+// if (battle_config.finding_ore_rate < 0)
+// battle_config.finding_ore_rate = 0;
+
+ if (battle_config.vending_max_value > MAX_ZENY || battle_config.vending_max_value==0)
+ battle_config.vending_max_value = MAX_ZENY;
+
+ if (battle_config.min_skill_delay_limit < 10)
+ battle_config.min_skill_delay_limit = 10; // minimum delay of 10ms
+
+ //Spawn delays [Skotlex]
+/* if (battle_config.mob_spawn_delay < 0)
+ battle_config.mob_spawn_delay = 0;
+ if (battle_config.boss_spawn_delay < 0)
+ battle_config.boss_spawn_delay = 0;
+ if (battle_config.plant_spawn_delay < 0)
+ battle_config.plant_spawn_delay = 0;
+*/
+ if (battle_config.no_spawn_on_player > 50)
+ battle_config.no_spawn_on_player = 50;
+ if (battle_config.mob_remove_delay < 15000) //Min 15 sec
+ battle_config.mob_remove_delay = 15000;
+ if (battle_config.dynamic_mobs > 1)
+ battle_config.dynamic_mobs = 1; //The flag will be used in assignations
+ if (battle_config.mob_max_skilllvl> 11 || battle_config.mob_max_skilllvl<1 )
+ battle_config.mob_max_skilllvl = 11;
+
+ if (battle_config.firewall_hits_on_undead < 1)
+ battle_config.firewall_hits_on_undead = 1;
+ else if (battle_config.firewall_hits_on_undead > 255) //The flag passed to battle_calc_damage is limited to 0xff
+ battle_config.firewall_hits_on_undead = 255;
+
+ if (battle_config.prevent_logout > 60000)
+ battle_config.prevent_logout = 60000;
+
+ if (battle_config.mobs_level_up_exp_rate < 1) // [Valaris]
+ battle_config.mobs_level_up_exp_rate = 1;
+
+#ifdef CELL_NOSTACK
+ if (battle_config.cell_stack_limit < 1)
+ battle_config.cell_stack_limit = 1;
+ else
+ if (battle_config.cell_stack_limit > 255)
+ battle_config.cell_stack_limit = 255;
+#else
+ if (battle_config.cell_stack_limit != 1)
+ ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");
+#endif
+}
+
+/*==========================================
+ * ?Ý’èƒtƒ@ƒCƒ‹‚ð“Ç‚Ý?ž‚Þ
+ *------------------------------------------
+ */
+int battle_config_read(const char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ static int count = 0;
+
+ if ((count++) == 0)
+ battle_set_defaults();
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]:%s", w1, w2) != 2)
+ continue;
+ if (strcmpi(w1, "import") == 0)
+ battle_config_read(w2);
+ else
+ battle_set_value(w1, w2);
+ }
+ fclose(fp);
+
+ if (--count == 0) {
+ battle_validate_conf();
+ add_timer_func_list(battle_delay_damage_sub, "battle_delay_damage_sub");
+ add_timer_func_list(battle_walkdelay_sub, "battle_walkdelay_sub");
+ }
+
+ return 0;
+}
diff --git a/src/map/battle.h b/src/map/battle.h
new file mode 100644
index 000000000..175aa49c4
--- /dev/null
+++ b/src/map/battle.h
@@ -0,0 +1,428 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _BATTLE_H_
+#define _BATTLE_H_
+
+// ƒ_ƒ[ƒW
+struct Damage {
+ int damage,damage2;
+ int type,div_;
+ int amotion,dmotion;
+ int blewcount;
+ int flag;
+ int dmg_lv; //ˆÍ‚܂ꌸŽZŒvŽZ—p@0:ƒXƒLƒ‹UŒ‚ ATK_LUCKY,ATK_FLEE,ATK_DEF
+};
+
+// ‘®«•\i“Ç‚Ýž‚Ý‚Ípc.cAbattle_attr_fix‚ÅŽg—pj
+extern int attr_fix_table[4][10][10];
+
+struct map_session_data;
+struct mob_data;
+struct block_list;
+
+// ƒ_ƒ[ƒWŒvŽZ
+
+struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag);
+
+int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_elem);
+
+// ƒ_ƒ[ƒWÅIŒvŽZ
+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 { // ÅIŒvŽZ‚̃tƒ‰ƒO
+ BF_WEAPON = 0x0001,
+ BF_MAGIC = 0x0002,
+ BF_MISC = 0x0004,
+ BF_SHORT = 0x0010,
+ BF_LONG = 0x0040,
+ BF_SKILL = 0x0100,
+ BF_NORMAL = 0x0200,
+ BF_WEAPONMASK=0x000f,
+ BF_RANGEMASK= 0x00f0,
+ BF_SKILLMASK= 0x0f00,
+};
+
+// ŽÀÛ‚ÉHP‚𑌸
+int battle_walkdelay(struct block_list *bl, unsigned int tick, int adelay, int delay, int div_); //Calcs walk delay based on attack type. [Skotlex]
+int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int flag);
+int battle_damage(struct block_list *bl,struct block_list *target,int damage,int flag);
+int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag);
+
+// UŒ‚‚âˆÚ“®‚ðŽ~‚ß‚é
+int battle_stopattack(struct block_list *bl);
+int battle_iswalking(struct block_list *bl);
+int battle_stopwalking(struct block_list *bl,int type);
+
+// ’ÊíUŒ‚ˆ—‚Ü‚Æ‚ß
+int battle_weapon_attack( struct block_list *bl,struct block_list *target,
+ unsigned int tick,int flag);
+
+// ŠeŽíƒpƒ‰ƒ[ƒ^‚𓾂é
+int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv);
+struct block_list* battle_gettargeted(struct block_list *target);
+int battle_gettarget(struct block_list *bl);
+int battle_getcurrentskill(struct block_list *bl);
+
+//New definitions [Skotlex]
+#define BCT_ENEMY 0x020000
+//This should be (~BCT_ENEMY&BCT_ALL)
+#define BCT_NOENEMY 0x1d0000
+#define BCT_PARTY 0x040000
+//This should be (~BCT_PARTY&BCT_ALL)
+#define BCT_NOPARTY 0x1b0000
+#define BCT_GUILD 0x080000
+//This should be (~BCT_GUILD&BCT_ALL)
+#define BCT_NOGUILD 0x170000
+#define BCT_ALL 0x1f0000
+#define BCT_NOONE 0x000000
+#define BCT_SELF 0x010000
+#define BCT_NEUTRAL 0x100000
+/*
+enum {
+ BCT_NOENEMY =0x00000,
+ BCT_PARTY =0x10000,
+ BCT_ENEMY =0x40000,
+ BCT_NOPARTY =0x50000,
+ BCT_ALL =0x20000,
+ BCT_NOONE =0x60000,
+ BCT_SELF =0x60000,
+};
+*/
+int battle_check_undead(int race,int element);
+int battle_check_target(struct block_list *src, struct block_list *target,int flag);
+int battle_check_range(struct block_list *src,struct block_list *bl,int range);
+
+// Ý’è
+
+int battle_config_switch(const char *str); // [Valaris]
+
+extern struct Battle_Config {
+ unsigned short warp_point_debug;
+ unsigned short enemy_critical_rate;
+ unsigned short enemy_str;
+ unsigned short enemy_perfect_flee;
+ unsigned short cast_rate,delay_rate,delay_dependon_dex;
+ unsigned short sdelay_attack_enable;
+ unsigned short left_cardfix_to_right;
+ unsigned short pc_skill_add_range;
+ unsigned short skill_out_range_consume;
+ unsigned short mob_skill_add_range;
+ unsigned short skillrange_by_distance; //[Skotlex]
+ unsigned short use_weapon_skill_range; //[Skotlex]
+ unsigned short pc_damage_delay_rate;
+ unsigned short defnotenemy;
+ unsigned short vs_traps_bctall;
+ unsigned short random_monster_checklv;
+ unsigned short attr_recover;
+ unsigned short flooritem_lifetime;
+ unsigned short item_auto_get;
+ int item_first_get_time;
+ int item_second_get_time;
+ int item_third_get_time;
+ int mvp_item_first_get_time;
+ int mvp_item_second_get_time;
+ int mvp_item_third_get_time;
+ int base_exp_rate,job_exp_rate;
+ unsigned short drop_rate0item;
+ unsigned short death_penalty_type;
+ unsigned short death_penalty_base,death_penalty_job;
+ unsigned short pvp_exp; // [MouseJstr]
+ unsigned short gtb_pvp_only; // [MouseJstr]
+ int zeny_penalty;
+ unsigned short restart_hp_rate;
+ unsigned short restart_sp_rate;
+ int mvp_exp_rate;
+ unsigned short mvp_hp_rate;
+ unsigned short monster_hp_rate;
+ unsigned short monster_max_aspd;
+ unsigned short atc_gmonly;
+ unsigned short atc_spawn_quantity_limit;
+ unsigned short atc_slave_clone_limit;
+ unsigned short gm_allskill;
+ unsigned short gm_allskill_addabra;
+ unsigned short gm_allequip;
+ unsigned short gm_skilluncond;
+ unsigned short gm_join_chat;
+ unsigned short gm_kick_chat;
+ unsigned short skillfree;
+ unsigned short skillup_limit;
+ unsigned short wp_rate;
+ unsigned short pp_rate;
+ unsigned short monster_active_enable;
+ unsigned short monster_damage_delay_rate;
+ unsigned short monster_loot_type;
+ unsigned short mob_skill_rate; //[Skotlex]
+ unsigned short mob_skill_delay; //[Skotlex]
+ unsigned short mob_count_rate;
+ unsigned short no_spawn_on_player; //[Skotlex]
+ unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
+ unsigned short slaves_inherit_speed;
+ unsigned short summons_inherit_effects;
+ unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
+ unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
+ unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
+ unsigned short quest_skill_learn;
+ unsigned short quest_skill_reset;
+ unsigned short basic_skill_check;
+ unsigned short guild_emperium_check;
+ unsigned short guild_exp_rate; //[Skotlex]
+ unsigned short guild_exp_limit;
+ unsigned short guild_max_castles;
+ unsigned short pc_invincible_time;
+ unsigned short pet_catch_rate;
+ unsigned short pet_rename;
+ unsigned short pet_friendly_rate;
+ unsigned short pet_hungry_delay_rate;
+ unsigned short pet_hungry_friendly_decrease;
+ unsigned short pet_str;
+ unsigned short pet_status_support;
+ unsigned short pet_attack_support;
+ unsigned short pet_damage_support;
+ unsigned short pet_support_min_friendly; //[Skotlex]
+ unsigned short pet_support_rate;
+ unsigned short pet_attack_exp_to_master;
+ unsigned short pet_attack_exp_rate;
+ unsigned short pet_lv_rate; //[Skotlex]
+ unsigned short pet_max_stats; //[Skotlex]
+ unsigned short pet_max_atk1; //[Skotlex]
+ unsigned short pet_max_atk2; //[Skotlex]
+ unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex]
+ unsigned short skill_min_damage;
+ unsigned short finger_offensive_type;
+ unsigned short heal_exp;
+ unsigned short resurrection_exp;
+ unsigned short shop_exp;
+ unsigned short combo_delay_rate;
+ unsigned short item_check;
+ unsigned short item_use_interval; //[Skotlex]
+ unsigned short wedding_modifydisplay;
+ unsigned short wedding_ignorepalette; //[Skotlex]
+ unsigned short xmas_ignorepalette; // [Valaris]
+ int natural_healhp_interval;
+ int natural_healsp_interval;
+ int natural_heal_skill_interval;
+ unsigned short natural_heal_weight_rate;
+ unsigned short item_name_override_grffile;
+ unsigned short indoors_override_grffile; // [Celest]
+ unsigned short skill_sp_override_grffile; // [Celest]
+ unsigned short cardillust_read_grffile;
+ unsigned short item_equip_override_grffile;
+ unsigned short item_slots_override_grffile;
+ unsigned short arrow_decrement;
+ unsigned short max_aspd;
+ unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex]
+ int max_hp;
+ int max_sp;
+ unsigned short max_lv, aura_lv;
+ unsigned short max_parameter, max_baby_parameter;
+ int max_cart_weight;
+ unsigned short pc_skill_log;
+ unsigned short mob_skill_log;
+ unsigned short battle_log;
+ unsigned short save_log;
+ unsigned short error_log;
+ unsigned short etc_log;
+ unsigned short save_clothcolor;
+ unsigned short undead_detect_type;
+ unsigned short pc_auto_counter_type;
+ unsigned short monster_auto_counter_type;
+ unsigned short min_hitrate; //[Skotlex]
+ unsigned short max_hitrate; //[Skotlex]
+ unsigned short agi_penalty_type;
+ unsigned short agi_penalty_count;
+ unsigned short agi_penalty_num;
+ unsigned short vit_penalty_type;
+ unsigned short vit_penalty_count;
+ unsigned short vit_penalty_num;
+ unsigned short player_defense_type;
+ unsigned short monster_defense_type;
+ unsigned short pet_defense_type;
+ unsigned short magic_defense_type;
+ unsigned short pc_skill_reiteration;
+ unsigned short monster_skill_reiteration;
+ unsigned short pc_skill_nofootset;
+ unsigned short monster_skill_nofootset;
+ unsigned short pc_cloak_check_type;
+ unsigned short monster_cloak_check_type;
+ unsigned short estimation_type;
+ unsigned short gvg_short_damage_rate;
+ unsigned short gvg_long_damage_rate;
+ unsigned short gvg_weapon_damage_rate;
+ unsigned short gvg_magic_damage_rate;
+ unsigned short gvg_misc_damage_rate;
+ unsigned short gvg_flee_penalty;
+ int gvg_eliminate_time;
+ unsigned short mob_changetarget_byskill;
+ unsigned short pc_attack_direction_change;
+ unsigned short monster_attack_direction_change;
+ unsigned short pc_land_skill_limit;
+ unsigned short monster_land_skill_limit;
+ unsigned short party_skill_penalty;
+ unsigned short monster_class_change_full_recover;
+ unsigned short produce_item_name_input;
+ unsigned short produce_potion_name_input;
+ unsigned short making_arrow_name_input;
+ unsigned short holywater_name_input;
+ unsigned short cdp_name_input;
+ unsigned short display_delay_skill_fail;
+ unsigned short display_snatcher_skill_fail;
+ unsigned short chat_warpportal;
+ unsigned short mob_warpportal;
+ unsigned short dead_branch_active;
+ unsigned int vending_max_value;
+ unsigned short show_steal_in_same_party;
+ unsigned short party_share_type;
+ unsigned short party_show_share_picker;
+ unsigned short pet_attack_attr_none;
+ unsigned short mob_attack_attr_none;
+ unsigned short mob_ghostring_fix;
+ unsigned short pc_attack_attr_none;
+ int item_rate_mvp, item_rate_common,item_rate_card,item_rate_equip,
+ item_rate_heal, item_rate_use, item_rate_treasure; // Added by RoVeRT, Additional Heal and Usable item rate by Val
+
+ unsigned short logarithmic_drops;
+ unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^
+ unsigned short item_drop_card_min,item_drop_card_max;
+ unsigned short item_drop_equip_min,item_drop_equip_max;
+ unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition
+ unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris
+ unsigned short item_drop_use_min,item_drop_use_max; //End
+ unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex]
+
+ unsigned short prevent_logout; // Added by RoVeRT
+
+ unsigned short alchemist_summon_reward; // [Valaris]
+ unsigned short max_base_level; //Max Base Level [Valaris]
+ unsigned short max_job_level; //Max job level (normal classes) [Skotlex]
+ unsigned short max_sn_level; //Max job level (super novice) [Skotlex]
+ unsigned short max_adv_level; //Max job level (advanced classes) [Skotlex]
+ unsigned short drops_by_luk;
+ unsigned short drops_by_luk2;
+ unsigned short equip_natural_break_rate; //Base Natural break rate for attacks.
+ unsigned short equip_self_break_rate; //Natural & Penalty skills break rate
+ unsigned short equip_skill_break_rate; //Offensive skills break rate
+ unsigned short pet_equip_required;
+ unsigned short multi_level_up;
+ unsigned short pk_mode;
+ unsigned short manner_system;
+ unsigned short show_mob_hp; // end additions [Valaris]
+
+ unsigned short agi_penalty_count_lv;
+ unsigned short vit_penalty_count_lv;
+
+ unsigned short gx_allhit;
+ unsigned short gx_disptype;
+ unsigned short devotion_level_difference;
+ unsigned short player_skill_partner_check;
+ unsigned short hide_GM_session;
+ unsigned short invite_request_check;
+ unsigned short skill_removetrap_type;
+ unsigned short disp_experience;
+ unsigned short disp_zeny;
+ unsigned short castle_defense_rate;
+ unsigned short backstab_bow_penalty;
+ unsigned short hp_rate;
+ unsigned short sp_rate;
+ unsigned short gm_cant_drop_min_lv;
+ unsigned short gm_cant_drop_max_lv;
+ unsigned short disp_hpmeter;
+ unsigned short bone_drop;
+ unsigned short buyer_name;
+
+// eAthena additions
+ unsigned short night_at_start; // added by [Yor]
+ int day_duration; // added by [Yor]
+ int night_duration; // added by [Yor]
+ unsigned short ban_spoof_namer; // added by [Yor]
+ short ban_hack_trade; // added by [Yor]
+ unsigned short hack_info_GM_level; // added by [Yor]
+ unsigned short any_warp_GM_min_level; // added by [Yor]
+ unsigned short packet_ver_flag; // added by [Yor]
+ unsigned short muting_players; // added by [PoW]
+
+ unsigned short min_hair_style; // added by [MouseJstr]
+ unsigned short max_hair_style; // added by [MouseJstr]
+ unsigned short min_hair_color; // added by [MouseJstr]
+ unsigned short max_hair_color; // added by [MouseJstr]
+ unsigned short min_cloth_color; // added by [MouseJstr]
+ unsigned short max_cloth_color; // added by [MouseJstr]
+ unsigned short pet_hair_style; // added by [Skotlex]
+
+ unsigned short castrate_dex_scale; // added by [MouseJstr]
+ unsigned short area_size; // added by [MouseJstr]
+
+ unsigned short max_def, over_def_bonus; //added by [Skotlex]
+
+ unsigned short zeny_from_mobs; // [Valaris]
+ unsigned short mobs_level_up; // [Valaris]
+ unsigned short mobs_level_up_exp_rate; // [Valaris]
+ unsigned short pk_min_level; // [celest]
+ unsigned short skill_steal_type; // [celest]
+ unsigned short skill_steal_rate; // [celest]
+// unsigned short night_darkness_level; // [celest]
+ unsigned short motd_type; // [celest]
+ unsigned short allow_atcommand_when_mute; // [celest]
+ unsigned short finding_ore_rate; // orn
+ unsigned short exp_calc_type;
+ unsigned short min_skill_delay_limit;
+ unsigned short require_glory_guild;
+ unsigned short idle_no_share;
+ unsigned short party_even_share_bonus;
+ unsigned short delay_battle_damage;
+ unsigned short display_version;
+ unsigned short who_display_aid;
+
+ unsigned short display_hallucination; // [Skotlex]
+ unsigned short use_statpoint_table; // [Skotlex]
+
+ unsigned short ignore_items_gender; //[Lupus]
+
+ unsigned short copyskill_restrict; // [Aru]
+ unsigned short berserk_cancels_buffs; // [Aru]
+
+ unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
+ unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
+ unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
+ int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
+
+ unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
+
+ unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
+ unsigned short mob_clear_delay; // [Valaris]
+
+ unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
+ unsigned short mob_max_skilllvl; // Max possible skill level [Lupus]
+ unsigned short rare_drop_announce; // chance <= to show rare drops global announces
+
+ unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
+ unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex]
+
+ unsigned short title_lvl1; // Players titles [Lupus]
+ unsigned short title_lvl2; // Players titles [Lupus]
+ unsigned short title_lvl3; // Players titles [Lupus]
+ unsigned short title_lvl4; // Players titles [Lupus]
+ unsigned short title_lvl5; // Players titles [Lupus]
+ unsigned short title_lvl6; // Players titles [Lupus]
+ unsigned short title_lvl7; // Players titles [Lupus]
+ unsigned short title_lvl8; // Players titles [Lupus]
+
+ unsigned short duel_enable; // [LuzZza]
+ unsigned short duel_allow_pvp; // [LuzZza]
+ unsigned short duel_allow_gvg; // [LuzZza]
+ unsigned short duel_allow_teleport; // [LuzZza]
+ unsigned short duel_autoleave_when_die; // [LuzZza]
+ unsigned short duel_time_interval; // [LuzZza]
+
+ unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
+
+ unsigned short allow_skill_without_day; // [Komurka]
+ unsigned short skill_wall_check; // [Skotlex]
+ unsigned short cell_stack_limit; // [Skotlex]
+} battle_config;
+
+extern int battle_config_read(const char *cfgName);
+extern void battle_validate_conf(void);
+extern void battle_set_defaults(void);
+extern int battle_set_value(char *, char *);
+
+#endif
diff --git a/src/map/charcommand.c b/src/map/charcommand.c
new file mode 100644
index 000000000..e2c97b15f
--- /dev/null
+++ b/src/map/charcommand.c
@@ -0,0 +1,1794 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "charcommand.h"
+#include "atcommand.h"
+#include "showmsg.h"
+
+static char command_symbol = '#';
+
+extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
+CCMD_FUNC(jobchange);
+CCMD_FUNC(petrename);
+CCMD_FUNC(petfriendly);
+CCMD_FUNC(stats);
+CCMD_FUNC(option);
+CCMD_FUNC(save);
+CCMD_FUNC(stats_all);
+CCMD_FUNC(reset);
+CCMD_FUNC(spiritball);
+CCMD_FUNC(itemlist);
+CCMD_FUNC(effect);
+CCMD_FUNC(storagelist);
+CCMD_FUNC(item);
+CCMD_FUNC(warp);
+CCMD_FUNC(zeny);
+CCMD_FUNC(fakename);
+CCMD_FUNC(baselevel);
+CCMD_FUNC(joblevel);
+CCMD_FUNC(questskill);
+CCMD_FUNC(lostskill);
+CCMD_FUNC(skreset);
+CCMD_FUNC(streset);
+CCMD_FUNC(model);
+CCMD_FUNC(stpoint);
+CCMD_FUNC(skpoint);
+CCMD_FUNC(changesex);
+CCMD_FUNC(feelreset);
+CCMD_FUNC(help);
+
+
+/*==========================================
+ *CharCommandInfo charcommand_info[]\‘¢‘Ì‚Ì’è‹`
+ *------------------------------------------
+ */
+
+// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value.
+// to set default level, read charcommand_athena.conf first please.
+static CharCommandInfo charcommand_info[] = {
+ { CharCommandJobChange, "#job", 60, charcommand_jobchange },
+ { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange },
+ { CharCommandPetRename, "#petrename", 50, charcommand_petrename },
+ { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly },
+ { CharCommandStats, "#stats", 40, charcommand_stats },
+ { CharCommandOption, "#option", 60, charcommand_option },
+ { CharCommandReset, "#reset", 60, charcommand_reset },
+ { CharCommandSave, "#save", 60, charcommand_save },
+ { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all },
+ { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball },
+ { CharCommandItemList, "#itemlist", 40, charcommand_itemlist },
+ { CharCommandEffect, "#effect", 40, charcommand_effect },
+ { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist },
+ { CharCommandItem, "#item", 60, charcommand_item },
+ { CharCommandWarp, "#warp", 60, charcommand_warp },
+ { CharCommandWarp, "#rura", 60, charcommand_warp },
+ { CharCommandWarp, "#rura+", 60, charcommand_warp },
+ { CharCommandZeny, "#zeny", 60, charcommand_zeny },
+ { CharCommandFakeName, "#fakename", 20, charcommand_fakename},
+
+ //*********************************Recently added commands*********************************************
+ { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel},
+ { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel},
+ { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel},
+ { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel},
+ { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill },
+ { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill },
+ { CharCommandSkReset, "#skreset", 60, charcommand_skreset },
+ { CharCommandStReset, "#streset", 60, charcommand_streset },
+ { CharCommandModel, "#model", 50, charcommand_model },
+ { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint },
+ { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint },
+ { CharCommandChangeSex, "#changesex", 60, charcommand_changesex },
+ { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset },
+ { CharCommandHelp, "#help", 20, charcommand_help },
+// add new commands before this line
+ { CharCommand_Unknown, NULL, 1, NULL }
+};
+
+int get_charcommand_level(const CharCommandType type) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_None; i++)
+ if (charcommand_info[i].type == type)
+ return charcommand_info[i].level;
+
+ return 100; // 100: command can not be used
+}
+
+/*==========================================
+ *is_charcommand @ƒRƒ}ƒ“ƒh‚É‘¶Ý‚·‚é‚©‚Ç‚¤‚©Šm”F‚·‚é
+ *------------------------------------------
+ */
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl) {
+ const char* str = message;
+ int s_flag = 0;
+ CharCommandInfo info;
+ CharCommandType type;
+
+ nullpo_retr(CharCommand_None, sd);
+
+ if (!message || !*message)
+ return CharCommand_None;
+
+ memset(&info, 0, sizeof(info));
+ str += strlen(sd->status.name);
+ while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
+ if (*str == ':')
+ s_flag = 1;
+ str++;
+ }
+ if (!*str)
+ return CharCommand_None;
+
+ type = charcommand(sd, gmlvl > 0 ? gmlvl : pc_isGM(sd), str, &info);
+ if (type != CharCommand_None) {
+ char command[100];
+ char output[200];
+ const char* p = str;
+ memset(command, '\0', sizeof(command));
+ memset(output, '\0', sizeof(output));
+ while (*p && !isspace(*p))
+ p++;
+ if (p - str >= sizeof(command)) // too long
+ return CharCommand_Unknown;
+ strncpy(command, str, p - str);
+ while (isspace(*p))
+ p++;
+
+ if (type == CharCommand_Unknown || info.proc == NULL) {
+ snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command.
+ clif_displaymessage(fd, output);
+ } else {
+ if (info.proc(fd, sd, command, p) != 0) {
+ // Command can not be executed
+ snprintf(output, sizeof(output), msg_txt(154), command); // %s failed.
+ clif_displaymessage(fd, output);
+ }
+ }
+
+ return info.type;
+ }
+
+ return CharCommand_None;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) {
+ char* p = (char *)message;
+
+ if (!info)
+ return CharCommand_None;
+ if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
+ return CharCommand_None;
+ if (!p || !*p) {
+ ShowError("char command message is empty\n");
+ return CharCommand_None;
+ }
+
+ if (*p == command_symbol) { // check first char.
+ char command[101];
+ int i = 0;
+ memset(info, 0, sizeof(CharCommandInfo));
+ sscanf(p, "%100s", command);
+ command[sizeof(command)-1] = '\0';
+
+ while (charcommand_info[i].type != CharCommand_Unknown) {
+ if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) {
+ p[0] = charcommand_info[i].command[0]; // set correct first symbol for after.
+ break;
+ }
+ i++;
+ }
+
+ if (charcommand_info[i].type == CharCommand_Unknown) {
+ // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
+ if (level == 0)
+ return CharCommand_None;
+ else
+ return CharCommand_Unknown;
+ } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) {
+ log_atcommand(sd, message);
+ }
+ memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]);
+ } else {
+ return CharCommand_None;
+ }
+
+ return info->type;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static CharCommandInfo* get_charcommandinfo_byname(const char* name) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++)
+ if (strcmpi(charcommand_info[i].command + 1, name) == 0)
+ return &charcommand_info[i];
+
+ return NULL;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ CharCommandInfo* p;
+ FILE* fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("CharCommands configuration file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
+ continue;
+ p = get_charcommandinfo_byname(w1);
+ if (p != NULL) {
+ p->level = atoi(w2);
+ if (p->level > 100)
+ p->level = 100;
+ else if (p->level < 0)
+ p->level = 0;
+ }
+
+ if (strcmpi(w1, "import") == 0)
+ charcommand_config_read(w2);
+ else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
+ w2[0] != '/' && // symbol of standard ragnarok GM commands
+ w2[0] != '%' && // symbol of party chat speaking
+ w2[0] != '$' && // symbol of guild chat speaking
+ w2[0] != '@') // symbol of atcommand
+ command_symbol = w2[0];
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * ‘ÎÛƒLƒƒƒ‰ƒNƒ^[‚ð“]E‚³‚¹‚é upperŽw’è‚Å“]¶‚â—{Žq‚à‰Â”\
+ *------------------------------------------
+ */
+int charcommand_jobchange(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[100];
+ struct map_session_data* pl_sd;
+ int job = 0, upper = -1;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+
+ if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upperŽw’肵‚Ä‚ ‚é
+ upper = -1;
+ if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upperŽw’肵‚Ä‚È‚¢ã‚ɉ½‚©‘«‚è‚È‚¢
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ int j;
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level
+ if ((job >= 0 && job < MAX_PC_CLASS)) {
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0)
+ pc_unequipitem(pl_sd, j, 3);
+ }
+ if (pc_jobchange(pl_sd, job, upper) == 0)
+ clif_displaymessage(fd, msg_table[48]); // Character's job changed.
+ else {
+ clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[49]); // Invalid job ID.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petrename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #petrename <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pl_sd->status.pet_id > 0 && pl_sd->pd) {
+ if (pl_sd->pet.rename_flag != 0) {
+ pl_sd->pet.rename_flag = 0;
+ intif_save_petdata(pl_sd->status.account_id, &pl_sd->pet);
+ clif_send_petstatus(pl_sd);
+ clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet.
+ } else {
+ clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petfriendly(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int friendly = 0;
+ int t = 0;
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) {
+ clif_displaymessage(fd, "Please, enter a valid value (usage: "
+ "#petfriendly <0-1000> <player>).");
+ return -1;
+ }
+
+ if (((pl_sd = map_nick2sd(character)) != NULL) && pc_isGM(sd)>pc_isGM(pl_sd)) {
+ if (pl_sd->status.pet_id > 0 && pl_sd->pd) {
+ if (friendly >= 0 && friendly <= 1000) {
+ if (friendly != pl_sd->pet.intimate) {
+ t = pl_sd->pet.intimate;
+ pl_sd->pet.intimate = friendly;
+ clif_send_petstatus(pl_sd);
+ clif_pet_emotion(pl_sd->pd,0);
+ if (battle_config.pet_status_support) {
+ if ((pl_sd->pet.intimate > 0 && t <= 0) ||
+ (pl_sd->pet.intimate <= 0 && t > 0)) {
+ if (pl_sd->bl.prev != NULL)
+ status_calc_pc(pl_sd, 0);
+ else
+ status_calc_pc(pl_sd, 2);
+ }
+ }
+ clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed!
+ clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed!
+ } else {
+ clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_stats(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char job_jobname[100];
+ char output[200];
+ struct map_session_data *pl_sd;
+ int i;
+
+ memset(character, '\0', sizeof(character));
+ memset(job_jobname, '\0', sizeof(job_jobname));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #stats <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ struct {
+ const char* format;
+ int value;
+ } output_table[] = {
+ { "Base Level - %d", pl_sd->status.base_level },
+ { job_jobname, pl_sd->status.job_level },
+ { "Hp - %d", pl_sd->status.hp },
+ { "MaxHp - %d", pl_sd->status.max_hp },
+ { "Sp - %d", pl_sd->status.sp },
+ { "MaxSp - %d", pl_sd->status.max_sp },
+ { "Str - %3d", pl_sd->status.str },
+ { "Agi - %3d", pl_sd->status.agi },
+ { "Vit - %3d", pl_sd->status.vit },
+ { "Int - %3d", pl_sd->status.int_ },
+ { "Dex - %3d", pl_sd->status.dex },
+ { "Luk - %3d", pl_sd->status.luk },
+ { "Zeny - %d", pl_sd->status.zeny },
+ { NULL, 0 }
+ };
+ sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)");
+ sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats:
+ clif_displaymessage(fd, output);
+ for (i = 0; output_table[i].format != NULL; i++) {
+ sprintf(output, output_table[i].format, output_table[i].value);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Reset
+ *------------------------------------------
+ */
+int charcommand_reset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #reset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetstate(pl_sd);
+ pc_resetskill(pl_sd);
+ sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_option(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ int opt1 = 0, opt2 = 0, opt3 = 0;
+ struct map_session_data* pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message ||
+ sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 ||
+ opt1 < 0 || opt2 < 0 || opt3 < 0) {
+ clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option <param1> <param2> <param3> <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level
+ pl_sd->opt1 = opt1;
+ pl_sd->opt2 = opt2;
+ pc_setoption(pl_sd, opt3);
+ clif_displaymessage(fd, msg_table[58]); // Character's options changed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_save(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+ int x = 0, y = 0;
+ int m;
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) {
+ clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save <map> <x> <y> <charname>).");
+ return -1;
+ }
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level
+ m = map_mapname2mapid(map_name);
+ if (m < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ } else {
+ if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to set this map as a save map.");
+ return -1;
+ }
+ pc_setsavepoint(pl_sd, map[m].index, x, y);
+ clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** Character Stats All by fritz
+int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ char output[1024], gmlevel[1024];
+ int i;
+ int count, users;
+ struct map_session_data *pl_sd, **pl_allsd;
+
+ memset(output, '\0', sizeof(output));
+ memset(gmlevel, '\0', sizeof(gmlevel));
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for(i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]))
+ {
+ if (pc_isGM(pl_sd) > 0)
+ sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd));
+ else
+ sprintf(gmlevel, " ");
+
+ sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp);
+ clif_displaymessage(fd, output);
+ sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel);
+ clif_displaymessage(fd, output);
+ clif_displaymessage(fd, "--------");
+ count++;
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * CharSpiritBall Function by PalasX
+ *------------------------------------------
+ */
+int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int spirit = 0;
+
+ memset(character, '\0', sizeof(character));
+
+ if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) {
+ clif_displaymessage(fd, "Usage: @spiritball <number: 0-1000>) <CHARACTER_NAME>.");
+ return -1;
+ }
+
+ if((pl_sd = map_nick2sd(character)) != NULL) {
+ if (spirit >= 0 && spirit <= 0x7FFF) {
+ if (pl_sd->spiritball != spirit || spirit > 999) {
+ if (pl_sd->spiritball > 0)
+ pc_delspiritball(pl_sd, pl_sd->spiritball, 1);
+ pl_sd->spiritball = spirit;
+ clif_spiritball(pl_sd);
+ // no message, player can look the difference
+ if (spirit > 1000)
+ clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
+ } else {
+ clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * #itemlist <character>: Displays the list of a player's items.
+ *------------------------------------------
+ */
+int
+charcommand_itemlist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, equip, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200];
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+ memset(equipstr, '\0', sizeof(equipstr));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.inventory[i].nameid)) != NULL) {
+ counter = counter + pl_sd->status.inventory[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if ((equip = pl_sd->status.inventory[i].equip)) {
+ strcpy(equipstr, "| equiped: ");
+ if (equip & 4)
+ strcat(equipstr, "robe/gargment, ");
+ if (equip & 8)
+ strcat(equipstr, "left accessory, ");
+ if (equip & 16)
+ strcat(equipstr, "body/armor, ");
+ if ((equip & 34) == 2)
+ strcat(equipstr, "right hand, ");
+ if ((equip & 34) == 32)
+ strcat(equipstr, "left hand, ");
+ if ((equip & 34) == 34)
+ strcat(equipstr, "both hands, ");
+ if (equip & 64)
+ strcat(equipstr, "feet, ");
+ if (equip & 128)
+ strcat(equipstr, "right accessory, ");
+ if ((equip & 769) == 1)
+ strcat(equipstr, "lower head, ");
+ if ((equip & 769) == 256)
+ strcat(equipstr, "top head, ");
+ if ((equip & 769) == 257)
+ strcat(equipstr, "lower/top head, ");
+ if ((equip & 769) == 512)
+ strcat(equipstr, "mid head, ");
+ if ((equip & 769) == 512)
+ strcat(equipstr, "lower/mid head, ");
+ if ((equip & 769) == 769)
+ strcat(equipstr, "lower/mid/top head, ");
+ // remove final ', '
+ equipstr[strlen(equipstr) - 2] = '\0';
+ } else
+ memset(equipstr, '\0', sizeof(equipstr));
+ if (sd->status.inventory[i].refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, pl_sd->status.inventory[i].refine, item_data->jname, pl_sd->status.inventory[i].refine, pl_sd->status.inventory[i].nameid, equipstr);
+ else
+ sprintf(output, "%d %s (%s, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, item_data->jname, pl_sd->status.inventory[i].nameid, equipstr);
+ clif_displaymessage(fd, output);
+ memset(output, '\0', sizeof(output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (pl_sd->status.inventory[i].card[j]) {
+ if ((item_temp = itemdb_search(pl_sd->status.inventory[i].card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found on this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #effect by [MouseJstr]
+ *
+ * Create a effect localized on another character
+ *------------------------------------------
+ */
+int
+charcommand_effect(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ char target[255];
+ int type = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) {
+ clif_displaymessage(fd, "usage: #effect <type+> <target>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) target)) == NULL)
+ return -1;
+
+ clif_specialeffect(&pl_sd->bl, type, 0);
+ clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
+
+ return 0;
+}
+
+/*==========================================
+ * #storagelist <character>: Displays the items list of a player's storage.
+ *------------------------------------------
+ */
+int
+charcommand_storagelist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor;
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], outputtmp[200];
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+ memset(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ if((stor = account2storage2(pl_sd->status.account_id)) != NULL) {
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_STORAGE; i++) {
+ if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) {
+ counter = counter + stor->storage_[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if (stor->storage_[i].refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid);
+ else
+ sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid);
+ clif_displaymessage(fd, output);
+ memset(output, '\0', sizeof(output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (stor->storage_[i].card[j]) {
+ if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found in the storage of this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, "This player has no storage.");
+ return 0;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number)
+{
+ int flag = 0;
+ int loop = 1, get_count = number,i;
+ struct item item_tmp;
+
+ if(sd && item_data){
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ loop = number;
+ get_count = 1;
+ }
+ for (i = 0; i < loop; i++) {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_data->nameid;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem((struct map_session_data*)sd,
+ &item_tmp, get_count)))
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ }
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ }
+}
+/*==========================================
+ * #item command (usage: #item <name/id_of_item> <quantity> <player>)
+ * by MC Cameri
+ *------------------------------------------
+ */
+int charcommand_item(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ int number = 0, item_id, flag;
+ struct item item_tmp;
+ struct item_data *item_data;
+ int get_count, i, pet_id;
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ memset(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) {
+ clif_displaymessage(fd, "Please, enter an item name/id (usage: #item <item name or ID> <quantity> <char name>).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+ get_count = number;
+ // check pet egg
+ pet_id = search_petDB_index(item_id, PET_EGG);
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ get_count = 1;
+ }
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ for (i = 0; i < number; i += get_count) {
+ // if pet egg
+ if (pet_id >= 0) {
+ pl_sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ // if not pet egg
+ } else {
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem(pl_sd, &item_tmp, get_count)))
+ clif_additem(pl_sd, 0, 0, flag);
+ }
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "A", 0, item_tmp.nameid, number, &item_tmp);
+ }
+ //Logs
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){
+ struct map_session_data **pl_allsd;
+ int users;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ charcommand_giveitem_sub(pl_sd,item_data,number);
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number);
+ clif_displaymessage(pl_sd->fd, tmp_cmdoutput);
+ }
+ }
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #warp/#rura/#rura+ <mapname> <x> <y> <char name>
+ *------------------------------------------
+ */
+int charcommand_warp(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ int x = 0, y = 0;
+ struct map_session_data *pl_sd;
+ int m;
+
+ nullpo_retr(-1, sd);
+
+ memset(map_name, '\0', sizeof(map_name));
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) {
+ clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ <mapname> <x> <y> <char name>");
+ return -1;
+ }
+
+ if (x <= 0)
+ x = rand() % 399 + 1;
+ if (y <= 0)
+ y = rand() % 399 + 1;
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level
+ if (x > 0 && x < 400 && y > 0 && y < 400) {
+ m = map_mapname2mapid(map_name);
+ if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp someone to this map.");
+ return -1;
+ }
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp this player from its actual map.");
+ return -1;
+ }
+ if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) {
+ clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped.
+ clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too).
+ } else {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #zeny <charname>
+ *------------------------------------------
+ */
+int charcommand_zeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int zeny = 0, new_zeny;
+ nullpo_retr(-1, sd);
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny <zeny> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ new_zeny = pl_sd->status.zeny + zeny;
+ if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
+ new_zeny = MAX_ZENY;
+ else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
+ new_zeny = 0;
+ if (new_zeny != pl_sd->status.zeny) {
+ pl_sd->status.zeny = new_zeny;
+ clif_updatestatus(pl_sd, SP_ZENY);
+ clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed!
+ } else {
+ if (zeny < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #fakename <char name> <fake name>
+ *------------------------------------------
+ */
+
+int charcommand_fakename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char name[NAME_LENGTH];
+ char char_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex]
+ if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) {
+ clif_displaymessage(sd->fd,"Usage: #fakename <char name> <fake name>.");
+ clif_displaymessage(sd->fd,"Or: #fakename <char name> to disable.");
+ return 0;
+ }
+
+ if(!(pl_sd = map_nick2sd(char_name))) {
+ clif_displaymessage(sd->fd,"Character not found.");
+ return -1;
+ }
+
+ if(strlen(name) < 1 || !name) {
+ if(strlen(pl_sd->fakename) > 1) {
+ pl_sd->fakename[0]='\0';
+ pc_setpos(pl_sd, pl_sd->mapindex, pl_sd->bl.x, sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Returned to real name.");
+ } else {
+ clif_displaymessage(sd->fd,"Character does not has a fake name.");
+ }
+ return 0;
+ }
+
+ if(strlen(name) < 2) {
+ clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
+ return 0;
+ }
+
+ memcpy(pl_sd->fakename,name, NAME_LENGTH-1);
+ pc_setpos(pl_sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3);
+ clif_displaymessage(sd->fd,"Fake name enabled.");
+
+ return 0;
+}
+
+
+/*==========================================
+ * #baselvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+*/
+int charcommand_baselevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int level = 0, i;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level
+
+ if (level > 0) {
+ if (pl_sd->status.base_level == battle_config.max_base_level) { // check for max level by Valaris
+ clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher.
+ return 0;
+ } // End Addition
+ if ((unsigned int)level > battle_config.max_base_level || (unsigned int)level > (battle_config.max_base_level - pl_sd->status.base_level)) // fix positiv overflow
+ level = battle_config.max_base_level - pl_sd->status.base_level;
+ for (i = 1; i <= level; i++)
+ pl_sd->status.status_point += (pl_sd->status.base_level + i + 14) / 5;
+ pl_sd->status.base_level += level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ status_calc_pc(pl_sd, 0);
+ pc_heal(pl_sd, pl_sd->status.max_hp, pl_sd->status.max_sp);
+ clif_misceffect(&pl_sd->bl, 0);
+ clif_displaymessage(fd, msg_table[65]); // Character's base level raised.
+ } else {
+ if (pl_sd->status.base_level == 1) {
+ clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower.
+ return -1;
+ }
+ if (level < -(int)battle_config.max_base_level || level < (1 - (int)pl_sd->status.base_level)) // fix negativ overflow
+ level = 1 - pl_sd->status.base_level;
+ if (pl_sd->status.status_point > 0) {
+ for (i = 0; i > level; i--)
+ pl_sd->status.status_point -= (pl_sd->status.base_level + i + 14) / 5;
+ if (pl_sd->status.status_point < 0)
+ pl_sd->status.status_point = 0;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ } // to add: remove status points from stats
+ pl_sd->status.base_level += level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[66]); // Character's base level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0; //³íI—¹
+}
+
+/*==========================================
+ * #jlvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_joblevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ unsigned int max_level = battle_config.max_job_level;
+ char player[NAME_LENGTH];
+ int level = 0;
+ //“]¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level
+ if ((pl_sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ max_level = 10; //Novice
+ else if ((pl_sd->class_&MAPID_BASEMASK) == MAPID_NOVICE)
+ max_level = battle_config.max_sn_level; //S. Novice
+ else if (pl_sd->class_&JOBL_UPPER && pl_sd->class_&JOBL_2)
+ max_level = battle_config.max_adv_level; //Adv. Class
+
+ if (level > 0) {
+ if (pl_sd->status.job_level == max_level) {
+ clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher.
+ return -1;
+ }
+ if (pl_sd->status.job_level + level > max_level)
+ level = max_level - pl_sd->status.job_level;
+ pl_sd->status.job_level += level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+ pl_sd->status.skill_point += level;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ status_calc_pc(pl_sd, 0);
+ clif_misceffect(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[68]); // character's job level raised.
+ } else {
+ if (pl_sd->status.job_level == 1) {
+ clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower.
+ return -1;
+ }
+ if (pl_sd->status.job_level + level < 1)
+ level = 1 - pl_sd->status.job_level;
+ pl_sd->status.job_level += level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+ if (pl_sd->status.skill_point > 0) {
+ pl_sd->status.skill_point += level;
+ if (pl_sd->status.skill_point < 0)
+ pl_sd->status.skill_point = 0;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ } // to add: remove status points from skills
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[69]); // Character's job level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #questskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_questskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> <nickname>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) == 0) {
+ pc_skill(pl_sd, skill_id, 1, 0);
+ clif_displaymessage(fd, msg_table[199]); // This player has learned the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #lostskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_lostskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> <char_name>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) > 0) {
+ pl_sd->status.skill[skill_id].lv = 0;
+ pl_sd->status.skill[skill_id].flag = 0;
+ clif_skillinfoblock(pl_sd);
+ clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Reset
+ *------------------------------------------
+ */
+int charcommand_skreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level
+ pc_resetskill(pl_sd);
+ sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Stat Reset
+ *------------------------------------------
+ */
+int charcommand_streset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level
+ pc_resetstate(pl_sd);
+ sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Model by chbrules
+ *------------------------------------------
+ */
+int charcommand_model(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hair_style = 0, hair_color = 0, cloth_color = 0;
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) {
+ sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d> <name>).",
+ MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
+ hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
+ cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ /* Removed this check for being too strange. [Skotlex]
+ if (cloth_color != 0 &&
+ pl_sd->status.sex == 1 &&
+ (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) {
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(pl_sd, LOOK_HAIR, hair_style);
+ pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color);
+ pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Point (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_skpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_skill_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ new_skill_point = (int)pl_sd->status.skill_point + point;
+ if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow
+ new_skill_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow
+ new_skill_point = 0;
+ if (new_skill_point != (int)pl_sd->status.skill_point) {
+ pl_sd->status.skill_point = new_skill_point;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Status Point (rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_stpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_status_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ new_status_point = (int)pl_sd->status.status_point + point;
+ if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow
+ new_status_point = 0x7FFF;
+ else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow
+ new_status_point = 0;
+ if (new_status_point != (int)pl_sd->status.status_point) {
+ pl_sd->status.status_point = new_status_point;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charchangesex command (usage: charchangesex <player_name>)
+ *------------------------------------------
+ */
+int charcommand_changesex(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char player[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex <name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(player) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(player) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Feel (SG save map) Reset
+ *------------------------------------------
+ */
+int charcommand_feelreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+ memset(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetfeel(pl_sd);
+ sprintf(output, msg_table[267], character); // '%s' designated maps reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #help - Char commands [Kayla]
+ *------------------------------------------
+ */
+int charcommand_help(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(charhelp_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/map/charcommand.h b/src/map/charcommand.h
new file mode 100644
index 000000000..741d5eb9e
--- /dev/null
+++ b/src/map/charcommand.h
@@ -0,0 +1,68 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARCOMMAND_H_
+#define _CHARCOMMAND_H_
+
+enum CharCommandType {
+ CharCommand_None = -1,
+ CharCommandJobChange,
+ CharCommandPetRename,
+ CharCommandPetFriendly,
+ CharCommandReset,
+ CharCommandStats,
+ CharCommandOption,
+ CharCommandSave,
+ CharCommandStatsAll,
+ CharCommandSpiritball,
+ CharCommandItemList,
+ CharCommandEffect,
+ CharCommandStorageList,
+ CharCommandItem, // by MC Cameri
+ CharCommandWarp,
+ CharCommandZeny,
+ CharCommandFakeName,
+ CharCommandBaseLevel,
+ CharCommandJobLevel,
+ CharCommandQuestSkill,
+ CharCommandLostSkill,
+ CharCommandSkReset,
+ CharCommandStReset,
+ CharCommandModel,
+ CharCommandSKPoint,
+ CharCommandSTPoint,
+ CharCommandChangeSex,
+ CharCommandFeelReset, // Komurka
+ CharCommandHelp,
+
+
+
+#ifdef TXT_ONLY
+/* TXT_ONLY */
+
+/* TXT_ONLY */
+#else
+/* SQL-only */
+
+/* SQL Only */
+#endif
+
+ // End. No more commans after this line.
+ CharCommand_Unknown,
+ CharCommand_MAX
+};
+
+typedef enum CharCommandType CharCommandType;
+typedef struct AtCommandInfo CharCommandInfo;
+
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl);
+
+CharCommandType charcommand(
+ struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info);
+int get_charcommand_level(const CharCommandType type);
+
+int charcommand_config_read(const char *cfgName);
+
+#endif
+
diff --git a/src/map/charsave.c b/src/map/charsave.c
new file mode 100644
index 000000000..8afa903b6
--- /dev/null
+++ b/src/map/charsave.c
@@ -0,0 +1,516 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+
+#include "charsave.h"
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+struct mmo_charstatus *charsave_loadchar(int charid){
+ int i,j, friends;
+ struct mmo_charstatus *c;
+ char *str_p;
+ friends = 0;
+
+ c = (struct mmo_charstatus *)aMalloc(sizeof(struct mmo_charstatus));
+
+ if(charid <= 0){
+ ShowError("charsave_loadchar() charid <= 0! (%d)", charid);
+ aFree(c);
+ return NULL;
+ }
+
+ //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius]
+ sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame` FROM `char` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(mysql_num_rows(charsql_res) <= 0){
+ ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid);
+ mysql_free_result(charsql_res);
+ aFree(c);
+ return NULL;
+ }
+
+ //fetch data
+ charsql_row = mysql_fetch_row(charsql_res);
+
+ //fill with data
+ c->char_id = charid;
+ c->account_id = atoi(charsql_row[1]);
+ c->char_num = atoi(charsql_row[2]);
+ strcpy(c->name, charsql_row[3]);
+ c->class_ = atoi(charsql_row[4]);
+ c->base_level = atoi(charsql_row[5]);
+ c->job_level = atoi(charsql_row[6]);
+ c->base_exp = atoi(charsql_row[7]);
+ c->job_exp = atoi(charsql_row[8]);
+ c->zeny = atoi(charsql_row[9]);
+ c->str = atoi(charsql_row[10]);
+ c->agi = atoi(charsql_row[11]);
+ c->vit = atoi(charsql_row[12]);
+ c->int_ = atoi(charsql_row[13]);
+ c->dex = atoi(charsql_row[14]);
+ c->luk = atoi(charsql_row[15]);
+ c->max_hp = atoi(charsql_row[16]);
+ c->hp = atoi(charsql_row[17]);
+ c->max_sp = atoi(charsql_row[18]);
+ c->sp = atoi(charsql_row[19]);
+ c->status_point = atoi(charsql_row[20]);
+ c->skill_point = atoi(charsql_row[21]);
+ c->option = atoi(charsql_row[22]);
+ c->karma = atoi(charsql_row[23]);
+ c->manner = atoi(charsql_row[24]);
+ c->party_id = atoi(charsql_row[25]);
+ c->guild_id = atoi(charsql_row[26]);
+ c->pet_id = atoi(charsql_row[27]);
+ c->hair = atoi(charsql_row[28]);
+ c->hair_color = atoi(charsql_row[29]);
+ c->clothes_color = atoi(charsql_row[30]);
+ c->weapon = atoi(charsql_row[31]);
+ c->shield = atoi(charsql_row[32]);
+ c->head_top = atoi(charsql_row[33]);
+ c->head_mid = atoi(charsql_row[34]);
+ c->head_bottom = atoi(charsql_row[35]);
+ c->last_point.map = mapindex_name2id(charsql_row[36]);
+ c->last_point.x = atoi(charsql_row[37]);
+ c->last_point.y = atoi(charsql_row[38]);
+ c->save_point.map = mapindex_name2id(charsql_row[39]);
+ c->save_point.x = atoi(charsql_row[40]);
+ c->save_point.y = atoi(charsql_row[41]);
+ c->partner_id = atoi(charsql_row[42]);
+ c->father = atoi(charsql_row[43]);
+ c->mother = atoi(charsql_row[44]);
+ c->child = atoi(charsql_row[45]);
+ c->fame = atoi(charsql_row[46]);
+
+ mysql_free_result(charsql_res);
+
+ //Check for '0' Savepoint / LastPoint
+ if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){
+ c->last_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->last_point.x = 100;
+ c->last_point.y = 100;
+ }
+
+ if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){
+ c->save_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->save_point.x = 100;
+ c->save_point.y = 100;
+ }
+
+
+ //read the memo points
+ sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ c->memo_point[i].map = mapindex_name2id(charsql_row[2]);
+ c->memo_point[i].x = atoi(charsql_row[3]);
+ c->memo_point[i].y = atoi(charsql_row[4]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+ //read inventory...
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->inventory[i].id = atoi(charsql_row[0]);
+ c->inventory[i].nameid = atoi(charsql_row[0]);
+ c->inventory[i].amount = atoi(charsql_row[1]);
+ c->inventory[i].equip = atoi(charsql_row[2]);
+ c->inventory[i].identify = atoi(charsql_row[3]);
+ c->inventory[i].refine = atoi(charsql_row[4]);
+ c->inventory[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->inventory[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //cart inventory ..
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->cart[i].id = atoi(charsql_row[0]);
+ c->cart[i].nameid = atoi(charsql_row[0]);
+ c->cart[i].amount = atoi(charsql_row[1]);
+ c->cart[i].equip = atoi(charsql_row[2]);
+ c->cart[i].identify = atoi(charsql_row[3]);
+ c->cart[i].refine = atoi(charsql_row[4]);
+ c->cart[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->cart[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //Skills...
+ sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ while((charsql_row = mysql_fetch_row(charsql_res))){
+ i = atoi(charsql_row[1]);
+ c->skill[i].id = i;
+ c->skill[i].lv = atoi(charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ }
+/* Reg values are handled by the char server.
+ //Global REG
+ sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ strcpy(c->global_reg[i].str, charsql_row[1]);
+ strcpy(c->global_reg[i].value, charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ c->global_reg_num = i;
+ }
+*/
+ //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
+ //Friend list
+ sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sql_res = NULL; //To avoid trying to read data.
+ }
+ else
+ sql_res = mysql_store_result(&charsql_handle);
+
+ if(sql_res)
+ {
+ for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
+ {
+ if (sql_row[2] != NULL)
+ {
+ c->friends[i].account_id = atoi(sql_row[0]);
+ c->friends[i].char_id = atoi(sql_row[1]);
+ strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex]
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+
+ ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name);
+ return c;
+}
+
+int charsave_savechar(int charid, struct mmo_charstatus *c){
+ int i,j;
+ char *str_p;
+// char tmp_str[64];
+// char tmp_str2[512];
+ //First save the 'char'
+ sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
+ "`base_exp`='%d', `job_exp`='%d', `zeny`='%d',"
+ "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
+ "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
+ "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+ "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
+ "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
+ "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d'"
+ "WHERE `account_id`='%d' AND `char_id` = '%d'",
+ c->class_, c->base_level, c->job_level,
+ c->base_exp, c->job_exp, c->zeny,
+ c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point,
+ c->str, c->agi, c->vit, c->int_, c->dex, c->luk,
+ c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id,
+ c->hair, c->hair_color, c->clothes_color,
+ c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom,
+ mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y,
+ mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother,
+ c->child, c->fame, c->account_id, c->char_id
+ );
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+
+ //Save the inventory
+ sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_INVENTORY; i++){
+ if(c->inventory[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip,
+ c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ //Save the cart
+ sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_CART; i++){
+ if(c->cart[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip,
+ c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save memo points
+ sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_MEMOPOINTS; i++){
+ if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){
+ sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save skills
+ sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_SKILL; i++){
+ if(c->skill[i].id > 0){
+ sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+/* Reg values are handled by the char server.
+ //global_reg_value saving
+ sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < c->global_reg_num; i++){
+ if(c->global_reg[i].str){
+ if(c->global_reg[i].value){
+ //jstrescapecpy(tmp_str, c->global_reg[i].str);
+ sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value));
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+*/
+
+ //friendlist saving
+ sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(c->friends[i].char_id > 0){
+ sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name);
+ return 0;
+}
+
+int charsave_load_scdata(int account_id, int char_id)
+{ //Loads character's sc_data
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if (!sd)
+ {
+ ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id);
+ return -1;
+ }
+ if (sd->status.char_id != char_id)
+ {
+ ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id);
+ return -1;
+ }
+ sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`"
+ "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ sql_res = mysql_store_result(&charsql_handle);
+ if(sql_res)
+ {
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ {
+ if (atoi(sql_row[1]) < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name);
+ continue;
+ }
+
+ status_change_start(&sd->bl, atoi(sql_row[0]), atoi(sql_row[2]), atoi(sql_row[3]),
+ atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 7);
+ }
+ }
+
+ //Once loaded, sc_data must be disposed.
+ sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+
+void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc)
+{ //Saves character's sc_data.
+ int i,count =0;
+ struct TimerData *timer;
+ unsigned int tick = gettick();
+
+ sprintf(tmp_sql, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ");
+
+ for(i = 0; i < max_sc; i++)
+ {
+ if (sc_data[i].timer == -1)
+ continue;
+ timer = get_timer(sc_data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+
+ sprintf (tmp_sql, "%s ('%d','%d','%hu','%d','%d','%d','%d','%d'),", tmp_sql, account_id, char_id,
+ i, DIFF_TICK(timer->tick,tick), sc_data[i].val1, sc_data[i].val2, sc_data[i].val3, sc_data[i].val4);
+
+ count++;
+ }
+ if (count > 0)
+ {
+ tmp_sql[strlen(tmp_sql)-1] = '\0'; //Remove the trailing comma.
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id);
+ return;
+}
+#endif
diff --git a/src/map/charsave.h b/src/map/charsave.h
new file mode 100644
index 000000000..6fa119e14
--- /dev/null
+++ b/src/map/charsave.h
@@ -0,0 +1,16 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARSAVE_H_
+#define _CHARSAVE_H_
+
+#include "status.h"
+
+#ifndef TXT_ONLY
+ struct mmo_charstatus *charsave_loadchar(int charid);
+ int charsave_savechar(int charid, struct mmo_charstatus *c);
+ int charsave_load_scdata(int account_id, int char_id);
+ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc);
+#endif
+
+#endif
diff --git a/src/map/chat.c b/src/map/chat.c
new file mode 100644
index 000000000..821cd0858
--- /dev/null
+++ b/src/map/chat.c
@@ -0,0 +1,371 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "battle.h"
+#include "chat.h"
+#include "map.h"
+#include "clif.h"
+#include "pc.h"
+#include "npc.h"
+
+int chat_triggerevent(struct chat_data *cd);
+
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒ‹[ƒ€ì¬
+ *------------------------------------------
+ */
+int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+
+ if (sd->chatID)
+ return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]
+
+ cd = (struct chat_data *) aCalloc(1,sizeof(struct chat_data));
+
+ cd->limit = limit;
+ cd->pub = pub;
+ cd->users = 1;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->owner = (struct block_list **)(&cd->usersd[0]);
+ cd->usersd[0] = sd;
+ cd->bl.m = sd->bl.m;
+ cd->bl.x = sd->bl.x;
+ cd->bl.y = sd->bl.y;
+ cd->bl.type = BL_CHAT;
+
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ clif_createchat(sd,1);
+ aFree(cd);
+ return 0;
+ }
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_createchat(sd,0);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Šù‘¶ƒ`ƒƒƒbƒgƒ‹[ƒ€‚ÉŽQ‰Á
+ *------------------------------------------
+ */
+int chat_joinchat (struct map_session_data *sd, int chatid, char* pass)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+ cd = (struct chat_data*)map_id2bl(chatid);
+
+ //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet
+ //a wrong chat id can be received. [Skotlex]
+ if (cd == NULL)
+ return 1;
+ if (cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) {
+ clif_joinchatfail(sd,0);
+ return 0;
+ }
+ //Allows Gm access to protected room with any password they want by valaris
+ if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) ||
+ chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe
+ {
+ clif_joinchatfail(sd,1);
+ return 0;
+ }
+
+ cd->usersd[cd->users] = sd;
+ cd->users++;
+
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_joinchatok(sd,cd); // V‚½‚ÉŽQ‰Á‚µ‚½l‚É‚Í‘Sˆõ‚̃ŠƒXƒg
+ clif_addchat(cd,sd); // Šù‚É’†‚É‹‚½l‚ɂ͒ljÁ‚µ‚½l‚Ì•ñ
+ clif_dispchat(cd,0); // ŽüˆÍ‚Ìl‚É‚Íl”•Ï‰»•ñ
+
+ chat_triggerevent(cd); // ƒCƒxƒ“ƒg
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒ‹[ƒ€‚©‚甲‚¯‚é
+ *------------------------------------------
+ */
+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‚ÉŠ‘®‚µ‚Ä‚¢‚È‚¢‚炵‚¢ (ƒoƒOŽž‚Ì‚Ý)
+ return -1;
+
+ if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){
+ // Š—LŽÒ‚¾‚Á‚½&‘¼‚Él‚ª‹‚é&PC‚̃`ƒƒƒbƒg
+ clif_changechatowner(cd,cd->usersd[1]);
+ clif_clearchat(cd,0);
+ }
+
+ // ”²‚¯‚éPC‚É‚à‘—‚é‚Ì‚Åusers‚ðŒ¸‚ç‚·‘O‚ÉŽÀs
+ clif_leavechat(cd,sd);
+
+ cd->users--;
+ pc_setchatid(sd,0);
+
+ if(cd->users == 0 && (*cd->owner)->type==BL_PC){
+ // ‘Sˆõ‹‚È‚­‚È‚Á‚½&PC‚̃`ƒƒƒbƒg‚È‚Ì‚ÅÁ‚·
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id); // free‚Ü‚Å‚µ‚Ä‚­‚ê‚é
+ } else {
+ for(i=leavechar;i < cd->users;i++)
+ cd->usersd[i] = cd->usersd[i+1];
+ if(leavechar==0 && (*cd->owner)->type==BL_PC){
+ // PC‚̃`ƒƒƒbƒg‚È‚Ì‚ÅŠ—LŽÒ‚ª”²‚¯‚½‚̂ňʒu•ÏX
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+ }
+ clif_dispchat(cd,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒ‹[ƒ€‚ÌŽ‚¿Žå‚ð÷‚é
+ *------------------------------------------
+ */
+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) // ‚»‚ñ‚Èl‚Í‹‚È‚¢
+ return -1;
+
+ clif_changechatowner(cd,cd->usersd[nextowner]);
+ // ˆê’UÁ‚·
+ clif_clearchat(cd,0);
+
+ // userlist‚̇”Ô•ÏX (0‚ªŠ—LŽÒ‚È‚Ì‚Å)
+ if( (tmp_sd = cd->usersd[0]) == NULL )
+ return 1; //‚ ‚肦‚é‚Ì‚©‚ÈH
+ cd->usersd[0] = cd->usersd[nextowner];
+ cd->usersd[nextowner] = tmp_sd;
+
+ // V‚µ‚¢Š—LŽÒ‚̈ʒu‚Ö•ÏX
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+
+ // Ä“x•\Ž¦
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ`ƒƒƒbƒg‚Ìó‘Ô(ƒ^ƒCƒgƒ‹“™)‚ð•ÏX
+ *------------------------------------------
+ */
+int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, sd);
+
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd==NULL || (struct block_list *)sd != (*cd->owner))
+ return 1;
+
+ cd->limit = limit;
+ cd->pub = pub;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ clif_changechatstatus(cd);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒ‹[ƒ€‚©‚çR‚èo‚·
+ *------------------------------------------
+ */
+int chat_kickchat(struct map_session_data *sd,char *kickusername)
+{
+ struct chat_data *cd;
+ int i;
+
+ nullpo_retr(1, sd);
+
+ cd = (struct chat_data *)map_id2bl(sd->chatID);
+
+ for(i = 0; i < cd->users; i++) {
+ if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) {
+ if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat)
+ //gm kick protection by valaris
+ return 0;
+
+ chat_leavechat(cd->usersd[i]);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*==========================================
+ * npcƒ`ƒƒƒbƒgƒ‹[ƒ€ì¬
+ *------------------------------------------
+ */
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, nd);
+
+ cd = (struct chat_data *) aCalloc(1,sizeof(struct chat_data));
+
+ cd->limit = cd->trigger = limit;
+ if(trigger>0)
+ cd->trigger = trigger;
+ cd->pub = pub;
+ cd->users = 0;
+ memcpy(cd->pass,"",1);
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->bl.m = nd->bl.m;
+ cd->bl.x = nd->bl.x;
+ cd->bl.y = nd->bl.y;
+ cd->bl.type = BL_CHAT;
+ cd->owner_ = (struct block_list *)nd;
+ cd->owner = &cd->owner_;
+ if (strlen(ev) > 49)
+ { //npc_event is a char[50] [Skotlex]
+ memcpy(cd->npc_event,ev,49);
+ cd->npc_event[49] = '\0';
+ } else
+ memcpy(cd->npc_event,ev,strlen(ev));
+
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ aFree(cd);
+ return 0;
+ }
+ nd->chat_id=cd->bl.id;
+
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+/*==========================================
+ * npcƒ`ƒƒƒbƒgƒ‹[ƒ€íœ
+ *------------------------------------------
+ */
+int chat_deletenpcchat(struct npc_data *nd)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, nd);
+ nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id));
+
+ chat_npckickall(cd);
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id); // free‚Ü‚Å‚µ‚Ä‚­‚ê‚é
+ nd->chat_id=0;
+
+ return 0;
+}
+
+/*==========================================
+ * ‹K’èl”ˆÈã‚ŃCƒxƒ“ƒg‚ª’è‹`‚³‚ê‚Ä‚é‚È‚çŽÀs
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒg‚Ì—LŒø‰»
+ *------------------------------------------
+ */
+int chat_enableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger&=0x7f;
+ chat_triggerevent(cd);
+ return 0;
+}
+/*==========================================
+ * ƒCƒxƒ“ƒg‚Ì–³Œø‰»
+ *------------------------------------------
+ */
+int chat_disableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger|=0x80;
+ return 0;
+}
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒ‹[ƒ€‚©‚ç‘SˆõR‚èo‚·
+ *------------------------------------------
+ */
+int chat_npckickall(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ while(cd->users>0){
+ chat_leavechat(cd->usersd[cd->users-1]);
+ }
+ return 0;
+}
diff --git a/src/map/chat.h b/src/map/chat.h
new file mode 100644
index 000000000..1251ad98c
--- /dev/null
+++ b/src/map/chat.h
@@ -0,0 +1,22 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHAT_H_
+#define _CHAT_H_
+
+#include "map.h"
+
+int chat_createchat(struct map_session_data *,int,int,char*,char*,int);
+int chat_joinchat(struct map_session_data *,int,char*);
+int chat_leavechat(struct map_session_data* );
+int chat_changechatowner(struct map_session_data *,char *);
+int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int);
+int chat_kickchat(struct map_session_data *,char *);
+
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev);
+int chat_deletenpcchat(struct npc_data *nd);
+int chat_enableevent(struct chat_data *cd);
+int chat_disableevent(struct chat_data *cd);
+int chat_npckickall(struct chat_data *cd);
+
+#endif
diff --git a/src/map/chrif.c b/src/map/chrif.c
new file mode 100644
index 000000000..2ae29ffbd
--- /dev/null
+++ b/src/map/chrif.c
@@ -0,0 +1,1572 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+#include <time.h>
+
+#include "../common/malloc.h"
+#include "socket.h"
+#include "timer.h"
+#include "map.h"
+#include "battle.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "nullpo.h"
+#include "showmsg.h"
+#ifndef TXT_ONLY
+#include "charsave.h"
+#endif
+//Updated table (only doc^^) [Sirius]
+//Used Packets: U->2af8
+//Free Packets: F->2af8
+
+struct dbt *auth_db;
+
+static const int packet_len_table[0x3d] = {
+ 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
+ 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07
+ 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
+ 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
+ -1,-1,-1,-1,-1,-1,-1, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, F->2b1e, U->2b1f
+ -1,-1,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, F->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
+};
+
+//Used Packets:
+//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
+//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)'
+//2afa: Outgoing, chrif_sendmap -> 'sending our maps'
+//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..'
+//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one.
+//2afd: Incomming, chrif_authok -> 'character selected, add to auth db'
+//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver'
+//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver'
+//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure)
+//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)'
+//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure)
+//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure)
+//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's'
+//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...'
+//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^'
+//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known
+//2b08: Outgoing, chrif_searchcharid -> '...'
+//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db'
+//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY'
+//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..'
+//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
+//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY'
+//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
+//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e'
+//2b10: FREE
+//2b11: Outgoing, chrif_changesex -> 'change sex of acc X'
+//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X'
+//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....'
+//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY'
+//2b15: Incomming, chrif_recvgmaccounts -> 'recive gm accs from charserver (seems to be incomplete !)'
+//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....'
+//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
+//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
+//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online'
+//2b1a: Outgoing, chrif_reqfamelist -> 'Request the fame list (top10)'
+//2b1b: Incomming, chrif_recvfamelist -> 'answer of 2b1a ..... the famelist top10^^'
+//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.'
+//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.'
+//2b1e: FREE
+//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
+//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
+//2b21-2b27: FREE
+
+int chrif_connected;
+int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
+int srvinfo;
+static char char_ip_str[16];
+static int char_ip;
+static int char_port = 6121;
+static char userid[NAME_LENGTH], passwd[NAME_LENGTH];
+static int chrif_state = 0;
+static int char_init_done = 0;
+//Interval at which map server updates online listing. [Valaris]
+#define CHECK_INTERVAL 3600000
+//Interval at which map server sends number of connected users. [Skotlex]
+#define UPDATE_INTERVAL 10000
+//This define should spare writing the check in every function. [Skotlex]
+#define chrif_check(a) { if(!chrif_isconnect()) return a; }
+
+// Ý’èƒtƒ@ƒCƒ‹“Ç‚Ýž‚ÝŠÖŒW
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setuserid(char *id)
+{
+ memcpy(userid, id, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setpasswd(char *pwd)
+{
+ memcpy(passwd, pwd, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setip(char *ip)
+{
+ memcpy(&char_ip_str, ip, 16);
+ char_ip = inet_addr(char_ip_str);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setport(int port)
+{
+ char_port = port;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_isconnect(void)
+{
+ return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_save(struct map_session_data *sd, int flag)
+{
+ nullpo_retr(-1, sd);
+ chrif_check(-1);
+ pc_makesavestatus(sd);
+
+ if (sd->state.finalsave)
+ return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
+ //For data sync
+ if (sd->state.storage_flag == 1)
+ storage_storage_save(sd->status.account_id);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storagesave(sd->status.account_id, sd->status.guild_id);
+
+ //Saving of registry values.
+ if (sd->state.reg_dirty&4)
+ intif_saveregistry(sd, 3); //Save char regs
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd, 2); //Save account regs
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd, 1); //Save account2 regs
+
+#ifndef TXT_ONLY
+ if(charsave_method){ //New 'Local' save
+ charsave_savechar(sd->char_id, &sd->status);
+ if (flag) chrif_char_offline(sd); //Tell char server that character went offline.
+ }else{
+#endif
+ WFIFOHEAD(char_fd, sizeof(sd->status) + 13);
+ WFIFOW(char_fd,0) = 0x2b01;
+ WFIFOW(char_fd,2) = sizeof(sd->status) + 13;
+ WFIFOL(char_fd,4) = sd->bl.id;
+ WFIFOL(char_fd,8) = sd->char_id;
+ WFIFOB(char_fd,12) = flag?1:0; //Flag to tell char-server this character is quitting.
+ memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
+ WFIFOSET(char_fd, WFIFOW(char_fd,2));
+#ifndef TXT_ONLY
+ }
+#endif
+ if (flag) {//Remove the storage from memory.
+ storage_delete(sd->status.account_id);
+ sd->state.finalsave = 1; //Mark the last save as done.
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connect(int fd)
+{
+ ShowStatus("Logging in to char server...\n", char_fd);
+ WFIFOHEAD(fd, 60);
+ WFIFOW(fd,0) = 0x2af8;
+ memcpy(WFIFOP(fd,2), userid, NAME_LENGTH);
+ memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH);
+ WFIFOL(fd,50) = 0;
+ WFIFOL(fd,54) = clif_getip();
+ WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov
+ WFIFOSET(fd,60);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ}ƒbƒv‘—M
+ *------------------------------------------
+ */
+int chrif_sendmap(int fd)
+{
+ int i;
+ ShowStatus("Sending maps to char server...\n");
+ WFIFOHEAD(fd, 4 + map_num * 4);
+ WFIFOW(fd,0) = 0x2afa;
+ for(i = 0; i < map_num; i++)
+ WFIFOW(fd,4+i*4) = map[i].index;
+ WFIFOW(fd,2) = 4 + i * 4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ}ƒbƒvŽóM
+ *------------------------------------------
+ */
+int chrif_recvmap(int fd)
+{
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+
+ if (chrif_state < 2) // ‚Ü‚¾€”õ’†
+ return -1;
+
+ ip = RFIFOL(fd,4);
+ port = RFIFOW(fd,8);
+ for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
+ map_setipport(RFIFOW(fd,i), ip, port);
+// if (battle_config.etc_log)
+// printf("recv map %d %s\n", j, RFIFOP(fd,i));
+ }
+ if (battle_config.etc_log)
+ ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+
+ return 0;
+}
+
+/*==========================================
+ * Delete maps of other servers, (if an other mapserver is going OFF)
+ *------------------------------------------
+ */
+int chrif_removemap(int fd){
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+
+ if(chrif_state < 2){
+ return -1; //i dunno, but i know if its 3 the link is ok^^
+ }
+
+ ip = RFIFOL(fd, 4);
+ port = RFIFOW(fd, 8);
+
+ for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){
+ map_eraseipport(RFIFOW(fd, i), ip, port);
+ }
+
+ if(battle_config.etc_log){
+ ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒ}ƒbƒvŽIŠÔˆÚ“®‚Ì‚½‚߂̃f[ƒ^€”õ—v‹
+ *------------------------------------------
+ */
+int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port)
+{
+ int i, s_ip=0;
+
+ nullpo_retr(-1, sd);
+
+ chrif_check(-1);
+
+ s_ip = 0;
+ for(i = 0; i < fd_max; i++)
+ if (session[i] && session[i]->session_data == sd) {
+ s_ip = session[i]->client_addr.sin_addr.s_addr;
+ break;
+ }
+
+ WFIFOHEAD(char_fd, 35);
+ WFIFOW(char_fd, 0) = 0x2b05;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = sd->status.char_id;
+ WFIFOW(char_fd,18) = map;
+ WFIFOW(char_fd,20) = x;
+ WFIFOW(char_fd,22) = y;
+ WFIFOL(char_fd,24) = ip;
+ WFIFOW(char_fd,28) = port;
+ WFIFOB(char_fd,30) = sd->status.sex;
+ WFIFOL(char_fd,31) = s_ip;
+ WFIFOSET(char_fd,35);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ}ƒbƒvŽIŠÔˆÚ“®ack
+ *------------------------------------------
+ */
+int chrif_changemapserverack(int fd)
+{
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+ sd = map_id2sd(RFIFOL(fd,2));
+
+ if (sd == NULL || sd->status.char_id != RFIFOL(fd,14))
+ return -1;
+
+ if (RFIFOL(fd,6) == 1) {
+ if (battle_config.error_log)
+ ShowError("map server change failed.\n");
+ pc_authfail(sd);
+ return 0;
+ }
+ clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connectack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd);
+ chrif_state = 1;
+ chrif_connected=1;
+
+ chrif_sendmap(fd);
+
+ ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
+ if(!char_init_done) {
+ char_init_done = 1;
+ ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_sendmapack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
+ ShowStatus("Map sending complete. Map Server is now online.\n");
+ chrif_state = 2;
+
+ //Re-save any storages that were modified in the disconnection time. [Skotlex]
+ do_reconnect_storage();
+
+ return 0;
+}
+
+/*==========================================
+ * Request sc_data from charserver [Skotlex]
+ *------------------------------------------
+ */
+int chrif_scdata_request(int account_id, int char_id)
+{
+#ifdef ENABLE_SC_SAVING
+#ifndef TXT_ONLY
+ if (charsave_method)
+ return charsave_load_scdata(account_id, char_id);
+#endif
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd, 0) = 0x2afc;
+ WFIFOL(char_fd, 2) = account_id;
+ WFIFOL(char_fd, 6) = char_id;
+ WFIFOSET(char_fd,10);
+#endif
+ return 0;
+}
+
+/*==========================================
+ * new auth system [Kevin]
+ *------------------------------------------
+ */
+void chrif_authreq(struct map_session_data *sd)
+{
+ struct auth_node *auth_data;
+ auth_data=idb_get(auth_db, sd->bl.id);
+
+ if(auth_data) {
+ if(auth_data->char_dat &&
+ auth_data->account_id== sd->bl.id &&
+ auth_data->login_id1 == sd->login_id1)
+ { //auth ok
+ pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat);
+ chrif_scdata_request(auth_data->account_id, auth_data->char_dat->char_id);
+ } else { //auth failed
+ pc_authfail(sd);
+ chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+ }
+ if (auth_data->char_dat)
+ aFree(auth_data->char_dat);
+ idb_remove(auth_db, sd->bl.id);
+ } else { //data from char server has not arrived yet.
+ auth_data = aCalloc(1, sizeof(struct auth_node));
+ auth_data->sd = sd;
+ auth_data->fd = sd->fd;
+ auth_data->account_id = sd->bl.id;
+ auth_data->login_id1 = sd->login_id1;
+ auth_data->node_created = gettick();
+ idb_put(auth_db, sd->bl.id, auth_data);
+ }
+ return;
+}
+
+//character selected, insert into auth db
+void chrif_authok(int fd) {
+ struct auth_node *auth_data;
+ RFIFOHEAD(fd);
+
+ if (map_id2sd(RFIFOL(fd, 4)) != NULL)
+ //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex]
+ return;
+
+ if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL)
+ { //Is the character already awaiting authorization?
+ if (auth_data->sd)
+ {
+ //First, check to see if the session data still exists (avoid dangling pointers)
+ if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd)
+ {
+ if (auth_data->char_dat == NULL &&
+ auth_data->account_id == RFIFOL(fd, 4) &&
+ auth_data->login_id1 == RFIFOL(fd, 8))
+ { //Auth Ok
+ pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20));
+ chrif_scdata_request(auth_data->account_id, auth_data->sd->status.char_id);
+ } else { //Auth Failed
+ pc_authfail(auth_data->sd);
+ chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already.
+ }
+ } //else: Character no longer exists, just go through.
+ }
+ //Delete the data of this node...
+ if (auth_data->char_dat)
+ aFree (auth_data->char_dat);
+ uidb_remove(auth_db, RFIFOL(fd, 4));
+ return;
+ }
+ // Awaiting for client to connect.
+ auth_data = (struct auth_node *)aCalloc(1, sizeof(struct auth_node));
+ auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1, sizeof(struct mmo_charstatus));
+
+ auth_data->account_id=RFIFOL(fd, 4);
+ auth_data->login_id1=RFIFOL(fd, 8);
+ auth_data->connect_until_time=RFIFOL(fd, 12);
+ auth_data->login_id2=RFIFOL(fd, 16);
+ memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus));
+ auth_data->node_created=gettick();
+ uidb_put(auth_db, RFIFOL(fd, 4), auth_data);
+}
+
+int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
+{
+ struct auth_node *node=(struct auth_node*)data;
+
+ if(DIFF_TICK(gettick(),node->node_created)>30000) {
+ ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id);
+ if (node->char_dat)
+ aFree(node->char_dat);
+ db_remove(auth_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+int auth_db_cleanup(int tid, unsigned int tick, int id, int data) {
+ auth_db->foreach(auth_db, auth_db_cleanup_sub);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_charselectreq(struct map_session_data *sd)
+{
+ int i, s_ip;
+
+ nullpo_retr(-1, sd);
+
+ if( !sd || !sd->bl.id || !sd->login_id1 )
+ return -1;
+ chrif_check(-1);
+
+ s_ip = 0;
+ for(i = 0; i < fd_max; i++)
+ if (session[i] && session[i]->session_data == sd) {
+ s_ip = session[i]->client_addr.sin_addr.s_addr;
+ break;
+ }
+
+ WFIFOHEAD(char_fd, 18);
+ WFIFOW(char_fd, 0) = 0x2b02;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = s_ip;
+ WFIFOSET(char_fd,18);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒLƒƒƒ‰–¼–â‚¢‡‚킹
+ *------------------------------------------
+ */
+int chrif_searchcharid(int char_id)
+{
+ if( !char_id )
+ return -1;
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 6);
+ WFIFOW(char_fd,0) = 0x2b08;
+ WFIFOL(char_fd,2) = char_id;
+ WFIFOSET(char_fd,6);
+
+ return 0;
+}
+
+/*==========================================
+ * GM‚ɕω»—v‹
+ *------------------------------------------
+ */
+int chrif_changegm(int id, const char *pass, int len)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, len + 8);
+ WFIFOW(char_fd,0) = 0x2b0a;
+ WFIFOW(char_fd,2) = len + 8;
+ WFIFOL(char_fd,4) = id;
+ memcpy(WFIFOP(char_fd,8), pass, len);
+ WFIFOSET(char_fd, len + 8);
+
+ return 0;
+}
+
+/*==========================================
+ * Change Email
+ *------------------------------------------
+ */
+int chrif_changeemail(int id, const char *actual_email, const char *new_email)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 86);
+ WFIFOW(char_fd,0) = 0x2b0c;
+ WFIFOL(char_fd,2) = id;
+ memcpy(WFIFOP(char_fd,6), actual_email, 40);
+ memcpy(WFIFOP(char_fd,46), new_email, 40);
+ WFIFOSET(char_fd,86);
+
+ return 0;
+}
+
+/*==========================================
+ * Send message to char-server with a character name to do some operations (by Yor)
+ * Used to ask Char-server about a character name to have the account number to modify account file in login-server.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ *------------------------------------------
+ */
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 44);
+ WFIFOW(char_fd, 0) = 0x2b0e;
+ WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody
+ memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH);
+ WFIFOW(char_fd, 30) = operation_type; // type of operation
+ if (operation_type == 2) {
+ WFIFOW(char_fd, 32) = year;
+ WFIFOW(char_fd, 34) = month;
+ WFIFOW(char_fd, 36) = day;
+ WFIFOW(char_fd, 38) = hour;
+ WFIFOW(char_fd, 40) = minute;
+ WFIFOW(char_fd, 42) = second;
+ }
+ ShowInfo("chrif : sended 0x2b0e\n");
+ WFIFOSET(char_fd,44);
+
+ return 0;
+}
+
+/*==========================================
+ * «•Ê•Ï‰»—v‹
+ *------------------------------------------
+ */
+int chrif_changesex(int id, int sex) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 9);
+ WFIFOW(char_fd,0) = 0x2b11;
+ WFIFOW(char_fd,2) = 9;
+ WFIFOL(char_fd,4) = id;
+ WFIFOB(char_fd,8) = sex;
+ ShowInfo("chrif : sent 0x3000(changesex)\n");
+ WFIFOSET(char_fd,9);
+ return 0;
+}
+
+/*==========================================
+ * Answer after a request about a character name to do some operations (by Yor)
+ * Used to answer of chrif_char_ask_name.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ * type of answer:
+ * 0: login-server resquest done
+ * 1: player not found
+ * 2: gm level too low
+ * 3: login-server offline
+ *------------------------------------------
+ */
+int chrif_char_ask_name_answer(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ char output[256];
+ char player_name[NAME_LENGTH];
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody)
+ memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sd = map_id2sd(acc);
+ if (acc >= 0 && sd != NULL) {
+ if (RFIFOW(fd, 32) == 1) // player not found
+ sprintf(output, "The player '%s' doesn't exist.", player_name);
+ else {
+ switch(RFIFOW(fd, 30)) {
+ case 1: // block
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to block the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 2: // ban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 3: // unblock
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 4: // unban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 5: // changesex
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ }
+ }
+ if (output[0] != '\0') {
+ output[sizeof(output)-1] = '\0';
+ clif_displaymessage(sd->fd, output);
+ }
+ } else
+ ShowError("chrif_char_ask_name_answer failed - player not online.\n");
+
+ return 0;
+}
+
+/*==========================================
+ * End of GM change (@GM) (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedgm(int fd)
+{
+ int acc, level;
+ struct map_session_data *sd = NULL;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ level = RFIFOL(fd,6);
+
+ sd = map_id2sd(acc);
+
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level);
+ if (sd != NULL) {
+ if (level > 0)
+ clif_displaymessage(sd->fd, "GM modification success.");
+ else
+ clif_displaymessage(sd->fd, "Failure of GM modification.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * «•Ê•Ï‰»I—¹ (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedsex(int fd)
+{
+ int acc, sex, i;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ sex = RFIFOL(fd,6);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedsex %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL && sd->status.sex != sex) {
+ if (sd->status.sex == 0) {
+ sd->status.sex = 1;
+ sd->sex = 1;
+ } else if (sd->status.sex == 1) {
+ sd->status.sex = 0;
+ sd->sex = 0;
+ }
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip)
+ pc_unequipitem((struct map_session_data*)sd, i, 2);
+ }
+ // reset skill of some job
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
+ // remove specifical skills of Bard classes
+ for(i = 315; i <= 322; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ // remove specifical skills of Dancer classes
+ for(i = 323; i <= 330; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ // change job if necessary
+ if (sd->status.sex) //Changed from Dancer
+ sd->status.class_ -= 1;
+ else //Changed from Bard
+ sd->status.class_ += 1;
+ //sd->class_ needs not be updated as both Dancer/Bard are the same.
+ }
+ // save character
+ //chrif_save(sd,1); Character will be saved on session closed -> map_quit
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
+ clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL) {
+ ShowError("chrif_changedsex failed.\n");
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * —£¥î•ñ“¯Šú—v‹
+ *------------------------------------------
+ */
+int chrif_divorce(int char_id, int partner_id)
+{
+ struct map_session_data *sd = NULL;
+
+ if (!char_id || !partner_id)
+ return 0;
+
+ nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id)));
+ if (sd->status.partner_id == char_id) {
+ int i;
+ //—£¥(‘Š•û‚ÍŠù‚ɃLƒƒƒ‰‚ªÁ‚¦‚Ä‚¢‚锤‚È‚Ì‚Å)
+ sd->status.partner_id = 0;
+
+ //‘Š•û‚ÌŒ‹¥Žw—Ö‚ð”’D
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(sd, i, 1, 0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been deleted in login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountdeletion(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountdeletion %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountdeletion failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountban(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountban %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban
+ switch (RFIFOL(fd,7)) { // status or final date of a banishment
+ case 1: // 0 = Unregistered ID
+ clif_displaymessage(sd->fd, "Your account has 'Unregistered'.");
+ break;
+ case 2: // 1 = Incorrect Password
+ clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'...");
+ break;
+ case 3: // 2 = This ID is expired
+ clif_displaymessage(sd->fd, "Your account has expired.");
+ break;
+ case 4: // 3 = Rejected from Server
+ clif_displaymessage(sd->fd, "Your account has been rejected from server.");
+ break;
+ case 5: // 4 = You have been blocked by the GM Team
+ clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team.");
+ break;
+ case 6: // 5 = Your Game's EXE file is not the latest version
+ clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version.");
+ break;
+ case 7: // 6 = Your are Prohibited to log in until %s
+ clif_displaymessage(sd->fd, "Your account has been prohibited to log in.");
+ break;
+ case 8: // 7 = Server is jammed due to over populated
+ clif_displaymessage(sd->fd, "Server is jammed due to over populated.");
+ break;
+ case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this)
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ case 100: // 99 = This ID has been totally erased
+ clif_displaymessage(sd->fd, "Your account has been totally erased.");
+ break;
+ default:
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ }
+ } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban
+ time_t timestamp;
+ char tmpstr[2048];
+ timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment
+ strcpy(tmpstr, "Your account has been banished until ");
+ strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(&timestamp));
+ clif_displaymessage(sd->fd, tmpstr);
+ }
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountban failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+//Disconnect the player out of the game, simple packet
+//packet.w AID.L WHY.B 2+4+1 = 7byte
+int chrif_disconnectplayer(int fd){
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ sd = map_id2sd(RFIFOL(fd, 2));
+
+ if(sd == NULL){
+ return -1;
+ }
+
+ if (!sd->fd)
+ { //No connection
+ if (sd->state.autotrade)
+ map_quit(sd); //Remove it.
+ //Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
+ //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
+ return 0;
+ }
+
+ switch(RFIFOB(fd, 6)){
+ //clif_authfail_fd
+ case 1: //server closed
+ clif_authfail_fd(sd->fd, 1);
+ break;
+
+ case 2: //someone else logged in
+ clif_authfail_fd(sd->fd, 2);
+ break;
+
+ case 3: //server overpopulated
+ clif_authfail_fd(sd->fd, 4);
+
+ break;
+
+ case 4: //out of time payd for .. (avail)
+ clif_authfail_fd(sd->fd, 10);
+ break;
+
+ case 5: //forced to dc by gm
+ clif_authfail_fd(sd->fd, 15);
+ break;
+ }
+
+return 0;
+}
+
+/*==========================================
+ * Request to reload GM accounts and their levels: send to char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_reloadGMdb(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2af7;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+/*==========================================
+ * Receiving GM accounts and their levels from char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_recvgmaccounts(int fd)
+{
+ ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd));
+ return 0;
+}
+
+/*==========================================
+ * Request/Receive top 10 Fame character list
+ *------------------------------------------
+ */
+int chrif_reqfamelist(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2b1a;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+int chrif_recvfamelist(int fd)
+{ // response from 0x2b1b
+ int num, size;
+ int total = 0, len = 8;
+ RFIFOHEAD(fd);
+
+ memset (smith_fame_list, 0, sizeof(smith_fame_list));
+ memset (chemist_fame_list, 0, sizeof(chemist_fame_list));
+ memset (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
+
+ size = RFIFOW(fd,6); //Blacksmith block size
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd,4); //Alchemist block size
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd,2); //Total packet length
+ for (num = 0; len < size && num < 10; num++) {
+ memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
+
+ return 0;
+}
+
+int chrif_save_scdata(struct map_session_data *sd)
+{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ int i, count=0;
+ unsigned int tick;
+ struct status_change_data data;
+ struct TimerData *timer;
+
+#ifndef TXT_ONLY
+ if(charsave_method) //New 'Local' save
+ {
+ charsave_save_scdata(sd->status.account_id, sd->status.char_id, sd->sc_data, MAX_STATUSCHANGE);
+ return 0;
+ }
+#endif
+
+ chrif_check(-1);
+ tick = gettick();
+
+ WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
+ WFIFOW(char_fd,0) = 0x2b1c;
+ WFIFOL(char_fd,4) = sd->status.account_id;
+ WFIFOL(char_fd,8) = sd->status.char_id;
+ for (i = 0; i < SC_MAX; i++)
+ {
+ if (sd->sc_data[i].timer == -1)
+ continue;
+ timer = get_timer(sd->sc_data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+ data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending.
+ data.type = i;
+ data.val1 = sd->sc_data[i].val1;
+ data.val2 = sd->sc_data[i].val2;
+ data.val3 = sd->sc_data[i].val3;
+ data.val4 = sd->sc_data[i].val4;
+ memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)),
+ &data, sizeof(struct status_change_data));
+ count++;
+ }
+ if (count == 0)
+ return 0; //Nothing to save.
+ WFIFOW(char_fd,12) = count;
+ WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
+ WFIFOSET(char_fd,WFIFOW(char_fd,2));
+#endif
+ return 0;
+}
+
+int chrif_load_scdata(int fd)
+{ //Retrieve and load sc_data for a player. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ struct map_session_data *sd;
+ struct status_change_data data;
+ int aid, cid, i, count;
+ RFIFOHEAD(fd);
+
+ aid = RFIFOL(fd,4); //Player Account ID
+ cid = RFIFOL(fd,8); //Player Char ID
+
+ sd = map_id2sd(aid);
+ if (!sd)
+ {
+ ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
+ return -1;
+ }
+ if (sd->status.char_id != cid)
+ {
+ ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
+ return -1;
+ }
+ count = RFIFOW(fd,12); //sc_count
+ for (i = 0; i < count; i++)
+ {
+ memcpy(&data, RFIFOP(fd,14 + i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
+ if (data.tick < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data.tick, sd->status.name);
+ continue;
+ }
+ status_change_start(&sd->bl, data.type, data.val1, data.val2, data.val3, data.val4, data.tick, 7);
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * Send rates and motd to char server [Wizputer]
+ *------------------------------------------
+ */
+ int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate)
+{
+ char buf[256];
+ FILE *fp;
+ int i;
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, sizeof(buf) + 10);
+ WFIFOW(char_fd,0) = 0x2b16;
+ WFIFOW(char_fd,2) = base_rate;
+ WFIFOW(char_fd,4) = job_rate;
+ WFIFOW(char_fd,6) = drop_rate;
+
+ if ((fp = fopen(motd_txt, "r")) != NULL) {
+ if (fgets(buf, 250, fp) != NULL) {
+ for(i = 0; buf[i]; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = 0;
+ break;
+ }
+ }
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ fclose(fp);
+ } else {
+ memset(buf, 0, sizeof(buf)); //No data found, send empty packets?
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ WFIFOSET(char_fd,WFIFOW(char_fd,8));
+ return 0;
+}
+
+
+/*=========================================
+ * Tell char-server charcter disconnected [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_offline(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b17;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_flush_fifo(void) {
+ chrif_check(-1);
+
+ set_nonblocking(char_fd, 0);
+ flush_fifos();
+ set_nonblocking(char_fd, 1);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_char_reset_offline(void) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2b18;
+ WFIFOSET(char_fd,2);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server charcter is online [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_online(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b19;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_disconnect_sub(struct map_session_data* sd,va_list va) {
+ if (sd->fd)
+ clif_authfail_fd(sd->fd,1);
+ else
+ map_quit(sd);
+ return 0;
+}
+
+int chrif_disconnect(int fd) {
+ if(fd == char_fd) {
+ char_fd = 0;
+ ShowWarning("Map Server disconnected from Char Server.\n\n");
+ if (kick_on_disconnect)
+ clif_foreachclient(chrif_disconnect_sub);
+ chrif_connected = 0;
+ // ‘¼‚Ìmap ŽI‚̃f[ƒ^‚ðÁ‚·
+ map_eraseallipport();
+
+ // ‘qŒÉƒLƒƒƒbƒVƒ…‚ðÁ‚·
+ if (kick_on_disconnect)
+ { //Do not clean the storage if players are gonna be left inside. [Skotlex]
+ do_final_storage();
+ do_init_storage();
+ }
+ //Attempt to reconnect in a second. [Skotlex]
+ add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_parse(int fd)
+{
+ int packet_len, cmd;
+ // only char-server can have an access to here.
+ // so, if it isn't the char-server, we disconnect the session (fd != char_fd).
+ if (fd != char_fd || session[fd]->eof) {
+ if (fd == char_fd && chrif_connected == 1) {
+ chrif_disconnect (fd);
+ }
+ else if (fd != char_fd)
+ ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd);
+
+ do_close(fd);
+ return 0;
+ }
+
+ while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex]
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) ||
+ packet_len_table[cmd-0x2af8] == 0) {
+
+ int r = intif_parse(fd); // intif‚É“n‚·
+
+ if (r == 1) continue; // intif‚ň—‚µ‚½
+ if (r == 2) return 0; // intif‚ň—‚µ‚½‚ªAƒf[ƒ^‚ª‘«‚è‚È‚¢
+
+ session[fd]->eof = 1;
+ ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd);
+ return 0;
+ }
+ packet_len = packet_len_table[cmd-0x2af8];
+ if (packet_len == -1) {
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ packet_len = RFIFOW(fd,2);
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0;
+
+ switch(cmd) {
+ case 0x2af9: chrif_connectack(fd); break;
+ case 0x2afb: chrif_sendmapack(fd); chrif_reqfamelist(); break;
+ case 0x2afd: chrif_authok(fd); break;
+ case 0x2b00: map_setusers(fd); break;
+ case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
+ case 0x2b04: chrif_recvmap(fd); break;
+ case 0x2b06: chrif_changemapserverack(fd); break;
+ case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
+ case 0x2b0b: chrif_changedgm(fd); break;
+ case 0x2b0d: chrif_changedsex(fd); break;
+ case 0x2b0f: chrif_char_ask_name_answer(fd); break;
+ case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b13: chrif_accountdeletion(fd); break;
+ case 0x2b14: chrif_accountban(fd); break;
+ case 0x2b15: chrif_recvgmaccounts(fd); break;
+ case 0x2b1b: chrif_recvfamelist(fd); break;
+ case 0x2b1d: chrif_load_scdata(fd); break;
+ case 0x2b1f: chrif_disconnectplayer(fd); break;
+ case 0x2b20: chrif_removemap(fd); break; //Remove maps of a server [Sirius]
+
+ default:
+ if (battle_config.error_log)
+ ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex]
+ RFIFOSKIP(fd, packet_len);
+ }
+
+ return 0;
+}
+
+int send_usercount_tochar(int tid, unsigned int tick, int id, int data) {
+ int count;
+ static int last_count = 0;
+
+ chrif_check(-1);
+
+ map_getallusers(&count);
+
+ if (count == last_count) //No need to waste packets.
+ return 0;
+ last_count = count;
+
+ WFIFOHEAD(char_fd, 4);
+ WFIFOW(char_fd,0) = 0x2afe;
+ WFIFOW(char_fd,2) = count;
+ WFIFOSET(char_fd,4);
+ return 0;
+}
+
+/*==========================================
+ * timerŠÖ”
+ * ¡‚±‚ÌmapŽI‚ÉŒq‚ª‚Á‚Ä‚¢‚éƒNƒ‰ƒCƒAƒ“ƒgl”‚ðcharŽI‚Ö‘—‚é
+ *------------------------------------------
+ */
+int send_users_tochar(int tid, unsigned int tick, int id, int data) {
+ int count, users=0, i;
+ struct map_session_data **all_sd;
+
+ chrif_check(-1);
+
+ all_sd = map_getallusers(&count);
+ WFIFOHEAD(char_fd, 6+8*users);
+ WFIFOW(char_fd,0) = 0x2aff;
+ for (i = 0; i < count; i++) {
+ if (all_sd[i] &&
+ !((battle_config.hide_GM_session || (all_sd[i]->status.option & OPTION_INVISIBLE)) && pc_isGM(all_sd[i])))
+ {
+ WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id;
+ WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id;
+ users++;
+ }
+ }
+ WFIFOW(char_fd,2) = 6 + 8 * users;
+ WFIFOW(char_fd,4) = users;
+ WFIFOSET(char_fd,6+8*users);
+
+ return 0;
+}
+
+/*==========================================
+ * timerŠÖ”
+ * charŽI‚Æ‚ÌÚ‘±‚ðŠm”F‚µA‚à‚µØ‚ê‚Ä‚¢‚½‚çÄ“xÚ‘±‚·‚é
+ *------------------------------------------
+ */
+int check_connect_char_server(int tid, unsigned int tick, int id, int data) {
+ static int displayed = 0;
+ if (char_fd <= 0 || session[char_fd] == NULL) {
+ if (!displayed) {
+ ShowStatus("Attempting to connect to Char Server. Please wait.\n");
+ displayed = 1;
+ }
+ chrif_state = 0;
+ char_fd = make_connection(char_ip, char_port);
+ if (char_fd == -1)
+ { //Attempt to connect later. [Skotlex]
+ char_fd = 0;
+ return 0;
+ }
+ session[char_fd]->func_parse = chrif_parse;
+ realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+ chrif_connect(char_fd);
+ chrif_connected = (chrif_state == 2);
+#ifndef TXT_ONLY
+ srvinfo = 0;
+#endif /* not TXT_ONLY */
+ } else {
+#ifndef TXT_ONLY
+ if (srvinfo == 0) {
+ chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common);
+ srvinfo = 1;
+ }
+#endif /* not TXT_ONLY */
+/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex]
+ //If for some reason the next iteration (10 secs) we are still not connected,
+ //assume the packets got lost, so we need to resend them. [Skotlex]
+ if (chrif_state == 0)
+ chrif_connect(char_fd);
+ else if (chrif_state == 1)
+ chrif_sendmap(char_fd);
+*/
+ }
+ if (chrif_isconnect()) displayed = 0;
+ return 0;
+}
+
+int auth_db_final(DBKey k,void *d,va_list ap) {
+ struct auth_node *node=(struct auth_node*)d;
+ if (node->char_dat)
+ aFree(node->char_dat);
+ return 0;
+}
+
+/*==========================================
+ * I—¹
+ *------------------------------------------
+ */
+int do_final_chrif(void)
+{
+ delete_session(char_fd);
+ auth_db->destroy(auth_db, auth_db_final);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_chrif(void)
+{
+ add_timer_func_list(check_connect_char_server, "check_connect_char_server");
+ add_timer_func_list(send_usercount_tochar, "send_usercount_tochar");
+ add_timer_func_list(send_users_tochar, "send_users_tochar");
+ add_timer_func_list(auth_db_cleanup, "auth_db_cleanup");
+ add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000);
+#ifdef TXT_ONLY
+ //Txt needs this more frequently because it is used for the online.html file.
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL);
+#else
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL);
+ add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL);
+#endif
+ add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000);
+
+ auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ return 0;
+}
diff --git a/src/map/chrif.h b/src/map/chrif.h
new file mode 100644
index 000000000..1be6d4182
--- /dev/null
+++ b/src/map/chrif.h
@@ -0,0 +1,54 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHRIF_H_
+#define _CHRIF_H_
+
+struct auth_node{
+ int account_id, login_id1, login_id2, sex, fd;
+ time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+ struct map_session_data *sd; //Data from logged on char.
+ struct mmo_charstatus *char_dat; //Data from char server.
+ unsigned int node_created; //For node auto-deleting
+};
+
+void chrif_setuserid(char*);
+void chrif_setpasswd(char*);
+void chrif_setip(char*);
+void chrif_setport(int);
+
+int chrif_isconnect(void);
+
+extern int chrif_connected;
+
+void chrif_authreq(struct map_session_data *);
+void chrif_authok(int fd);
+int chrif_save(struct map_session_data*, int flag);
+int chrif_charselectreq(struct map_session_data *);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port);
+
+int chrif_searchcharid(int char_id);
+int chrif_changegm(int id,const char *pass,int len);
+int chrif_changeemail(int id, const char *actual_email, const char *new_email);
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second);
+int chrif_reloadGMdb(void);
+int chrif_reqfamelist(void);
+int chrif_save_scdata(struct map_session_data *sd);
+int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate);
+int chrif_char_offline(struct map_session_data *sd);
+int chrif_char_reset_offline(void);
+int send_users_tochar(int tid, unsigned int tick, int id, int data);
+int chrif_char_online(struct map_session_data *sd);
+int chrif_changesex(int id, int sex);
+int chrif_chardisconnect(struct map_session_data *sd);
+int check_connect_char_server(int tid, unsigned int tick, int id, int data);
+
+int chrif_pcauthok(int fd);
+
+int do_final_chrif(void);
+int do_init_chrif(void);
+
+int chrif_flush_fifo(void);
+
+#endif
diff --git a/src/map/clif.c b/src/map/clif.c
new file mode 100644
index 000000000..77e50d0d6
--- /dev/null
+++ b/src/map/clif.c
@@ -0,0 +1,12146 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#define DUMP_UNKNOWN_PACKET 0
+#define DUMP_ALL_PACKETS 0
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <time.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "itemdb.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "script.h"
+#include "skill.h"
+#include "atcommand.h"
+#include "charcommand.h"
+#include "intif.h"
+#include "battle.h"
+#include "mob.h"
+#include "party.h"
+#include "guild.h"
+#include "vending.h"
+#include "pet.h"
+#include "log.h"
+
+struct Clif_Config {
+ int packet_db_ver; //Preferred packet version.
+ int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex]
+} clif_config;
+
+struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
+
+static const int packet_len_table[MAX_PACKET_DB] = {
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//#0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2,
+#if PACKETVER < 2
+ 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6,
+#else // 78-7b ‹T“‡ˆÈ~ lv99ƒGƒtƒFƒNƒg—p
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+#endif
+//#0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23)
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+//#0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6)
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+
+//#0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+//#0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
+ 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+//#0x0180
+ 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+#if PACKETVER < 1
+ 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#else // 196 comodoˆÈ~ ó‘Ô•\Ž¦ƒAƒCƒRƒ“—p
+ 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#endif
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+ 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+//#0x01C0, Set 0x1d5=-1
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28,
+ 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+//#0x200
+ 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19)
+ 22, 0, 26, 26, 42, -1, -1, 2, 2,282,282,10, 10, -1, -1, 66,
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 69, 5,
+ 12, 26, 9, 11, -1, -1, 10, 2, 282, 11, 4, 36, -1,-1, 4, 2,
+ -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8,12, 4, 10,
+ 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, -1, 4,-1, 4
+};
+
+// local define
+enum {
+ ALL_CLIENT,
+ ALL_SAMEMAP,
+ AREA,
+ AREA_WOS,
+ AREA_WOC,
+ AREA_WOSC,
+ AREA_CHAT_WOC,
+ CHAT,
+ CHAT_WOS,
+ CHAT_MAINCHAT,
+ PARTY,
+ PARTY_WOS,
+ PARTY_SAMEMAP,
+ PARTY_SAMEMAP_WOS,
+ PARTY_AREA,
+ PARTY_AREA_WOS,
+ GUILD,
+ GUILD_WOS,
+ GUILD_SAMEMAP, // [Valaris]
+ GUILD_SAMEMAP_WOS,
+ GUILD_AREA,
+ GUILD_AREA_WOS, // end additions [Valaris]
+ SELF
+};
+
+//Converts item type in case of pet eggs.
+#define itemtype(a) (a == 7)?4:a
+
+#define WBUFPOS(p,pos,x,y) { unsigned char *__p = (p); __p+=(pos); __p[0] = (x)>>2; __p[1] = ((x)<<6) | (((y)>>4)&0x3f); __p[2] = (y)<<4; }
+#define WBUFPOS2(p,pos,x0,y0,x1,y1) { unsigned char *__p = (p); __p+=(pos); __p[0] = (unsigned char)((x0)>>2); __p[1] = (unsigned char)(((x0)<<6) | (((y0)>>4)&0x3f)); __p[2] = (unsigned char)(((y0)<<4) | (((x1)>>6)&0x0f)); __p[3]=(unsigned char)(((x1)<<2) | (((y1)>>8)&0x03)); __p[4]=(unsigned char)(y1); }
+
+#define WFIFOPOS(fd,pos,x,y) { WBUFPOS (WFIFOP(fd,pos),0,x,y); }
+#define WFIFOPOS2(fd,pos,x0,y0,x1,y1) { WBUFPOS2(WFIFOP(fd,pos),0,x0,y0,x1,y1); }
+
+//To make the assignation of the level based on limits clearer/easier. [Skotlex]
+#define clif_setlevel(lv) (lv<battle_config.max_lv?lv:battle_config.max_lv-(lv<battle_config.aura_lv?1:0));
+
+static char map_ip_str[16];
+static in_addr_t map_ip;
+static in_addr_t bind_ip = INADDR_ANY;
+static int map_port = 5121;
+int map_fd;
+char talkie_mes[MESSAGE_SIZE];
+
+//These two will be used to verify the incoming player's validity.
+//It helps identify their client packet version correctly. [Skotlex]
+static int max_account_id = DEFAULT_MAX_ACCOUNT_ID;
+static int max_char_id = DEFAULT_MAX_CHAR_ID;
+
+int clif_parse (int fd);
+static void clif_hpmeter_single(int fd, struct map_session_data *sd);
+
+/*==========================================
+ * mapŽI‚ÌipÝ’è
+ *------------------------------------------
+ */
+void clif_setip(char *ip)
+{
+ memcpy(map_ip_str, ip, 16);
+ map_ip = inet_addr(map_ip_str);
+}
+
+void clif_setbindip(char *ip)
+{
+ bind_ip = inet_addr(ip);
+}
+
+/*==========================================
+ * mapŽI‚ÌportÝ’è
+ *------------------------------------------
+ */
+void clif_setport(int port)
+{
+ map_port = port;
+}
+
+/*==========================================
+ * mapŽI‚Ìip“Ç‚Ýo‚µ
+ *------------------------------------------
+ */
+in_addr_t clif_getip(void)
+{
+ return map_ip;
+}
+
+/*==========================================
+ * mapŽI‚Ìport“Ç‚Ýo‚µ
+ *------------------------------------------
+ */
+int clif_getport(void)
+{
+ return map_port;
+}
+
+/*==========================================
+ * Counts connected players.
+ *------------------------------------------
+ */
+int clif_countusers(void)
+{
+ int users = 0, i;
+ struct map_session_data *sd;
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) && sd->state.auth &&
+ !(battle_config.hide_GM_session && pc_isGM(sd)))
+ users++;
+ }
+ return users;
+}
+
+/*==========================================
+ * ‘S‚Ä‚Ìclient‚ɑ΂µ‚Äfunc()ŽÀs
+ *------------------------------------------
+ */
+
+int clif_foreachclient(int (*func)(struct map_session_data*, va_list),...) //recoded by sasuke, bug when player count gets higher [Kevin]
+{
+ int i;
+ va_list ap;
+ struct map_session_data *sd;
+
+ va_start(ap,func);
+
+ for(i = 0; i < fd_max; i++) {
+ if ( session[i] ) {
+ sd = (struct map_session_data*)session[i]->session_data;
+ if ( sd && session[i]->func_parse == clif_parse &&
+ sd->state.auth && !sd->state.waitingdisconnect )
+ func(sd, ap);
+ }
+ }
+
+ va_end(ap);
+ return 0;
+}
+
+/*==========================================
+ * clif_send‚ÅAREA*Žw’莞—p
+ *------------------------------------------
+ */
+int clif_send_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list *src_bl;
+ struct map_session_data *sd;
+ unsigned char *buf;
+ int len, type;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+
+ if (!sd->fd) //Avoid attempting to send to disconnected chars (may prevent buffer overrun errors?) [Skotlex]
+ return 0;
+
+ buf = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ nullpo_retr(0, src_bl = va_arg(ap,struct block_list*));
+ type = va_arg(ap,int);
+
+ switch(type) {
+ case AREA_WOS:
+ if (bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOC:
+ if (sd->chatID || bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOSC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)src_bl;
+ if ((ssd != 0) && (src_bl->type == BL_PC) && (sd->chatID != 0) && (sd->chatID == ssd->chatID))
+ return 0;
+ }
+ break;
+ }
+
+ if (session[sd->fd] != NULL) {
+ WFIFOHEAD(sd->fd, len);
+ if (WFIFOP(sd->fd,0) == buf) {
+ printf("WARNING: Invalid use of clif_send function\n");
+ printf(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
+ printf(" Please correct your code.\n");
+ // don't send to not move the pointer of the packet for next sessions in the loop
+ } else {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ //Check if hidden, better to modify the char's buffer than the
+ //given buffer to prevent intravision affecting the packet as
+ //it's being received by everyone. [Skotlex]
+ if ((sd->special_state.intravision || sd->sc_data[SC_INTRAVISION].timer != -1 ) && bl != src_bl) {
+ short *src_option = status_get_option(src_bl);
+ if(src_option && (*src_option)&(OPTION_HIDE|OPTION_CLOAK))
+ { //option‚ÌC³
+ switch(((unsigned short*)buf)[0])
+ {
+ case 0x119:
+ WFIFOW(sd->fd,10) &= ~(OPTION_HIDE|OPTION_CLOAK);
+ break;
+#if PACKETVER < 4
+ case 0x78:
+#else
+ case 0x1da:
+#endif
+ case 0x7b:
+ case 0x7c:
+ case 0x1d8:
+ WFIFOW(sd->fd,12) &=~(OPTION_HIDE|OPTION_CLOAK);
+ break;
+ }
+ }
+ }
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) {
+ int i;
+ struct map_session_data *sd = NULL;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+ int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
+
+ if (type != ALL_CLIENT &&
+ type != CHAT_MAINCHAT) {
+ nullpo_retr(0, bl);
+ if (bl->type == BL_PC) {
+ sd = (struct map_session_data *)bl;
+ }
+ }
+
+ switch(type) {
+ case ALL_CLIENT: // ‘SƒNƒ‰ƒCƒAƒ“ƒg‚É‘—M
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL && sd->state.auth) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(i, len);
+ memcpy(WFIFOP(i,0), buf, len);
+ WFIFOSET(i,len);
+ }
+ }
+ }
+ break;
+ case ALL_SAMEMAP: // “¯‚¶ƒ}ƒbƒv‚Ì‘SƒNƒ‰ƒCƒAƒ“ƒg‚É‘—M
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL &&
+ sd->state.auth && sd->bl.m == bl->m) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(i,len);
+ memcpy(WFIFOP(i,0), buf, len);
+ WFIFOSET(i,len);
+ }
+ }
+ }
+ break;
+ case AREA:
+ case AREA_WOS:
+ case AREA_WOC:
+ case AREA_WOSC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE,
+ BL_PC, buf, len, bl, type);
+ break;
+ case AREA_CHAT_WOC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5),
+ bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC);
+ break;
+ case CHAT:
+ case CHAT_WOS:
+ {
+ struct chat_data *cd;
+ if (sd) {
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ } else if (bl->type == BL_CHAT) {
+ cd = (struct chat_data*)bl;
+ } else break;
+ if (cd == NULL)
+ break;
+ for(i = 0; i < cd->users; i++) {
+ if (type == CHAT_WOS && cd->usersd[i] == sd)
+ continue;
+ if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ if (cd->usersd[i]->fd >0 && session[cd->usersd[i]->fd]) // Added check to see if session exists [PoW]
+ {
+ WFIFOHEAD(cd->usersd[i]->fd,len);
+ memcpy(WFIFOP(cd->usersd[i]->fd,0), buf, len);
+ WFIFOSET(cd->usersd[i]->fd,len);
+ }
+ }
+ }
+ }
+ break;
+ case CHAT_MAINCHAT: //[LuzZza]
+ for(i=1; i<fd_max; i++) {
+ if(session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL &&
+ sd->state.mainchat && sd->fd) {
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd, len);
+ }
+ }
+ break;
+ case PARTY_AREA: // “¯‚¶‰æ–Ê“à‚Ì‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ case PARTY_AREA_WOS: // Ž©•ªˆÈŠO‚Ì“¯‚¶‰æ–Ê“à‚Ì‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case PARTY: // ‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ case PARTY_WOS: // Ž©•ªˆÈŠO‚Ì‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ case PARTY_SAMEMAP: // “¯‚¶ƒ}ƒbƒv‚Ì‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ case PARTY_SAMEMAP_WOS: // Ž©•ªˆÈŠO‚Ì“¯‚¶ƒ}ƒbƒv‚Ì‘Sƒp[ƒeƒB[ƒƒ“ƒo‚É‘—M
+ if (sd && sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if (p) {
+ for(i=0;i<MAX_PARTY;i++){
+ if ((sd = p->member[i].sd) != NULL) {
+ if (!sd->fd || session[sd->fd] == NULL || sd->state.auth == 0
+ || session[sd->fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER)
+ continue;
+
+ if (sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS))
+ continue;
+
+ if (type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m) // ƒ}ƒbƒvƒ`ƒFƒbƒN
+ continue;
+
+ if ((type == PARTY_AREA || type == PARTY_AREA_WOS) &&
+ (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1))
+ continue;
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+ for (i = 1; i < fd_max; i++){ // partyspy [Syrus22]
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->partyspy) {
+ if (sd->partyspy == p->party_id) {
+ if (sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case SELF:
+ if (sd && sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ break;
+
+// New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex]
+ case GUILD_AREA:
+ case GUILD_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case GUILD_SAMEMAP:
+ case GUILD_SAMEMAP_WOS:
+ case GUILD:
+ case GUILD_WOS:
+ if (sd && sd->status.guild_id)
+ g = guild_search(sd->status.guild_id);
+
+ if (g) {
+ for(i = 0; i < g->max_member; i++) {
+ if ((sd = g->member[i].sd) != NULL) {
+ if (!sd->fd || session[sd->fd] == NULL || sd->state.auth == 0
+ || session[sd->fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER)
+ continue;
+
+ if (sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS))
+ continue;
+
+ if (type != GUILD && type != GUILD_WOS && sd->bl.m != bl->m)
+ continue;
+
+ if ((type == GUILD_AREA || type == GUILD_AREA_WOS) &&
+ (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1))
+ continue;
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+ for (i = 1; i < fd_max; i++){ // guildspy [Syrus22]
+ if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->guildspy) {
+ if (sd->guildspy == g->guild_id) {
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(sd->fd,len);
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ }
+ }
+ break;
+/* End [Valaris] */
+
+ default:
+ if (battle_config.error_log)
+ ShowError("clif_send: Unrecognized type %d\n",type);
+ return -1;
+ }
+
+ return 0;
+}
+
+//
+// ƒpƒPƒbƒgì‚Á‚Ä‘—M
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_authok(struct map_session_data *sd) {
+ int fd;
+
+ if (!sd->fd)
+ return 0;
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len_table[0x73]);
+ WFIFOW(fd, 0) = 0x73;
+ WFIFOL(fd, 2) = gettick();
+ WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y);
+ WFIFOB(fd, 9) = 5;
+ WFIFOB(fd,10) = 5;
+ WFIFOSET(fd,packet_len_table[0x73]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_authfail_fd(int fd, int type) {
+ if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players!
+ return 0;
+
+ WFIFOHEAD(fd, packet_len_table[0x81]);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len_table[0x81]);
+ clif_setwaitclose(fd);
+ return 0;
+}
+
+/*==========================================
+ * Used to know which is the max valid account/char id [Skotlex]
+ *------------------------------------------
+ */
+void clif_updatemaxid(int account_id, int char_id)
+{
+ max_account_id = account_id;
+ max_char_id = char_id;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_charselectok(int id) {
+ struct map_session_data *sd;
+ int fd;
+
+ if ((sd = map_id2sd(id)) == NULL)
+ return 1;
+
+ if (!sd->fd)
+ return 1;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb3]);
+ WFIFOW(fd,0) = 0xb3;
+ WFIFOB(fd,2) = 1;
+ WFIFOSET(fd,packet_len_table[0xb3]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set009e(struct flooritem_data *fitem,unsigned char *buf) {
+ int view;
+
+ nullpo_retr(0, fitem);
+
+ //009e <ID>.l <name ID>.w <identify flag>.B <X>.w <Y>.w <subX>.B <subY>.B <amount>.w
+ WBUFW(buf, 0) = 0x9e;
+ WBUFL(buf, 2) = fitem->bl.id;
+ if ((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WBUFW(buf, 6) = view;
+ else
+ WBUFW(buf, 6) = fitem->item_data.nameid;
+ WBUFB(buf, 8) = fitem->item_data.identify;
+ WBUFW(buf, 9) = fitem->bl.x;
+ WBUFW(buf,11) = fitem->bl.y;
+ WBUFB(buf,13) = fitem->subx;
+ WBUFB(buf,14) = fitem->suby;
+ WBUFW(buf,15) = fitem->item_data.amount;
+
+ return packet_len_table[0x9e];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_dropflooritem(struct flooritem_data *fitem) {
+ unsigned char buf[64];
+
+ nullpo_retr(0, fitem);
+
+ if (fitem->item_data.nameid <= 0)
+ return 0;
+ clif_set009e(fitem, buf);
+ clif_send(buf, packet_len_table[0x9e], &fitem->bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearflooritem(struct flooritem_data *fitem, int fd) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, fitem);
+
+ WBUFW(buf,0) = 0xa1;
+ WBUFL(buf,2) = fitem->bl.id;
+
+ if (fd == 0) {
+ clif_send(buf, packet_len_table[0xa1], &fitem->bl, AREA);
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0xa1]);
+ memcpy(WFIFOP(fd,0), buf, 6);
+ WFIFOSET(fd,packet_len_table[0xa1]);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchar(struct block_list *bl, int type) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = bl->id;
+ WBUFB(buf,6) = type;
+ clif_send(buf, packet_len_table[0x80], bl, type == 1 ? AREA : AREA_WOS);
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len_table[0x80], bl, AREA);
+ }
+
+ return 0;
+}
+
+static int clif_clearchar_delay_sub(int tid, unsigned int tick, int id, int data) {
+ struct block_list *bl = (struct block_list *)id;
+
+ clif_clearchar(bl,data);
+ aFree(bl);
+ return 0;
+}
+
+int clif_clearchar_delay(unsigned int tick, struct block_list *bl, int type) {
+ struct block_list *tbl;
+ tbl = aCalloc(1, sizeof (struct block_list));
+ memcpy (tbl, bl, sizeof (struct block_list));
+ add_timer(tick, clif_clearchar_delay_sub, (int)tbl, type);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchar_id(int id, int type, int fd) {
+ unsigned char buf[16];
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = id;
+ WBUFB(buf,6) = type;
+ WFIFOHEAD(fd, packet_len_table[0x80]);
+ memcpy(WFIFOP(fd,0), buf, 7);
+ WFIFOSET(fd, packet_len_table[0x80]);
+
+ return 0;
+}
+
+//Small define to specify the weapon view sprite, makes code easier to read down below... [Skotlex]
+#define clif_weapon_viewid(sd, n) ((sd->equip_index[n] >= 0 && sd->inventory_data[sd->equip_index[n]])?(\
+ (sd->inventory_data[sd->equip_index[n]]->view_id > 0)?sd->inventory_data[sd->equip_index[n]]->view_id: \
+ sd->status.inventory[sd->equip_index[n]].nameid):0)
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set0078(struct map_session_data *sd, unsigned char *buf) {
+ int sdoption;
+
+ nullpo_retr(0, sd);
+
+ // Disable showing Falcon when player is hide [LuzZza]
+ if(sd->disguise)
+ sdoption = OPTION_INVISIBLE;
+ else {
+ sdoption = sd->status.option;
+ if(sdoption&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE))
+ sdoption &= ~OPTION_FALCON;
+ }
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if (sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS)
+ WBUFW(buf,18) = sd->status.weapon;
+ else
+ WBUFW(buf,18)=0;
+ WBUFW(buf,20)=sd->status.head_bottom;
+ WBUFW(buf,22)=sd->status.shield;
+ WBUFW(buf,24)=sd->status.head_top;
+ WBUFW(buf,26)=sd->status.head_mid;
+ WBUFW(buf,28)=sd->status.hair_color;
+ WBUFW(buf,30)=sd->status.clothes_color;
+ WBUFW(buf,32)=sd->head_dir;
+ WBUFL(buf,34)=sd->status.guild_id;
+ WBUFL(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,42)=sd->status.manner;
+ WBUFB(buf,44)=sd->status.karma;
+ WBUFB(buf,45)=sd->sex;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x78];
+#else
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if (sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS)
+ WBUFW(buf,18) = clif_weapon_viewid(sd,9);
+ else
+ WBUFW(buf,18) = 0;
+ if (sd->equip_index[8] != sd->equip_index[9] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,20) = clif_weapon_viewid(sd,8);
+ else
+ WBUFW(buf,20) = 0;
+ WBUFW(buf,22)=sd->status.head_bottom;
+ WBUFW(buf,24)=sd->status.head_top;
+ WBUFW(buf,26)=sd->status.head_mid;
+ WBUFW(buf,28)=sd->status.hair_color;
+ WBUFW(buf,30)=sd->status.clothes_color;
+ WBUFW(buf,32)=sd->head_dir;
+ WBUFL(buf,34)=sd->status.guild_id;
+ WBUFW(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,40)=sd->status.manner;
+ WBUFW(buf,42)=sd->opt3;
+ WBUFB(buf,44)=sd->status.karma;
+ WBUFB(buf,45)=sd->sex;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir & 0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x1d8];
+#endif
+}
+
+// non-moving function for disguises [Valaris]
+static int clif_dis0078(struct map_session_data *sd, unsigned char *buf) {
+
+ nullpo_retr(0, sd);
+
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,14)=sd->disguise;
+ //WBUFL(buf,34)=sd->status.guild_id;
+ //WBUFL(buf,38)=sd->guild_emblem_id;
+ WBUFW(buf,42)=0;
+ WBUFB(buf,44)=0;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y);
+ WBUFB(buf,48)|=sd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=sd->state.dead_sit;
+ WBUFW(buf,52)=0;
+
+ return packet_len_table[0x78];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set007b(struct map_session_data *sd,unsigned char *buf) {
+
+ int sdoption;
+
+ nullpo_retr(0, sd);
+
+ // Disable showing Falcon when player is hide [LuzZza]
+ if(sd->disguise)
+ sdoption = OPTION_INVISIBLE;
+ else {
+ sdoption = sd->status.option;
+ if(sdoption&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE))
+ sdoption &= ~OPTION_FALCON;
+ }
+
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if(sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,18)=sd->status.weapon;
+ else
+ WBUFW(buf,18)=0;
+ WBUFW(buf,20)=sd->status.head_bottom;
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=sd->status.shield;
+ WBUFW(buf,28)=sd->status.head_top;
+ WBUFW(buf,30)=sd->status.head_mid;
+ WBUFW(buf,32)=sd->status.hair_color;
+ WBUFW(buf,34)=sd->status.clothes_color;
+ WBUFW(buf,36)=sd->head_dir;
+ WBUFL(buf,38)=sd->status.guild_id;
+ WBUFL(buf,42)=sd->guild_emblem_id;
+ WBUFW(buf,46)=sd->opt3;
+ WBUFB(buf,48)=sd->status.karma;
+ WBUFB(buf,49)=sd->sex;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x7b];
+#else
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=sd->opt1;
+ WBUFW(buf,10)=sd->opt2;
+ WBUFW(buf,12)=sdoption;
+ WBUFW(buf,14)=sd->view_class;
+ WBUFW(buf,16)=sd->status.hair;
+ if(sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,18)= clif_weapon_viewid(sd, 9);
+ else
+ WBUFW(buf,18)=0;
+ if(sd->equip_index[8] != sd->equip_index[9] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS)
+ WBUFW(buf,20)= clif_weapon_viewid(sd, 8);
+ else
+ WBUFW(buf,20)=0;
+ WBUFW(buf,22)=sd->status.head_bottom;
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=sd->status.head_top;
+ WBUFW(buf,30)=sd->status.head_mid;
+ WBUFW(buf,32)=sd->status.hair_color;
+ WBUFW(buf,34)=sd->status.clothes_color;
+ WBUFW(buf,36)=sd->head_dir;
+ WBUFL(buf,38)=sd->status.guild_id;
+ WBUFW(buf,42)=sd->guild_emblem_id;
+ WBUFW(buf,44)=sd->status.manner;
+ WBUFW(buf,46)=sd->opt3;
+ WBUFB(buf,48)=sd->status.karma;
+ WBUFB(buf,49)=sd->sex;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(sd->status.base_level);
+
+ return packet_len_table[0x1da];
+#endif
+}
+
+// moving function for disguises [Valaris]
+static int clif_dis007b(struct map_session_data *sd,unsigned char *buf) {
+
+ nullpo_retr(0, sd);
+
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,14)=sd->disguise;
+ WBUFL(buf,22)=gettick();
+ //WBUFL(buf,38)=sd->status.guild_id;
+ //WBUFL(buf,42)=sd->guild_emblem_id;
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=0;
+
+ return packet_len_table[0x7b];
+}
+
+/*==========================================
+ * ƒNƒ‰ƒXƒ`ƒFƒ“ƒW type‚ÍMob‚ÌꇂÍ1‚Å‘¼‚Í0H
+ *------------------------------------------
+ */
+int clif_class_change(struct block_list *bl,int class_,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ if(class_ >= MAX_PC_CLASS) {
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=class_;
+
+ clif_send(buf,packet_len_table[0x1b0],bl,AREA);
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_mob_class_change(struct mob_data *md, int class_) {
+ unsigned char buf[16];
+ int view = mob_get_viewclass(class_);
+
+ nullpo_retr(0, md);
+
+ if(view >= MAX_PC_CLASS) {
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFB(buf,6)=1;
+ WBUFL(buf,7)=view;
+
+ clif_send(buf,packet_len_table[0x1b0],&md->bl,AREA);
+ }
+ return 0;
+}
+// mob equipment [Valaris]
+
+int clif_mob_equip(struct mob_data *md, int nameid) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, md);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=3;
+ WBUFL(buf,3)=md->bl.id;
+ WBUFL(buf,7)=nameid;
+
+ clif_send(buf,packet_len_table[0x1a4],&md->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * MOB•\Ž¦1
+ *------------------------------------------
+ */
+static int clif_mob0078(struct mob_data *md, unsigned char *buf)
+{
+ int level, view_class;
+
+ nullpo_retr(0, md);
+
+ level=status_get_lv(&md->bl);
+ view_class = mob_get_viewclass(md->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(md->class_);
+ WBUFW(buf,22)=mob_get_shield(md->class_);
+ WBUFW(buf,24)=mob_get_head_top(md->class_);
+ WBUFW(buf,26)=mob_get_head_mid(md->class_);
+ WBUFW(buf,28)=mob_get_hair_color(md->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,32)|=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,34)=md->guardian_data->guild_id;
+ WBUFL(buf,38)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,42)=md->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(md->class_);
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_shield(md->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(md->class_);
+ WBUFW(buf,24)=mob_get_head_top(md->class_);
+ WBUFW(buf,26)=mob_get_head_mid(md->class_);
+ WBUFW(buf,28)=mob_get_hair_color(md->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,32)|=md->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=md->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(md->class_);
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x1d8];
+#endif
+ } else {
+ // Use 0x78 packet for monsters sprites [Valaris]
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,34)=md->guardian_data->guild_id;
+ WBUFL(buf,38)=md->guardian_data->emblem_id;
+ } // End addition
+ WBUFPOS(buf,46,md->bl.x,md->bl.y);
+ WBUFB(buf,48)|=md->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+ }
+}
+
+/*==========================================
+ * MOB•\Ž¦2
+ *------------------------------------------
+ */
+static int clif_mob007b(struct mob_data *md, unsigned char *buf) {
+ int level, view_class;
+
+ nullpo_retr(0, md);
+
+ level=status_get_lv(&md->bl);
+ view_class = mob_get_viewclass(md->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(md->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(md->class_);
+ WBUFW(buf,28)=mob_get_head_top(md->class_);
+ WBUFW(buf,30)=mob_get_head_mid(md->class_);
+ WBUFW(buf,32)=mob_get_hair_color(md->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,36)=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFL(buf,42)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,46)=md->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(md->class_);
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(md->class_);
+ WBUFW(buf,18)=mob_get_weapon(md->class_);
+ WBUFW(buf,20)=mob_get_shield(md->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(md->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(md->class_);
+ WBUFW(buf,30)=mob_get_head_mid(md->class_);
+ WBUFW(buf,32)=mob_get_hair_color(md->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(md->class_);
+ WBUFW(buf,36)=md->dir&0x0f; // head direction
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFW(buf,42)=md->guardian_data->emblem_id;
+ }
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=md->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(md->class_);
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x1da];
+#endif
+ } else {
+ // Use 0x7b packet for monsters sprites [Valaris]
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=status_get_speed(&md->bl);
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,14)=view_class;
+ WBUFL(buf,22)=gettick();
+ if (md->guardian_data && md->guardian_data->guild_id) { // Added guardian emblems [Valaris]
+ WBUFL(buf,38)=md->guardian_data->guild_id;
+ WBUFL(buf,42)=md->guardian_data->emblem_id;
+ } // End addition
+ WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ level = status_get_lv(&md->bl);
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_npc0078(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g=NULL;
+ int view_class;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_len_table[0x78]);
+
+ if (nd->class_ == 722 && nd->u.scr.guild_id > 0)
+ g=guild_search(nd->u.scr.guild_id);
+
+ if(mobdb_checkid(nd->class_) &&
+ pcdb_checkid((view_class = mob_get_viewclass(nd->class_)))) {
+ //Disguised player sprite
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(nd->class_);
+ WBUFW(buf,22)=mob_get_shield(nd->class_);
+ WBUFW(buf,24)=mob_get_head_top(nd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,32)|=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,34)=g->guild_id;
+ WBUFL(buf,38)=g->emblem_id;
+ }
+ WBUFW(buf,42)=nd->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(nd->class_);
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=50; // No level info.
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_shield(nd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(nd->class_);
+ WBUFW(buf,24)=mob_get_head_top(nd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,32)|=nd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=nd->opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(nd->class_);
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=50; //No level data.
+
+ return packet_len_table[0x1d8];
+#endif
+ }
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,14)=nd->class_;
+ if (g) {
+ WBUFL(buf,22)=g->emblem_id;
+ WBUFL(buf,26)=g->guild_id;
+ // pc packet says the actual location of these are, but they are not. Why the discordance? [Skotlex]
+ // WBUFL(buf,34)=g->emblem_id;
+ // WBUFL(buf,38)=g->guild_id;
+ }
+ WBUFPOS(buf,46,nd->bl.x,nd->bl.y);
+ WBUFB(buf,48)|=nd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+
+ return packet_len_table[0x78];
+}
+
+// NPC Walking [Valaris]
+static int clif_npc007b(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g=NULL;
+ int view_class;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_len_table[0x7b]);
+
+ if (nd->class_ == 722 && nd->u.scr.guild_id > 0)
+ g=guild_search(nd->u.scr.guild_id);
+
+ if(mobdb_checkid(nd->class_) &&
+ pcdb_checkid((view_class = mob_get_viewclass(nd->class_)))) {
+ //Disguised player sprite
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(nd->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(nd->class_);
+ WBUFW(buf,28)=mob_get_head_top(nd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,36)=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,38)=g->guild_id;
+ WBUFL(buf,42)=g->emblem_id;
+ }
+ WBUFW(buf,46)=nd->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(nd->class_);
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=50; //Ehm.. no level data.
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,8)=nd->opt1;
+ WBUFW(buf,10)=nd->opt2;
+ WBUFW(buf,12)=nd->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(nd->class_);
+ WBUFW(buf,18)=mob_get_weapon(nd->class_);
+ WBUFW(buf,20)=mob_get_shield(nd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(nd->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(nd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(nd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(nd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(nd->class_);
+ WBUFW(buf,36)=nd->dir&0x0f; // head direction
+ if (g) {
+ WBUFL(buf,38)=g->guild_id;
+ WBUFW(buf,42)=g->emblem_id;
+ }
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=nd->opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(nd->class_);
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ WBUFW(buf,58)=50; //No level data.
+
+ return packet_len_table[0x1da];
+#endif
+ }
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,14)=nd->class_;
+ if (g) {
+ WBUFL(buf,22)=g->emblem_id;
+ WBUFL(buf,26)=g->guild_id;
+ // pc packet says the actual location of these are, but they are not. Why the discordance? [Skotlex]
+ // WBUFL(buf,38)=g->emblem_id;
+ // WBUFL(buf,42)=g->guild_id;
+ }
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+
+ return packet_len_table[0x7b];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet0078(struct pet_data *pd, unsigned char *buf) {
+ int view_class,level;
+
+ nullpo_retr(0, pd);
+
+ level = status_get_lv(&pd->bl);
+ view_class = mob_get_viewclass(pd->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)= 0; //opt1
+ WBUFW(buf,10)= 0; //opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(pd->class_);
+ WBUFW(buf,22)=mob_get_shield(pd->class_);
+ WBUFW(buf,24)=mob_get_head_top(pd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,32)|=pd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; //Guild id
+ WBUFL(buf,38)=0; //Guild emblem
+ WBUFW(buf,42)=0; //opt3;
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(pd->class_);
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+#else
+ // Use 0x1d8 packet for pets with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1d8]);
+
+ WBUFW(buf,0)=0x1d8;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)=0; // opt1
+ WBUFW(buf,10)=0; // opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_shield(pd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(pd->class_);
+ WBUFW(buf,24)=mob_get_head_top(pd->class_);
+ WBUFW(buf,26)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,28)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,30)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,32)|=pd->dir&0x0f; // head direction
+ WBUFL(buf,34)=0; // guild id
+ WBUFW(buf,38)=0; // emblem id
+ WBUFW(buf,40)=0; // manner
+ WBUFW(buf,42)=0; // opt3
+ WBUFB(buf,44)=0; // karma
+ WBUFB(buf,45)=mob_get_sex(pd->class_);
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WBUFB(buf,51)=0; // dead or sit state
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x1d8];
+#endif
+ } else {
+ memset(buf,0,packet_len_table[0x78]);
+
+ WBUFW(buf,0)=0x78;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=battle_config.pet_hair_style;
+ if((view_class = itemdb_viewid(pd->equip)) > 0)
+ WBUFW(buf,20)=view_class;
+ else
+ WBUFW(buf,20)=pd->equip;
+ WBUFPOS(buf,46,pd->bl.x,pd->bl.y);
+ WBUFB(buf,48)|=pd->dir&0x0f;
+ WBUFB(buf,49)=0;
+ WBUFB(buf,50)=0;
+ WBUFW(buf,52)=clif_setlevel(level);
+
+ return packet_len_table[0x78];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet007b(struct pet_data *pd, unsigned char *buf) {
+ int view_class,level;
+
+ nullpo_retr(0, pd);
+
+ level = status_get_lv(&pd->bl);
+ view_class = mob_get_viewclass(pd->class_);
+ if(pcdb_checkid(view_class)) {
+#if PACKETVER < 4
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)= 0; //opt1;
+ WBUFW(buf,10)= 0; //opt2;
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_head_buttom(pd->class_);
+ WBUFL(buf,22)=gettick();
+ WBUFW(buf,26)=mob_get_shield(pd->class_);
+ WBUFW(buf,28)=mob_get_head_top(pd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,36)=pd->dir&0x0f; // head direction
+ WBUFL(buf,38)=0; // guild id
+ WBUFL(buf,42)=0; // emblem id
+ WBUFW(buf,46)=0; // opt3;
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(pd->class_);
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0; //0? These are always five for mobs and pets, /hmm [Skotlex]
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+#else
+ // Use 0x1da packet for monsters with player sprites [Valaris]
+ memset(buf,0,packet_len_table[0x1da]);
+
+ WBUFW(buf,0)=0x1da;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,8)=0; // opt1
+ WBUFW(buf,10)=0; // opt2
+ WBUFW(buf,12)=pd->db->option;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=mob_get_hair(pd->class_);
+ WBUFW(buf,18)=mob_get_weapon(pd->class_);
+ WBUFW(buf,20)=mob_get_shield(pd->class_);
+ WBUFW(buf,22)=mob_get_head_buttom(pd->class_);
+ WBUFL(buf,24)=gettick();
+ WBUFW(buf,28)=mob_get_head_top(pd->class_);
+ WBUFW(buf,30)=mob_get_head_mid(pd->class_);
+ WBUFW(buf,32)=mob_get_hair_color(pd->class_);
+ WBUFW(buf,34)=mob_get_clothes_color(pd->class_);
+ WBUFW(buf,36)=pd->dir&0x0f; // head direction
+ WBUFL(buf,38)=0; // guild id
+ WBUFW(buf,42)=0; // emblem id
+ WBUFW(buf,44)=0; // manner
+ WBUFW(buf,46)=0; // opt3
+ WBUFB(buf,48)=0; // karma
+ WBUFB(buf,49)=mob_get_sex(pd->class_);
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0;
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x1da];
+#endif
+ } else {
+ // Use 0x7b packet for pets sprites [Valaris]
+ memset(buf,0,packet_len_table[0x7b]);
+
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,14)=view_class;
+ WBUFW(buf,16)=battle_config.pet_hair_style;
+ if ((view_class = itemdb_viewid(pd->equip)) > 0)
+ WBUFW(buf,20)=view_class;
+ else
+ WBUFW(buf,20)=pd->equip;
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y);
+ WBUFB(buf,55)=0x88; // Deals with acceleration in directions. [Valaris]
+ WBUFB(buf,56)=0;
+ WBUFB(buf,57)=0;
+ WBUFW(buf,58)=clif_setlevel(level);
+
+ return packet_len_table[0x7b];
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set01e1(struct map_session_data *sd, unsigned char *buf) {
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1e1;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->spiritball;
+
+ return packet_len_table[0x1e1];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_set0192(int fd, int m, int x, int y, int type) {
+ WFIFOHEAD(fd, packet_len_table[0x192]);
+ WFIFOW(fd,0) = 0x192;
+ WFIFOW(fd,2) = x;
+ WFIFOW(fd,4) = y;
+ WFIFOW(fd,6) = type;
+ memcpy(WFIFOP(fd,8),map[m].name,MAP_NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x192]);
+
+ return 0;
+}
+
+// new and improved weather display [Valaris]
+int clif_weather_sub(int fd, int type) {
+ WFIFOHEAD(fd, packet_len_table[0x1f3]);
+ WFIFOW(fd,0) = 0x1f3;
+ WFIFOL(fd,2) = -10;
+ WFIFOL(fd,6) = type;
+ WFIFOSET(fd,packet_len_table[0x1f3]);
+
+ return 0;
+}
+
+int clif_weather(int m) {
+ int i;
+
+ struct map_session_data *sd=NULL;
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth && sd->bl.m == m) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x80]);
+ WFIFOW(sd->fd,0) = 0x80;
+ WFIFOL(sd->fd,2) = -10;
+ WFIFOB(sd->fd,6) = 0;
+ WFIFOSET(sd->fd,packet_len_table[0x80]);
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x7c]);
+ WFIFOW(sd->fd,0)=0x7c;
+ WFIFOL(sd->fd,2)=-10;
+ WFIFOW(sd->fd,6)=0;
+ WFIFOW(sd->fd,8)=0;
+ WFIFOW(sd->fd,10)=0;
+ WFIFOW(sd->fd,12)=OPTION_INVISIBLE;
+ WFIFOW(sd->fd,20)=100;
+ WFIFOPOS(sd->fd,36,sd->bl.x,sd->bl.y);
+ WFIFOSET(sd->fd,packet_len_table[0x7c]);
+
+ if (map[sd->bl.m].flag.snow)
+ clif_weather_sub(sd->fd, 162);
+ if (map[sd->bl.m].flag.clouds)
+ clif_weather_sub(sd->fd, 233);
+ if (map[sd->bl.m].flag.clouds2)
+ clif_weather_sub(sd->fd, 516);
+ if (map[sd->bl.m].flag.fog)
+ clif_weather_sub(sd->fd, 515);
+ if (map[sd->bl.m].flag.fireworks) {
+ clif_weather_sub(sd->fd, 297);
+ clif_weather_sub(sd->fd, 299);
+ clif_weather_sub(sd->fd, 301);
+ }
+ if (map[sd->bl.m].flag.sakura)
+ clif_weather_sub(sd->fd, 163);
+ if (map[sd->bl.m].flag.leaves)
+ clif_weather_sub(sd->fd, 333);
+ if (map[sd->bl.m].flag.rain)
+ clif_weather_sub(sd->fd, 161);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnpc(struct map_session_data *sd) {
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ clif_set0078(sd, buf);
+
+#if PACKETVER < 4
+ WBUFW(buf, 0) = 0x79;
+ WBUFW(buf,51) = clif_setlevel(sd->status.base_level);
+ clif_send(buf, packet_len_table[0x79], &sd->bl, AREA_WOS);
+#else
+ WBUFW(buf, 0) = 0x1d9;
+ WBUFW(buf,51) = clif_setlevel(sd->status.base_level);
+ clif_send(buf, packet_len_table[0x1d9], &sd->bl, AREA_WOS);
+#endif
+
+ if(sd->disguise > 0) {
+ int len;
+ memset(buf,0,packet_len_table[0x7c]);
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=-sd->bl.id;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,12)=sd->status.option;
+ WBUFW(buf,20)=sd->disguise;
+ WBUFPOS(buf,36,sd->bl.x,sd->bl.y);
+ clif_send(buf,packet_len_table[0x7c],&sd->bl,AREA);
+
+ len = clif_dis0078(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ }
+
+ if (sd->spiritball > 0)
+ clif_spiritball(sd);
+
+ if (sd->status.guild_id > 0) { // force display of guild emblem [Valaris]
+ struct guild *g = guild_search(sd->status.guild_id);
+ if (g)
+ clif_guild_emblem(sd,g);
+ } // end addition [Valaris]
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x7c]);
+ WFIFOW(sd->fd,0)=0x7c;
+ WFIFOL(sd->fd,2)=-10;
+ WFIFOW(sd->fd,6)=0;
+ WFIFOW(sd->fd,8)=0;
+ WFIFOW(sd->fd,10)=0;
+ WFIFOW(sd->fd,12)=OPTION_INVISIBLE;
+ WFIFOW(sd->fd,20)=100;
+ WFIFOPOS(sd->fd,36,sd->bl.x,sd->bl.y);
+ WFIFOSET(sd->fd,packet_len_table[0x7c]);
+
+ if (map[sd->bl.m].flag.snow)
+ clif_weather_sub(sd->fd, 162);
+ if (map[sd->bl.m].flag.clouds)
+ clif_weather_sub(sd->fd, 233);
+ if (map[sd->bl.m].flag.clouds2)
+ clif_weather_sub(sd->fd, 516);
+ if (map[sd->bl.m].flag.fog)
+ clif_weather_sub(sd->fd, 515);
+ if (map[sd->bl.m].flag.fireworks) {
+ clif_weather_sub(sd->fd, 297);
+ clif_weather_sub(sd->fd, 299);
+ clif_weather_sub(sd->fd, 301);
+ }
+ if (map[sd->bl.m].flag.sakura)
+ clif_weather_sub(sd->fd, 163);
+ if (map[sd->bl.m].flag.leaves)
+ clif_weather_sub(sd->fd, 333);
+ if (map[sd->bl.m].flag.rain)
+ clif_weather_sub(sd->fd, 161);
+ }
+
+ //New 'night' effect by dynamix [Skotlex]
+ if (night_flag && map[sd->bl.m].flag.nightenabled)
+ { //Display night.
+ if (sd->state.night) //It must be resent because otherwise players get this annoying aura...
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ else
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ } else if (sd->state.night) { //Clear night display.
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ sd->state.night = 0;
+ }
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnnpc(struct npc_data *nd)
+{
+ unsigned char buf[64];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ if(nd->class_ < 0 || nd->flag&1 || nd->class_ == INVISIBLE_CLASS)
+ return 0;
+
+ if(!mobdb_checkid(nd->class_) ||
+ !pcdb_checkid(mob_get_viewclass(nd->class_))) {
+ //Normal npcs.
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=nd->bl.id;
+ WBUFW(buf,6)=nd->speed;
+ WBUFW(buf,20)=nd->class_;
+ WBUFPOS(buf,36,nd->bl.x,nd->bl.y);
+
+ clif_send(buf,packet_len_table[0x7c],&nd->bl,AREA);
+ }
+ len = clif_npc0078(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnmob(struct mob_data *md)
+{
+ unsigned char buf[64];
+ int len;
+ int viewclass = mob_get_viewclass(md->class_);
+
+ if (!pcdb_checkid(viewclass)) {
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=md->bl.id;
+ WBUFW(buf,6)=md->speed;
+ WBUFW(buf,8)=md->opt1;
+ WBUFW(buf,10)=md->opt2;
+ WBUFW(buf,12)=md->option;
+ WBUFW(buf,20)=viewclass;
+ WBUFPOS(buf,36,md->bl.x,md->bl.y);
+ clif_send(buf,packet_len_table[0x7c],&md->bl,AREA);
+ }
+
+ len = clif_mob0078(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+
+ if(battle_config.save_clothcolor && pcdb_checkid(viewclass) && mob_get_clothes_color(md->class_) > 0) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if (mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+ return 0;
+}
+
+// pet
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_spawnpet(struct pet_data *pd)
+{
+ unsigned char buf[64];
+ int len;
+ int viewclass = mob_get_viewclass(pd->class_);
+
+ if (!pcdb_checkid(viewclass)) {
+ memset(buf,0,packet_len_table[0x7c]);
+
+ WBUFW(buf,0)=0x7c;
+ WBUFL(buf,2)=pd->bl.id;
+ WBUFW(buf,6)=pd->speed;
+ WBUFW(buf,20)=viewclass;
+ WBUFPOS(buf,36,pd->bl.x,pd->bl.y);
+
+ clif_send(buf,packet_len_table[0x7c],&pd->bl,AREA);
+ }
+
+ len = clif_pet0078(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_movepet(struct pet_data *pd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, pd);
+
+ len = clif_pet007b(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * npc walking [Valaris]
+ *------------------------------------------
+ */
+int clif_movenpc(struct npc_data *nd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_servertick(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x7f]);
+ WFIFOW(fd,0)=0x7f;
+ WFIFOL(fd,2)=sd->server_tick;
+ WFIFOSET(fd,packet_len_table[0x7f]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_walkok(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x87]);
+ WFIFOW(fd,0)=0x87;
+ WFIFOL(fd,2)=gettick();
+ WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WFIFOB(fd,11)=0x88;
+ WFIFOSET(fd,packet_len_table[0x87]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_movechar(struct map_session_data *sd) {
+ int fd;
+ int len;
+ unsigned char buf[256];
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+
+ len = clif_set007b(sd, buf);
+ clif_send(buf, len, &sd->bl, AREA_WOS);
+
+ if (map[sd->bl.m].flag.snow || map[sd->bl.m].flag.clouds || map[sd->bl.m].flag.fog || map[sd->bl.m].flag.fireworks ||
+ map[sd->bl.m].flag.sakura || map[sd->bl.m].flag.leaves || map[sd->bl.m].flag.rain || map[sd->bl.m].flag.clouds2) {
+ memset(buf,0,packet_len_table[0x7b]);
+ WBUFW(buf,0)=0x7b;
+ WBUFL(buf,2)=-10;
+ WBUFW(buf,6)=sd->speed;
+ WBUFW(buf,8)=0;
+ WBUFW(buf,10)=0;
+ WBUFW(buf,12)=OPTION_INVISIBLE;
+ WBUFW(buf,14)=100;
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y);
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+ clif_send(buf, len, &sd->bl, SELF);
+ }
+
+ if(sd->disguise) {
+ memset(buf,0,packet_len_table[0x7b]);
+ len = clif_dis007b(sd, buf);
+ clif_send(buf, len, &sd->bl, AREA);
+ return 0;
+ }
+
+ //Stupid client that needs this resent every time someone walks :X
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 && ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) ||
+ (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Delays the map_quit of a player after they are disconnected. [Skotlex]
+ *------------------------------------------
+ */
+static int clif_delayquit(int tid, unsigned int tick, int id, int data) {
+ struct map_session_data *sd = NULL;
+
+ if (chrif_isconnect())
+ { //Remove player from map server
+ if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
+ map_quit(sd);
+ } else //Save later.
+ add_timer(tick + 10000, clif_delayquit, id, 0);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_quitsave(int fd,struct map_session_data *sd)
+{
+ if (chrif_isconnect() && (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout))
+ map_quit(sd);
+ else if (sd->fd)
+ { //Disassociate session from player (session is deleted after this function was called)
+ //And set a timer to delete this player later.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_waitclose(int tid, unsigned int tick, int id, int data) {
+ if (session[id] && session[id]->func_parse == clif_parse) //Avoid disconnecting non-players, as pointed out by End of Exam [Skotlex]
+ session[id]->eof = 1;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_setwaitclose(int fd) {
+ struct map_session_data *sd;
+
+ // if player is not already in the game (double connection probably)
+ if ((sd = (struct map_session_data*)session[fd]->session_data) == NULL) {
+ // limited timer, just to send information.
+ add_timer(gettick() + 1000, clif_waitclose, fd, 0);
+ } else
+ add_timer(gettick() + 5000, clif_waitclose, fd, 0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemap(struct map_session_data *sd, short map, int x, int y) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len_table[0x91]);
+ WFIFOW(fd,0) = 0x91;
+ memcpy(WFIFOP(fd,2), mapindex_id2name(map), MAP_NAME_LENGTH);
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOSET(fd, packet_len_table[0x91]);
+
+ if(pc_isdead(sd)) // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris]
+ clif_clearchar_area(&sd->bl,1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemapserver(struct map_session_data *sd, char *mapname, int x, int y, int ip, int port) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x92]);
+ WFIFOW(fd,0) = 0x92;
+ //Better not trust the null-terminator is there. [Skotlex]
+ memcpy(WFIFOP(fd,2), mapname, MAP_NAME_LENGTH);
+ WFIFOB(fd,17) = 0; //Null terminator for mapname
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOL(fd,22) = ip;
+ WFIFOW(fd,26) = port;
+ WFIFOSET(fd, packet_len_table[0x92]);
+
+ return 0;
+}
+
+int clif_blown(struct block_list *bl) {
+//Previous Aegis versions simply used clif_fixpos, but it seems clif_slide works better on current clients.
+// return clif_fixpos(bl);
+ return clif_slide(bl, bl->x, bl->y);
+
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_fixpos(struct block_list *bl) {
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x88;
+
+ if (bl->type ==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2)=-bl->id;
+ else
+ WBUFL(buf,2)=bl->id;
+
+ WBUFW(buf,6)=bl->x;
+ WBUFW(buf,8)=bl->y;
+ clif_send(buf, packet_len_table[0x88], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_npcbuysell(struct map_session_data* sd, int id) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xc4]);
+ WFIFOW(fd,0)=0xc4;
+ WFIFOL(fd,2)=id;
+ WFIFOSET(fd,packet_len_table[0xc4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_buylist(struct map_session_data *sd, struct npc_data *nd) {
+ struct item_data *id;
+ int fd,i,val;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, nd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 200 * 11 + 4);
+ WFIFOW(fd,0)=0xc6;
+ for(i=0;nd->u.shop_item[i].nameid > 0;i++){
+ id = itemdb_search(nd->u.shop_item[i].nameid);
+ val=nd->u.shop_item[i].value;
+ WFIFOL(fd,4+i*11)=val;
+ if (!id->flag.value_notdc)
+ val=pc_modifybuyvalue(sd,val);
+ WFIFOL(fd,8+i*11)=val;
+ WFIFOB(fd,12+i*11)=itemtype(id->type);
+ if (id->view_id > 0)
+ WFIFOW(fd,13+i*11)=id->view_id;
+ else
+ WFIFOW(fd,13+i*11)=nd->u.shop_item[i].nameid;
+ }
+ WFIFOW(fd,2)=i*11+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_selllist(struct map_session_data *sd) {
+ int fd,i,c=0,val;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ WFIFOW(fd,0)=0xc7;
+ for(i=0;i<MAX_INVENTORY;i++) {
+ if(sd->status.inventory[i].nameid > 0 && sd->inventory_data[i]) {
+ if (!itemdb_cansell(sd->status.inventory[i].nameid, pc_isGM(sd)))
+ continue;
+
+ val=sd->inventory_data[i]->value_sell;
+ if (val < 0)
+ continue;
+ WFIFOW(fd,4+c*10)=i+2;
+ WFIFOL(fd,6+c*10)=val;
+ if (!sd->inventory_data[i]->flag.value_notoc)
+ val=pc_modifysellvalue(sd,val);
+ WFIFOL(fd,10+c*10)=val;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*10+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptmes(struct map_session_data *sd, int npcid, char *mes) {
+ int fd;
+ int slen = strlen(mes) + 9;
+ WFIFOHEAD(fd, slen);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0xb4;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ strcpy((char*)WFIFOP(fd,8),mes);
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptnext(struct map_session_data *sd,int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb5]);
+ WFIFOW(fd,0)=0xb5;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0xb5]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptclose(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xb6]);
+ WFIFOW(fd,0)=0xb6;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0xb6]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptmenu(struct map_session_data *sd, int npcid, char *mes) {
+ int fd;
+ int slen = strlen(mes) + 8;
+ WFIFOHEAD(fd, slen);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0xb7;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ strcpy((char*)WFIFOP(fd,8),mes);
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptinput(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x142]);
+ WFIFOW(fd,0)=0x142;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0x142]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_scriptinputstr(struct map_session_data *sd, int npcid) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x1d4]);
+ WFIFOW(fd,0)=0x1d4;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len_table[0x1d4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x144]);
+ WFIFOW(fd,0)=0x144;
+ WFIFOL(fd,2)=npc_id;
+ WFIFOL(fd,6)=type;
+ WFIFOL(fd,10)=x;
+ WFIFOL(fd,14)=y;
+ WFIFOB(fd,18)=id;
+ WFIFOL(fd,19)=color;
+ WFIFOSET(fd,packet_len_table[0x144]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_cutin(struct map_session_data *sd, char *image, int type) {
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x1b3]);
+ WFIFOW(fd,0)=0x1b3;
+ strncpy((char*)WFIFOP(fd,2),image,64);
+ WFIFOB(fd,66)=type;
+ WFIFOSET(fd,packet_len_table[0x1b3]);
+
+ return 0;
+}
+
+/*==========================================
+ * Fills in card data from the given item and into the buffer. [Skotlex]
+ *------------------------------------------
+ */
+static void clif_addcards(unsigned char* buf, struct item* item)
+{
+ int i=0,j;
+ if (item == NULL) { //Blank data
+ WBUFW(buf,0)=0;
+ WBUFW(buf,2)=0;
+ WBUFW(buf,4)=0;
+ WBUFW(buf,6)=0;
+ return;
+ }
+ if(item->card[0]==(short)0xff00) { //pet eggs
+ WBUFW(buf,0)=0;
+ WBUFW(buf,2)=0;
+ WBUFW(buf,4)=0;
+ WBUFW(buf,6)=item->card[3]; //Pet renamed flag.
+ return;
+ }
+ if(item->card[0]==0x00ff || item->card[0]==0x00fe) { //Forged/created items
+ WBUFW(buf,0)=item->card[0];
+ WBUFW(buf,2)=item->card[1];
+ WBUFW(buf,4)=item->card[2];
+ WBUFW(buf,6)=item->card[3];
+ return;
+ }
+ //Client only receives four cards.. so randomly send them a set of cards. [Skotlex]
+ if (MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4)
+ i = rand()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rand()%3;
+
+ //Normal items.
+ if (item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,0)=j;
+ else
+ WBUFW(buf,0)= item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,2)=j;
+ else
+ WBUFW(buf,2)=item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,4)=j;
+ else
+ WBUFW(buf,4)=item->card[i];
+
+ if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0)
+ WBUFW(buf,6)=j;
+ else
+ WBUFW(buf,6)=item->card[i];
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_additem(struct map_session_data *sd, int n, int amount, int fail) {
+ int fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd)) //Sasuke-
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0xa0]);
+ buf = WFIFOP(fd,0);
+ if(fail) {
+ WBUFW(buf,0)=0xa0;
+ WBUFW(buf,2)=n+2;
+ WBUFW(buf,4)=amount;
+ WBUFW(buf,6)=0;
+ WBUFB(buf,8)=0;
+ WBUFB(buf,9)=0;
+ WBUFB(buf,10)=0;
+ WBUFW(buf,11)=0;
+ WBUFW(buf,13)=0;
+ WBUFW(buf,15)=0;
+ WBUFW(buf,17)=0;
+ WBUFW(buf,19)=0;
+ WBUFB(buf,21)=0;
+ WBUFB(buf,22)=fail;
+ } else {
+ if (n<0 || n>=MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xa0;
+ WBUFW(buf,2)=n+2;
+ WBUFW(buf,4)=amount;
+ if (sd->inventory_data[n]->view_id > 0)
+ WBUFW(buf,6)=sd->inventory_data[n]->view_id;
+ else
+ WBUFW(buf,6)=sd->status.inventory[n].nameid;
+ WBUFB(buf,8)=sd->status.inventory[n].identify;
+ WBUFB(buf,9)=sd->status.inventory[n].attribute;
+ WBUFB(buf,10)=sd->status.inventory[n].refine;
+ clif_addcards(WBUFP(buf,11), &sd->status.inventory[n]);
+ WBUFW(buf,19)=pc_equippoint(sd,n);
+ WBUFB(buf,21)=itemtype(sd->inventory_data[n]->type);
+ WBUFB(buf,22)=fail;
+ }
+
+ WFIFOSET(fd,packet_len_table[0xa0]);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0xaf]);
+ WFIFOW(fd,0)=0xaf;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+
+ WFIFOSET(fd,packet_len_table[0xaf]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_itemlist(struct map_session_data *sd)
+{
+ int i,n,fd,arrow=-1;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa3;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL || itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WBUFW(buf,n*10+4)=i+2;
+ if (sd->inventory_data[i]->view_id > 0)
+ WBUFW(buf,n*10+6)=sd->inventory_data[i]->view_id;
+ else
+ WBUFW(buf,n*10+6)=sd->status.inventory[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(sd->inventory_data[i]->type);
+ WBUFB(buf,n*10+9)=sd->status.inventory[i].identify;
+ WBUFW(buf,n*10+10)=sd->status.inventory[i].amount;
+ if (sd->inventory_data[i]->equip == 0x8000) {
+ WBUFW(buf,n*10+12)=0x8000;
+ if (sd->status.inventory[i].equip)
+ arrow=i; // ‚‚¢‚Å‚É–î‘•”õƒ`ƒFƒbƒN
+ } else
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if (n) {
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1ee;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL || itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WBUFW(buf,n*18+4)=i+2;
+ if(sd->inventory_data[i]->view_id > 0)
+ WBUFW(buf,n*18+6)=sd->inventory_data[i]->view_id;
+ else
+ WBUFW(buf,n*18+6)=sd->status.inventory[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(sd->inventory_data[i]->type);
+ WBUFB(buf,n*18+9)=sd->status.inventory[i].identify;
+ WBUFW(buf,n*18+10)=sd->status.inventory[i].amount;
+ if (sd->inventory_data[i]->equip == 0x8000) {
+ WBUFW(buf,n*18+12)=0x8000;
+ if(sd->status.inventory[i].equip)
+ arrow=i; // ‚‚¢‚Å‚É–î‘•”õƒ`ƒFƒbƒN
+ } else
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf, n*18+14), &sd->status.inventory[i]);
+ n++;
+ }
+ if (n) {
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ if(arrow >= 0)
+ clif_arrowequip(sd,arrow);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_equiplist(struct map_session_data *sd)
+{
+ int i,n,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (!session_isActive(fd))
+ return 0;
+ WFIFOHEAD(fd, 4 + MAX_INVENTORY * 20);
+ WFIFOW(fd,0)=0xa4;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || !itemdb_isequip2(sd->inventory_data[i]))
+ continue;
+ WFIFOW(fd,n*20+4)=i+2;
+ if(sd->inventory_data[i]->view_id > 0)
+ WFIFOW(fd,n*20+6)=sd->inventory_data[i]->view_id;
+ else
+ WFIFOW(fd,n*20+6)=sd->status.inventory[i].nameid;
+ WFIFOB(fd,n*20+8)=itemtype(sd->inventory_data[i]->type);
+ WFIFOB(fd,n*20+9)=sd->status.inventory[i].identify;
+ WFIFOW(fd,n*20+10)=pc_equippoint(sd,i);
+ WFIFOW(fd,n*20+12)=sd->status.inventory[i].equip;
+ WFIFOB(fd,n*20+14)=sd->status.inventory[i].attribute;
+ WFIFOB(fd,n*20+15)=sd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd, n*20+16), &sd->status.inventory[i]);
+ n++;
+ }
+ if(n){
+ WFIFOW(fd,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‚³‚ñ‚É—a‚¯‚Ä‚ ‚éÁ–Õ•i&ŽûW•iƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_storageitemlist(struct map_session_data *sd,struct storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,MAX_STORAGE * 18 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*10+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*10+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1f0;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*18+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*18+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf,n*18+14), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‚³‚ñ‚É—a‚¯‚Ä‚ ‚é‘•”õƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_storageequiplist(struct map_session_data *sd,struct storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,MAX_STORAGE * 20 + 4);
+ buf = WFIFOP(fd,0);
+ WBUFW(buf,0)=0xa6;
+ for(i=0,n=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=stor->storage_[i].equip;
+ WBUFB(buf,n*20+14)=stor->storage_[i].attribute;
+ WBUFB(buf,n*20+15)=stor->storage_[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_GUILD_STORAGE * 18 + 4);
+ buf=WFIFOP(fd,0);
+
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*10+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*10+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ WBUFW(buf,0)=0x1f0;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(itemdb_isequip2(id))
+ continue;
+
+ WBUFW(buf,n*18+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*18+10)=stor->storage_[i].amount;
+ WBUFW(buf,n*18+12)=0;
+ clif_addcards(WBUFP(buf,n*18+14), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_GUILD_STORAGE * 20 + 4);
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xa6;
+ for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid<=0)
+ continue;
+ nullpo_retr(0, id = itemdb_search(stor->storage_[i].nameid));
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+1;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=stor->storage_[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=stor->storage_[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=stor->storage_[i].equip;
+ WBUFB(buf,n*20+14)=stor->storage_[i].attribute;
+ WBUFB(buf,n*20+15)=stor->storage_[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &stor->storage_[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+// Guild XY locators [Valaris]
+int clif_guild_xy(struct map_session_data *sd)
+{
+unsigned char buf[10];
+
+nullpo_retr(0, sd);
+
+WBUFW(buf,0)=0x1eb;
+WBUFL(buf,2)=sd->status.account_id;
+WBUFW(buf,6)=sd->bl.x;
+WBUFW(buf,8)=sd->bl.y;
+clif_send(buf,packet_len_table[0x1eb],&sd->bl,GUILD_SAMEMAP_WOS);
+
+return 0;
+}
+
+// Guild XY locators [Valaris]
+int clif_guild_xy_remove(struct map_session_data *sd)
+{
+unsigned char buf[10];
+
+nullpo_retr(0, sd);
+
+WBUFW(buf,0)=0x1eb;
+WBUFL(buf,2)=sd->status.account_id;
+WBUFW(buf,6)=-1;
+WBUFW(buf,8)=-1;
+clif_send(buf,packet_len_table[0x1eb],&sd->bl,GUILD_SAMEMAP_WOS);
+
+return 0;
+}
+
+/*==========================================
+ * ƒXƒe[ƒ^ƒX‚ð‘—‚è‚‚¯‚é
+ * •\Ž¦ê—p”Žš‚Í‚±‚Ì’†‚ÅŒvŽZ‚µ‚Ä‘—‚é
+ *------------------------------------------
+ */
+int clif_updatestatus(struct map_session_data *sd,int type)
+{
+ int fd,len=8;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin]
+ return 0;
+
+ WFIFOHEAD(fd, 14);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type;
+ switch(type){
+ // 00b0
+ case SP_WEIGHT:
+ pc_checkweighticon(sd);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type; //Added this packet back, Temp fix to the slow motion [Lupus]
+ WFIFOL(fd,4)=sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ WFIFOL(fd,4)=sd->max_weight;
+ break;
+ case SP_SPEED:
+ WFIFOL(fd,4)=sd->speed;
+ break;
+ case SP_BASELEVEL:
+ WFIFOL(fd,4)=sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ WFIFOL(fd,4)=sd->status.job_level;
+ break;
+ case SP_MANNER:
+ WFIFOL(fd,4)=sd->status.manner;
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+ break;
+ case SP_STATUSPOINT:
+ WFIFOL(fd,4)=sd->status.status_point;
+ break;
+ case SP_SKILLPOINT:
+ WFIFOL(fd,4)=sd->status.skill_point;
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4)=sd->hit;
+ break;
+ case SP_FLEE1:
+ WFIFOL(fd,4)=sd->flee;
+ break;
+ case SP_FLEE2:
+ WFIFOL(fd,4)=sd->flee2/10;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4)=sd->status.max_hp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4)=sd->status.max_sp;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4)=sd->status.hp;
+ if (sd->status.party_id)
+ clif_party_hp(sd);
+ if (battle_config.disp_hpmeter)
+ clif_hpmeter(sd);
+ break;
+ case SP_SP:
+ WFIFOL(fd,4)=sd->status.sp;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4)=sd->aspd;
+ break;
+ case SP_ATK1:
+ WFIFOL(fd,4)=sd->base_atk+sd->right_weapon.watk;
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4)=sd->def;
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4)=sd->mdef;
+ break;
+ case SP_ATK2:
+ WFIFOL(fd,4)=sd->right_weapon.watk2;
+ break;
+ case SP_DEF2:
+ WFIFOL(fd,4)=sd->def2;
+ break;
+ case SP_MDEF2:
+ WFIFOL(fd,4)=sd->mdef2;
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4)=sd->critical/10;
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4)=sd->matk1;
+ break;
+ case SP_MATK2:
+ WFIFOL(fd,4)=sd->matk2;
+ break;
+
+
+ case SP_ZENY:
+ WFIFOW(fd,0)=0xb1;
+ if(sd->status.zeny < 0)
+ sd->status.zeny = 0;
+ WFIFOL(fd,4)=sd->status.zeny;
+ break;
+ case SP_BASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextjobexp(sd);
+ break;
+
+ // 00be I—¹
+ 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 I—¹
+ case SP_ATTACKRANGE:
+ WFIFOW(fd,0)=0x13a;
+ WFIFOW(fd,2)=sd->attackrange;
+ len=4;
+ break;
+
+ // 0141 I—¹
+ case SP_STR:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.str;
+ WFIFOL(fd,10)=sd->paramb[0] + sd->parame[0];
+ len=14;
+ break;
+ case SP_AGI:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.agi;
+ WFIFOL(fd,10)=sd->paramb[1] + sd->parame[1];
+ len=14;
+ break;
+ case SP_VIT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.vit;
+ WFIFOL(fd,10)=sd->paramb[2] + sd->parame[2];
+ len=14;
+ break;
+ case SP_INT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.int_;
+ WFIFOL(fd,10)=sd->paramb[3] + sd->parame[3];
+ len=14;
+ break;
+ case SP_DEX:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.dex;
+ WFIFOL(fd,10)=sd->paramb[4] + sd->parame[4];
+ len=14;
+ break;
+ case SP_LUK:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.luk;
+ WFIFOL(fd,10)=sd->paramb[5] + sd->parame[5];
+ len=14;
+ break;
+
+ case SP_CARTINFO:
+ WFIFOW(fd,0)=0x121;
+ WFIFOW(fd,2)=sd->cart_num;
+ WFIFOW(fd,4)=sd->cart_max_num;
+ WFIFOL(fd,6)=sd->cart_weight;
+ WFIFOL(fd,10)=sd->cart_max_weight;
+ len=14;
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_updatestatus : unrecognized type %d\n",type);
+ return 1;
+ }
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+int clif_changestatus(struct block_list *bl,int type,int val)
+{
+ unsigned char buf[12];
+ struct map_session_data *sd = NULL;
+
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ sd = (struct map_session_data *)bl;
+
+//printf("clif_changestatus id:%d type:%d val:%d\n",bl->id,type,val);
+ if(sd){
+ WBUFW(buf,0)=0x1ab;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=type;
+ switch(type){
+ case SP_MANNER:
+ WBUFL(buf,8)=val;
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_changestatus : unrecognized type %d.\n",type);
+ return 1;
+ }
+ clif_send(buf,packet_len_table[0x1ab],bl,AREA_WOS);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changelook(struct block_list *bl,int type,int val)
+{
+
+ unsigned char buf[32];
+ struct map_session_data *sd = NULL;
+
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ sd = (struct map_session_data *)bl;
+
+#if PACKETVER < 4
+ if(sd && (type == LOOK_WEAPON || type == LOOK_SHIELD) && (sd->view_class == JOB_WEDDING || sd->view_class == JOB_XMAS))
+ val =0;
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len_table[0xc3],bl,AREA);
+#else
+ if(sd && (type == LOOK_WEAPON || type == LOOK_SHIELD || type == LOOK_SHOES)) {
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ if(type == LOOK_SHOES) {
+ WBUFB(buf,6)=9;
+ if(sd->equip_index[2] >= 0 && sd->inventory_data[sd->equip_index[2]]) {
+ if(sd->inventory_data[sd->equip_index[2]]->view_id > 0)
+ WBUFW(buf,7)=sd->inventory_data[sd->equip_index[2]]->view_id;
+ else
+ WBUFW(buf,7)=sd->status.inventory[sd->equip_index[2]].nameid;
+ } else
+ WBUFW(buf,7)=0;
+ WBUFW(buf,9)=0;
+ }
+ else {
+ WBUFB(buf,6)=2;
+ if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if(sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ WBUFW(buf,7)=sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ WBUFW(buf,7)=sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ WBUFW(buf,7)=0;
+ if(sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] &&
+ sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if(sd->inventory_data[sd->equip_index[8]]->view_id > 0)
+ WBUFW(buf,9)=sd->inventory_data[sd->equip_index[8]]->view_id;
+ else
+ WBUFW(buf,9)=sd->status.inventory[sd->equip_index[8]].nameid;
+ } else
+ WBUFW(buf,9)=0;
+ }
+ clif_send(buf,packet_len_table[0x1d7],bl,AREA);
+ }
+ else if(sd && (type == LOOK_BASE) && (val > 255))
+ {
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len_table[0x1d7],bl,AREA);
+ } else {
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len_table[0xc3],bl,AREA);
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_initialstatus(struct map_session_data *sd)
+{
+ int fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xbd]);
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xbd;
+ WBUFW(buf,2)=sd->status.status_point;
+ WBUFB(buf,4)=(sd->status.str > 255)? 255:sd->status.str;
+ WBUFB(buf,5)=pc_need_status_point(sd,SP_STR);
+ WBUFB(buf,6)=(sd->status.agi > 255)? 255:sd->status.agi;
+ WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI);
+ WBUFB(buf,8)=(sd->status.vit > 255)? 255:sd->status.vit;
+ WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT);
+ WBUFB(buf,10)=(sd->status.int_ > 255)? 255:sd->status.int_;
+ WBUFB(buf,11)=pc_need_status_point(sd,SP_INT);
+ WBUFB(buf,12)=(sd->status.dex > 255)? 255:sd->status.dex;
+ WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX);
+ WBUFB(buf,14)=(sd->status.luk > 255)? 255:sd->status.luk;
+ WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK);
+
+ WBUFW(buf,16) = sd->base_atk + sd->right_weapon.watk;
+ WBUFW(buf,18) = sd->right_weapon.watk2; //atk bonus
+ WBUFW(buf,20) = sd->matk1;
+ WBUFW(buf,22) = sd->matk2;
+ WBUFW(buf,24) = sd->def; // def
+ WBUFW(buf,26) = sd->def2;
+ WBUFW(buf,28) = sd->mdef; // mdef
+ WBUFW(buf,30) = sd->mdef2;
+ WBUFW(buf,32) = sd->hit;
+ WBUFW(buf,34) = sd->flee;
+ WBUFW(buf,36) = sd->flee2/10;
+ WBUFW(buf,38) = sd->critical/10;
+ WBUFW(buf,40) = sd->status.karma;
+ WBUFW(buf,42) = sd->status.manner;
+
+ WFIFOSET(fd,packet_len_table[0xbd]);
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ clif_updatestatus(sd,SP_ASPD);
+
+ return 0;
+}
+
+/*==========================================
+ *–î‘•”õ
+ *------------------------------------------
+ */
+int clif_arrowequip(struct map_session_data *sd,int val)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ if(sd->attacktarget && sd->attacktarget > 0) // [Valaris]
+ sd->attacktarget = 0;
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x013c]);
+ WFIFOW(fd,0)=0x013c;
+ WFIFOW(fd,2)=val+2;//–î‚̃AƒCƒeƒ€ID
+
+ WFIFOSET(fd,packet_len_table[0x013c]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_arrow_fail(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len_table[0x013b]);
+ WFIFOW(fd,0)=0x013b;
+ WFIFOW(fd,2)=type;
+
+ WFIFOSET(fd,packet_len_table[0x013b]);
+
+ return 0;
+}
+
+/*==========================================
+ * 쬉”\ –ƒXƒg‘—M
+ *------------------------------------------
+ */
+int clif_arrow_create_list(struct map_session_data *sd)
+{
+ int i, c, j;
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4);
+ WFIFOW(fd,0) = 0x1ad;
+
+ for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) {
+ if (skill_arrow_db[i].nameid > 0 &&
+ (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 &&
+ !sd->status.inventory[j].equip)
+ {
+ if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+4) = j;
+ else
+ WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) sd->state.produce_flag = 1;
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_statusupack(struct map_session_data *sd,int type,int ok,int val)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xbc]);
+ WFIFOW(fd,0)=0xbc;
+ WFIFOW(fd,2)=type;
+ WFIFOB(fd,4)=ok;
+ WFIFOB(fd,5)=val;
+ WFIFOSET(fd,packet_len_table[0xbc]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xaa]);
+ WFIFOW(fd,0)=0xaa;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xaa]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xac]);
+ WFIFOW(fd,0)=0xac;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xac]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_misceffect(struct block_list* bl,int type)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x19b;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf,packet_len_table[0x19b],bl,AREA);
+
+ return 0;
+}
+int clif_misceffect2(struct block_list *bl, int type) {
+ unsigned char buf[24];
+
+ nullpo_retr(0, bl);
+
+ memset(buf, 0, packet_len_table[0x1f3]);
+
+ WBUFW(buf,0) = 0x1f3;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA);
+
+ return 0;
+
+}
+/*==========================================
+ * •\Ž¦ƒIƒvƒVƒ‡ƒ“•ÏX
+ *------------------------------------------
+ */
+int clif_changeoption(struct block_list* bl)
+{
+ unsigned char buf[32];
+ short option;
+
+ nullpo_retr(0, bl);
+
+ option = *status_get_option(bl);
+
+ WBUFW(buf,0) = 0x119;
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise) {
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFW(buf,8) = 0;
+ WBUFW(buf,10) = OPTION_INVISIBLE;
+ WBUFB(buf,12) = 0;
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFW(buf,8) = 0;
+ WBUFW(buf,10) = option;
+ WBUFB(buf,12) = 0;
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ } else {
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = *status_get_opt1(bl);
+ WBUFW(buf,8) = *status_get_opt2(bl);
+ WBUFW(buf,10) = option;
+ WBUFB(buf,12) = 0; // ??
+ clif_send(buf,packet_len_table[0x119],bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_useitemack(struct map_session_data *sd,int index,int amount,int ok)
+{
+ nullpo_retr(0, sd);
+
+ if(!ok) {
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xa8]);
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xa8]);
+ }
+ else {
+#if PACKETVER < 3
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xa8]);
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len_table[0xa8]);
+#else
+ unsigned char buf[32];
+
+ WBUFW(buf,0)=0x1c8;
+ WBUFW(buf,2)=index+2;
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,4)=sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,4)=sd->status.inventory[index].nameid;
+ WBUFL(buf,6)=sd->bl.id;
+ WBUFW(buf,10)=amount;
+ WBUFB(buf,12)=ok;
+ clif_send(buf,packet_len_table[0x1c8],&sd->bl,AREA);
+#endif
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_createchat(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd6]);
+ WFIFOW(fd,0)=0xd6;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xd6]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_dispchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[128]; // Å‘åtitle(60ƒoƒCƒg)+17
+
+ if(cd==NULL || *cd->owner==NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xd7;
+ WBUFW(buf,2)=strlen((const char*)cd->title)+17;
+ WBUFL(buf,4)=(*cd->owner)->id;
+ WBUFL(buf,8)=cd->bl.id;
+ WBUFW(buf,12)=cd->limit;
+ WBUFW(buf,14)=cd->users;
+ WBUFB(buf,16)=cd->pub;
+ strcpy((char*)WBUFP(buf,17),(const char*)cd->title);
+ if(fd){
+ WFIFOHEAD(fd, WBUFW(buf,2));
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WBUFW(buf,2));
+ } else {
+ clif_send(buf,WBUFW(buf,2),*cd->owner,AREA_WOSC);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * chat‚Ìó‘Ô•ÏX¬Œ÷
+ * ŠO•”‚Ìl—p‚Æ–½—߃R[ƒh(d7->df)‚ªˆá‚¤‚¾‚¯
+ *------------------------------------------
+ */
+int clif_changechatstatus(struct chat_data *cd)
+{
+ unsigned char buf[128]; // Å‘åtitle(60ƒoƒCƒg)+17
+
+ if(cd==NULL || cd->usersd[0]==NULL)
+ return 1;
+
+ WBUFW(buf,0)=0xdf;
+ WBUFW(buf,2)=strlen((char*)cd->title)+17;
+ WBUFL(buf,4)=cd->usersd[0]->bl.id;
+ WBUFL(buf,8)=cd->bl.id;
+ WBUFW(buf,12)=cd->limit;
+ WBUFW(buf,14)=cd->users;
+ WBUFB(buf,16)=cd->pub;
+ strcpy((char*)WBUFP(buf,17),(const char*)cd->title);
+ clif_send(buf,WBUFW(buf,2),&cd->usersd[0]->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_clearchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, cd);
+
+ WBUFW(buf,0)=0xd8;
+ WBUFL(buf,2)=cd->bl.id;
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0xd8]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0xd8]);
+ WFIFOSET(fd,packet_len_table[0xd8]);
+ } else {
+ clif_send(buf,packet_len_table[0xd8],*cd->owner,AREA_WOSC);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_joinchatfail(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0xda]);
+ WFIFOW(fd,0)=0xda;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xda]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_joinchatok(struct map_session_data *sd,struct chat_data* cd)
+{
+ int fd;
+ int i;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd))
+ return 0;
+ WFIFOHEAD(fd, 8 + (28*cd->users));
+ WFIFOW(fd, 0) = 0xdb;
+ WFIFOW(fd, 2) = 8 + (28*cd->users);
+ WFIFOL(fd, 4) = cd->bl.id;
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+i*28) = (i!=0) || ((*cd->owner)->type == BL_NPC);
+ memcpy(WFIFOP(fd, 8+i*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ WFIFOSET(fd, WFIFOW(fd, 2));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_addchat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0x0dc;
+ WBUFW(buf, 2) = cd->users;
+ memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH);
+ clif_send(buf,packet_len_table[0xdc],&sd->bl,CHAT_WOS);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changechatowner(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0xe1;
+ WBUFL(buf, 2) = 1;
+ memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH);
+ WBUFW(buf,30) = 0xe1;
+ WBUFL(buf,32) = 0;
+ memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH);
+
+ clif_send(buf,packet_len_table[0xe1]*2,&sd->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_leavechat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, cd);
+
+ WBUFW(buf, 0) = 0xdd;
+ WBUFW(buf, 2) = cd->users-1;
+ memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH);
+ WBUFB(buf,28) = 0;
+
+ clif_send(buf,packet_len_table[0xdd],&sd->bl,CHAT);
+
+ return 0;
+}
+
+/*==========================================
+ * Žæ‚èˆø‚«—v¿Žó‚¯
+ *------------------------------------------
+ */
+int clif_traderequest(struct map_session_data *sd,char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0xe5]);
+ WFIFOW(fd,0)=0xe5;
+
+ strcpy((char*)WFIFOP(fd,2),name);
+
+ WFIFOSET(fd,packet_len_table[0xe5]);
+
+ return 0;
+}
+
+/*==========================================
+ * Žæ‚èˆø‚«—v‹‰ž“š
+ *------------------------------------------
+ */
+int clif_tradestart(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xe7]);
+ WFIFOW(fd,0)=0xe7;
+ WFIFOB(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0xe7]);
+
+ return 0;
+}
+
+/*==========================================
+ * ‘ŠŽè•û‚©‚ç‚̃AƒCƒeƒ€’ljÁ
+ *------------------------------------------
+ */
+int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd=tsd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xe9]);
+ WFIFOW(fd,0)=0xe9;
+ WFIFOL(fd,2)=amount;
+ if(index==0){
+ WFIFOW(fd,6) = 0; // type id
+ WFIFOB(fd,8) = 0; //identify flag
+ WFIFOB(fd,9) = 0; // attribute
+ WFIFOB(fd,10)= 0; //refine
+ WFIFOW(fd,11)= 0; //card (4w)
+ WFIFOW(fd,13)= 0; //card (4w)
+ WFIFOW(fd,15)= 0; //card (4w)
+ WFIFOW(fd,17)= 0; //card (4w)
+ }
+ else{
+ index-=2; //index fix
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WFIFOW(fd,6) = sd->inventory_data[index]->view_id;
+ else
+ WFIFOW(fd,6) = sd->status.inventory[index].nameid; // type id
+ WFIFOB(fd,8) = sd->status.inventory[index].identify; //identify flag
+ WFIFOB(fd,9) = sd->status.inventory[index].attribute; // attribute
+ WFIFOB(fd,10)= sd->status.inventory[index].refine; //refine
+ clif_addcards(WFIFOP(fd, 11), &sd->status.inventory[index]);
+ }
+ WFIFOSET(fd,packet_len_table[0xe9]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€’ljÁ¬Œ÷/Ž¸”s
+ *------------------------------------------
+ */
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xea]);
+ WFIFOW(fd,0)=0xea;
+ WFIFOW(fd,2)=index;
+ WFIFOB(fd,4)=fail;
+ WFIFOSET(fd,packet_len_table[0xea]);
+
+ return 0;
+}
+
+/*==========================================
+ * Žæ‚èˆø‚«ok‰Ÿ‚µ
+ *------------------------------------------
+ */
+int clif_tradedeal_lock(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xec]);
+ WFIFOW(fd,0)=0xec;
+ WFIFOB(fd,2)=fail; // 0=you 1=the other person
+ WFIFOSET(fd,packet_len_table[0xec]);
+
+ return 0;
+}
+
+/*==========================================
+ * Žæ‚èˆø‚«‚ªƒLƒƒƒ“ƒZƒ‹‚³‚ê‚Ü‚µ‚½
+ *------------------------------------------
+ */
+int clif_tradecancelled(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xee]);
+ WFIFOW(fd,0)=0xee;
+ WFIFOSET(fd,packet_len_table[0xee]);
+
+ return 0;
+}
+
+/*==========================================
+ * Žæ‚èˆø‚«Š®—¹
+ *------------------------------------------
+ */
+int clif_tradecompleted(struct map_session_data *sd,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf0]);
+ WFIFOW(fd,0)=0xf0;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xf0]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚̃AƒCƒeƒ€”‚ðXV
+ *------------------------------------------
+ */
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf2]);
+ WFIFOW(fd,0) = 0xf2; // update storage amount
+ WFIFOW(fd,2) = stor->storage_amount; //items
+ WFIFOW(fd,4) = MAX_STORAGE; //items max
+ WFIFOSET(fd,packet_len_table[0xf2]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ɃAƒCƒeƒ€‚ð’ljÁ‚·‚é
+ *------------------------------------------
+ */
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf4]);
+ WFIFOW(fd,0) =0xf4; // Storage item added
+ WFIFOW(fd,2) =index+1; // index
+ WFIFOL(fd,4) =amount; // amount
+ if((view = itemdb_viewid(stor->storage_[index].nameid)) > 0)
+ WFIFOW(fd,8) =view;
+ else
+ WFIFOW(fd,8) =stor->storage_[index].nameid; // id
+ WFIFOB(fd,10)=stor->storage_[index].identify; //identify flag
+ WFIFOB(fd,11)=stor->storage_[index].attribute; // attribute
+ WFIFOB(fd,12)=stor->storage_[index].refine; //refine
+ clif_addcards(WFIFOP(fd,13), &stor->storage_[index]);
+ WFIFOSET(fd,packet_len_table[0xf4]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf2]);
+ WFIFOW(fd,0) = 0xf2; // update storage amount
+ WFIFOW(fd,2) = stor->storage_amount; //items
+ WFIFOW(fd,4) = MAX_GUILD_STORAGE; //items max
+ WFIFOSET(fd,packet_len_table[0xf2]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf4]);
+ WFIFOW(fd,0) =0xf4; // Storage item added
+ WFIFOW(fd,2) =index+1; // index
+ WFIFOL(fd,4) =amount; // amount
+ if((view = itemdb_viewid(stor->storage_[index].nameid)) > 0)
+ WFIFOW(fd,8) =view;
+ else
+ WFIFOW(fd,8) =stor->storage_[index].nameid; // id
+ WFIFOB(fd,10)=stor->storage_[index].identify; //identify flag
+ WFIFOB(fd,11)=stor->storage_[index].attribute; // attribute
+ WFIFOB(fd,12)=stor->storage_[index].refine; //refine
+ clif_addcards(WFIFOP(fd,13), &stor->storage_[index]);
+ WFIFOSET(fd,packet_len_table[0xf4]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚©‚çƒAƒCƒeƒ€‚ðŽæ‚è‹Ž‚é
+ *------------------------------------------
+ */
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf6]);
+ WFIFOW(fd,0)=0xf6; // Storage item removed
+ WFIFOW(fd,2)=index+1;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len_table[0xf6]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ð•Â‚¶‚é
+ *------------------------------------------
+ */
+int clif_storageclose(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xf8]);
+ WFIFOW(fd,0)=0xf8; // Storage Closed
+ WFIFOSET(fd,packet_len_table[0xf8]);
+
+ return 0;
+}
+
+//
+// callbackŒn ?
+//
+/*==========================================
+ * PC•\Ž¦
+ *------------------------------------------
+ */
+void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd)
+{
+ int len;
+
+ nullpo_retv(sd);
+ nullpo_retv(dstsd);
+
+ if(dstsd->walktimer != -1){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd, packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd, packet_len_table[0x1da]);
+#endif
+ len = clif_set007b(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ if(dstsd->disguise) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+ len = clif_dis007b(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_set0078(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ if(dstsd->disguise) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+ len = clif_dis0078(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ }
+
+ if(dstsd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(dstsd->chatID);
+ if(cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ }
+ if(dstsd->vender_id){
+ clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
+ }
+ if(dstsd->spiritball > 0) {
+ WFIFOHEAD(sd->fd, packet_len_table[0x1e1]);
+ clif_set01e1(dstsd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,packet_len_table[0x1e1]);
+ }
+
+ if(battle_config.save_clothcolor && dstsd->status.clothes_color > 0 && ((dstsd->view_class != JOB_WEDDING && dstsd->view_class !=JOB_XMAS) ||
+ (dstsd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (dstsd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&dstsd->bl, LOOK_CLOTHES_COLOR, dstsd->status.clothes_color);
+
+ if((sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting.
+ (battle_config.disp_hpmeter && (len = pc_isGM(sd)) >= battle_config.disp_hpmeter && len >= pc_isGM(dstsd))
+ )
+ clif_hpmeter_single(sd->fd, dstsd);
+
+ if(sd->status.manner < 0 && battle_config.manner_system)
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+
+ if(sd->state.size==2) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,0);
+ else if(sd->state.size==1)
+ clif_specialeffect(&sd->bl,421,0);
+
+ // pvp circle for duel [LuzZza]
+ /*
+ if(dstsd->duel_group)
+ clif_specialeffect(&dstsd->bl, 159, 4);
+ */
+
+}
+
+/*==========================================
+ * NPC•\Ž¦
+ *------------------------------------------
+ */
+//fixed by Valaris
+void clif_getareachar_npc(struct map_session_data* sd,struct npc_data* nd)
+{
+ int len;
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+ if(nd->class_ < 0 || nd->flag&1 || nd->class_ == INVISIBLE_CLASS)
+ return;
+ if(nd->state.state == MS_WALK){
+ WFIFOHEAD(sd->fd, packet_len_table[0x7b]);
+ len = clif_npc007b(nd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+ WFIFOHEAD(sd->fd, packet_len_table[0x78]);
+ len = clif_npc0078(nd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+ if(nd->chat_id){
+ clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd);
+ }
+}
+
+/*==========================================
+ * ˆÚ“®’âŽ~
+ *------------------------------------------
+ */
+int clif_movemob(struct mob_data *md)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, md);
+
+ len = clif_mob007b(md,buf);
+ clif_send(buf,len,&md->bl,AREA);
+
+ if(battle_config.save_clothcolor && pcdb_checkid(mob_get_viewclass(md->class_)) && mob_get_clothes_color(md->class_)) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if(mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ‚ƒ“ƒXƒ^[‚̈ʒuC³
+ *------------------------------------------
+ */
+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‚̈ʒuC³
+ *------------------------------------------
+ */
+int clif_fixpcpos(struct map_session_data *sd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, sd);
+
+ if(sd->walktimer != -1){
+ len = clif_set007b(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ } else {
+ len = clif_set0078(sd,buf);
+ clif_send(buf,len,&sd->bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_fixpetpos(struct pet_data *pd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, pd);
+
+ if(pd->state.state == MS_WALK){
+ len = clif_pet007b(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+ } else {
+ len = clif_pet0078(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_fixnpcpos(struct npc_data *nd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK){
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ } else {
+ len = clif_npc0078(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Modifies the type of damage according to status changes [Skotlex]
+ *------------------------------------------
+ */
+static int clif_calc_delay(struct block_list *dst, int type, int delay)
+{
+ if (type == 1 || type == 4 || type == 0x0a) //Type 1 is the crouching animation, type 4 are non-flinching attacks, 0x0a - crits.
+ return type;
+
+ if (delay == 0)
+ return 9; //Endure type attack (damage delay is 0)
+
+ return type;
+}
+/*==========================================
+ * ’ÊíUŒ‚ƒGƒtƒFƒNƒg•ƒ_ƒ[ƒW
+ *------------------------------------------
+ */
+int clif_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2)
+{
+ unsigned char buf[256];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, type, ddelay); //Type defaults to 0 for normal attacks.
+
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1) {
+ if(damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ if(damage2 > 0)
+ damage2 = damage2*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+ }
+
+ WBUFW(buf,0)=0x8a;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,2)=-src->id;
+ else
+ WBUFL(buf,2)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,6)=-dst->id;
+ else
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+ WBUFW(buf,22)=(damage > 0x7fff)? 0x7fff:damage;
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+ WBUFW(buf,27)=damage2;
+ clif_send(buf,packet_len_table[0x8a],src,AREA);
+
+ if((src->type==BL_PC && ((struct map_session_data *)src)->disguise) || (dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)) {
+ memset(buf,0,packet_len_table[0x8a]);
+ WBUFW(buf,0)=0x8a;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,2)=src->id;
+ else
+ WBUFL(buf,2)=-src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,6)=dst->id;
+ else
+ WBUFL(buf,2)=-dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+ if(damage > 0)
+ WBUFW(buf,22)=-1;
+ else
+ WBUFW(buf,22)=0;
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+ WBUFW(buf,27)=0;
+ clif_send(buf,packet_len_table[0x8a],src,AREA);
+ }
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage+damage2 > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+
+ // [Valaris]
+ if(battle_config.save_clothcolor && src->type==BL_MOB &&
+ pcdb_checkid(mob_get_viewclass(((struct mob_data *)src)->class_)) && mob_get_clothes_color(((struct mob_data *)src)->class_) > 0)
+ clif_changelook(src, LOOK_CLOTHES_COLOR, mob_get_clothes_color(((struct mob_data *)src)->class_));
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_mob(struct map_session_data* sd,struct mob_data* md)
+{
+ int len;
+ nullpo_retv(sd);
+ nullpo_retv(md);
+
+ if (session[sd->fd] == NULL)
+ return;
+
+ if(md->state.state == MS_WALK){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_mob007b(md,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x78]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1d8]);
+#endif
+ len = clif_mob0078(md,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+
+ if(battle_config.save_clothcolor && pcdb_checkid(mob_get_viewclass(md->class_)) && mob_get_clothes_color(md->class_)) // [Valaris]
+ clif_changelook(&md->bl, LOOK_CLOTHES_COLOR, mob_get_clothes_color(md->class_));
+
+ if(mob_get_equip(md->class_) > 0) // mob equipment [Valaris]
+ clif_mob_equip(md,mob_get_equip(md->class_));
+
+ if(md->special_state.size==2) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,0);
+ else if(md->special_state.size==1)
+ clif_specialeffect(&md->bl,421,0);
+
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_pet(struct map_session_data* sd,struct pet_data* pd)
+{
+ int len;
+
+ nullpo_retv(sd);
+ nullpo_retv(pd);
+
+ if(pd->state.state == MS_WALK){
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1da]);
+#endif
+ len = clif_pet007b(pd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ } else {
+#if PACKETVER < 4
+ WFIFOHEAD(sd->fd,packet_len_table[0x7b]);
+#else
+ WFIFOHEAD(sd->fd,packet_len_table[0x1da]);
+#endif
+ len = clif_pet0078(pd,WFIFOP(sd->fd,0));
+ WFIFOSET(sd->fd,len);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(fitem);
+
+ fd=sd->fd;
+ //009d <ID>.l <item ID>.w <identify flag>.B <X>.w <Y>.w <amount>.w <subX>.B <subY>.B
+ WFIFOHEAD(fd,packet_len_table[0x9d]);
+
+ WFIFOW(fd,0)=0x9d;
+ WFIFOL(fd,2)=fitem->bl.id;
+ if((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WFIFOW(fd,6)=view;
+ else
+ WFIFOW(fd,6)=fitem->item_data.nameid;
+ WFIFOB(fd,8)=fitem->item_data.identify;
+ WFIFOW(fd,9)=fitem->bl.x;
+ WFIFOW(fd,11)=fitem->bl.y;
+ WFIFOW(fd,13)=fitem->item_data.amount;
+ WFIFOB(fd,15)=fitem->subx;
+ WFIFOB(fd,16)=fitem->suby;
+
+ WFIFOSET(fd,packet_len_table[0x9d]);
+}
+/*==========================================
+ * ꊃXƒLƒ‹ƒGƒtƒFƒNƒg‚ªŽ‹ŠE‚É“ü‚é
+ *------------------------------------------
+ */
+int clif_getareachar_skillunit(struct map_session_data *sd,struct skill_unit *unit)
+{
+ int fd;
+ struct block_list *bl;
+
+ nullpo_retr(0, unit);
+
+ fd=sd->fd;
+ bl=map_id2bl(unit->group->src_id);
+#if PACKETVER < 3
+ WFIFOHEAD(fd,packet_len_table[0x11f]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x11f]);
+ WFIFOW(fd, 0)=0x11f;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=0;
+ WFIFOSET(fd,packet_len_table[0x11f]);
+#else
+ WFIFOHEAD(fd,packet_len_table[0x1c9]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x1c9]);
+ WFIFOW(fd, 0)=0x1c9;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1;
+ if(unit->group->unit_id==0xb0) { // Graffiti [Valaris]
+ WFIFOB(fd,16)=1;
+ memcpy(WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
+ } else {
+ WFIFOL(fd,15+1)=0; //1-4’²‚ׂ½ŒÀ‚èŒÅ’è
+ WFIFOL(fd,15+5)=0; //5-8’²‚ׂ½ŒÀ‚èŒÅ’è
+ //9-12ƒ}ƒbƒv‚²‚Ƃňê’è‚Ì77-80‚Æ‚Í‚Ü‚½ˆá‚¤4ƒoƒCƒg‚Ì‚©‚È‚è‘å‚«‚È”Žš
+ WFIFOL(fd,15+13)=unit->bl.y - 0x12; //13-16ƒ†ƒjƒbƒg‚ÌYÀ•W-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-76pŽÒ‚ÌYÀ•W
+ WFIFOL(fd,15+77)=unit->bl.m; //77-80ƒ}ƒbƒvID‚©‚È‚ŸH‚©‚È‚è2ƒoƒCƒg‚Å‘«‚è‚»‚¤‚È”Žš
+ WFIFOB(fd,15+81)=0xaa; //81I’[•¶Žš0xaa
+ }
+
+ WFIFOSET(fd,packet_len_table[0x1c9]);
+#endif
+ if(unit->group->skill_id == WZ_ICEWALL)
+ clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,5);
+
+ return 0;
+}
+/*==========================================
+ * ꊃXƒLƒ‹ƒGƒtƒFƒNƒg‚ªŽ‹ŠE‚©‚çÁ‚¦‚é
+ *------------------------------------------
+ */
+int clif_clearchar_skillunit(struct skill_unit *unit,int fd)
+{
+ nullpo_retr(0, unit);
+
+ WFIFOHEAD(fd,packet_len_table[0x120]);
+ WFIFOW(fd, 0)=0x120;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOSET(fd,packet_len_table[0x120]);
+ if(unit->group && unit->group->skill_id == WZ_ICEWALL)
+ clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_01ac(struct block_list *bl)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x1ac;
+ WBUFL(buf, 2) = bl->id;
+
+ clif_send(buf,packet_len_table[0x1ac],bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+ int clif_getareachar(struct block_list* bl,va_list ap)
+{
+ struct map_session_data *sd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=va_arg(ap,struct map_session_data*);
+
+ if (sd == NULL || session[sd->fd] == NULL)
+ return 0;
+
+ switch(bl->type){
+ case BL_PC:
+ if(sd==(struct map_session_data*)bl)
+ break;
+ clif_getareachar_pc(sd,(struct map_session_data*) bl);
+ break;
+ case BL_NPC:
+ clif_getareachar_npc(sd,(struct npc_data*) bl);
+ break;
+ case BL_MOB:
+ clif_getareachar_mob(sd,(struct mob_data*) bl);
+ break;
+ case BL_PET:
+ clif_getareachar_pet(sd,(struct pet_data*) bl);
+ break;
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*) bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(struct skill_unit *)bl);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("clif_getareachar: Unrecognized type %d.\n",bl->type);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pcoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd,*dstsd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data*));
+
+ switch(bl->type){
+ case BL_PC:
+ dstsd = (struct map_session_data*) bl;
+ if(sd != dstsd) {
+ clif_clearchar_id(dstsd->bl.id,0,sd->fd);
+ clif_clearchar_id(sd->bl.id,0,dstsd->fd);
+ if(dstsd->disguise || sd->disguise) {
+ clif_clearchar_id(-dstsd->bl.id,0,sd->fd);
+ clif_clearchar_id(-sd->bl.id,0,dstsd->fd);
+ }
+ if(dstsd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(dstsd->chatID);
+ if(cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ }
+ if(dstsd->vender_id){
+ clif_closevendingboard(&dstsd->bl,sd->fd);
+ }
+ }
+ break;
+ case BL_NPC:
+ if( ((struct npc_data *)bl)->class_ != INVISIBLE_CLASS )
+ clif_clearchar_id(bl->id,0,sd->fd);
+ break;
+ case BL_MOB:
+ case BL_PET:
+ clif_clearchar_id(bl->id,0,sd->fd);
+ break;
+ case BL_ITEM:
+ clif_clearflooritem((struct flooritem_data*)bl,sd->fd);
+ break;
+ case BL_SKILL:
+ clif_clearchar_skillunit((struct skill_unit *)bl,sd->fd);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pcinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd,*dstsd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data*));
+
+ switch(bl->type){
+ case BL_PC:
+ dstsd = (struct map_session_data *)bl;
+ if(sd != dstsd) {
+ clif_getareachar_pc(sd,dstsd);
+ clif_getareachar_pc(dstsd,sd);
+ }
+ break;
+ case BL_NPC:
+ clif_getareachar_npc(sd,(struct npc_data*)bl);
+ break;
+ case BL_MOB:
+ clif_getareachar_mob(sd,(struct mob_data*)bl);
+ break;
+ case BL_PET:
+ clif_getareachar_pet(sd,(struct pet_data*)bl);
+ break;
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*)bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(struct skill_unit *)bl);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_moboutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(md->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_mobinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ md=va_arg(ap,struct mob_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_mob(sd,md);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_petoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, pd=va_arg(ap,struct pet_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(pd->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_npcoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data*));
+
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_clearchar_id(nd->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_petinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ pd=va_arg(ap,struct pet_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_pet(sd,pd);
+ }
+
+ return 0;
+}
+
+// npc walking [Valaris]
+int clif_npcinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ nd=va_arg(ap,struct npc_data*);
+ if(bl->type==BL_PC
+ && ((sd = (struct map_session_data*) bl) != NULL)
+ && session[sd->fd] != NULL) {
+ clif_getareachar_npc(sd,nd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range)
+{
+ int fd,id, inf2;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if( (id=sd->status.skill[skillid].id) <= 0 )
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x147]);
+ WFIFOW(fd,0)=0x147;
+ WFIFOW(fd,2) = id;
+ if(type < 0)
+ WFIFOW(fd,4) = skill_get_inf(id);
+ else
+ WFIFOW(fd,4) = type;
+ WFIFOW(fd,6) = 0;
+ WFIFOW(fd,8) = sd->status.skill[skillid].lv;
+ WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[skillid].lv);
+ if(range < 0)
+ range = skill_get_range2(&sd->bl, id,sd->status.skill[skillid].lv);
+
+ WFIFOW(fd,12)= range;
+ strncpy(WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
+ inf2 = skill_get_inf2(id);
+ if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) ||
+ (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill))
+ //WFIFOB(fd,38)= (sd->status.skill[skillid].lv < skill_get_max(id) && sd->status.skill[skillid].flag ==0 )? 1:0;
+ WFIFOB(fd,38)= (sd->status.skill[skillid].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[skillid].flag ==0 )? 1:0;
+ else
+ WFIFOB(fd,38) = 0;
+ WFIFOSET(fd,packet_len_table[0x147]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒŠƒXƒg‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_skillinfoblock(struct map_session_data *sd)
+{
+ int fd;
+ int i,c,len=4,id, inf2;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
+ WFIFOW(fd,0)=0x10f;
+ for ( i = c = 0; i < MAX_SKILL; i++){
+ if( (id=sd->status.skill[i].id)!=0 ){
+ WFIFOW(fd,len ) = id;
+ WFIFOW(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+4) = 0;
+ WFIFOW(fd,len+6) = sd->status.skill[i].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
+ strncpy(WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ inf2 = skill_get_inf2(id);
+ if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) ||
+ (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill) )
+ //WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_get_max(id) && sd->status.skill[i].flag ==0 )? 1:0;
+ WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[i].flag ==0 )? 1:0;
+ else
+ WFIFOB(fd,len+36) = 0;
+ len+=37;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Š„‚èU‚è’Ê’m
+ *------------------------------------------
+ */
+int clif_skillup(struct map_session_data *sd,int skill_num)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10e]);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_num;
+ WFIFOW(fd,4) = sd->status.skill[skill_num].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_num,sd->status.skill[skill_num].lv);
+ WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_num,sd->status.skill[skill_num].lv);
+ //WFIFOB(fd,10) = (sd->status.skill[skill_num].lv < skill_get_max(sd->status.skill[skill_num].id)) ? 1 : 0;
+ WFIFOB(fd,10) = (sd->status.skill[skill_num].lv < skill_tree_get_max(sd->status.skill[skill_num].id, sd->status.class_)) ? 1 : 0;
+ WFIFOSET(fd,packet_len_table[0x10e]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹‰r¥ƒGƒtƒFƒNƒg‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime)
+{
+ int pl = skill_get_pl(skill_num);
+ unsigned char buf[32];
+ WBUFW(buf,0) = 0x13e;
+ WBUFL(buf,2) = src_id;
+ WBUFL(buf,6) = dst_id;
+ WBUFW(buf,10) = dst_x;
+ WBUFW(buf,12) = dst_y;
+ WBUFW(buf,14) = skill_num;
+ WBUFL(buf,16) = pl<0?0:pl; //Avoid sending negatives as element [Skotlex]
+ WBUFL(buf,20) = casttime;
+ clif_send(buf,packet_len_table[0x13e], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_skillcastcancel(struct block_list* bl)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x1b9;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf,packet_len_table[0x1b9], bl, AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹‰r¥Ž¸”s
+ *------------------------------------------
+ */
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ // reset all variables [celest]
+ sd->skillx = sd->skilly = -1;
+ sd->skillid = sd->skilllv = -1;
+ sd->skillitem = sd->skillitemlv = -1;
+
+ if(type==0x4 && !sd->state.showdelay)
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0x110]);
+ WFIFOW(fd,0) = 0x110;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = btype;
+ WFIFOW(fd,6) = 0;
+ WFIFOB(fd,8) = 0;
+ WFIFOB(fd,9) = type;
+ WFIFOSET(fd,packet_len_table[0x110]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹UŒ‚ƒGƒtƒFƒNƒg•ƒ_ƒ[ƒW
+ *------------------------------------------
+ */
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, (type>0)?type:skill_get_hit(skill_id), ddelay);
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+
+#if PACKETVER < 3
+ WBUFW(buf,0)=0x114;
+ WBUFW(buf,2)=skill_id;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,4)=-src->id;
+ else
+ WBUFL(buf,4)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,8)=-dst->id;
+ else
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=damage;
+ WBUFW(buf,26)=skill_lv;
+ WBUFW(buf,28)=div;
+ WBUFB(buf,30)=type;
+ clif_send(buf,packet_len_table[0x114],src,AREA);
+#else
+ WBUFW(buf,0)=0x1de;
+ WBUFW(buf,2)=skill_id;
+ if(src->type==BL_PC && ((struct map_session_data *)src)->disguise)
+ WBUFL(buf,4)=-src->id;
+ else
+ WBUFL(buf,4)=src->id;
+ if(dst->type==BL_PC && ((struct map_session_data *)dst)->disguise)
+ WBUFL(buf,8)=-dst->id;
+ else
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFL(buf,24)=damage;
+ WBUFW(buf,28)=skill_lv;
+ WBUFW(buf,30)=div;
+ WBUFB(buf,32)=type;
+ clif_send(buf,packet_len_table[0x1de],src,AREA);
+#endif
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+ return 0;
+}
+
+/*==========================================
+ * ‚«”ò‚΂µƒXƒLƒ‹UŒ‚ƒGƒtƒFƒNƒg•ƒ_ƒ[ƒW
+ *------------------------------------------
+ */
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc_data;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ type = clif_calc_delay(dst, (type>0)?type:skill_get_hit(skill_id), ddelay);
+ sc_data = status_get_sc_data(dst);
+
+ if(sc_data) {
+ if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0)
+ damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100;
+ }
+
+ WBUFW(buf,0)=0x115;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=dst->x;
+ WBUFW(buf,26)=dst->y;
+ WBUFW(buf,28)=damage;
+ WBUFW(buf,30)=skill_lv;
+ WBUFW(buf,32)=div;
+ WBUFB(buf,34)=type;
+ clif_send(buf,packet_len_table[0x115],src,AREA);
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ if (type != 4 && type != 9 && damage > 0) //Non-endure/Non-flinch attack, update walk delay.
+ battle_walkdelay(dst, tick, sdelay, ddelay, div);
+ return 0;
+}
+
+/*==========================================
+ * Žx‰‡/‰ñ•œƒXƒLƒ‹ƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ꊃXƒLƒ‹ƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ꊃXƒLƒ‹ƒGƒtƒFƒNƒg•\Ž¦
+ *------------------------------------------
+ */
+int clif_skill_setunit(struct skill_unit *unit)
+{
+ unsigned char buf[128];
+ struct block_list *bl;
+
+ nullpo_retr(0, unit);
+
+ bl=map_id2bl(unit->group->src_id);
+
+#if PACKETVER < 3
+ memset(WBUFP(buf, 0),0,packet_len_table[0x11f]);
+ WBUFW(buf, 0)=0x11f;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=0;
+ clif_send(buf,packet_len_table[0x11f],&unit->bl,AREA);
+#else
+ memset(WBUFP(buf, 0),0,packet_len_table[0x1c9]);
+ WBUFW(buf, 0)=0x1c9;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1;
+ if(unit->group->unit_id==0xb0) { // Graffiti [Valaris]
+ WBUFB(buf,16)=1;
+ memcpy(WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE);
+ } else {
+ WBUFL(buf,15+1)=0; //1-4’²‚ׂ½ŒÀ‚èŒÅ’è
+ WBUFL(buf,15+5)=0; //5-8’²‚ׂ½ŒÀ‚èŒÅ’è
+ //9-12ƒ}ƒbƒv‚²‚Ƃňê’è‚Ì77-80‚Æ‚Í‚Ü‚½ˆá‚¤4ƒoƒCƒg‚Ì‚©‚È‚è‘å‚«‚È”Žš
+ WBUFL(buf,15+13)=unit->bl.y - 0x12; //13-16ƒ†ƒjƒbƒg‚ÌYÀ•W-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-76pŽÒ‚ÌYÀ•W
+ WBUFL(buf,15+77)=unit->bl.m; //77-80ƒ}ƒbƒvID‚©‚È‚ŸH‚©‚È‚è2ƒoƒCƒg‚Å‘«‚è‚»‚¤‚È”Žš
+ WBUFB(buf,15+81)=0xaa; //81I’[•¶Žš0xaa
+ }
+ clif_send(buf,packet_len_table[0x1c9],&unit->bl,AREA);
+#endif
+ return 0;
+}
+/*==========================================
+ * ꊃXƒLƒ‹ƒGƒtƒFƒNƒgíœ
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒ[ƒvꊑI‘ð
+ *------------------------------------------
+ */
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num,
+ const char *map1,const char *map2,const char *map3,const char *map4)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x11c]);
+ WFIFOW(fd,0)=0x11c;
+ WFIFOW(fd,2)=skill_num;
+ strncpy((char*)WFIFOP(fd, 4),map1,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,20),map2,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,36),map3,MAP_NAME_LENGTH);
+ strncpy((char*)WFIFOP(fd,52),map4,MAP_NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x11c]);
+ return 0;
+}
+/*==========================================
+ * ƒƒ‚‰ž“š
+ *------------------------------------------
+ */
+int clif_skill_memo(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x11e]);
+ WFIFOW(fd,0)=0x11e;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x11e]);
+ return 0;
+}
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x189]);
+ WFIFOW(fd,0)=0x189;
+ WFIFOW(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x189]);
+ return 0;
+}
+
+/*==========================================
+ * ƒ‚ƒ“ƒXƒ^[î•ñ
+ *------------------------------------------
+ */
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
+{
+ struct mob_data *md;
+ unsigned char buf[64];
+ int i;//, fix;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, dst);
+
+ if(dst->type!=BL_MOB )
+ return 0;
+ if((md=(struct mob_data *)dst) == NULL)
+ return 0;
+
+ WBUFW(buf, 0)=0x18c;
+ WBUFW(buf, 2)=mob_get_viewclass(md->class_);
+ WBUFW(buf, 4)=md->level;
+ WBUFW(buf, 6)=md->db->size;
+ WBUFL(buf, 8)=md->hp;
+ WBUFW(buf,12)= (battle_config.estimation_type&1?status_get_def(&md->bl):0)
+ +(battle_config.estimation_type&2?status_get_def2(&md->bl):0);
+ WBUFW(buf,14)=md->db->race;
+ WBUFW(buf,16)= (battle_config.estimation_type&1?status_get_mdef(&md->bl):0)
+ +(battle_config.estimation_type&2?status_get_mdef2(&md->bl) - (md->db->vit>>1):0);
+ WBUFW(buf,18)=status_get_elem_type(&md->bl);
+ for(i=0;i<9;i++)
+ WBUFB(buf,20+i)= (unsigned char)battle_attr_fix(NULL,dst,100,i+1,md->def_ele);
+// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex]
+// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_fix(NULL,dst,100,i+1,md->def_ele))<0?0:fix);
+
+ if(sd->status.party_id>0)
+ clif_send(buf,packet_len_table[0x18c],&sd->bl,PARTY_AREA);
+ else{
+ WFIFOHEAD(sd->fd,packet_len_table[0x18c]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x18c]);
+ WFIFOSET(sd->fd,packet_len_table[0x18c]);
+ }
+ return 0;
+}
+/*==========================================
+ * ƒAƒCƒeƒ€‡¬‰Â”\ƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger)
+{
+ int i,c,view,fd;
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8);
+ WFIFOW(fd, 0)=0x18d;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, 1) ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*8+ 4)= view;
+ else
+ WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid;
+ WFIFOW(fd,c*8+ 6)= 0x0012;
+ WFIFOL(fd,c*8+ 8)= sd->status.char_id;
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*8+8;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if(c > 0) sd->state.produce_flag = 1;
+ return 0;
+}
+
+/*==========================================
+ * Sends a status change packet to the object only, used for loading status changes. [Skotlex]
+ *------------------------------------------
+ */
+int clif_status_load(struct block_list *bl,int type, int flag)
+{
+ int fd;
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return 0;
+
+ if (bl->type != BL_PC)
+ return 0;
+
+ fd = ((struct map_session_data*)bl)->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x196]);
+ WFIFOW(fd,0)=0x0196;
+ WFIFOW(fd,2)=type;
+ WFIFOL(fd,4)=bl->id;
+ WFIFOB(fd,8)=flag; //Status start
+ WFIFOSET(fd, packet_len_table[0x196]);
+ return 0;
+}
+/*==========================================
+ * ó‘ÔˆÙíƒAƒCƒRƒ“/ƒƒbƒZ[ƒW•\Ž¦
+ *------------------------------------------
+ */
+int clif_status_change(struct block_list *bl,int type,int flag)
+{
+ unsigned char buf[16];
+
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return 0;
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x0196;
+ WBUFW(buf,2)=type;
+ WBUFL(buf,4)=bl->id;
+ WBUFB(buf,8)=flag;
+ clif_send(buf,packet_len_table[0x196],bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ * Send message (modified by [Yor])
+ *------------------------------------------
+ */
+int clif_displaymessage(const int fd, char* mes)
+{
+ // invalid pointer?
+ nullpo_retr(-1, mes);
+
+ //Console [Wizputer] //Scrapped, as these are shared by disconnected players =X [Skotlex]
+ if (fd == 0)
+ return 0;
+ else {
+ int len_mes = strlen(mes);
+
+ if (len_mes > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
+ WFIFOHEAD(fd, 5 + len_mes);
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = 5 + len_mes; // 4 + len + NULL teminate
+ memcpy(WFIFOP(fd,4), mes, len_mes + 1);
+ WFIFOSET(fd, 5 + len_mes);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * “V‚̺‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_GMmessage(struct block_list *bl, char* mes, int len, int flag)
+{
+ unsigned char *buf;
+ int lp;
+
+ lp = (flag & 0x10) ? 8 : 4;
+ buf = (unsigned char*)aCallocA(len + lp + 8, sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = len + lp;
+ WBUFL(buf,4) = 0x65756c62;
+ memcpy(WBUFP(buf,lp), mes, len);
+ flag &= 0x07;
+ clif_send(buf, WBUFW(buf,2), bl,
+ (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT);
+ if(buf) aFree(buf);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒOƒ[ƒoƒ‹ƒƒbƒZ[ƒW
+ *------------------------------------------
+ */
+void clif_GlobalMessage(struct block_list *bl,char *message)
+{
+ char buf[100];
+ int len;
+
+ nullpo_retv(bl);
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=bl->id;
+ strncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),bl,AREA_CHAT_WOC);
+}
+
+/*==========================================
+ * Send main chat message [LuzZza]
+ *------------------------------------------
+ */
+void clif_MainChatMessage(char* message) {
+
+ char buf[100];
+ int len;
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=0;
+ strncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT);
+}
+
+/*==========================================
+ * Does an announce message in the given color.
+ *------------------------------------------
+ */
+int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag)
+{
+ unsigned char *buf;
+ buf = (unsigned char*)aCallocA(len + 16, sizeof(unsigned char));
+ WBUFW(buf,0) = 0x1c3;
+ WBUFW(buf,2) = len + 16;
+ WBUFL(buf,4) = color;
+ WBUFW(buf,8) = 0x190; //Font style? Type?
+ WBUFW(buf,10) = 0x0c; //12? Font size?
+ WBUFL(buf,12) = 0; //Unknown!
+ memcpy(WBUFP(buf,16), mes, len);
+
+ flag &= 0x07;
+ clif_send(buf, WBUFW(buf,2), bl,
+ (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT);
+
+ if(buf) aFree(buf);
+ return 0;
+}
+/*==========================================
+ * HPSP‰ñ•œƒGƒtƒFƒNƒg‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_heal(int fd,int type,int val)
+{
+ WFIFOHEAD(fd,packet_len_table[0x13d]);
+ WFIFOW(fd,0)=0x13d;
+ WFIFOW(fd,2)=type;
+ WFIFOW(fd,4)=val;
+ WFIFOSET(fd,packet_len_table[0x13d]);
+
+ return 0;
+}
+
+/*==========================================
+ * •œŠˆ‚·‚é
+ *------------------------------------------
+ */
+int clif_resurrection(struct block_list *bl,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x148;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=type;
+
+ clif_send(buf,packet_len_table[0x148],bl,type==1 ? AREA : AREA_WOS);
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ clif_spawnpc(((struct map_session_data *)bl));
+
+ return 0;
+}
+
+/*==========================================
+ * PVPŽÀ‘•Hi‰¼j
+ *------------------------------------------
+ */
+int clif_set0199(int fd,int type)
+{
+ WFIFOHEAD(fd,packet_len_table[0x199]);
+ WFIFOW(fd,0)=0x199;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0x199]);
+
+ return 0;
+}
+
+/*==========================================
+ * PVPŽÀ‘•H(‰¼)
+ *------------------------------------------
+ */
+int clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
+{
+ nullpo_retr(0, sd);
+
+ if(map[sd->bl.m].flag.nopvp)
+ return 0;
+
+ if(type == 2) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x19a]);
+ WFIFOW(sd->fd,0) = 0x19a;
+ WFIFOL(sd->fd,2) = sd->bl.id;
+ if(pvprank<=0)
+ pc_calc_pvprank(sd);
+ WFIFOL(sd->fd,6) = pvprank;
+ WFIFOL(sd->fd,10) = pvpnum;
+ WFIFOSET(sd->fd,packet_len_table[0x19a]);
+ } else {
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = 0x19a;
+ WBUFL(buf,2) = sd->bl.id;
+ if(sd->status.option&0x46)
+ // WTF? a -1 to an unsigned value...
+ WBUFL(buf,6) = 0xFFFFFFFF;
+ else
+ if(pvprank<=0)
+ pc_calc_pvprank(sd);
+ WBUFL(buf,6) = pvprank;
+ WBUFL(buf,10) = pvpnum;
+ if(!type)
+ clif_send(buf,packet_len_table[0x19a],&sd->bl,AREA);
+ else
+ clif_send(buf,packet_len_table[0x19a],&sd->bl,ALL_SAMEMAP);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_send0199(int map,int type)
+{
+ struct block_list bl;
+ unsigned char buf[16];
+
+ bl.m = map;
+ WBUFW(buf,0)=0x199;
+ WBUFW(buf,2)=type;
+ clif_send(buf,packet_len_table[0x199],&bl,ALL_SAMEMAP);
+
+ return 0;
+}
+
+/*==========================================
+ * ¸˜BƒGƒtƒFƒNƒg‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val)
+{
+ WFIFOHEAD(fd,packet_len_table[0x188]);
+ WFIFOW(fd,0)=0x188;
+ WFIFOW(fd,2)=fail;
+ WFIFOW(fd,4)=index+2;
+ WFIFOW(fd,6)=val;
+ WFIFOSET(fd,packet_len_table[0x188]);
+
+ return 0;
+}
+
+/*==========================================
+ * Wisp/page is transmitted to the destination player
+ *------------------------------------------
+ */
+int clif_wis_message(int fd, char *nick, char *mes, int mes_len) // R 0097 <len>.w <nick>.24B <message>.?B
+{
+// printf("clif_wis_message(%d, %s, %s)\n", fd, nick, mes);
+
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4;
+ memcpy(WFIFOP(fd,4), nick, NAME_LENGTH);
+ memcpy(WFIFOP(fd,28), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * The transmission result of Wisp/page is transmitted to the source player
+ *------------------------------------------
+ */
+int clif_wis_end(int fd, int flag) // R 0098 <type>.B: 0: success to send wisper, 1: target character is not loged in, 2: ignored by target, 3: everyone ignored by target
+{
+ WFIFOHEAD(fd,packet_len_table[0x98]);
+ WFIFOW(fd,0) = 0x98;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len_table[0x98]);
+ return 0;
+}
+
+/*==========================================
+ * ƒLƒƒƒ‰ID–¼‘Oˆø‚«Œ‹‰Ê‚ð‘—M‚·‚é
+ *------------------------------------------
+ */
+int clif_solved_charname(struct map_session_data *sd,int char_id)
+{
+ char *p= map_charid2nick(char_id);
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if(p!=NULL){
+ WFIFOHEAD(fd,packet_len_table[0x194]);
+ WFIFOW(fd,0)=0x194;
+ WFIFOL(fd,2)=char_id;
+ memcpy(WFIFOP(fd,6), p, NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x194]);
+ }else{
+ map_reqchariddb(sd,char_id);
+ chrif_searchcharid(char_id);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒh‚Ì‘}“ü‰Â”\ƒŠƒXƒg‚ð•Ô‚·
+ *------------------------------------------
+ */
+int clif_use_card(struct map_session_data *sd,int idx)
+{
+ int i,c,ep;
+ int fd=sd->fd;
+
+ nullpo_retr(0, sd);
+ if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets.
+ return 0;
+
+ if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != 6)
+ return 0; //Avoid parsing invalid item indexes (no card/no item)
+
+ ep=sd->inventory_data[idx]->equip;
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x017b;
+
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ int j;
+
+ if(sd->inventory_data[i] == NULL)
+ continue;
+ if(sd->inventory_data[i]->type!=4 && sd->inventory_data[i]->type!=5) // •Ší–h‹ï‚¶‚á‚È‚¢
+ continue;
+ if(sd->status.inventory[i].card[0]==0x00ff || sd->status.inventory[i].card[0]==(short)0xff00 || sd->status.inventory[i].card[0]==0x00fe)
+ continue;
+ if(sd->status.inventory[i].identify==0 ) // –¢ŠÓ’è
+ continue;
+
+ if((sd->inventory_data[i]->equip&ep)==0) // ‘•”õŒÂŠ‚ªˆá‚¤
+ continue;
+ if(sd->inventory_data[i]->type==4 && ep==32) // ‚ƒJ[ƒh‚Æ—¼Žè•Ší
+ 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) // ‚·‚łɃJ[ƒh‚ªˆê”t
+ continue;
+
+ WFIFOW(fd,4+c*2)=i+2;
+ c++;
+ }
+ WFIFOW(fd,2)=4+c*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+/*==========================================
+ * ƒJ[ƒh‚Ì‘}“üI—¹
+ *------------------------------------------
+ */
+int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x17d]);
+ WFIFOW(fd,0)=0x17d;
+ WFIFOW(fd,2)=idx_equip+2;
+ WFIFOW(fd,4)=idx_card+2;
+ WFIFOB(fd,6)=flag;
+ WFIFOSET(fd,packet_len_table[0x17d]);
+ return 0;
+}
+
+/*==========================================
+ * ŠÓ’è‰Â”\ƒAƒCƒeƒ€ƒŠƒXƒg‘—M
+ *------------------------------------------
+ */
+int clif_item_identify_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x177;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){
+ WFIFOW(fd,c*2+4)=i+2;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*2+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * ŠÓ’茋‰Ê
+ *------------------------------------------
+ */
+int clif_item_identified(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x179]);
+ WFIFOW(fd, 0)=0x179;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len_table[0x179]);
+ return 0;
+}
+
+/*==========================================
+ * C—‰Â”\ƒAƒCƒeƒ€ƒŠƒXƒg‘—M
+ *------------------------------------------
+ */
+int clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd)
+{
+ int i,c;
+ int fd;
+ int nameid;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, dstsd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x1fc;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){
+ WFIFOW(fd,c*13+4) = i;
+ WFIFOW(fd,c*13+6) = nameid;
+ WFIFOL(fd,c*13+8) = sd->status.char_id;
+ WFIFOL(fd,c*13+12)= dstsd->status.char_id;
+ WFIFOB(fd,c*13+16)= c;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->state.produce_flag = 1;
+ sd->repair_target=dstsd;
+ }else
+ clif_skill_fail(sd,sd->skillid,0,0);
+
+ return 0;
+}
+int clif_item_repaireffect(struct map_session_data *sd,int nameid,int flag)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x1fe]);
+ WFIFOW(fd, 0)=0x1fe;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 2)=view;
+ else
+ WFIFOW(fd, 2)=nameid;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len_table[0x1fe]);
+
+ return 0;
+}
+
+/*==========================================
+ * Weapon Refining - Taken from jAthena
+ *------------------------------------------
+ */
+int clif_item_refine_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+ int skilllv;
+ int wlv;
+ int refine_item[5];
+
+ nullpo_retr(0, sd);
+
+ skilllv = pc_checkskill(sd,WS_WEAPONREFINE);
+
+ fd=sd->fd;
+
+ refine_item[0] = -1;
+ refine_item[1] = pc_search_inventory(sd,1010);
+ refine_item[2] = pc_search_inventory(sd,1011);
+ refine_item[3] = refine_item[4] = pc_search_inventory(sd,984);
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x221;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skilllv &&
+ sd->status.inventory[i].identify==1 && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 &&
+ refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&0x0022)){
+ WFIFOW(fd,c*13+ 4)=i+2;
+ WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid;
+ WFIFOW(fd,c*13+ 8)=0; //TODO: Wonder what are these for? Perhaps ID of weapon's crafter if any?
+ WFIFOW(fd,c*13+10)=0;
+ WFIFOB(fd,c*13+12)=c;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->state.produce_flag = 1;
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚É‚æ‚éˆêŽž“I‚ȃXƒLƒ‹Œø‰Ê
+ *------------------------------------------
+ */
+int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x147]);
+ WFIFOW(fd, 0)=0x147;
+ WFIFOW(fd, 2)=skillid;
+ WFIFOW(fd, 4)=skill_get_inf(skillid);
+ WFIFOW(fd, 6)=0;
+ WFIFOW(fd, 8)=skilllv;
+ WFIFOW(fd,10)=skill_get_sp(skillid,skilllv);
+ WFIFOW(fd,12)=skill_get_range2(&sd->bl, skillid,skilllv);
+ strncpy((char*)WFIFOP(fd,14),name,NAME_LENGTH);
+ WFIFOB(fd,38)=0;
+ WFIFOSET(fd,packet_len_table[0x147]);
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒg‚ɃAƒCƒeƒ€’ljÁ
+ *------------------------------------------
+ */
+int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
+{
+ int view,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x124]);
+ buf=WFIFOP(fd,0);
+ if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0)
+ return 1;
+
+ WBUFW(buf,0)=0x124;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=sd->status.cart[n].identify;
+ WBUFB(buf,11)=sd->status.cart[n].attribute;
+ WBUFB(buf,12)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,13), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len_table[0x124]);
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒg‚©‚çƒAƒCƒeƒ€íœ
+ *------------------------------------------
+ */
+int clif_cart_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len_table[0x125]);
+ WFIFOW(fd,0)=0x125;
+ WFIFOW(fd,2)=n+2;
+ WFIFOL(fd,4)=amount;
+
+ WFIFOSET(fd,packet_len_table[0x125]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒg‚̃AƒCƒeƒ€ƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_cart_itemlist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_CART * 18 + 4);
+ buf = WFIFOP(fd,0);
+#if PACKETVER < 5
+ for(i=0,n=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*10+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*10+6)=id->view_id;
+ else
+ WBUFW(buf,n*10+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*10+8)=itemtype(id->type);
+ WBUFB(buf,n*10+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*10+10)=sd->status.cart[i].amount;
+ WBUFW(buf,n*10+12)=0;
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x123;
+ WBUFW(buf,2)=4+n*10;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#else
+ for(i=0,n=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*18+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*18+6)=id->view_id;
+ else
+ WBUFW(buf,n*18+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*18+8)=itemtype(id->type);
+ WBUFB(buf,n*18+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*18+10)=sd->status.cart[i].amount;
+ WBUFW(buf,n*18+12)=0; //Here goes the equip location, which seems unnecessary to fill for the cart data.
+ clif_addcards(WBUFP(buf,n*18+14), &sd->status.cart[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x1ef;
+ WBUFW(buf,2)=4+n*18;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒg‚Ì‘•”õ•iƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_cart_equiplist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 20 + 4);
+ buf = WFIFOP(fd,0);
+
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.cart[i].nameid<=0)
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if(!itemdb_isequip2(id))
+ continue;
+ WBUFW(buf,n*20+4)=i+2;
+ if(id->view_id > 0)
+ WBUFW(buf,n*20+6)=id->view_id;
+ else
+ WBUFW(buf,n*20+6)=sd->status.cart[i].nameid;
+ WBUFB(buf,n*20+8)=itemtype(id->type);
+ WBUFB(buf,n*20+9)=sd->status.cart[i].identify;
+ WBUFW(buf,n*20+10)=id->equip;
+ WBUFW(buf,n*20+12)=sd->status.cart[i].equip;
+ WBUFB(buf,n*20+14)=sd->status.cart[i].attribute;
+ WBUFB(buf,n*20+15)=sd->status.cart[i].refine;
+ clif_addcards(WBUFP(buf, n*20+16), &sd->status.cart[i]);
+ n++;
+ }
+ if(n){
+ WBUFW(buf,0)=0x122;
+ WBUFW(buf,2)=4+n*20;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return 0;
+}
+
+/*==========================================
+ * ˜I“XŠJÝ
+ *------------------------------------------
+ */
+int clif_openvendingreq(struct map_session_data *sd,int num)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x12d]);
+ WFIFOW(fd,0)=0x12d;
+ WFIFOW(fd,2)=num;
+ WFIFOSET(fd,packet_len_table[0x12d]);
+
+ return 0;
+}
+
+/*==========================================
+ * ˜I“XŠÅ”•\Ž¦
+ *------------------------------------------
+ */
+int clif_showvendingboard(struct block_list* bl,char *message,int fd)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x131;
+ WBUFL(buf,2)=bl->id;
+ strncpy((char*)WBUFP(buf,6),message,80);
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0x131]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x131]);
+ WFIFOSET(fd,packet_len_table[0x131]);
+ }else{
+ clif_send(buf,packet_len_table[0x131],bl,AREA_WOS);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ˜I“XŠÅ”ÂÁ‹Ž
+ *------------------------------------------
+ */
+int clif_closevendingboard(struct block_list* bl,int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x132;
+ WBUFL(buf,2)=bl->id;
+ if(fd){
+ WFIFOHEAD(fd,packet_len_table[0x132]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x132]);
+ WFIFOSET(fd,packet_len_table[0x132]);
+ }else{
+ clif_send(buf,packet_len_table[0x132],bl,AREA_WOS);
+ }
+
+ return 0;
+}
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€ƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending)
+{
+ struct item_data *data;
+ int i,n,index,fd;
+ struct map_session_data *vsd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, vending);
+ nullpo_retr(0, vsd=map_id2sd(id));
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 8+vsd->vend_num*22);
+ buf = WFIFOP(fd,0);
+ for(i=0,n=0;i<vsd->vend_num;i++){
+ if(vending[i].amount<=0)
+ continue;
+ WBUFL(buf,8+n*22)=vending[i].value;
+ WBUFW(buf,12+n*22)=vending[i].amount;
+ WBUFW(buf,14+n*22)=(index=vending[i].index)+2;
+ if(vsd->status.cart[index].nameid <= 0 || vsd->status.cart[index].amount <= 0)
+ continue;
+ data = itemdb_search(vsd->status.cart[index].nameid);
+ WBUFB(buf,16+n*22)=itemtype(data->type);
+ if(data->view_id > 0)
+ WBUFW(buf,17+n*22)=data->view_id;
+ else
+ WBUFW(buf,17+n*22)=vsd->status.cart[index].nameid;
+ WBUFB(buf,19+n*22)=vsd->status.cart[index].identify;
+ WBUFB(buf,20+n*22)=vsd->status.cart[index].attribute;
+ WBUFB(buf,21+n*22)=vsd->status.cart[index].refine;
+ clif_addcards(WBUFP(buf, 22+n*22), &vsd->status.cart[index]);
+ n++;
+ }
+ if(n > 0){
+ WBUFW(buf,0)=0x133;
+ WBUFW(buf,2)=8+n*22;
+ WBUFL(buf,4)=id;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€w“üŽ¸”s
+ *------------------------------------------
+*/
+int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x135]);
+ WFIFOW(fd,0)=0x135;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=fail;
+ WFIFOSET(fd,packet_len_table[0x135]);
+
+ return 0;
+}
+
+/*==========================================
+ * ˜I“XŠJݬŒ÷
+ *------------------------------------------
+*/
+int clif_openvending(struct map_session_data *sd,int id,struct vending *vending)
+{
+ struct item_data *data;
+ int i,n,index,fd;
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, 8+sd->vend_num*22);
+ buf = WFIFOP(fd,0);
+ for(i=0,n=0;i<sd->vend_num;i++){
+ if (sd->vend_num > 2+pc_checkskill(sd,MC_VENDING)) return 0;
+ WBUFL(buf,8+n*22)=vending[i].value;
+ WBUFW(buf,12+n*22)=(index=vending[i].index)+2;
+ WBUFW(buf,14+n*22)=vending[i].amount;
+ if(sd->status.cart[index].nameid <= 0 || sd->status.cart[index].amount <= 0 || sd->status.cart[index].identify==0 ||
+ sd->status.cart[index].attribute==1) // Prevent unidentified and broken items from being sold [Valaris]
+ continue;
+ data = itemdb_search(sd->status.cart[index].nameid);
+ WBUFB(buf,16+n*22)=itemtype(data->type);
+ if(data->view_id > 0)
+ WBUFW(buf,17+n*22)=data->view_id;
+ else
+ WBUFW(buf,17+n*22)=sd->status.cart[index].nameid;
+ WBUFB(buf,19+n*22)=sd->status.cart[index].identify;
+ WBUFB(buf,20+n*22)=sd->status.cart[index].attribute;
+ WBUFB(buf,21+n*22)=sd->status.cart[index].refine;
+ clif_addcards(WBUFP(buf, 22+n*22), &sd->status.cart[index]);
+ n++;
+ }
+ if(n > 0){
+ WBUFW(buf,0)=0x136;
+ WBUFW(buf,2)=8+n*22;
+ WBUFL(buf,4)=id;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ return n;
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€”Ì”„•ñ
+ *------------------------------------------
+*/
+int clif_vendingreport(struct map_session_data *sd,int index,int amount)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x137]);
+ WFIFOW(fd,0)=0x137;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOSET(fd,packet_len_table[0x137]);
+
+ return 0;
+}
+/*==========================================
+ * ƒp[ƒeƒB쬊®—¹
+ *------------------------------------------
+ */
+int clif_party_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ // printf("clif_party_message(%s, %d, %s)\n", p->name, account_id, mes);
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xfa]);
+ WFIFOW(fd,0)=0xfa;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0xfa]);
+ return 0;
+}
+
+int clif_party_main_info(struct party *p, int fd)
+{
+ struct map_session_data *sd;
+ int i;
+ unsigned char buf[96];
+
+ for (i=0; i<MAX_PARTY && !p->member[i].leader; i++);
+ if (i >= MAX_PARTY) return 0; //Should never happen...
+ sd = p->member[i].sd;
+ WBUFW(buf,0)=0x1e9;
+ WBUFL(buf,2)= p->member[i].account_id;
+ WBUFL(buf,6)= 0; //We don't know yet what this long is about.
+ WBUFW(buf,10)=sd?sd->bl.x:0;
+ WBUFW(buf,12)=sd?sd->bl.y:0;
+ WBUFB(buf,14)=(p->member[i].online)?0:1; //This byte is also unconfirmed...
+ memcpy(WBUFP(buf,15), p->name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39), p->member[i].name, NAME_LENGTH);
+ memcpy(WBUFP(buf,63), mapindex_id2name(p->member[i].map), MAP_NAME_LENGTH);
+ WBUFB(buf,79) = (p->item&1)?1:0;
+ WBUFB(buf,80) = (p->item&2)?1:0;
+ if(fd>=0){
+ WFIFOHEAD(fd,packet_len_table[0x1e9]);
+ memcpy(WFIFOP(fd,0),buf,packet_len_table[0x1e9]);
+ WFIFOSET(fd,packet_len_table[0x1e9]);
+ return 1;
+ }
+ if (!sd) {
+ for (i=0; i<MAX_PARTY && !p->member[i].sd; i++)
+ if (i >= MAX_PARTY) return 0; //Should never happen...
+ sd=p->member[i].sd;
+ }
+ clif_send(buf,packet_len_table[0x1e9],&sd->bl,PARTY);
+ return 1;
+}
+
+/*==========================================
+ * ƒp[ƒeƒBî•ñ‘—M
+ *------------------------------------------
+ */
+int clif_party_info(struct party *p,int fd)
+{
+ unsigned char buf[1024];
+ int i,c;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, p);
+
+ WBUFW(buf,0)=0xfb;
+ memcpy(WBUFP(buf,4),p->name,NAME_LENGTH);
+ for(i=c=0;i<MAX_PARTY;i++){
+ struct party_member *m=&p->member[i];
+ if(m->account_id>0){
+ if(sd==NULL) sd=m->sd;
+ WBUFL(buf,28+c*46)=m->account_id;
+ memcpy(WBUFP(buf,28+c*46+ 4),m->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,28+c*46+28),mapindex_id2name(m->map),MAP_NAME_LENGTH);
+ WBUFB(buf,28+c*46+44)=(m->leader)?0:1;
+ WBUFB(buf,28+c*46+45)=(m->online)?0:1;
+ c++;
+ }
+ }
+ WBUFW(buf,2)=28+c*46;
+ if(fd>=0){ // fd‚ªÝ’肳‚ê‚Ä‚é‚È‚ç‚»‚ê‚É‘—‚é
+ WFIFOHEAD(fd, 28+c*46);
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 9;
+ }
+ if(sd!=NULL)
+ clif_send(buf,WBUFW(buf,2),&sd->bl,PARTY);
+ return 0;
+}
+/*==========================================
+ * ƒp[ƒeƒBŠ©—U
+ *------------------------------------------
+ */
+int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ int fd;
+ struct party *p;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd=tsd->fd;
+
+ if( (p=party_search(sd->status.party_id))==NULL )
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0xfe]);
+ WFIFOW(fd,0)=0xfe;
+ WFIFOL(fd,2)=sd->status.account_id;
+ memcpy(WFIFOP(fd,6),p->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0xfe]);
+ return 0;
+}
+
+/*==========================================
+ * ƒp[ƒeƒBŠ©—UŒ‹‰Ê
+ *------------------------------------------
+ */
+int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xfd]);
+ WFIFOW(fd,0)=0xfd;
+ memcpy(WFIFOP(fd,2),nick,NAME_LENGTH);
+ WFIFOB(fd,26)=flag;
+ WFIFOSET(fd,packet_len_table[0xfd]);
+ return 0;
+}
+
+/*==========================================
+ * ƒp[ƒeƒBÝ’è‘—M
+ * flag & 0x001=exp•ÏXƒ~ƒX
+ * 0x010=item•ÏXƒ~ƒX
+ * 0x100=ˆêl‚É‚Ì‚Ý‘—M
+ *------------------------------------------
+ */
+int clif_party_option(struct party *p,struct map_session_data *sd,int flag)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, p);
+
+// if(battle_config.etc_log)
+// printf("clif_party_option: %d %d %d\n",p->exp,p->item,flag);
+ if(sd==NULL && flag==0){
+ int i;
+ for(i=0;i<MAX_PARTY;i++)
+ if((sd=map_id2sd(p->member[i].account_id))!=NULL)
+ break;
+ }
+ if(sd==NULL)
+ return 0;
+ WBUFW(buf,0)=0x101;
+ WBUFW(buf,2)=((flag&0x01)?2:p->exp);
+ WBUFW(buf,4)=0; //NOTE: We don't know yet what this is for, it is NOT for item share rules, though. [Skotlex]
+ if(flag==0)
+ clif_send(buf,packet_len_table[0x101],&sd->bl,PARTY);
+ else {
+ WFIFOHEAD(sd->fd,packet_len_table[0x101]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x101]);
+ WFIFOSET(sd->fd,packet_len_table[0x101]);
+ }
+ return 0;
+}
+/*==========================================
+ * ƒp[ƒeƒB’E‘Þi’E‘Þ‘O‚ɌĂԂ±‚Æj
+ *------------------------------------------
+ */
+int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag)
+{
+ unsigned char buf[64];
+ int i;
+
+ nullpo_retr(0, p);
+
+ WBUFW(buf,0)=0x105;
+ WBUFL(buf,2)=account_id;
+ memcpy(WBUFP(buf,6),name,NAME_LENGTH);
+ WBUFB(buf,30)=flag&0x0f;
+
+ if((flag&0xf0)==0){
+ if(sd==NULL)
+ for(i=0;i<MAX_PARTY;i++)
+ if((sd=p->member[i].sd)!=NULL)
+ break;
+ if (sd!=NULL)
+ clif_send(buf,packet_len_table[0x105],&sd->bl,PARTY);
+ } else if (sd!=NULL) {
+ WFIFOHEAD(sd->fd,packet_len_table[0x105]);
+ memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x105]);
+ WFIFOSET(sd->fd,packet_len_table[0x105]);
+ }
+ return 0;
+}
+/*==========================================
+ * ƒp[ƒeƒBƒƒbƒZ[ƒW‘—M
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒp[ƒeƒBÀ•W’Ê’m
+ *------------------------------------------
+ */
+int clif_party_xy(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len_table[0x107],&sd->bl,PARTY_SAMEMAP_WOS);
+
+ return 0;
+}
+/*==========================================
+ * ƒp[ƒeƒBHP’Ê’m
+ *------------------------------------------
+ */
+int clif_party_hp(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x106;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=(sd->status.hp > 0x7fff)? 0x7fff:sd->status.hp;
+ WBUFW(buf,8)=(sd->status.max_hp > 0x7fff)? 0x7fff:sd->status.max_hp;
+ clif_send(buf,packet_len_table[0x106],&sd->bl,PARTY_AREA_WOS);
+ return 0;
+}
+
+/*==========================================
+ * Sends HP bar to a single fd. [Skotlex]
+ *------------------------------------------
+ */
+static void clif_hpmeter_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len_table[0x106]);
+ WFIFOW(fd,0) = 0x106;
+ WFIFOL(fd,2) = sd->status.account_id;
+ WFIFOW(fd,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp;
+ WFIFOW(fd,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp;
+ WFIFOSET (fd, packet_len_table[0x106]);
+}
+
+/*==========================================
+ * GM‚ÖꊂÆHP’Ê’m
+ *------------------------------------------
+ */
+int clif_hpmeter(struct map_session_data *sd)
+{
+ struct map_session_data *sd2;
+ unsigned char buf[16];
+ int i, x0, y0, x1, y1;
+ int level;
+
+ nullpo_retr(0, sd);
+
+ x0 = sd->bl.x - AREA_SIZE;
+ y0 = sd->bl.y - AREA_SIZE;
+ x1 = sd->bl.x + AREA_SIZE;
+ y1 = sd->bl.y + AREA_SIZE;
+
+ WBUFW(buf,0) = 0x106;
+ WBUFL(buf,2) = sd->status.account_id;
+ WBUFW(buf,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp;
+ WBUFW(buf,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp;
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd2 = (struct map_session_data*)session[i]->session_data) && sd != sd2 && sd2->state.auth) {
+ if (sd2->bl.m != sd->bl.m ||
+ sd2->bl.x < x0 || sd2->bl.y < y0 ||
+ sd2->bl.x > x1 || sd2->bl.y > y1 ||
+ (level = pc_isGM(sd2)) < battle_config.disp_hpmeter ||
+ level < pc_isGM(sd))
+ continue;
+ WFIFOHEAD (i, packet_len_table[0x106]);
+ memcpy (WFIFOP(i,0), buf, packet_len_table[0x106]);
+ WFIFOSET (i, packet_len_table[0x106]);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒp[ƒeƒBꊈړ®i–¢Žg—pj
+ *------------------------------------------
+ */
+int clif_party_move(struct party *p,struct map_session_data *sd,int online)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, p);
+
+ WBUFW(buf, 0)=0x104;
+ WBUFL(buf, 2)=sd->status.account_id;
+ WBUFL(buf, 6)=0;
+ WBUFW(buf,10)=sd->bl.x;
+ WBUFW(buf,12)=sd->bl.y;
+ WBUFB(buf,14)=!online;
+ memcpy(WBUFP(buf,15),p->name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39),sd->status.name, NAME_LENGTH);
+ memcpy(WBUFP(buf,63),map[sd->bl.m].name, MAP_NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x104],&sd->bl,PARTY);
+ return 0;
+}
+/*==========================================
+ * UŒ‚‚·‚邽‚߂Ɉړ®‚ª•K—v
+ *------------------------------------------
+ */
+int clif_movetoattack(struct map_session_data *sd,struct block_list *bl)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x139]);
+ WFIFOW(fd, 0)=0x139;
+ WFIFOL(fd, 2)=bl->id;
+ WFIFOW(fd, 6)=bl->x;
+ WFIFOW(fd, 8)=bl->y;
+ WFIFOW(fd,10)=sd->bl.x;
+ WFIFOW(fd,12)=sd->bl.y;
+ WFIFOW(fd,14)=sd->attackrange;
+ WFIFOSET(fd,packet_len_table[0x139]);
+ return 0;
+}
+/*==========================================
+ * »‘¢ƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ // –¼‘O‚Ì“o˜^‚Æ‘—M‚ðæ‚É‚µ‚Ä‚¨‚­
+ if( map_charid2nick(sd->status.char_id)==NULL )
+ map_addchariddb(sd->status.char_id,sd->status.name);
+ clif_solved_charname(sd,sd->status.char_id);
+
+ WFIFOHEAD(fd,packet_len_table[0x18f]);
+ WFIFOW(fd, 0)=0x18f;
+ WFIFOW(fd, 2)=flag;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 4)=view;
+ else
+ WFIFOW(fd, 4)=nameid;
+ WFIFOSET(fd,packet_len_table[0x18f]);
+ return 0;
+}
+
+// pet
+int clif_catch_process(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x19e]);
+ WFIFOW(fd,0)=0x19e;
+ WFIFOSET(fd,packet_len_table[0x19e]);
+
+ return 0;
+}
+
+int clif_pet_rulet(struct map_session_data *sd,int data)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a0]);
+ WFIFOW(fd,0)=0x1a0;
+ WFIFOB(fd,2)=data;
+ WFIFOSET(fd,packet_len_table[0x1a0]);
+
+ return 0;
+}
+
+/*==========================================
+ * pet—‘ƒŠƒXƒgì¬
+ *------------------------------------------
+ */
+int clif_sendegg(struct map_session_data *sd)
+{
+ //R 01a6 <len>.w <index>.w*
+ int i,n=0,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m))
+ { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex]
+ clif_displaymessage(fd, "Pets are not allowed in Guild Wars.");
+ return 0;
+ }
+ WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x1a6;
+ if(sd->status.pet_id <= 0) {
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->inventory_data[i]->type!=7 ||
+ sd->status.inventory[i].amount<=0)
+ continue;
+ WFIFOW(fd,n*2+4)=i+2;
+ n++;
+ }
+ }
+ WFIFOW(fd,2)=4+n*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+int clif_send_petdata(struct map_session_data *sd,int type,int param)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, sd->pd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a4]);
+ WFIFOW(fd,0)=0x1a4;
+ WFIFOB(fd,2)=type;
+ WFIFOL(fd,3)=sd->pd->bl.id;
+ WFIFOL(fd,7)=param;
+ WFIFOSET(fd,packet_len_table[0x1a4]);
+
+ return 0;
+}
+
+int clif_send_petstatus(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a2]);
+ WFIFOW(fd,0)=0x1a2;
+ memcpy(WFIFOP(fd,2),sd->pet.name,NAME_LENGTH);
+ WFIFOB(fd,26)=(battle_config.pet_rename == 1)? 0:sd->pet.rename_flag;
+ WFIFOW(fd,27)=sd->pet.level;
+ WFIFOW(fd,29)=sd->pet.hungry;
+ WFIFOW(fd,31)=sd->pet.intimate;
+ WFIFOW(fd,33)=sd->pet.equip;
+ WFIFOSET(fd,packet_len_table[0x1a2]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_pet_emotion(struct pet_data *pd,int param)
+{
+ unsigned char buf[16];
+ struct map_session_data *sd;
+
+ nullpo_retr(0, pd);
+ nullpo_retr(0, sd = pd->msd);
+
+ memset(buf,0,packet_len_table[0x1aa]);
+
+ WBUFW(buf,0)=0x1aa;
+ WBUFL(buf,2)=pd->bl.id;
+ if(param >= 100 && sd->petDB->talk_convert_class) {
+ if(sd->petDB->talk_convert_class < 0)
+ return 0;
+ else if(sd->petDB->talk_convert_class > 0) {
+ param -= (pd->class_ - 100)*100;
+ param += (sd->petDB->talk_convert_class - 100)*100;
+ }
+ }
+ WBUFL(buf,6)=param;
+
+ clif_send(buf,packet_len_table[0x1aa],&pd->bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_performance(struct block_list *bl,int param)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, bl);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=4;
+ WBUFL(buf,3)=bl->id;
+ WBUFL(buf,7)=param;
+
+ clif_send(buf,packet_len_table[0x1a4],bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_equip(struct pet_data *pd,int nameid)
+{
+ unsigned char buf[16];
+ int view;
+
+ nullpo_retr(0, pd);
+
+ memset(buf,0,packet_len_table[0x1a4]);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=3;
+ WBUFL(buf,3)=pd->bl.id;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WBUFL(buf,7)=view;
+ else
+ WBUFL(buf,7)=nameid;
+
+ clif_send(buf,packet_len_table[0x1a4],&pd->bl,AREA);
+
+ return 0;
+}
+
+int clif_pet_food(struct map_session_data *sd,int foodid,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1a3]);
+ WFIFOW(fd,0)=0x1a3;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len_table[0x1a3]);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒI[ƒgƒXƒyƒ‹ ƒŠƒXƒg‘—M
+ *------------------------------------------
+ */
+int clif_autospell(struct map_session_data *sd,int skilllv)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1cd]);
+ WFIFOW(fd, 0)=0x1cd;
+
+ if(skilllv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0)
+ WFIFOL(fd,2)= MG_NAPALMBEAT;
+ else
+ WFIFOL(fd,2)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_COLDBOLT)>0)
+ WFIFOL(fd,6)= MG_COLDBOLT;
+ else
+ WFIFOL(fd,6)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_FIREBOLT)>0)
+ WFIFOL(fd,10)= MG_FIREBOLT;
+ else
+ WFIFOL(fd,10)= 0x00000000;
+ if(skilllv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0)
+ WFIFOL(fd,14)= MG_LIGHTNINGBOLT;
+ else
+ WFIFOL(fd,14)= 0x00000000;
+ if(skilllv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0)
+ WFIFOL(fd,18)= MG_SOULSTRIKE;
+ else
+ WFIFOL(fd,18)= 0x00000000;
+ if(skilllv>7 && pc_checkskill(sd,MG_FIREBALL)>0)
+ WFIFOL(fd,22)= MG_FIREBALL;
+ else
+ WFIFOL(fd,22)= 0x00000000;
+ if(skilllv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0)
+ WFIFOL(fd,26)= MG_FROSTDIVER;
+ else
+ WFIFOL(fd,26)= 0x00000000;
+
+ WFIFOSET(fd,packet_len_table[0x1cd]);
+ return 0;
+}
+
+/*==========================================
+ * ƒfƒBƒ{[ƒVƒ‡ƒ“‚Ì‚¢Ž…
+ *------------------------------------------
+ */
+int clif_devotion(struct map_session_data *sd)
+{
+ unsigned char buf[56];
+ int i,n;
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1cf;
+ WBUFL(buf,2)=sd->bl.id;
+ for(i=0,n=0;i<5;i++) {
+ if (!sd->devotion[i])
+ continue;
+ WBUFL(buf,6+4*n)=sd->devotion[i];
+ n++;
+ }
+ for(;n<5;n++)
+ WBUFL(buf,6+4*n)=0;
+
+ WBUFB(buf,26)=8;
+ WBUFB(buf,27)=0;
+
+ clif_send(buf,packet_len_table[0x1cf],&sd->bl,AREA);
+ return 0;
+}
+
+int clif_marionette(struct block_list *src, struct block_list *target)
+{
+ unsigned char buf[56];
+ int n;
+
+ WBUFW(buf,0)=0x1cf;
+ WBUFL(buf,2)=src->id;
+ for(n=0;n<5;n++)
+ WBUFL(buf,6+4*n)=0;
+ if (target) //The target goes on the second slot.
+ WBUFL(buf,6+4) = target->id;
+ WBUFB(buf,26)=8;
+ WBUFB(buf,27)=0;
+
+ clif_send(buf,packet_len_table[0x1cf],src,AREA);
+ return 0;
+}
+
+/*==========================================
+ * Ÿ†‹…
+ *------------------------------------------
+ */
+int clif_spiritball(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf,0)=0x1d0;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=sd->spiritball;
+ clif_send(buf,packet_len_table[0x1d0],&sd->bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_combo_delay(struct block_list *bl,int wait)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x1d2;
+ WBUFL(buf,2)=bl->id;
+ WBUFL(buf,6)=wait;
+ clif_send(buf,packet_len_table[0x1d2],bl,AREA);
+
+ return 0;
+}
+/*==========================================
+ *”’nŽæ‚è
+ *------------------------------------------
+ */
+int clif_bladestop(struct block_list *src,struct block_list *dst,
+ int _bool)
+{
+ unsigned char buf[32];
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, dst);
+
+ WBUFW(buf,0)=0x1d1;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=_bool;
+
+ clif_send(buf,packet_len_table[0x1d1],src,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_changemapcell(int m,int x,int y,int cell_type,int type)
+{
+ struct block_list bl;
+ unsigned char buf[32];
+
+ bl.m = m;
+ bl.x = x;
+ bl.y = y;
+ WBUFW(buf,0) = 0x192;
+ WBUFW(buf,2) = x;
+ WBUFW(buf,4) = y;
+ WBUFW(buf,6) = cell_type;
+ memcpy(WBUFP(buf,8),map[m].name,MAP_NAME_LENGTH);
+ if(!type)
+ clif_send(buf,packet_len_table[0x192],&bl,AREA);
+ else
+ clif_send(buf,packet_len_table[0x192],&bl,ALL_SAMEMAP);
+
+ return 0;
+}
+
+/*==========================================
+ * MVPƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+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ƒAƒCƒeƒ€Š“¾
+ *------------------------------------------
+ */
+int clif_mvp_item(struct map_session_data *sd,int nameid)
+{
+ int view,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10a]);
+ WFIFOW(fd,0)=0x10a;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd,2)=view;
+ else
+ WFIFOW(fd,2)=nameid;
+ WFIFOSET(fd,packet_len_table[0x10a]);
+ return 0;
+}
+/*==========================================
+ * MVPŒoŒ±’lŠ“¾
+ *------------------------------------------
+ */
+int clif_mvp_exp(struct map_session_data *sd,int exp)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x10b]);
+ WFIFOW(fd,0)=0x10b;
+ WFIFOL(fd,2)=exp;
+ WFIFOSET(fd,packet_len_table[0x10b]);
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh쬉”ےʒm
+ *------------------------------------------
+ */
+int clif_guild_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x167]);
+ WFIFOW(fd,0)=0x167;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x167]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhŠ‘®’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g)
+{
+ int ps,fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+ ps=guild_getposition(sd,g);
+
+ WFIFOHEAD(fd,packet_len_table[0x16c]);
+ memset(WFIFOP(fd,0),0,packet_len_table[0x16c]);
+ WFIFOW(fd,0)=0x16c;
+ WFIFOL(fd,2)=g->guild_id;
+ WFIFOL(fd,6)=g->emblem_id;
+ WFIFOL(fd,10)=g->position[ps].mode;
+ memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x16c]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒoƒƒOƒCƒ“’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag)
+{
+ unsigned char buf[64];
+
+ nullpo_retr(0, g);
+
+ // printf("clif_guild_message(%s, %d, %s)\n", g->name, account_id, mes);
+
+ WBUFW(buf, 0)=0x16d;
+ WBUFL(buf, 2)=g->member[idx].account_id;
+ WBUFL(buf, 6)=g->member[idx].char_id;
+ WBUFL(buf,10)=flag;
+ if(g->member[idx].sd==NULL){
+ struct map_session_data *sd=guild_getavailablesd(g);
+ if(sd!=NULL)
+ clif_send(buf,packet_len_table[0x16d],&sd->bl,GUILD);
+ }else
+ clif_send(buf,packet_len_table[0x16d],&g->member[idx].sd->bl,GUILD_WOS);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒ}ƒXƒ^[’Ê’m(14d‚ւ̉ž“š)
+ *------------------------------------------
+ */
+int clif_guild_masterormember(struct map_session_data *sd)
+{
+ int type=0x57,fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if(sd->state.gmaster_flag)
+ type=0xd7;
+ WFIFOHEAD(fd,packet_len_table[0x14e]);
+ WFIFOW(fd,0)=0x14e;
+ WFIFOL(fd,2)=type;
+ WFIFOSET(fd,packet_len_table[0x14e]);
+ return 0;
+}
+/*==========================================
+ * Basic Info (Territories [Valaris])
+ *------------------------------------------
+ */
+int clif_guild_basicinfo(struct map_session_data *sd)
+{
+ int fd,i,t=0;
+ struct guild *g;
+ struct guild_castle *gc=NULL;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ WFIFOHEAD(fd,packet_len_table[0x1b6]);
+ WFIFOW(fd, 0)=0x1b6;//0x150;
+ WFIFOL(fd, 2)=g->guild_id;
+ WFIFOL(fd, 6)=g->guild_lv;
+ WFIFOL(fd,10)=g->connect_member;
+ WFIFOL(fd,14)=g->max_member;
+ WFIFOL(fd,18)=g->average_lv;
+ WFIFOL(fd,22)=g->exp;
+ WFIFOL(fd,26)=g->next_exp;
+ WFIFOL(fd,30)=0; // ã”[
+ WFIFOL(fd,34)=0; // VWi«Ši‚̈«‚³HF«ŒüƒOƒ‰ƒt¶‰Ej
+ WFIFOL(fd,38)=0; // RFi³‹`‚Ì“x‡‚¢HF«ŒüƒOƒ‰ƒt㉺j
+ WFIFOL(fd,42)=0; // l”H
+ memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH);
+ memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH);
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(g->guild_id == gc->guild_id) t++;
+ }
+ if (t>=0 && t<=MAX_GUILDCASTLE) //(0=None, 1..24 = N of Casles) [Lupus]
+ strncpy((char*)WFIFOP(fd,94),msg_txt(300+t),20);
+ else
+ strncpy((char*)WFIFOP(fd,94),msg_txt(299),20);
+
+ WFIFOSET(fd,packet_len_table[WFIFOW(fd,0)]);
+ clif_guild_emblem(sd,g); // Guild emblem vanish fix [Valaris]
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh“¯–¿/“G‘Îî•ñ
+ *------------------------------------------
+ */
+int clif_guild_allianceinfo(struct map_session_data *sd)
+{
+ int fd,i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4);
+ WFIFOW(fd, 0)=0x14c;
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ struct guild_alliance *a=&g->alliance[i];
+ if(a->guild_id>0){
+ WFIFOL(fd,c*32+4)=a->opposition;
+ WFIFOL(fd,c*32+8)=a->guild_id;
+ memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH);
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*32+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒo[ƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_guild_memberlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ if (!fd)
+ return 0;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ WFIFOHEAD(fd, g->max_member * 104 + 4);
+ WFIFOW(fd, 0)=0x154;
+ for(i=0,c=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==0)
+ continue;
+ WFIFOL(fd,c*104+ 4)=m->account_id;
+ WFIFOL(fd,c*104+ 8)=m->char_id;
+ WFIFOW(fd,c*104+12)=m->hair;
+ WFIFOW(fd,c*104+14)=m->hair_color;
+ WFIFOW(fd,c*104+16)=m->gender;
+ WFIFOW(fd,c*104+18)=m->class_;
+ WFIFOW(fd,c*104+20)=m->lv;
+ WFIFOL(fd,c*104+22)=m->exp;
+ WFIFOL(fd,c*104+26)=m->online;
+ WFIFOL(fd,c*104+30)=m->position;
+ memset(WFIFOP(fd,c*104+34),0,50); // ƒƒ‚H
+ memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH);
+ c++;
+ }
+ WFIFOW(fd, 2)=c*104+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh–ðE–¼ƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_guild_positionnamelist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4);
+ WFIFOW(fd, 0)=0x166;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ WFIFOL(fd,i*28+4)=i;
+ memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH);
+ }
+ WFIFOW(fd,2)=i*28+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh–ðEî•ñƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_guild_positioninfolist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4);
+ WFIFOW(fd, 0)=0x160;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ struct guild_position *p=&g->position[i];
+ WFIFOL(fd,i*16+ 4)=i;
+ WFIFOL(fd,i*16+ 8)=p->mode;
+ WFIFOL(fd,i*16+12)=i;
+ WFIFOL(fd,i*16+16)=p->exp_mode;
+ }
+ WFIFOW(fd, 2)=i*16+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh–ðE•ÏX’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_positionchanged(struct guild *g,int idx)
+{
+ struct map_session_data *sd;
+ unsigned char buf[128];
+
+ nullpo_retr(0, g);
+
+ WBUFW(buf, 0)=0x174;
+ WBUFW(buf, 2)=44;
+ WBUFL(buf, 4)=idx;
+ WBUFL(buf, 8)=g->position[idx].mode;
+ WBUFL(buf,12)=idx;
+ WBUFL(buf,16)=g->position[idx].exp_mode;
+ memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH);
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒo•ÏX’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_memberpositionchanged(struct guild *g,int idx)
+{
+ struct map_session_data *sd;
+ unsigned char buf[64];
+
+ nullpo_retr(0, g);
+
+ WBUFW(buf, 0)=0x156;
+ WBUFW(buf, 2)=16;
+ WBUFL(buf, 4)=g->member[idx].account_id;
+ WBUFL(buf, 8)=g->member[idx].char_id;
+ WBUFL(buf,12)=g->member[idx].position;
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€‘—M
+ *------------------------------------------
+ */
+int clif_guild_emblem(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+
+ if(g->emblem_len<=0)
+ return 0;
+ WFIFOHEAD(fd,g->emblem_len+12);
+ WFIFOW(fd,0)=0x152;
+ WFIFOW(fd,2)=g->emblem_len+12;
+ WFIFOL(fd,4)=g->guild_id;
+ WFIFOL(fd,8)=g->emblem_id;
+ memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒXƒLƒ‹‘—M
+ *------------------------------------------
+ */
+int clif_guild_skillinfo(struct map_session_data *sd)
+{
+ int fd;
+ int i,id,c,up=1;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd, MAX_GUILDSKILL * 37 + 6);
+ WFIFOW(fd,0)=0x0162;
+ WFIFOW(fd,4)=g->skill_point;
+ for(i=c=0;i<MAX_GUILDSKILL;i++){
+ if(g->skill[i].id>0 && guild_check_skill_require(g,g->skill[i].id)){
+ WFIFOW(fd,c*37+ 6) = id = g->skill[i].id;
+ WFIFOW(fd,c*37+ 8) = guild_skill_get_inf(id);
+ WFIFOW(fd,c*37+10) = 0;
+ WFIFOW(fd,c*37+12) = g->skill[i].lv;
+ WFIFOW(fd,c*37+14) = skill_get_sp(id,g->skill[i].lv);
+ WFIFOW(fd,c*37+16) = skill_get_range(id,g->skill[i].lv);
+ memset(WFIFOP(fd,c*37+18),0,24);
+ if(g->skill[i].lv < guild_skill_get_max(id) && (sd == g->member[0].sd))
+ up = 1;
+ else
+ up = 0;
+ WFIFOB(fd,c*37+42)= up;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*37+6;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh’m‘—M
+ *------------------------------------------
+ */
+int clif_guild_notice(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd = sd->fd;
+
+ if ( !session_isActive(fd) ) //null pointer right here [Kevin]
+ return 0;
+
+ if (fd <= 0)
+ return 0;
+ if(*g->mes1==0 && *g->mes2==0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x16f]);
+ WFIFOW(fd,0)=0x16f;
+ memcpy(WFIFOP(fd,2),g->mes1,60);
+ memcpy(WFIFOP(fd,62),g->mes2,120);
+ WFIFOSET(fd,packet_len_table[0x16f]);
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒoŠ©—U
+ *------------------------------------------
+ */
+int clif_guild_invite(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x16a]);
+ WFIFOW(fd,0)=0x16a;
+ WFIFOL(fd,2)=g->guild_id;
+ memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x16a]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒoŠ©—UŒ‹‰Ê
+ *------------------------------------------
+ */
+int clif_guild_inviteack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x169]);
+ WFIFOW(fd,0)=0x169;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x169]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒo’E‘Þ’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf, 0)=0x15a;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ clif_send(buf,packet_len_table[0x15a],&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒo’Ç•ú’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes,
+ int account_id)
+{
+ unsigned char buf[128];
+
+ nullpo_retr(0, sd);
+
+ WBUFW(buf, 0)=0x15c;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ memcpy(WBUFP(buf,66),"dummy",NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x15c],&sd->bl,GUILD);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh’Ç•úƒƒ“ƒoƒŠƒXƒg
+ *------------------------------------------
+ */
+int clif_guild_explusionlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+ WFIFOHEAD(fd,MAX_GUILDEXPLUSION * 88 + 4);
+ WFIFOW(fd,0)=0x163;
+ for(i=c=0;i<MAX_GUILDEXPLUSION;i++){
+ struct guild_explusion *e=&g->explusion[i];
+ if(e->account_id>0){
+ memcpy(WFIFOP(fd,c*88+ 4),e->name,NAME_LENGTH);
+ memcpy(WFIFOP(fd,c*88+28),e->acc,24);
+ memcpy(WFIFOP(fd,c*88+52),e->mes,44);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*88+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh‰ï˜b
+ *------------------------------------------
+ */
+int clif_guild_message(struct guild *g,int account_id,const char *mes,int len)
+{
+ struct map_session_data *sd;
+ unsigned char *buf;
+
+ buf = (unsigned char*)aCallocA(len + 4, sizeof(unsigned char));
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 4;
+ memcpy(WBUFP(buf,4), mes, len);
+
+ if ((sd = guild_getavailablesd(g)) != NULL)
+ clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD);
+
+ if(buf) aFree(buf);
+
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhƒXƒLƒ‹Š„‚èU‚è’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,11);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_num;
+ WFIFOW(fd,4) = lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_num,lv);
+ WFIFOW(fd,8) = skill_get_range(skill_num,lv);
+ WFIFOB(fd,10) = 1;
+ WFIFOSET(fd,11);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh“¯–¿—v¿
+ *------------------------------------------
+ */
+int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x171]);
+ WFIFOW(fd,0)=0x171;
+ WFIFOL(fd,2)=account_id;
+ memcpy(WFIFOP(fd,6),name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len_table[0x171]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh“¯–¿Œ‹‰Ê
+ *------------------------------------------
+ */
+int clif_guild_allianceack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x173]);
+ WFIFOW(fd,0)=0x173;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x173]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhŠÖŒW‰ðÁ’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ if (fd <= 0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x184]);
+ WFIFOW(fd,0)=0x184;
+ WFIFOL(fd,2)=guild_id;
+ WFIFOL(fd,6)=flag;
+ WFIFOSET(fd,packet_len_table[0x184]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒh“G‘ÎŒ‹‰Ê
+ *------------------------------------------
+ */
+int clif_guild_oppositionack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x181]);
+ WFIFOW(fd,0)=0x181;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x181]);
+ return 0;
+}
+/*==========================================
+ * ƒMƒ‹ƒhŠÖŒW’ljÁ
+ *------------------------------------------
+ */
+/*int clif_guild_allianceadded(struct guild *g,int idx)
+{
+ unsigned char buf[64];
+ WBUFW(fd,0)=0x185;
+ WBUFL(fd,2)=g->alliance[idx].opposition;
+ WBUFL(fd,6)=g->alliance[idx].guild_id;
+ memcpy(WBUFP(fd,10),g->alliance[idx].name,NAME_LENGTH);
+ clif_send(buf,packet_len_table[0x185],guild_getavailablesd(g),GUILD);
+ return 0;
+}*/
+
+/*==========================================
+ * ƒMƒ‹ƒh‰ðŽU’Ê’m
+ *------------------------------------------
+ */
+int clif_guild_broken(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x15e]);
+ WFIFOW(fd,0)=0x15e;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len_table[0x15e]);
+ return 0;
+}
+
+/*==========================================
+ * ƒGƒ‚[ƒVƒ‡ƒ“
+ *------------------------------------------
+ */
+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);
+}
+
+/*==========================================
+ * ƒg[ƒL[ƒ{ƒbƒNƒX
+ *------------------------------------------
+ */
+void clif_talkiebox(struct block_list *bl,char* talkie)
+{
+ unsigned char buf[86];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x191;
+ WBUFL(buf,2)=bl->id;
+ memcpy(WBUFP(buf,6),talkie,MESSAGE_SIZE);
+ clif_send(buf,packet_len_table[0x191],bl,AREA);
+}
+
+/*==========================================
+ * Œ‹¥ƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+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);
+}
+/*==========================================
+ * ‚ ‚È‚½‚Ɉ§‚¢‚½‚¢Žg—pŽž–¼‘O‹©‚Ñ
+ *------------------------------------------
+
+void clif_callpartner(struct map_session_data *sd)
+{
+ unsigned char buf[26];
+ char *p;
+
+ nullpo_retv(sd);
+
+ if(sd->status.partner_id){
+ WBUFW(buf,0)=0x1e6;
+ p = map_charid2nick(sd->status.partner_id);
+ if(p){
+ memcpy(WBUFP(buf,2),p,NAME_LENGTH);
+ }else{
+ map_reqchariddb(sd,sd->status.partner_id);
+ chrif_searchcharid(sd->status.partner_id);
+ WBUFB(buf,2) = 0;
+ }
+ clif_send(buf,packet_len_table[0x1e6],&sd->bl,AREA);
+ }
+ return;
+}
+*/
+/*==========================================
+ * Adopt baby [Celest]
+ *------------------------------------------
+ */
+void clif_adopt_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1f8]);
+ WFIFOW(fd,0)=0x1f8;
+ WFIFOSET(fd,packet_len_table[0x1f8]);
+}
+/*==========================================
+ * Marry [DracoRPG]
+ *------------------------------------------
+ */
+void clif_marriage_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1e4]);
+ WFIFOW(fd,0)=0x1e4;
+ WFIFOSET(fd,packet_len_table[0x1e4]);
+}
+
+
+/*==========================================
+ * Notice of divorce
+ *------------------------------------------
+ */
+void clif_divorced(struct map_session_data *sd, char *name)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x205]);
+ WFIFOW(fd,0)=0x205;
+ memcpy(WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len_table[0x205]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ReqAdopt(int fd, struct map_session_data *sd) {
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len_table[0x1f6]);
+ WFIFOW(fd,0)=0x1f6;
+ WFIFOSET(fd, packet_len_table[0x1f6]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ReqMarriage(int fd, struct map_session_data *sd) {
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len_table[0x1e2]);
+ WFIFOW(fd,0)=0x1e2;
+ WFIFOSET(fd, packet_len_table[0x1e2]);
+}
+
+/*==========================================
+ * À‚é
+ *------------------------------------------
+ */
+void clif_sitting(struct map_session_data *sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = sd->bl.id;
+ WBUFB(buf,26) = 2;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+
+ if(sd->disguise) {
+ WBUFL(buf, 2) = -sd->bl.id;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_disp_onlyself(struct map_session_data *sd, char *mes, int len)
+{
+ unsigned char *buf;
+
+ nullpo_retr(0, sd);
+
+ buf = (unsigned char*)aCallocA(len + 5, sizeof(unsigned char));
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 5;
+ memcpy(WBUFP(buf,4), mes, len);
+
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+
+ if(buf) aFree(buf);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+int clif_GM_kickack(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xcd]);
+ WFIFOW(fd,0) = 0xcd;
+ WFIFOL(fd,2) = id;
+ WFIFOSET(fd, packet_len_table[0xcd]);
+ return 0;
+}
+
+void clif_parse_QuitGame(int fd,struct map_session_data *sd);
+
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type)
+{
+ nullpo_retr(0, tsd);
+
+ if(type)
+ clif_GM_kickack(sd,tsd->status.account_id);
+ tsd->opt1 = tsd->opt2 = 0;
+ WFIFOHEAD(tsd->fd,packet_len_table[0x18b]);
+ WFIFOW(tsd->fd,0) = 0x18b;
+ WFIFOW(tsd->fd,2) = 0;
+ WFIFOSET(tsd->fd,packet_len_table[0x18b]);
+
+ if (tsd->fd)
+ {
+ ShowDebug("clif_GM_kick: Disconnecting session #%d\n", tsd->fd);
+ clif_setwaitclose(tsd->fd);
+ } else { //Player has no session attached, delete it right away. [Skotlex]
+ map_quit(tsd);
+ }
+
+ return 0;
+}
+
+int clif_GM_silence(struct map_session_data *sd, struct map_session_data *tsd, int type)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, tsd);
+
+ fd = tsd->fd;
+ if (fd <= 0)
+ return 0;
+ WFIFOHEAD(fd,packet_len_table[0x14b]);
+ WFIFOW(fd,0) = 0x14b;
+ WFIFOB(fd,2) = 0;
+ memcpy(WFIFOP(fd,3), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len_table[0x14b]);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+int clif_timedout(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ ShowInfo("%sCharacter with Account ID '"CL_WHITE"%d"CL_RESET"' timed out.\n", (pc_isGM(sd))?"GM ":"", sd->bl.id);
+ clif_authfail_fd(sd->fd,3); // Even if player is not on we still send anyway
+ clif_setwaitclose(sd->fd); // Set session to EOF
+ return 0;
+}
+
+/*==========================================
+ * Wis‹‘”Û‹–‰Â‰ž“š
+ *------------------------------------------
+ */
+int clif_wisexin(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd1]);
+ WFIFOW(fd,0)=0xd1;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len_table[0xd1]);
+
+ return 0;
+}
+/*==========================================
+ * Wis‘S‹‘”Û‹–‰Â‰ž“š
+ *------------------------------------------
+ */
+int clif_wisall(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0)=0xd2;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len_table[0xd2]);
+
+ return 0;
+}
+/*==========================================
+ * ƒTƒEƒ“ƒhƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x1d3]);
+ WFIFOW(fd,0)=0x1d3;
+ memcpy(WFIFOP(fd,2),name,NAME_LENGTH);
+ WFIFOB(fd,26)=type;
+ WFIFOL(fd,27)=0;
+ WFIFOL(fd,31)=bl->id;
+ WFIFOSET(fd,packet_len_table[0x1d3]);
+
+ return;
+}
+
+int clif_soundeffectall(struct block_list *bl, char *name, int type)
+{
+ unsigned char buf[40];
+ memset(buf, 0, packet_len_table[0x1d3]);
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0)=0x1d3;
+ memcpy(WBUFP(buf,2), name, NAME_LENGTH);
+ WBUFB(buf,26)=type;
+ WBUFL(buf,27)=0;
+ WBUFL(buf,31)=bl->id;
+ clif_send(buf, packet_len_table[0x1d3], bl, AREA);
+
+ return 0;
+}
+
+// displaying special effects (npcs, weather, etc) [Valaris]
+int clif_specialeffect(struct block_list *bl, int type, int flag)
+{
+ unsigned char buf[24];
+
+ nullpo_retr(0, bl);
+
+ memset(buf, 0, packet_len_table[0x1f3]);
+
+ WBUFW(buf,0) = 0x1f3;
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2) = -bl->id;
+ else
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ switch (flag) {
+ case 4:
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA_WOS);
+ break;
+ case 3:
+ clif_send(buf, packet_len_table[0x1f3], bl, ALL_CLIENT);
+ break;
+ case 2:
+ clif_send(buf, packet_len_table[0x1f3], bl, ALL_SAMEMAP);
+ break;
+ case 1:
+ clif_send(buf, packet_len_table[0x1f3], bl, SELF);
+ break;
+ default:
+ clif_send(buf, packet_len_table[0x1f3], bl, AREA);
+ }
+
+ return 0;
+}
+
+// refresh the client's screen, getting rid of any effects
+int clif_refresh(struct map_session_data *sd) {
+ nullpo_retr(-1, sd);
+ clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+ map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,BL_ALL,sd);
+ return 0;
+}
+
+// updates the object's (bl) name on client
+int clif_charnameack (int fd, struct block_list *bl)
+{
+ unsigned char buf[103];
+ int cmd = 0x95;
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = cmd;
+
+ if(bl->type==BL_PC && ((struct map_session_data *)bl)->disguise)
+ WBUFL(buf,2) = -bl->id;
+ else
+ WBUFL(buf,2) = bl->id;
+
+ switch(bl->type) {
+ case BL_PC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)bl;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retr(0, ssd);
+
+ if (strlen(ssd->fakename)>1) {
+ memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH);
+ break;
+ }
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+
+ if (ssd->status.guild_id > 0)
+ g = guild_search(ssd->status.guild_id);
+
+ if (p == NULL && g == NULL)
+ break;
+
+ WBUFW(buf, 0) = cmd = 0x195;
+ if (p)
+ memcpy(WBUFP(buf,30), p->name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g)
+ {
+ int i, ps = -1;
+ for(i = 0; i < g->max_member; i++) {
+ if (g->member[i].account_id == ssd->status.account_id &&
+ g->member[i].char_id == ssd->status.char_id )
+ {
+ ps = g->member[i].position;
+ break;
+ }
+ }
+ if (ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ } else {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ break;
+ case BL_PET:
+ memcpy(WBUFP(buf,6), ((struct pet_data*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_NPC:
+ memcpy(WBUFP(buf,6), ((struct npc_data*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+
+ memcpy(WBUFP(buf,6), md->name, NAME_LENGTH);
+ if (md->guardian_data && md->guardian_data->guild_id) {
+ WBUFW(buf, 0) = cmd = 0x195;
+ WBUFB(buf,30) = 0;
+ memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH);
+ memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH);
+ } else if (battle_config.show_mob_hp == 1) {
+ char mobhp[50];
+ WBUFW(buf, 0) = cmd = 0x195;
+ sprintf(mobhp, "HP: %d/%d", md->hp, md->max_hp);
+ //Even thought mobhp ain't a name, we send it as one so the client
+ //can parse it. [Skotlex]
+ memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH);
+ WBUFB(buf,54) = 0;
+ memcpy(WBUFP(buf,78), mobhp, NAME_LENGTH);
+ }
+ }
+ break;
+ case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex]
+// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
+// break;
+ return 0;
+ default:
+ if (battle_config.error_log)
+ ShowError("clif_parse_GetCharNameRequest : bad type %d(%d)\n", bl->type, bl->id);
+ return 0;
+ }
+
+ // if no receipient specified just update nearby clients
+ if (fd == 0)
+ clif_send(buf, packet_len_table[cmd], bl, AREA);
+ else {
+ WFIFOHEAD(fd, packet_len_table[cmd]);
+ memcpy(WFIFOP(fd, 0), buf, packet_len_table[cmd]);
+ WFIFOSET(fd, packet_len_table[cmd]);
+ }
+
+ return 0;
+}
+
+//Used to update when a char leaves a party/guild. [Skotlex]
+//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent.
+int clif_charnameupdate (struct map_session_data *ssd)
+{
+ unsigned char buf[103];
+ int cmd = 0x195;
+ struct party *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retr(0, ssd);
+
+ if (strlen(ssd->fakename)>1)
+ return 0; //No need to update as the party/guild was not displayed anyway.
+
+ WBUFW(buf,0) = cmd;
+
+ if(ssd->disguise)
+ WBUFL(buf,2) = -(ssd->bl.id);
+ else
+ WBUFL(buf,2) = ssd->bl.id;
+
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+
+ if (ssd->status.guild_id > 0)
+ g = guild_search(ssd->status.guild_id);
+
+ if (p)
+ memcpy(WBUFP(buf,30), p->name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g)
+ {
+ int i, ps = -1;
+ for(i = 0; i < g->max_member; i++) {
+ if (g->member[i].account_id == ssd->status.account_id &&
+ g->member[i].char_id == ssd->status.char_id )
+ {
+ ps = g->member[i].position;
+ break;
+ }
+ }
+ if (ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ } else {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+
+ // Update nearby clients
+ clif_send(buf, packet_len_table[cmd], &ssd->bl, AREA);
+ return 0;
+}
+
+int clif_slide(struct block_list *bl, int x, int y){
+ unsigned char buf[10];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x01ff;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = x;
+ WBUFW(buf, 8) = y;
+
+ clif_send(buf, packet_len_table[0x1ff], bl, AREA);
+ return 0;
+}
+
+/*------------------------------------------
+ * @me command by lordalfa, rewritten implementation by Skotlex
+ *------------------------------------------
+*/
+int clif_disp_overhead(struct map_session_data *sd, char* mes)
+{
+ unsigned char buf[256]; //This should be more than sufficient, the theorical max is MESSAGE_SIZE+NAME_LENGTH + 8 (pads and extra inserted crap)
+ int len_mes = strlen(mes)+1; //Account for \0
+
+ if (len_mes + 8 >= 256) {
+ if (battle_config.error_log)
+ ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes);
+ len_mes = 247; //Trunk it to avoid problems.
+ }
+ // send message to others
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = len_mes + 8; // len of message + 8 (command+len+id)
+ WBUFL(buf,4) = sd->bl.id;
+ memcpy(WBUFP(buf,8), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ WBUFW(buf,0) = 0x8e;
+ WBUFW(buf, 2) = len_mes + 4;
+ memcpy(WBUFP(buf,4), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+
+ return 0;
+}
+
+/*==========================
+ * Minimap fix [Kevin]
+ * Remove dot from minimap
+ *--------------------------
+*/
+int clif_party_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+ nullpo_retr(0, sd);
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len_table[0x107],&sd->bl,PARTY_SAMEMAP_WOS);
+ return 0;
+}
+
+/*==========================================
+ * Info about Star Glaldiator save map [Komurka]
+ *------------------------------------------
+ */
+void clif_feel_info(struct map_session_data *sd, int feel_level)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ memcpy(WFIFOP(fd,2),mapindex_id2name(sd->feel_map[feel_level].index), MAP_NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=0x100+feel_level;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+/*==========================================
+ * Info about Star Glaldiator hate mob [Komurka]
+ *------------------------------------------
+ */
+void clif_hate_mob(struct map_session_data *sd, int skilllv,int mob_id)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ if (pcdb_checkid(mob_id))
+ strncpy(WFIFOP(fd,2),job_name(mob_id), NAME_LENGTH);
+ else if (mobdb_checkid(mob_id))
+ strncpy(WFIFOP(fd,2),mob_db(mob_id)->jname, NAME_LENGTH);
+ else //Really shouldn't happen...
+ memset(WFIFOP(fd,2), 0, NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=0xa00+skilllv-1;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+/*==========================================
+ * Info about TaeKwon Do TK_MISSION mob [Skotlex]
+ *------------------------------------------
+ */
+void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsigned short progress)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ strncpy(WFIFOP(fd,2),mob_db(mob_id)->jname, NAME_LENGTH);
+ WFIFOL(fd,26)=mob_id;
+ WFIFOW(fd,30)=0x1400+progress; //Message to display
+ WFIFOSET(fd, packet_len_table[0x20e]);
+}
+
+// ---------------------
+// clif_guess_PacketVer
+// ---------------------
+// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex]
+static int clif_guess_PacketVer(int fd, int get_previous)
+{
+ static int packet_ver = -1;
+ int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex
+ RFIFOHEAD(fd);
+
+ if (get_previous) //For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex]
+ return packet_ver;
+
+ //By default, start searching on the default one.
+ packet_ver = clif_config.packet_db_ver;
+ cmd = RFIFOW(fd,0);
+ packet_len = RFIFOREST(fd);
+
+ if (
+ cmd == clif_config.connect_cmd[packet_ver] &&
+ packet_len == packet_db[packet_ver][cmd].len &&
+ ((value = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) == 0 || value == 1) &&
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) > 700000 && //Account ID is valid
+ value <= max_account_id &&
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) > 0 && //Char ID is valid
+ value <= max_char_id &&
+ (int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) > 0 //Login 1 is a positive value (?)
+ )
+ return clif_config.packet_db_ver; //Default packet version found.
+
+ for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--)
+ { //Start guessing the version, giving priority to the newer ones. [Skotlex]
+ if (cmd != clif_config.connect_cmd[packet_ver] || //it is not a wanttoconnection for this version.
+ packet_len != packet_db[packet_ver][cmd].len) //The size of the wantoconnection packet does not matches.
+ continue;
+
+ if (
+ (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < 700000 || value > max_account_id
+ || (value = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) < 1 || value > max_char_id
+ //What is login 1? In my tests it is a very very high value.
+ || (int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) < 1
+ //This check seems redundant, all wanttoconnection packets have the gender on the very
+ //last byte of the packet.
+ || (value = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) < 0 || value > 1
+ )
+ continue;
+
+ return packet_ver; //This is our best guess.
+ }
+ packet_ver = -1;
+ return -1;
+}
+
+// ------------
+// clif_parse_*
+// ------------
+// ƒpƒPƒbƒg“Ç‚ÝŽæ‚Á‚ÄFX‘€ì
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WantToConnection(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *old_sd;
+ int cmd, account_id, char_id, login_id1, sex;
+ unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
+ RFIFOHEAD(fd);
+
+ if (sd) {
+ if (battle_config.error_log)
+ ShowError("clif_parse_WantToConnection : invalid request (character already logged in)?\n");
+ return;
+ }
+
+ packet_ver = clif_guess_PacketVer(fd, 1);
+ cmd = RFIFOW(fd,0);
+
+ if (packet_ver > 0)
+ {
+ account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]);
+ char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]);
+ login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]);
+ client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]);
+ sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]);
+
+ if ((old_sd = map_id2sd(account_id)) != NULL)
+ { // if same account already connected, we disconnect the 2 sessions
+ //Check for characters with no connection (includes those that are using autotrade) [durf],[Skotlex]
+ if (!old_sd->fd)
+ map_quit(old_sd);
+ else
+ clif_authfail_fd(old_sd->fd, 2); // same id
+ clif_authfail_fd(fd, 8); // still recognizes last connection
+ } else {
+ sd = (struct map_session_data*)aCalloc(1, sizeof(struct map_session_data));
+
+ sd->fd = fd;
+ sd->packet_ver = packet_ver;
+ session[fd]->session_data = sd;
+
+ pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd);
+ WFIFOHEAD(fd,4);
+ WFIFOL(fd,0) = sd->bl.id;
+ WFIFOSET(fd,4);
+
+ chrif_authreq(sd);
+ }
+ }
+ return;
+}
+
+/*==========================================
+ * 007d ƒNƒ‰ƒCƒAƒ“ƒg‘¤ƒ}ƒbƒv“Ç‚Ýž‚ÝŠ®—¹
+ * mapN“üŽž‚É•K—v‚ȃf[ƒ^‚ð‘S‚Ä‘—‚è‚‚¯‚é
+ *------------------------------------------
+ */
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
+{
+ if(sd->bl.prev != NULL)
+ return;
+
+ if(sd->npc_id) npc_event_dequeue(sd);
+ clif_skillinfoblock(sd);
+ pc_checkitem(sd);
+
+ // loadendackŽž
+ // next exp
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ // skill point
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ // item
+ clif_itemlist(sd);
+ clif_equiplist(sd);
+ // cart
+ if(pc_iscarton(sd)){
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+ // param all
+ clif_initialstatus(sd);
+ // party
+ party_send_movemap(sd);
+ // guild
+ guild_send_memberinfoshort(sd,1);
+
+ if(battle_config.pc_invincible_time > 0) {
+ if(map_flag_gvg(sd->bl.m))
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1);
+ else
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time);
+ }
+
+ map_addblock(&sd->bl); // ƒuƒƒbƒN“o˜^
+ clif_spawnpc(sd); // spawn
+
+ // weight max , now
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ // Show hp after displacement [LuzZza]
+ if(sd->status.party_id)
+ clif_party_hp(sd);
+
+ // set flag, if it's a duel [LuzZza]
+ if(sd->duel_group) {
+ clif_set0199(fd, 1);
+ //clif_misceffect2(&sd->bl, 159);
+ }
+
+ // pvp
+ //if(sd->pvp_timer!=-1 && !battle_config.pk_mode) /PVP Client crash fix* Removed timer deletion
+ // delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
+ if(map[sd->bl.m].flag.pvp){
+ if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
+ if (sd->pvp_timer == -1)
+ sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0);
+ sd->pvp_rank=0;
+ sd->pvp_lastusers=0;
+ sd->pvp_point=5;
+ sd->pvp_won=0;
+ sd->pvp_lost=0;
+ }
+ clif_set0199(sd->fd,1);
+ } else {
+ sd->pvp_timer=-1;
+ }
+ if(map_flag_gvg(sd->bl.m))
+ clif_set0199(sd->fd,3);
+
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_send_petstatus(sd);
+ }
+
+ if(sd->state.connect_new) {
+ sd->state.connect_new = 0;
+ if(sd->status.class_ != sd->view_class)
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 900)
+ clif_pet_emotion(sd->pd,(sd->pd->class_ - 100)*100 + 50 + pet_hungry_val(sd));
+
+/* Stop players from spawning inside castles [Valaris] */
+ {
+ struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name);
+ if (gc)
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,2);
+ }
+/* End Addition [Valaris] */
+ }
+
+ // view equipment item
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 && ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) ||
+ (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) || (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+
+ /* There shouldn't be a need for this anymore because... [Skotlex]
+ * 1. sc_data is saved and loaded now.
+ * 2. if it fails (sc_data is not being saved?) players can just reuse the skill.
+ //if(sd->status.hp<sd->status.max_hp>>2 && pc_checkskill(sd,SM_AUTOBERSERK)>0 &&
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 ))
+ // ƒI[ƒgƒo[ƒT[ƒN”­“®
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+ */
+ if(battle_config.muting_players && sd->status.manner < 0 && battle_config.manner_system)
+ status_change_start(&sd->bl,SC_NOCHAT,0,0,0,0,0,0);
+
+// Lance
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.loadmap_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id);
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.loadmap_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.loadmap_event_name, sd->bl.id), script_config.loadmap_event_name);
+ }
+/* These should not be needed anymore. [Skotlex]
+ * - the option is sent on every player packet, why send it?
+ * - There should be no need to do a signum check on map change, it is done on equipment change.
+ * - Trick-dead is finished on pc_setpos
+ * - Night effect is handled on clif_spawnpc
+ // option
+ clif_changeoption(&sd->bl);
+ if(sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl,SC_TRICKDEAD,-1);
+ if(sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ if(sd->special_state.infinite_endure && sd->sc_data[SC_ENDURE].timer == -1)
+ status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0);
+
+ // Required to eliminate glow bugs because it's executed before clif_changeoption [Lance]
+ //New 'night' effect by dynamix [Skotlex]
+ if (night_flag && map[sd->bl.m].flag.nightenabled)
+ { //Display night.
+ if (sd->state.night) //It must be resent because otherwise players get this annoying aura...
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ else
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ } else if (sd->state.night) { //Clear night display.
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ sd->state.night = 0;
+ }
+*/
+ if (pc_checkskill(sd,SG_KNOWLEDGE) ||
+ pc_checkskill(sd,SG_SUN_COMFORT) ||
+ pc_checkskill(sd,SG_MOON_COMFORT) ||
+ pc_checkskill(sd,SG_STAR_COMFORT))
+ status_calc_pc(sd,0);
+
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka]
+
+ map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,BL_ALL,sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_TickSend(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ sd->client_tick=RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ sd->server_tick = gettick();
+ clif_servertick(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WalkToXY(int fd, struct map_session_data *sd) {
+ int x, y;
+ int cmd;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+
+ if (pc_issit(sd)) //No walking when you are sit!
+ return;
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->state.storage_flag)
+ return;
+
+ if (sd->skilltimer != -1 && pc_checkskill(sd, SA_FREECAST) <= 0) // ƒtƒŠ[ƒLƒƒƒXƒg
+ return;
+
+ if (sd->chatID)
+ return;
+
+ if (!pc_can_move(sd))
+ return;
+
+ if(sd->sc_data && sd->sc_data[SC_RUN].timer != -1)
+ return;
+
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ pc_stopattack(sd);
+
+ cmd = RFIFOW(fd,0);
+ x = RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]) * 4 +
+ (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 1) >> 6);
+ y = ((RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]+1) & 0x3f) << 4) +
+ (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 2) >> 4);
+ //Set last idle time... [Skotlex]
+ sd->idletime = last_tick;
+
+ pc_walktoxy(sd, x, y);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_QuitGame(int fd, struct map_session_data *sd) {
+// unsigned int tick=gettick();
+// struct skill_unit_group* sg;
+
+ WFIFOHEAD(fd,packet_len_table[0x18b]);
+ WFIFOW(fd,0) = 0x18b;
+
+ /* Rovert's prevent logout option fixed [Valaris] */
+ if (sd->sc_data[SC_CLOAKING].timer==-1 && sd->sc_data[SC_HIDING].timer==-1 &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ ) {
+ clif_setwaitclose(fd);
+ WFIFOW(fd,2)=0;
+ } else {
+ WFIFOW(fd,2)=1;
+ }
+ WFIFOSET(fd,packet_len_table[0x18b]);
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void check_fake_id(int fd, struct map_session_data *sd, int target_id) {
+ // if player asks for the fake player (only bot and modified client can see a hiden player)
+/* if (target_id == server_char_id) {
+ char message_to_gm[strlen(msg_txt(536)) + strlen(msg_txt(540)) + strlen(msg_txt(507)) + strlen(msg_txt(508))];
+ sprintf(message_to_gm, msg_txt(536), sd->status.name, sd->status.account_id); // Character '%s' (account: %d) try to use a bot (it tries to detect a fake player).
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_bot < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_bot > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_bot, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_bot); // This player has been banned for %d minute(s).
+ } else { // impossible to display: we don't send fake player if battle_config.ban_bot is == 0
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // send this info cause the bot ask until get an answer, damn spam
+ memset(WPACKETP(0), 0, packet_len_table[0x95]);
+ WPACKETW(0) = 0x95;
+ WPACKETL(2) = server_char_id;
+ strncpy(WPACKETP(6), sd->status.name, 24);
+ SENDPACKET(fd, packet_len_table[0x95]);
+ // take fake player out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_char_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ // take fake mob out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_fake_mob_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ }
+
+ // if player asks for the fake mob (only bot and modified client can see a hiden mob)
+ if (target_id == server_fake_mob_id) {
+ int fake_mob;
+ char message_to_gm[strlen(msg_txt(537)) + strlen(msg_txt(540)) + strlen(msg_txt(507)) + strlen(msg_txt(508))];
+ sprintf(message_to_gm, msg_txt(537), sd->status.name, sd->status.account_id); // Character '%s' (account: %d) try to use a bot (it tries to detect a fake mob).
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_bot < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_bot > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_bot, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_bot); // This player has been banned for %d minute(s).
+ } else { // impossible to display: we don't send fake player if battle_config.ban_bot is == 0
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // send this info cause the bot ask until get an answer, damn spam
+ memset(WPACKETP(0), 0, packet_len_table[0x95]);
+ WPACKETW(0) = 0x95;
+ WPACKETL(2) = server_fake_mob_id;
+ fake_mob = fake_mob_list[(sd->bl.m + sd->fd + sd->status.char_id) % (sizeof(fake_mob_list) / sizeof(fake_mob_list[0]))]; // never same mob
+ if (!mobdb_checkid(fake_mob))
+ fake_mob = 1002; // poring (default)
+ strncpy(WPACKETP(6), mob_db[fake_mob].name, 24);
+ SENDPACKET(fd, packet_len_table[0x95]);
+ // take fake mob out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_fake_mob_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ // take fake player out of screen
+ WPACKETW(0) = 0x80;
+ WPACKETL(2) = server_char_id;
+ WPACKETB(6) = 0;
+ SENDPACKET(fd, packet_len_table[0x80]);
+ }
+*/
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) {
+ int account_id;
+ struct block_list* bl;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if(account_id<0) // for disguises [Valaris]
+ account_id-=account_id*2;
+
+ //Is this possible? Lagged clients could request names of already gone mobs/players. [Skotlex]
+ if ((bl = map_id2bl(account_id)) != NULL)
+ clif_charnameack(fd, bl);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GlobalMessage(int fd, struct map_session_data *sd) { // S 008c <len>.w <str>.?B
+ char *message;
+ unsigned char *buf;
+ RFIFOHEAD(fd);
+
+ message = (char*)RFIFOP(fd,4);
+ if (strlen(message) < strlen(sd->status.name) || //If the incoming string is too short...
+ strncmp(message, sd->status.name, strlen(sd->status.name)) != 0) //Or the name does not matches...
+ {
+ ShowWarning("Hack on global message: character '%s' (account: %d), use an other name to send a (normal) message.\n", sd->status.name, sd->status.account_id);
+ message = (char*)aCallocA(256, sizeof(char));
+ // information is sended to all online GM
+ sprintf(message, "Hack on global message (normal message): character '%s' (account: %d) uses another name.", sd->status.name, sd->status.account_id);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+
+ if (strlen((char*)RFIFOP(fd,4)) == 0)
+ strcpy(message, " This player sends a void name and a void message.");
+ else
+ snprintf(message, 255, " This player sends (name:message): '%128s'.", RFIFOP(fd,4));
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+ // message about the ban
+ if (battle_config.ban_spoof_namer > 0)
+ sprintf(message, " This player has been banned for %d minute(s).", battle_config.ban_spoof_namer);
+ else
+ sprintf(message, " This player hasn't been banned (Ban option is disabled).");
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message);
+
+ // if we ban people
+ if (battle_config.ban_spoof_namer > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_spoof_namer, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(fd); // forced to disconnect because of the hack
+ }
+ else
+ session[fd]->eof = 1; //Disconnect them too, bad packets can cause problems down the road. [Skotlex]
+
+ if(message) aFree(message);
+ return;
+ }
+
+ if ((is_atcommand(fd, sd, message, 0) != AtCommand_None) ||
+ (is_charcommand(fd, sd, message,0) != CharCommand_None) ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer != -1 || //ƒo[ƒT[ƒNŽž‚͉ï˜b‚à•s‰Â
+ sd->sc_data[SC_NOCHAT].timer != -1 ))) //ƒ`ƒƒƒbƒg‹ÖŽ~
+ return;
+
+ buf = (unsigned char*)aCallocA(RFIFOW(fd,2) + 4, sizeof(char));
+
+ // send message to others
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = RFIFOW(fd,2) + 4; // len of message - 4 + 8
+ WBUFL(buf,4) = sd->bl.id;
+ memcpy(WBUFP(buf,8), RFIFOP(fd,4), RFIFOW(fd,2) - 4);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ WFIFOHEAD(fd, RFIFOW(fd,2) + 4);
+ memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2));
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOSET(fd, WFIFOW(fd,2));
+
+#ifdef PCRE_SUPPORT
+ map_foreachinarea(npc_chat_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_NPC, RFIFOP(fd,4), strlen(RFIFOP(fd,4)), &sd->bl);
+#endif
+
+ // Celest
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) { //Super Novice.
+ int next = pc_nextbaseexp(sd);
+ char *rfifo = (char*)RFIFOP(fd,4);
+ if (next > 0 && (sd->status.base_exp * 1000 / next)% 100 == 0) {
+ if (sd->state.snovice_flag == 0 && strstr(rfifo, msg_txt(504)))
+ sd->state.snovice_flag = 1;
+ else if (sd->state.snovice_flag == 1) {
+ message = (char*)aCallocA(128, sizeof(char));
+ sprintf(message, msg_txt(505), sd->status.name);
+ if (strstr(rfifo, message))
+ sd->state.snovice_flag = 2;
+ aFree(message);
+ }
+ else if (sd->state.snovice_flag == 2 && strstr(rfifo, msg_txt(506)))
+ sd->state.snovice_flag = 3;
+ else if (sd->state.snovice_flag == 3) {
+ clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,-1,1);
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_EXPLOSIONSPIRITS],
+ 17,0,0,0,skill_get_time(MO_EXPLOSIONSPIRITS,1),0 ); //Lv17-> +50 critical (noted by Poki) [Skotlex]
+ sd->state.snovice_flag = 0;
+ }
+ }
+ }
+
+ if(buf) aFree(buf);
+
+ return;
+}
+
+int clif_message(struct block_list *bl, char* msg)
+{
+ unsigned short msg_len = strlen(msg) + 1;
+ unsigned char buf[256];
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf, 0) = 0x8d;
+ WBUFW(buf, 2) = msg_len + 8;
+ WBUFL(buf, 4) = bl->id;
+ memcpy(WBUFP(buf, 8), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); // by Gengar
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_MapMove(int fd, struct map_session_data *sd) {
+// /m /mapmove (as @rura GM command)
+ char output[30]; // 17+4+4=26, 30 max.
+ char map_name[MAP_NAME_LENGTH]; //Err... map names are 15+'\0' in size, not 16+'\0' [Skotlex]
+ RFIFOHEAD(fd);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_MapMove))) {
+ memcpy(map_name, RFIFOP(fd,2), MAP_NAME_LENGTH-1);
+ map_name[MAP_NAME_LENGTH-1]='\0';
+ sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20));
+ atcommand_rura(fd, sd, "@rura", output);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_changed_dir(struct block_list *bl) {
+ unsigned char buf[64];
+ struct map_session_data *sd = NULL;
+
+ if (bl->type == BL_PC)
+ nullpo_retv (sd=(struct map_session_data *)bl);
+
+ WBUFW(buf,0) = 0x9c;
+ WBUFL(buf,2) = bl->id;
+ if (sd)
+ WBUFW(buf,6) = sd->head_dir;
+ WBUFB(buf,8) = status_get_dir(bl);
+
+ clif_send(buf, packet_len_table[0x9c], bl, AREA_WOS);
+
+ if(sd && sd->disguise) {
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ WBUFB(buf,8) = status_get_dir(bl);
+ clif_send(buf, packet_len_table[0x9c], bl, AREA);
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeDir(int fd, struct map_session_data *sd) {
+ unsigned char headdir, dir;
+ RFIFOHEAD(fd);
+
+ headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_setdir(sd, dir, headdir);
+
+ clif_changed_dir(&sd->bl);
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Emotion(int fd, struct map_session_data *sd) {
+ unsigned char buf[64];
+ RFIFOHEAD(fd);
+
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
+ if (RFIFOB(fd,2) == 34) {// prevent use of the mute emote [Valaris]
+ clif_skill_fail(sd, 1, 0, 1);
+ return;
+ }
+ // fix flood of emotion icon (ro-proxy): flood only the hacker player
+ if (sd->emotionlasttime >= time(NULL)) {
+ sd->emotionlasttime = time(NULL) + 1; // not more than 1 per second (using /commands the client can spam it)
+ clif_skill_fail(sd, 1, 0, 1);
+ return;
+ }
+ sd->emotionlasttime = time(NULL) + 1; // not more than 1 per second (using /commands the client can spam it)
+
+ WBUFW(buf,0) = 0xc0;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFB(buf,6) = RFIFOB(fd,2);
+ clif_send(buf, packet_len_table[0xc0], &sd->bl, AREA);
+ } else
+ clif_skill_fail(sd, 1, 0, 1);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_HowManyConnections(int fd, struct map_session_data *sd) {
+ WFIFOHEAD(fd,packet_len_table[0xc2]);
+ WFIFOW(fd,0) = 0xc2;
+ WFIFOL(fd,2) = map_getusers();
+ WFIFOSET(fd,packet_len_table[0xc2]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ActionRequest(int fd, struct map_session_data *sd) {
+ unsigned int tick;
+ unsigned char buf[64];
+ int action_type, target_id;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->npc_id != 0 || sd->opt1 > 0 || sd->status.option & 2 || sd->state.storage_flag ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_TRICKDEAD].timer != -1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //ƒI[ƒgƒJƒEƒ“ƒ^[
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //”’nŽæ‚è
+ sd->sc_data[SC_DANCING].timer != -1))) //ƒ_ƒ“ƒX’†
+ return;
+
+ tick = gettick();
+
+ pc_stop_walking(sd, 0);
+ pc_stopattack(sd);
+
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ action_type = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ //Regardless of what they have to do, they have just requested an action, no longer idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if(target_id<0) // for disguises [Valaris]
+ target_id-=(target_id*2);
+
+ switch(action_type) {
+ case 0x00: // once attack
+ case 0x07: // continuous attack
+ if(sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class==JOB_WEDDING)
+ return;
+ if(sd->sc_data[SC_XMAS].timer != -1 || sd->view_class==JOB_XMAS)
+ return;
+ if (sd->vender_id != 0)
+ return;
+ if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
+ if (DIFF_TICK(tick, sd->canact_tick) < 0) {
+ clif_skill_fail(sd, 1, 4, 0);
+ return;
+ }
+ }
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+ pc_attack(sd, target_id, action_type != 0);
+ break;
+ case 0x02: // sitdown
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 3) {
+ if (sd->skilltimer != -1) //No sitting while casting :P
+ break;
+ pc_stopattack(sd);
+ pc_stop_walking(sd, 1);
+ pc_setsit(sd);
+ skill_gangsterparadise(sd, 1); // ƒMƒƒƒ“ƒOƒXƒ^[ƒpƒ‰ƒ_ƒCƒXÝ’è fixed Valaris
+ skill_rest(sd, 1); // TK_HPTIME sitting down mode [Dralnu]
+ clif_sitting(sd);
+ } else
+ clif_skill_fail(sd, 1, 0, 2);
+ break;
+ case 0x03: // standup
+ pc_setstand(sd);
+ skill_gangsterparadise(sd, 0); // ƒMƒƒƒ“ƒOƒXƒ^[ƒpƒ‰ƒ_ƒCƒX‰ðœ fixed Valaris
+ skill_rest(sd, 0); // TK_HPTIME standing up mode [Dralnu]
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = sd->bl.id;
+ WBUFB(buf,26) = 3;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ if(sd->disguise) {
+ WBUFL(buf, 2) = -sd->bl.id;
+ clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA);
+ }
+ break;
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Restart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ switch(RFIFOB(fd,2)) {
+ case 0x00:
+ if (pc_isdead(sd)) {
+ pc_setstand(sd);
+ pc_setrestartvalue(sd, 3);
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 2);
+ }
+ // in case the player's status somehow wasn't updated yet [Celest]
+ else if (sd->status.hp <= 0)
+ pc_setdead(sd);
+ break;
+ case 0x01:
+ /* Rovert's Prevent logout option - Fixed [Valaris] */
+ if (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ {
+ //map_quit(sd); //A clif_quitsave is sent inmediately after this, so no need to quit yet. [Skotlex]
+ chrif_charselectreq(sd);
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0x18b]);
+ WFIFOW(fd,0)=0x18b;
+ WFIFOW(fd,2)=1;
+
+ WFIFOSET(fd,packet_len_table[0x018b]);
+ }
+ break;
+ }
+}
+
+/*==========================================
+ * Transmission of a wisp (S 0096 <len>.w <nick>.24B <message>.?B)
+ *------------------------------------------
+ */
+void clif_parse_Wis(int fd, struct map_session_data *sd) { // S 0096 <len>.w <nick>.24B <message>.?B // rewritten by [Yor]
+ char *gm_command;
+ struct map_session_data *dstsd;
+ int i=0;
+ struct npc_data *npc;
+ char split_data[10][50];
+ int j=0,k=0;
+ char *whisper_tmp;
+ char output[256];
+ RFIFOHEAD(fd);
+
+ //printf("clif_parse_Wis: message: '%s'.\n", RFIFOP(fd,28));
+
+ gm_command = (char*)aCallocA(strlen((const char*)RFIFOP(fd,28)) + 28, sizeof(char)); // 24+3+(RFIFOW(fd,2)-28)+1 or 24+3+(strlen(RFIFOP(fd,28))+1 (size can be wrong with hacker)
+
+ sprintf(gm_command, "%s : %s", sd->status.name, RFIFOP(fd,28));
+ if ((is_charcommand(fd, sd, gm_command, 0) != CharCommand_None) ||
+ (is_atcommand(fd, sd, gm_command, 0) != AtCommand_None) ||
+ (sd && sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //ƒo[ƒT[ƒNŽž‚͉ï˜b‚à•s‰Â
+ sd->sc_data[SC_NOCHAT].timer != -1))) //ƒ`ƒƒƒbƒg‹ÖŽ~
+ {
+ if(gm_command) aFree(gm_command);
+ return;
+ }
+
+ if(gm_command) aFree(gm_command);
+
+ //Chat Logging type 'W' / Whisper
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&2 //if Whisper bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("W", 0, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, (char*)RFIFOP(fd, 4), (char*)RFIFOP(fd, 28));
+
+
+//-------------------------------------------------------//
+// Lordalfa - Paperboy - To whisper NPC commands //
+//-------------------------------------------------------//
+if ((strncasecmp((const char*)RFIFOP(fd,4),"NPC:",4) == 0) && (strlen((const char*)RFIFOP(fd,4)) >4)) {
+ whisper_tmp = (char*) RFIFOP(fd,4) + 4;
+ if ((npc = npc_name2id(whisper_tmp)))
+ {
+ whisper_tmp=(char *)aCallocA(strlen((char *)(RFIFOP(fd,28))+1),sizeof(char));
+ whisper_tmp[0]=0;
+
+ sprintf(whisper_tmp, "%s", (const char*)RFIFOP(fd,28));
+ for( j=0;whisper_tmp[j]!='\0';j++)
+ {
+ if(whisper_tmp[j]!='#')
+ {
+ split_data[i][j-k]=whisper_tmp[j];
+ }
+ else
+ {
+ split_data[i][j-k]='\0';
+ k=j+1;
+ i++;
+ }
+ } // Splits the message using '#' as separators
+ split_data[i][j-k]='\0';
+
+ aFree(whisper_tmp);
+ whisper_tmp=(char *)aCallocA(15,sizeof(char));
+ whisper_tmp[0]=0;
+
+ for (j=0;j<=10;j++)
+ {
+ sprintf(whisper_tmp, "@whispervar%d$", j);
+ set_var(sd,whisper_tmp,(char *) split_data[j]);
+ }//You don't need to zero them, just reset them [Kevin]
+
+ aFree(whisper_tmp);
+ whisper_tmp=(char *)aCallocA(strlen(npc->name)+18,sizeof(char));
+ whisper_tmp[0]=0;
+
+ sprintf(whisper_tmp, "%s::OnWhisperGlobal", npc->name);
+ npc_event(sd,whisper_tmp,0); // Calls the NPC label
+ return;
+
+ aFree(whisper_tmp); //I rewrote it a little to use memory allocation, a bit more stable =P [Kevin]
+ whisper_tmp = NULL;
+ } //should have just removed the one below that was a my bad =P
+}
+
+ // Main chat [LuzZza]
+ if(strcmpi((const char*)RFIFOP(fd,4), main_chat_nick) == 0) {
+ if(!sd->state.mainchat) {
+ sd->state.mainchat = 1;
+ clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
+ }
+ if (sd->sc_data[SC_NOCHAT].timer != -1) {
+ clif_displaymessage(fd, msg_txt(387));
+ return;
+ }
+ sprintf(output, msg_txt(386), sd->status.name, (char *)RFIFOP(fd,28));
+ intif_announce(output, strlen(output) + 1, 0xFE000000, 0);
+ return;
+ }
+
+ // searching destination character
+ dstsd = map_nick2sd((char*)RFIFOP(fd,4));
+ // player is not on this map-server
+ if (dstsd == NULL ||
+ // At this point, don't send wisp/page if it's not exactly the same name, because (example)
+ // if there are 'Test' player on an other map-server and 'test' player on this map-server,
+ // and if we ask for 'Test', we must not contact 'test' player
+ // so, we send information to inter-server, which is the only one which decide (and copy correct name).
+ strcmp(dstsd->status.name, (const char*)RFIFOP(fd,4)) != 0) // not exactly same name
+ // send message to inter-server
+ intif_wis_message(sd, (char*)RFIFOP(fd,4), (char*)RFIFOP(fd,28), RFIFOW(fd,2)-28);
+ // player is on this map-server
+ else {
+ // if you send to your self, don't send anything to others
+ if (dstsd->fd == fd) // but, normaly, it's impossible!
+ clif_wis_message(fd, wisp_server_name, "You can not page yourself. Sorry.", strlen("You can not page yourself. Sorry.") + 1);
+ // otherwise, send message and answer immediatly
+ else {
+ if (dstsd->ignoreAll == 1) {
+ if (dstsd->status.option & OPTION_INVISIBLE && pc_isGM(sd) < pc_isGM(dstsd))
+ clif_wis_end(fd, 1); // 1: target character is not loged in
+ else
+ clif_wis_end(fd, 3); // 3: everyone ignored by target
+ } else {
+ // if player ignore the source character
+ for(i = 0; i < MAX_IGNORE_LIST; i++)
+ if (strcmp(dstsd->ignore[i].name, sd->status.name) == 0) {
+ clif_wis_end(fd, 2); // 2: ignored by target
+ break;
+ }
+ // if source player not found in ignore list
+ if (i == MAX_IGNORE_LIST) {
+ if(strlen(dstsd->away_message) > 0) { // Send away automessage [LuzZza]
+ //(Automessage has been sent)
+ sprintf(output, "%s %s", (char*)RFIFOP(fd,28),msg_txt(543));
+ clif_wis_message(dstsd->fd, sd->status.name, output, strlen(output) + 1);
+ clif_wis_end(fd, 0); // 0: success to send wisper
+ if(dstsd->state.autotrade)
+ //"Away [AT] - \"%s\""
+ sprintf(output, msg_txt(544), dstsd->away_message);
+ else
+ //"Away - \"%s\""
+ sprintf(output, msg_txt(545), dstsd->away_message);
+ clif_wis_message(fd, dstsd->status.name, output, strlen(output) + 1);
+ } else { // Normal message
+ clif_wis_message(dstsd->fd, sd->status.name, (char*)RFIFOP(fd,28), RFIFOW(fd,2) - 28);
+ clif_wis_end(fd, 0); // 0: success to send wisper
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GMmessage(int fd, struct map_session_data *sd) {
+// /b
+ RFIFOHEAD(fd);
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Broadcast)))
+ intif_GMmessage((char*)RFIFOP(fd,4), RFIFOW(fd,2)-4, 0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_TakeItem(int fd, struct map_session_data *sd) {
+ struct flooritem_data *fitem;
+ int map_object_id;
+ RFIFOHEAD(fd);
+
+ map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ fitem = (struct flooritem_data*)map_id2bl(map_object_id);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+
+ if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m)
+ return;
+
+ if( sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner!=0 ||
+ pc_iscloaking(sd) || pc_ischasewalk(sd) || //Disable cloaking/chasewalking characters from looting [Skotlex]
+ sd->sc_data[SC_TRICKDEAD].timer != -1 || //Ž€‚ñ‚¾‚Ó‚è
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //”’nŽæ‚è
+ sd->sc_data[SC_NOCHAT].timer!=-1 ) //‰ï˜b‹ÖŽ~
+ {
+ clif_additem(sd,0,0,6); // send fail packet! [Valaris]
+ return;
+ }
+
+ pc_takeitem(sd, fitem);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_DropItem(int fd, struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner != 0 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //ƒI[ƒgƒJƒEƒ“ƒ^[
+ sd->sc_data[SC_BLADESTOP].timer != -1)//”’nŽæ‚è
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_dropitem(sd, item_index, item_amount);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UseItem(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl, 1);
+ return;
+ }
+ if (sd->vender_id != 0 || (sd->opt1 > 0 && sd->opt1 != OPT1_STONEWAIT) || sd->trade_partner != 0)
+ return;
+ if (sd->npc_id!=0 && sd->npc_id != sd->npc_item_flag) //This flag enables you to use items while in an NPC. [Skotlex]
+ return;
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1 || //Ž€‚ñ‚¾‚Ó‚è
+ sd->sc_data[SC_BLADESTOP].timer != -1 || //”’nŽæ‚è
+ sd->sc_data[SC_BERSERK].timer!=-1 || //ƒo[ƒT[ƒN
+ sd->sc_data[SC_NOCHAT].timer!=-1 ||
+ sd->sc_data[SC_GRAVITATION].timer!=-1) //‰ï˜b‹ÖŽ~
+ return;
+
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+ pc_useitem(sd,RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_EquipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ index = RFIFOW(fd,2)-2;
+ if (index < 0 || index >= MAX_INVENTORY)
+ return; //Out of bounds check.
+
+ if(sd->npc_id!=0 && sd->npc_id != sd->npc_item_flag)
+ return;
+
+ if(sd->vender_id != 0 || sd->trade_partner != 0)
+ return;
+
+ if(sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1 ) return;
+
+ if(sd->status.inventory[index].identify != 1) { // –¢ŠÓ’è
+ clif_equipitemack(sd,index,0,0); // fail
+ return;
+ }
+ //ƒyƒbƒg—p‘•”õ‚Å‚ ‚é‚©‚È‚¢‚©
+ if(sd->inventory_data[index]) {
+ if(sd->inventory_data[index]->type != 8){
+ if(sd->inventory_data[index]->type == 10)
+ RFIFOW(fd,4)=0x8000; // –î‚ð–³—‚â‚è‘•”õ‚Å‚«‚é‚悤‚Éi||G
+ pc_equipitem(sd,index,RFIFOW(fd,4));
+ } else
+ pet_equipitem(sd,index);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || sd->trade_partner != 0)
+ return;
+ index = RFIFOW(fd,2)-2;
+
+ pc_unequipitem(sd,index,1);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0 || RFIFOL(fd,2) < 0) //Clicked on a negative ID? Player disguised as NPC! [Skotlex]
+ return;
+ npc_click(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+ RFIFOHEAD(fd);
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->trade_partner != 0)
+ fail = 1;
+ else
+ fail = npc_buylist(sd,n,item_list);
+
+ WFIFOHEAD(fd,packet_len_table[0xca]);
+ WFIFOW(fd,0)=0xca;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xca]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+ RFIFOHEAD(fd);
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->trade_partner != 0)
+ fail = 1;
+ else
+ fail = npc_selllist(sd,n,item_list);
+
+ WFIFOHEAD(fd,packet_len_table[0xcb]);
+ WFIFOW(fd,0)=0xcb;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_len_table[0xcb]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_CreateChatRoom(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 4){
+ chat_createchat(sd,RFIFOW(fd,4),RFIFOB(fd,6),(char*)RFIFOP(fd,7),(char*)RFIFOP(fd,15),RFIFOW(fd,2)-15);
+ } else
+ clif_skill_fail(sd,1,0,3);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatAddMember(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_joinchat(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatRoomStatusChange(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_changechatstatus(sd,RFIFOW(fd,4),RFIFOB(fd,6),(char*)RFIFOP(fd,7),(char*)RFIFOP(fd,15),RFIFOW(fd,2)-15);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeChatOwner(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_changechatowner(sd,(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ chat_kickchat(sd,(char*)RFIFOP(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatLeave(int fd,struct map_session_data *sd)
+{
+ chat_leavechat(sd);
+}
+
+/*==========================================
+ * Žæˆø—v¿‚ð‘ŠŽè‚É‘—‚é
+ *------------------------------------------
+ */
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 1){
+ trade_traderequest(sd,RFIFOL(sd->fd,2));
+ } else
+ clif_skill_fail(sd,1,0,0);
+}
+
+/*==========================================
+ * Žæˆø—v¿
+ *------------------------------------------
+ */
+void clif_parse_TradeAck(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ trade_tradeack(sd,RFIFOB(sd->fd,2));
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€’ljÁ
+ *------------------------------------------
+ */
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ trade_tradeadditem(sd,RFIFOW(sd->fd,2),RFIFOL(sd->fd,4));
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€’ljÁŠ®—¹(ok‰Ÿ‚µ)
+ *------------------------------------------
+ */
+void clif_parse_TradeOk(int fd,struct map_session_data *sd)
+{
+ trade_tradeok(sd);
+}
+
+/*==========================================
+ * ŽæˆøƒLƒƒƒ“ƒZƒ‹
+ *------------------------------------------
+ */
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd)
+{
+ trade_tradecancel(sd);
+}
+
+/*==========================================
+ * Žæˆø‹–‘ø(trade‰Ÿ‚µ)
+ *------------------------------------------
+ */
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd)
+{
+ trade_tradecommit(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_StopAttack(int fd,struct map_session_data *sd)
+{
+ pc_stopattack(sd);
+}
+
+/*==========================================
+ * ƒJ[ƒg‚ÖƒAƒCƒeƒ€‚ðˆÚ‚·
+ *------------------------------------------
+ */
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0)
+ return;
+ pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+/*==========================================
+ * ƒJ[ƒg‚©‚çƒAƒCƒeƒ€‚ðo‚·
+ *------------------------------------------
+ */
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0 || sd->trade_partner != 0) return;
+ pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+/*==========================================
+ * •t‘®•i(‘é,ƒyƒR,ƒJ[ƒg)‚ð‚Í‚¸‚·
+ *------------------------------------------
+ */
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
+{
+ pc_setoption(sd,0);
+}
+
+/*==========================================
+ * ƒ`ƒFƒ“ƒWƒJ[ƒg
+ *------------------------------------------
+ */
+void clif_parse_ChangeCart(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_setcart(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * ƒXƒe[ƒ^ƒXƒAƒbƒv
+ *------------------------------------------
+ */
+void clif_parse_StatusUp(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_statusup(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒŒƒxƒ‹ƒAƒbƒv
+ *------------------------------------------
+ */
+void clif_parse_SkillUp(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ pc_skillup(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—piIDŽw’èj
+ *------------------------------------------
+ */
+void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) {
+ int skillnum, skilllv, lv, target_id;
+ unsigned int tick = gettick();
+ RFIFOHEAD(fd);
+
+ if (sd->chatID || sd->npc_id != 0 || sd->vender_id != 0 || sd->state.storage_flag || pc_issit(sd))
+ return;
+
+ skilllv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if (skilllv < 1) skilllv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
+ skillnum = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if (skillnotok(skillnum, sd))
+ return;
+
+ if (sd->skilltimer != -1) {
+ if (skillnum != SA_CASTCANCEL)
+ return;
+ } else if (DIFF_TICK(tick, sd->canact_tick) < 0 &&
+ // allow monk combos to ignore this delay [celest]
+ !(sd->sc_count && sd->sc_data[SC_COMBO].timer!=-1 &&
+ (skillnum == MO_EXTREMITYFIST ||
+ skillnum == MO_CHAINCOMBO ||
+ skillnum == MO_COMBOFINISH ||
+ skillnum == CH_PALMSTRIKE ||
+ skillnum == CH_TIGERFIST ||
+ skillnum == CH_CHAINCRUSH))) {
+ clif_skill_fail(sd, skillnum, 4, 0);
+ return;
+ }
+
+ if ((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) ||
+ sd->sc_data[SC_BERSERK].timer != -1 || sd->sc_data[SC_NOCHAT].timer != -1 ||
+ sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 || sd->view_class == JOB_XMAS)
+
+ return;
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ if(target_id<0) // for disguises [Valaris]
+ target_id-=target_id*2;
+
+ if (sd->skillitem >= 0 && sd->skillitem == skillnum) {
+ if (skilllv != sd->skillitemlv)
+ skilllv = sd->skillitemlv;
+ skill_use_id(sd, target_id, skillnum, skilllv);
+ } else {
+ sd->skillitem = sd->skillitemlv = -1;
+ if (skillnum == MO_EXTREMITYFIST) {
+ if ((sd->sc_data[SC_COMBO].timer == -1 ||
+ (sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH &&
+ sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST &&
+ sd->sc_data[SC_COMBO].val1 != CH_CHAINCRUSH))) {
+ if (!sd->state.skill_flag ) {
+ sd->state.skill_flag = 1;
+ clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
+ return;
+ } else if (sd->bl.id == target_id) {
+ clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
+ return;
+ }
+ }
+ }
+ if (skillnum == TK_JUMPKICK) {
+ if (sd->sc_data[SC_COMBO].timer == -1 ||
+ sd->sc_data[SC_COMBO].val1 != TK_JUMPKICK) {
+ if (!sd->state.skill_flag ) {
+ sd->state.skill_flag = 1;
+ clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
+ return;
+ } else if (sd->bl.id == target_id) {
+ clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
+ return;
+ }
+ }
+ }
+
+
+ if ((lv = pc_checkskill(sd, skillnum)) > 0) {
+ if (skilllv > lv)
+ skilllv = lv;
+ skill_use_id(sd, target_id, skillnum, skilllv);
+ if (sd->state.skill_flag)
+ sd->state.skill_flag = 0;
+ }
+ }
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—piꊎw’èj
+ *------------------------------------------
+ */
+void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, int skilllv, int skillnum, int x, int y, int skillmoreinfo)
+{
+ int lv;
+ unsigned int tick = gettick();
+ RFIFOHEAD(fd);
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if (skillnotok(skillnum, sd))
+ return;
+
+ if (skillmoreinfo != -1) {
+ if (pc_issit(sd)) {
+ clif_skill_fail(sd, skillnum, 0, 0);
+ return;
+ }
+ memcpy(talkie_mes, RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
+ talkie_mes[MESSAGE_SIZE-1] = '\0'; //Overflow protection [Skotlex]
+ }
+
+ if (sd->skilltimer != -1)
+ return;
+ else if (DIFF_TICK(tick, sd->canact_tick) < 0)
+ {
+ clif_skill_fail(sd, skillnum, 4, 0);
+ return;
+ }
+
+ if ((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) ||
+ sd->sc_data[SC_BERSERK].timer != -1 || sd->sc_data[SC_NOCHAT].timer != -1 ||
+ sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class == JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 || sd->view_class == JOB_XMAS)
+ return;
+ if (sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+ if (sd->skillitem >= 0 && sd->skillitem == skillnum) {
+ if (skilllv != sd->skillitemlv)
+ skilllv = sd->skillitemlv;
+ skill_use_pos(sd, x, y, skillnum, skilllv);
+ } else {
+ sd->skillitem = sd->skillitemlv = -1;
+ if ((lv = pc_checkskill(sd, skillnum)) > 0) {
+ if (skilllv > lv)
+ skilllv = lv;
+ skill_use_pos(sd, x, y, skillnum,skilllv);
+ }
+ }
+}
+
+
+void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->chatID || sd->state.storage_flag || pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ -1 //Skill more info.
+ );
+}
+
+void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->chatID || sd->state.storage_flag) return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info
+ );
+}
+/*==========================================
+ * ƒXƒLƒ‹Žg—pimapŽw’èj
+ *------------------------------------------
+ */
+void clif_parse_UseSkillMap(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(sd->chatID) return;
+
+ if (sd->npc_id!=0 || sd->vender_id != 0 || (sd->sc_data &&
+ (sd->sc_data[SC_TRICKDEAD].timer != -1 ||
+ sd->sc_data[SC_BERSERK].timer!=-1 ||
+ sd->sc_data[SC_NOCHAT].timer!=-1 ||
+ sd->sc_data[SC_WEDDING].timer!=-1 ||
+ sd->view_class==JOB_WEDDING ||
+ sd->sc_data[SC_XMAS].timer != -1 ||
+ sd->view_class == JOB_XMAS)))
+ return;
+
+ if(sd->invincible_timer != -1)
+ pc_delinvincibletimer(sd);
+
+ skill_castend_map(sd,RFIFOW(fd,2),(char*)RFIFOP(fd,4));
+}
+/*==========================================
+ * ƒƒ‚—v‹
+ *------------------------------------------
+ */
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd)
+{
+ if (!pc_isdead(sd))
+ pc_memo(sd,-1);
+}
+/*==========================================
+ * ƒAƒCƒeƒ€‡¬
+ *------------------------------------------
+ */
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
+}
+/*==========================================
+ * •ŠíC—
+ *------------------------------------------
+ */
+void clif_parse_RepairItem(int fd, struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_repairweapon(sd,RFIFOW(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) {
+ int idx;
+ RFIFOHEAD(fd);
+
+ if (!sd->state.produce_flag) //Packet exploit?
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ idx = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_weaponrefine(sd, idx-2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ sd->npc_menu=RFIFOB(fd,6);
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+#ifdef __WIN32
+ //For some extraordinarily eerie reason that noone has figured out yet,
+ //windows native compiles NEED this nullpo_retv or the function is not found!
+ nullpo_retv(sd);
+#endif
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+#define RFIFOL_(fd,pos) (*(int*)(session[fd]->rdata+session[fd]->rdata_pos+(pos)))
+ //Input Value overflow Exploit FIX
+ sd->npc_amount=RFIFOL_(fd,6); //fixed by Lupus. npc_amount is (int) but was RFIFOL changing it to (unsigned int)
+#undef RFIFOL_
+
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcStringInput(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+
+ if(RFIFOW(fd,2)-7 >= sizeof(sd->npc_str)){
+ ShowWarning("clif: input string too long !\n");
+ memcpy(sd->npc_str,RFIFOP(fd,8),sizeof(sd->npc_str));
+ sd->npc_str[sizeof(sd->npc_str)-1]=0;
+ } else
+ strcpy(sd->npc_str,(char*)RFIFOP(fd,8));
+ npc_scriptcont(sd,RFIFOL(fd,4));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€ŠÓ’è
+ *------------------------------------------
+ */
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ skill_identify(sd,RFIFOW(fd,2)-2);
+}
+/*==========================================
+ * –îì¬
+ *------------------------------------------
+ */
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (!sd->state.produce_flag)
+ return;
+ sd->state.produce_flag = 0;
+ if (sd->npc_id) { //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ skill_arrow_create(sd,RFIFOW(fd,2));
+}
+/*==========================================
+ * ƒI[ƒgƒXƒyƒ‹ŽóM
+ *------------------------------------------
+ */
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ skill_autospell(sd,RFIFOW(fd,2));
+}
+/*==========================================
+ * ƒJ[ƒhŽg—p
+ *------------------------------------------
+ */
+void clif_parse_UseCard(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ clif_use_card(sd,RFIFOW(fd,2)-2);
+}
+/*==========================================
+ * ƒJ[ƒh‘}“ü‘•”õ‘I‘ð
+ *------------------------------------------
+ */
+void clif_parse_InsertCard(int fd,struct map_session_data *sd)
+{
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2);
+}
+
+/*==========================================
+ * 0193 ƒLƒƒƒ‰ID–¼‘Oˆø‚«
+ *------------------------------------------
+ */
+void clif_parse_SolveCharName(int fd, struct map_session_data *sd) {
+ int char_id;
+ RFIFOHEAD(fd);
+
+ char_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ clif_solved_charname(sd, char_id);
+}
+
+/*==========================================
+ * 0197 /resetskill /resetstate
+ *------------------------------------------
+ */
+void clif_parse_ResetChar(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ pc_isGM(sd) >= get_atcommand_level(AtCommand_ResetState)) {
+ switch(RFIFOW(fd,2)){
+ case 0:
+ pc_resetstate(sd);
+ break;
+ case 1:
+ pc_resetskill(sd);
+ break;
+ }
+ }
+}
+
+/*==========================================
+ * 019c /lb“™
+ *------------------------------------------
+ */
+void clif_parse_LGMmessage(int fd, struct map_session_data *sd) {
+ unsigned char buf[512];
+ RFIFOHEAD(fd);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_LocalBroadcast))) {
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = RFIFOW(fd,2);
+ memcpy(WBUFP(buf,4), RFIFOP(fd,4), RFIFOW(fd,2) - 4);
+ clif_send(buf, RFIFOW(fd,2), &sd->bl, ALL_SAMEMAP);
+ }
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚Ö“ü‚ê‚é
+ *------------------------------------------
+ */
+void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ if (item_index < 0 || item_index >= MAX_INVENTORY)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageadd(sd, item_index, item_amount);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageadd(sd, item_index, item_amount);
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚©‚ço‚·
+ *------------------------------------------
+ */
+void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) {
+ int item_index, item_amount;
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageget(sd, item_index, item_amount);
+ else if(sd->state.storage_flag == 2)
+ storage_guild_storageget(sd, item_index, item_amount);
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ÖƒJ[ƒg‚©‚ç“ü‚ê‚é
+ *------------------------------------------
+ */
+void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚©‚ço‚·
+ *------------------------------------------
+ */
+void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0 || !sd->state.storage_flag)
+ return;
+ if (sd->state.storage_flag == 1)
+ storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ð•Â‚¶‚é
+ *------------------------------------------
+ */
+void clif_parse_CloseKafra(int fd, struct map_session_data *sd) {
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+}
+
+/*==========================================
+ * ƒp[ƒeƒB‚ðì‚é
+ *------------------------------------------
+ */
+void clif_parse_CreateParty(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7) {
+ party_create(sd,(char*)RFIFOP(fd,2),0,0);
+ } else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * ƒp[ƒeƒB‚ðì‚é
+ *------------------------------------------
+ */
+void clif_parse_CreateParty2(int fd, struct map_session_data *sd) {
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7){
+ RFIFOHEAD(fd);
+ party_create(sd,(char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27));
+ } else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * ƒp[ƒeƒB‚ÉŠ©—U
+ *------------------------------------------
+ */
+void clif_parse_PartyInvite(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_invite(sd, RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ƒp[ƒeƒBŠ©—U•Ô“š
+ *------------------------------------------
+ */
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 5){
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+ } else {
+ party_reply_invite(sd,RFIFOL(fd,2),-1);
+ clif_skill_fail(sd,1,0,4);
+ }
+}
+
+/*==========================================
+ * ƒp[ƒeƒB’E‘Þ—v‹
+ *------------------------------------------
+ */
+void clif_parse_LeaveParty(int fd, struct map_session_data *sd) {
+ party_leave(sd);
+}
+
+/*==========================================
+ * ƒp[ƒeƒBœ–¼—v‹
+ *------------------------------------------
+ */
+void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ * ƒp[ƒeƒBÝ’è•ÏX—v‹
+ *------------------------------------------
+ */
+void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ party_changeoption(sd, RFIFOW(fd,2), RFIFOW(fd,4));
+}
+
+/*==========================================
+ * ƒp[ƒeƒBƒƒbƒZ[ƒW‘—M—v‹
+ *------------------------------------------
+ */
+void clif_parse_PartyMessage(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (is_charcommand(fd, sd, (char*)RFIFOP(fd,4), 0) != CharCommand_None ||
+ is_atcommand(fd, sd, (char*)RFIFOP(fd,4), 0) != AtCommand_None ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //ƒo[ƒT[ƒNŽž‚͉ï˜b‚à•s‰Â
+ sd->sc_data[SC_NOCHAT].timer!=-1))) //ƒ`ƒƒƒbƒg‹ÖŽ~
+ return;
+
+ party_send_message(sd, (char*)RFIFOP(fd,4), RFIFOW(fd,2)-4);
+}
+
+/*==========================================
+ * ˜I“X•Â½
+ *------------------------------------------
+ */
+void clif_parse_CloseVending(int fd, struct map_session_data *sd) {
+ vending_closevending(sd);
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€ƒŠƒXƒg—v‹
+ *------------------------------------------
+ */
+void clif_parse_VendingListReq(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ vending_vendinglistreq(sd,RFIFOL(fd,2));
+ if(sd->npc_id)
+ npc_event_dequeue(sd);
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€w“ü
+ *------------------------------------------
+ */
+void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ vending_purchasereq(sd, RFIFOW(fd,2), RFIFOL(fd,4), RFIFOP(fd,8));
+}
+
+/*==========================================
+ * ˜I“XŠJÝ
+ *------------------------------------------
+ */
+void clif_parse_OpenVending(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ if (sd->trade_partner != 0)
+ return;
+ vending_openvending(sd, RFIFOW(fd,2), (char*)RFIFOP(fd,4), RFIFOB(fd,84), RFIFOP(fd,85));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh‚ðì‚é
+ *------------------------------------------
+ */
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_create(sd, (char*)RFIFOP(fd,6));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒ}ƒXƒ^[‚©‚Ç‚¤‚©Šm”F
+ *------------------------------------------
+ */
+void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd) {
+ clif_guild_masterormember(sd);
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhî•ñ—v‹
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ switch(RFIFOL(fd,2)){
+ case 0: // ƒMƒ‹ƒhŠî–{î•ñA“¯–¿“G‘Îî•ñ
+ clif_guild_basicinfo(sd);
+ clif_guild_allianceinfo(sd);
+ break;
+ case 1: // ƒƒ“ƒo[ƒŠƒXƒgA–ðE–¼ƒŠƒXƒg
+ clif_guild_positionnamelist(sd);
+ clif_guild_memberlist(sd);
+ break;
+ case 2: // –ðE–¼ƒŠƒXƒgA–ðEî•ñƒŠƒXƒg
+ clif_guild_positionnamelist(sd);
+ clif_guild_positioninfolist(sd);
+ break;
+ case 3: // ƒXƒLƒ‹ƒŠƒXƒg
+ clif_guild_skillinfo(sd);
+ break;
+ case 4: // ’Ç•úƒŠƒXƒg
+ clif_guild_explusionlist(sd);
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2));
+ break;
+ }
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh–ðE•ÏX
+ *------------------------------------------
+ */
+void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) {
+ int i;
+ RFIFOHEAD(fd);
+
+ for(i = 4; i < RFIFOW(fd,2); i += 40 ){
+ guild_change_position(sd, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16));
+ }
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒƒ“ƒo–ðE•ÏX
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) {
+ int i;
+ RFIFOHEAD(fd);
+
+ for(i=4;i<RFIFOW(fd,2);i+=12){
+ guild_change_memberposition(sd->status.guild_id,
+ RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8));
+ }
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€—v‹
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd) {
+ struct guild *g;
+ RFIFOHEAD(fd);
+ g=guild_search(RFIFOL(fd,2));
+ if(g!=NULL)
+ clif_guild_emblem(sd,g);
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_change_emblem(sd,RFIFOW(fd,2)-4,(char*)RFIFOP(fd,4));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh’m•ÏX
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeNotice(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_change_notice(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6),(char*)RFIFOP(fd,66));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhŠ©—U
+ *------------------------------------------
+ */
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_invite(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhŠ©—U•ÔM
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh’E‘Þ
+ *------------------------------------------
+ */
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh’Ç•ú
+ *------------------------------------------
+ */
+void clif_parse_GuildExplusion(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_explusion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh‰ï˜b
+ *------------------------------------------
+ */
+void clif_parse_GuildMessage(int fd,struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+
+ if (is_charcommand(fd, sd, (char*)RFIFOP(fd, 4), 0) != CharCommand_None ||
+ is_atcommand(fd, sd, (char*)RFIFOP(fd, 4), 0) != AtCommand_None ||
+ (sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //ƒo[ƒT[ƒNŽž‚͉ï˜b‚à•s‰Â
+ sd->sc_data[SC_NOCHAT].timer!=-1))) //ƒ`ƒƒƒbƒg‹ÖŽ~
+ return;
+
+ guild_send_message(sd, (char*)RFIFOP(fd,4), RFIFOW(fd,2)-4);
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh“¯–¿—v‹
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reqalliance(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh“¯–¿—v‹•ÔM
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhŠÖŒW‰ðÁ
+ *------------------------------------------
+ */
+void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh“G‘Î
+ *------------------------------------------
+ */
+void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_opposition(sd,RFIFOL(fd,2));
+}
+
+/*==========================================
+ * ƒMƒ‹ƒh‰ðŽU
+ *------------------------------------------
+ */
+void clif_parse_GuildBreak(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ guild_break(sd,(char*)RFIFOP(fd,2));
+}
+
+// pet
+void clif_parse_PetMenu(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_menu(sd,RFIFOB(fd,2));
+}
+
+void clif_parse_CatchPet(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_catch_process2(sd,RFIFOL(fd,2));
+}
+
+void clif_parse_SelectEgg(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_select_egg(sd,RFIFOW(fd,2)-2);
+}
+
+void clif_parse_SendEmotion(int fd, struct map_session_data *sd) {
+ if(sd->pd) {
+ RFIFOHEAD(fd);
+ clif_pet_emotion(sd->pd,RFIFOL(fd,2));
+ }
+}
+
+void clif_parse_ChangePetName(int fd, struct map_session_data *sd) {
+ RFIFOHEAD(fd);
+ pet_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+// Kick (right click menu for GM "(name) force to quit")
+void clif_parse_GMKick(int fd, struct map_session_data *sd) {
+ struct block_list *target;
+ int tid;
+
+ RFIFOHEAD(fd);
+ tid = RFIFOL(fd,2);
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Kick))) {
+ target = map_id2bl(tid);
+ if (target) {
+ if (target->type == BL_PC) {
+ struct map_session_data *tsd = (struct map_session_data *)target;
+ if (pc_isGM(sd) > pc_isGM(tsd))
+ clif_GM_kick(sd, tsd, 1);
+ else
+ clif_GM_kickack(sd, 0);
+ } else if (target->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)target;
+ sd->state.attack_type = 0;
+ mob_damage(&sd->bl, md, md->hp, 2);
+ } else
+ clif_GM_kickack(sd, 0);
+ } else
+ clif_GM_kickack(sd, 0);
+ }
+}
+
+/*==========================================
+ * /shift
+ *------------------------------------------
+ */
+void clif_parse_Shift(int fd, struct map_session_data *sd) { // Rewriten by [Yor]
+ char player_name[NAME_LENGTH+1];
+
+ memset(player_name, '\0', sizeof(player_name));
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_JumpTo))) {
+ RFIFOHEAD(fd);
+ memcpy(player_name, RFIFOP(fd,2), NAME_LENGTH);
+ atcommand_jumpto(fd, sd, "@jumpto", player_name); // as @jumpto
+ }
+
+ return;
+}
+
+/*==========================================
+ * /recall
+ *------------------------------------------
+ */
+void clif_parse_Recall(int fd, struct map_session_data *sd) { // Added by RoVeRT
+ char player_name[25];
+
+ memset(player_name, '\0', sizeof(player_name));
+
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Recall))) {
+ RFIFOHEAD(fd);
+ memcpy(player_name, RFIFOP(fd,2), NAME_LENGTH);
+ atcommand_recall(fd, sd, "@recall", player_name); // as @recall
+ }
+
+ return;
+}
+
+/*==========================================
+ * /monster /item rewriten by [Yor]
+ *------------------------------------------
+ */
+void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) {
+ char monster_item_name[NAME_LENGTH+10]; //Additional space is for logging, eg: "@monster Poring"
+ int level;
+
+ memset(monster_item_name, '\0', sizeof(monster_item_name));
+
+ if (battle_config.atc_gmonly == 0 || pc_isGM(sd)) {
+ RFIFOHEAD(fd);
+ memcpy(monster_item_name, RFIFOP(fd,2), NAME_LENGTH);
+
+ if (mobdb_searchname(monster_item_name) != 0) {
+ if (pc_isGM(sd) >= (level =get_atcommand_level(AtCommand_Monster)))
+ {
+ atcommand_monster(fd, sd, "@spawn", monster_item_name); // as @spawn
+ if(log_config.gm && level >= log_config.gm)
+ { //Log action. [Skotlex]
+ snprintf(monster_item_name, sizeof(monster_item_name)-1, "@spawn %s", RFIFOP(fd,2));
+ log_atcommand(sd, monster_item_name);
+ }
+ }
+ } else if (itemdb_searchname(monster_item_name) != NULL) {
+ if (pc_isGM(sd) >= (level = get_atcommand_level(AtCommand_Item)))
+ {
+ atcommand_item(fd, sd, "@item", monster_item_name); // as @item
+ if(log_config.gm && level >= log_config.gm)
+ { //Log action. [Skotlex]
+ snprintf(monster_item_name, sizeof(monster_item_name)-1, "@item %s", RFIFOP(fd,2));
+ log_atcommand(sd, monster_item_name);
+ }
+ }
+ }
+ }
+}
+
+void clif_parse_GMHide(int fd, struct map_session_data *sd) { // Modified by [Yor]
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide))) {
+ if (sd->status.option & OPTION_INVISIBLE) {
+ sd->status.option &= ~OPTION_INVISIBLE;
+ clif_displaymessage(fd, "Invisible: Off.");
+ } else {
+ sd->status.option |= OPTION_INVISIBLE;
+ clif_displaymessage(fd, "Invisible: On.");
+ }
+ clif_changeoption(&sd->bl);
+ }
+}
+
+/*==========================================
+ * GM‚É‚æ‚éƒ`ƒƒƒbƒg‹ÖŽ~ŽžŠÔ•t—^
+ *------------------------------------------
+ */
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd)
+{
+ int type, limit, level;
+ struct block_list *bl;
+ struct map_session_data *dstsd;
+
+ if(!battle_config.muting_players) {
+ clif_displaymessage(fd, "Muting is disabled.");
+ return;
+ }
+
+ RFIFOHEAD(fd);
+ bl = map_id2bl(RFIFOL(fd,2));
+ if (!bl || bl->type != BL_PC)
+ return;
+ nullpo_retv(dstsd =(struct map_session_data *)bl);
+
+ type = RFIFOB(fd,6);
+ limit = RFIFOW(fd,7);
+ if (type == 0)
+ limit = 0 - limit;
+
+ //Temporarily disable chars from muting themselves due to the mysterious "DON'T USE BOT!" message. [Skotlex]
+ //Also, if type is 2 and the ids don't match, this is a crafted hacked packet!
+ //So for now, type 2is just totally disabled.
+ if (type == 2/*&& sd->bl.id != dstsd->bl.id*/)
+ return;
+
+ if (
+ ((level = pc_isGM(sd)) > pc_isGM(dstsd) && level >= get_atcommand_level(AtCommand_Mute))
+ || (type == 2 && !level)) {
+ clif_GM_silence(sd, dstsd, ((type == 2) ? 1 : type));
+ if (battle_config.manner_system)
+ {
+ dstsd->status.manner -= limit;
+ if(dstsd->status.manner < 0)
+ status_change_start(bl,SC_NOCHAT,0,0,0,0,0,0);
+ else
+ {
+ dstsd->status.manner = 0;
+ status_change_end(bl,SC_NOCHAT,-1);
+ }
+ }
+ ShowDebug("GMReqNoChat: name:%s type:%d limit:%d manner:%d\n", dstsd->status.name, type, limit, dstsd->status.manner);
+ }
+
+ return;
+}
+/*==========================================
+ * GM‚É‚æ‚éƒ`ƒƒƒbƒg‹ÖŽ~ŽžŠÔŽQÆiHj
+ *------------------------------------------
+ */
+void clif_parse_GMReqNoChatCount(int fd, struct map_session_data *sd)
+{
+ int tid;
+ RFIFOHEAD(fd);
+ tid = RFIFOL(fd,2);
+
+ WFIFOHEAD(fd,packet_len_table[0x1e0]);
+ WFIFOW(fd,0) = 0x1e0;
+ WFIFOL(fd,2) = tid;
+ sprintf((char*)WFIFOP(fd,6),"%d",tid);
+// memcpy(WFIFOP(fd,6), "TESTNAME", 24);
+ WFIFOSET(fd, packet_len_table[0x1e0]);
+
+ return;
+}
+
+void clif_parse_PMIgnore(int fd, struct map_session_data *sd) { // Rewritten by [Yor]
+ char output[512];
+ char *nick; // S 00cf <nick>.24B <type>.B: 00 (/ex nick) deny speech from nick, 01 (/in nick) allow speech from nick
+ int i, pos;
+ RFIFOHEAD(fd);
+
+ memset(output, '\0', sizeof(output));
+
+ nick = (char*)RFIFOP(fd,2); // speed up
+ RFIFOB(fd,NAME_LENGTH+1) = '\0'; // to be sure that the player name have at maximum 23 characters (nick range: [2]->[26])
+ //printf("Ignore: char '%s' state: %d\n", nick, RFIFOB(fd,26));
+
+ WFIFOHEAD(fd,packet_len_table[0xd1]);
+ WFIFOW(fd,0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = RFIFOB(fd,26);
+ // do nothing only if nick can not exist
+ if (strlen(nick) < 4) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ if (RFIFOB(fd,26) == 0) // type
+ clif_wis_message(fd, wisp_server_name, "It's impossible to block this player.", strlen("It's impossible to block this player.") + 1);
+ else
+ clif_wis_message(fd, wisp_server_name, "It's impossible to unblock this player.", strlen("It's impossible to unblock this player.") + 1);
+ return;
+ // name can exist
+ } else {
+ // deny action (we add nick only if it's not already exist
+ if (RFIFOB(fd,26) == 0) { // type
+ pos = -1;
+ for(i = 0; i < MAX_IGNORE_LIST; i++) {
+ if (strcmp(sd->ignore[i].name, nick) == 0) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "This player is already blocked.", strlen("This player is already blocked.") + 1);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried AGAIN to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ }
+ return;
+ } else if (pos == -1 && sd->ignore[i].name[0] == '\0')
+ pos = i;
+ }
+ // if a position is found and name not found, we add it in the list
+ if (pos != -1) {
+ memcpy(sd->ignore[pos].name, nick, NAME_LENGTH-1);
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ // send something to be inform and force bot to ignore twice... If GM receiving block + block again, it's a bot :)
+ clif_wis_message(fd, wisp_server_name, "Add me in your ignore list, doesn't block my wisps.", strlen("Add me in your ignore list, doesn't block my wisps.") + 1);
+ }
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "You can not block more people.", strlen("You can not block more people.") + 1);
+ if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users who automaticaly ignore people.
+ sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name);
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output);
+ }
+ }
+ // allow action (we remove all same nicks if they exist)
+ } else {
+ pos = -1;
+ for(i = 0; i < MAX_IGNORE_LIST; i++)
+ if (strcmp(sd->ignore[i].name, nick) == 0) {
+ memset(sd->ignore[i].name, 0, sizeof(sd->ignore[i].name));
+ if (pos == -1) {
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ pos = i; // don't break, to remove ALL same nick
+ }
+ }
+ if (pos == -1) {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d1]);
+ clif_wis_message(fd, wisp_server_name, "This player is not blocked by you.", strlen("This player is not blocked by you.") + 1);
+ }
+ }
+ }
+
+// for(i = 0; i < MAX_IGNORE_LIST; i++) // for debug only
+// if (sd->ignore[i].name[0] != '\0')
+// printf("Ignored player: '%s'\n", sd->ignore[i].name);
+
+ return;
+}
+
+void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) { // Rewritten by [Yor]
+ //printf("Ignore all: state: %d\n", RFIFOB(fd,2));
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2) == 0) {// S 00d0 <type>len.B: 00 (/exall) deny all speech, 01 (/inall) allow all speech
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = 0;
+ if (sd->ignoreAll == 0) {
+ sd->ignoreAll = 1;
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ clif_wis_message(fd, wisp_server_name, "You already block everyone.", strlen("You already block everyone.") + 1);
+ }
+ } else {
+ WFIFOHEAD(fd,packet_len_table[0xd2]);
+ WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail
+ WFIFOB(fd,2) = 1;
+ if (sd->ignoreAll == 1) {
+ sd->ignoreAll = 0;
+ WFIFOB(fd,3) = 0; // success
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ } else {
+ WFIFOB(fd,3) = 1; // fail
+ WFIFOSET(fd, packet_len_table[0x0d2]);
+ clif_wis_message(fd, wisp_server_name, "You already allow everyone.", strlen("You already allow everyone.") + 1);
+ }
+ }
+
+ return;
+}
+
+/*==========================================
+ * Wis‹‘”ÛƒŠƒXƒg
+ *------------------------------------------
+ */
+ int pstrcmp(const void *a, const void *b)
+{
+ return strcmp((char *)a, (char *)b);
+}
+void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd)
+{
+ int i,j=0,count=0;
+
+ qsort (sd->ignore[0].name, MAX_IGNORE_LIST, sizeof(sd->ignore[0].name), pstrcmp);
+ for(i = 0; i < MAX_IGNORE_LIST; i++){ //’†g‚ª‚ ‚é‚̂𔂦‚é
+ if(sd->ignore[i].name[0] != 0)
+ count++;
+ }
+ WFIFOHEAD(fd, 4 + (NAME_LENGTH * count));
+ WFIFOW(fd,0) = 0xd4;
+ WFIFOW(fd,2) = 4 + (NAME_LENGTH * count);
+ for(i = 0; i < MAX_IGNORE_LIST; i++){
+ if(sd->ignore[i].name[0] != 0){
+ memcpy(WFIFOP(fd, 4 + j * 24),sd->ignore[i].name, NAME_LENGTH);
+ j++;
+ }
+ }
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if(count >= MAX_IGNORE_LIST) //–žƒ^ƒ“‚È‚çÅŒã‚Ì1ŒÂ‚ðÁ‚·
+ sd->ignore[MAX_IGNORE_LIST - 1].name[0] = 0;
+
+ return;
+}
+
+/*==========================================
+ * ƒXƒpƒmƒr‚Ì/doridori‚É‚æ‚éSPR2”{
+ *------------------------------------------
+ */
+void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) {
+ int level;
+ sd->doridori_counter = 1;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON
+ && sd->state.rest && (level = pc_checkskill(sd,TK_SPTIME)))
+ status_change_start(&sd->bl,SkillStatusChangeTable[TK_SPTIME],level,0,0,0,skill_get_time(TK_SPTIME, level),0);
+ return;
+}
+/*==========================================
+ * ƒXƒpƒmƒr‚Ì”š—ô”g“®
+ *------------------------------------------
+ */
+void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd)
+{
+ if(sd){
+ int nextbaseexp=pc_nextbaseexp(sd);
+ if (battle_config.etc_log){
+ if(nextbaseexp != 0)
+ ShowInfo("SuperNovice explosionspirits!! %d %d %d %d\n",sd->bl.id,sd->status.class_,sd->status.base_exp,(int)((double)1000*sd->status.base_exp/nextbaseexp));
+ else
+ ShowInfo("SuperNovice explosionspirits!! %d %d %d 000\n",sd->bl.id,sd->status.class_,sd->status.base_exp);
+ }
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_exp > 0 && nextbaseexp > 0 && (int)((double)1000*sd->status.base_exp/nextbaseexp)%100==0){
+ clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,5,1);
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_EXPLOSIONSPIRITS],5,0,0,0,skill_get_time(MO_EXPLOSIONSPIRITS,5),0 );
+ }
+ }
+ return;
+}
+
+// random notes:
+// 0x214: monster/player info ?
+
+/*==========================================
+ * Friends List
+ *------------------------------------------
+ */
+void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online)
+{ //Toggles a single friend online/offline [Skotlex]
+ int i;
+
+ //Seek friend.
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0)
+ return; //Not found
+
+ WFIFOHEAD(sd->fd,packet_len_table[0x206]);
+ WFIFOW(sd->fd, 0) = 0x206;
+ WFIFOL(sd->fd, 2) = sd->status.friends[i].account_id;
+ WFIFOL(sd->fd, 6) = sd->status.friends[i].char_id;
+ WFIFOB(sd->fd,10) = !online; //Yeah, a 1 here means "logged off", go figure...
+
+ WFIFOSET(sd->fd, packet_len_table[0x206]);
+}
+
+//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex]
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap)
+{
+ int account_id, char_id, online;
+ account_id = va_arg(ap, int);
+ char_id = va_arg(ap, int);
+ online = va_arg(ap, int);
+ clif_friendslist_toggle(sd, account_id, char_id, online);
+ return 0;
+}
+
+//For sending the whole friends list.
+void clif_friendslist_send(struct map_session_data *sd) {
+ int i = 0, n;
+
+ // Send friends list
+ WFIFOHEAD(sd->fd, MAX_FRIENDS * 32 + 4);
+ WFIFOW(sd->fd, 0) = 0x201;
+ for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++)
+ {
+ WFIFOL(sd->fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id;
+ WFIFOL(sd->fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id;
+ memcpy(WFIFOP(sd->fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
+ }
+
+ WFIFOW(sd->fd,2) = 4 + 32 * i;
+ WFIFOSET(sd->fd, WFIFOW(sd->fd,2));
+
+ for (n = 0; n < i; n++)
+ { //Sending the online players
+ if (map_charid2sd(sd->status.friends[n].char_id))
+ clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1);
+ }
+}
+
+
+// Status for adding friend - 0: successfull 1: not exist/rejected 2: over limit
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x209]);
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = type;
+ if (f_sd)
+ {
+ WFIFOW(fd,4) = f_sd->status.account_id;
+ WFIFOW(fd,8) = f_sd->status.char_id;
+ memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH);
+ }
+ WFIFOSET(fd, packet_len_table[0x209]);
+}
+
+void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd) {
+ struct map_session_data *f_sd;
+ int i, f_fd;
+ RFIFOHEAD(fd);
+
+ f_sd = map_nick2sd((char*)RFIFOP(fd,2));
+
+ // Friend doesn't exist (no player with this name)
+ if (f_sd == NULL) {
+ clif_displaymessage(fd, msg_txt(3));
+ return;
+ }
+
+ // Friend already exists
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id) {
+ clif_displaymessage(fd, "Friend already exists.");
+ return;
+ }
+ }
+
+ if (i == MAX_FRIENDS) {
+ //No space, list full.
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ f_fd = f_sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x207]);
+ WFIFOW(f_fd,0) = 0x207;
+ WFIFOL(f_fd,2) = sd->status.account_id;
+ WFIFOL(f_fd,6) = sd->status.char_id;
+ memcpy(WFIFOP(f_fd,10), sd->status.name, NAME_LENGTH);
+ WFIFOSET(f_fd, packet_len_table[0x207]);
+
+ return;
+}
+
+void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) {
+ //<W: id> <L: Player 1 chara ID> <L: Player 1 AID> <B: Response>
+ struct map_session_data *f_sd;
+ int char_id, account_id;
+ char reply;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+ reply = RFIFOB(fd,10);
+ //printf ("reply: %d %d %d\n", char_id, id, reply);
+
+ f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players.
+ if (f_sd == NULL)
+ return;
+
+ if (reply == 0)
+ clif_friendslist_reqack(f_sd, sd, 1);
+ else {
+ int i;
+ // Find an empty slot
+ for (i = 0; i < MAX_FRIENDS; i++)
+ if (f_sd->status.friends[i].char_id == 0)
+ break;
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(f_sd, sd, 2);
+ return;
+ }
+
+ f_sd->status.friends[i].account_id = sd->status.account_id;
+ f_sd->status.friends[i].char_id = sd->status.char_id;
+ memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(f_sd, sd, 0);
+
+// clif_friendslist_send(sd); //This is not needed anymore.
+ }
+
+ return;
+}
+
+void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd) {
+ // 0x203 </o> <ID to be removed W 4B>
+ int account_id, char_id;
+ int i, j;
+ RFIFOHEAD(fd);
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+
+ // Search friend
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if (i == MAX_FRIENDS) {
+ clif_displaymessage(fd, "Name not found in list.");
+ return;
+ }
+
+ // move all chars down
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0]));
+
+ memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1]));
+ clif_displaymessage(fd, "Friend removed");
+
+ WFIFOHEAD(fd,packet_len_table[0x20a]);
+ WFIFOW(fd,0) = 0x20a;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ WFIFOSET(fd, packet_len_table[0x20a]);
+// clif_friendslist_send(sd); //This is not needed anymore.
+
+ return;
+}
+
+/*==========================================
+ * /killall
+ *------------------------------------------
+ */
+void clif_parse_GMKillAll(int fd,struct map_session_data *sd)
+{
+ char message[50];
+
+ strncpy(message,sd->status.name, NAME_LENGTH);
+ is_atcommand(fd, sd, strcat(message," : @kickall"),0);
+
+ return;
+}
+
+/*==========================================
+ * /pvpinfo
+ *------------------------------------------
+ */
+void clif_parse_PVPInfo(int fd,struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len_table[0x210]);
+ WFIFOW(fd,0) = 0x210;
+ //WFIFOL(fd,2) = 0; // not sure what for yet
+ //WFIFOL(fd,6) = 0;
+ WFIFOL(fd,10) = sd->pvp_won; // times won
+ WFIFOL(fd,14) = sd->pvp_lost; // times lost
+ WFIFOL(fd,18) = sd->pvp_point;
+ WFIFOSET(fd, packet_len_table[0x210]);
+
+ return;
+}
+
+/*==========================================
+ * /blacksmith
+ *------------------------------------------
+ */
+void clif_parse_Blacksmith(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x219]);
+ WFIFOW(fd,0) = 0x219;
+ for (i = 0; i < 10; i++) {
+ if (smith_fame_list[i].id > 0) {
+ if (strcmp(smith_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(smith_fame_list[i].id)) != NULL)
+ {
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5);
+ WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x219]);
+}
+
+int clif_fame_blacksmith(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x21b]);
+ WFIFOW(fd,0) = 0x21b;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x21b]);
+
+ return 0;
+}
+
+/*==========================================
+ * /alchemist
+ *------------------------------------------
+ */
+void clif_parse_Alchemist(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x21a]);
+ WFIFOW(fd,0) = 0x21a;
+ for (i = 0; i < 10; i++) {
+ if (chemist_fame_list[i].id > 0) {
+ if (strcmp(chemist_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(chemist_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x21a]);
+}
+
+int clif_fame_alchemist(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x21c]);
+ WFIFOW(fd,0) = 0x21c;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x21c]);
+
+ return 0;
+}
+
+/*==========================================
+ * /taekwon
+ *------------------------------------------
+ */
+void clif_parse_Taekwon(int fd,struct map_session_data *sd)
+{
+ int i;
+ char *name;
+
+ WFIFOHEAD(fd,packet_len_table[0x226]);
+ WFIFOW(fd,0) = 0x226;
+ for (i = 0; i < 10; i++) {
+ if (taekwon_fame_list[i].id > 0) {
+ if (strcmp(taekwon_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame;
+ }
+ WFIFOSET(fd, packet_len_table[0x226]);
+}
+
+int clif_fame_taekwon(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len_table[0x224]);
+ WFIFOW(fd,0) = 0x224;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len_table[0x224]);
+
+ return 0;
+}
+
+/*==========================================
+ * PK Ranking table?
+ *------------------------------------------
+ */
+void clif_parse_RankingPk(int fd,struct map_session_data *sd)
+{
+ int i;
+
+ WFIFOHEAD(fd,packet_len_table[0x238]);
+ WFIFOW(fd,0) = 0x238;
+ for(i=0;i<10;i++){
+ memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH);
+ WFIFOL(fd,i*4+242) = 0;
+ }
+ WFIFOSET(fd, packet_len_table[0x238]);
+ return;
+}
+
+/*==========================================
+ * SG Feel save OK [Komurka]
+ *------------------------------------------
+ */
+void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
+{
+ if (sd->feel_level!=-1)
+ {
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ sd->feel_map[sd->feel_level].index = map[sd->bl.m].index;
+ sd->feel_map[sd->feel_level].m = sd->bl.m;
+ pc_setglobalreg(sd,feel_var[sd->feel_level],map[sd->bl.m].index);
+
+ WFIFOHEAD(fd,packet_len_table[0x20e]);
+ WFIFOW(fd,0)=0x20e;
+ memcpy(WFIFOP(fd,2),map[sd->bl.m].name, MAP_NAME_LENGTH);
+ WFIFOL(fd,26)=sd->bl.id;
+ WFIFOW(fd,30)=sd->feel_level;
+ WFIFOSET(fd, packet_len_table[0x20e]);
+ sd->feel_level = -1;
+ if (pc_checkskill(sd,SG_KNOWLEDGE)) status_calc_pc(sd,0);
+ }
+}
+
+/*==========================================
+ * Question about Star Glaldiator save map [Komurka]
+ *------------------------------------------
+ */
+void clif_parse_ReqFeel(int fd, struct map_session_data *sd) {
+ WFIFOHEAD(fd,packet_len_table[0x253]);
+ WFIFOW(fd,0)=0x253;
+ WFIFOSET(fd, packet_len_table[0x253]);
+}
+
+/*==========================================
+ * ƒpƒPƒbƒgƒfƒoƒbƒO
+ *------------------------------------------
+ */
+void clif_parse_debug(int fd,struct map_session_data *sd)
+{
+ int i, cmd, len;
+ RFIFOHEAD(fd);
+
+ cmd = RFIFOW(fd,0);
+ len = sd?packet_db[sd->packet_ver][cmd].len:RFIFOREST(fd); //With no session, just read the remaining in the buffer.
+ ShowDebug("packet debug 0x%4X\n",cmd);
+ printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i=0;i<len;i++){
+ if((i&15)==0)
+ printf("\n%04X ",i);
+ printf("%02X ",RFIFOB(fd,i));
+ }
+ printf("\n");
+}
+
+/*==========================================
+ * ƒNƒ‰ƒCƒAƒ“ƒg‚©‚ç‚̃pƒPƒbƒg‰ðÍ
+ * socket.c‚Ìdo_parsepacket‚©‚çŒÄ‚Ño‚³‚ê‚é
+ *------------------------------------------
+ */
+int clif_parse(int fd) {
+ int packet_len = 0, cmd, packet_ver, dump = 0;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ if (fd <= 0)
+ { //Just in case, there are some checks for this later down below anyway which should be removed. [Skotlex]
+ ShowError("clif_parse: Received invalid session %d\n", fd);
+ return 0;
+ }
+
+ sd = (struct map_session_data*)session[fd]->session_data;
+
+ // Ú‘±‚ªØ‚ê‚Ä‚é‚Ì‚ÅŒãŽn––
+ if (!chrif_isconnect() && kick_on_disconnect)
+ {
+ ShowInfo("Closing session #%d (Not connected to Char server)\n", fd);
+ if (sd && sd->state.auth)
+ clif_quitsave(fd, sd); // the function doesn't send to inter-server/char-server if it is not connected [Yor]
+ do_close(fd);
+ return 0;
+ } else
+ if (session[fd]->eof) {
+ if (sd && sd->state.autotrade) {
+ //Disassociate character from the socket connection.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", (pc_isGM(sd))?"GM ":"",sd->status.name); // Player logout display [Valaris]
+ } else if (sd && sd->state.auth) {
+ clif_quitsave(fd, sd); // the function doesn't send to inter-server/char-server if it is not connected [Yor]
+ if (sd->status.name != NULL)
+ ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"",sd->status.name); // Player logout display [Valaris]
+ else
+ ShowInfo("%sCharacter with Account ID '"CL_WHITE"%d"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"", sd->bl.id); // Player logout display [Yor]
+ } else {
+ unsigned char *ip = (unsigned char *) &session[fd]->client_addr.sin_addr;
+ ShowInfo("Player not identified with IP '"CL_WHITE"%d.%d.%d.%d"CL_RESET"' logged off.\n", ip[0],ip[1],ip[2],ip[3]);
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ if (RFIFOREST(fd) < 2)
+ return 0;
+
+// printf("clif_parse: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd));
+
+ cmd = RFIFOW(fd,0);
+
+ // ŠÇ——pƒpƒPƒbƒgˆ—
+ if (cmd >= 30000) {
+ switch(cmd) {
+ case 0x7530: { // Athenaî•ñŠ“¾
+ WFIFOHEAD(fd, 10);
+ WFIFOW(fd,0) = 0x7531;
+ WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
+ WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
+ WFIFOB(fd,4) = ATHENA_REVISION;
+ WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
+ WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
+ WFIFOB(fd,7) = ATHENA_SERVER_MAP;
+ WFIFOW(fd,8) = ATHENA_MOD_VERSION;
+ WFIFOSET(fd,10);
+ RFIFOSKIP(fd,2);
+ break;
+ }
+ case 0x7532: // Ú‘±‚ÌØ’f
+ ShowWarning("clif_parse: session #%d disconnected for sending packet 0x04%x\n", fd, cmd);
+ session[fd]->eof=1;
+ break;
+ }
+ ShowWarning("Ignoring incoming packet (command: 0x%04x, session: %d)\n", cmd, fd);
+ return 0;
+ }
+
+ // get packet version before to parse
+ packet_ver = 0;
+ if (sd) {
+ packet_ver = sd->packet_ver;
+ if (packet_ver < 0 || packet_ver > MAX_PACKET_VER) { // This should never happen unless we have some corrupted memory issues :X [Skotlex]
+ session[fd]->eof = 1;
+ return 0;
+ }
+ } else {
+ // check authentification packet to know packet version
+ packet_ver = clif_guess_PacketVer(fd, 0);
+ // check if version is accepted
+ if (packet_ver < 5 || // reject really old client versions
+ (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04
+ (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0) ||
+ packet_ver > MAX_PACKET_VER) // no packet version support yet
+ {
+ ShowInfo("clif_parse: Disconnecting session #%d for not having latest client version (has version %d).\n", fd, packet_ver);
+ WFIFOHEAD(fd, 23);
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 5; // 05 = Game's EXE is not the latest version
+ WFIFOSET(fd,23);
+ packet_len = RFIFOREST(fd);
+ RFIFOSKIP(fd, packet_len);
+ clif_setwaitclose(fd);
+ if (session[fd]->func_send) //socket.c doesn't wants to send the data when left on it's own... [Skotlex]
+ session[fd]->func_send(fd);
+ return 0;
+ }
+ }
+
+ // ƒQ[ƒ€—pˆÈŠOƒpƒPƒbƒg‚©A”FØ‚ðI‚¦‚é‘O‚É0072ˆÈŠO‚ª—ˆ‚½‚çAØ’f‚·‚é
+ if (cmd >= MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) { // if packet is not inside these values: session is incorrect?? or auth packet is unknown
+ ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd);
+ session[fd]->eof = 1;
+ return 0;
+ }
+
+ // ƒpƒPƒbƒg’·‚ðŒvŽZ
+ packet_len = packet_db[packet_ver][cmd].len;
+ if (packet_len == -1) {
+ if (RFIFOREST(fd) < 4)
+ return 0; // ‰Â•Ï’·ƒpƒPƒbƒg‚Å’·‚³‚ÌŠ‚܂Ńf[ƒ^‚ª—ˆ‚Ä‚È‚¢
+
+ packet_len = RFIFOW(fd,2);
+ if (packet_len < 4 || packet_len > 32768) {
+ ShowWarning("clif_parse: Packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd);
+ session[fd]->eof =1;
+ return 0;
+ }
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0; // ‚Ü‚¾1ƒpƒPƒbƒg•ªƒf[ƒ^‚ª‘µ‚Á‚Ä‚È‚¢
+
+ #if DUMP_ALL_PACKETS
+ dump = 1;
+ #endif
+
+ if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) { // Ø’f‘Ò‚¿‚ÌꇃpƒPƒbƒg‚ðˆ—‚µ‚È‚¢
+
+ } else if (packet_db[packet_ver][cmd].func) {
+ if (sd
+ || packet_db[packet_ver][cmd].func == clif_parse_WantToConnection
+ || packet_db[packet_ver][cmd].func == clif_parse_debug
+ ) //Only execute the function when there's an sd (except for debug/wanttoconnect packets)
+ packet_db[packet_ver][cmd].func(fd, sd);
+ } else {
+ // •s–¾‚ȃpƒPƒbƒg
+ if (battle_config.error_log) {
+#if DUMP_UNKNOWN_PACKET
+ {
+ int i;
+ FILE *fp;
+ char packet_txt[256] = "save/packet.txt";
+ time_t now;
+ dump = 1;
+
+ if ((fp = fopen(packet_txt, "a")) == NULL) {
+ ShowError("clif.c: cant write [%s] !!! data is lost !!!\n", packet_txt);
+ return 1;
+ } else {
+ time(&now);
+ if (sd && sd->state.auth) {
+ if (sd->status.name != NULL)
+ fprintf(fp, "%sPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n",
+ asctime(localtime(&now)), sd->status.account_id, sd->status.char_id, sd->status.name);
+ else
+ fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+ } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
+ fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id);
+
+ fprintf(fp, "\t---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i = 0; i < packet_len; i++) {
+ if ((i & 15) == 0)
+ fprintf(fp, "\n\t%04X ", i);
+ fprintf(fp, "%02X ", RFIFOB(fd,i));
+ }
+ fprintf(fp, "\n\n");
+ fclose(fp);
+ }
+ }
+#endif
+ }
+ }
+
+ if (dump) {
+ int i;
+ if (fd)
+ ShowDebug("\nclif_parse: session #%d, packet 0x%04x, length %d, version %d\n", fd, cmd, packet_len, packet_ver);
+ printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i = 0; i < packet_len; i++) {
+ if ((i & 15) == 0)
+ printf("\n%04X ",i);
+ printf("%02X ", RFIFOB(fd,i));
+ }
+ printf("\n");
+ if (sd && sd->state.auth) {
+ if (sd->status.name != NULL)
+ printf("\nAccount ID %d, character ID %d, player name %s.\n",
+ sd->status.account_id, sd->status.char_id, sd->status.name);
+ else
+ printf("\nAccount ID %d.\n", sd->bl.id);
+ } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
+ printf("\nAccount ID %d.\n", sd->bl.id);
+ }
+
+ RFIFOSKIP(fd, packet_len);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒpƒPƒbƒgƒf[ƒ^ƒx[ƒX“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static int packetdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int cmd,i,j,k,packet_ver;
+ char *str[64],*p,*str2[64],*p2,w1[64],w2[64];
+
+ struct {
+ void (*func)(int, struct map_session_data *);
+ char *name;
+ } clif_parse_func[]={
+ {clif_parse_WantToConnection,"wanttoconnection"},
+ {clif_parse_LoadEndAck,"loadendack"},
+ {clif_parse_TickSend,"ticksend"},
+ {clif_parse_WalkToXY,"walktoxy"},
+ {clif_parse_QuitGame,"quitgame"},
+ {clif_parse_GetCharNameRequest,"getcharnamerequest"},
+ {clif_parse_GlobalMessage,"globalmessage"},
+ {clif_parse_MapMove,"mapmove"},
+ {clif_parse_ChangeDir,"changedir"},
+ {clif_parse_Emotion,"emotion"},
+ {clif_parse_HowManyConnections,"howmanyconnections"},
+ {clif_parse_ActionRequest,"actionrequest"},
+ {clif_parse_Restart,"restart"},
+ {clif_parse_Wis,"wis"},
+ {clif_parse_GMmessage,"gmmessage"},
+ {clif_parse_TakeItem,"takeitem"},
+ {clif_parse_DropItem,"dropitem"},
+ {clif_parse_UseItem,"useitem"},
+ {clif_parse_EquipItem,"equipitem"},
+ {clif_parse_UnequipItem,"unequipitem"},
+ {clif_parse_NpcClicked,"npcclicked"},
+ {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
+ {clif_parse_NpcBuyListSend,"npcbuylistsend"},
+ {clif_parse_NpcSellListSend,"npcselllistsend"},
+ {clif_parse_CreateChatRoom,"createchatroom"},
+ {clif_parse_ChatAddMember,"chataddmember"},
+ {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
+ {clif_parse_ChangeChatOwner,"changechatowner"},
+ {clif_parse_KickFromChat,"kickfromchat"},
+ {clif_parse_ChatLeave,"chatleave"},
+ {clif_parse_TradeRequest,"traderequest"},
+ {clif_parse_TradeAck,"tradeack"},
+ {clif_parse_TradeAddItem,"tradeadditem"},
+ {clif_parse_TradeOk,"tradeok"},
+ {clif_parse_TradeCancel,"tradecancel"},
+ {clif_parse_TradeCommit,"tradecommit"},
+ {clif_parse_StopAttack,"stopattack"},
+ {clif_parse_PutItemToCart,"putitemtocart"},
+ {clif_parse_GetItemFromCart,"getitemfromcart"},
+ {clif_parse_RemoveOption,"removeoption"},
+ {clif_parse_ChangeCart,"changecart"},
+ {clif_parse_StatusUp,"statusup"},
+ {clif_parse_SkillUp,"skillup"},
+ {clif_parse_UseSkillToId,"useskilltoid"},
+ {clif_parse_UseSkillToPos,"useskilltopos"},
+ {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"},
+ {clif_parse_UseSkillMap,"useskillmap"},
+ {clif_parse_RequestMemo,"requestmemo"},
+ {clif_parse_ProduceMix,"producemix"},
+ {clif_parse_NpcSelectMenu,"npcselectmenu"},
+ {clif_parse_NpcNextClicked,"npcnextclicked"},
+ {clif_parse_NpcAmountInput,"npcamountinput"},
+ {clif_parse_NpcStringInput,"npcstringinput"},
+ {clif_parse_NpcCloseClicked,"npccloseclicked"},
+ {clif_parse_ItemIdentify,"itemidentify"},
+ {clif_parse_SelectArrow,"selectarrow"},
+ {clif_parse_AutoSpell,"autospell"},
+ {clif_parse_UseCard,"usecard"},
+ {clif_parse_InsertCard,"insertcard"},
+ {clif_parse_RepairItem,"repairitem"},
+ {clif_parse_WeaponRefine,"weaponrefine"},
+ {clif_parse_SolveCharName,"solvecharname"},
+ {clif_parse_ResetChar,"resetchar"},
+ {clif_parse_LGMmessage,"lgmmessage"},
+ {clif_parse_MoveToKafra,"movetokafra"},
+ {clif_parse_MoveFromKafra,"movefromkafra"},
+ {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
+ {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
+ {clif_parse_CloseKafra,"closekafra"},
+ {clif_parse_CreateParty,"createparty"},
+ {clif_parse_CreateParty2,"createparty2"},
+ {clif_parse_PartyInvite,"partyinvite"},
+ {clif_parse_ReplyPartyInvite,"replypartyinvite"},
+ {clif_parse_LeaveParty,"leaveparty"},
+ {clif_parse_RemovePartyMember,"removepartymember"},
+ {clif_parse_PartyChangeOption,"partychangeoption"},
+ {clif_parse_PartyMessage,"partymessage"},
+ {clif_parse_CloseVending,"closevending"},
+ {clif_parse_VendingListReq,"vendinglistreq"},
+ {clif_parse_PurchaseReq,"purchasereq"},
+ {clif_parse_OpenVending,"openvending"},
+ {clif_parse_CreateGuild,"createguild"},
+ {clif_parse_GuildCheckMaster,"guildcheckmaster"},
+ {clif_parse_GuildRequestInfo,"guildrequestinfo"},
+ {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
+ {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
+ {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
+ {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
+ {clif_parse_GuildChangeNotice,"guildchangenotice"},
+ {clif_parse_GuildInvite,"guildinvite"},
+ {clif_parse_GuildReplyInvite,"guildreplyinvite"},
+ {clif_parse_GuildLeave,"guildleave"},
+ {clif_parse_GuildExplusion,"guildexplusion"},
+ {clif_parse_GuildMessage,"guildmessage"},
+ {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
+ {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
+ {clif_parse_GuildDelAlliance,"guilddelalliance"},
+ {clif_parse_GuildOpposition,"guildopposition"},
+ {clif_parse_GuildBreak,"guildbreak"},
+ {clif_parse_PetMenu,"petmenu"},
+ {clif_parse_CatchPet,"catchpet"},
+ {clif_parse_SelectEgg,"selectegg"},
+ {clif_parse_SendEmotion,"sendemotion"},
+ {clif_parse_ChangePetName,"changepetname"},
+ {clif_parse_GMKick,"gmkick"},
+ {clif_parse_GMHide,"gmhide"},
+ {clif_parse_GMReqNoChat,"gmreqnochat"},
+ {clif_parse_GMReqNoChatCount,"gmreqnochatcount"},
+ {clif_parse_NoviceDoriDori,"sndoridori"},
+ {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"},
+ {clif_parse_PMIgnore,"wisexin"},
+ {clif_parse_PMIgnoreList,"wisexlist"},
+ {clif_parse_PMIgnoreAll,"wisall"},
+ {clif_parse_FriendsListAdd,"friendslistadd"},
+ {clif_parse_FriendsListRemove,"friendslistremove"},
+ {clif_parse_FriendsListReply,"friendslistreply"},
+ {clif_parse_GMKillAll,"killall"},
+ {clif_parse_Recall,"summon"},
+ {clif_parse_GM_Monster_Item,"itemmonster"},
+ {clif_parse_Shift,"shift"},
+ {clif_parse_Blacksmith,"blacksmith"},
+ {clif_parse_Alchemist,"alchemist"},
+ {clif_parse_Taekwon,"taekwon"},
+ {clif_parse_RankingPk,"rankingpk"},
+ {clif_parse_FeelSaveOk,"feelsaveok"},
+ {clif_parse_debug,"debug"},
+
+ {NULL,NULL}
+ };
+
+ sprintf(line, "%s/packet_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowFatalError("can't read %s\n", line);
+ exit(1);
+ }
+
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ packet_ver = MAX_PACKET_VER; // read into packet_db's version by default
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if (sscanf(line,"%[^:]: %[^\r\n]",w1,w2) == 2) {
+ if(strcmpi(w1,"packet_ver")==0) {
+ int prev_ver = packet_ver;
+ packet_ver = atoi(w2);
+ if (packet_ver > MAX_PACKET_VER)
+ { //Check to avoid overflowing. [Skotlex]
+ ShowWarning("The packet_db table only has support up to version %d\n", MAX_PACKET_VER);
+ break;
+ }
+ // copy from previous version into new version and continue
+ // - indicating all following packets should be read into the newer version
+ memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
+ continue;
+ } else if(strcmpi(w1,"packet_db_ver")==0) {
+ //This is the preferred version.
+ if(strcmpi(w2,"default")==0)
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ else {
+ // to manually set the packet DB version
+ clif_config.packet_db_ver = atoi(w2);
+ // check for invalid version
+ if (clif_config.packet_db_ver > MAX_PACKET_VER ||
+ clif_config.packet_db_ver < 0)
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ }
+ continue;
+ }
+ }
+
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<4 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+ cmd=strtol(str[0],(char **)NULL,0);
+ if(cmd<=0 || cmd>=MAX_PACKET_DB)
+ continue;
+ if(str[1]==NULL){
+ ShowError("packet_db: packet len error\n");
+ continue;
+ }
+ k = atoi(str[1]);
+ packet_db[packet_ver][cmd].len = k;
+
+ if(str[2]==NULL){
+ packet_db[packet_ver][cmd].func = NULL;
+ ln++;
+ continue;
+ }
+ for(j=0;j<sizeof(clif_parse_func)/sizeof(clif_parse_func[0]);j++){
+ if(clif_parse_func[j].name != NULL &&
+ strcmp(str[2],clif_parse_func[j].name)==0)
+ {
+ if (packet_db[packet_ver][cmd].func != clif_parse_func[j].func)
+ { //If we are updating a function, we need to zero up the previous one. [Skotlex]
+ for(i=0;i<MAX_PACKET_DB;i++){
+ if (packet_db[packet_ver][i].func == clif_parse_func[j].func)
+ {
+ memset(&packet_db[packet_ver][i], 0, sizeof(struct packet_db));
+ break;
+ }
+ }
+ }
+ packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
+ break;
+ }
+ }
+ // set the identifying cmd for the packet_db version
+ if (strcmp(str[2],"wanttoconnection")==0)
+ clif_config.connect_cmd[packet_ver] = cmd;
+
+ if(str[3]==NULL){
+ ShowError("packet_db: packet error\n");
+ exit(1);
+ }
+ for(j=0,p2=str[3];p2;j++){
+ str2[j]=p2;
+ p2=strchr(p2,':');
+ if(p2) *p2++=0;
+ k = atoi(str2[j]);
+ // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now
+ packet_db[packet_ver][cmd].pos[j] = k;
+ }
+
+ ln++;
+// if(packet_db[clif_config.packet_db_ver][cmd].len > 2 /* && packet_db[cmd].pos[0] == 0 */)
+// printf("packet_db ver %d: %d 0x%x %d %s %p\n",packet_ver,ln,cmd,packet_db[packet_ver][cmd].len,str[2],packet_db[packet_ver][cmd].func);
+ }
+ if (!clif_config.connect_cmd[clif_config.packet_db_ver])
+ { //Locate the nearest version that we still support. [Skotlex]
+ for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--);
+
+ clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
+ }
+ ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_clif(void) {
+#ifndef __WIN32
+ int i;
+#endif
+
+ clif_config.packet_db_ver = -1; // the main packet version of the DB
+ memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex]
+
+ memset(packet_db,0,sizeof(packet_db));
+ //Using the packet_db file is the only way to set up packets now [Skotlex]
+ packetdb_readdb();
+
+ set_defaultparse(clif_parse);
+#ifdef __WIN32
+ //if (!make_listen_port(map_port)) {
+ if (!make_listen_bind(bind_ip,map_port)) {
+ ShowFatalError("cant bind game port\n");
+ exit(1);
+ }
+#else
+ for(i = 0; i < 10; i++) {
+ //if (make_listen_port(map_port))
+ if (make_listen_bind(bind_ip,map_port))
+ break;
+ sleep(20);
+ }
+ if (i == 10) {
+ ShowFatalError("cant bind game port\n");
+ exit(1);
+ }
+#endif
+
+ add_timer_func_list(clif_waitclose, "clif_waitclose");
+ add_timer_func_list(clif_clearchar_delay_sub, "clif_clearchar_delay_sub");
+ add_timer_func_list(clif_delayquit, "clif_delayquit");
+
+ return 0;
+}
+
diff --git a/src/map/clif.h b/src/map/clif.h
new file mode 100644
index 000000000..64bb102a2
--- /dev/null
+++ b/src/map/clif.h
@@ -0,0 +1,343 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CLIF_H_
+#define _CLIF_H_
+
+#ifdef __WIN32
+typedef unsigned int in_addr_t;
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include "map.h"
+
+// protocol version
+#define PACKETVER 6
+
+// packet DB
+#define MAX_PACKET_DB 0x25f
+#define MAX_PACKET_VER 25
+
+struct packet_db {
+ short len;
+ void (*func)(int, struct map_session_data *);
+ short pos[20];
+};
+
+extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
+
+void clif_setip(char*);
+void clif_setbindip(char*);
+void clif_setport(int);
+
+in_addr_t clif_getip(void);
+int clif_getport(void);
+int clif_countusers(void);
+void clif_setwaitclose(int);
+
+int clif_authok(struct map_session_data *);
+int clif_authfail_fd(int,int);
+void clif_updatemaxid(int, int);
+int clif_charselectok(int);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int clif_dropflooritem(struct flooritem_data *);
+int clif_clearflooritem(struct flooritem_data *,int);
+int clif_clearchar(struct block_list*,int); // area or fd
+int clif_clearchar_delay(unsigned int,struct block_list *,int);
+#define clif_clearchar_area(bl,type) clif_clearchar(bl,type)
+int clif_clearchar_id(int,int,int);
+int clif_spawnpc(struct map_session_data*); //area
+int clif_spawnnpc(struct npc_data*); // area
+int clif_spawnmob(struct mob_data*); // area
+int clif_spawnpet(struct pet_data*); // area
+int clif_walkok(struct map_session_data*); // self
+int clif_movechar(struct map_session_data*); // area
+int clif_movemob(struct mob_data*); //area
+int clif_movepet(struct pet_data *pd); //area
+int clif_movenpc(struct npc_data *nd); // [Valaris]
+int clif_changemap(struct map_session_data*,short,int,int); //self
+int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self
+int clif_blown(struct block_list *); // area
+int clif_slide(struct block_list *,int,int); // area
+int clif_fixpos(struct block_list *); // area
+int clif_fixmobpos(struct mob_data *md);
+int clif_fixpcpos(struct map_session_data *sd);
+int clif_fixpetpos(struct pet_data *pd);
+int clif_fixnpcpos(struct npc_data *nd); // [Valaris]
+int clif_npcbuysell(struct map_session_data*,int); //self
+int clif_buylist(struct map_session_data*,struct npc_data*); //self
+int clif_selllist(struct map_session_data*); //self
+int clif_scriptmes(struct map_session_data*,int,char*); //self
+int clif_scriptnext(struct map_session_data*,int); //self
+int clif_scriptclose(struct map_session_data*,int); //self
+int clif_scriptmenu(struct map_session_data*,int,char*); //self
+int clif_scriptinput(struct map_session_data*,int); //self
+int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self
+int clif_cutin(struct map_session_data*,char*,int); //self
+int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self
+int clif_additem(struct map_session_data*,int,int,int); //self
+int clif_delitem(struct map_session_data*,int,int); //self
+int clif_updatestatus(struct map_session_data*,int); //self
+int clif_changestatus(struct block_list*,int,int); //area
+int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area
+#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0)
+int clif_changelook(struct block_list *,int,int); // area
+int clif_arrowequip(struct map_session_data *sd,int val); //self
+int clif_arrow_fail(struct map_session_data *sd,int type); //self
+int clif_arrow_create_list(struct map_session_data *sd); //self
+int clif_statusupack(struct map_session_data *,int,int,int); // self
+int clif_equipitemack(struct map_session_data *,int,int,int); // self
+int clif_unequipitemack(struct map_session_data *,int,int,int); // self
+int clif_misceffect(struct block_list*,int); // area
+int clif_misceffect2(struct block_list *bl,int type);
+int clif_changeoption(struct block_list*); // area
+int clif_useitemack(struct map_session_data*,int,int,int); // self
+void clif_GlobalMessage(struct block_list *bl,char *message);
+int clif_createchat(struct map_session_data*,int); // self
+int clif_dispchat(struct chat_data*,int); // area or fd
+int clif_joinchatfail(struct map_session_data*,int); // self
+int clif_joinchatok(struct map_session_data*,struct chat_data*); // self
+int clif_addchat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat
+int clif_clearchat(struct chat_data*,int); // area or fd
+int clif_leavechat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatstatus(struct chat_data*); // chat
+int clif_refresh(struct map_session_data*); // self
+
+int clif_fame_blacksmith(struct map_session_data *, int);
+int clif_fame_alchemist(struct map_session_data *, int);
+int clif_fame_taekwon(struct map_session_data *, int);
+
+void clif_emotion(struct block_list *bl,int type);
+void clif_talkiebox(struct block_list *bl,char* talkie);
+void clif_wedding_effect(struct block_list *bl);
+void clif_divorced(struct map_session_data *sd, char *);
+//void clif_sitting(int fd, struct map_session_data *sd);
+//void clif_callpartner(struct map_session_data *sd);
+void clif_adopt_process(struct map_session_data *sd);
+void clif_sitting(struct map_session_data *sd);
+void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type);
+int clif_soundeffectall(struct block_list *bl, char *name, int type);
+
+// trade
+int clif_traderequest(struct map_session_data *sd,char *name);
+int clif_tradestart(struct map_session_data *sd,int type);
+int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount);
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail);
+int clif_tradedeal_lock(struct map_session_data *sd,int fail);
+int clif_tradecancelled(struct map_session_data *sd);
+int clif_tradecompleted(struct map_session_data *sd,int fail);
+
+// storage
+#include "storage.h"
+int clif_storageitemlist(struct map_session_data *sd,struct storage *stor);
+int clif_storageequiplist(struct map_session_data *sd,struct storage *stor);
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor);
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount);
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount);
+int clif_storageclose(struct map_session_data *sd);
+int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount);
+
+int clif_pcinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_pcoutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_mobinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_moboutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_petoutsight(struct block_list *bl,va_list ap);
+int clif_petinsight(struct block_list *bl,va_list ap);
+int clif_npcoutsight(struct block_list *bl,va_list ap);
+int clif_npcinsight(struct block_list *bl,va_list ap);
+
+int clif_class_change(struct block_list *bl,int class_,int type);
+int clif_mob_class_change(struct mob_data *md,int class_);
+int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris]
+
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range);
+int clif_skillinfoblock(struct map_session_data *sd);
+int clif_skillup(struct map_session_data *sd,int skill_num);
+
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime);
+int clif_skillcastcancel(struct block_list* bl);
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype);
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
+ int skill_id,int heal,int fail);
+int clif_skill_poseffect(struct block_list *src,int skill_id,
+ int val,int x,int y,int tick);
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst);
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num,
+ const char *map1,const char *map2,const char *map3,const char *map4);
+int clif_skill_memo(struct map_session_data *sd,int flag);
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag);
+int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger);
+
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid);
+
+int clif_skill_setunit(struct skill_unit *unit);
+int clif_skill_delunit(struct skill_unit *unit);
+
+int clif_01ac(struct block_list *bl);
+
+int clif_autospell(struct map_session_data *sd,int skilllv);
+int clif_devotion(struct map_session_data *sd);
+int clif_marionette(struct block_list *src, struct block_list *target);
+int clif_spiritball(struct map_session_data *sd);
+int clif_combo_delay(struct block_list *src,int wait);
+int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_);
+int clif_changemapcell(int m,int x,int y,int cell_type,int type);
+
+int clif_status_load(struct block_list *bl,int type, int flag);
+int clif_status_change(struct block_list *bl,int type,int flag);
+
+int clif_wis_message(int fd,char *nick,char *mes,int mes_len);
+int clif_wis_end(int fd,int flag);
+
+int clif_solved_charname(struct map_session_data *sd,int char_id);
+int clif_charnameack(int fd, struct block_list *bl);
+int clif_charnameupdate(struct map_session_data *ssd);
+
+int clif_use_card(struct map_session_data *sd,int idx);
+int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag);
+
+int clif_itemlist(struct map_session_data *sd);
+int clif_equiplist(struct map_session_data *sd);
+
+int clif_cart_additem(struct map_session_data*,int,int,int);
+int clif_cart_delitem(struct map_session_data*,int,int);
+int clif_cart_itemlist(struct map_session_data *sd);
+int clif_cart_equiplist(struct map_session_data *sd);
+
+int clif_item_identify_list(struct map_session_data *sd);
+int clif_item_identified(struct map_session_data *sd,int idx,int flag);
+int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd);
+int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag);
+int clif_item_refine_list(struct map_session_data *sd);
+
+int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name);
+
+int clif_mvp_effect(struct map_session_data *sd);
+int clif_mvp_item(struct map_session_data *sd,int nameid);
+int clif_mvp_exp(struct map_session_data *sd,int exp);
+void clif_changed_dir(struct block_list *bl);
+
+// vending
+int clif_openvendingreq(struct map_session_data *sd,int num);
+int clif_showvendingboard(struct block_list* bl,char *message,int fd);
+int clif_closevendingboard(struct block_list* bl,int fd);
+int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending);
+int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail);
+int clif_openvending(struct map_session_data *sd,int id,struct vending *vending);
+int clif_vendingreport(struct map_session_data *sd,int index,int amount);
+
+int clif_movetoattack(struct map_session_data *sd,struct block_list *bl);
+
+// party
+int clif_party_created(struct map_session_data *sd,int flag);
+int clif_party_main_info(struct party *p, int fd);
+int clif_party_info(struct party *p,int fd);
+int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd);
+int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag);
+int clif_party_option(struct party *p,struct map_session_data *sd,int flag);
+int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag);
+int clif_party_message(struct party *p,int account_id,char *mes,int len);
+int clif_party_move(struct party *p,struct map_session_data *sd,int online);
+int clif_party_xy(struct map_session_data *sd);
+int clif_party_hp(struct map_session_data *sd);
+int clif_hpmeter(struct map_session_data *sd);
+
+// guild
+int clif_guild_created(struct map_session_data *sd,int flag);
+int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g);
+int clif_guild_basicinfo(struct map_session_data *sd);
+int clif_guild_allianceinfo(struct map_session_data *sd);
+int clif_guild_memberlist(struct map_session_data *sd);
+int clif_guild_skillinfo(struct map_session_data *sd);
+int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag);
+int clif_guild_invite(struct map_session_data *sd,struct guild *g);
+int clif_guild_inviteack(struct map_session_data *sd,int flag);
+int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes);
+int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes,
+ int account_id);
+int clif_guild_positionchanged(struct guild *g,int idx);
+int clif_guild_memberpositionchanged(struct guild *g,int idx);
+int clif_guild_emblem(struct map_session_data *sd,struct guild *g);
+int clif_guild_notice(struct map_session_data *sd,struct guild *g);
+int clif_guild_message(struct guild *g,int account_id,const char *mes,int len);
+int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv);
+int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name);
+int clif_guild_allianceack(struct map_session_data *sd,int flag);
+int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int clif_guild_oppositionack(struct map_session_data *sd,int flag);
+int clif_guild_broken(struct map_session_data *sd,int flag);
+int clif_guild_xy(struct map_session_data *sd);
+int clif_guild_xy_remove(struct map_session_data *sd);
+
+
+// atcommand
+int clif_displaymessage(const int fd,char* mes);
+int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len);
+int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag);
+void clif_MainChatMessage(char* message); //luzza
+int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag);
+int clif_heal(int fd,int type,int val);
+int clif_resurrection(struct block_list *bl,int type);
+int clif_set0199(int fd,int type);
+int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type);
+int clif_send0199(int map,int type);
+int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val);
+
+//petsystem
+int clif_catch_process(struct map_session_data *sd);
+int clif_pet_rulet(struct map_session_data *sd,int data);
+int clif_sendegg(struct map_session_data *sd);
+int clif_send_petdata(struct map_session_data *sd,int type,int param);
+int clif_send_petstatus(struct map_session_data *sd);
+int clif_pet_emotion(struct pet_data *pd,int param);
+int clif_pet_performance(struct block_list *bl,int param);
+int clif_pet_equip(struct pet_data *pd,int nameid);
+int clif_pet_food(struct map_session_data *sd,int foodid,int fail);
+
+//friends list
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap);
+void clif_friendslist_send(struct map_session_data *sd);
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type);
+
+// [Valaris]
+int clif_mob_hp(struct mob_data *md);
+int clif_weather_sub(int fd, int type); // [Valaris]
+int clif_weather(int m); // [Valaris]
+int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris]
+int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris]
+
+int clif_GM_kickack(struct map_session_data *sd,int id);
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_timedout(struct map_session_data *sd);
+
+int clif_foreachclient(int (*)(struct map_session_data*,va_list),...);
+int clif_disp_overhead(struct map_session_data *sd, char* mes);
+
+int do_final_clif(void);
+int do_init_clif(void);
+
+
+int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin]
+void clif_parse_ReqFeel(int fd, struct map_session_data *sd);
+void clif_feel_info(struct map_session_data *sd, int feel_level);
+void clif_hate_mob(struct map_session_data *sd, int skilllv,int mob_id);
+void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsigned short progress);
+#endif
+
+
diff --git a/src/map/date.c b/src/map/date.c
new file mode 100644
index 000000000..3bb7dca66
--- /dev/null
+++ b/src/map/date.c
@@ -0,0 +1,72 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "date.h"
+#include <time.h>
+
+int date_get_year(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_year+1900;
+}
+int date_get_month(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mon+1;
+}
+int date_get_day(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mday;
+}
+int date_get_hour(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_hour;
+}
+
+int date_get_min(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_min;
+}
+
+int date_get_sec(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_sec;
+}
+
+int is_day_of_sun(void)
+{
+ return date_get_day()%2 == 0;
+}
+
+int is_day_of_moon(void)
+{
+ return date_get_day()%2 == 1;
+}
+
+int is_day_of_star(void)
+{
+ return date_get_day()%5 == 0;
+}
+
diff --git a/src/map/date.h b/src/map/date.h
new file mode 100644
index 000000000..2dfbf58dd
--- /dev/null
+++ b/src/map/date.h
@@ -0,0 +1,17 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _DATE_H_
+#define _DATE_H_
+#endif
+
+int date_get_year(void);
+int date_get_month(void);
+int date_get_day(void);
+int date_get_hour(void);
+int date_get_min(void);
+int date_get_sec(void);
+
+int is_day_of_sun(void);
+int is_day_of_moon(void);
+int is_day_of_star(void);
diff --git a/src/map/guild.c b/src/map/guild.c
new file mode 100644
index 000000000..9a12b0425
--- /dev/null
+++ b/src/map/guild.c
@@ -0,0 +1,1976 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "map.h"
+#include "guild.h"
+#include "storage.h"
+#include "timer.h"
+#include "socket.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "battle.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "intif.h"
+#include "clif.h"
+#include "skill.h"
+#include "showmsg.h"
+#include "log.h"
+
+static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex]
+static struct dbt *guild_db;
+static struct dbt *castle_db;
+static struct dbt *guild_expcache_db;
+static struct dbt *guild_infoevent_db;
+static struct dbt *guild_castleinfoevent_db;
+
+struct eventlist {
+ char name[50];
+ struct eventlist *next;
+};
+
+// ƒMƒ‹ƒh‚ÌEXPƒLƒƒƒbƒVƒ…‚̃tƒ‰ƒbƒVƒ…‚ÉŠÖ˜A‚·‚é’è”
+#define GUILD_SEND_XY_INVERVAL 5000 // À•W‚â‚g‚o‘—M‚ÌŠÔŠu
+#define GUILD_PAYEXP_INVERVAL 10000 // ŠÔŠu(ƒLƒƒƒbƒVƒ…‚Ìő嶑¶ŽžŠÔAƒ~ƒŠ•b)
+#define GUILD_PAYEXP_LIST 8192 // ƒLƒƒƒbƒVƒ…‚ÌÅ‘å”
+
+// ƒMƒ‹ƒh‚ÌEXPƒLƒƒƒbƒVƒ…
+struct guild_expcache {
+ int guild_id, account_id, char_id, exp;
+};
+
+struct{
+ int id;
+ int max;
+ struct{
+ short id;
+ short lv;
+ }need[6];
+} guild_skill_tree[MAX_GUILDSKILL];
+
+// timer for auto saving guild data during WoE
+#define GUILD_SAVE_INTERVAL 300000
+int guild_save_timer = -1;
+
+int guild_payexp_timer(int tid,unsigned int tick,int id,int data);
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data);
+int guild_save_sub(int tid,unsigned int tick,int id,int data);
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data);
+
+// ƒMƒ‹ƒhƒXƒLƒ‹db‚̃AƒNƒZƒTi¡‚Í’¼‘Å‚¿‚Å‘ã—pj
+// Modified for new skills [Sara]
+int guild_skill_get_inf(int id)
+{
+ switch(id) {
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ case GD_EMERGENCYCALL:
+ return 4;
+ }
+ return 0;
+}
+
+ // Modified [Komurka]
+int guild_skill_get_max (int id)
+{
+ if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL)
+ return 0;
+ return guild_skill_tree[id-GD_SKILLBASE].max;
+}
+
+// ƒMƒ‹ƒhƒXƒLƒ‹‚ª‚ ‚é‚©Šm”F
+int guild_checkskill(struct guild *g,int id)
+{
+ int idx = id-GD_SKILLBASE;
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+ return g->skill[idx].lv;
+}
+
+/*==========================================
+ * guild_skill_tree.txt reading - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_read_guildskill_tree_db(void)
+{
+ int i,k,id=0,ln=0;
+ FILE *fp;
+ char line[1024],*p;
+
+ memset(guild_skill_tree,0,sizeof(guild_skill_tree));
+ sprintf(line, "%s/guild_skill_tree.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(i=0,p=line;i<12 && p;i++){
+ split[i]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(i<12)
+ continue;
+ id = atoi(split[0]) - GD_SKILLBASE;
+ if(id<0 || id>=MAX_GUILDSKILL)
+ continue;
+ guild_skill_tree[id].id=atoi(split[0]);
+ guild_skill_tree[id].max=atoi(split[1]);
+ if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1;
+ for(k=0;k<5;k++){
+ guild_skill_tree[id].need[k].id=atoi(split[k*2+2]);
+ guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]);
+ }
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * Guild skill check - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_check_skill_require(struct guild *g,int id)
+{
+ int i;
+ int idx = id-GD_SKILLBASE;
+
+ if(g == NULL)
+ return 0;
+
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+
+ for(i=0;i<5;i++)
+ {
+ if(guild_skill_tree[idx].need[i].id == 0) break;
+ if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id))
+ return 0;
+ }
+ return 1;
+}
+
+static int guild_read_castledb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int j,ln=0;
+ char *str[32],*p;
+ struct guild_castle *gc;
+
+ sprintf(line, "%s/castle_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<6 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if (j < 4) //Insufficient data for castle. [Skotlex]
+ {
+ ShowError("castle_db.txt: invalid line '%s'\n", line);
+ continue;
+ }
+
+ gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle));
+ gc->castle_id=atoi(str[0]);
+ memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1);
+ memcpy(gc->castle_name,str[2],NAME_LENGTH-1);
+ memcpy(gc->castle_event,str[3],NAME_LENGTH-1);
+
+ idb_put(castle_db,gc->castle_id,gc);
+
+ //intif_guild_castle_info(gc->castle_id);
+
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt");
+ return 0;
+}
+
+// ‰Šú‰»
+void do_init_guild(void)
+{
+ guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+
+ guild_read_castledb();
+
+ guild_read_guildskill_tree_db(); //guild skill tree [Komurka]
+
+ add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer");
+ add_timer_func_list(guild_payexp_timer,"guild_payexp_timer");
+ add_timer_func_list(guild_save_sub, "guild_save_sub");
+ add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer");
+ add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL);
+ add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL);
+}
+
+
+// ŒŸõ
+struct guild *guild_search(int guild_id)
+{
+ if(guild_cache && guild_cache->guild_id == guild_id)
+ return guild_cache;
+ guild_cache = idb_get(guild_db,guild_id);
+ return guild_cache;
+}
+int guild_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct guild **);
+ if(strcmpi(g->name,str)==0)
+ *dst=g;
+ return 0;
+}
+// ƒMƒ‹ƒh–¼ŒŸõ
+struct guild* guild_searchname(char *str)
+{
+ struct guild *g=NULL;
+ guild_db->foreach(guild_db,guild_searchname_sub,str,&g);
+ return g;
+}
+struct guild_castle *guild_castle_search(int gcid)
+{
+ return idb_get(castle_db,gcid);
+}
+
+// mapname‚ɑΉž‚µ‚½ƒAƒWƒg‚Ìgc‚ð•Ô‚·
+struct guild_castle *guild_mapname2gc(char *mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapname)==0) return gc;
+ }
+ return NULL;
+}
+
+struct guild_castle *guild_mapindex2gc(short mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapindex_id2name(mapname))==0) return gc;
+ }
+ return NULL;
+}
+
+
+
+// ƒƒOƒCƒ“’†‚̃Mƒ‹ƒhƒƒ“ƒo[‚Ì‚Pl‚Ì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;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒo[‚̃Cƒ“ƒfƒbƒNƒX‚ð•Ô‚·
+int guild_getindex(struct guild *g,int account_id,int char_id)
+{
+ int i;
+ if(g==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id )
+ return i;
+ return -1;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo[‚Ì–ðE‚ð•Ô‚·
+int guild_getposition(struct map_session_data *sd,struct guild *g)
+{
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id )
+ return g->member[i].position;
+ return -1;
+}
+
+// ƒƒ“ƒo[î•ñ‚Ìì¬
+void guild_makemember(struct guild_member *m,struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+
+ memset(m,0,sizeof(struct guild_member));
+ m->account_id =sd->status.account_id;
+ m->char_id =sd->status.char_id;
+ m->hair =sd->status.hair;
+ m->hair_color =sd->status.hair_color;
+ m->gender =sd->sex;
+ m->class_ =sd->status.class_;
+ m->lv =sd->status.base_level;
+// m->exp =0;
+// m->exp_payper =0;
+ m->online =1;
+ m->position =MAX_GUILDPOSITION-1;
+ memcpy(m->name,sd->status.name,NAME_LENGTH-1);
+ return;
+}
+// ƒMƒ‹ƒh‹£‡Šm”F
+int guild_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_guild_checkconflict(sd->status.guild_id,
+ sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+// ƒMƒ‹ƒh‚ÌEXPƒLƒƒƒbƒVƒ…‚ðinterŽI‚Ƀtƒ‰ƒbƒVƒ…‚·‚é
+int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap)
+{
+ int i, *dellist, *delp;
+ struct guild_expcache *c;
+ struct guild *g;
+ double exp2;
+
+ c = (struct guild_expcache *)data;
+ dellist = va_arg(ap,int *);
+ delp = va_arg(ap,int *);
+
+ if (*delp >= GUILD_PAYEXP_LIST ||
+ (g = guild_search(c->guild_id)) == NULL ||
+ (i = guild_getindex(g, c->account_id, c->char_id)) < 0)
+ return 0;
+
+ // It is *already* fixed... this would be more appropriate ^^; [celest]
+ exp2 = g->member[i].exp + c->exp;
+ g->member[i].exp = (exp2 > 0x7FFFFFFF) ? 0x7FFFFFFF : (int)exp2;
+
+ //fixed a bug in exp overflow [Kevin]
+ //g->member[i].exp += c->exp;
+ //if(g->member[i].exp < 0)
+ //g->member[i].exp = 0x7FFFFFFF;
+
+ intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id,
+ GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp));
+ c->exp=0;
+
+ dellist[(*delp)++]=dataid.i;
+ return 0;
+}
+
+int guild_payexp_timer(int tid, unsigned int tick, int id, int data)
+{
+ int dellist[GUILD_PAYEXP_LIST], delp = 0, i;
+ guild_expcache_db->foreach(guild_expcache_db, guild_payexp_timer_sub, dellist, &delp);
+ for (i = 0; i < delp; i++)
+ idb_remove(guild_expcache_db, dellist[i]);
+ return 0;
+}
+
+//Taken from party_send_xy_timer_sub. [Skotlex]
+int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int i;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<g->max_member;i++){
+ struct map_session_data *sd;
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){
+ clif_guild_xy(sd);
+ sd->guild_x=sd->bl.x;
+ sd->guild_y=sd->bl.y;
+ }
+ }
+ }
+ return 0;
+}
+
+//Code from party_send_xy_timer [Skotlex]
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick);
+ return 0;
+}
+
+int guild_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.guild_id)
+ clif_guild_xy_remove(sd);
+ return 0;
+}
+//------------------------------------------------------------------------
+
+// 쬗v‹
+int guild_create(struct map_session_data *sd,char *name)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id==0){
+ if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) {
+ struct guild_member m;
+ guild_makemember(&m,sd);
+ m.position=0;
+ intif_guild_create(name,&m);
+ } else
+ clif_guild_created(sd,3); // ƒGƒ“ƒyƒŠƒEƒ€‚ª‚¢‚È‚¢
+ }else
+ clif_guild_created(sd,1); // ‚·‚Å‚ÉŠ‘®‚µ‚Ä‚¢‚é
+
+ return 0;
+}
+
+// 쬉”Û
+int guild_created(int account_id,int guild_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+
+ if(sd==NULL)
+ return 0;
+ if(guild_id>0) {
+ //struct guild *g;
+ sd->status.guild_id=guild_id;
+ sd->state.guild_sent=0;
+ clif_guild_created(sd,0);
+ if(battle_config.guild_emperium_check)
+ pc_delitem(sd,pc_search_inventory(sd,714),1,0); // ƒGƒ“ƒyƒŠƒEƒ€Á–Õ
+ } else {
+ clif_guild_created(sd,2); // 쬎¸”si“¯–¼ƒMƒ‹ƒh‘¶Ýj
+ }
+ return 0;
+}
+
+// î•ñ—v‹
+int guild_request_info(int guild_id)
+{
+// if(battle_config.etc_log)
+// printf("guild_request_info\n");
+ return intif_guild_request_info(guild_id);
+}
+// ƒCƒxƒ“ƒg•t‚«î•ñ—v‹
+int guild_npc_request_info(int guild_id,const char *event)
+{
+ struct eventlist *ev;
+
+ if( guild_search(guild_id) ){
+ if(event && *event)
+ npc_event_do(event);
+ return 0;
+ }
+
+ if(event==NULL || *event==0)
+ return guild_request_info(guild_id);
+
+ ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist));
+ memcpy(ev->name,event,strlen(event));
+ //The one in the db becomes the next event from this.
+ ev->next=idb_put(guild_infoevent_db,guild_id,ev);
+ return guild_request_info(guild_id);
+}
+
+// Š‘®ƒLƒƒƒ‰‚ÌŠm”F
+int guild_check_member(const struct guild *g)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, g);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd=all_sd[i])){
+ if(sd->status.guild_id==g->guild_id){
+ int j,f=1;
+ for(j=0;j<MAX_GUILD;j++){ // ƒf[ƒ^‚ª‚ ‚é‚©
+ if( g->member[j].account_id==sd->status.account_id &&
+ g->member[j].char_id==sd->status.char_id)
+ f=0;
+ }
+ if(f){
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ sd->guild_emblem_id=0;
+ if(battle_config.error_log)
+ ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+// î•ñŠ“¾Ž¸”si‚»‚ÌID‚̃Lƒƒƒ‰‚ð‘S•”–¢Š‘®‚É‚·‚éj
+int guild_recv_noinfo(int guild_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd=all_sd[i])){
+ if(sd->status.guild_id==guild_id)
+ sd->status.guild_id=0;
+ }
+ }
+ return 0;
+}
+// î•ñŠ“¾
+int guild_recv_info(struct guild *sg)
+{
+ struct guild *g,before;
+ int i,bm,m;
+ struct eventlist *ev,*ev2;
+
+ nullpo_retr(0, sg);
+
+ if((g=idb_get(guild_db,sg->guild_id))==NULL){
+ struct map_session_data *sd;
+ g=(struct guild *)aCalloc(1,sizeof(struct guild));
+ idb_put(guild_db,sg->guild_id,g);
+ before=*sg;
+
+ // ʼn‚̃[ƒh‚Ȃ̂ц[ƒU[‚̃`ƒFƒbƒN‚ðs‚¤
+ guild_check_member(sg);
+ //If the guild master is online the first time the guild_info is received, that means he was the first to join,
+ //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex]
+ if ((sd = map_nick2sd(sg->master)) != NULL)
+ {
+ guild_block_skill(sd, 300000);
+ //Also set the guild master flag.
+ sd->state.gmaster_flag = g;
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }else
+ before=*g;
+ memcpy(g,sg,sizeof(struct guild));
+
+ for(i=bm=m=0;i<g->max_member;i++){ // sd‚ÌÝ’è‚Æl”‚ÌŠm”F
+ if(g->member[i].account_id>0){
+ struct map_session_data *sd = map_id2sd(g->member[i].account_id);
+ if (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id &&
+ !sd->state.waitingdisconnect) {
+ g->member[i].sd = sd;
+ clif_charnameupdate(sd); // [LuzZza]
+ } else g->member[i].sd = NULL;
+ m++;
+ }else
+ g->member[i].sd=NULL;
+ if(before.member[i].account_id>0)
+ bm++;
+ }
+
+ for(i=0;i<g->max_member;i++){ // î•ñ‚Ì‘—M
+ 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); // Šî–{î•ñ‘—M
+ clif_guild_emblem(sd,g); // ƒGƒ“ƒuƒŒƒ€‘—M
+ }
+
+ if(bm!=m){ // ƒƒ“ƒo[î•ñ‘—M
+ clif_guild_memberlist(g->member[i].sd);
+ }
+
+ if( before.skill_point!=g->skill_point)
+ clif_guild_skillinfo(sd); // ƒXƒLƒ‹î•ñ‘—M
+
+ if( sd->state.guild_sent==0){ // –¢‘—M‚Ȃ犑®î•ñ‚à‘—‚é
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+ sd->guild_emblem_id=g->emblem_id;
+ sd->state.guild_sent=1;
+ }
+ }
+
+ // ƒCƒxƒ“ƒg‚Ì”­¶
+ if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+
+ return 0;
+}
+
+
+// ƒMƒ‹ƒh‚Ö‚ÌŠ©—U
+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 ){ // ‘ŠŽè‚ÌŠ‘®Šm”F
+ clif_guild_inviteack(sd,0);
+ return 0;
+ }
+
+ // ’èˆõŠm”F
+ 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;
+}
+// ƒMƒ‹ƒhŠ©—U‚Ö‚Ì•Ô“š
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag)
+{
+ struct map_session_data *tsd;
+
+ nullpo_retr(0, sd);
+
+ //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account ));
+ //I checked the code, and there's no "check" for the case where the guy
+ //that invites another to a guild quits the map-server before being replied.
+ //Hence that's a valid null pointer scenario. :) [Skotlex]
+ if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL)
+ { //Do we send a "invitation failed" msg or something to the player?
+ //Or should we accept the invitation and add it to the guild anyway?
+ //afterall, guild_invite holds the guild id that the player was invited to.
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ return 0;
+ }
+
+ if(sd->guild_invite!=guild_id) // Š©—U‚ƃMƒ‹ƒhID‚ªˆá‚¤
+ return 0;
+
+ if(flag==1){ // ³‘ø
+ struct guild_member m;
+ struct guild *g;
+ int i;
+
+ // ’èˆõŠm”F
+ 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ŽI‚֒ljÁ—v‹
+ guild_makemember(&m,sd);
+ intif_guild_addmember( sd->guild_invite, &m );
+ return 0;
+ }else{ // ‹‘”Û
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ clif_guild_inviteack(tsd,1);
+ }
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚ª’ljÁ‚³‚ꂽ
+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){
+ // ƒLƒƒƒ‰‘¤‚É“o˜^‚Å‚«‚È‚©‚Á‚½‚½‚ß’E‘Þ—v‹‚ðo‚·
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("guild: member added error %d is not online\n",account_id);
+ intif_guild_leave(guild_id,account_id,char_id,0,"**“o˜^Ž¸”s**");
+ }
+ return 0;
+ }
+ sd2 = map_id2sd(sd->guild_invite_account);
+ sd->guild_invite = 0;
+ sd->guild_invite_account = 0;
+
+ if(flag==1){ // Ž¸”s
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,3);
+ return 0;
+ }
+
+ // ¬Œ÷
+ sd->state.guild_sent=0;
+ sd->status.guild_id=guild_id;
+
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,2);
+
+ // ‚¢‚¿‚¨‚¤‹£‡Šm”F
+ guild_check_conflict(sd);
+ //Next line commented because it do nothing, look at guild_recv_info [LuzZza]
+ //clif_charnameupdate(sd); //Update display name [Skotlex]
+ return 0;
+}
+
+// ƒMƒ‹ƒh’E‘Þ—v‹
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if( sd->status.account_id!=account_id ||
+ sd->status.char_id!=char_id || sd->status.guild_id!=guild_id)
+ return 0;
+
+ for(i=0;i<g->max_member;i++){ // Š‘®‚µ‚Ä‚¢‚é‚©
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id ){
+ intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes);
+ return 0;
+ }
+ }
+ return 0;
+}
+// ƒMƒ‹ƒh’Ç•ú—v‹
+int guild_explusion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i,ps;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if( sd->status.guild_id!=guild_id)
+ return 0;
+
+ if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) )
+ return 0; // ˆ”±Œ ŒÀ–³‚µ
+
+ for(i=0;i<g->max_member;i++){ // Š‘®‚µ‚Ä‚¢‚é‚©
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id ){
+ intif_guild_leave(g->guild_id,account_id,char_id,1,mes);
+ memset(&g->member[i],0,sizeof(struct guild_member));
+ return 0;
+ }
+ }
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚ª’E‘Þ‚µ‚½
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes)
+{
+ struct map_session_data *sd=map_charid2sd(char_id);
+ struct guild *g=guild_search(guild_id);
+
+ if(g!=NULL){
+ int i;
+ for(i=0;i<g->max_member;i++) {
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id ){
+ struct map_session_data *sd2=sd;
+ if(sd2==NULL)
+ sd2=guild_getavailablesd(g);
+ else
+ {
+ if(flag==0)
+ clif_guild_leave(sd2,name,mes);
+ else
+ clif_guild_explusion(sd2,name,mes,account_id);
+ }
+ memset(&g->member[i],0,sizeof(struct guild_member));
+ }
+ // ƒƒ“ƒo[ƒŠƒXƒg‚ð‘Sˆõ‚ÉÄ’Ê’m
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].sd!=NULL )
+ clif_guild_memberlist(g->member[i].sd);
+ }
+ }
+ }
+ if(sd!=NULL) {
+ if (sd->status.guild_id==guild_id){
+ guild_send_dot_remove(sd);
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+ clif_charnameupdate(sd); //Update display name [Skotlex]
+ }
+ }
+
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚̃Iƒ“ƒ‰ƒCƒ“ó‘Ô/LvXV‘—M
+int guild_send_memberinfoshort(struct map_session_data *sd,int online)
+{
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id<=0)
+ return 0;
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL)
+ return 0;
+
+ intif_guild_memberinfoshort(g->guild_id,
+ sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_);
+
+ if( !online ){ // ƒƒOƒAƒEƒg‚·‚é‚È‚çsd‚ðƒNƒŠƒA‚µ‚ÄI—¹
+ int i=guild_getindex(g,sd->status.account_id,sd->status.char_id);
+ if(i>=0)
+ g->member[i].sd=NULL;
+ return 0;
+ }
+
+ if( sd->state.guild_sent!=0 ) // ƒMƒ‹ƒh‰Šú‘—Mƒf[ƒ^‚Í‘—MÏ‚Ý
+ return 0;
+
+ // ‹£‡Šm”F
+ guild_check_conflict(sd);
+
+ // ‚ ‚é‚È‚çƒMƒ‹ƒh‰Šú‘—Mƒf[ƒ^‘—M
+ if( (g=guild_search(sd->status.guild_id))!=NULL ){
+ guild_check_member(g); // Š‘®‚ðŠm”F‚·‚é
+ if(sd->status.guild_id==g->guild_id){
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+ sd->state.guild_sent=1;
+ sd->guild_emblem_id=g->emblem_id;
+ }
+ }
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚̃Iƒ“ƒ‰ƒCƒ“ó‘Ô/LvXV’Ê’m
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_)
+{
+ int i,alv,c,idx=-1,om=0,oldonline=-1;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+ for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==account_id && m->char_id==char_id ){
+ oldonline=m->online;
+ m->online=online;
+ m->lv=lv;
+ m->class_=class_;
+ idx=i;
+ }
+ if(m->account_id>0){
+ alv+=m->lv;
+ c++;
+ }
+ if(m->online)
+ om++;
+ }
+ if(idx == -1 || c == 0) {
+ // ƒMƒ‹ƒh‚̃ƒ“ƒo[ŠO‚È‚Ì‚Å’Ç•úˆµ‚¢‚·‚é
+ struct map_session_data *sd = map_id2sd(account_id);
+ if(sd && sd->char_id == char_id) {
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+ }
+ if(battle_config.error_log)
+ ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name);
+ return 0;
+ }
+ g->average_lv=alv/c;
+ g->connect_member=om;
+
+ if(oldonline!=online) // ƒIƒ“ƒ‰ƒCƒ“ó‘Ô‚ª•Ï‚í‚Á‚½‚Ì‚Å’Ê’m
+ clif_guild_memberlogin_notice(g,idx,online);
+
+ for(i=0;i<g->max_member;i++){ // sdÄÝ’è
+ struct map_session_data *sd= map_id2sd(g->member[i].account_id);
+ if (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id &&
+ !sd->state.waitingdisconnect)
+ g->member[i].sd = sd;
+ else sd = NULL;
+ }
+
+ // ‚±‚±‚ɃNƒ‰ƒCƒAƒ“ƒg‚É‘—Mˆ—‚ª•K—v
+
+ return 0;
+}
+// ƒMƒ‹ƒh‰ï˜b‘—M
+int guild_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id==0)
+ return 0;
+ intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len);
+ guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len);
+
+ //Chatlogging type 'G'
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&8 //if Guild bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+// ƒMƒ‹ƒh‰ï˜bŽóM
+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;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚Ì–ðE•ÏX
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx)
+{
+ return intif_guild_change_memberinfo(
+ guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx));
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚Ì–ðE•ÏX’Ê’m
+int guild_memberposition_changed(struct guild *g,int idx,int pos)
+{
+ nullpo_retr(0, g);
+
+ g->member[idx].position=pos;
+ clif_guild_memberpositionchanged(g,idx);
+
+ // Update char position in client [LuzZza]
+ if(g->member[idx].sd != NULL)
+ clif_charnameupdate(g->member[idx].sd);
+ return 0;
+}
+// ƒMƒ‹ƒh–ðE•ÏX
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name)
+{
+ struct guild_position p;
+
+ nullpo_retr(0, sd);
+
+ if(exp_mode>battle_config.guild_exp_limit)
+ exp_mode=battle_config.guild_exp_limit;
+ if(exp_mode<0)exp_mode=0;
+ p.mode=mode;
+ p.exp_mode=exp_mode;
+ memcpy(p.name,name,NAME_LENGTH-1);
+ p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex]
+ return intif_guild_position(sd->status.guild_id,idx,&p);
+}
+// ƒMƒ‹ƒh–ðE•ÏX’Ê’m
+int guild_position_changed(int guild_id,int idx,struct guild_position *p)
+{
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ memcpy(&g->position[idx],p,sizeof(struct guild_position));
+ clif_guild_positionchanged(g,idx);
+
+ // Update char name in client [LuzZza]
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].position == idx && g->member[i].sd != NULL)
+ clif_charnameupdate(g->member[i].sd);
+ return 0;
+}
+// ƒMƒ‹ƒh’m•ÏX
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2)
+{
+ nullpo_retr(0, sd);
+
+ if(guild_id!=sd->status.guild_id)
+ return 0;
+ return intif_guild_notice(guild_id,mes1,mes2);
+}
+// ƒMƒ‹ƒh’m•ÏX’Ê’m
+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;
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (battle_config.require_glory_guild &&
+ !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
+ clif_skill_fail(sd,GD_GLORYGUILD,0,0);
+ return 0;
+ }
+
+ return intif_guild_emblem(sd->status.guild_id,len,data);
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX’Ê’m
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data)
+{
+ int i;
+ struct map_session_data *sd;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->emblem_data,data,len);
+ g->emblem_len=len;
+ g->emblem_id=emblem_id;
+
+ for(i=0;i<g->max_member;i++){
+ if((sd=g->member[i].sd)!=NULL){
+ sd->guild_emblem_id=emblem_id;
+ clif_guild_belonginfo(sd,g);
+ clif_guild_emblem(sd,g);
+ }
+ }
+ return 0;
+}
+
+static void* create_expcache(DBKey key, va_list args) {
+ struct guild_expcache *c;
+ struct map_session_data *sd = va_arg(args, struct map_session_data*);
+ c = (struct guild_expcache *)aCallocA(1, sizeof(struct guild_expcache));
+ c->guild_id = sd->status.guild_id;
+ c->account_id = sd->status.account_id;
+ c->char_id = sd->status.char_id;
+ return c;
+}
+// ƒMƒ‹ƒh‚ÌEXPã”[
+int guild_payexp(struct map_session_data *sd,int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ int per, exp2;
+ double tmp;
+
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id == 0 ||
+ (g = guild_search(sd->status.guild_id)) == NULL ||
+ (per = g->position[guild_getposition(sd,g)].exp_mode) <= 0)
+ return 0;
+
+ if (per > 100) per = 100;
+
+ if ((exp2 = exp * per / 100) <= 0)
+ return 0;
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+ tmp = c->exp;
+ if (battle_config.guild_exp_rate != 100)
+ tmp += battle_config.guild_exp_rate*exp2/100;
+ else
+ tmp += exp2;
+ c->exp = (tmp > 0x7fffffff) ? 0x7fffffff : (int)tmp;
+ return exp2;
+}
+
+// Celest
+int guild_getexp(struct map_session_data *sd,int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ double tmp;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL)
+ return 0;
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+ tmp = c->exp + exp;
+ c->exp = (tmp > 0x7fffffff) ? 0x7fffffff : (int)tmp;
+ return exp;
+}
+
+// ƒXƒLƒ‹ƒ|ƒCƒ“ƒgŠ„‚èU‚è
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag)
+{
+ struct guild *g;
+ int idx = skill_num - GD_SKILLBASE;
+
+ nullpo_retr(0, sd);
+
+ if(idx < 0 || idx >= MAX_GUILDSKILL)
+
+ return 0;
+ if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL)
+ return 0;
+ if(strcmp(sd->status.name,g->master))
+ return 0;
+
+ if( (g->skill_point>0 || flag&1) &&
+ g->skill[idx].id!=0 &&
+ g->skill[idx].lv < guild_skill_get_max(skill_num) ){
+ intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag);
+ }
+ status_calc_pc (sd, 0); // Celest
+
+ return 0;
+}
+// ƒXƒLƒ‹ƒ|ƒCƒ“ƒgŠ„‚èU‚è’Ê’m
+int guild_skillupack(int guild_id,int skill_num,int account_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ if(sd!=NULL)
+ clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv);
+ // ‘Sˆõ‚É’Ê’m
+ for(i=0;i<g->max_member;i++)
+ if((sd=g->member[i].sd)!=NULL)
+ clif_guild_skillinfo(sd);
+ return 0;
+}
+
+// ƒMƒ‹ƒh“¯–¿”Š“¾
+int guild_get_alliance_count(struct guild *g,int flag)
+{
+ int i,c;
+
+ nullpo_retr(0, g);
+
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ if( g->alliance[i].guild_id>0 &&
+ g->alliance[i].opposition==flag )
+ c++;
+ }
+ return c;
+}
+
+// Blocks all guild skills which have a common delay time.
+void guild_block_skill(struct map_session_data *sd, int time) {
+ int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL };
+ int i;
+ for (i = 0; i < 4; i++)
+ pc_blockskill_start(sd, skill_num[i], time);
+}
+
+// “¯–¿ŠÖŒW‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+// “¯–¿‚È‚ç1A‚»‚êˆÈŠO‚Í0
+int guild_check_alliance(int guild_id1, int guild_id2, int flag)
+{
+ struct guild *g;
+ int i;
+
+ g = guild_search(guild_id1);
+ if (g == NULL)
+ return 0;
+
+ for (i=0; i<MAX_GUILDALLIANCE; i++)
+ if ((g->alliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag))
+ return 1;
+
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿—v‹
+int guild_reqalliance(struct map_session_data *sd,int account_id)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+ struct guild *g[2];
+ int i;
+
+ if(agit_flag) { // Disable alliance creation during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || tsd->status.guild_id<=0)
+ return 0;
+
+ g[0]=guild_search(sd->status.guild_id);
+ g[1]=guild_search(tsd->status.guild_id);
+
+ if(g[0]==NULL || g[1]==NULL)
+ return 0;
+
+ if( guild_get_alliance_count(g[0],0)>=3 ) // “¯–¿”Šm”F
+ clif_guild_allianceack(sd,4);
+ if( guild_get_alliance_count(g[1],0)>=3 )
+ clif_guild_allianceack(sd,3);
+
+ if( tsd->guild_alliance>0 ){ // ‘ŠŽè‚ª“¯–¿—v¿ó‘Ô‚©‚Ç‚¤‚©Šm”F
+ clif_guild_allianceack(sd,1);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // ‚·‚Å‚É“¯–¿ó‘Ô‚©Šm”F
+ 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;
+}
+// ƒMƒ‹ƒhŠ©—U‚Ö‚Ì•Ô“š
+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) // Š©—U‚ƃMƒ‹ƒhID‚ªˆá‚¤
+ return 0;
+
+ if(flag==1){ // ³‘ø
+ int i;
+
+ struct guild *g,*tg; // “¯–¿”ÄŠm”F
+ g=guild_search(sd->status.guild_id);
+ tg=guild_search(tsd->status.guild_id);
+
+ if(g==NULL || guild_get_alliance_count(g,0)>=3){
+ clif_guild_allianceack(sd,4);
+ clif_guild_allianceack(tsd,3);
+ return 0;
+ }
+ if(tg==NULL || guild_get_alliance_count(tg,0)>=3){
+ clif_guild_allianceack(sd,3);
+ clif_guild_allianceack(tsd,4);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(g->alliance[i].guild_id==tsd->status.guild_id &&
+ g->alliance[i].opposition==1)
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,9 );
+ }
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(tg->alliance[i].guild_id==sd->status.guild_id &&
+ tg->alliance[i].opposition==1)
+ intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id,
+ tsd->status.account_id,sd->status.account_id,9 );
+ }
+
+ // interŽI‚Ö“¯–¿—v¿
+ 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;
+}
+// ƒMƒ‹ƒhŠÖŒW‰ðÁ
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ if(agit_flag) { // Disable alliance breaking during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+ nullpo_retr(0, sd);
+
+ intif_guild_alliance( sd->status.guild_id,guild_id,
+ sd->status.account_id,0,flag|8 );
+ return 0;
+}
+// ƒMƒ‹ƒh“G‘Î
+int guild_opposition(struct map_session_data *sd,int char_id)
+{
+ struct map_session_data *tsd=map_id2sd(char_id);
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL || tsd==NULL)
+ return 0;
+
+ if( guild_get_alliance_count(g,1)>=3 ) // “G‘ΔŠm”F
+ clif_guild_oppositionack(sd,1);
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // ‚·‚Å‚ÉŠÖŒW‚ðŽ‚Á‚Ä‚¢‚é‚©Šm”F
+ if(g->alliance[i].guild_id==tsd->status.guild_id){
+ if(g->alliance[i].opposition==1){ // ‚·‚Å‚É“G‘Î
+ clif_guild_oppositionack(sd,2);
+ return 0;
+ }else // “¯–¿”jŠü
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,8 );
+ }
+ }
+
+ // interŽI‚É“G‘Ηv¿
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,1 );
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿/“G‘Î’Ê’m
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2)
+{
+ struct guild *g[2];
+ int guild_id[2];
+ const char *guild_name[2];
+ struct map_session_data *sd[2];
+ int j,i;
+
+ guild_id[0] = guild_id1;
+ guild_id[1] = guild_id2;
+ guild_name[0] = name1;
+ guild_name[1] = name2;
+ sd[0] = map_id2sd(account_id1);
+ sd[1] = map_id2sd(account_id2);
+
+ g[0]=guild_search(guild_id1);
+ g[1]=guild_search(guild_id2);
+
+ if(sd[0]!=NULL && (flag&0x0f)==0){
+ sd[0]->guild_alliance=0;
+ sd[0]->guild_alliance_account=0;
+ }
+
+ if(flag&0x70){ // Ž¸”s
+ 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)){ // ŠÖŒW’ljÁ
+ for(i=0;i<2-(flag&1);i++)
+ if(g[i]!=NULL)
+ for(j=0;j<MAX_GUILDALLIANCE;j++)
+ if(g[i]->alliance[j].guild_id==0){
+ g[i]->alliance[j].guild_id=guild_id[1-i];
+ memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1);
+ g[i]->alliance[j].opposition=flag&1;
+ break;
+ }
+ }else{ // ŠÖŒW‰ðÁ
+ 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 ) // ‰ðÁ’Ê’m
+ clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1));
+ }
+ }
+
+ if((flag&0x0f)==0){ // “¯–¿’Ê’m
+ if( sd[1]!=NULL )
+ clif_guild_allianceack(sd[1],2);
+ }else if((flag&0x0f)==1){ // “G‘Î’Ê’m
+ if( sd[0]!=NULL )
+ clif_guild_oppositionack(sd[0],0);
+ }
+
+
+ for(i=0;i<2-(flag&1);i++){ // “¯–¿/“G‘΃ŠƒXƒg‚ÌÄ‘—M
+ 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;
+}
+// ƒMƒ‹ƒh‰ðŽU’Ê’m—p
+int guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int guild_id=va_arg(ap,int);
+ int i,j;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // ŠÖŒW‚ð”jŠü
+ if(g->alliance[i].guild_id==guild_id){
+ for(j=0;j<g->max_member;j++)
+ if( (sd=g->member[j].sd)!=NULL )
+ clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition);
+ intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8);
+ g->alliance[i].guild_id=0;
+ }
+ }
+ return 0;
+}
+
+//Invoked on Castles when a guild is broken. [Skotlex]
+int castle_guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild_castle *gc=(struct guild_castle *)data;
+ int guild_id=va_arg(ap,int);
+
+ nullpo_retr(0, gc);
+
+ if (gc->guild_id == guild_id)
+ { //Save the new 'owner', this should invoke guardian clean up and other such things.
+ gc->guild_id = 0;
+ guild_castledatasave(gc->castle_id, 1, 0);
+ }
+ return 0;
+}
+
+//Innvoked on /breakguild "Guild name"
+int guild_broken(int guild_id,int flag)
+{
+ struct guild *g=guild_search(guild_id);
+// struct guild_castle *gc=NULL;
+ struct map_session_data *sd;
+ int i;
+// char *name;;
+
+ if(flag!=0 || g==NULL)
+ return 0;
+
+ //we call castle_event::OnGuildBreak of all castlesof the guild
+ //you can set all castle_events in the castle_db.txt
+/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak"
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(gc->guild_id == guild_id){
+ memcpy(name,gc->castle_event,50);
+ npc_event_do(strcat(name,"::OnGuildBreak"));
+ }
+ }
+ }
+ free(name);
+*/
+ for(i=0;i<g->max_member;i++){ // ƒMƒ‹ƒh‰ðŽU‚ð’Ê’m
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ clif_guild_broken(g->member[i].sd,0);
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }
+
+ guild_db->foreach(guild_db,guild_broken_sub,guild_id);
+ castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
+ if (guild_cache && guild_cache->guild_id == guild_id)
+ guild_cache = NULL;
+ guild_storage_delete(guild_id);
+ idb_remove(guild_db,guild_id);
+ return 0;
+}
+
+//Changes the Guild Master to the specified player. [Skotlex]
+int guild_gm_change(int guild_id, struct map_session_data *sd)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id != guild_id)
+ return 0;
+
+ g=guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+ if (strcmp(g->master, sd->status.name) == 0) //Nothing to change.
+ return 0;
+
+ //Notify servers that master has changed.
+ intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name));
+ return 1;
+}
+
+//Notification from Char server that a guild's master has changed. [Skotlex]
+int guild_gm_changed(int guild_id, int pos)
+{
+ struct guild *g;
+ struct guild_member gm;
+
+ g=guild_search(guild_id);
+
+ if (!g || pos < 0 || pos > g->max_member)
+ return 0;
+
+ memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
+ memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
+ memcpy(&g->member[0], &gm, sizeof(struct guild_member));
+
+ g->member[pos].position = g->member[0].position;
+ g->member[0].position = 0; //Position 0: guild Master.
+ strcpy(g->master, g->member[0].name);
+
+ if (g->member[pos].sd && g->member[pos].sd->fd)
+ {
+ clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master.");
+ g->member[pos].sd->state.gmaster_flag = 0;
+ }
+
+ if (g->member[0].sd && g->member[0].sd->fd)
+ {
+ clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!");
+ g->member[0].sd->state.gmaster_flag = g;
+ }
+ return 1;
+}
+
+// ƒMƒ‹ƒh‰ðŽU
+int guild_break(struct map_session_data *sd,char *name)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (g=guild_search(sd->status.guild_id))==NULL )
+ return 0;
+ if(strcmp(g->name,name)!=0)
+ return 0;
+ if(strcmp(sd->status.name,g->master)!=0)
+ return 0;
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].account_id>0 && (
+ g->member[i].account_id!=sd->status.account_id ||
+ g->member[i].char_id!=sd->status.char_id ))
+ break;
+ }
+ if(i<g->max_member){
+ clif_guild_broken(sd,2);
+ return 0;
+ }
+
+ intif_guild_break(g->guild_id);
+ return 0;
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^—v‹
+int guild_castledataload(int castle_id,int index)
+{
+ return intif_guild_castle_dataload(castle_id,index);
+}
+// ƒMƒ‹ƒhéî•ñŠ“¾ŽžƒCƒxƒ“ƒg’ljÁ
+int guild_addcastleinfoevent(int castle_id,int index,const char *name)
+{
+ struct eventlist *ev;
+ int code=castle_id|(index<<16);
+
+ if( name==NULL || *name==0 )
+ return 0;
+
+ ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist));
+ memcpy(ev->name,name,sizeof(ev->name));
+ //The next event becomes whatever was currently stored.
+ ev->next= idb_put(guild_castleinfoevent_db,code,ev);
+ return 0;
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^—v‹•ÔM
+int guild_castledataloadack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ int code=castle_id|(index<<16);
+ struct eventlist *ev,*ev2;
+
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1:
+ gc->guild_id = value;
+ if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex]
+ guild_request_info(value);
+ break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+ return 1;
+}
+// ƒMƒ‹ƒhéƒf[ƒ^•ÏX—v‹
+int guild_castledatasave(int castle_id,int index,int value)
+{
+ if (index == 1)
+ { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex]
+ struct guild_castle *gc = guild_castle_search(castle_id);
+ int m = -1;
+ if (gc) m = map_mapname2mapid(gc->map_name);
+ if (m != -1)
+ map_foreachinmap(mob_guardian_guildchange, m, BL_MOB);
+ }
+ return intif_guild_castle_datasave(castle_id,index,value);
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^•ÏX’Ê’m
+int guild_castledatasaveack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1: gc->guild_id = value; break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ return 1;
+}
+
+// ƒMƒ‹ƒhƒf[ƒ^ˆêŠ‡ŽóMi‰Šú‰»Žžj
+int guild_castlealldataload(int len,struct guild_castle *gc)
+{
+ int i;
+ int n = (len-4) / sizeof(struct guild_castle), ev = -1;
+
+ nullpo_retr(0, gc);
+
+ //Last owned castle in the list invokes ::OnAgitinit
+ for(i = 0; i < n; i++) {
+ if ((gc + i)->guild_id)
+ ev = i;
+ }
+
+ // éƒf[ƒ^Ši”[‚ƃMƒ‹ƒhî•ñ—v‹
+ for(i = 0; i < n; i++, gc++) {
+ struct guild_castle *c = guild_castle_search(gc->castle_id);
+ if (!c) {
+ ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id);
+ continue;
+ }
+ memcpy(&c->guild_id,&gc->guild_id,
+ sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) );
+ if( c->guild_id ){
+ if(i!=ev)
+ guild_request_info(c->guild_id);
+ else
+ guild_npc_request_info(c->guild_id, "::OnAgitInit");
+ }
+ }
+ if (ev == -1) //No castles owned, invoke OnAgitInit as it is.
+ npc_event_doall("OnAgitInit");
+ return 0;
+}
+
+int guild_agit_start(void)
+{ // Run All NPC_Event[OnAgitStart]
+ int c = npc_event_doall("OnAgitStart");
+ ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c);
+ // Start auto saving
+ guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL);
+ return 0;
+}
+
+int guild_agit_end(void)
+{ // Run All NPC_Event[OnAgitEnd]
+ int c = npc_event_doall("OnAgitEnd");
+ ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c);
+ // Stop auto saving
+ delete_timer (guild_save_timer, guild_save_sub);
+ return 0;
+}
+
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data)
+{ // Run One NPC_Event[OnAgitEliminate]
+ char *name = (char*)data;
+ size_t len = (name) ? strlen(name) : 0;
+ // the rest is dangerous, but let it crash,
+ // if this happens, it's ruined anyway
+ int c=0;
+
+ if(agit_flag) // Agit not already End
+ {
+ char *evname=(char*)aMalloc( (len + 10) * sizeof(char));
+ memcpy(evname,name,len - 5);
+ strcpy(evname + len - 5,"Eliminate");
+ c = npc_event_do(evname);
+ ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c);
+ aFree(evname); // [Lance] Should fix this
+ }
+ if(name) aFree(name);
+ return 0;
+}
+
+static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor]
+static int Gid[MAX_GUILDCASTLE];
+int guild_save_sub(int tid,unsigned int tick,int id,int data)
+{
+ struct guild_castle *gc;
+ int i,j;
+
+ for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor]
+ gc = guild_castle_search(i);
+ if (!gc) continue;
+ if (gc->guild_id != Gid[i]) {
+ // Re-save guild id if its owner guild has changed
+ guild_castledatasave(gc->castle_id, 1, gc->guild_id);
+ Gid[i] = gc->guild_id;
+ }
+ for (j = 0; j < MAX_GUARDIANS; j++)
+ {
+ if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp)
+ guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp);
+ }
+ }
+
+ return 0;
+}
+
+int guild_agit_break(struct mob_data *md)
+{ // Run One NPC_Event[OnAgitBreak]
+ char *evname;
+
+ nullpo_retr(0, md);
+
+ evname=(char *)aCallocA(strlen(md->npc_event) + 1, sizeof(char));
+
+ strcpy(evname,md->npc_event);
+// Now By User to Run [OnAgitBreak] NPC Event...
+// It's a little impossible to null point with player disconnect in this!
+// But Script will be stop, so nothing...
+// Maybe will be changed in the futher..
+// int c = npc_event_do(evname);
+ if(!agit_flag) return 0; // Agit already End
+ add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname);
+ return 0;
+}
+
+// [MouseJstr]
+// How many castles does this guild have?
+int guild_checkcastles(struct guild *g) {
+ int i,nb_cas=0, id,cas_id=0;
+ struct guild_castle *gc;
+ id=g->guild_id;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ cas_id=gc->guild_id;
+ if(g->guild_id==cas_id)
+ nb_cas=nb_cas+1;
+ } //end for
+ return nb_cas;
+}
+
+// [MouseJstr]
+// is this guild allied with this castle?
+int guild_isallied(struct guild *g, struct guild_castle *gc)
+{
+ int i;
+
+ nullpo_retr(0, g);
+
+ if(g->guild_id == gc->guild_id)
+ return 1;
+
+ if (gc->guild_id == 0)
+ return 0;
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == gc->guild_id) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+int guild_idisallied(int guild_id, int guild_id2)
+{
+ int i;
+ struct guild *g;
+
+ if (guild_id <= 0 || guild_id2 <= 0)
+ return 0;
+
+ if(guild_id == guild_id2)
+ return 1;
+
+ g = guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == guild_id2) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+static int guild_infoevent_db_final(DBKey key,void *data,va_list ap)
+{
+ aFree(data);
+ return 0;
+}
+void do_final_guild(void)
+{
+ guild_db->destroy(guild_db,NULL);
+ castle_db->destroy(castle_db,NULL);
+ guild_expcache_db->destroy(guild_expcache_db,NULL);
+ guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final);
+ guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final);
+}
diff --git a/src/map/guild.h b/src/map/guild.h
new file mode 100644
index 000000000..eaca29f46
--- /dev/null
+++ b/src/map/guild.h
@@ -0,0 +1,96 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GUILD_H_
+#define _GUILD_H_
+
+struct map_session_data;
+struct mob_data;
+struct guild;
+struct guild_member;
+struct guild_position;
+struct guild_castle;
+
+int guild_skill_get_inf(int id);
+int guild_skill_get_max(int id);
+
+int guild_checkskill(struct guild *g,int id);
+int guild_check_skill_require(struct guild *g,int id); // [Komurka]
+int guild_checkcastles(struct guild *g); // [MouseJstr]
+int guild_isallied(struct guild *g, struct guild_castle *gc);
+int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex]
+
+void do_init_guild(void);
+struct guild *guild_search(int guild_id);
+struct guild *guild_searchname(char *str);
+struct guild_castle *guild_castle_search(int gcid);
+
+struct guild_castle *guild_mapname2gc(char *mapname);
+struct guild_castle *guild_mapindex2gc(short mapname);
+
+struct map_session_data *guild_getavailablesd(struct guild *g);
+int guild_getindex(struct guild *g,int account_id,int char_id);
+int guild_getposition(struct map_session_data *sd,struct guild *g);
+int guild_payexp(struct map_session_data *sd,int exp);
+int guild_getexp(struct map_session_data *sd,int exp); // [Celest]
+
+int guild_create(struct map_session_data *sd,char *name);
+int guild_created(int account_id,int guild_id);
+int guild_request_info(int guild_id);
+int guild_recv_noinfo(int guild_id);
+int guild_recv_info(struct guild *sg);
+int guild_npc_request_info(int guild_id,const char *ev);
+int guild_invite(struct map_session_data *sd,int account_id);
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag);
+int guild_member_added(int guild_id,int account_id,int char_id,int flag);
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes);
+int guild_explusion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag);
+void guild_block_skill(struct map_session_data *sd, int time);
+int guild_reqalliance(struct map_session_data *sd,int account_id);
+int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag);
+int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2);
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2);
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int guild_opposition(struct map_session_data *sd,int char_id);
+int guild_check_alliance(int guild_id1, int guild_id2, int flag);
+
+int guild_send_memberinfoshort(struct map_session_data *sd,int online);
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_);
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx);
+int guild_memberposition_changed(struct guild *g,int idx,int pos);
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name);
+int guild_position_changed(int guild_id,int idx,struct guild_position *p);
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2);
+int guild_notice_changed(int guild_id,const char *mes1,const char *mes2);
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data);
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data);
+int guild_send_message(struct map_session_data *sd,char *mes,int len);
+int guild_recv_message(int guild_id,int account_id,char *mes,int len);
+int guild_send_dot_remove(struct map_session_data *sd);
+int guild_skillupack(int guild_id,int skill_num,int account_id);
+int guild_break(struct map_session_data *sd,char *name);
+int guild_broken(int guild_id,int flag);
+int guild_gm_change(int guild_id, struct map_session_data *sd);
+int guild_gm_changed(int guild_id, int pos);
+
+int guild_addcastleinfoevent(int castle_id,int index,const char *name);
+int guild_castledataload(int castle_id,int index);
+int guild_castledataloadack(int castle_id,int index,int value);
+int guild_castledatasave(int castle_id,int index,int value);
+int guild_castledatasaveack(int castle_id,int index,int value);
+int guild_castlealldataload(int len,struct guild_castle *gc);
+
+int guild_agit_start(void);
+int guild_agit_end(void);
+int guild_agit_break(struct mob_data *md);
+
+void do_final_guild(void);
+
+#endif
diff --git a/src/map/intif.c b/src/map/intif.c
new file mode 100644
index 000000000..6b53d5c0d
--- /dev/null
+++ b/src/map/intif.c
@@ -0,0 +1,1401 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "../common/showmsg.h"
+
+#include "socket.h"
+#include "timer.h"
+#include "map.h"
+#include "battle.h"
+#include "chrif.h"
+#include "clif.h"
+#include "pc.h"
+#include "intif.h"
+#include "storage.h"
+#include "party.h"
+#include "guild.h"
+#include "pet.h"
+#include "nullpo.h"
+#include "malloc.h"
+
+static const int packet_len_table[]={
+ -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f
+ -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810
+ 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820
+ 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
+ 9, 9,-1,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3840
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
+};
+
+extern int char_fd; // inter server‚Ìfd‚Íchar_fd‚ðŽg‚¤
+#define inter_fd (char_fd) // ƒGƒCƒŠƒAƒX
+
+//-----------------------------------------------------------------
+// inter server‚Ö‚Ì‘—M
+
+int CheckForCharServer(void) {
+ return ((char_fd <= 0) || session[char_fd] == NULL || session[char_fd]->wdata == NULL);
+}
+
+// pet
+int intif_create_pet(int account_id,int char_id,short pet_class,short pet_lv,short pet_egg_id,
+ short pet_equip,short intimate,short hungry,char rename_flag,char incuvate,char *pet_name)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 24 + NAME_LENGTH);
+ WFIFOW(inter_fd,0) = 0x3080;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOW(inter_fd,10) = pet_class;
+ WFIFOW(inter_fd,12) = pet_lv;
+ WFIFOW(inter_fd,14) = pet_egg_id;
+ WFIFOW(inter_fd,16) = pet_equip;
+ WFIFOW(inter_fd,18) = intimate;
+ WFIFOW(inter_fd,20) = hungry;
+ WFIFOB(inter_fd,22) = rename_flag;
+ WFIFOB(inter_fd,23) = incuvate;
+ memcpy(WFIFOP(inter_fd,24),pet_name,NAME_LENGTH);
+ WFIFOSET(inter_fd,24+NAME_LENGTH);
+
+ return 0;
+}
+
+int intif_request_petdata(int account_id,int char_id,int pet_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 14);
+ WFIFOW(inter_fd,0) = 0x3081;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOL(inter_fd,10) = pet_id;
+ WFIFOSET(inter_fd,14);
+
+ return 0;
+}
+
+int intif_save_petdata(int account_id,struct s_pet *p)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, sizeof(struct s_pet) + 8);
+ WFIFOW(inter_fd,0) = 0x3082;
+ WFIFOW(inter_fd,2) = sizeof(struct s_pet) + 8;
+ WFIFOL(inter_fd,4) = account_id;
+ memcpy(WFIFOP(inter_fd,8),p,sizeof(struct s_pet));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+
+ return 0;
+}
+
+int intif_delete_petdata(int pet_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3083;
+ WFIFOL(inter_fd,2) = pet_id;
+ WFIFOSET(inter_fd,6);
+
+ return 0;
+}
+
+// GMƒƒbƒZ[ƒW‚ð‘—M
+int intif_GMmessage(char* mes,int len,int flag)
+{
+ int lp = (flag&0x10) ? 8 : 4;
+
+ // Send to the local players
+ clif_GMmessage(NULL, mes, len, flag);
+
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,lp + len + 4);
+ WFIFOW(inter_fd,0) = 0x3000;
+ WFIFOW(inter_fd,2) = lp + len + 4;
+ WFIFOL(inter_fd,4) = 0xFF000000; //"invalid" color signals standard broadcast.
+ WFIFOL(inter_fd,8) = 0x65756c62;
+ memcpy(WFIFOP(inter_fd,4+lp), mes, len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_announce(char* mes,int len, unsigned long color, int flag)
+{
+ // Send to the local players
+ if(color == 0xFE000000) // This is main chat message [LuzZza]
+ clif_MainChatMessage(mes);
+ else
+ clif_announce(NULL, mes, len, color, flag);
+
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 8 + len);
+ WFIFOW(inter_fd,0) = 0x3000;
+ WFIFOW(inter_fd,2) = 8 + len;
+ WFIFOL(inter_fd,4) = color;
+ memcpy(WFIFOP(inter_fd,8), mes, len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+ return 0;
+}
+
+// The transmission of Wisp/Page to inter-server (player not found on this server)
+int intif_wis_message(struct map_session_data *sd, char *nick, char *mes, int mes_len) {
+ nullpo_retr(0, sd);
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,mes_len + 52);
+ WFIFOW(inter_fd,0) = 0x3001;
+ WFIFOW(inter_fd,2) = mes_len + 52;
+ memcpy(WFIFOP(inter_fd,4), sd->status.name, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,4+NAME_LENGTH), nick, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,4+2*NAME_LENGTH), mes, mes_len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_wis_message from %s to %s (message: '%s')\n", sd->status.name, nick, mes);
+
+ return 0;
+}
+
+// The reply of Wisp/page
+int intif_wis_replay(int id, int flag) {
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,7);
+ WFIFOW(inter_fd,0) = 0x3002;
+ WFIFOL(inter_fd,2) = id;
+ WFIFOB(inter_fd,6) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ WFIFOSET(inter_fd,7);
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_wis_replay: id: %d, flag:%d\n", id, flag);
+
+ return 0;
+}
+
+// The transmission of GM only Wisp/Page from server to inter-server
+int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes) {
+ int mes_len;
+ if (CheckForCharServer())
+ return 0;
+ mes_len = strlen(mes) + 1; // + null
+ WFIFOHEAD(inter_fd, mes_len + 30);
+ WFIFOW(inter_fd,0) = 0x3003;
+ WFIFOW(inter_fd,2) = mes_len + 30;
+ memcpy(WFIFOP(inter_fd,4), Wisp_name, NAME_LENGTH);
+ WFIFOW(inter_fd,4+NAME_LENGTH) = (short)min_gm_level;
+ memcpy(WFIFOP(inter_fd,6+NAME_LENGTH), mes, mes_len);
+ WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
+
+ if (battle_config.etc_log)
+ ShowNotice("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", Wisp_name, min_gm_level, mes);
+
+ return 0;
+}
+
+int intif_regtostr(char* str, struct global_reg *reg, int qty) {
+ int len =0, i;
+
+ for (i = 0; i < qty; i++) {
+ len+= sprintf(str+len, "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ len+= sprintf(str+len, "%s", reg[i].value)+1;
+ }
+ return len;
+}
+
+//Request for saving registry values.
+int intif_saveregistry(struct map_session_data *sd, int type)
+{
+ struct global_reg *reg;
+ int count;
+
+ if (CheckForCharServer())
+ return -1;
+
+ switch (type) {
+ case 3: //Character reg
+ reg = sd->save_reg.global;
+ count = sd->save_reg.global_num;
+ sd->state.reg_dirty &= ~0x4;
+ break;
+ case 2: //Account reg
+ reg = sd->save_reg.account;
+ count = sd->save_reg.account_num;
+ sd->state.reg_dirty &= ~0x2;
+ break;
+ case 1: //Account2 reg
+ reg = sd->save_reg.account2;
+ count = sd->save_reg.account2_num;
+ sd->state.reg_dirty &= ~0x1;
+ break;
+ default: //Broken code?
+ if (battle_config.error_log)
+ ShowError("intif_saveregistry: Invalid type %d\n", type);
+ return -1;
+ }
+ WFIFOHEAD(inter_fd, 288 * MAX_REG_NUM+13);
+ WFIFOW(inter_fd,0)=0x3004;
+ WFIFOL(inter_fd,4)=sd->status.account_id;
+ WFIFOL(inter_fd,8)=sd->status.char_id;
+ WFIFOB(inter_fd,12)=type;
+ if(count ==0){
+ WFIFOW(inter_fd,2)=13;
+ }else{
+ int i,p;
+ for (p=13,i = 0; i < count; i++) {
+ if (reg[i].str[0] && reg[i].value != 0) {
+ p+= sprintf(WFIFOP(inter_fd,p), "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place.
+ p+= sprintf(WFIFOP(inter_fd,p), "%s", reg[i].value)+1;
+ }
+ }
+ WFIFOW(inter_fd,2)=p;
+ }
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+//Request the registries for this player.
+int intif_request_registry(struct map_session_data *sd, int flag)
+{
+ nullpo_retr(0, sd);
+
+ sd->save_reg.account2_num = -1;
+ sd->save_reg.account_num = -1;
+ sd->save_reg.global_num = -1;
+
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3005;
+ WFIFOL(inter_fd,2) = sd->status.account_id;
+ WFIFOL(inter_fd,6) = sd->status.char_id;
+ WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2
+ WFIFOB(inter_fd,11) = (flag&2?1:0); //Request Acc Reg
+ WFIFOB(inter_fd,12) = (flag&4?1:0); //Request Char Reg
+ WFIFOSET(inter_fd,13);
+
+ return 0;
+}
+
+// ‘qŒÉƒf[ƒ^—v‹
+int intif_request_storage(int account_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3010;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ‘qŒÉƒf[ƒ^‘—M
+int intif_send_storage(struct storage *stor)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, stor);
+ WFIFOHEAD(inter_fd,sizeof(struct storage)+8);
+ WFIFOW(inter_fd,0) = 0x3011;
+ WFIFOW(inter_fd,2) = sizeof(struct storage)+8;
+ WFIFOL(inter_fd,4) = stor->account_id;
+ memcpy( WFIFOP(inter_fd,8),stor, sizeof(struct storage) );
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_request_guild_storage(int account_id,int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,10);
+ WFIFOW(inter_fd,0) = 0x3018;
+ WFIFOL(inter_fd,2) = account_id;
+ WFIFOL(inter_fd,6) = guild_id;
+ WFIFOSET(inter_fd,10);
+ return 0;
+}
+int intif_send_guild_storage(int account_id,struct guild_storage *gstor)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,sizeof(struct guild_storage)+12);
+ WFIFOW(inter_fd,0) = 0x3019;
+ WFIFOW(inter_fd,2) = sizeof(struct guild_storage)+12;
+ WFIFOL(inter_fd,4) = account_id;
+ WFIFOL(inter_fd,8) = gstor->guild_id;
+ memcpy( WFIFOP(inter_fd,12),gstor, sizeof(struct guild_storage) );
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+// ƒp[ƒeƒB쬗v‹
+int intif_create_party(struct map_session_data *sd,char *name,int item,int item2)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, sd);
+
+ WFIFOHEAD(inter_fd,64);
+ WFIFOW(inter_fd,0) = 0x3020;
+ WFIFOL(inter_fd,2) = sd->status.account_id;
+ WFIFOL(inter_fd,6) = sd->status.char_id;
+ memcpy(WFIFOP(inter_fd,10),name, NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,34),sd->status.name,NAME_LENGTH);
+ WFIFOW(inter_fd,58) = sd->mapindex;
+ WFIFOW(inter_fd,60)= sd->status.base_level;
+ WFIFOB(inter_fd,62)= item;
+ WFIFOB(inter_fd,63)= item2;
+ WFIFOSET(inter_fd,64);
+ return 0;
+}
+// ƒp[ƒeƒBî•ñ—v‹
+int intif_request_partyinfo(int party_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3021;
+ WFIFOL(inter_fd,2) = party_id;
+ WFIFOSET(inter_fd,6);
+// if(battle_config.etc_log)
+// printf("intif: request party info\n");
+ return 0;
+}
+// ƒp[ƒeƒB’ljÁ—v‹
+int intif_party_addmember(int party_id,struct map_session_data *sd)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,42);
+ WFIFOW(inter_fd,0)=0x3022;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=sd->status.account_id;
+ WFIFOL(inter_fd,10)=sd->status.char_id;
+ memcpy(WFIFOP(inter_fd,14),sd->status.name,NAME_LENGTH);
+ WFIFOW(inter_fd,38) = sd->mapindex;
+ WFIFOW(inter_fd,40)=sd->status.base_level;
+ WFIFOSET(inter_fd,42);
+ return 1;
+}
+// ƒp[ƒeƒBÝ’è•ÏX
+int intif_party_changeoption(int party_id,int account_id,int exp,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3023;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOW(inter_fd,10)=exp;
+ WFIFOW(inter_fd,12)=flag;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ƒp[ƒeƒB’E‘Þ—v‹
+int intif_party_leave(int party_id,int account_id, int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3024;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ƒp[ƒeƒBˆÚ“®—v‹
+int intif_party_changemap(struct map_session_data *sd,int online)
+{
+ if (CheckForCharServer())
+ return 0;
+ if(!sd)
+ return 0;
+
+ WFIFOHEAD(inter_fd,19);
+ WFIFOW(inter_fd,0)=0x3025;
+ WFIFOL(inter_fd,2)=sd->status.party_id;
+ WFIFOL(inter_fd,6)=sd->status.account_id;
+ WFIFOL(inter_fd,10)=sd->status.char_id;
+ WFIFOW(inter_fd,14)=sd->mapindex;
+ WFIFOB(inter_fd,16)=online;
+ WFIFOW(inter_fd,17)=sd->status.base_level;
+ WFIFOSET(inter_fd,19);
+ return 1;
+}
+// ƒp[ƒeƒB[‰ðŽU—v‹
+int intif_break_party(int party_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0)=0x3026;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ƒp[ƒeƒB‰ï˜b‘—M
+int intif_party_message(int party_id,int account_id,char *mes,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+// if(battle_config.etc_log)
+// printf("intif_party_message: %s\n",mes);
+ WFIFOHEAD(inter_fd,len + 12);
+ WFIFOW(inter_fd,0)=0x3027;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=party_id;
+ WFIFOL(inter_fd,8)=account_id;
+ memcpy(WFIFOP(inter_fd,12),mes,len);
+ WFIFOSET(inter_fd,len+12);
+ return 0;
+}
+// ƒp[ƒeƒB‹£‡ƒ`ƒFƒbƒN—v‹
+int intif_party_checkconflict(int party_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,10 + NAME_LENGTH);
+ WFIFOW(inter_fd,0)=0x3028;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+
+int intif_party_leaderchange(int party_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd,0)=0x3029;
+ WFIFOL(inter_fd,2)=party_id;
+ WFIFOL(inter_fd,6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+
+
+// ƒMƒ‹ƒh쬗v‹
+int intif_guild_create(const char *name,const struct guild_member *master)
+{
+ if (CheckForCharServer())
+ return 0;
+ nullpo_retr(0, master);
+
+ WFIFOHEAD(inter_fd,sizeof(struct guild_member)+(8+NAME_LENGTH));
+ WFIFOW(inter_fd,0)=0x3030;
+ WFIFOW(inter_fd,2)=sizeof(struct guild_member)+(8+NAME_LENGTH);
+ WFIFOL(inter_fd,4)=master->account_id;
+ memcpy(WFIFOP(inter_fd,8),name,NAME_LENGTH);
+ memcpy(WFIFOP(inter_fd,8+NAME_LENGTH),master,sizeof(struct guild_member));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+// ƒMƒ‹ƒhî•ñ—v‹
+int intif_guild_request_info(int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x3031;
+ WFIFOL(inter_fd,2) = guild_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo’ljÁ—v‹
+int intif_guild_addmember(int guild_id,struct guild_member *m)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,sizeof(struct guild_member)+8);
+ WFIFOW(inter_fd,0) = 0x3032;
+ WFIFOW(inter_fd,2) = sizeof(struct guild_member)+8;
+ WFIFOL(inter_fd,4) = guild_id;
+ memcpy(WFIFOP(inter_fd,8),m,sizeof(struct guild_member));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+
+int intif_guild_change_gm(int guild_id, const char* name, int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 8);
+ WFIFOW(inter_fd, 0)=0x3033;
+ WFIFOW(inter_fd, 2)=len+8;
+ WFIFOL(inter_fd, 4)=guild_id;
+ memcpy(WFIFOP(inter_fd,8),name,len);
+ WFIFOSET(inter_fd,len+8);
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒo’E‘Þ/’Ç•ú—v‹
+int intif_guild_leave(int guild_id,int account_id,int char_id,int flag,const char *mes)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 55);
+ WFIFOW(inter_fd, 0) = 0x3034;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOL(inter_fd, 6) = account_id;
+ WFIFOL(inter_fd,10) = char_id;
+ WFIFOB(inter_fd,14) = flag;
+ memcpy(WFIFOP(inter_fd,15),mes,40);
+ WFIFOSET(inter_fd,55);
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo‚̃Iƒ“ƒ‰ƒCƒ“ó‹µ/LvXV—v‹
+int intif_guild_memberinfoshort(int guild_id,
+ int account_id,int char_id,int online,int lv,int class_)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 19);
+ WFIFOW(inter_fd, 0) = 0x3035;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOL(inter_fd, 6) = account_id;
+ WFIFOL(inter_fd,10) = char_id;
+ WFIFOB(inter_fd,14) = online;
+ WFIFOW(inter_fd,15) = lv;
+ WFIFOW(inter_fd,17) = class_;
+ WFIFOSET(inter_fd,19);
+ return 0;
+}
+// ƒMƒ‹ƒh‰ðŽU’Ê’m
+int intif_guild_break(int guild_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 6);
+ WFIFOW(inter_fd, 0) = 0x3036;
+ WFIFOL(inter_fd, 2) = guild_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+// ƒMƒ‹ƒh‰ï˜b‘—M
+int intif_guild_message(int guild_id,int account_id,char *mes,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 12);
+ WFIFOW(inter_fd,0)=0x3037;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=account_id;
+ memcpy(WFIFOP(inter_fd,12),mes,len);
+ WFIFOSET(inter_fd,len+12);
+
+ return 0;
+}
+// ƒMƒ‹ƒh‹£‡ƒ`ƒFƒbƒN—v‹
+int intif_guild_checkconflict(int guild_id,int account_id,int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, 14);
+ WFIFOW(inter_fd, 0)=0x3038;
+ WFIFOL(inter_fd, 2)=guild_id;
+ WFIFOL(inter_fd, 6)=account_id;
+ WFIFOL(inter_fd,10)=char_id;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ƒMƒ‹ƒhŠî–{î•ñ•ÏX—v‹
+int intif_guild_change_basicinfo(int guild_id,int type,const void *data,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 10);
+ WFIFOW(inter_fd,0)=0x3039;
+ WFIFOW(inter_fd,2)=len+10;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOW(inter_fd,8)=type;
+ memcpy(WFIFOP(inter_fd,10),data,len);
+ WFIFOSET(inter_fd,len+10);
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒoî•ñ•ÏX—v‹
+int intif_guild_change_memberinfo(int guild_id,int account_id,int char_id,
+ int type,const void *data,int len)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, len + 18);
+ WFIFOW(inter_fd, 0)=0x303a;
+ WFIFOW(inter_fd, 2)=len+18;
+ WFIFOL(inter_fd, 4)=guild_id;
+ WFIFOL(inter_fd, 8)=account_id;
+ WFIFOL(inter_fd,12)=char_id;
+ WFIFOW(inter_fd,16)=type;
+ memcpy(WFIFOP(inter_fd,18),data,len);
+ WFIFOSET(inter_fd,len+18);
+ return 0;
+}
+// ƒMƒ‹ƒh–ðE•ÏX—v‹
+int intif_guild_position(int guild_id,int idx,struct guild_position *p)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd, sizeof(struct guild_position)+12);
+ WFIFOW(inter_fd,0)=0x303b;
+ WFIFOW(inter_fd,2)=sizeof(struct guild_position)+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=idx;
+ memcpy(WFIFOP(inter_fd,12),p,sizeof(struct guild_position));
+ WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
+ return 0;
+}
+// ƒMƒ‹ƒhƒXƒLƒ‹ƒAƒbƒv—v‹
+int intif_guild_skillup(int guild_id,int skill_num,int account_id,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,14);
+ WFIFOW(inter_fd, 0)=0x303c;
+ WFIFOL(inter_fd, 2)=guild_id;
+ WFIFOL(inter_fd, 6)=skill_num;
+ WFIFOL(inter_fd,10)=account_id;
+ //WFIFOL(inter_fd,14)=flag;
+ WFIFOSET(inter_fd,14);
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿/“G‘Ηv‹
+int intif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2,int flag)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,19);
+ WFIFOW(inter_fd, 0)=0x303d;
+ WFIFOL(inter_fd, 2)=guild_id1;
+ WFIFOL(inter_fd, 6)=guild_id2;
+ WFIFOL(inter_fd,10)=account_id1;
+ WFIFOL(inter_fd,14)=account_id2;
+ WFIFOB(inter_fd,18)=flag;
+ WFIFOSET(inter_fd,19);
+ return 0;
+}
+// ƒMƒ‹ƒh’m•ÏX—v‹
+int intif_guild_notice(int guild_id,const char *mes1,const char *mes2)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,186);
+ WFIFOW(inter_fd,0)=0x303e;
+ WFIFOL(inter_fd,2)=guild_id;
+ memcpy(WFIFOP(inter_fd,6),mes1,60);
+ memcpy(WFIFOP(inter_fd,66),mes2,120);
+ WFIFOSET(inter_fd,186);
+ return 0;
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX—v‹
+int intif_guild_emblem(int guild_id,int len,const char *data)
+{
+ if (CheckForCharServer())
+ return 0;
+ if(guild_id<=0 || len<0 || len>2000)
+ return 0;
+ WFIFOHEAD(inter_fd,len + 12);
+ WFIFOW(inter_fd,0)=0x303f;
+ WFIFOW(inter_fd,2)=len+12;
+ WFIFOL(inter_fd,4)=guild_id;
+ WFIFOL(inter_fd,8)=0;
+ memcpy(WFIFOP(inter_fd,12),data,len);
+ WFIFOSET(inter_fd,len+12);
+ return 0;
+}
+//Œ»Ý‚̃Mƒ‹ƒhéè—̃Mƒ‹ƒh‚𒲂ׂé
+int intif_guild_castle_dataload(int castle_id,int index)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,5);
+ WFIFOW(inter_fd,0)=0x3040;
+ WFIFOW(inter_fd,2)=castle_id;
+ WFIFOB(inter_fd,4)=index;
+ WFIFOSET(inter_fd,5);
+ return 0;
+}
+
+//ƒMƒ‹ƒhéè—̃Mƒ‹ƒh•ÏX—v‹
+int intif_guild_castle_datasave(int castle_id,int index, int value)
+{
+ if (CheckForCharServer())
+ return 0;
+ WFIFOHEAD(inter_fd,9);
+ WFIFOW(inter_fd,0)=0x3041;
+ WFIFOW(inter_fd,2)=castle_id;
+ WFIFOB(inter_fd,4)=index;
+ WFIFOL(inter_fd,5)=value;
+ WFIFOSET(inter_fd,9);
+ return 0;
+}
+
+//-----------------------------------------------------------------
+// Packets receive from inter server
+
+// Wisp/Page reception
+int intif_parse_WisMessage(int fd) { // rewritten by [Yor]
+ struct map_session_data* sd;
+ char *wisp_source;
+ char name[NAME_LENGTH];
+ int id, i;
+ RFIFOHEAD(fd);
+ id=RFIFOL(fd,4);
+ i=0; //,j=0;
+
+// if(battle_config.etc_log)
+// printf("intif_parse_wismessage: %d %s %s %s\n",id,RFIFOP(fd,6),RFIFOP(fd,30),RFIFOP(fd,54) );
+ memcpy(name, RFIFOP(fd,32), NAME_LENGTH);
+ name[NAME_LENGTH-1] = '\0'; //In case name arrived without it's terminator. [Skotlex]
+ sd=(struct map_session_data *) map_nick2sd(name); // ‘—Mæ‚ð’T‚·
+ if(sd!=NULL && strcmp(sd->status.name, name) == 0){
+ if(sd->ignoreAll == 1)
+ intif_wis_replay(RFIFOL(fd,4), 2); // ŽóM‹‘”Û
+ else {
+ wisp_source = (char *) RFIFOP(fd,8); // speed up [Yor]
+ for(i=0;i<MAX_IGNORE_LIST;i++){ //‹‘”ÛƒŠƒXƒg‚É–¼‘O‚ª‚ ‚é‚©‚Ç‚¤‚©”»’肵‚Ä‚ ‚ê‚΋‘”Û
+ if(strcmp(sd->ignore[i].name, wisp_source)==0){
+ break;
+ }
+ }
+ if(i==MAX_IGNORE_LIST) // run out of list, so we are not ignored
+ {
+ clif_wis_message(sd->fd, wisp_source, (char*)RFIFOP(fd,56),RFIFOW(fd,2)-56);
+ intif_wis_replay(id,0); // ‘—M¬Œ÷
+ }
+ else
+ intif_wis_replay(id, 2); // ŽóM‹‘”Û
+ }
+ }else
+ intif_wis_replay(id,1); // ‚»‚ñ‚Èl‚¢‚Ü‚¹‚ñ
+ return 0;
+}
+
+// Wisp/page transmission result reception
+int intif_parse_WisEnd(int fd) {
+ struct map_session_data* sd;
+ RFIFOHEAD(fd);
+
+ if (battle_config.etc_log)
+ ShowInfo("intif_parse_wisend: player: %s, flag: %d\n", RFIFOP(fd,2), RFIFOB(fd,26)); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ sd = (struct map_session_data *)map_nick2sd((char *) RFIFOP(fd,2));
+ if (sd != NULL)
+ clif_wis_end(sd->fd, RFIFOB(fd,26));
+
+ return 0;
+}
+
+// Received wisp message from map-server via char-server for ALL gm
+int mapif_parse_WisToGM(int fd) { // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B
+ int i, min_gm_level, mes_len;
+ struct map_session_data *pl_sd;
+ char Wisp_name[NAME_LENGTH];
+ char mbuf[255];
+ char *message;
+ RFIFOHEAD(fd);
+
+ mes_len = RFIFOW(fd,2) - 30;
+ message = (char *) (mes_len >= 255 ? (char *) aMallocA(mes_len) : mbuf);
+
+ min_gm_level = (int)RFIFOW(fd,28);
+ memcpy(Wisp_name, RFIFOP(fd,4), NAME_LENGTH);
+ Wisp_name[NAME_LENGTH-1] = '\0';
+ memcpy(message, RFIFOP(fd,30), mes_len);
+ message[mes_len-1] = '\0';
+ // information is sended to all online GM
+ for (i = 0; i < fd_max; i++)
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth)
+ if (pc_isGM(pl_sd) >= min_gm_level)
+ clif_wis_message(i, Wisp_name, message, strlen(message) + 1);
+
+ if (message != mbuf)
+ aFree(message);
+
+ return 0;
+}
+
+// ƒAƒJƒEƒ“ƒg•Ï”’Ê’m
+int intif_parse_Registers(int fd) {
+ int j,p,len,max, flag;
+ struct map_session_data *sd;
+ struct global_reg *reg;
+ int *qty;
+ RFIFOHEAD(fd);
+
+ if( (sd=map_id2sd(RFIFOL(fd,4)))==NULL)
+ return 1;
+
+ if (RFIFOB(fd,12) == 3 && sd->status.char_id != RFIFOL(fd,8))
+ return 1; //Character registry from another character.
+
+ flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1);
+
+ switch (RFIFOB(fd,12)) {
+ case 3: //Character Registry
+ reg = sd->save_reg.global;
+ qty = &sd->save_reg.global_num;
+ max = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account Registry
+ reg = sd->save_reg.account;
+ qty = &sd->save_reg.account_num;
+ max = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 Registry
+ reg = sd->save_reg.account2;
+ qty = &sd->save_reg.account2_num;
+ max = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ if (battle_config.error_log)
+ ShowError("intif_parse_Registers: Unrecognized type %d\n",RFIFOB(fd,12));
+ return 0;
+ }
+ for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){
+ sscanf(RFIFOP(fd,p), "%31c%n", reg[j].str,&len);
+ reg[j].str[len]='\0';
+ p += len+1; //+1 to skip the '\0' between strings.
+ sscanf(RFIFOP(fd,p), "%255c%n", reg[j].value,&len);
+ reg[j].value[len]='\0';
+ p += len+1;
+ }
+ *qty = j;
+
+ if (flag && sd->save_reg.global_num > -1 && sd->save_reg.account_num > -1 && sd->save_reg.account2_num > -1)
+ pc_reg_received(sd); //Received all registry values, execute init scripts and what-not. [Skotlex]
+ return 1;
+}
+
+// ‘qŒÉƒf[ƒ^ŽóM
+int intif_parse_LoadStorage(int fd) {
+ struct storage *stor;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ stor = account2storage( RFIFOL(fd,4));
+
+ if (stor->storage_status == 1) { // Already open.. lets ignore this update
+ if (battle_config.error_log)
+ ShowWarning("intif_parse_LoadStorage: storage received for a client already open\n");
+ return 0;
+ }
+
+ if (RFIFOW(fd,2)-8 != sizeof(struct storage)) {
+ if (battle_config.error_log)
+ ShowError("intif_parse_LoadStorage: data size error %d %d\n", RFIFOW(fd,2)-8, sizeof(struct storage));
+ return 1;
+ }
+ sd=map_id2sd( RFIFOL(fd,4) );
+ if(sd==NULL){
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadStorage: user not found %d\n",RFIFOL(fd,4));
+ return 1;
+ }
+ if(battle_config.save_log)
+ ShowInfo("intif_openstorage: %d\n",RFIFOL(fd,4) );
+ memcpy(stor,RFIFOP(fd,8),sizeof(struct storage));
+ stor->dirty=0;
+ stor->storage_status=1;
+ sd->state.storage_flag = 1;
+ clif_storageitemlist(sd,stor);
+ clif_storageequiplist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+
+ return 0;
+}
+
+// ‘qŒÉƒf[ƒ^‘—M¬Œ÷
+int intif_parse_SaveStorage(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.save_log)
+ ShowInfo("intif_savestorage: done %d %d\n",RFIFOL(fd,2),RFIFOB(fd,6) );
+ storage_storage_saved(RFIFOL(fd,2));
+ return 0;
+}
+
+int intif_parse_LoadGuildStorage(int fd)
+{
+ struct guild_storage *gstor;
+ struct map_session_data *sd;
+ int guild_id;
+ RFIFOHEAD(fd);
+ guild_id = RFIFOL(fd,8);
+ if(guild_id > 0) {
+ gstor=guild2storage(guild_id);
+ if(!gstor) {
+ if(battle_config.error_log)
+ ShowWarning("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id);
+ return 1;
+ }
+ if( RFIFOW(fd,2)-12 != sizeof(struct guild_storage) ){
+ gstor->storage_status = 0;
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-12 , sizeof(struct guild_storage));
+ return 1;
+ }
+ sd=map_id2sd( RFIFOL(fd,4) );
+ if(sd==NULL){
+ if(battle_config.error_log)
+ ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4));
+ return 1;
+ }
+ if(battle_config.save_log)
+ ShowInfo("intif_open_guild_storage: %d\n",RFIFOL(fd,4) );
+ memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage));
+ gstor->storage_status = 1;
+ sd->state.storage_flag = 2;
+ clif_guildstorageitemlist(sd,gstor);
+ clif_guildstorageequiplist(sd,gstor);
+ clif_updateguildstorageamount(sd,gstor);
+ }
+ return 0;
+}
+int intif_parse_SaveGuildStorage(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.save_log) {
+ ShowInfo("intif_save_guild_storage: done %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOB(fd,10) );
+ }
+ storage_guild_storagesaved(RFIFOL(fd,2), RFIFOL(fd,6));
+ return 0;
+}
+
+// ƒp[ƒeƒB쬉”Û
+int intif_parse_PartyCreated(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party created by account %d\n\n", RFIFOL(fd,2));
+ party_created(RFIFOL(fd,2), RFIFOL(fd,6),RFIFOB(fd,10),RFIFOL(fd,11), (char *)RFIFOP(fd,15));
+ return 0;
+}
+// ƒp[ƒeƒBî•ñ
+int intif_parse_PartyInfo(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)==8){
+ if(battle_config.error_log)
+ ShowWarning("intif: party noinfo %d\n",RFIFOL(fd,4));
+ party_recv_noinfo(RFIFOL(fd,4));
+ return 0;
+ }
+
+// printf("intif: party info %d\n",RFIFOL(fd,4));
+ if( RFIFOW(fd,2)!=sizeof(struct party)+4 ){
+ if(battle_config.error_log)
+ ShowError("intif: party info : data size error %d %d %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct party)+4);
+ }
+ party_recv_info((struct party *)RFIFOP(fd,4));
+ return 0;
+}
+// ƒp[ƒeƒB’ljÁ’Ê’m
+int intif_parse_PartyMemberAdded(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party member added Party (%d), Account(%d), Char(%d)\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ party_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10), RFIFOB(fd, 14));
+ return 0;
+}
+// ƒp[ƒeƒBÝ’è•ÏX’Ê’m
+int intif_parse_PartyOptionChanged(int fd)
+{
+ RFIFOHEAD(fd);
+ party_optionchanged(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOB(fd,14));
+ return 0;
+}
+// ƒp[ƒeƒB’E‘Þ’Ê’m
+int intif_parse_PartyMemberLeaved(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: party member leaved: Party(%d), Account(%d), Char(%d)\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ party_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ return 0;
+}
+// ƒp[ƒeƒB‰ðŽU’Ê’m
+int intif_parse_PartyBroken(int fd)
+{
+ RFIFOHEAD(fd);
+ party_broken(RFIFOL(fd,2));
+ return 0;
+}
+// ƒp[ƒeƒBˆÚ“®’Ê’m
+int intif_parse_PartyMove(int fd)
+{
+ RFIFOHEAD(fd);
+ party_recv_movemap(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOW(fd,14),RFIFOB(fd,16),RFIFOW(fd,17));
+ return 0;
+}
+// ƒp[ƒeƒBƒƒbƒZ[ƒW
+int intif_parse_PartyMessage(int fd)
+{
+ RFIFOHEAD(fd);
+// if(battle_config.etc_log)
+// printf("intif_parse_PartyMessage: %s\n",RFIFOP(fd,12));
+ party_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),(char *) RFIFOP(fd,12),RFIFOW(fd,2)-12);
+ return 0;
+}
+
+// ƒMƒ‹ƒh쬉”Û
+int intif_parse_GuildCreated(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_created(RFIFOL(fd,2),RFIFOL(fd,6));
+ return 0;
+}
+// ƒMƒ‹ƒhî•ñ
+int intif_parse_GuildInfo(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)==8){
+ if(battle_config.error_log)
+ ShowWarning("intif: guild noinfo %d\n",RFIFOL(fd,4));
+ guild_recv_noinfo(RFIFOL(fd,4));
+ return 0;
+ }
+
+// if(battle_config.etc_log)
+// printf("intif: guild info %d\n",RFIFOL(fd,4));
+ if( RFIFOW(fd,2)!=sizeof(struct guild)+4 ){
+ if(battle_config.error_log)
+ ShowError("intif: guild info : data size error Gid: %d recv size: %d Expected size: %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild)+4);
+ }
+ guild_recv_info((struct guild *)RFIFOP(fd,4));
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo’ljÁ’Ê’m
+int intif_parse_GuildMemberAdded(int fd)
+{
+ RFIFOHEAD(fd);
+ if(battle_config.etc_log)
+ ShowInfo("intif: guild member added %d %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14));
+ guild_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14));
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒo’E‘Þ/’Ç•ú’Ê’m
+int intif_parse_GuildMemberLeaved(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),
+ (char *) RFIFOP(fd,55), (char *) RFIFOP(fd,15));
+ return 0;
+}
+
+// ƒMƒ‹ƒhƒƒ“ƒoƒIƒ“ƒ‰ƒCƒ“ó‘Ô/Lv•ÏX’Ê’m
+int intif_parse_GuildMemberInfoShort(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_recv_memberinfoshort(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17));
+ return 0;
+}
+// ƒMƒ‹ƒh‰ðŽU’Ê’m
+int intif_parse_GuildBroken(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_broken(RFIFOL(fd,2),RFIFOB(fd,6));
+ return 0;
+}
+
+// ƒMƒ‹ƒhŠî–{î•ñ•ÏX’Ê’m
+int intif_parse_GuildBasicInfoChanged(int fd)
+{
+ int type, guild_id, dd;
+ void *data;
+ struct guild *g;
+ short dw;
+ RFIFOHEAD(fd);
+ type=RFIFOW(fd,8);
+ guild_id=RFIFOL(fd,4);
+ data=RFIFOP(fd,10);
+ g=guild_search(guild_id);
+ dw=*((short *)data);
+ dd=*((int *)data);
+ if( g==NULL )
+ return 0;
+ switch(type){
+ case GBI_EXP: g->exp=dd; break;
+ case GBI_GUILDLV: g->guild_lv=dw; break;
+ case GBI_SKILLPOINT: g->skill_point=dd; break;
+ }
+ return 0;
+}
+// ƒMƒ‹ƒhƒƒ“ƒoî•ñ•ÏX’Ê’m
+int intif_parse_GuildMemberInfoChanged(int fd)
+{
+ int type, guild_id, account_id, char_id, idx, dd;
+ void* data;
+ struct guild *g;
+ RFIFOHEAD(fd);
+ type=RFIFOW(fd,16);
+ guild_id=RFIFOL(fd,4);
+ account_id=RFIFOL(fd,8);
+ char_id=RFIFOL(fd,12);
+ data=RFIFOP(fd,18);
+ g=guild_search(guild_id);
+ dd=*((int *)data);
+ if( g==NULL )
+ return 0;
+ idx=guild_getindex(g,account_id,char_id);
+ switch(type){
+ case GMI_POSITION:
+ g->member[idx].position=dd;
+ guild_memberposition_changed(g,idx,dd);
+ break;
+ case GMI_EXP:
+ g->member[idx].exp=dd;
+ break;
+ case GMI_HAIR:
+ g->member[idx].hair=dd;
+ break;
+ case GMI_HAIR_COLOR:
+ g->member[idx].hair_color=dd;
+ break;
+ case GMI_GENDER:
+ g->member[idx].gender=dd;
+ break;
+ case GMI_CLASS:
+ g->member[idx].class_=dd;
+ break;
+ case GMI_LEVEL:
+ g->member[idx].lv=dd;
+ break;
+ }
+ return 0;
+}
+
+// ƒMƒ‹ƒh–ðE•ÏX’Ê’m
+int intif_parse_GuildPosition(int fd)
+{
+ RFIFOHEAD(fd);
+ if( RFIFOW(fd,2)!=sizeof(struct guild_position)+12 ){
+ if(battle_config.error_log)
+ ShowError("intif: guild info : data size error\n %d %d %d",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild_position)+12);
+ }
+ guild_position_changed(RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12));
+ return 0;
+}
+// ƒMƒ‹ƒhƒXƒLƒ‹Š„‚èU‚è’Ê’m
+int intif_parse_GuildSkillUp(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_skillupack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10));
+ return 0;
+}
+// ƒMƒ‹ƒh“¯–¿/“G‘Î’Ê’m
+int intif_parse_GuildAlliance(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_allianceack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14),
+ RFIFOB(fd,18),(char *) RFIFOP(fd,19),(char *) RFIFOP(fd,43));
+ return 0;
+}
+// ƒMƒ‹ƒh’m•ÏX’Ê’m
+int intif_parse_GuildNotice(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_notice_changed(RFIFOL(fd,2),(char *) RFIFOP(fd,6),(char *) RFIFOP(fd,66));
+ return 0;
+}
+// ƒMƒ‹ƒhƒGƒ“ƒuƒŒƒ€•ÏX’Ê’m
+int intif_parse_GuildEmblem(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_emblem_changed(RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8), (char *)RFIFOP(fd,12));
+ return 0;
+}
+// ƒMƒ‹ƒh‰ï˜bŽóM
+int intif_parse_GuildMessage(int fd)
+{
+ RFIFOHEAD(fd);
+ guild_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),(char *) RFIFOP(fd,12),RFIFOW(fd,2)-12);
+ return 0;
+}
+// ƒMƒ‹ƒhéƒf[ƒ^—v‹•ÔM
+int intif_parse_GuildCastleDataLoad(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castledataloadack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5));
+}
+// ƒMƒ‹ƒhéƒf[ƒ^•ÏX’Ê’m
+int intif_parse_GuildCastleDataSave(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castledatasaveack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5));
+}
+
+// ƒMƒ‹ƒhéƒf[ƒ^ˆêŠ‡ŽóM(‰Šú‰»Žž)
+int intif_parse_GuildCastleAllDataLoad(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_castlealldataload(RFIFOW(fd,2),(struct guild_castle *)RFIFOP(fd,4));
+}
+
+int intif_parse_GuildMasterChanged(int fd)
+{
+ RFIFOHEAD(fd);
+ return guild_gm_changed(RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+// pet
+int intif_parse_CreatePet(int fd)
+{
+ RFIFOHEAD(fd);
+ pet_get_egg(RFIFOL(fd,2),RFIFOL(fd,7),RFIFOB(fd,6));
+
+ return 0;
+}
+
+int intif_parse_RecvPetData(int fd)
+{
+ struct s_pet p;
+ int len;
+ RFIFOHEAD(fd);
+ len=RFIFOW(fd,2);
+ if(sizeof(struct s_pet)!=len-9) {
+ if(battle_config.etc_log)
+ ShowError("intif: pet data: data size error %d %d\n",sizeof(struct s_pet),len-9);
+ }
+ else{
+ memcpy(&p,RFIFOP(fd,9),sizeof(struct s_pet));
+ pet_recv_petdata(RFIFOL(fd,4),&p,RFIFOB(fd,8));
+ }
+
+ return 0;
+}
+int intif_parse_SavePetOk(int fd)
+{
+ RFIFOHEAD(fd);
+ if(RFIFOB(fd,6) == 1) {
+ if(battle_config.error_log)
+ ShowError("pet data save failure\n");
+ }
+
+ return 0;
+}
+
+int intif_parse_DeletePetOk(int fd)
+{
+ RFIFOHEAD(fd);
+ if(RFIFOB(fd,2) == 1) {
+ if(battle_config.error_log)
+ ShowError("pet data delete failure\n");
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------
+// inter server‚©‚ç‚Ì’ÊM
+// ƒGƒ‰[‚ª‚ ‚ê‚Î0(false)‚ð•Ô‚·‚±‚Æ
+// ƒpƒPƒbƒg‚ªˆ—‚Å‚«‚ê‚Î1,ƒpƒPƒbƒg’·‚ª‘«‚è‚È‚¯‚ê‚Î2‚ð•Ô‚·‚±‚Æ
+int intif_parse(int fd)
+{
+ int packet_len, cmd;
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ // ƒpƒPƒbƒg‚ÌIDŠm”F
+ if(cmd<0x3800 || cmd>=0x3800+(sizeof(packet_len_table)/sizeof(packet_len_table[0])) ||
+ packet_len_table[cmd-0x3800]==0){
+ return 0;
+ }
+ // ƒpƒPƒbƒg‚Ì’·‚³Šm”F
+ packet_len = packet_len_table[cmd-0x3800];
+ if(packet_len==-1){
+ if(RFIFOREST(fd)<4)
+ return 2;
+ packet_len = RFIFOW(fd,2);
+ }
+// if(battle_config.etc_log)
+// printf("intif_parse %d %x %d %d\n",fd,cmd,packet_len,RFIFOREST(fd));
+ if((int)RFIFOREST(fd)<packet_len){
+ return 2;
+ }
+ // ˆ—•ªŠò
+ switch(cmd){
+ case 0x3800:
+ if (RFIFOL(fd,4) == 0xFF000000) //Normal announce.
+ clif_GMmessage(NULL,(char *) RFIFOP(fd,8),packet_len-8,0);
+ else if (RFIFOL(fd,4) == 0xFE000000) //Main chat message [LuzZza]
+ clif_MainChatMessage((char *)RFIFOP(fd,8));
+ else //Color announce.
+ clif_announce(NULL,(char *) RFIFOP(fd,8),packet_len-8,RFIFOL(fd,4),0);
+ break;
+ case 0x3801: intif_parse_WisMessage(fd); break;
+ case 0x3802: intif_parse_WisEnd(fd); break;
+ case 0x3803: mapif_parse_WisToGM(fd); break;
+ case 0x3804: intif_parse_Registers(fd); break;
+ case 0x3810: intif_parse_LoadStorage(fd); break;
+ case 0x3811: intif_parse_SaveStorage(fd); break;
+ case 0x3818: intif_parse_LoadGuildStorage(fd); break;
+ case 0x3819: intif_parse_SaveGuildStorage(fd); break;
+ case 0x3820: intif_parse_PartyCreated(fd); break;
+ case 0x3821: intif_parse_PartyInfo(fd); break;
+ case 0x3822: intif_parse_PartyMemberAdded(fd); break;
+ case 0x3823: intif_parse_PartyOptionChanged(fd); break;
+ case 0x3824: intif_parse_PartyMemberLeaved(fd); break;
+ case 0x3825: intif_parse_PartyMove(fd); break;
+ case 0x3826: intif_parse_PartyBroken(fd); break;
+ case 0x3827: intif_parse_PartyMessage(fd); break;
+ case 0x3830: intif_parse_GuildCreated(fd); break;
+ case 0x3831: intif_parse_GuildInfo(fd); break;
+ case 0x3832: intif_parse_GuildMemberAdded(fd); break;
+ case 0x3834: intif_parse_GuildMemberLeaved(fd); break;
+ case 0x3835: intif_parse_GuildMemberInfoShort(fd); break;
+ case 0x3836: intif_parse_GuildBroken(fd); break;
+ case 0x3837: intif_parse_GuildMessage(fd); break;
+ case 0x3839: intif_parse_GuildBasicInfoChanged(fd); break;
+ case 0x383a: intif_parse_GuildMemberInfoChanged(fd); break;
+ case 0x383b: intif_parse_GuildPosition(fd); break;
+ case 0x383c: intif_parse_GuildSkillUp(fd); break;
+ case 0x383d: intif_parse_GuildAlliance(fd); break;
+ case 0x383e: intif_parse_GuildNotice(fd); break;
+ case 0x383f: intif_parse_GuildEmblem(fd); break;
+ case 0x3840: intif_parse_GuildCastleDataLoad(fd); break;
+ case 0x3841: intif_parse_GuildCastleDataSave(fd); break;
+ case 0x3842: intif_parse_GuildCastleAllDataLoad(fd); break;
+ case 0x3843: intif_parse_GuildMasterChanged(fd); break;
+ case 0x3880: intif_parse_CreatePet(fd); break;
+ case 0x3881: intif_parse_RecvPetData(fd); break;
+ case 0x3882: intif_parse_SavePetOk(fd); break;
+ case 0x3883: intif_parse_DeletePetOk(fd); break;
+ default:
+ if(battle_config.error_log)
+ ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0));
+ return 0;
+ }
+ // ƒpƒPƒbƒg“Ç‚Ý”ò‚΂µ
+ RFIFOSKIP(fd,packet_len);
+ return 1;
+}
diff --git a/src/map/intif.h b/src/map/intif.h
new file mode 100644
index 000000000..0d0fc563d
--- /dev/null
+++ b/src/map/intif.h
@@ -0,0 +1,61 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INTIF_H_
+#define _INFIF_H_
+
+int intif_parse(int fd);
+
+int intif_GMmessage(char* mes,int len,int flag);
+int intif_announce(char* mes,int len, unsigned long color, int flag);
+
+int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
+int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes);
+
+int intif_saveregistry(struct map_session_data *sd, int type);
+int intif_request_registry(struct map_session_data *sd, int flag);
+
+int intif_request_storage(int account_id);
+int intif_send_storage(struct storage *stor);
+int intif_request_guild_storage(int account_id, int guild_id);
+int intif_send_guild_storage(int account_id, struct guild_storage *gstor);
+
+
+int intif_create_party(struct map_session_data *sd,char *name,int item,int item2);
+int intif_request_partyinfo(int party_id);
+int intif_party_addmember(int party_id, struct map_session_data *sd);
+int intif_party_changeoption(int party_id, int account_id, int exp, int item);
+int intif_party_leave(int party_id,int account_id, int char_id);
+int intif_party_changemap(struct map_session_data *sd, int online);
+int intif_break_party(int party_id);
+int intif_party_message(int party_id, int account_id, char *mes,int len);
+int intif_party_checkconflict(int party_id,int account_id,int char_id);
+int intif_party_leaderchange(int party_id,int account_id,int char_id);
+
+
+int intif_guild_create(const char *name, const struct guild_member *master);
+int intif_guild_request_info(int guild_id);
+int intif_guild_addmember(int guild_id, struct guild_member *m);
+int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes);
+int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_);
+int intif_guild_break(int guild_id);
+int intif_guild_message(int guild_id, int account_id, char *mes, int len);
+int intif_guild_checkconflict(int guild_id, int account_id, int char_id);
+int intif_guild_change_gm(int guild_id, const char* name, int len);
+int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len);
+int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len);
+int intif_guild_position(int guild_id, int idx, struct guild_position *p);
+int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag);
+int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag);
+int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
+int intif_guild_emblem(int guild_id, int len, const char *data);
+int intif_guild_castle_dataload(int castle_id, int index);
+int intif_guild_castle_datasave(int castle_id, int index, int value);
+
+int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
+ short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);
+int intif_request_petdata(int account_id, int char_id, int pet_id);
+int intif_save_petdata(int account_id, struct s_pet *p);
+int intif_delete_petdata(int pet_id);
+
+#endif
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
new file mode 100644
index 000000000..667ab10af
--- /dev/null
+++ b/src/map/itemdb.c
@@ -0,0 +1,1103 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "map.h"
+#include "grfio.h"
+#include "battle.h"
+#include "itemdb.h"
+#include "script.h"
+#include "pc.h"
+
+#define MAX_RANDITEM 2000
+#define MAX_ITEMGROUP 32
+// ** ITEMDB_OVERRIDE_NAME_VERBOSE **
+// ’è‹`‚·‚é‚ÆAitemdb.txt‚Ægrf‚Å–¼‘O‚ªˆÙ‚È‚éê‡A•\Ž¦‚µ‚Ü‚·.
+//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1
+
+static struct dbt* item_db;
+
+static struct random_item_data blue_box[MAX_RANDITEM], violet_box[MAX_RANDITEM], card_album[MAX_RANDITEM], gift_box[MAX_RANDITEM], scroll[MAX_RANDITEM], finding_ore[MAX_RANDITEM];
+static int blue_box_count=0, violet_box_count=0, card_album_count=0, gift_box_count=0, scroll_count=0, finding_ore_count = 0;
+static int blue_box_default=0, violet_box_default=0, card_album_default=0, gift_box_default=0, scroll_default=0, finding_ore_default = 0;
+
+static struct item_group itemgroup_db[MAX_ITEMGROUP];
+
+/*==========================================
+ * –¼‘O‚ÅŒŸõ—p
+ *------------------------------------------
+ */
+// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
+int itemdb_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if( strcmpi(item->name,str)==0 ) //by lupus
+ *dst=item;
+ return 0;
+}
+
+/*==========================================
+ * –¼‘O‚ÅŒŸõ—p
+ *------------------------------------------
+ */
+int itemdb_searchjname_sub(int key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if( strcmpi(item->jname,str)==0 )
+ *dst=item;
+ return 0;
+}
+/*==========================================
+ * –¼‘O‚ÅŒŸõ
+ *------------------------------------------
+ */
+struct item_data* itemdb_searchname(const char *str)
+{
+ struct item_data *item=NULL;
+ item_db->foreach(item_db,itemdb_searchname_sub,str,&item);
+ return item;
+}
+
+/*==========================================
+ * ” ŒnƒAƒCƒeƒ€ŒŸõ
+ *------------------------------------------
+ */
+int itemdb_searchrandomid(int flags)
+{
+ int nameid=0,i,index,count;
+ struct random_item_data *list=NULL;
+
+ struct {
+ int nameid,count;
+ struct random_item_data *list;
+ } data[7];
+
+ // for BCC32 compile error
+ data[0].nameid = 0; data[0].count = 0; data[0].list = NULL;
+ data[1].nameid = blue_box_default; data[1].count = blue_box_count; data[1].list = blue_box;
+ data[2].nameid = violet_box_default; data[2].count = violet_box_count; data[2].list = violet_box;
+ data[3].nameid = card_album_default; data[3].count = card_album_count; data[3].list = card_album;
+ data[4].nameid = gift_box_default; data[4].count = gift_box_count; data[4].list = gift_box;
+ data[5].nameid = scroll_default; data[5].count = scroll_count; data[5].list = scroll;
+ data[6].nameid = finding_ore_default; data[6].count = finding_ore_count; data[6].list = finding_ore;
+
+ if(flags>=1 && flags<=6){
+ nameid=data[flags].nameid;
+ count=data[flags].count;
+ list=data[flags].list;
+
+ if(count > 0) {
+ for(i=0;i<1000;i++) {
+ index = rand()%count;
+ if( rand()%1000000 < list[index].per) {
+ nameid = list[index].nameid;
+ break;
+ }
+ }
+ }
+ }
+ return nameid;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_group (int nameid)
+{
+ int i, j;
+ for (i=0; i < MAX_ITEMGROUP; i++) {
+ for (j=0; j < itemgroup_db[i].qty && itemgroup_db[i].id[j]; j++) {
+ if (itemgroup_db[i].id[j] == nameid)
+ return i;
+ }
+ }
+ return -1;
+}
+int itemdb_searchrandomgroup (int groupid)
+{
+ if (groupid < 0 || groupid >= MAX_ITEMGROUP ||
+ itemgroup_db[groupid].qty == 0 || itemgroup_db[groupid].id[0] == 0)
+ return 0;
+
+ return itemgroup_db[groupid].id[ rand()%itemgroup_db[groupid].qty ];
+}
+
+/*==========================================
+ * DB‚Ì‘¶ÝŠm”F
+ *------------------------------------------
+ */
+struct item_data* itemdb_exists(int nameid)
+{
+ return idb_get(item_db,nameid);
+}
+
+/*==========================================
+ * Converts the jobid from the format in itemdb
+ * to the format used by the map server. [Skotlex]
+ *------------------------------------------
+ */
+static void itemdb_jobid2mapid(unsigned short *bclass, int jobmask)
+{
+ int i;
+ bclass[0]= bclass[1]= bclass[2]= 0;
+ //Base classes
+ for (i = JOB_NOVICE; i <= JOB_THIEF; i++)
+ {
+ if (jobmask & 1<<i)
+ bclass[0] |= 1<<(MAPID_NOVICE+i);
+ }
+ //2-1 classes
+ if (jobmask & 1<<JOB_KNIGHT)
+ bclass[1] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_PRIEST)
+ bclass[1] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_WIZARD)
+ bclass[1] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_BLACKSMITH)
+ bclass[1] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_HUNTER)
+ bclass[1] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ASSASSIN)
+ bclass[1] |= 1<<MAPID_THIEF;
+ //2-2 classes
+ if (jobmask & 1<<JOB_CRUSADER)
+ bclass[2] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_MONK)
+ bclass[2] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_SAGE)
+ bclass[2] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_ALCHEMIST)
+ bclass[2] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_BARD)
+ bclass[2] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_DANCER)
+ bclass[2] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ROGUE)
+ bclass[2] |= 1<<MAPID_THIEF;
+ //Special classes that don't fit above.
+ if (jobmask & 1<<JOB_SUPER_NOVICE)
+ bclass[1] |= 1<<MAPID_NOVICE;
+ if (jobmask & 1<<24) //Taekwon boy
+ bclass[0] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<25) //Star Gladiator
+ bclass[1] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<26) //Soul Linker
+ bclass[2] |= 1<<MAPID_TAEKWON;
+}
+
+static void* create_item_data(DBKey key, va_list args) {
+ struct item_data *id;
+ int nameid = key.i;
+
+ id=(struct item_data *)aCalloc(1,sizeof(struct item_data));
+ id->nameid=nameid;
+ id->value_buy=10;
+ id->value_sell=id->value_buy/2;
+ id->weight=10;
+ id->sex=2;
+ id->class_base[0]=0xff;
+ id->class_base[1]=0xff;
+ id->class_base[2]=0xff;
+ id->class_upper=5;
+
+ if(nameid>500 && nameid<600)
+ id->type=0; //heal item
+ else if(nameid>600 && nameid<700)
+ id->type=2; //use item
+ else if((nameid>700 && nameid<1100) ||
+ (nameid>7000 && nameid<8000))
+ id->type=3; //correction
+ else if(nameid>=1750 && nameid<1771)
+ id->type=10; //arrow
+ else if(nameid>1100 && nameid<2000)
+ id->type=4; //weapon
+ else if((nameid>2100 && nameid<3000) ||
+ (nameid>5000 && nameid<6000))
+ id->type=5; //armor
+ else if(nameid>4000 && nameid<5000)
+ id->type=6; //card
+ else if(nameid>9000 && nameid<10000)
+ id->type=7; //egg
+ else if(nameid>10000)
+ id->type=8; //petequip
+ return id;
+}
+/*==========================================
+ * DB‚ÌŒŸõ
+ *------------------------------------------
+ */
+struct item_data* itemdb_search(int nameid)
+{
+ return idb_ensure(item_db,nameid,create_item_data);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip(int nameid)
+{
+ int type=itemdb_type(nameid);
+ if(type==0 || type==2 || type==3 || type==6 || type==10)
+ return 0;
+ return 1;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip2(struct item_data *data)
+{
+ if(data) {
+ int type=data->type;
+ if(type==0 || type==2 || type==3 || type==6 || type==10)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Trade Restriction functions [Skotlex]
+ *------------------------------------------
+ */
+int itemdb_isdropable(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cantrade(int nameid, int gmlv, int gmlv2)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_canpartnertrade(int nameid, int gmlv, int gmlv2)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&(2|4)) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_cansell(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cancartstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canguildstore(int nameid, int gmlv)
+{
+ struct item_data* item = itemdb_exists(nameid);
+ return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int itemdb_isequip3(int nameid)
+{
+ int type=itemdb_type(nameid);
+ if(type==4 || type==5 || type == 8)
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * ƒ‰ƒ“ƒ_ƒ€ƒAƒCƒeƒ€oŒ»ƒf[ƒ^‚Ì“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static int itemdb_read_randomitem(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,i,j;
+ char *str[10],*p;
+
+ const struct {
+ char filename[64];
+ struct random_item_data *pdata;
+ int *pcount,*pdefault;
+ } data[] = {
+ {"item_bluebox.txt", blue_box, &blue_box_count, &blue_box_default },
+ {"item_violetbox.txt", violet_box, &violet_box_count, &violet_box_default },
+ {"item_cardalbum.txt", card_album, &card_album_count, &card_album_default },
+ {"item_giftbox.txt", gift_box, &gift_box_count, &gift_box_default },
+ {"item_scroll.txt", scroll, &scroll_count, &scroll_default },
+ {"item_findingore.txt", finding_ore,&finding_ore_count, &finding_ore_default },
+ };
+
+ for(i=0;i<sizeof(data)/sizeof(data[0]);i++){
+ struct random_item_data *pd=data[i].pdata;
+ int *pc=data[i].pcount;
+ int *pdefault=data[i].pdefault;
+ char *fn=(char *) data[i].filename;
+
+ *pdefault = 0;
+ sprintf(line, "%s/%s", db_path, fn);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n",line);
+ continue;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<3 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<0 || nameid>=20000)
+ continue;
+ if(nameid == 0) {
+ if(str[2])
+ *pdefault = atoi(str[2]);
+ continue;
+ }
+
+ if(str[2]){
+ pd[ *pc ].nameid = nameid;
+ pd[(*pc)++].per = atoi(str[2]);
+ }
+
+ if(ln >= MAX_RANDITEM)
+ break;
+ ln++;
+ }
+ fclose(fp);
+ if (*pc > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",*pc,fn);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€Žg—p‰Â”\ƒtƒ‰ƒO‚̃I[ƒo[ƒ‰ƒCƒh
+ *------------------------------------------
+ */
+static int itemdb_read_itemavail (void)
+{
+ FILE *fp;
+ int nameid, j, k, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_avail.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 2 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 2 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || nameid >= 20000 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ k = atoi(str[1]);
+ if (k > 0) {
+ id->flag.available = 1;
+ id->view_id = k;
+ } else
+ id->flag.available = 0;
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * read item group data
+ *------------------------------------------
+ */
+static int itemdb_read_itemgroup(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int groupid,j,k;
+ char *str[31],*p;
+
+ sprintf(line, "%s/item_group_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<31 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ groupid = atoi(str[0]);
+ if (groupid < 0 || groupid >= MAX_ITEMGROUP)
+ continue;
+
+ for (j=1; j<=30; j++) {
+ if (!str[j])
+ break;
+ k=atoi(str[j]);
+ if (k < 0 || k >= 20000 || !itemdb_exists(k))
+ continue;
+ //printf ("%d[%d] = %d\n", groupid, j-1, k);
+ itemgroup_db[groupid].id[j-1] = k;
+ itemgroup_db[groupid].qty=j;
+ }
+ for (j=1; j<30; j++) { //Cleanup the contents. [Skotlex]
+ if (itemgroup_db[groupid].id[j-1] == 0 &&
+ itemgroup_db[groupid].id[j] != 0)
+ {
+ itemgroup_db[groupid].id[j-1] = itemgroup_db[groupid].id[j];
+ itemgroup_db[groupid].id[j] = 0;
+ itemgroup_db[groupid].qty = j;
+ }
+ }
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_group_db.txt");
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚Ì–¼‘Oƒe[ƒuƒ‹‚ð“Ç‚Ýž‚Þ
+ *------------------------------------------
+ */
+static int itemdb_read_itemnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\idnum2itemdisplaynametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64]; //Why 64? What's this for, other than holding an item's name? [Skotlex]
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+
+#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE
+ if( itemdb_exists(nameid) &&
+ strncmp(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH)!=0 ){
+ ShowNotice("[override] %d %s => %s\n",nameid
+ ,itemdb_search(nameid)->jname,buf2);
+ }
+#endif
+
+ memcpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒhƒCƒ‰ƒXƒg‚̃Šƒ\[ƒX–¼‘Oƒe[ƒuƒ‹‚ð“Ç‚Ýž‚Þ
+ *------------------------------------------
+ */
+static int itemdb_read_cardillustnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64];
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+ strcat(buf2,".bmp");
+ memcpy(itemdb_search(nameid)->cardillustname,buf2,64);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt");
+
+ return 0;
+}
+
+//
+// ‰Šú‰»
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslottable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslottable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s; ) {
+ int nameid, equip;
+ struct item_data* item;
+ sscanf(p, "%d#%d#", &nameid, &equip);
+ item = itemdb_search(nameid);
+ if (item && itemdb_isequip2(item))
+ item->equip = equip;
+ p = strchr(p, 10);
+ if(!p) break;
+ p++;
+ p=strchr(p, 10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslotcounttable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s;){
+ int nameid, slot;
+ sscanf(p, "%d#%d#", &nameid, &slot);
+ if (slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS);
+ slot = MAX_SLOTS;
+ }
+ itemdb_slot(nameid) = slot;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * ‘•”õ§ŒÀƒtƒ@ƒCƒ‹“Ç‚Ýo‚µ
+ *------------------------------------------
+ */
+static int itemdb_read_noequip(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,j;
+ char *str[32],*p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_noequip.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<2 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>=20000 || !(id=itemdb_exists(nameid)))
+ continue;
+
+ id->flag.no_equip=atoi(str[1]);
+
+ ln++;
+
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Reads item trade restrictions [Skotlex]
+ *------------------------------------------
+ */
+static int itemdb_read_itemtrade(void)
+{
+ FILE *fp;
+ int nameid, j, flag, gmlv, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_trade.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 3 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 3 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || nameid >= 20000 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ flag = atoi(str[1]);
+ gmlv = atoi(str[2]);
+
+ if (flag > 0 && flag < 128 && gmlv > 0) { //Check range
+ id->flag.trade_restriction = flag;
+ id->gm_lv_trade_override = gmlv;
+ ln++;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt");
+
+ return 0;
+}
+
+/*======================================
+ * Applies gender restrictions according to settings. [Skotlex]
+ *======================================
+ */
+static int itemdb_gendercheck(struct item_data *id)
+{
+ if (id->nameid == WEDDING_RING_M) //Grom Ring
+ return 1;
+ if (id->nameid == WEDDING_RING_F) //Bride Ring
+ return 0;
+ if (id->look == 13 && id->type == 4) //Musical instruments are always male-only
+ return 1;
+ if (id->look == 14 && id->type == 4) //Whips are always female-only
+ return 0;
+
+ return (battle_config.ignore_items_gender?2:id->sex);
+}
+#ifndef TXT_ONLY
+
+/*======================================
+* SQL
+*===================================
+*/
+static int itemdb_read_sqldb(void)
+{
+ unsigned short nameid;
+ struct item_data *id;
+ char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator
+ char *item_db_name[] = { item_db_db, item_db2_db };
+ long unsigned int ln = 0;
+ int i;
+
+ // ----------
+
+ for (i = 0; i < 2; i++) {
+ sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]);
+
+ // Execute the query; if the query execution succeeded...
+ if (mysql_query(&mmysql_handle, tmp_sql) == 0) {
+ sql_res = mysql_store_result(&mmysql_handle);
+
+ // If the storage of the query result succeeded...
+ if (sql_res) {
+ // Parse each row in the query result into sql_row
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ {
+ /* +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
+ +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+
+ | id | name_english | name_japanese | type | price_buy | price_sell | weight | attack | defence | range | slots | equip_jobs | equip_upper | equip_genders | equip_locations | weapon_level | equip_level | refineable | view | script |
+ +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+ */
+
+ nameid = atoi(sql_row[0]);
+
+ // If the identifier is not within the valid range, process the next row
+ if (nameid == 0 || nameid >= 20000)
+ continue;
+
+ ln++;
+
+ // ----------
+ id = itemdb_search(nameid);
+
+ memcpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1);
+ memcpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1);
+
+ id->type = atoi(sql_row[3]);
+ if (id->type == 11)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = 2;
+ id->flag.delay_consume=1;
+ }
+
+ // If price_buy is not NULL and price_sell is not NULL...
+ if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is not NULL and price_sell is NULL...
+ else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[4]) / 2;
+ }
+ // If price_buy is NULL and price_sell is not NULL...
+ else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[5]) * 2;
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is NULL and price_sell is NULL...
+ if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = 0;
+ id->value_sell = 0;
+ }
+
+ id->weight = atoi(sql_row[6]);
+ id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0;
+ id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0;
+ id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0;
+ id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0;
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? atoi(sql_row[11]) : 0);
+ id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0;
+ id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0;
+ id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0;
+ id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0;
+ id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0;
+ id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1;
+ id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0;
+ id->view_id = 0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ // ----------
+
+ if (id->script)
+ aFree(id->script);
+ if (sql_row[19] != NULL) {
+ if (sql_row[19][0] == '{')
+ id->script = parse_script((unsigned char *) sql_row[19], 0);
+ else {
+ sprintf(script, "{%s}", sql_row[19]);
+ id->script = parse_script((unsigned char *) script, 0);
+ }
+ } else id->script = NULL;
+
+ // ----------
+
+ id->flag.available = 1;
+ id->flag.value_notdc = 0;
+ id->flag.value_notoc = 0;
+ }
+
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]);
+ ln = 0;
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ // Free the query result
+ mysql_free_result(sql_res);
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+/*==========================================
+ * ƒAƒCƒeƒ€ƒf[ƒ^ƒx[ƒX‚Ì“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static int itemdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0,lines=0;
+ int nameid,j;
+ char *str[32],*p,*np;
+ struct item_data *id;
+ int i=0;
+ char *filename[]={ "item_db.txt","item_db2.txt" };
+
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowFatalError("can't read %s\n",line);
+ exit(1);
+ }
+
+ lines=0;
+ while(fgets(line,1020,fp)){
+ lines++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,np=p=line;j<19 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p){ *p++=0; np=p; }
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>=20000)
+ continue;
+ if (j < 19)
+ { //Crash-fix on broken item lines. [Skotlex]
+ ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid);
+ continue;
+ }
+ ln++;
+
+ //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View
+ id=itemdb_search(nameid);
+ memcpy(id->name, str[1], ITEM_NAME_LENGTH-1);
+ memcpy(id->jname, str[2], ITEM_NAME_LENGTH-1);
+ id->type=atoi(str[3]);
+ if (id->type == 11)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = 2;
+ id->flag.delay_consume=1;
+ }
+
+ {
+ int buy = atoi(str[4]), sell = atoi(str[5]);
+ // if buying price > selling price * 2 consider it valid and don't change it [celest]
+ if (buy && sell && buy > sell*2){
+ id->value_buy = buy;
+ id->value_sell = sell;
+ } else {
+ // buy‚sell*2 ‚Í item_value_db.txt ‚ÅŽw’肵‚Ä‚­‚¾‚³‚¢B
+ if (sell) { // sell’l‚ð—Dæ‚Æ‚·‚é
+ id->value_buy = sell*2;
+ id->value_sell = sell;
+ } else {
+ id->value_buy = buy;
+ id->value_sell = buy/2;
+ }
+ }
+ // check for bad prices that can possibly cause exploits
+ if (id->value_buy*75/100 < id->value_sell*124/100) {
+ ShowWarning ("Item %s [%d] buying:%d < selling:%d\n",
+ id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100);
+ }
+ }
+ id->weight=atoi(str[6]);
+ id->atk=atoi(str[7]);
+ id->def=atoi(str[8]);
+ id->range=atoi(str[9]);
+ id->slot=atoi(str[10]);
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, atoi(str[11]));
+ id->class_upper = atoi(str[12]);
+ id->sex = atoi(str[13]);
+ if(id->equip != atoi(str[14])){
+ id->equip=atoi(str[14]);
+ }
+ id->wlv=atoi(str[15]);
+ id->elv=atoi(str[16]);
+ id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0
+ id->look=atoi(str[18]);
+ id->flag.available=1;
+ id->flag.value_notdc=0;
+ id->flag.value_notoc=0;
+ id->view_id=0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ if (id->script) {
+ aFree(id->script);
+ id->script=NULL;
+ }
+ if((p=strchr(np,'{'))==NULL)
+ continue;
+ id->script = parse_script((unsigned char *) p,lines);
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]);
+ }
+ ln=0; // reset to 0
+ }
+ return 0;
+}
+
+/*====================================
+ * Removed item_value_db, don't re-add
+ *------------------------------------
+ */
+static void itemdb_read(void)
+{
+#ifndef TXT_ONLY
+ if (db_use_sqldbs)
+ itemdb_read_sqldb();
+ else
+#endif
+ itemdb_readdb();
+
+ itemdb_read_itemgroup();
+ itemdb_read_randomitem();
+ itemdb_read_itemavail();
+ itemdb_read_noequip();
+ itemdb_read_itemtrade();
+ if (battle_config.cardillust_read_grffile)
+ itemdb_read_cardillustnametable();
+ if (battle_config.item_equip_override_grffile)
+ itemdb_read_itemslottable();
+ if (battle_config.item_slots_override_grffile)
+ itemdb_read_itemslotcounttable();
+ if (battle_config.item_name_override_grffile)
+ itemdb_read_itemnametable();
+}
+
+/*==========================================
+ * Initialize / Finalize
+ *------------------------------------------
+ */
+static int itemdb_final_sub (DBKey key,void *data,va_list ap)
+{
+ int flag;
+ struct item_data *id = (struct item_data *)data;
+
+ flag = va_arg(ap, int);
+ if (id->script)
+ {
+ aFree(id->script);
+ id->script = NULL;
+ }
+ // Whether to clear the item data
+ if (flag)
+ aFree(id);
+
+ return 0;
+}
+
+void itemdb_reload(void)
+{
+ // free up all item scripts first
+ item_db->foreach(item_db, itemdb_final_sub, 0);
+ itemdb_read();
+}
+
+void do_final_itemdb(void)
+{
+ item_db->destroy(item_db, itemdb_final_sub, 1);
+}
+
+int do_init_itemdb(void)
+{
+ item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ itemdb_read();
+
+ return 0;
+}
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
new file mode 100644
index 000000000..0064d17b4
--- /dev/null
+++ b/src/map/itemdb.h
@@ -0,0 +1,106 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ITEMDB_H_
+#define _ITEMDB_H_
+
+#include "map.h"
+
+struct item_data {
+ int nameid;
+ char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
+ char prefix[NAME_LENGTH],suffix[NAME_LENGTH];
+ char cardillustname[64];
+ int value_buy;
+ int value_sell;
+ int type;
+ int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus]
+ int sex;
+ int equip;
+ int weight;
+ int atk;
+ int def;
+ int range;
+ int slot;
+ int look;
+ int elv;
+ int wlv;
+//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command
+// some script commands should be revised as well...
+ unsigned short class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2)
+ unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby)
+ unsigned char *script; // UŒ‚,–hŒä‚Ì‘®«Ý’è‚à‚±‚Ì’†‚ʼn”\‚©‚È?
+ struct {
+ unsigned available : 1;
+ unsigned value_notdc : 1;
+ unsigned value_notoc : 1;
+ unsigned no_equip : 3;
+ unsigned no_use : 1;
+ unsigned no_refine : 1; // [celest]
+ unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex]
+ unsigned trade_restriction : 7; //Item restrictions mask [Skotlex]
+ } flag;
+ short gm_lv_trade_override; //GM-level to override trade_restriction
+ int view_id;
+};
+
+struct random_item_data {
+ int nameid;
+ int per;
+};
+
+struct item_group {
+ int qty; //Counts amount of items in the group.
+ int id[30]; // 120 bytes
+};
+
+struct item_data* itemdb_searchname(const char *name);
+struct item_data* itemdb_search(int nameid);
+struct item_data* itemdb_exists(int nameid);
+#define itemdb_type(n) itemdb_search(n)->type
+#define itemdb_atk(n) itemdb_search(n)->atk
+#define itemdb_def(n) itemdb_search(n)->def
+#define itemdb_look(n) itemdb_search(n)->look
+#define itemdb_weight(n) itemdb_search(n)->weight
+#define itemdb_equip(n) itemdb_search(n)->equip
+#define itemdb_usescript(n) itemdb_search(n)->script
+#define itemdb_equipscript(n) itemdb_search(n)->script
+#define itemdb_wlv(n) itemdb_search(n)->wlv
+#define itemdb_range(n) itemdb_search(n)->range
+#define itemdb_slot(n) itemdb_search(n)->slot
+#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available)
+#define itemdb_viewid(n) (itemdb_search(n)->view_id)
+int itemdb_group(int nameid);
+
+int itemdb_searchrandomid(int flags);
+int itemdb_searchrandomgroup(int groupid);
+
+#define itemdb_value_buy(n) itemdb_search(n)->value_buy
+#define itemdb_value_sell(n) itemdb_search(n)->value_sell
+#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc
+#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc
+#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine
+//Item trade restrictions [Skotlex]
+int itemdb_isdropable(int nameid, int gmlv);
+int itemdb_cantrade(int nameid, int gmlv, int gmlv2);
+int itemdb_cansell(int nameid, int gmlv);
+int itemdb_canstore(int nameid, int gmlv);
+int itemdb_canguildstore(int nameid, int gmlv);
+int itemdb_cancartstore(int nameid, int gmlv);
+int itemdb_canpartnertrade(int nameid, int gmlv, int gmlv2);
+
+int itemdb_isequip(int);
+int itemdb_isequip2(struct item_data *);
+int itemdb_isequip3(int);
+
+// itemdb_equipƒ}ƒNƒ‚Æitemdb_equippoint‚Ƃ̈Ⴂ‚Í
+// ‘OŽÒ‚ªŽI‘¤db‚Å’è‹`‚³‚ꂽ’l‚»‚Ì‚à‚Ì‚ð•Ô‚·‚̂ɑ΂µ
+// ŒãŽÒ‚Ísessiondata‚ðl—¶‚µ‚½ˆÆ‘¤‚Å‚Ì‘•”õ‰Â”\êŠ
+// ‚·‚ׂĂ̑g‚݇‚킹‚ð•Ô‚·
+
+void itemdb_reload(void);
+
+void do_final_itemdb(void);
+int do_init_itemdb(void);
+
+#endif
diff --git a/src/map/log.c b/src/map/log.c
new file mode 100644
index 000000000..0879023fa
--- /dev/null
+++ b/src/map/log.c
@@ -0,0 +1,956 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Logging functions by Azndragon & Codemaster
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+#include "itemdb.h"
+#include "map.h"
+#include "log.h"
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+struct Log_Config log_config;
+
+char timestring[255];
+time_t curtime;
+
+//FILTER OPTIONS
+//0 = Don't log
+//1 = Log any item
+//Bits: ||
+//2 - Healing items (0)
+//3 - Etc Items(3) + Arrows (10)
+//4 - Usable Items(2) + Scrolls,Lures(11)
+//5 - Weapon(4)
+//6 - Shields,Armor,Headgears,Accessories,etc(5)
+//7 - Cards(6)
+//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs)
+//9 - Log expensive items ( >= price_log)
+//10 - Log big amount of items ( >= amount_log)
+//11 - Log refined items (if their refine >= refine_log )
+//12 - Log rare items (if their drop chance <= rare_log )
+
+//check if this item should be logged according the settings
+int should_log_item(int filter, int nameid, int amount) {
+ struct item_data *item_data;
+ if (nameid<501 || (item_data= itemdb_search(nameid)) == NULL) return 0;
+ if ( (filter&1) || // Filter = 1, we log any item
+ (filter&2 && item_data->type == 0 ) || //healing items
+ (filter&4 && (item_data->type == 3 || item_data->type == 10) ) || //etc+arrows
+ (filter&8 && (item_data->type == 2 || item_data->type == 11) ) || //usable
+ (filter&16 && item_data->type == 4 ) || //weapon
+ (filter&32 && item_data->type == 5 ) || //armor
+ (filter&64 && item_data->type == 6 ) || //cards
+ (filter&128 && (item_data->type == 7 || item_data->type == 8) ) || //eggs+pet access
+ (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items
+ (filter&512 && amount >= log_config.amount_items_log ) || //big amount of items
+ (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium
+ ) return item_data->nameid;
+
+ return 0;
+}
+
+int log_branch(struct map_session_data *sd)
+{
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+ FILE *logfp;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')",
+ log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_branch,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+
+int log_pick(struct map_session_data *sd, char *type, int mob_id, int nameid, int amount, struct item *itm)
+{
+ FILE *logfp;
+ char *mapname;
+ int obj_id;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ //Should we log this item? [Lupus]
+ if (!should_log_item(log_config.pick,nameid, amount))
+ return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+ //either PLAYER or MOB (here we get map name and objects ID)
+ if(mob_id) {
+ struct mob_data *md = (struct mob_data*)sd;
+ obj_id = mob_id;
+ mapname = map[md->m].name;
+ } else {
+ obj_id = sd->char_id;
+ mapname = (char*)mapindex_id2name(sd->mapindex);
+ }
+ if(mapname==NULL)
+ mapname="";
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ if (itm==NULL) {
+ //We log common item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
+ log_config.log_pick_db, obj_id, type, nameid, amount, mapname);
+ } else {
+ //We log Extended item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
+ log_config.log_pick_db, obj_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
+ }
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_pick,"a+")) != NULL) {
+ time_t curtime;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+
+ if (itm==NULL) {
+ //We log common item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
+ timestring, obj_id, type, nameid, amount, mapname, RETCODE);
+
+ } else {
+ //We log Extended item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
+ timestring, obj_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
+ }
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 1; //Logged
+}
+
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount)
+{
+// FILE *logfp;
+ if(log_config.enable_logs <= 0 || (log_config.zeny!=1 && abs(amount)<log_config.zeny))
+ return 0;
+
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
+ log_config.log_zeny_db, sd->char_id, src_sd->char_id, type, amount, mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+// if((logfp=fopen(log_config.log_zeny,"a+")) != NULL) {
+// time(&curtime);
+// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE);
+// fclose(logfp);
+// }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop)
+{
+ FILE *logfp;
+ int i,flag = 0;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ for (i = 0; i<10; i++) { //Should we log these items? [Lupus]
+ flag += should_log_item(log_config.drop,log_drop[i],1);
+ }
+ if (flag==0) return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`drop_date`, `kill_char_id`, `monster_id`, `item1`, `item2`, `item3`, `item4`, `item5`, `item6`, `item7`, `item8`, `item9`, `itemCard`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s') ", log_config.log_drop_db, sd->status.char_id, monster_id, log_drop[0], log_drop[1], log_drop[2], log_drop[3], log_drop[4], log_drop[5], log_drop[6], log_drop[7], log_drop[8], log_drop[9], mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_drop,"a+")) != NULL) {
+
+
+ time_t curtime;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_drop[0], log_drop[1], log_drop[2], log_drop[3], log_drop[4], log_drop[5], log_drop[6], log_drop[7], log_drop[8], log_drop[9], RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 1; //Logged
+}
+
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
+{
+ FILE *logfp;
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_mvpdrop,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_present(struct map_session_data *sd, int source_type, int nameid)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ if(!should_log_item(log_config.present,nameid,1)) return 0; //filter [Lupus]
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`present_date`, `src_id`, `account_id`, `char_id`, `char_name`, `nameid`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%s', '%d', '%s') ",
+ log_config.log_present_db, source_type, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), nameid, mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_present,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, source_type, nameid, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+ if(!should_log_item(log_config.produce,nameid,1)) return 0; //filter [Lupus]
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`produce_date`, `account_id`, `char_id`, `char_name`, `nameid`, `slot1`, `slot2`, `slot3`, `map`, `success`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%d') ",
+ log_config.log_produce_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), nameid, slot1, slot2, slot3, mapindex_id2name(sd->mapindex), success);
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_produce,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d,%d\t%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, nameid, slot1, slot2, slot3, success, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_refine(struct map_session_data *sd, int n, int success)
+{
+ FILE *logfp;
+ int log_card[MAX_SLOTS];
+ int item_level;
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(success == 0)
+ item_level = sd->status.inventory[n].refine; //leaving there 0 wasn't informative! we have SUCCESS field anyways
+ else
+ item_level = sd->status.inventory[n].refine + 1;
+ if(!should_log_item(log_config.refine,sd->status.inventory[n].nameid,1) || log_config.refine_items_log<item_level) return 0; //filter [Lupus]
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`refine_date`, `account_id`, `char_id`, `char_name`, `nameid`, `refine`"
+ ", `map`, `success`, `item_level`", log_config.log_refine_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ sd->status.inventory[n].nameid, sd->status.inventory[n].refine, mapindex_id2name(sd->mapindex), success, item_level);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_refine,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d,%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp,"%d,",log_card[i]);
+
+ fprintf(logfp,"\t%d,%d%s", success, item_level, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_tostorage(struct map_session_data *sd,int n, int guild)
+{
+ FILE *logfp;
+ int i;
+
+ if(log_config.enable_logs <= 0 || log_config.storage == 0 || log_config.log_storage[0] == '\0')
+ return 0;
+
+ nullpo_retr(0, sd);
+ if(sd->status.inventory[n].nameid==0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - to %s: %s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, guild ? "guild_storage": "storage", sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].amount, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+ fclose(logfp);
+ }
+ return 0;
+}
+
+int log_fromstorage(struct map_session_data *sd,int n, int guild)
+{
+ FILE *logfp;
+ int i;
+
+ if(log_config.enable_logs <= 0 || log_config.storage == 0 || log_config.log_storage[0] == '\0')
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ fprintf(logfp,"%s - from %s: %s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, guild ? "guild_storage": "storage", sd->status.name, sd->status.account_id, sd->status.char_id,
+ sd->status.inventory[n].nameid, sd->status.inventory[n].amount, sd->status.inventory[n].refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+
+ fclose(logfp);
+ }
+ return 0;
+}
+
+int log_trade(struct map_session_data *sd, struct map_session_data *target_sd, int n,int amount)
+{
+ FILE *logfp;
+ int log_nameid, log_amount, log_refine, log_card[MAX_SLOTS];
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2],t_name2[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount < 0)
+ return 1;
+ if(!should_log_item(log_config.trade,sd->status.inventory[n].nameid,sd->status.inventory[n].amount)) return 0; //filter [Lupus]
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`trade_date`, `src_account_id`, `src_char_id`, `src_char_name`, `des_account_id`, `des_char_id`, `des_char_name`, `nameid`, `amount`, `refine`, `map`",
+ log_config.log_trade_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%s'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ target_sd->status.account_id, target_sd->status.char_id, jstrescapecpy(t_name2, target_sd->status.name),
+ log_nameid, log_amount, log_refine, mapindex_id2name(sd->mapindex));
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql, ")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_trade,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ target_sd->status.name, target_sd->status.account_id, target_sd->status.char_id,
+ log_nameid, log_amount, log_refine);
+
+ for (i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "%s", RETCODE);
+
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount, int zeny)
+{
+ FILE *logfp;
+ int log_nameid, log_amount, log_refine, log_card[MAX_SLOTS];
+ int i;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2],t_name2[NAME_LENGTH*2];
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+ if(sd->status.inventory[n].amount< 0)
+ return 1;
+ if(!should_log_item(log_config.vend,sd->status.inventory[n].nameid,sd->status.inventory[n].amount)) return 0; //filter [Lupus]
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+ for(i=0;i<MAX_SLOTS;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ char *str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT DELAYED INTO `%s` (`vend_date`, `vend_account_id`, `vend_char_id`, `vend_char_name`, `buy_account_id`, `buy_char_id`, `buy_char_name`, `nameid`, `amount`, `refine`, `map`, `zeny`",
+ log_config.log_vend_db);
+
+ for (i=0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+
+ str_p += sprintf(str_p, ") VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%s', '%d'",
+ sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name),
+ vsd->status.account_id, vsd->status.char_id, jstrescapecpy(t_name2, vsd->status.name),
+ log_nameid, log_amount, log_refine, mapindex_id2name(sd->mapindex), zeny);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", '%d'", log_card[i]);
+
+ strcat(tmp_sql, ")");
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_vend,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s[%d:%d]\t%d\t%d\t%d\t",
+ timestring, sd->status.name, sd->status.account_id, sd->status.char_id,
+ vsd->status.name, vsd->status.account_id, vsd->status.char_id,
+ log_nameid, log_amount, log_refine);
+
+ for(i=0; i<MAX_SLOTS; i++)
+ fprintf(logfp, "%d,", sd->status.inventory[n].card[i]);
+
+ fprintf(logfp, "\t%d%s", zeny, RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_atcommand(struct map_session_data *sd, const char *message)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here?
+#endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_gm,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+int log_npc(struct map_session_data *sd, const char *message)
+{ //[Lupus]
+ FILE *logfp;
+ #ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[255+1]; //it's 255 chars MAX.
+ #endif
+
+ if(log_config.enable_logs <= 0)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ } else {
+#endif
+ if((logfp=fopen(log_config.log_npc,"a+")) != NULL) {
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ }
+#ifndef TXT_ONLY
+ }
+#endif
+ return 0;
+}
+
+//ChatLogging
+// Log CHAT (currently only: Party, Guild, Whisper)
+// LOGGING FILTERS [Lupus]
+//=============================================================
+//0 = Don't log at all
+//1 = Log any chat messages
+//Advanced Filter Bits: ||
+//2 - Log Whisper messages
+//3 - Log Party messages
+//4 - Log Guild messages
+//5 - Log Common messages (not implemented)
+//6 - Don't log when WOE is on
+//Example:
+//log_chat: 1 = logs ANY messages
+//log_chat: 6 = logs both Whisper & Party messages
+//log_chat: 8 = logs only Guild messages
+//log_chat: 18 = logs only Whisper, when WOE is off
+
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){
+#ifndef TXT_ONLY
+ char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case.
+#else
+ FILE *logfp;
+#endif
+
+ //Check ON/OFF
+ if(log_config.chat <= 0)
+ return 0; //Deactivated
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0){
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')",
+ log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, dst_charname, jstrescapecpy(t_msg, (char *)message));
+
+ if(mysql_query(&logmysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }else{
+ return 0;
+ }
+ }
+#endif
+
+#ifdef TXT_ONLY
+ if((logfp = fopen(log_config.log_chat, "a+")) != NULL){
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message
+ fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s",
+ timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE);
+ fclose(logfp);
+ return 0;
+ }else{
+ return -1;
+ }
+#endif
+return -1;
+}
+
+
+void log_set_defaults(void)
+{
+ memset(&log_config, 0, sizeof(log_config));
+
+ //LOG FILTER Default values
+ log_config.refine_items_log = 5; //log refined items, with refine >= +7
+ log_config.rare_items_log = 100; //log rare items. drop chance <= 1%
+ log_config.price_items_log = 1000; //1000z
+ log_config.amount_items_log = 100;
+}
+
+int log_config_read(char *cfgName)
+{
+ static int count = 0;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if ((count++) == 0)
+ log_set_defaults();
+
+ if((fp = fopen(cfgName, "r")) == NULL)
+ {
+ ShowError("Log configuration file not found at: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line) -1, fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2)
+ {
+ if(strcmpi(w1,"enable_logs") == 0) {
+ log_config.enable_logs = (atoi(w2));
+ } else if(strcmpi(w1,"sql_logs") == 0) {
+ log_config.sql_logs = (atoi(w2));
+//start of common filter settings
+ } else if(strcmpi(w1,"rare_items_log") == 0) {
+ log_config.rare_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"refine_items_log") == 0) {
+ log_config.refine_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"price_items_log") == 0) {
+ log_config.price_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"amount_items_log") == 0) {
+ log_config.amount_items_log = (atoi(w2));
+//end of common filter settings
+ } else if(strcmpi(w1,"log_branch") == 0) {
+ log_config.branch = (atoi(w2));
+ } else if(strcmpi(w1,"log_pick") == 0) {
+ log_config.pick = (atoi(w2));
+ } else if(strcmpi(w1,"log_drop") == 0) {
+ log_config.drop = (atoi(w2));
+ } else if(strcmpi(w1,"log_steal") == 0) {
+ log_config.steal = (atoi(w2));
+ } else if(strcmpi(w1,"log_mvpdrop") == 0) {
+ log_config.mvpdrop = (atoi(w2));
+ } else if(strcmpi(w1,"log_present") == 0) {
+ log_config.present = (atoi(w2));
+ } else if(strcmpi(w1,"log_produce") == 0) {
+ log_config.produce = (atoi(w2));
+ } else if(strcmpi(w1,"log_refine") == 0) {
+ log_config.refine = (atoi(w2));
+ } else if(strcmpi(w1,"log_trade") == 0) {
+ log_config.trade = (atoi(w2));
+ } else if(strcmpi(w1,"log_storage") == 0) {
+ log_config.storage = (atoi(w2));
+ } else if(strcmpi(w1,"log_vend") == 0) {
+ log_config.vend = (atoi(w2));
+ } else if(strcmpi(w1,"log_zeny") == 0) {
+ log_config.zeny = (atoi(w2));
+ } else if(strcmpi(w1,"log_gm") == 0) {
+ log_config.gm = (atoi(w2));
+ } else if(strcmpi(w1,"log_npc") == 0) {
+ log_config.npc = (atoi(w2));
+ } else if(strcmpi(w1, "log_chat") == 0) {
+ log_config.chat = (atoi(w2));
+ }
+
+#ifndef TXT_ONLY
+ else if(strcmpi(w1, "log_branch_db") == 0) {
+ strcpy(log_config.log_branch_db, w2);
+ if(log_config.branch == 1)
+ ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_pick_db") == 0) {
+ strcpy(log_config.log_pick_db, w2);
+ if(log_config.pick == 1)
+ ShowNotice("Logging Item Picks to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_zeny_db") == 0) {
+ strcpy(log_config.log_zeny_db, w2);
+ if(log_config.zeny == 1)
+ ShowNotice("Logging Zeny to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_drop_db") == 0) {
+ strcpy(log_config.log_drop_db, w2);
+ if(log_config.drop == 1)
+ ShowNotice("Logging Item Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_db") == 0) {
+ strcpy(log_config.log_mvpdrop_db, w2);
+ if(log_config.mvpdrop == 1)
+ ShowNotice("Logging MVP Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_present_db") == 0) {
+ strcpy(log_config.log_present_db, w2);
+ if(log_config.present == 1)
+ ShowNotice("Logging Present Usage & Results to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_produce_db") == 0) {
+ strcpy(log_config.log_produce_db, w2);
+ if(log_config.produce == 1)
+ ShowNotice("Logging Producing to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_refine_db") == 0) {
+ strcpy(log_config.log_refine_db, w2);
+ if(log_config.refine == 1)
+ ShowNotice("Logging Refining to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_trade_db") == 0) {
+ strcpy(log_config.log_trade_db, w2);
+ if(log_config.trade == 1)
+ ShowNotice("Logging Item Trades to table `%s`\n", w2);
+// } else if(strcmpi(w1, "log_storage_db") == 0) {
+// strcpy(log_config.log_storage_db, w2);
+// if(log_config.storage == 1)
+// {
+// printf("Logging Item Storages");
+// printf(" to table `%s`\n", w2);
+// }
+ } else if(strcmpi(w1, "log_vend_db") == 0) {
+ strcpy(log_config.log_vend_db, w2);
+ if(log_config.vend == 1)
+ ShowNotice("Logging Vending to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_gm_db") == 0) {
+ strcpy(log_config.log_gm_db, w2);
+ if(log_config.gm > 0)
+ ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_db") == 0) {
+ strcpy(log_config.log_npc_db, w2);
+ if(log_config.npc > 0)
+ ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_chat_db") == 0) {
+ strcpy(log_config.log_chat_db, w2);
+ if(log_config.chat > 0)
+ ShowNotice("Logging CHAT to table `%s`\n", w2);
+ }
+#endif
+
+ else if(strcmpi(w1, "log_branch_file") == 0) {
+ strcpy(log_config.log_branch, w2);
+ if(log_config.branch > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_drop_file") == 0) {
+ strcpy(log_config.log_drop, w2);
+ if(log_config.drop > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Drops to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_pick_file") == 0) {
+ strcpy(log_config.log_pick, w2);
+ if(log_config.pick > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_zeny_file") == 0) {
+ strcpy(log_config.log_zeny, w2);
+ if(log_config.zeny > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
+ strcpy(log_config.log_mvpdrop, w2);
+ if(log_config.mvpdrop > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_present_file") == 0) {
+ strcpy(log_config.log_present, w2);
+ if(log_config.present > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Present Usage & Results to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_produce_file") == 0) {
+ strcpy(log_config.log_produce, w2);
+ if(log_config.produce > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Producing to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_refine_file") == 0) {
+ strcpy(log_config.log_refine, w2);
+ if(log_config.refine > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Refining to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_trade_file") == 0) {
+ strcpy(log_config.log_trade, w2);
+ if(log_config.trade > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Trades to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_storage_file") == 0) {
+ strcpy(log_config.log_storage, w2);
+ if(log_config.storage > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Storages to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_vend_file") == 0) {
+ strcpy(log_config.log_vend, w2);
+ if(log_config.vend > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Vending to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_gm_file") == 0) {
+ strcpy(log_config.log_gm, w2);
+ if(log_config.gm > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_file") == 0) {
+ strcpy(log_config.log_npc, w2);
+ if(log_config.npc > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_chat_file") == 0) {
+ strcpy(log_config.log_chat, w2);
+ if(log_config.chat > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
+ //support the import command, just like any other config
+ } else if(strcmpi(w1,"import") == 0) {
+ log_config_read(w2);
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/src/map/log.h b/src/map/log.h
new file mode 100644
index 000000000..e650d453e
--- /dev/null
+++ b/src/map/log.h
@@ -0,0 +1,47 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+extern char db_server_logdb[32];
+
+#endif //NOT TXT_ONLY
+
+int log_branch(struct map_session_data *sd);
+int log_pick(struct map_session_data *sd, char *type, int mob_id, int nameid, int amount, struct item *itm);
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount);
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop);
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp);
+int log_present(struct map_session_data *sd, int source_type, int nameid);
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success);
+int log_refine(struct map_session_data *sd, int n, int success);
+int log_trade(struct map_session_data *sd,struct map_session_data *target_sd,int n,int amount);
+int log_tostorage(struct map_session_data *sd,int n, int guild);
+int log_fromstorage(struct map_session_data *sd,int n, int guild);
+
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount,int zeny);
+int log_atcommand(struct map_session_data *sd, const char *message);
+int log_npc(struct map_session_data *sd, const char *message);
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message);
+
+int log_config_read(char *cfgName);
+
+int should_log_item(int filter, int nameid, int amount); //log filter check
+
+extern struct Log_Config {
+ int enable_logs;
+ int sql_logs;
+ int rare_items_log,refine_items_log,price_items_log,amount_items_log;
+ int branch, pick, drop, steal, mvpdrop, present, produce, refine, trade, vend, zeny, gm, npc, storage, chat;
+ char log_branch[32], log_pick[32], log_zeny[32], log_drop[32], log_mvpdrop[32], log_present[32], log_produce[32], log_refine[32], log_trade[32], log_vend[32], log_gm[32], log_npc[32], log_storage[32], log_chat[32];
+ char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_drop_db[32], log_mvpdrop_db[32], log_present_db[32], log_produce_db[32], log_refine_db[32], log_trade_db[32], log_vend_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32];
+ int uptime;
+ char log_uptime[32];
+} log_config;
+
+#endif
diff --git a/src/map/mail.c b/src/map/mail.c
new file mode 100644
index 000000000..6244a57c7
--- /dev/null
+++ b/src/map/mail.c
@@ -0,0 +1,361 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef TXT_ONLY
+// Mail System for eAthena SQL
+// Created by Valaris
+// moved all strings to msg_athena.conf [Lupus]
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "pc.h"
+#include "mail.h"
+
+#ifndef TXT_ONLY
+ #ifndef SQL_DEBUG
+
+ #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y))
+
+ #else
+
+ #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+ #endif
+#endif
+
+int MAIL_CHECK_TIME = 120000;
+int mail_timer;
+//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+int mail_check(struct map_session_data *sd,int type)
+{
+ int i = 0, new_ = 0, priority = 0;
+ char message[50];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` "
+ "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ //clif_displaymessage(sd->fd,"You have no messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+
+ mysql_free_result(mail_res);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ i++;
+ if(!atoi(mail_row[5])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if(!atoi(mail_row[3])) {
+ new_++;
+ if(atoi(mail_row[4]))
+ priority++;
+ if(type==2 || type==3) {
+ if(atoi(mail_row[4])) {
+ //sprintf(message, "%d - From : %s (New - Priority)", i, mail_row[2]);
+ sprintf(message, msg_txt(511), i, mail_row[2]);
+
+ clif_displaymessage(sd->fd, jstrescape(message));
+ } else {
+ //sprintf(message, "%d - From : %s (New)", i, mail_row[2]);
+ sprintf(message, msg_txt(512), i, mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ }
+ } else if(type==2){
+ //sprintf(message, "%d - From : %s", i, mail_row[2]);
+ sprintf(message, msg_txt(513), i, mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ }
+
+ mysql_free_result(mail_res);
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ if(i>0 && new_>0 && type==1) {
+ //sprintf(message, "You have %d new messages.", new_);
+ sprintf(message, msg_txt(514), new_);
+
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(i>0 && new_>0 && priority>0 && type==1) {
+ //sprintf(message, "You have %d unread priority messages.", priority);
+ sprintf(message, msg_txt(515), priority);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(!new_) {
+ //clif_displaymessage(sd->fd, "You have no new messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+ }
+
+ return 0;
+}
+
+int mail_read(struct map_session_data *sd, int message_id)
+{
+
+ char message[80];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[6])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ //sprintf(message, "Reading message from %s", mail_row[2]);
+ sprintf(message, msg_txt(518), mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(message, "%s", mail_row[3]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_delete(struct map_session_data *sd, int message_id)
+{
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[2]) && atoi(mail_row[3])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Cannot delete unread priority mail.");
+ clif_displaymessage(sd->fd,msg_txt(519));
+
+ return 0;
+ }
+ if(!atoi(mail_row[4])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting.");
+ clif_displaymessage(sd->fd,msg_txt(520));
+ return 0;
+ }
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ //else clif_displaymessage(sd->fd,"Message deleted.");
+ else clif_displaymessage(sd->fd,msg_txt(521));
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag)
+{
+ nullpo_retr (0, sd);
+
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0) {
+ //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message");
+ clif_displaymessage(sd->fd,msg_txt(522));
+ return 0;
+ }
+
+ if(strcmp(name,"*")==0) {
+ if(pc_isGM(sd) < 80) {
+ //clif_displaymessage(sd->fd, "Access Denied.");
+ clif_displaymessage(sd->fd, msg_txt(523));
+ return 0;
+ }
+ else
+ sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id);
+ }
+ else
+ sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name));
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Character does not exist.");
+ clif_displaymessage(sd->fd,msg_txt(524));
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ if(strcmp(name,"*")==0) {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ }
+ else {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ if(pc_isGM(sd) < 80)
+ sd->mail_counter=5;
+ }
+
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ }
+ }
+
+ //clif_displaymessage(sd->fd,"Mail has been sent.");
+ clif_displaymessage(sd->fd,msg_txt(525));
+
+ return 0;
+}
+
+int mail_check_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = NULL;
+ int i;
+
+ if(mail_timer != tid)
+ return 0;
+
+ sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+
+ if (mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ for (i = 0; i < fd_max; i++) {
+ if (session[i] && (sd = (struct map_session_data *) session[i]->session_data) && sd->state.auth){
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0)
+ sd->mail_counter--;
+ if(sd->status.account_id==atoi(mail_row[0]))
+ //clif_displaymessage(sd->fd, "You have new mail.");
+ clif_displaymessage(sd->fd, msg_txt(526));
+ }
+ }
+ }
+ }
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db);
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+int do_init_mail(void)
+{
+ add_timer_func_list(mail_check_timer,"mail_check_timer");
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+#endif
diff --git a/src/map/mail.h b/src/map/mail.h
new file mode 100644
index 000000000..2460de238
--- /dev/null
+++ b/src/map/mail.h
@@ -0,0 +1,12 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Mail System for eAthena
+// Created by Valaris
+
+int mail_check(struct map_session_data *sd, int type);
+int mail_read(struct map_session_data *sd, int message_id);
+int mail_delete(struct map_session_data *sd, int message_id);
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag);
+
+int do_init_mail(void);
diff --git a/src/map/map.c b/src/map/map.c
new file mode 100644
index 000000000..06ddd7cb1
--- /dev/null
+++ b/src/map/map.c
@@ -0,0 +1,3952 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <netdb.h>
+#include <unistd.h>
+#endif
+#include <math.h>
+
+#include "../common/core.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/showmsg.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "chat.h"
+#include "itemdb.h"
+#include "storage.h"
+#include "skill.h"
+#include "trade.h"
+#include "party.h"
+#include "battle.h"
+#include "script.h"
+#include "guild.h"
+#include "pet.h"
+#include "atcommand.h"
+#include "charcommand.h"
+
+#include "log.h"
+
+#include "charsave.h"
+
+
+// maybe put basic macros to somewhere else
+#define swap(a,b) ((a == b) || ((a ^= b), (b ^= a), (a ^= b)))
+
+#ifndef TXT_ONLY
+
+#include "mail.h" // mail system [Valaris]
+
+MYSQL mmysql_handle;
+MYSQL_RES* sql_res ;
+MYSQL_ROW sql_row ;
+char tmp_sql[65535]="";
+
+MYSQL lmysql_handle;
+MYSQL_RES* lsql_res ;
+MYSQL_ROW lsql_row ;
+
+MYSQL logmysql_handle; //For the log database - fix by [Maeki]
+MYSQL_RES* logsql_res ;
+MYSQL_ROW logsql_row ;
+
+MYSQL mail_handle; // mail system [Valaris]
+MYSQL_RES* mail_res ;
+MYSQL_ROW mail_row ;
+
+int map_server_port = 3306;
+char map_server_ip[16] = "127.0.0.1";
+char map_server_id[32] = "ragnarok";
+char map_server_pw[32] = "ragnarok";
+char map_server_db[32] = "ragnarok";
+char default_codepage[32] = ""; //Feature by irmin.
+int db_use_sqldbs = 0;
+
+int login_server_port = 3306;
+char login_server_ip[16] = "127.0.0.1";
+char login_server_id[32] = "ragnarok";
+char login_server_pw[32] = "ragnarok";
+char login_server_db[32] = "ragnarok";
+
+char item_db_db[32] = "item_db";
+char item_db2_db[32] = "item_db2";
+char mob_db_db[32] = "mob_db";
+char mob_db2_db[32] = "mob_db2";
+
+// SQL for databases not supported yet. [Valaris]
+int db_use_newsqldbs = 0;
+
+char abra_sqldb[32]="abra_db";
+char attr_fix_sqldb[32]="attr_fix";
+char cast_sqldb[32]="cast_db";
+char castle_sqldb[32]="castle_db";
+char create_arrow_sqldb[32]="create_arrow_db";
+char exp_sqldb[32]="exp";
+char exp_guild_sqldb[32]="exp_guild";
+char item_bluebox_sqldb[32]="item_bluebox";
+char item_cardalbum_sqldb[32]="item_cardalbum";
+char item_giftbox_sqldb[32]="item_giftbox";
+char item_scroll_sqldb[32]="item_scroll";
+char item_violetbox_sqldb[32]="item_violetbox";
+char job_sqldb1[32]="job_db1";
+char mob_boss_sqldb[32]="mob_boss";
+char mob_branch_sqldb[32]="mob_branch";
+char mob_poring_sqldb[32]="mob_poring";
+char mob_skill_sqldb[32]="mob_skill_db";
+char pet_sqldb[32]="pet_db";
+char produce_sqldb[32]="produce_db";
+char refine_sqldb[32]="refine_db";
+char size_fix_sqldb[32]="size_fix";
+char skill_sqldb[32]="skill_db";
+char skill_require_sqldb[32]="skill_require_db";
+char skill_tree_sqldb[32]="skill_tree";
+// End [Valaris]
+
+char login_db[32] = "login";
+char login_db_level[32] = "level";
+char login_db_account_id[32] = "account_id";
+
+int log_db_port = 3306;
+char log_db_ip[16] = "127.0.0.1";
+char log_db_id[32] = "ragnarok";
+char log_db_pw[32] = "ragnarok";
+char log_db[32] = "log";
+
+int mail_server_port = 3306;
+char mail_server_ip[16] = "127.0.0.1";
+char mail_server_id[32] = "ragnarok";
+char mail_server_pw[32] = "ragnarok";
+char mail_server_db[32] = "ragnarok";
+int mail_server_enable = 0;
+
+char gm_db[32] = "login";
+char gm_db_level[32] = "level";
+char gm_db_account_id[32] = "account_id";
+
+int read_gm_interval = 600000;
+
+char char_db[32] = "char";
+
+char mail_db[32] = "mail";
+
+char charsql_host[40] = "localhost";
+int charsql_port = 3306;
+char charsql_user[32] = "ragnarok";
+char charsql_pass[32] = "eAthena";
+char charsql_db[40] = "ragnarok";
+MYSQL charsql_handle;
+MYSQL_RES* charsql_res;
+MYSQL_ROW charsql_row;
+
+#ifdef MAPREGSQL
+// [zBuffer] SQL Mapreg
+MYSQL mapregsql_handle;
+MYSQL_RES* mapregsql_res ;
+MYSQL_ROW mapregsql_row;
+#endif
+
+#endif /* not TXT_ONLY */
+
+int lowest_gm_level = 1;
+
+// This param using for sending mainchat
+// messages like whispers to this nick. [LuzZza]
+char main_chat_nick[16] = "Main";
+
+char *INTER_CONF_NAME;
+char *LOG_CONF_NAME;
+char *MAP_CONF_NAME;
+char *BATTLE_CONF_FILENAME;
+char *ATCOMMAND_CONF_FILENAME;
+char *CHARCOMMAND_CONF_FILENAME;
+char *SCRIPT_CONF_NAME;
+char *MSG_CONF_NAME;
+char *GRF_PATH_FILENAME;
+
+// ‹É—Í static‚Ń?ƒJƒ‹‚É?‚ß‚é
+static struct dbt * id_db=NULL;
+static struct dbt * pc_db=NULL;
+static struct dbt * map_db=NULL;
+static struct dbt * charid_db=NULL;
+
+static int map_users=0;
+static struct block_list *objects[MAX_FLOORITEM];
+static int first_free_object_id=0,last_object_id=0;
+
+#define block_free_max 1048576
+struct block_list *block_free[block_free_max];
+static int block_free_count = 0, block_free_lock = 0;
+
+#define BL_LIST_MAX 1048576
+static struct block_list *bl_list[BL_LIST_MAX];
+static int bl_list_count = 0;
+
+static char afm_dir[1024] = ""; // [Valaris]
+
+struct map_data map[MAX_MAP_PER_SERVER];
+int map_num = 0;
+
+int map_port=0;
+
+int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+int charsave_method = 0; //Default 'OLD' Save method (SQL ONLY!) [Sirius]
+int agit_flag = 0;
+int night_flag = 0; // 0=day, 1=night [Yor]
+int kick_on_disconnect = 1;
+
+struct charid2nick {
+ char nick[NAME_LENGTH];
+ int req_id;
+};
+
+// «Þ«Ã«×«­«ã«Ã«·«å××éÄ«Õ«é«°(map_athana.conf?ªÎread_map_from_cacheªÇò¦E)
+// 0:××éĪ·ªÊª¤ 1:Þª?õêÜÁðí 2:?õêÜÁðí
+int map_read_flag = READ_FROM_GAT;
+char map_cache_file[256]="db/map.info"; // «Þ«Ã«×«­«ã«Ã«·«å«Õ«¡«¤«E£
+
+char db_path[256] = "db";
+char motd_txt[256] = "conf/motd.txt";
+char help_txt[256] = "conf/help.txt";
+char help2_txt[256] = "conf/help2.txt";
+char charhelp_txt[256] = "conf/charhelp.txt";
+
+char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file
+
+int console = 0;
+int enable_spy = 0; //To enable/disable @spy commands, which consume too much cpu time when sending packets. [Skotlex]
+
+static const int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static const int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+/*==========================================
+ * ‘SmapŽI?Œv‚Å‚ÌÚ??Ý’è
+ * (charŽI‚©‚ç‘—‚ç‚ê‚Ä‚­‚é)
+ *------------------------------------------
+ */
+void map_setusers(int fd)
+{
+ RFIFOHEAD(fd);
+ WFIFOHEAD(fd, 2);
+
+ map_users = RFIFOL(fd,2);
+ // send some anser
+ WFIFOW(fd,0) = 0x2718;
+ WFIFOSET(fd,2);
+}
+
+/*==========================================
+ * ‘SmapŽI?Œv‚Å‚ÌÚ??Žæ“¾ (/w‚Ö‚Ì?“š—p)
+ *------------------------------------------
+ */
+int map_getusers(void) {
+ return map_users;
+}
+
+
+//Distance functions, taken from http://www.flipcode.com/articles/article_fastdistance.shtml
+int check_distance(int dx, int dy, int distance) {
+ //In this case, we just do a square comparison. Add 1 tile grace for diagonal range checks.
+ return (dx*dx + dy*dy <= distance*distance + (dx&&dy?1:0));
+}
+
+unsigned int distance(int dx, int dy) {
+ unsigned int min, max;
+
+ if ( dx < 0 ) dx = -dx;
+ if ( dy < 0 ) dy = -dy;
+ //There appears to be something wrong with the aproximation below when either dx/dy is 0! [Skotlex]
+ if ( dx == 0 ) return dy;
+ if ( dy == 0 ) return dx;
+
+ if ( dx < dy )
+ {
+ min = dx;
+ max = dy;
+ } else {
+ min = dy;
+ max = dx;
+ }
+ // coefficients equivalent to ( 123/128 * max ) and ( 51/128 * min )
+ return ((( max << 8 ) + ( max << 3 ) - ( max << 4 ) - ( max << 1 ) +
+ ( min << 7 ) - ( min << 5 ) + ( min << 3 ) - ( min << 1 )) >> 8 );
+}
+
+//
+// block휂̈À‘S«Šm•Û?—
+//
+
+/*==========================================
+ * block‚ðfree‚·‚é‚Æ‚«free‚Ì?‚í‚è‚ɌĂÔ
+ * ƒƒbƒN‚³‚ê‚Ä‚¢‚é‚Æ‚«‚̓oƒbƒtƒ@‚É‚½‚ß‚é
+ *------------------------------------------
+ */
+int map_freeblock (struct block_list *bl)
+{
+ if (block_free_lock == 0 || block_free_count >= block_free_max)
+ {
+ aFree(bl);
+ bl = NULL;
+ if (block_free_count >= block_free_max)
+ if (battle_config.error_log)
+ ShowWarning("map_freeblock: too many free block! %d %d\n",
+ block_free_count, block_free_lock);
+ } else
+ block_free[block_free_count++] = bl;
+
+ return block_free_lock;
+}
+/*==========================================
+ * block‚Ìfree‚ðˆêŽsI‚É‹ÖŽ~‚·‚é
+ *------------------------------------------
+ */
+int map_freeblock_lock (void)
+{
+ return ++block_free_lock;
+}
+
+/*==========================================
+ * block‚Ìfree‚̃ƒbƒN‚ð‰ðœ‚·‚é
+ * ‚±‚Ì‚Æ‚«AƒƒbƒN‚ªŠ®‘S‚É‚È‚­‚È‚é‚Æ
+ * ƒoƒbƒtƒ@‚É‚½‚Ü‚Á‚Ä‚¢‚½block‚ð‘S•”íœ
+ *------------------------------------------
+ */
+int map_freeblock_unlock (void)
+{
+ if ((--block_free_lock) == 0) {
+ int i;
+ for (i = 0; i < block_free_count; i++)
+ { //Directly calling aFree shouldn't be a leak, as Free remembers the size the original pointed to memory was allocated with? [Skotlex]
+ aFree(block_free[i]);
+ block_free[i] = NULL;
+ }
+ block_free_count = 0;
+ } else if (block_free_lock < 0) {
+ if (battle_config.error_log)
+ ShowError("map_freeblock_unlock: lock count < 0 !\n");
+ block_free_lock = 0; // ŽŸ‰ñˆÈ~‚̃ƒbƒN‚ÉŽxႪo‚Ä‚­‚é‚̂ŃŠƒZƒbƒg
+ }
+
+ return block_free_lock;
+}
+
+// map_freeblock_lock() ‚ðŒÄ‚ñ‚Å map_freeblock_unlock() ‚ðŒÄ‚΂Ȃ¢
+// ŠÖ”‚ª‚ ‚Á‚½‚Ì‚ÅA’èŠú“I‚Éblock_free_lock‚ðƒŠƒZƒbƒg‚·‚é‚悤‚É‚·‚éB
+// ‚±‚ÌŠÖ”‚ÍAdo_timer() ‚̃gƒbƒvƒŒƒxƒ‹‚©‚çŒÄ‚΂ê‚é‚Ì‚ÅA
+// block_free_lock ‚ð’¼Ú‚¢‚¶‚Á‚Ä‚àŽxá–³‚¢‚Í‚¸B
+
+int map_freeblock_timer (int tid, unsigned int tick, int id, int data)
+{
+ if (block_free_lock > 0) {
+ ShowError("map_freeblock_timer: block_free_lock(%d) is invalid.\n", block_free_lock);
+ block_free_lock = 1;
+ map_freeblock_unlock();
+ }
+
+ return 0;
+}
+
+//
+// block‰»?—
+//
+/*==========================================
+ * map[]‚Ìblock_list‚©‚ç?‚ª‚Á‚Ä‚¢‚éꇂÉ
+ * bl->prev‚Ébl_head‚̃AƒhƒŒƒX‚ð“ü‚ê‚Ä‚¨‚­
+ *------------------------------------------
+ */
+static struct block_list bl_head;
+
+#ifdef CELL_NOSTACK
+/*==========================================
+ * These pair of functions update the counter of how many objects
+ * lie on a tile.
+ *------------------------------------------
+ */
+void map_addblcell(struct block_list *bl)
+{
+ if(bl->m<0 || bl->x<0 || bl->x>=map[bl->m].xs
+ || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR))
+ return;
+ map[bl->m].cell_bl[bl->x+bl->y*map[bl->m].xs]++;
+ return;
+}
+
+void map_delblcell(struct block_list *bl)
+{
+ if(bl->m <0 || bl->x<0 || bl->x>=map[bl->m].xs
+ || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR))
+ return;
+ map[bl->m].cell_bl[bl->x+bl->y*map[bl->m].xs]--;
+}
+#endif
+
+/*==========================================
+ * Adds a block to the map.
+ * If flag is 1, then the block was just added
+ * otherwise it is part of a transition.
+ *------------------------------------------
+ */
+int map_addblock_sub (struct block_list *bl, int flag)
+{
+ int m, x, y, pos;
+
+ nullpo_retr(0, bl);
+
+ if (bl->prev != NULL) {
+ if(battle_config.error_log)
+ ShowError("map_addblock error : bl->prev != NULL\n");
+ return 0;
+ }
+
+ m = bl->m;
+ x = bl->x;
+ y = bl->y;
+ if (m < 0 || m >= map_num ||
+ x < 0 || x >= map[m].xs ||
+ y < 0 || y >= map[m].ys)
+ return 1;
+
+#ifdef CELL_NOSTACK
+ map_addblcell(bl);
+#endif
+
+ pos = x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs;
+ if (bl->type == BL_MOB) {
+ bl->next = map[m].block_mob[pos];
+ bl->prev = &bl_head;
+ if (bl->next) bl->next->prev = bl;
+ map[m].block_mob[pos] = bl;
+ map[m].block_mob_count[pos]++;
+ } else {
+ bl->next = map[m].block[pos];
+ bl->prev = &bl_head;
+ if (bl->next) bl->next->prev = bl;
+ map[m].block[pos] = bl;
+ map[m].block_count[pos]++;
+ if (bl->type == BL_PC && flag)
+ {
+ struct map_session_data* sd;
+ if (map[m].users++ == 0 && battle_config.dynamic_mobs) //Skotlex
+ map_spawnmobs(m);
+ sd = (struct map_session_data*)bl;
+ if (battle_config.pet_no_gvg && map_flag_gvg(m) && sd->pd)
+ { //Return the pet to egg. [Skotlex]
+ clif_displaymessage(sd->fd, "Pets are not allowed in Guild Wars.");
+ pet_menu(sd, 3); //Option 3 is return to egg.
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Removes a block from the map.
+ * If flag is 1, then the block is removed for good
+ * otherwise it is part of a transition.
+ *------------------------------------------
+ */
+int map_delblock_sub (struct block_list *bl, int flag)
+{
+ int b;
+ nullpo_retr(0, bl);
+
+ // ?‚Éblocklist‚©‚ç?‚¯‚Ä‚¢‚é
+ if (bl->prev == NULL) {
+ if (bl->next != NULL) {
+ // prev‚ªNULL‚Ånext‚ªNULL‚Å‚È‚¢‚Ì‚Í—L‚Á‚Ä‚Í‚È‚ç‚È‚¢
+ if(battle_config.error_log)
+ ShowError("map_delblock error : bl->next!=NULL\n");
+ }
+ return 0;
+ }
+
+#ifdef CELL_NOSTACK
+ map_delblcell(bl);
+#endif
+
+ b = bl->x/BLOCK_SIZE+(bl->y/BLOCK_SIZE)*map[bl->m].bxs;
+
+ if (bl->type == BL_PC && flag)
+ if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
+ map_removemobs(bl->m);
+
+ if (bl->next)
+ bl->next->prev = bl->prev;
+ if (bl->prev == &bl_head) {
+ // ƒŠƒXƒg‚Ì“ª‚È‚Ì‚ÅAmap[]‚Ìblock_list‚ðXV‚·‚é
+ if (bl->type == BL_MOB) {
+ map[bl->m].block_mob[b] = bl->next;
+ if ((map[bl->m].block_mob_count[b]--) < 0)
+ map[bl->m].block_mob_count[b] = 0;
+ } else {
+ map[bl->m].block[b] = bl->next;
+ if((map[bl->m].block_count[b]--) < 0)
+ map[bl->m].block_count[b] = 0;
+ }
+ } else {
+ bl->prev->next = bl->next;
+ }
+ bl->next = NULL;
+ bl->prev = NULL;
+
+ return 0;
+}
+
+/*==========================================
+ * Moves a block a x/y target position. [Skotlex]
+ * Pass flag as 1 to prevent doing skill_unit_move checks
+ * (which are executed by default on BL_CHAR types)
+ *------------------------------------------
+ */
+int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) {
+ int x0 = bl->x, y0 = bl->y;
+ int moveblock = ( x0/BLOCK_SIZE != x1/BLOCK_SIZE || y0/BLOCK_SIZE != y1/BLOCK_SIZE);
+
+ if (!bl->prev) {
+ //Block not in map, just update coordinates, but do naught else.
+ bl->x = x1;
+ bl->y = y1;
+ return 0;
+ }
+ //TODO: Perhaps some outs of bounds checking should be placed here?
+ if (bl->type&BL_CHAR)
+ skill_unit_move(bl,tick,2);
+
+ if (moveblock) map_delblock_sub(bl,0);
+#ifdef CELL_NOSTACK
+ else map_delblcell(bl);
+#endif
+ bl->x = x1;
+ bl->y = y1;
+ if (moveblock) map_addblock_sub(bl,0);
+#ifdef CELL_NOSTACK
+ else map_addblcell(bl);
+#endif
+ if (bl->type&BL_CHAR)
+ skill_unit_move(bl,tick,3);
+ return 0;
+}
+
+/*==========================================
+ * Žü?‚ÌPCl?‚ð?‚¦‚é (unused)
+ *------------------------------------------
+ */
+int map_countnearpc (int m, int x, int y)
+{
+ int bx, by, c = 0;
+ struct block_list *bl=NULL;
+
+ if (map[m].users == 0)
+ return 0;
+ for (by = y/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1; by<=y/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1; by++) {
+ if (by < 0 || by >= map[m].bys)
+ continue;
+ for (bx = x/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1; bx <= x/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1; bx++) {
+ if (bx < 0 || bx >= map[m].bxs)
+ continue;
+ bl = map[m].block[bx+by*map[m].bxs];
+ while(bl) {
+ if (bl->type == BL_PC)
+ c++;
+ bl = bl->next;
+ }
+ }
+ }
+
+ return c;
+}
+
+/*==========================================
+ * Counts specified number of objects on given cell.
+ *------------------------------------------
+ */
+int map_count_oncell(int m, int x, int y, int type) {
+ int bx,by;
+ struct block_list *bl=NULL;
+ int i,c;
+ int count = 0;
+
+ if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys))
+ return 0;
+ bx = x/BLOCK_SIZE;
+ by = y/BLOCK_SIZE;
+
+ if (type == 0 || type != BL_MOB)
+ {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ if(bl->x == x && bl->y == y && bl->type == (type?type:BL_PC)) count++;
+ }
+
+ if (type == 0 || type == BL_MOB)
+ {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl->x == x && bl->y == y) count++;
+ }
+ }
+ return count;
+}
+/*
+ * «»«E¾ªÎõÌôøªË̸ªÄª±ª¿«¹«­«Eæ«Ë«Ã«ÈªòÚ÷ª¹
+ */
+struct skill_unit *map_find_skill_unit_oncell(struct block_list *target,int x,int y,int skill_id,struct skill_unit *out_unit)
+{
+ int m,bx,by;
+ struct block_list *bl;
+ int i,c;
+ struct skill_unit *unit;
+ m = target->m;
+
+ if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys))
+ return NULL;
+ bx = x/BLOCK_SIZE;
+ by = y/BLOCK_SIZE;
+
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if (bl->x != x || bl->y != y || bl->type != BL_SKILL)
+ continue;
+ unit = (struct skill_unit *) bl;
+ if (unit==out_unit || !unit->alive ||
+ !unit->group || unit->group->skill_id!=skill_id)
+ continue;
+ if (battle_check_target(&unit->bl,target,unit->group->target_flag)>0)
+ return unit;
+ }
+ return NULL;
+}
+
+/*==========================================
+ * map m (x0,y0)-(x1,y1)?‚Ì‘Sobj‚É?‚µ‚Ä
+ * func‚ðŒÄ‚Ô
+ * type!=0 ‚È‚ç‚»‚ÌŽí—Þ‚Ì‚Ý
+ *------------------------------------------
+ */
+int map_foreachinarea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int type,...) {
+ va_list ap;
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ int blockcount=bl_list_count,i,c;
+
+ if (m < 0)
+ return 0;
+ va_start(ap,type);
+ if (x1 < x0)
+ { //Swap range
+ bx = x0;
+ x0 = x1;
+ x1 = bx;
+ }
+ if (y1 < y0)
+ {
+ bx = y0;
+ y0 = y1;
+ y1 = bx;
+ }
+ if (x0 < 0) x0 = 0;
+ if (y0 < 0) y0 = 0;
+ if (x1 >= map[m].xs) x1 = map[m].xs-1;
+ if (y1 >= map[m].ys) y1 = map[m].ys-1;
+
+ if (type&~BL_MOB)
+ for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) {
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ if(type&BL_MOB)
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinarea: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // ƒƒ‚ƒŠ‚©‚ç‚̉ð•ú‚ð‹ÖŽ~‚·‚é
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // —L?‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount; //[Skotlex]
+}
+
+/*==========================================
+ * ‹éŒ`(x0,y0)-(x1,y1)‚ª(dx,dy)ˆÚ“®‚µ‚½ŽbÌ
+ * —̈æŠO‚É‚È‚é—̈æ(‹éŒ`‚©LŽšŒ`)?‚Ìobj‚É
+ * ?‚µ‚Äfunc‚ðŒÄ‚Ô
+ *
+ * dx,dy‚Í-1,0,1‚Ì‚Ý‚Æ‚·‚éi‚Ç‚ñ‚È’l‚Å‚à‚¢‚¢‚Á‚Û‚¢Hj
+ *------------------------------------------
+ */
+int map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int dx,int dy,int type,...) {
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+ if (x1 < x0)
+ { //Swap range
+ bx = x0;
+ x0 = x1;
+ x1 = bx;
+ }
+ if (y1 < y0)
+ {
+ bx = y0;
+ y0 = y1;
+ y1 = bx;
+ }
+ if(dx==0 || dy==0){
+ // ‹éŒ`—̈æ‚Ìê‡
+ if(dx==0){
+ if(dy<0){
+ y0=y1+dy+1;
+ } else {
+ y1=y0+dy-1;
+ }
+ } else if(dy==0){
+ if(dx<0){
+ x0=x1+dx+1;
+ } else {
+ x1=x0+dx-1;
+ }
+ }
+ if(x0<0) x0=0;
+ if(y0<0) y0=0;
+ if(x1>=map[m].xs) x1=map[m].xs-1;
+ if(y1>=map[m].ys) y1=map[m].ys-1;
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ if (type&~BL_MOB) {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ if (type&BL_MOB) {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+ }else{
+ // LŽš—̈æ‚Ìê‡
+
+ if(x0<0) x0=0;
+ if(y0<0) y0=0;
+ if(x1>=map[m].xs) x1=map[m].xs-1;
+ if(y1>=map[m].ys) y1=map[m].ys-1;
+ for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){
+ for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
+ if (type & ~BL_MOB) {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ continue;
+ if(bl && bl->type&type && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
+ bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ if (type & BL_MOB) {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(!bl || !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1))
+ continue;
+ if(bl && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) ||
+ (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) &&
+ bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinmovearea: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // ƒƒ‚ƒŠ‚©‚ç‚̉ð•ú‚ð‹ÖŽ~‚·‚é
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) { // —L?‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ if (bl_list[i]->type == BL_PC
+ && session[((struct map_session_data *) bl_list[i])->fd] == NULL)
+ continue;
+ returnCount += func(bl_list[i],ap);
+ }
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+// -- moonsoul (added map_foreachincell which is a rework of map_foreachinarea but
+// which only checks the exact single x/y passed to it rather than an
+// area radius - may be more useful in some instances)
+//
+int map_foreachincell(int (*func)(struct block_list*,va_list),int m,int x,int y,int type,...) {
+ int bx,by;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+
+ by=y/BLOCK_SIZE;
+ bx=x/BLOCK_SIZE;
+
+ if(type&~BL_MOB)
+ {
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->type&type && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+
+ if(type&BL_MOB)
+ {
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachincell: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // ƒƒ‚ƒŠ‚©‚ç‚̉ð•ú‚ð‹ÖŽ~‚·‚é
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // —L?‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+/*============================================================
+* For checking a path between two points (x0, y0) and (x1, y1)
+*------------------------------------------------------------
+ */
+int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...)
+{
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+//////////////////////////////////////////////////////////////
+//
+// sharp shooting 3 [Skotlex]
+//
+//////////////////////////////////////////////////////////////
+// problem:
+// Same as Sharp Shooting 1. Hits all targets within range of
+// the line.
+// (t1,t2 t3 and t4 get hit)
+//
+// target 1
+// x t4
+// t2
+// t3 x
+// x
+// S
+//////////////////////////////////////////////////////////////
+// Methodology:
+// My trigonometrics and math is a little rusty... so the approach I am writing
+// here is basicly do a double for to check for all targets in the square that
+// contains the initial and final positions (area range increased to match the
+// radius given), then for each object to test, calculate the distance to the
+// path and include it if the range fits and the target is in the line (0<k<1,
+// as they call it).
+// The implementation I took as reference is found at
+// http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
+// (they have a link to a C implementation, too)
+// This approach is a lot like #2 commented on this function, which I have no
+// idea why it was commented. I won't use doubles/floats, but pure int math for speed purposes
+// The range considered is always the same no matter how close/far the target is because that's
+// how SharpShooting works currently in kRO.
+
+ //Generic map_foreach* variables.
+ va_list ap;
+ int i, blockcount = bl_list_count;
+ struct block_list *bl;
+ int c, bx, by;
+ //method specific variables
+ int magnitude2; //The square of the magnitude
+ int k, xi, yi, xu, yu;
+ int mx0 = x0, mx1 = x1, my0 = y0, my1 = y1;
+
+ //Avoid needless calculations by not getting the sqrt right away.
+ #define MAGNITUDE2(x0, y0, x1, y1) (((x1)-(x0))*((x1)-(x0)) + ((y1)-(y0))*((y1)-(y0)))
+
+ if (m < 0)
+ return 0;
+
+ va_start(ap,type);
+
+ //Expand target area to cover range.
+ if (mx0 > mx1)
+ {
+ mx0+=range;
+ mx1-=range;
+ } else {
+ mx0-=range;
+ mx1+=range;
+ }
+ if (my0 > my1)
+ {
+ my0+=range;
+ my1-=range;
+ } else {
+ my0-=range;
+ my1+=range;
+ }
+
+ //The two fors assume mx0 < mx1 && my0 < my1
+ if (mx0 > mx1)
+ {
+ k = mx1;
+ mx1 = mx0;
+ mx0 = k;
+ }
+ if (my0 > my1)
+ {
+ k = my1;
+ my1 = my0;
+ my0 = k;
+ }
+
+ if (mx0 < 0) mx0 = 0;
+ if (my0 < 0) my0 = 0;
+ if (mx1 >= map[m].xs) mx1 = map[m].xs-1;
+ if (my1 >= map[m].ys) my1 = map[m].ys-1;
+
+ range*=range<<8; //Values are shifted later on for higher precision using int math.
+ magnitude2 = MAGNITUDE2(x0,y0, x1,y1);
+ if (magnitude2 < 1) //Same begin and ending point, can't trace path.
+ return 0;
+
+ if (type & ~BL_MOB)
+ for (by = my0 / BLOCK_SIZE; by <= my1 / BLOCK_SIZE; by++) {
+ for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){
+ bl = map[m].block[bx+by*map[m].bxs];
+ c = map[m].block_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl->type&type && bl_list_count<BL_LIST_MAX)
+ {
+ xi = bl->x;
+ yi = bl->y;
+
+ k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0);
+ if (k < 0)// || k > magnitude2) //No check to see if it lies after the target's point.
+ continue;
+
+ //All these shifts are to increase the precision of the intersection point and distance considering how it's
+ //int math.
+ k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1
+ xi<<=4;
+ yi<<=4;
+ xu= (x0<<4) +k*(x1-x0);
+ yu= (y0<<4) +k*(y1-y0);
+ k = MAGNITUDE2(xi, yi, xu, yu);
+
+ //If all dot coordinates were <<4 the square of the magnitude is <<8
+ if (k > range)
+ continue;
+
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+ if(type&BL_MOB)
+ for(by=my0/BLOCK_SIZE;by<=my1/BLOCK_SIZE;by++){
+ for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl && bl_list_count<BL_LIST_MAX)
+ {
+ xi = bl->x;
+ yi = bl->y;
+ k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0);
+ if (k < 0)// || k > magnitude2) //No check to see if it lies after the target's point.
+ continue;
+
+ k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1
+ xi<<=4;
+ yi<<=4;
+ xu= (x0<<4) +k*(x1-x0);
+ yu= (y0<<4) +k*(y1-y0);
+ k = MAGNITUDE2(xi, yi, xu, yu);
+
+ //If all dot coordinates were <<4 the square of the magnitude is <<8
+ if (k > range)
+ continue;
+
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinpath: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // ƒƒ‚ƒŠ‚©‚ç‚̉ð•ú‚ð‹ÖŽ~‚·‚é
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // —L?‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount; //[Skotlex]
+
+}
+
+// Copy of map_foreachincell, but applied to the whole map. [Skotlex]
+int map_foreachinmap(int (*func)(struct block_list*,va_list),int m,int type,...) {
+ int b, bsize;
+ int returnCount =0; //total sum of returned values of func() [Skotlex]
+ struct block_list *bl=NULL;
+ va_list ap;
+ int blockcount=bl_list_count,i,c;
+
+ va_start(ap,type);
+
+ bsize = map[m].bxs * map[m].bys;
+ if(type&~BL_MOB)
+ {
+ for(b=0;b<bsize;b++){
+ bl = map[m].block[b];
+ c = map[m].block_count[b];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl->type&type && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(type&BL_MOB)
+ {
+ for(b=0;b<bsize;b++){
+ bl = map[m].block_mob[b];
+ c = map[m].block_mob_count[b];
+ for(i=0;i<c && bl;i++,bl=bl->next)
+ {
+ if(bl && bl_list_count<BL_LIST_MAX)
+ bl_list[bl_list_count++]=bl;
+ }
+ }
+ }
+
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachinmap: block count too many!\n");
+ }
+
+ map_freeblock_lock(); // ƒƒ‚ƒŠ‚©‚ç‚̉ð•ú‚ð‹ÖŽ~‚·‚é
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if(bl_list[i]->prev) // —L?‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ returnCount += func(bl_list[i],ap);
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+ bl_list_count = blockcount;
+ return returnCount;
+}
+
+/*==========================================
+ * °ƒAƒCƒeƒ€‚âƒGƒtƒFƒNƒg—p‚̈êŽObjŠ„‚è?‚Ä
+ * object[]‚Ö‚Ì•Û‘¶‚Æid_db“o?‚Ü‚Å
+ *
+ * bl->id‚à‚±‚Ì’†‚Åݒ肵‚Ä–â‘è–³‚¢?
+ *------------------------------------------
+ */
+int map_addobject(struct block_list *bl) {
+ int i;
+ if( bl == NULL ){
+ ShowWarning("map_addobject nullpo?\n");
+ return 0;
+ }
+ if(first_free_object_id<2 || first_free_object_id>=MAX_FLOORITEM)
+ first_free_object_id=2;
+ for(i=first_free_object_id;i<MAX_FLOORITEM && objects[i];i++);
+ if(i>=MAX_FLOORITEM){
+ if(battle_config.error_log)
+ ShowWarning("no free object id\n");
+ return 0;
+ }
+ first_free_object_id=i;
+ if(last_object_id<i)
+ last_object_id=i;
+ objects[i]=bl;
+ idb_put(id_db,i,bl);
+ return i;
+}
+
+/*==========================================
+ * ˆêŽObject‚̉ð•ú
+ * map_delobject‚Ìfree‚µ‚È‚¢ƒo?ƒWƒ‡ƒ“
+ *------------------------------------------
+ */
+int map_delobjectnofree(int id) {
+ if(objects[id]==NULL)
+ return 0;
+
+ map_delblock(objects[id]);
+ idb_remove(id_db,id);
+ objects[id]=NULL;
+
+ if(first_free_object_id>id)
+ first_free_object_id=id;
+
+ while(last_object_id>2 && objects[last_object_id]==NULL)
+ last_object_id--;
+
+ return 0;
+}
+
+/*==========================================
+ * ˆêŽObject‚̉ð•ú
+ * block_list‚©‚ç‚ÌíœAid_db‚©‚ç‚Ìíœ
+ * object data‚ÌfreeAobject[]‚Ö‚ÌNULL‘ã“ü
+ *
+ * add‚Æ‚Ì??«‚ª–³‚¢‚Ì‚ª?‚É‚È‚é
+ *------------------------------------------
+ */
+int map_delobject(int id) {
+ struct block_list *obj = objects[id];
+
+ if(obj==NULL)
+ return 0;
+
+ map_delobjectnofree(id);
+ map_freeblock(obj);
+
+ return 0;
+}
+
+/*==========================================
+ * ‘SˆêŽObj‘ŠŽè‚Éfunc‚ðŒÄ‚Ô
+ *
+ *------------------------------------------
+ */
+void map_foreachobject(int (*func)(struct block_list*,va_list),int type,...) {
+ int i;
+ int blockcount=bl_list_count;
+ va_list ap;
+
+ va_start(ap,type);
+
+ for(i=2;i<=last_object_id;i++){
+ if(objects[i]){
+ if(!(objects[i]->type&type))
+ continue;
+ if(bl_list_count>=BL_LIST_MAX) {
+ if(battle_config.error_log)
+ ShowWarning("map_foreachobject: too many blocks !\n");
+ break;
+ }
+ bl_list[bl_list_count++]=objects[i];
+ }
+ }
+
+ map_freeblock_lock();
+
+ for(i=blockcount;i<bl_list_count;i++)
+ if( bl_list[i]->prev || bl_list[i]->next )
+ func(bl_list[i],ap);
+
+ map_freeblock_unlock();
+
+ va_end(ap);
+ bl_list_count = blockcount;
+}
+
+/*==========================================
+ * °ƒAƒCƒeƒ€‚ðÁ‚·
+ *
+ * data==0‚ÌŽbÍtimer‚ÅÁ‚¦‚½Žê * data!=0‚ÌŽbÍE‚¤“™‚ÅÁ‚¦‚½ŽbÆ‚µ‚Ä“®ì
+ *
+ * ŒãŽÒ‚ÍAmap_clearflooritem(id)‚Ö
+ * map.h?‚Å#define‚µ‚Ä‚ ‚é
+ *------------------------------------------
+ */
+int map_clearflooritem_timer(int tid,unsigned int tick,int id,int data) {
+ struct flooritem_data *fitem=NULL;
+
+ fitem = (struct flooritem_data *)objects[id];
+ if(fitem==NULL || fitem->bl.type!=BL_ITEM || (!data && fitem->cleartimer != tid)){
+ if(battle_config.error_log)
+ ShowError("map_clearflooritem_timer : error\n");
+ return 1;
+ }
+ if(data)
+ delete_timer(fitem->cleartimer,map_clearflooritem_timer);
+ else if(fitem->item_data.card[0] == (short)0xff00)
+ intif_delete_petdata( MakeDWord(fitem->item_data.card[1],fitem->item_data.card[2]) );
+ clif_clearflooritem(fitem,0);
+ map_delobject(fitem->bl.id);
+
+ return 0;
+}
+
+/*==========================================
+ * (m,x,y)‚ÌŽü?rangeƒ}ƒX?‚Ì‹ó‚«(=N“ü‰Â”\)cell‚Ì
+ * ?‚©‚ç“K?‚ȃ}ƒX–Ú‚ÌÀ•W‚ðx+(y<<16)‚Å•Ô‚·
+ *
+ * Œ»?range=1‚ŃAƒCƒeƒ€ƒhƒƒbƒv—p“r‚Ì‚Ý
+ *------------------------------------------
+ */
+int map_searchrandfreecell(int m,int x,int y,int range) {
+ int free_cell,i,j;
+ int* free_cells;
+
+ if (range < 0)
+ return -1;
+
+ //FIXME: Would it be quicker to hardcode an array of 9 since this function is always called with range 1?
+ free_cells = aCalloc((2*range+1)*(2*range+1), sizeof(int)); //better use more memory than having to wipe twice the cells. [Skotlex]
+
+ for(free_cell=0,i=-range;i<=range;i++){
+ if(i+y<0 || i+y>=map[m].ys)
+ continue;
+ for(j=-range;j<=range;j++){
+ if(j+x<0 || j+x>=map[m].xs)
+ continue;
+ if(map_getcell(m,j+x,i+y,CELL_CHKNOPASS))
+ continue;
+ if(map_count_oncell(m,j+x,i+y, BL_ITEM) > 1) //Avoid item stacking to prevent against exploits. [Skotlex]
+ continue;
+ free_cells[free_cell++] = j+x+((i+y)<<16);
+ }
+ }
+ if(free_cell==0)
+ {
+ aFree(free_cells);
+ return -1;
+ }
+ free_cell=free_cells[rand()%free_cell];
+ aFree(free_cells);
+ return free_cell;
+/*
+ for(i=-range;i<=range;i++){
+ if(i+y<0 || i+y>=map[m].ys)
+ continue;
+ for(j=-range;j<=range;j++){
+ if(j+x<0 || j+x>=map[m].xs)
+ continue;
+ if(map_getcell(m,j+x,i+y,CELL_CHKNOPASS))
+ continue;
+ if(map_count_oncell(m,j+x,i+y, BL_ITEM) > 1) //Avoid item stacking to prevent against exploits. [Skotlex]
+ continue;
+ if(free_cell==0){
+ x+=j;
+ y+=i;
+ i=range+1;
+ break;
+ }
+ free_cell--;
+ }
+ }
+
+ return x+(y<<16);
+*/
+}
+
+/*==========================================
+ * (m,x,y)‚ð’†S‚É3x3ˆÈ?‚É°ƒAƒCƒeƒ€Ý’u
+ *
+ * item_data‚ÍamountˆÈŠO‚ðcopy‚·‚é
+ *------------------------------------------
+ */
+int map_addflooritem(struct item *item_data,int amount,int m,int x,int y,struct map_session_data *first_sd,
+ struct map_session_data *second_sd,struct map_session_data *third_sd,int type) {
+ int xy,r;
+ unsigned int tick;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_retr(0, item_data);
+
+ if((xy=map_searchrandfreecell(m,x,y,1))<0)
+ return 0;
+ r=rand();
+
+ fitem = (struct flooritem_data *)aCalloc(1,sizeof(*fitem));
+ fitem->bl.type=BL_ITEM;
+ fitem->bl.prev = fitem->bl.next = NULL;
+ fitem->bl.m=m;
+ fitem->bl.x=xy&0xffff;
+ fitem->bl.y=(xy>>16)&0xffff;
+ fitem->bl.id = map_addobject(&fitem->bl);
+ if(fitem->bl.id==0){
+ aFree(fitem);
+ return 0;
+ }
+
+ tick = gettick();
+ if(first_sd) {
+ fitem->first_get_id = first_sd->bl.id;
+ if(type)
+ fitem->first_get_tick = tick + battle_config.mvp_item_first_get_time;
+ else
+ fitem->first_get_tick = tick + battle_config.item_first_get_time;
+ }
+ if(second_sd) {
+ fitem->second_get_id = second_sd->bl.id;
+ if(type)
+ fitem->second_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time;
+ else
+ fitem->second_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time;
+ }
+ if(third_sd) {
+ fitem->third_get_id = third_sd->bl.id;
+ if(type)
+ fitem->third_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time + battle_config.mvp_item_third_get_time;
+ else
+ fitem->third_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time + battle_config.item_third_get_time;
+ }
+
+ memcpy(&fitem->item_data,item_data,sizeof(*item_data));
+ fitem->item_data.amount=amount;
+ fitem->subx=(r&3)*3+3;
+ fitem->suby=((r>>2)&3)*3+3;
+ fitem->cleartimer=add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0);
+
+ map_addblock(&fitem->bl);
+ clif_dropflooritem(fitem);
+
+ return fitem->bl.id;
+}
+
+static void* create_charid2nick(DBKey key, va_list args) {
+ struct charid2nick *p;
+ p = (struct charid2nick *)aCallocA(1, sizeof (struct charid2nick));
+ return p;
+}
+/*==========================================
+ * charid_db‚֒ljÁ(•ÔM‘Ò‚¿‚ª‚ ‚ê‚ΕÔM)
+ *------------------------------------------
+ */
+void map_addchariddb(int charid, char *name) {
+ struct charid2nick *p;
+ int req = 0;
+
+ p = idb_ensure(charid_db,charid,create_charid2nick);
+ req = p->req_id;
+ p->req_id = 0;
+ //We overwrite the nick anyway in case a different one arrived.
+ memcpy(p->nick, name, NAME_LENGTH);
+
+ if (req) {
+ struct map_session_data *sd = map_id2sd(req);
+ if (sd) clif_solved_charname(sd,charid);
+ }
+}
+
+/*==========================================
+ * charid_db‚֒ljÁi•ÔM—v‹‚Ì‚Ýj
+ *------------------------------------------
+ */
+int map_reqchariddb(struct map_session_data * sd,int charid) {
+ struct charid2nick *p=NULL;
+
+ nullpo_retr(0, sd);
+
+ p = (struct charid2nick*)idb_get(charid_db,charid);
+ if(p) return 0; //Nothing to request, we already have the name!
+ p = (struct charid2nick *)aCalloc(1,sizeof(struct charid2nick));
+ p->req_id=sd->bl.id;
+ idb_put(charid_db,charid,p);
+ return 0;
+}
+
+/*==========================================
+ * id_db‚Öbl‚ð’ljÁ
+ *------------------------------------------
+ */
+void map_addiddb(struct block_list *bl) {
+ nullpo_retv(bl);
+
+ if (bl->type == BL_PC)
+ idb_put(pc_db,bl->id,bl);
+ idb_put(id_db,bl->id,bl);
+}
+
+/*==========================================
+ * id_db‚©‚çbl‚ðíœ
+ *------------------------------------------
+ */
+void map_deliddb(struct block_list *bl) {
+ nullpo_retv(bl);
+
+ if (bl->type == BL_PC)
+ idb_remove(pc_db,bl->id);
+ idb_remove(id_db,bl->id);
+}
+
+/*==========================================
+ * PC‚Ìquit?— map.c?•ª
+ *
+ * quit?—‚ÌŽå?‚ªˆá‚¤‚悤‚È?‚à‚µ‚Ä‚«‚½
+ *------------------------------------------
+ */
+int map_quit(struct map_session_data *sd) {
+
+ //nullpo_retr(0, sd); //Utterly innecessary, all invokations to this function already have an SD non-null check.
+ //Learn to use proper coding and stop relying on nullpo_'s for safety :P [Skotlex]
+
+
+ if(!sd->state.waitingdisconnect) {
+ if (sd->state.event_disconnect) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.logout_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLogoutNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.logout_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.logout_event_name, sd->bl.id), script_config.logout_event_name);
+ }
+ }
+
+ if(sd->chatID) // ƒ`ƒƒƒbƒg‚©‚ço‚é
+ chat_leavechat(sd);
+
+ if(sd->trade_partner) // Žæˆø‚ð’†?‚·‚é
+ trade_tradecancel(sd);
+
+ if(sd->party_invite>0) // ƒp?ƒeƒB?—U‚ð‹‘”Û‚·‚é
+ party_reply_invite(sd,sd->party_invite_account,0);
+
+ if(sd->guild_invite>0) // ƒMƒ‹ƒh?—U‚ð‹‘”Û‚·‚é
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0) // ƒMƒ‹ƒh“¯–¿?—U‚ð‹‘”Û‚·‚é
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+
+ // Force exiting from duel and rejecting
+ // all duel invitations when player quit [LuzZza]
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
+
+ if(sd->duel_invite > 0)
+ duel_reject(sd->duel_invite, sd);
+
+ party_send_logout(sd); // ƒp?ƒeƒB‚̃ƒOƒAƒEƒgƒƒbƒZ?ƒW‘—M
+
+ party_send_dot_remove(sd);//minimap dot fix [Kevin]
+
+ guild_send_memberinfoshort(sd,0); // ƒMƒ‹ƒh‚̃ƒOƒAƒEƒgƒƒbƒZ?ƒW‘—M
+
+ guild_send_dot_remove(sd);
+
+ pc_cleareventtimer(sd); // ƒCƒxƒ“ƒgƒ^ƒCƒ}‚ð”jŠü‚·‚é
+
+ // check if we've been authenticated [celest]
+ if (sd->state.auth)
+ skill_castcancel(&sd->bl,0); // ‰r¥‚ð’†?‚·‚é
+
+ skill_stop_dancing(&sd->bl);// ƒ_ƒ“ƒX/‰‰‘t’†?
+
+ //Status that are not saved...
+ if(sd->sc_count) {
+ if(sd->sc_data[SC_HIDING].timer!=-1)
+ status_change_end(&sd->bl,SC_HIDING,-1);
+ if(sd->sc_data[SC_CLOAKING].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if(sd->sc_data[SC_SPURT].timer!=-1)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+ if(sd->sc_data && sd->sc_data[SC_BERSERK].timer!=-1)
+ status_change_end(&sd->bl,SC_BERSERK,-1);
+ }
+ skill_clear_unitgroup(&sd->bl); // ƒXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹?ƒv‚Ìíœ
+
+ // check if we've been authenticated [celest]
+ if (sd->state.auth) {
+ skill_cleartimerskill(&sd->bl);
+ pc_stop_walking(sd,0);
+ pc_stopattack(sd);
+ pc_stop_following(sd);
+ pc_delinvincibletimer(sd);
+ }
+ pc_delspiritball(sd,sd->spiritball,1);
+ skill_gangsterparadise(sd,0);
+ skill_unit_move(&sd->bl,gettick(),4);
+
+ if (sd->state.auth)
+ status_calc_pc(sd,4);
+ // skill_clear_unitgroup(&sd->bl); // [Sara-chan]
+
+ if (!(sd->status.option & OPTION_INVISIBLE))
+ clif_clearchar_area(&sd->bl,2);
+
+ chrif_save_scdata(sd); //Save status changes, then clear'em out from memory. [Skotlex]
+ status_change_clear(&sd->bl,1);
+
+ if(sd->status.pet_id && sd->pd) {
+ pet_lootitem_drop(sd->pd,sd);
+ pet_remove_map(sd);
+ if(sd->pet.intimate <= 0) {
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ }
+ else
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ }
+
+ if(pc_isdead(sd))
+ pc_setrestartvalue(sd,2);
+
+ pc_clean_skilltree(sd);
+
+ //The storage closing routines will save the char if needed. [Skotlex]
+ if (!sd->state.storage_flag)
+ chrif_save(sd,1);
+ else if (sd->state.storage_flag == 1)
+ storage_storage_quit(sd,1);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+
+ map_delblock(&sd->bl);
+ } else { //Try to free some data, without saving anything (this could be invoked on map server change. [Skotlex]
+ if (sd->bl.prev != NULL)
+ { //Remove from map...
+ if (!(sd->status.option & OPTION_INVISIBLE))
+ clif_clearchar_area(&sd->bl,2);
+ map_delblock(&sd->bl);
+ }
+ if (sd->pd)
+ pet_remove_map(sd);
+ }
+
+ if (sd->stack) {
+ script_free_stack(sd->stack);
+ sd->stack= NULL;
+ }
+
+// chrif_char_offline(sd); //chrif_save handles this now.
+
+ //Do we really need to remove the name?
+ idb_remove(charid_db,sd->status.char_id);
+ idb_remove(id_db,sd->bl.id);
+ idb_remove(pc_db,sd->bl.id);
+
+ // Notify friends that this char logged out. [Skotlex]
+ clif_foreachclient(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);
+
+ if(sd->reg)
+ { //Double logout already freed pointer fix... [Skotlex]
+ aFree(sd->reg);
+ sd->reg = NULL;
+ sd->reg_num = 0;
+ }
+
+ if(sd->regstr)
+ {
+ aFree(sd->regstr);
+ sd->regstr = NULL;
+ sd->regstr_num = 0;
+ }
+
+ if(!sd->fd) //There is no session connected, and as such socket.c won't free the data, we must do it. [Skotlex]
+ aFree(sd);
+ return 0;
+}
+
+/*==========================================
+ * id”Ô?‚ÌPC‚ð’T‚·B‹‚È‚¯‚ê‚ÎNULL
+ *------------------------------------------
+ */
+struct map_session_data * map_id2sd(int id) {
+// Now using pc_db to handle all players, should be quicker than both previous methods at a small expense of more memory. [Skotlex]
+ if (id <= 0) return NULL;
+ return (struct map_session_data*)idb_get(pc_db,id);
+}
+
+/*==========================================
+ * char_id”Ô?‚Ì–¼‘O‚ð’T‚·
+ *------------------------------------------
+ */
+char * map_charid2nick(int id) {
+ struct charid2nick *p = (struct charid2nick*)idb_get(charid_db,id);
+
+ if(p==NULL)
+ return NULL;
+ return p->nick;
+}
+
+struct map_session_data * map_charid2sd(int id) {
+ int i, users;
+ struct map_session_data **all_sd;
+
+ if (id <= 0) return 0;
+
+ all_sd = map_getallusers(&users);
+ for(i = 0; i < users; i++)
+ if (all_sd[i] && all_sd[i]->status.char_id == id)
+ return all_sd[i];
+
+ return NULL;
+}
+
+/*==========================================
+ * Search session data from a nick name
+ * (without sensitive case if necessary)
+ * return map_session_data pointer or NULL
+ *------------------------------------------
+ */
+struct map_session_data * map_nick2sd(char *nick) {
+ int i, quantity=0, nicklen, users;
+ struct map_session_data *sd = NULL;
+ struct map_session_data *pl_sd = NULL, **pl_allsd;
+
+ if (nick == NULL)
+ return NULL;
+
+ nicklen = strlen(nick);
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++) {
+ pl_sd = pl_allsd[i];
+ // Without case sensitive check (increase the number of similar character names found)
+ if (strnicmp(pl_sd->status.name, nick, nicklen) == 0) {
+ // Strict comparison (if found, we finish the function immediatly with correct value)
+ if (strcmp(pl_sd->status.name, nick) == 0)
+ return pl_sd;
+ quantity++;
+ sd = pl_sd;
+ }
+ }
+ // Here, the exact character name is not found
+ // We return the found index of a similar account ONLY if there is 1 similar character
+ if (quantity == 1)
+ return sd;
+
+ // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found
+ return NULL;
+}
+
+/*==========================================
+ * id”Ô?‚Ì•¨‚ð’T‚·
+ * ˆêŽObject‚Ìꇂ͔z—ñ‚ðˆø‚­‚Ì‚Ý
+ *------------------------------------------
+ */
+struct block_list * map_id2bl(int id)
+{
+ struct block_list *bl=NULL;
+ if(id >= 0 && id < sizeof(objects)/sizeof(objects[0]))
+ bl = objects[id];
+ else
+ bl = idb_get(id_db,id);
+
+ return bl;
+}
+
+static int map_getallpc_sub(DBKey key,void * data,va_list ap)
+{
+ struct map_session_data *sd = (struct map_session_data*) data;
+ if (!sd->state.auth || sd->state.waitingdisconnect || sd->state.finalsave)
+ return 1; //Do not count in not-yet authenticated characters or ready to disconnect ones.
+
+ return 0;
+}
+
+/*==========================================
+ * Returns an array of all players in the server (includes non connected ones) [Skotlex]
+ * The int pointer given returns the count of elements in the array.
+ * If null is passed, it is requested that the memory be freed (for shutdown), and null is returned.
+ *------------------------------------------
+ */
+struct map_session_data** map_getallusers(int *users) {
+ static struct map_session_data **all_sd=NULL;
+ static unsigned int all_count = 0;
+
+ if (users == NULL)
+ { //Free up data
+ if (all_sd) aFree(all_sd);
+ all_sd = NULL;
+ return NULL;
+ }
+
+ if (all_sd == NULL)
+ { //Init data
+ all_count = pc_db->size(pc_db); //This is the real number of chars in the db, better use this than the actual "online" count.
+ if (all_count < 1)
+ all_count = 10; //Allow room for at least 10 chars.
+ all_sd = aCalloc(all_count, sizeof(struct map_session_data*)); //it's actually just the size of a pointer.
+ }
+
+ if (all_count < pc_db->size(pc_db))
+ {
+ all_count = pc_db->size(pc_db)+10; //Give some room to prevent doing reallocs often.
+ all_sd = aRealloc(all_sd, all_count*sizeof(struct map_session_data*));
+ }
+ *users = pc_db->getall(pc_db,(void**)all_sd,all_count,map_getallpc_sub);
+ if (*users > all_count) //Which should be impossible...
+ *users = all_count;
+ return all_sd;
+}
+
+/*==========================================
+ * id_db?‚Ì‘S‚Ä‚Éfunc‚ð?s
+ *------------------------------------------
+ */
+int map_foreachiddb(int (*func)(DBKey,void*,va_list),...) {
+ va_list ap;
+
+ va_start(ap,func);
+ id_db->foreach(id_db,func,ap);
+ va_end(ap);
+ return 0;
+}
+
+/*==========================================
+ * map.npc‚֒ljÁ (warp“™‚̗̈掂¿‚Ì‚Ý)
+ *------------------------------------------
+ */
+int map_addnpc(int m,struct npc_data *nd) {
+ int i;
+ if(m<0 || m>=map_num)
+ return -1;
+ for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++)
+ if(map[m].npc[i]==NULL)
+ break;
+ if(i==MAX_NPC_PER_MAP){
+ if(battle_config.error_log)
+ ShowWarning("too many NPCs in one map %s\n",map[m].name);
+ return -1;
+ }
+ if(i==map[m].npc_num){
+ map[m].npc_num++;
+ }
+
+ nullpo_retr(0, nd);
+
+ map[m].npc[i]=nd;
+ nd->n = i;
+ idb_put(id_db,nd->bl.id,nd);
+
+ return i;
+}
+
+void map_removenpc(void) {
+ int i,m,n=0;
+
+ for(m=0;m<map_num;m++) {
+ for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++) {
+ if(map[m].npc[i]!=NULL) {
+ clif_clearchar_area(&map[m].npc[i]->bl,2);
+ map_delblock(&map[m].npc[i]->bl);
+ idb_remove(id_db,map[m].npc[i]->bl.id);
+ if(map[m].npc[i]->bl.subtype==SCRIPT) {
+ aFree(map[m].npc[i]->u.scr.script);
+ aFree(map[m].npc[i]->u.scr.label_list);
+ }
+ aFree(map[m].npc[i]);
+ map[m].npc[i] = NULL;
+ n++;
+ }
+ }
+ }
+
+ ShowStatus("Successfully removed and freed from memory '"CL_WHITE"%d"CL_RESET"' NPCs.\n",n);
+}
+
+/*=========================================
+ * Dynamic Mobs [Wizputer]
+ *-----------------------------------------
+ */
+
+// allocates a struct when it there is place free in the cache,
+// and returns NULL otherwise
+// -- i'll just leave the old code in case it's needed ^^;
+struct mob_list* map_addmobtolist(unsigned short m)
+{
+ size_t i;
+ for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
+ if (map[m].moblist[i] == NULL) {
+ map[m].moblist[i] = (struct mob_list *) aMalloc (sizeof(struct mob_list));
+ return map[m].moblist[i];
+ }
+ }
+ return NULL;
+}
+
+void map_spawnmobs(int m)
+{
+ int i, k=0;
+ if (map[m].mob_delete_timer != -1)
+ { //Mobs have not been removed yet [Skotlex]
+ delete_timer(map[m].mob_delete_timer, map_removemobs_timer);
+ map[m].mob_delete_timer = -1;
+ return;
+ }
+ for(i=0; i<MAX_MOB_LIST_PER_MAP; i++)
+ if(map[m].moblist[i]!=NULL)
+ {
+ k+=map[m].moblist[i]->num;
+ npc_parse_mob2(map[m].moblist[i],1);
+ }
+
+ if (battle_config.etc_log && k > 0)
+ {
+ ShowStatus("Map %s: Spawned '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[m].name, k);
+ }
+}
+
+int mob_cache_cleanup_sub(struct block_list *bl, va_list ap) {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+
+ //When not to remove:
+ //Mob has the cached flag on 0
+ if (!md->special_state.cached)
+ return 0;
+ if (!battle_config.mob_remove_damaged &&
+ md->hp < md->db->max_hp) //don't use status_get_maxhp for speed (by the time you have to remove a mob, their status changes should have expired anyway)
+ return 0; //Do not remove damaged mobs.
+
+ mob_remove_map(md, 0);
+ map_deliddb(&md->bl);
+ aFree(md);
+ md = NULL;
+
+ return 1;
+}
+
+int map_removemobs_timer(int tid, unsigned int tick, int id, int data)
+{
+ int k;
+ if (id < 0 || id >= MAX_MAP_PER_SERVER)
+ { //Incorrect map id!
+ if (battle_config.error_log)
+ ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, id);
+ return 0;
+ }
+ if (map[id].mob_delete_timer != tid)
+ { //Incorrect timer call!
+ if (battle_config.error_log)
+ ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[id].mob_delete_timer, tid, map[id].name);
+ return 0;
+ }
+ map[id].mob_delete_timer = -1;
+ if (map[id].users > 0) //Map not empty!
+ return 1;
+ k = map_foreachinmap(mob_cache_cleanup_sub, id, BL_MOB);
+
+ if (battle_config.etc_log && k > 0)
+ ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[id].name, k);
+
+ return 1;
+}
+
+void map_removemobs(int m)
+{
+ if (map[m].mob_delete_timer != -1)
+ return; //Mobs are already scheduled for removal
+
+ map[m].mob_delete_timer = add_timer(gettick()+battle_config.mob_remove_delay, map_removemobs_timer, m, 0);
+}
+
+/*==========================================
+ * map–¼‚©‚çmap”Ô?‚Ö?Š·
+ *------------------------------------------
+ */
+int map_mapname2mapid(char *name) {
+ unsigned short map_index;
+ map_index = mapindex_name2id(name);
+ if (!map_index)
+ return -1;
+ return map_mapindex2mapid(map_index);
+}
+
+/*==========================================
+ * Returns the map of the given mapindex. [Skotlex]
+ *------------------------------------------
+ */
+int map_mapindex2mapid(unsigned short mapindex) {
+ struct map_data *md=NULL;
+
+ if (!mapindex)
+ return -1;
+
+ md = (struct map_data*)uidb_get(map_db,(unsigned int)mapindex);
+ if(md==NULL || md->gat==NULL)
+ return -1;
+ return md->m;
+}
+
+/*==========================================
+ * ‘¼ŽImap–¼‚©‚çip,port?Š·
+ *------------------------------------------
+ */
+int map_mapname2ipport(unsigned short name,int *ip,int *port) {
+ struct map_data_other_server *mdos=NULL;
+
+ mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)name);
+ if(mdos==NULL || mdos->gat) //If gat isn't null, this is a local map.
+ return -1;
+ *ip=mdos->ip;
+ *port=mdos->port;
+ return 0;
+}
+
+/*==========================================
+ * Checks if both dirs point in the same direction.
+ *------------------------------------------
+ */
+int map_check_dir(int s_dir,int t_dir) {
+ if(s_dir == t_dir)
+ return 0;
+ switch(s_dir) {
+ case 0:
+ if(t_dir == 7 || t_dir == 1 || t_dir == 0)
+ return 0;
+ break;
+ case 1:
+ if(t_dir == 0 || t_dir == 2 || t_dir == 1)
+ return 0;
+ break;
+ case 2:
+ if(t_dir == 1 || t_dir == 3 || t_dir == 2)
+ return 0;
+ break;
+ case 3:
+ if(t_dir == 2 || t_dir == 4 || t_dir == 3)
+ return 0;
+ break;
+ case 4:
+ if(t_dir == 3 || t_dir == 5 || t_dir == 4)
+ return 0;
+ break;
+ case 5:
+ if(t_dir == 4 || t_dir == 6 || t_dir == 5)
+ return 0;
+ break;
+ case 6:
+ if(t_dir == 5 || t_dir == 7 || t_dir == 6)
+ return 0;
+ break;
+ case 7:
+ if(t_dir == 6 || t_dir == 0 || t_dir == 7)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+/*==========================================
+ * Returns the direction of the given cell in absolute relation to the char
+ * (regardless of where the char is facing)
+ *------------------------------------------
+ */
+int map_calc_dir( struct block_list *src,int x,int y) {
+ int dir=0;
+ int dx,dy;
+
+ nullpo_retr(0, src);
+
+ dx=x-src->x;
+ dy=y-src->y;
+ if( dx==0 && dy==0 ){ // ”Þ‰ä‚Ìꊈê’v
+ dir=0; // ã
+ }else if( dx>=0 && dy>=0 ){ // •ûŒü“I‚ɉEã
+ dir=7; // ‰Eã
+ if( dx*2-1<dy ) dir=0; // ã
+ if( dx>dy*2 ) dir=6; // ‰E
+ }else if( dx>=0 && dy<=0 ){ // •ûŒü“I‚ɉE‰º
+ dir=5; // ‰E‰º
+ if( dx*2-1<-dy ) dir=4; // ‰º
+ if( dx>-dy*2 ) dir=6; // ‰E
+ }else if( dx<=0 && dy<=0 ){ // •ûŒü“I‚ɶ‰º
+ dir=3; // ¶‰º
+ if( dx*2+1>dy ) dir=4; // ‰º
+ if( dx<dy*2 ) dir=2; // ¶
+ }else{ // •ûŒü“I‚ɶã
+ dir=1; // ¶ã
+ if( -dx*2-1<dy ) dir=0; // ã
+ if( -dx>dy*2 ) dir=2; // ¶
+ }
+ return dir;
+}
+
+/*==========================================
+ * Randomizes target cell x,y to a random walkable cell that
+ * has the same distance from object as given coordinates do. [Skotlex]
+ *------------------------------------------
+ */
+int map_random_dir(struct block_list *bl, short *x, short *y) {
+ short xi = *x-bl->x;
+ short yi = *y-bl->y;
+ short i=0, j;
+ int dist2 = xi*xi + yi*yi;
+ short dist = (short)sqrt(dist2);
+ short segment;
+
+ if (dist < 1) dist =1;
+
+ do {
+ j = rand()%8; //Pick a random direction
+ segment = rand()%dist; //Pick a random interval from the whole vector in that direction
+ xi = bl->x + segment*dirx[j];
+ segment = (short)sqrt(dist2 - segment*segment); //The complement of the previously picked segment
+ yi = bl->y + segment*diry[j];
+ } while (map_getcell(bl->m,xi,yi,CELL_CHKNOPASS) && (++i)<100);
+ if (i < 100) {
+ *x = xi;
+ *y = yi;
+ return 1;
+ }
+ return 0;
+}
+// gatŒn
+/*==========================================
+ * (m,x,y)‚Ìó‘Ԃ𒲂ׂé
+ *------------------------------------------
+ */
+
+int map_getcell(int m,int x,int y,cell_t cellchk)
+{
+ return (m < 0 || m >= MAX_MAP_PER_SERVER) ? 0 : map_getcellp(&map[m],x,y,cellchk);
+}
+
+int map_getcellp(struct map_data* m,int x,int y,cell_t cellchk)
+{
+ int type, type2;
+#ifdef CELL_NOSTACK
+ int type3;
+#endif
+
+ nullpo_ret(m);
+
+ if(x<0 || x>=m->xs-1 || y<0 || y>=m->ys-1)
+ {
+ if(cellchk==CELL_CHKNOPASS) return 1;
+ return 0;
+ }
+ type = m->gat[x+y*m->xs];
+ type2 = m->cell[x+y*m->xs];
+#ifdef CELL_NOSTACK
+ type3 = m->cell_bl[x+y*m->xs];
+#endif
+
+ switch(cellchk)
+ {
+ case CELL_CHKPASS:
+#ifdef CELL_NOSTACK
+ if (type3 >= battle_config.cell_stack_limit) return 0;
+#endif
+ return (type!=1 && type!=5 && !(type2&(CELL_MOONLIT|CELL_ICEWALL)));
+ case CELL_CHKNOPASS:
+#ifdef CELL_NOSTACK
+ if (type3 >= battle_config.cell_stack_limit) return 1;
+#endif
+ return (type==1 || type==5 || type2&(CELL_MOONLIT|CELL_ICEWALL));
+ case CELL_CHKWALL:
+ return (type==1/* || type2&CELL_ICEWALL*/); //Uncomment to prevent sniping/casting through the icewall. [Skotlex]
+ case CELL_CHKWATER:
+ return (type==3);
+ case CELL_CHKGROUND:
+ return (type==5 || type2&CELL_ICEWALL);
+ case CELL_GETTYPE:
+ return type;
+ case CELL_GETCELLTYPE:
+ return type2;
+ case CELL_CHKNPC:
+ return (type2&CELL_NPC);
+ case CELL_CHKPNEUMA:
+ return (type2&CELL_PNEUMA);
+ case CELL_CHKSAFETYWALL:
+ return (type2&CELL_SAFETYWALL);
+ case CELL_CHKBASILICA:
+ return (type2&CELL_BASILICA);
+ case CELL_CHKLANDPROTECTOR:
+ return (type2&CELL_LANDPROTECTOR);
+ case CELL_CHKMOONLIT:
+ return (type2&CELL_MOONLIT);
+ case CELL_CHKREGEN:
+ return (type2&CELL_REGEN);
+ case CELL_CHKICEWALL:
+ return (type2&CELL_ICEWALL);
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+ * (m,x,y)‚Ìó‘Ô‚ðÝ’è‚·‚é
+ *------------------------------------------
+ */
+void map_setcell(int m,int x,int y,int cell)
+{
+ int j;
+ if(x<0 || x>=map[m].xs || y<0 || y>=map[m].ys)
+ return;
+ j=x+y*map[m].xs;
+
+ switch (cell) {
+ case CELL_SETNPC:
+ map[m].cell[j] |= CELL_NPC;
+ break;
+ case CELL_CLRNPC:
+ map[m].cell[j] &= ~CELL_NPC;
+ break;
+ case CELL_SETICEWALL:
+ map[m].cell[j] |= CELL_ICEWALL;
+ break;
+ case CELL_CLRICEWALL:
+ map[m].cell[j] &= ~CELL_ICEWALL;
+ break;
+ case CELL_SETBASILICA:
+ map[m].cell[j] |= CELL_BASILICA;
+ break;
+ case CELL_CLRBASILICA:
+ map[m].cell[j] &= ~CELL_BASILICA;
+ break;
+ case CELL_SETPNEUMA:
+ map[m].cell[j] |= CELL_PNEUMA;
+ break;
+ case CELL_CLRPNEUMA:
+ map[m].cell[j] &= ~CELL_PNEUMA;
+ break;
+ case CELL_SETSAFETYWALL:
+ map[m].cell[j] |= CELL_SAFETYWALL;
+ break;
+ case CELL_CLRSAFETYWALL:
+ map[m].cell[j] &= ~CELL_SAFETYWALL;
+ break;
+ case CELL_SETMOONLIT:
+ map[m].cell[j] |= CELL_MOONLIT;
+ break;
+ case CELL_CLRMOONLIT:
+ map[m].cell[j] &= ~CELL_MOONLIT;
+ break;
+ case CELL_SETLANDPROTECTOR:
+ map[m].cell[j] |= CELL_LANDPROTECTOR;
+ break;
+ case CELL_CLRLANDPROTECTOR:
+ map[m].cell[j] &= ~CELL_LANDPROTECTOR;
+ break;
+ case CELL_SETREGEN:
+ map[m].cell[j] |= CELL_REGEN;
+ break;
+ default:
+ map[m].gat[j] = cell;
+ break;
+ }
+}
+static void* create_map_data_other_server(DBKey key, va_list args) {
+ struct map_data_other_server *mdos;
+ unsigned short mapindex = (unsigned short)key.ui;
+ mdos=(struct map_data_other_server *)aCalloc(1,sizeof(struct map_data_other_server));
+ mdos->index = mapindex;
+ memcpy(mdos->name, mapindex_id2name(mapindex), MAP_NAME_LENGTH);
+ return mdos;
+}
+/*==========================================
+ * ‘¼ŽIŠÇ—‚̃}ƒbƒv‚ðdb‚ɒljÁ
+ *------------------------------------------
+ */
+int map_setipport(unsigned short mapindex,unsigned long ip,int port) {
+ struct map_data_other_server *mdos=NULL;
+
+ mdos=(struct map_data_other_server *)uidb_ensure(map_db,(unsigned int)mapindex, create_map_data_other_server);
+
+ if(mdos->gat) //Local map,Do nothing. Give priority to our own local maps over ones from another server. [Skotlex]
+ return 0;
+ if(ip == clif_getip() && port == clif_getport()) {
+ //That's odd, we received info that we are the ones with this map, but... we don't have it.
+ ShowFatalError("map_setipport : received info that this map-server SHOULD have map '%s', but it is not loaded.\n",mapindex_id2name(mapindex));
+ exit(1);
+ }
+ mdos->ip = ip;
+ mdos->port = port;
+ return 1;
+}
+
+/*==========================================
+ * ‘¼ŽIŠÇ—‚̃}ƒbƒv‚ð‘S‚Äíœ
+ *------------------------------------------
+ */
+int map_eraseallipport_sub(DBKey key,void *data,va_list va) {
+ struct map_data_other_server *mdos = (struct map_data_other_server*)data;
+ if(mdos->gat == NULL) {
+ db_remove(map_db,key);
+ aFree(mdos);
+ }
+ return 0;
+}
+
+int map_eraseallipport(void) {
+ map_db->foreach(map_db,map_eraseallipport_sub);
+ return 1;
+}
+
+/*==========================================
+ * ‘¼ŽIŠÇ—‚̃}ƒbƒv‚ðdb‚©‚çíœ
+ *------------------------------------------
+ */
+int map_eraseipport(unsigned short mapindex,unsigned long ip,int port)
+{
+ struct map_data_other_server *mdos;
+// unsigned char *p=(unsigned char *)&ip;
+
+ mdos = uidb_get(map_db,(unsigned int)mapindex);
+ if(!mdos || mdos->gat) //Map either does not exists or is a local map.
+ return 0;
+
+ if(mdos->ip==ip && mdos->port == port) {
+ uidb_remove(map_db,(unsigned int)mapindex);
+ aFree(mdos);
+ return 1;
+ }
+ return 0;
+}
+
+// ‰Šú‰»Žü‚è
+/*==========================================
+ * …ê‚‚³Ý’è
+ *------------------------------------------
+ */
+static struct waterlist_ {
+ char mapname[MAP_NAME_LENGTH];
+ int waterheight;
+} *waterlist=NULL;
+
+#define NO_WATER 1000000
+
+static int map_setwaterheight_sub(int m, int wh) {
+ char fn[256];
+ char *gat;
+ int x,y;
+ struct gat_1cell {float high[4]; int type;} *p = NULL;
+
+ if (m < 0)
+ return 0;
+
+ sprintf(fn,"data\\%s",mapindex_id2name(map[m].index));
+
+ // read & convert fn
+ // again, might not need to be unsigned char
+ gat = (char *) grfio_read (fn);
+ if (gat == NULL)
+ return 0;
+
+ for (y = 0; y < map[m].ys; y++) {
+ p = (struct gat_1cell*)(gat+y*map[m].xs*20+14);
+ for (x = 0; x < map[m].xs; x++) {
+ if (wh != NO_WATER && p->type == 0) //Set water cell
+ map[m].gat[x+y*map[m].xs] = (p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0;
+ else //Remove water cell
+ map[m].gat[x+y*map[m].xs] = p->type==3?0:p->type;
+ p++;
+ }
+ }
+ aFree(gat);
+ return 1;
+}
+int map_setwaterheight(int m, char *mapname, int height) {
+ int i=0;
+ if (height < 0)
+ height = NO_WATER;
+ if(waterlist){
+ for(i=0;waterlist[i].mapname[0] && i < MAX_MAP_PER_SERVER;i++)
+ if(strcmp(waterlist[i].mapname,mapname)==0) {
+ waterlist[i].waterheight = height;
+ }
+ }
+ if (i < MAX_MAP_PER_SERVER) {
+ memcpy(waterlist[i].mapname,mapname, MAP_NAME_LENGTH-1);
+ waterlist[i].waterheight = height;
+ }
+ return map_setwaterheight_sub(m, height);
+}
+
+int map_waterheight(char *mapname) {
+ if(waterlist){
+ int i;
+ for(i=0;waterlist[i].mapname[0] && i < MAX_MAP_PER_SERVER;i++)
+ if(strcmp(waterlist[i].mapname,mapname)==0)
+ return waterlist[i].waterheight;
+ }
+ return NO_WATER;
+}
+
+static void map_readwater(char *watertxt) {
+ char line[1024],w1[1024];
+ FILE *fp=NULL;
+ int n=0;
+
+ fp=fopen(watertxt,"r");
+ if(fp==NULL){
+ ShowError("file not found: %s\n",watertxt);
+ return;
+ }
+ if(waterlist==NULL)
+ waterlist = (struct waterlist_*)aCallocA(MAX_MAP_PER_SERVER,sizeof(*waterlist));
+ while(fgets(line,1020,fp) && n < MAX_MAP_PER_SERVER){
+ int wh,count;
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ if((count=sscanf(line,"%s%d",w1,&wh)) < 1){
+ continue;
+ }
+ memcpy(waterlist[n].mapname,w1, MAP_NAME_LENGTH-1);
+ if(count >= 2)
+ waterlist[n].waterheight = wh;
+ else
+ waterlist[n].waterheight = 3;
+ n++;
+ }
+ fclose(fp);
+}
+/*==========================================
+* ƒ}ƒbƒvƒLƒƒƒbƒVƒ…‚ɒljÁ‚·‚é
+*===========================================*/
+
+// ƒ}ƒbƒvƒLƒƒƒbƒVƒ…‚ÌÅ‘å’l
+#define MAX_MAP_CACHE 768
+
+//Šeƒ}ƒbƒv‚²‚Æ‚ÌŬŒÀî•ñ‚ð“ü‚ê‚é‚à‚ÌAREAD_FROM_BITMAP—p
+struct map_cache_info {
+ char fn[32];//ƒtƒ@ƒCƒ‹–¼
+ int xs,ys; //•‚Æ‚‚³
+ int water_height;
+ int pos; // ƒf[ƒ^‚ª“ü‚ê‚Ä‚ ‚éêŠ
+ int compressed; // zilb’Ê‚¹‚é‚悤‚É‚·‚éˆ×‚Ì—\–ñ
+ int compressed_len; // zilb’Ê‚¹‚é‚悤‚É‚·‚éˆ×‚Ì—\–ñ
+}; // 56 byte
+
+struct map_cache_head {
+ int sizeof_header;
+ int sizeof_map;
+ // ã‚Ì‚Q‚‰ü•Ï•s‰Â
+ int nmaps; // ƒ}ƒbƒv‚̌”
+ int filesize;
+};
+
+struct {
+ struct map_cache_head head;
+ struct map_cache_info *map;
+ FILE *fp;
+ int dirty;
+} map_cache;
+
+static int map_cache_open(char *fn);
+static void map_cache_close(void);
+static int map_cache_read(struct map_data *m);
+static int map_cache_write(struct map_data *m);
+
+static int map_cache_open(char *fn)
+{
+ if (map_cache.fp)
+ map_cache_close();
+ map_cache.fp = fopen(fn, "r+b");
+ if (map_cache.fp) {
+ fread(&map_cache.head,1,sizeof(struct map_cache_head),map_cache.fp);
+ fseek(map_cache.fp,0,SEEK_END);
+ if(
+ map_cache.head.sizeof_header == sizeof(struct map_cache_head) &&
+ map_cache.head.sizeof_map == sizeof(struct map_cache_info) &&
+ map_cache.head.filesize == ftell(map_cache.fp)
+ ) {
+ // ƒLƒƒƒbƒVƒ…“Ç‚ÝbݬŒ÷
+ map_cache.map = (struct map_cache_info *) aMalloc(sizeof(struct map_cache_info) * map_cache.head.nmaps);
+ fseek(map_cache.fp,sizeof(struct map_cache_head),SEEK_SET);
+ fread(map_cache.map,sizeof(struct map_cache_info),map_cache.head.nmaps,map_cache.fp);
+ return 1;
+ }
+ fclose(map_cache.fp);
+ }
+ // “Ç‚ÝbÝ‚ÉŽ¸”s‚µ‚½‚Ì‚ÅV‹K‚É쬂·‚é
+ map_cache.fp = fopen(fn,"wb");
+ if(map_cache.fp) {
+ memset(&map_cache.head,0,sizeof(struct map_cache_head));
+ map_cache.map = (struct map_cache_info *) aCalloc(sizeof(struct map_cache_info),MAX_MAP_CACHE);
+ map_cache.head.nmaps = MAX_MAP_CACHE;
+ map_cache.head.sizeof_header = sizeof(struct map_cache_head);
+ map_cache.head.sizeof_map = sizeof(struct map_cache_info);
+
+ map_cache.head.filesize = sizeof(struct map_cache_head);
+ map_cache.head.filesize += sizeof(struct map_cache_info) * map_cache.head.nmaps;
+
+ map_cache.dirty = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static void map_cache_close(void)
+{
+ if(!map_cache.fp) { return; }
+ if(map_cache.dirty) {
+ fseek(map_cache.fp,0,SEEK_SET);
+ fwrite(&map_cache.head,1,sizeof(struct map_cache_head),map_cache.fp);
+ fwrite(map_cache.map,map_cache.head.nmaps,sizeof(struct map_cache_info),map_cache.fp);
+ }
+ fclose(map_cache.fp);
+ aFree(map_cache.map);
+ map_cache.fp = NULL;
+ return;
+}
+
+int map_cache_read(struct map_data *m)
+{
+ int i;
+ if(!map_cache.fp) { return 0; }
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(!strcmp(m->name,map_cache.map[i].fn)) {
+ if(map_cache.map[i].water_height != map_waterheight(m->name)) {
+ // …ê‚Ì‚‚³‚ªˆá‚¤‚Ì‚Å“Ç‚Ý’¼‚µ
+ return 0;
+ } else if(map_cache.map[i].compressed == 0) {
+ // ”ñˆ³kƒtƒ@ƒCƒ‹
+ int size = map_cache.map[i].xs * map_cache.map[i].ys;
+ m->xs = map_cache.map[i].xs;
+ m->ys = map_cache.map[i].ys;
+ m->gat = (unsigned char *)aCalloc(m->xs * m->ys,sizeof(unsigned char));
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ if(fread(m->gat,1,size,map_cache.fp) == size) {
+ // ¬Œ÷
+ return 1;
+ } else {
+ // ‚È‚º‚©ƒtƒ@ƒCƒ‹Œã”¼‚ªŒ‡‚¯‚Ä‚é‚Ì‚Å“Ç‚Ý’¼‚µ
+ m->xs = 0; m->ys = 0; aFree(m->gat); m->gat = NULL;
+ return 0;
+ }
+ } else if(map_cache.map[i].compressed == 1) {
+ // ˆ³kƒtƒ‰ƒO=1 : zlib
+ unsigned char *buf;
+ unsigned long dest_len;
+ int size_compress = map_cache.map[i].compressed_len;
+ m->xs = map_cache.map[i].xs;
+ m->ys = map_cache.map[i].ys;
+ m->gat = (unsigned char *)aMalloc(m->xs * m->ys * sizeof(unsigned char));
+ buf = (unsigned char*)aMalloc(size_compress);
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ if(fread(buf,1,size_compress,map_cache.fp) != size_compress) {
+ // ‚È‚º‚©ƒtƒ@ƒCƒ‹Œã”¼‚ªŒ‡‚¯‚Ä‚é‚Ì‚Å“Ç‚Ý’¼‚µ
+ ShowError("fread error\n");
+ aFree(m->gat); m->xs = 0; m->ys = 0; m->gat = NULL;
+ aFree(buf);
+ return 0;
+ }
+ dest_len = m->xs * m->ys;
+ decode_zip(m->gat,&dest_len,buf,size_compress);
+ if(dest_len != map_cache.map[i].xs * map_cache.map[i].ys) {
+ // ³í‚ɉ𓀂ªo—ˆ‚Ä‚È‚¢
+ aFree(m->gat); m->xs = 0; m->ys = 0; m->gat = NULL;
+ aFree(buf);
+ return 0;
+ }
+ aFree(buf);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int map_cache_write(struct map_data *m)
+{
+ int i;
+ unsigned long len_new , len_old;
+ char *write_buf;
+ if(!map_cache.fp) { return 0; }
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(!strcmp(m->name,map_cache.map[i].fn)) {
+ // “¯‚¶ƒGƒ“ƒgƒŠ[‚ª‚ ‚ê‚Îã‘‚«
+ if(map_cache.map[i].compressed == 0) {
+ len_old = map_cache.map[i].xs * map_cache.map[i].ys;
+ } else if(map_cache.map[i].compressed == 1) {
+ len_old = map_cache.map[i].compressed_len;
+ } else {
+ // ƒTƒ|[ƒg‚³‚ê‚Ä‚È‚¢Œ`Ž®‚È‚Ì‚Å’·‚³‚O
+ len_old = 0;
+ }
+ if(map_read_flag == 2) {
+ // ˆ³k•Û‘¶
+ // ‚³‚·‚ª‚É‚Q”{‚É–c‚ê‚鎖‚Í‚È‚¢‚Æ‚¢‚¤Ž–‚Å
+ write_buf = (char *) aMalloc(m->xs * m->ys * 2);
+ len_new = m->xs * m->ys * 2;
+ encode_zip((unsigned char *) write_buf,&len_new,m->gat,m->xs * m->ys);
+ map_cache.map[i].compressed = 1;
+ map_cache.map[i].compressed_len = len_new;
+ } else {
+ len_new = m->xs * m->ys;
+ write_buf = (char *) m->gat;
+ map_cache.map[i].compressed = 0;
+ map_cache.map[i].compressed_len = 0;
+ }
+ if(len_new <= len_old) {
+ // ƒTƒCƒY‚ª“¯‚¶‚©¬‚³‚­‚È‚Á‚½‚Ì‚Åꊂ͕ςí‚ç‚È‚¢
+ fseek(map_cache.fp,map_cache.map[i].pos,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ } else {
+ // V‚µ‚¢êŠ‚É“o˜^
+ fseek(map_cache.fp,map_cache.head.filesize,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ map_cache.map[i].pos = map_cache.head.filesize;
+ map_cache.head.filesize += len_new;
+ }
+ map_cache.map[i].xs = m->xs;
+ map_cache.map[i].ys = m->ys;
+ map_cache.map[i].water_height = map_waterheight(m->name);
+ map_cache.dirty = 1;
+ if(map_read_flag == 2) {
+ aFree(write_buf);
+ }
+ return 0;
+ }
+ }
+ // “¯‚¶ƒGƒ“ƒgƒŠ‚ª–³‚¯‚ê‚Α‚«bß‚éꊂð’T‚·
+ for(i = 0;i < map_cache.head.nmaps ; i++) {
+ if(map_cache.map[i].fn[0] == 0) {
+ // V‚µ‚¢êŠ‚É“o˜^
+ if(map_read_flag == 2) {
+ write_buf = (char *) aMalloc(m->xs * m->ys * 2);
+ len_new = m->xs * m->ys * 2;
+ encode_zip((unsigned char *) write_buf,&len_new,m->gat,m->xs * m->ys);
+ map_cache.map[i].compressed = 1;
+ map_cache.map[i].compressed_len = len_new;
+ } else {
+ len_new = m->xs * m->ys;
+ write_buf = (char *) m->gat;
+ map_cache.map[i].compressed = 0;
+ map_cache.map[i].compressed_len = 0;
+ }
+ strncpy(map_cache.map[i].fn,m->name,sizeof(map_cache.map[0].fn));
+ fseek(map_cache.fp,map_cache.head.filesize,SEEK_SET);
+ fwrite(write_buf,1,len_new,map_cache.fp);
+ map_cache.map[i].pos = map_cache.head.filesize;
+ map_cache.map[i].xs = m->xs;
+ map_cache.map[i].ys = m->ys;
+ map_cache.map[i].water_height = map_waterheight(m->name);
+ map_cache.head.filesize += len_new;
+ map_cache.dirty = 1;
+ if(map_read_flag == 2) {
+ aFree(write_buf);
+ }
+ return 0;
+ }
+ }
+ // ‘‚«bß‚È‚©‚Á‚½
+ return 1;
+}
+
+/*==========================================
+ * ?‚Ý?‚Þmap‚ð’ljÁ‚·‚é
+ *------------------------------------------
+ */
+int map_addmap(char *mapname) {
+ if (strcmpi(mapname,"clear")==0) {
+ map_num=0;
+ return 0;
+ }
+
+ if (map_num >= MAX_MAP_PER_SERVER - 1) {
+ ShowError("Could not add map '"
+ CL_WHITE"%s"CL_RESET"', the limit of maps has been reached.\n",mapname);
+ return 1;
+ }
+ memcpy(map[map_num].name, mapname, MAP_NAME_LENGTH-1);
+ map_num++;
+ return 0;
+}
+
+/*==========================================
+ * Removes the map in the index passed.
+ *------------------------------------------
+ */
+static void map_delmapid(int id)
+{
+ ShowNotice("Removing map [ %s ] from maplist\n",map[id].name);
+ memmove(map+id, map+id+1, sizeof(map[0])*(map_num-id-1));
+ map_num--;
+}
+
+/*==========================================
+ * ?‚Ý?‚Þmap‚ð휂·‚é
+ *------------------------------------------
+ */
+int map_delmap(char *mapname) {
+
+ int i;
+
+ if (strcmpi(mapname, "all") == 0) {
+ map_num = 0;
+ return 0;
+ }
+
+ for(i = 0; i < map_num; i++) {
+ if (strcmp(map[i].name, mapname) == 0) {
+ map_delmapid(i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////
+
+/*
+ Advanced Fusion Maps Support
+ (c) 2003-2004, The Fusion Project
+ - AlexKreuz
+
+ The following code has been provided by me for eAthena
+ under the GNU GPL. It provides Advanced Fusion
+ Map, the map format desgined by me for Fusion, support
+ for the eAthena emulator.
+
+ I understand that because it is under the GPL
+ that other emulators may very well use this code in their
+ GNU project as well.
+
+ The AFM map format was not originally a part of the GNU
+ GPL. It originated from scratch by my own hand. I understand
+ that distributing this code to read the AFM maps with eAthena
+ causes the GPL to apply to this code. But the actual AFM
+ maps are STILL copyrighted to the Fusion Project. By choosing
+
+ In exchange for that 'act of faith' I ask for the following.
+
+ A) Give credit where it is due. If you use this code, do not
+ place your name on the changelog. Credit should be given
+ to AlexKreuz.
+ B) As an act of courtesy, ask me and let me know that you are putting
+ AFM support in your project. You will have my blessings if you do.
+ C) Use the code in its entirety INCLUDING the copyright message.
+ Although the code provided may now be GPL, the AFM maps are not
+ and so I ask you to display the copyright message on the STARTUP
+ SCREEN as I have done here. (refer to core.c)
+ "Advanced Fusion Maps (c) 2003-2004 The Fusion Project"
+
+ Without this copyright, you are NOT entitled to bundle or distribute
+ the AFM maps at all. On top of that, your "support" for AFM maps
+ becomes just as shady as your "support" for Gravity GRF files.
+
+ The bottom line is this. I know that there are those of you who
+ would like to use this code but aren't going to want to provide the
+ proper credit. I know this because I speak frome experience. If
+ you are one of those people who is going to try to get around my
+ requests, then save your breath because I don't want to hear it.
+
+ I have zero faith in GPL and I know and accept that if you choose to
+ not display the copyright for the AFMs then there is absolutely nothing
+ I can do about it. I am not about to start a legal battle over something
+ this silly.
+
+ Provide the proper credit because you believe in the GPL. If you choose
+ not to and would rather argue about it, consider the GPL failed.
+
+ October 18th, 2004
+ - AlexKreuz
+ - The Fusion Project
+ */
+static int map_loadafm (struct map_data *m, char *fn)
+{
+ // check if .afm file exists
+ FILE *afm_file = fopen(fn, "r");
+ if (afm_file != NULL) {
+ int x,y,xs,ys;
+ char afm_line[65535];
+ int afm_size[2];
+ char *str;
+
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ sscanf(str , "%d%d", &afm_size[0], &afm_size[1]);
+
+ xs = m->xs = afm_size[0];
+ ys = m->ys = afm_size[1];
+ // check this, unsigned where it might not need to be
+ m->gat = (unsigned char*)aCallocA(xs * ys, 1);
+
+ for (y = 0; y < ys; y++) {
+ str = fgets(afm_line, sizeof(afm_line)-1, afm_file);
+ for (x = 0; x < xs; x++)
+ m->gat[x+y*xs] = str[x]-48;
+ }
+
+ fclose(afm_file);
+ return 1;
+ }
+
+ return 0;
+}
+/*==================================
+ * .AFM format
+ *----------------------------------
+ */
+int map_readafm (struct map_data *m)
+{
+ char afm_name[256] = "";
+ char fn[256], *p;
+
+ // convert map name to .afm
+ if(!strstr(m->name, ".afm")) {
+ // check if it's necessary to replace the extension - speeds up loading a bit
+ strncpy(afm_name, m->name, strlen(m->name) - 4);
+ strcat(afm_name, ".afm");
+ }
+
+ sprintf(fn, "%s\\%s", afm_dir, afm_name);
+ for (p = &fn[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ return map_loadafm(m, fn);
+}
+/*==================================
+ * .AF2 format
+ *----------------------------------
+ */
+int map_readaf2 (struct map_data *m)
+{
+ FILE *af2_file;
+ char af2_name[256] = "";
+ char fn[256], *p, *out;
+
+ // convert map name to .af2
+ p = out = m->name;
+ while ((p = strchr(p, '/')) != NULL)
+ out = ++p;
+ strncpy (af2_name, out, strlen(out));
+ // grr, this is so troublesome >.< [celest]
+ p = strrchr (af2_name, '.');
+ if (p) *p++ = 0;
+ strcat(af2_name, ".af2");
+ sprintf(fn, "%s\\%s", afm_dir, af2_name);
+ for (p = &fn[0]; *p != 0; p++)
+ if (*p == '\\') *p = '/'; // * At the time of Unix
+
+ // check if .af2 file exists
+ af2_file = fopen(fn, "r");
+ if (af2_file != NULL) {
+ char out_file[256];
+
+ fclose(af2_file);
+
+ // convert map name to .out
+ strncpy (out_file, out, strlen(out));
+ p = strrchr (out_file, '.');
+ if (p) *p++ = 0;
+ strcat(out_file, ".out");
+
+ // unzip .out file and use loadafm()
+ if (deflate_file(fn, out_file) &&
+ map_loadafm(m, out_file))
+ {
+ unlink (out_file);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * ƒ}ƒbƒv1–‡“Ç‚ÝbÝ
+ * ===================================================*/
+//static int map_readmap(int m,char *fn, char *alias, int *map_cache, int maxmap) {
+
+/*==================================
+ * .GAT format
+ *----------------------------------
+ */
+int map_readgat (struct map_data *m)
+{
+ char fn[256], *pt;
+ char *gat;
+ int wh,x,y,xs,ys;
+ struct gat_1cell {float high[4]; int type;} *p = NULL;
+
+ if (strstr(m->name,".gat") == NULL)
+ return 0;
+
+ if ((pt = strstr(m->name,"<")) != NULL) { // [MouseJstr]
+ char buf[64];
+ *pt++ = '\0';
+ sprintf(buf,"data\\%s", pt);
+ m->alias = aStrdup(buf);
+ }
+
+ sprintf(fn,"data\\%s",m->name);
+
+ // read & convert fn
+ // again, might not need to be unsigned char
+ gat = (char *) grfio_read (fn);
+ if (gat == NULL)
+ return 0;
+
+ xs = m->xs = *(int*)(gat+6);
+ ys = m->ys = *(int*)(gat+10);
+ m->gat = (unsigned char *)aCallocA(m->xs * m->ys, sizeof(unsigned char));
+
+ wh = map_waterheight(m->name);
+ for (y = 0; y < ys; y++) {
+ p = (struct gat_1cell*)(gat+y*xs*20+14);
+ for (x = 0; x < xs; x++) {
+ if (wh != NO_WATER && p->type == 0)
+ // …ê”»’è
+ m->gat[x+y*xs] = (p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0;
+ else
+ m->gat[x+y*xs] = p->type;
+ p++;
+ }
+ }
+
+ aFree(gat);
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////
+
+static int map_cache_init (void);
+static int map_readafm_init (void);
+static int map_readaf2_init (void);
+static int map_readgat_init (void);
+
+// Todo: Properly implement this system as plugins/safer code [Celest]
+enum {
+ MAP_CACHE = 0, // jAthena map cache
+ MAP_AFM, // Advanced Fusion Map
+ MAP_AF2, // Advanced Fusion Map
+ MAP_GAT, // GRF map
+ MAP_MAXSOURCE
+};
+// in descending order
+int (*mapsource_init[MAP_MAXSOURCE])(void) = {
+ map_cache_init,
+ map_readafm_init,
+ map_readaf2_init,
+ map_readgat_init
+};
+int (*mapsource_read[MAP_MAXSOURCE])(struct map_data *) = {
+ map_cache_read,
+ map_readafm,
+ map_readaf2,
+ map_readgat
+};
+void (*mapsource_final[MAP_MAXSOURCE])(void) = {
+ map_cache_close,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int map_cache_init (void)
+{
+ if (map_read_flag >= READ_FROM_BITMAP && map_cache_open(map_cache_file)) {
+ ShowMessage("[cache] ");
+ return 1;
+ }
+
+ return 0;
+}
+static int map_readafm_init (void)
+{
+ ShowMessage("[afm] ");
+ return 1;
+}
+static int map_readaf2_init (void)
+{
+ // check if AFM loading is available,
+ // otherwise disable AF2 loading
+ if (mapsource_read[1] != NULL) {
+ ShowMessage("[af2] ");
+ return 1;
+ }
+
+ return 0;
+}
+static int map_readgat_init (void)
+{
+ ShowMessage("[gat] ");
+ return 1;
+}
+
+/*======================================
+ * Initiate maps loading stage
+ *--------------------------------------
+ */
+int map_readallmaps (void)
+{
+ // pre-loading stage
+ int i;
+ int maps_removed = 0;
+ int maps_cached = 0;
+
+ ShowMessage(CL_GREEN"[Status]"CL_RESET": Loading Maps with... "CL_WHITE);
+
+ for (i = 0; i < MAP_MAXSOURCE; i++) {
+ if (mapsource_init[i] && // check if source requires initialisation
+ mapsource_init[i]() == 0) // if init failed
+ {
+ // remove all loading methods associated with this source
+ mapsource_init[i] = NULL;
+ mapsource_read[i] = NULL;
+ mapsource_final[i] = NULL;
+ }
+ }
+
+ ShowMessage(CL_RESET"\n");
+
+ // initiate map loading
+ for (i = 0; i < map_num; i++)
+ {
+ int success = 0;
+ static int lasti = -1;
+ static int last_time = -1;
+ int j = i*20/map_num;
+
+ // show progress
+ if (map_num && //avoid map-server crashing if there are 0 maps
+ (j != lasti || last_time != time(0)))
+ {
+ char progress[21] = " ";
+ char c = '-';
+ int k;
+
+ lasti = j;
+ printf("\r");
+ ShowStatus("Progress: [");
+ for (k=0; k < j; k++) progress[k] = '#';
+ printf(progress);
+ last_time = (int)time(0);
+ switch(last_time % 4) {
+ case 0: c='\\'; break;
+ case 1: c='|'; break;
+ case 2: c='/'; break;
+ case 3: c='-'; break;
+ }
+ printf("] Working: [%c]",c);
+ fflush(stdout);
+ }
+
+ // pre-init some data
+ map[i].alias = NULL;
+ map[i].m = i;
+ memset (map[i].moblist, 0, sizeof(map[i].moblist)); //Initialize moblist [Skotlex]
+ map[i].mob_delete_timer = -1; //Initialize timer [Skotlex]
+ if (battle_config.pk_mode)
+ map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris]
+
+ for (j = 0; j < MAP_MAXSOURCE; j++)
+ {
+ if (mapsource_read[j] && // check if map source is valid
+ mapsource_read[j](&map[i])) // check if map source is available
+ {
+ // successful, now initialise map
+ size_t size;
+ char *alias;
+
+ if (map[i].alias && (alias = strstr(map[i].name, "<")) != NULL) { // alias has been set by one of the sources
+ *alias++ = '\0';
+ }
+ if (map[i].alias)
+ map[i].index = mapindex_name2id(map[i].alias);
+ else
+ map[i].index = mapindex_name2id(map[i].name);
+
+ if (!map[i].index) {
+ if (map[i].alias)
+ ShowWarning("Map %s (alias %s) is not in the map-index cache!\n", map[i].name, map[i].alias);
+ else
+ ShowWarning("Map %s is not in the map-index cache!\n", map[i].name);
+ success = 0; //Can't load a map that isn't in our cache.
+ if (map[i].gat) {
+ aFree(map[i].gat);
+ map[i].gat = NULL;
+ }
+ break;
+ }
+ if (uidb_get(map_db,(unsigned int)map[i].index) != NULL) {
+ ShowWarning("Map %s already loaded!\n", map[i].name);
+ success = 0; //Can't load a map already in the db
+ if (map[i].gat) {
+ aFree(map[i].gat);
+ map[i].gat = NULL;
+ }
+ break;
+ }
+
+ map[i].cell = (unsigned char *)aCalloc(map[i].xs * map[i].ys, sizeof(unsigned char));
+#ifdef CELL_NOSTACK
+ map[i].cell_bl = (unsigned char *)aCalloc(map[i].xs * map[i].ys, sizeof(unsigned char));
+#endif
+
+ map[i].bxs = (map[i].xs + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ map[i].bys = (map[i].ys + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ size = map[i].bxs * map[i].bys * sizeof(struct block_list*);
+ map[i].block = (struct block_list**)aCalloc(size, 1);
+ map[i].block_mob = (struct block_list**)aCalloc(size, 1);
+
+ size = map[i].bxs * map[i].bys * sizeof(int);
+ map[i].block_count = (int*)aCallocA(size, 1);
+ memset(map[i].block_count, 0, size);
+
+ map[i].block_mob_count = (int*)aCallocA(size, 1);
+ memset(map[i].block_mob_count, 0, size);
+
+ uidb_put(map_db, (unsigned int)map[i].index, &map[i]);
+
+ // cache our map if necessary
+ if (j != MAP_CACHE && mapsource_read[MAP_CACHE] != NULL) { // map data is not cached yet
+ map_cache_write(&map[i]);
+ maps_cached++;
+ }
+
+ // next map
+ success = 1;
+ break;
+ }
+ }
+
+ // no sources have been found, so remove map from list
+ if (!success) {
+ map_delmapid(i);
+ maps_removed++;
+ i--;
+ }
+ }
+
+ // unload map sources
+ for (i = 0; i < MAP_MAXSOURCE; i++) {
+ if (mapsource_final[i])
+ mapsource_final[i]();
+ }
+
+ // finished map loading
+ aFree(waterlist);
+ printf("\r");
+ ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps.%30s\n",map_num,"");
+
+ if (maps_removed)
+ ShowNotice("Maps Removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed);
+ if (maps_cached)
+ ShowNotice("Maps Added to Cache: '"CL_WHITE"%d"CL_RESET"'\n",maps_cached);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+static int map_ip_set_ = 0;
+static int char_ip_set_ = 0;
+//static int bind_ip_set_ = 0;
+
+/*==========================================
+ * Console Command Parser [Wizputer]
+ *------------------------------------------
+ */
+int parse_console(char *buf) {
+ char *type,*command,*map, *buf2;
+ int x = 0, y = 0;
+ int m, n;
+ struct map_session_data *sd;
+
+ sd = (struct map_session_data*)aCalloc(sizeof(*sd), 1);
+
+ sd->fd = 0;
+ strcpy( sd->status.name , "console");
+
+ type = (char *)aMallocA(64);
+ command = (char *)aMallocA(64);
+ map = (char *)aMallocA(64);
+ buf2 = (char *)aMallocA(72);
+
+ memset(type,0,64);
+ memset(command,0,64);
+ memset(map,0,64);
+ memset(buf2,0,72);
+
+ if ( ( n = sscanf(buf, "%[^:]:%[^:]:%99s %d %d[^\n]", type , command , map , &x , &y )) < 5 )
+ if ( ( n = sscanf(buf, "%[^:]:%[^\n]", type , command )) < 2 )
+ n = sscanf(buf,"%[^\n]",type);
+
+ if ( n == 5 ) {
+ if (x <= 0) {
+ x = rand() % 399 + 1;
+ sd->bl.x = x;
+ } else {
+ sd->bl.x = x;
+ }
+
+ if (y <= 0) {
+ y = rand() % 399 + 1;
+ sd->bl.y = y;
+ } else {
+ sd->bl.y = y;
+ }
+
+ m = map_mapname2mapid(map);
+ if ( m >= 0 )
+ sd->bl.m = m;
+ else {
+ ShowWarning("Console: Unknown map\n");
+ goto end;
+ }
+ }
+
+ ShowInfo("Type of command: %s || Command: %s || Map: %s Coords: %d %d\n",type,command,map,x,y);
+
+ if ( strcmpi("admin",type) == 0 && n == 5 ) {
+ sprintf(buf2,"console: %s",command);
+ if( is_atcommand(sd->fd,sd,buf2,99) == AtCommand_None )
+ printf("Console: not atcommand\n");
+ } else if ( strcmpi("server",type) == 0 && n == 2 ) {
+ if ( strcmpi("shutdown", command) == 0 || strcmpi("exit",command) == 0 || strcmpi("quit",command) == 0 ) {
+ runflag = 0;
+ }
+ } else if ( strcmpi("help",type) == 0 ) {
+ ShowNotice("To use GM commands:\n");
+ printf("admin:<gm command>:<map of \"gm\"> <x> <y>\n");
+ printf("You can use any GM command that doesn't require the GM.\n");
+ printf("No using @item or @warp however you can use @charwarp\n");
+ printf("The <map of \"gm\"> <x> <y> is for commands that need coords of the GM\n");
+ printf("IE: @spawn\n");
+ printf("To shutdown the server:\n");
+ printf("server:shutdown\n");
+ }
+
+ end:
+ aFree(buf);
+ aFree(type);
+ aFree(command);
+ aFree(map);
+ aFree(buf2);
+ aFree(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * Ý’èƒtƒ@ƒCƒ‹‚ð?‚Ý?‚Þ
+ *------------------------------------------
+ */
+int map_config_read(char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ struct hostent *h = NULL;
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ ShowFatalError("Map configuration file not found at: %s\n", cfgName);
+ exit(1);
+ }
+ while(fgets(line, sizeof(line) -1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if(strcmpi(w1,"timestamp_format")==0){
+ strncpy(timestamp_format, w2, 20);
+ } else if(strcmpi(w1,"console_silent")==0){
+ msg_silent = 0; //To always allow the next line to show up.
+ ShowInfo("Console Silent Setting: %d\n", atoi(w2));
+ msg_silent = atoi(w2);
+ } else if (strcmpi(w1, "userid")==0){
+ chrif_setuserid(w2);
+ } else if (strcmpi(w1, "passwd") == 0) {
+ chrif_setpasswd(w2);
+ } else if (strcmpi(w1, "char_ip") == 0) {
+ char_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if(h != NULL) {
+ ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2,"%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ chrif_setip(w2);
+ } else if (strcmpi(w1, "char_port") == 0) {
+ chrif_setport(atoi(w2));
+ } else if (strcmpi(w1, "map_ip") == 0) {
+ map_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ clif_setip(w2);
+ } else if (strcmpi(w1, "bind_ip") == 0) {
+ //bind_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%d.%d.%d.%d"CL_RESET"'.\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ sprintf(w2, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ clif_setbindip(w2);
+ } else if (strcmpi(w1, "map_port") == 0) {
+ clif_setport(atoi(w2));
+ map_port = (atoi(w2));
+ } else if (strcmpi(w1, "water_height") == 0) {
+ map_readwater(w2);
+ } else if (strcmpi(w1, "map") == 0) {
+ map_addmap(w2);
+ } else if (strcmpi(w1, "delmap") == 0) {
+ map_delmap(w2);
+ } else if (strcmpi(w1, "npc") == 0) {
+ npc_addsrcfile(w2);
+ } else if (strcmpi(w1, "delnpc") == 0) {
+ npc_delsrcfile(w2);
+ } else if (strcmpi(w1, "autosave_time") == 0) {
+ autosave_interval = atoi(w2) * 1000;
+ if (autosave_interval <= 0)
+ autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+ } else if (strcmpi(w1, "motd_txt") == 0) {
+ strcpy(motd_txt, w2);
+ } else if (strcmpi(w1, "help_txt") == 0) {
+ strcpy(help_txt, w2);
+ } else if (strcmpi(w1, "help2_txt") == 0) {
+ strcpy(help2_txt, w2);
+ } else if (strcmpi(w1, "charhelp_txt") == 0) {
+ strcpy(charhelp_txt, w2);
+ } else if (strcmpi(w1, "mapreg_txt") == 0) {
+ strcpy(mapreg_txt, w2);
+ } else if(strcmpi(w1,"read_map_from_cache") == 0){
+ if (atoi(w2) == 2)
+ map_read_flag = READ_FROM_BITMAP_COMPRESSED;
+ else if (atoi(w2) == 1)
+ map_read_flag = READ_FROM_BITMAP;
+ else
+ map_read_flag = READ_FROM_GAT;
+ } else if(strcmpi(w1,"map_cache_file") == 0) {
+ strncpy(map_cache_file,w2,255);
+ } else if(strcmpi(w1,"db_path") == 0) {
+ strncpy(db_path,w2,255);
+ } else if(strcmpi(w1,"afm_dir") == 0) {
+ strcpy(afm_dir, w2);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) {
+ console = 1;
+ ShowNotice("Console Commands are enabled.\n");
+ }
+ } else if (strcmpi(w1, "enable_spy") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ enable_spy = 1;
+ else
+ enable_spy = 0;
+ } else if (strcmpi(w1, "import") == 0) {
+ map_config_read(w2);
+ }
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+int inter_config_read(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: '%s'.\n",cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"kick_on_disconnect")==0){
+ kick_on_disconnect = battle_config_switch(w2);
+ } else if(strcmpi(w1,"party_share_level")==0){
+ party_share_level = battle_config_switch(w2);
+ } else if(strcmpi(w1,"lowest_gm_level")==0){
+ lowest_gm_level = atoi(w2);
+
+ /* Main chat nick [LuzZza] */
+ } else if(strcmpi(w1, "main_chat_nick")==0){
+ strcpy(main_chat_nick, w2);
+
+ #ifndef TXT_ONLY
+ } else if(strcmpi(w1,"charsave_method")==0){
+ charsave_method = atoi(w2); //New char saving method.
+ } else if(strcmpi(w1,"item_db_db")==0){
+ strcpy(item_db_db,w2);
+ } else if(strcmpi(w1,"mob_db_db")==0){
+ strcpy(mob_db_db,w2);
+ } else if(strcmpi(w1,"item_db2_db")==0){
+ strcpy(item_db2_db,w2);
+ } else if(strcmpi(w1,"mob_db2_db")==0){
+ strcpy(mob_db2_db,w2);
+ } else if(strcmpi(w1,"login_db_level")==0){
+ strcpy(login_db_level,w2);
+ } else if(strcmpi(w1,"login_db_account_id")==0){
+ strcpy(login_db_account_id,w2);
+ } else if(strcmpi(w1,"login_db")==0){
+ strcpy(login_db,w2);
+ } else if (strcmpi(w1, "char_db") == 0) {
+ strcpy(char_db, w2);
+ } else if(strcmpi(w1,"gm_db_level")==0){
+ strcpy(gm_db_level,w2);
+ } else if(strcmpi(w1,"gm_db_account_id")==0){
+ strcpy(gm_db_account_id,w2);
+ } else if(strcmpi(w1,"gm_db")==0){
+ strcpy(gm_db,w2);
+ //Map Server SQL DB
+ } else if(strcmpi(w1,"map_server_ip")==0){
+ strcpy(map_server_ip, w2);
+ } else if(strcmpi(w1,"map_server_port")==0){
+ map_server_port=atoi(w2);
+ } else if(strcmpi(w1,"map_server_id")==0){
+ strcpy(map_server_id, w2);
+ } else if(strcmpi(w1,"map_server_pw")==0){
+ strcpy(map_server_pw, w2);
+ } else if(strcmpi(w1,"map_server_db")==0){
+ strcpy(map_server_db, w2);
+ } else if(strcmpi(w1,"default_codepage")==0){
+ strcpy(default_codepage, w2);
+ } else if(strcmpi(w1,"use_sql_db")==0){
+ db_use_sqldbs = battle_config_switch(w2);
+ ShowStatus ("Using SQL dbs: %s\n",w2);
+ } else if(strcmpi(w1,"use_new_sql_db")==0){
+ db_use_newsqldbs = battle_config_switch(w2);
+ ShowStatus ("Using New SQL dbs: %s\n",w2);
+ //Login Server SQL DB
+ } else if(strcmpi(w1,"login_server_ip")==0){
+ strcpy(login_server_ip, w2);
+ } else if(strcmpi(w1,"login_server_port")==0){
+ login_server_port = atoi(w2);
+ } else if(strcmpi(w1,"login_server_id")==0){
+ strcpy(login_server_id, w2);
+ } else if(strcmpi(w1,"login_server_pw")==0){
+ strcpy(login_server_pw, w2);
+ } else if(strcmpi(w1,"login_server_db")==0){
+ strcpy(login_server_db, w2);
+ } else if(strcmpi(w1,"read_gm_interval")==0){
+ read_gm_interval = ( atoi(w2) * 60 * 1000 ); // Minutes multiplied by 60 secs per min by 1000 milliseconds per second
+ }else if(strcmpi(w1, "char_server_ip") == 0){
+ strcpy(charsql_host, w2);
+ }else if(strcmpi(w1, "char_server_port") == 0){
+ charsql_port = atoi(w2);
+ }else if(strcmpi(w1, "char_server_id") == 0){
+ strcpy(charsql_user, w2);
+ }else if(strcmpi(w1, "char_server_pw") == 0){
+ strcpy(charsql_pass, w2);
+ }else if(strcmpi(w1, "char_server_db") == 0){
+ strcpy(charsql_db, w2);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_ip")==0) {
+ strcpy(log_db_ip, w2);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_id")==0) {
+ strcpy(log_db_id, w2);
+ } else if(strcmpi(w1,"log_db_pw")==0) {
+ strcpy(log_db_pw, w2);
+ } else if(strcmpi(w1,"log_db_port")==0) {
+ log_db_port = atoi(w2);
+ // Mail Server SQL
+ } else if(strcmpi(w1,"mail_server_enable")==0){
+ mail_server_enable = battle_config_switch(w2);
+ ShowStatus ("Using Mail Server: %s\n",w2);
+ } else if(strcmpi(w1,"mail_server_ip")==0){
+ strcpy(mail_server_ip, w2);
+ } else if(strcmpi(w1,"mail_server_port")==0){
+ mail_server_port=atoi(w2);
+ } else if(strcmpi(w1,"mail_server_id")==0){
+ strcpy(mail_server_id, w2);
+ } else if(strcmpi(w1,"mail_server_pw")==0){
+ strcpy(mail_server_pw, w2);
+ } else if(strcmpi(w1,"mail_server_db")==0){
+ strcpy(mail_server_db, w2);
+ } else if(strcmpi(w1,"mail_db")==0) {
+ strcpy(mail_db, w2);
+ #endif
+ //support the import command, just like any other config
+ } else if(strcmpi(w1,"import")==0){
+ inter_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+#ifndef TXT_ONLY
+/*=======================================
+ * MySQL Init
+ *---------------------------------------
+ */
+
+int map_sql_init(void){
+
+ mysql_init(&mmysql_handle);
+
+ //DB connection start
+ ShowInfo("Connecting to the Map DB Server....\n");
+ if(!mysql_real_connect(&mmysql_handle, map_server_ip, map_server_id, map_server_pw,
+ map_server_db ,map_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ exit(1);
+ }
+ else {
+ ShowStatus("connect success! (Map Server Connection)\n");
+ }
+
+ mysql_init(&lmysql_handle);
+
+ //DB connection start
+ ShowInfo("Connecting to the Login DB Server....\n");
+ if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw,
+ login_server_db ,login_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ exit(1);
+ }
+ else {
+ ShowStatus ("connect success! (Login Server Connection)\n");
+ }
+
+#ifdef MAPREGSQL
+ // [zBuffer] SQL Mapreg connection start
+ ShowInfo("Connect Mapreg DB Server....\n");
+ if(!mysql_real_connect(&mapregsql_handle, map_server_ip, map_server_id, map_server_pw,
+ map_server_db ,map_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ exit(1);
+ } else {
+ ShowStatus ("Connect success! (Mapreg DB Connection)\n");
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mapregsql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+#endif
+ if(mail_server_enable) { // mail system [Valaris]
+ mysql_init(&mail_handle);
+ ShowInfo("Connecting to the Mail DB Server....\n");
+ if(!mysql_real_connect(&mail_handle, mail_server_ip, mail_server_id, mail_server_pw,
+ mail_server_db ,mail_server_port, (char *)NULL, 0)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ exit(1);
+ }
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ if (mysql_query(&lmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+}
+
+int map_sql_close(void){
+ mysql_close(&mmysql_handle);
+ ShowStatus("Close Map DB Connection....\n");
+
+ mysql_close(&lmysql_handle);
+ ShowStatus("Close Login DB Connection....\n");
+
+#ifdef MAPREGSQL
+ // [zBuffer] SQL Mapreg connection stop
+ mysql_close(&mapregsql_handle);
+ ShowStatus("Close Mapreg DB Connection....\n");
+#endif
+
+ if (log_config.sql_logs)
+//Updating this if each time there's a log_config addition is too much of a hassle. [Skotlex]
+ /*&& (log_config.branch || log_config.drop || log_config.mvpdrop ||
+ log_config.present || log_config.produce || log_config.refine || log_config.trade))*/
+ {
+ mysql_close(&logmysql_handle);
+ ShowStatus("Close Log DB Connection....\n");
+ }
+
+ return 0;
+}
+
+int log_sql_init(void){
+
+ mysql_init(&logmysql_handle);
+
+ //DB connection start
+ ShowInfo(""CL_WHITE"[SQL]"CL_RESET": Connecting to the Log Database "CL_WHITE"%s"CL_RESET" At "CL_WHITE"%s"CL_RESET"...\n",log_db,log_db_ip);
+ if(!mysql_real_connect(&logmysql_handle, log_db_ip, log_db_id, log_db_pw,
+ log_db ,log_db_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ exit(1);
+ }
+
+ ShowStatus(""CL_WHITE"[SQL]"CL_RESET": Successfully '"CL_GREEN"connected"CL_RESET"' to Database '"CL_WHITE"%s"CL_RESET"'.\n", log_db);
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&logmysql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+int map_db_final(DBKey k,void *d,va_list ap)
+{
+ // Not needed actually, these are already freed. [Lance]
+ //struct map_data_other_server *mdos = (struct map_data_other_server*)d;
+ //if(mdos->gat == NULL)
+ // aFree(mdos);
+ return 0;
+}
+int nick_db_final(void *k,void *d,va_list ap)
+{
+ char *p = (char *) d;
+ if (p) aFree(p);
+ return 0;
+}
+
+int cleanup_sub(struct block_list *bl, va_list ap) {
+ nullpo_retr(0, bl);
+
+ switch(bl->type) {
+ case BL_PC:
+ map_quit((struct map_session_data *) bl);
+ break;
+ case BL_NPC:
+ npc_unload((struct npc_data *)bl);
+ break;
+ case BL_MOB:
+ mob_unload((struct mob_data *)bl);
+ break;
+ case BL_PET:
+ //There is no need for this, the pet is removed together with the player. [Skotlex]
+// pet_remove_map(((struct pet_data *)bl)->msd);
+ break;
+ case BL_ITEM:
+ map_clearflooritem(bl->id);
+ break;
+ case BL_SKILL:
+ skill_delunit((struct skill_unit *) bl);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * mapŽII—¹E—
+ *------------------------------------------
+ */
+void do_final(void) {
+ int i, j;
+ struct map_session_data **pl_allsd;
+
+ ShowStatus("Terminating...\n");
+
+ // we probably don't need the cache open at all times 'yet', so this is closed by mapsource_final [celest]
+ //map_cache_close();
+ grfio_final();
+
+ for (i = 0; i < map_num; i++)
+ if (map[i].m >= 0)
+ map_foreachinmap(cleanup_sub, i, BL_ALL);
+
+ //Scan any remaining players (between maps?) to kick them out. [Skotlex]
+ pl_allsd = map_getallusers(&j);
+ for (i = 0; i < j; i++)
+ map_quit(pl_allsd[i]);
+
+ chrif_char_reset_offline();
+ chrif_flush_fifo();
+
+ do_final_atcommand();
+ do_final_chrif(); // ‚±‚Ì“à•”‚ŃLƒƒƒ‰‚ð‘S‚ÄØ’f‚·‚é
+ do_final_npc();
+// map_removenpc();
+ do_final_script();
+ do_final_itemdb();
+ do_final_storage();
+ do_final_guild();
+ do_final_party();
+ do_final_pc();
+ do_final_pet();
+ do_final_mob();
+ do_final_msg();
+
+ map_getallusers(NULL); //Clear the memory allocated for this array.
+
+ map_db->destroy(map_db, map_db_final);
+
+ for (i=0; i<map_num; i++) {
+ if(map[i].gat) aFree(map[i].gat);
+ if(map[i].cell) aFree(map[i].cell);
+#ifdef CELL_NOSTACK
+ if(map[i].cell_bl) aFree(map[i].cell_bl);
+#endif
+ if(map[i].block) aFree(map[i].block);
+ if(map[i].block_mob) aFree(map[i].block_mob);
+ if(map[i].block_count) aFree(map[i].block_count);
+ if(map[i].block_mob_count) aFree(map[i].block_mob_count);
+ if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random]
+ for (j=0; j<MAX_MOB_LIST_PER_MAP; j++)
+ if (map[i].moblist[j]) aFree(map[i].moblist[j]);
+ }
+ }
+
+ mapindex_final();
+
+ id_db->destroy(id_db, NULL);
+ pc_db->destroy(pc_db, NULL);
+ charid_db->destroy(charid_db, NULL);
+
+//#endif
+
+#ifndef TXT_ONLY
+ map_sql_close();
+ if(charsave_method)
+ charsql_db_init(0); //Connecting to chardb
+#endif /* not TXT_ONLY */
+ ShowStatus("Successfully terminated.\n");
+}
+
+/*======================================================
+ * Map-Server Version Screen [MC Cameri]
+ *------------------------------------------------------
+ */
+void map_helpscreen(int flag) { // by MC Cameri
+ puts("Usage: map-server [options]");
+ puts("Options:");
+ puts(CL_WHITE" Commands\t\t\tDescription"CL_RESET);
+ puts("-----------------------------------------------------------------------------");
+ puts(" --help, --h, --?, /? Displays this help screen");
+ puts(" --map-config <file> Load map-server configuration from <file>");
+ puts(" --battle-config <file> Load battle configuration from <file>");
+ puts(" --atcommand-config <file> Load atcommand configuration from <file>");
+ puts(" --charcommand-config <file> Load charcommand configuration from <file>");
+ puts(" --script-config <file> Load script configuration from <file>");
+ puts(" --msg-config <file> Load message configuration from <file>");
+ puts(" --grf-path-file <file> Load grf path file configuration from <file>");
+ puts(" --sql-config <file> Load inter-server configuration from <file>");
+ puts(" (SQL Only)");
+ puts(" --log-config <file> Load logging configuration from <file>");
+ puts(" (SQL Only)");
+ puts(" --version, --v, -v, /v Displays the server's version");
+ puts("\n");
+ if (flag) exit(1);
+}
+
+/*======================================================
+ * Map-Server Version Screen [MC Cameri]
+ *------------------------------------------------------
+ */
+void map_versionscreen(int flag) {
+ printf("CL_WHITE" "eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n",
+ ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION,
+ ATHENA_MOD_VERSION);
+ puts(CL_GREEN "Website/Forum:" CL_RESET "\thttp://eathena.deltaanime.net/");
+ puts(CL_GREEN "Download URL:" CL_RESET "\thttp://eathena.systeminplace.net/");
+ puts(CL_GREEN "IRC Channel:" CL_RESET "\tirc://irc.deltaanime.net/#athena");
+ puts("\nOpen " CL_WHITE "readme.html" CL_RESET " for more information.");
+ if (ATHENA_RELEASE_FLAG) ShowNotice("This version is not for release.\n");
+ if (flag) exit(1);
+}
+
+/*======================================================
+ * Map-Server Init and Command-line Arguments [Valaris]
+ *------------------------------------------------------
+ */
+void set_server_type(void)
+{
+ SERVER_TYPE = ATHENA_SERVER_MAP;
+}
+int do_init(int argc, char *argv[]) {
+ int i;
+
+#ifdef GCOLLECT
+ GC_enable_incremental();
+#endif
+
+ INTER_CONF_NAME="conf/inter_athena.conf";
+ LOG_CONF_NAME="conf/log_athena.conf";
+ MAP_CONF_NAME = "conf/map_athena.conf";
+ BATTLE_CONF_FILENAME = "conf/battle_athena.conf";
+ ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf";
+ CHARCOMMAND_CONF_FILENAME = "conf/charcommand_athena.conf";
+ SCRIPT_CONF_NAME = "conf/script_athena.conf";
+ MSG_CONF_NAME = "conf/msg_athena.conf";
+ GRF_PATH_FILENAME = "conf/grf-files.txt";
+
+ chrif_connected = 0;
+
+ srand(gettick());
+
+ for (i = 1; i < argc ; i++) {
+ if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--h") == 0 || strcmp(argv[i], "--?") == 0 || strcmp(argv[i], "/?") == 0)
+ map_helpscreen(1);
+ else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "--v") == 0 || strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "/v") == 0)
+ map_versionscreen(1);
+ else if (strcmp(argv[i], "--map_config") == 0 || strcmp(argv[i], "--map-config") == 0)
+ MAP_CONF_NAME=argv[i+1];
+ else if (strcmp(argv[i],"--battle_config") == 0 || strcmp(argv[i],"--battle-config") == 0)
+ BATTLE_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--atcommand_config") == 0 || strcmp(argv[i],"--atcommand-config") == 0)
+ ATCOMMAND_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--charcommand_config") == 0 || strcmp(argv[i],"--charcommand-config") == 0)
+ CHARCOMMAND_CONF_FILENAME = argv[i+1];
+ else if (strcmp(argv[i],"--script_config") == 0 || strcmp(argv[i],"--script-config") == 0)
+ SCRIPT_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--msg_config") == 0 || strcmp(argv[i],"--msg-config") == 0)
+ MSG_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--grf_path_file") == 0 || strcmp(argv[i],"--grf-path-file") == 0)
+ GRF_PATH_FILENAME = argv[i+1];
+#ifndef TXT_ONLY
+ else if (strcmp(argv[i],"--inter_config") == 0 || strcmp(argv[i],"--inter-config") == 0)
+ INTER_CONF_NAME = argv[i+1];
+#endif
+ else if (strcmp(argv[i],"--log_config") == 0 || strcmp(argv[i],"--log-config") == 0)
+ LOG_CONF_NAME = argv[i+1];
+ else if (strcmp(argv[i],"--run_once") == 0) // close the map-server as soon as its done.. for testing [Celest]
+ runflag = 0;
+ }
+
+ map_config_read(MAP_CONF_NAME);
+
+ if ((naddr_ == 0) && (map_ip_set_ == 0 || char_ip_set_ == 0)) {
+ ShowError("\nUnable to determine your IP address... please edit the map_athena.conf file and set it.\n");
+ ShowError("(127.0.0.1 is valid if you have no network interface)\n");
+ }
+
+ if (map_ip_set_ == 0 || char_ip_set_ == 0) {
+ // The map server should know what IP address it is running on
+ // - MouseJstr
+ int localaddr = ntohl(addr_[0]);
+ unsigned char *ptr = (unsigned char *) &localaddr;
+ char buf[16];
+ sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);;
+ if (naddr_ != 1)
+ ShowNotice("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ ShowInfo("Defaulting to %s as our IP address\n", buf);
+ if (map_ip_set_ == 0)
+ clif_setip(buf);
+ if (char_ip_set_ == 0)
+ chrif_setip(buf);
+ if (ptr[0] == 192 && ptr[1] == 168)
+ ShowError("\nFirewall detected.. \n edit lan_support.conf and map_athena.conf\n\n");
+ }
+
+ if (SHOW_DEBUG_MSG)
+ ShowNotice("Server running in '"CL_WHITE"Debug Mode"CL_RESET"'.\n");
+
+
+ battle_config_read(BATTLE_CONF_FILENAME);
+ msg_config_read(MSG_CONF_NAME);
+ atcommand_config_read(ATCOMMAND_CONF_FILENAME);
+ charcommand_config_read(CHARCOMMAND_CONF_FILENAME);
+ script_config_read(SCRIPT_CONF_NAME);
+ inter_config_read(INTER_CONF_NAME);
+ log_config_read(LOG_CONF_NAME);
+
+ id_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ pc_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); //Added for reliable map_id2sd() use. [Skotlex]
+ map_db = db_alloc(__FILE__,__LINE__,DB_UINT,DB_OPT_BASE,sizeof(int));
+ charid_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+#ifndef TXT_ONLY
+ map_sql_init();
+ if(charsave_method)
+ charsql_db_init(1); //Connecting to chardb
+#endif /* not TXT_ONLY */
+
+ mapindex_init();
+ grfio_init(GRF_PATH_FILENAME);
+
+ map_readallmaps();
+
+ add_timer_func_list(map_freeblock_timer, "map_freeblock_timer");
+ add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer");
+ add_timer_func_list(map_removemobs_timer, "map_removemobs_timer");
+ add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000);
+
+ do_init_atcommand();
+ do_init_chrif();
+ do_init_clif();
+ do_init_script();
+ do_init_itemdb();
+ do_init_mob(); // npc‚̉Šú‰»E‚Åmob_spawn‚µ‚ÄAmob_db‚ð?Æ‚·‚é‚Ì‚Åinit_npc‚æ‚èæ
+ do_init_pc();
+ do_init_status();
+ do_init_party();
+ do_init_guild();
+ do_init_storage();
+ do_init_skill();
+ do_init_pet();
+ do_init_npc();
+
+#ifndef TXT_ONLY /* mail system [Valaris] */
+ if(mail_server_enable)
+ do_init_mail();
+
+ if (log_config.sql_logs)
+ {
+ log_sql_init();
+ }
+#endif /* not TXT_ONLY */
+
+ npc_event_do_oninit(); // npc‚ÌOnInitƒCƒxƒ“ƒg?s
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ if (battle_config.pk_mode == 1)
+ ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n");
+
+ ShowStatus("Server is '"CL_GREEN"ready"CL_RESET"' and listening on port '"CL_WHITE"%d"CL_RESET"'.\n\n", map_port);
+
+ return 0;
+}
+
+int compare_item(struct item *a, struct item *b) {
+
+ if (a->nameid == b->nameid &&
+ a->identify == b->identify &&
+ a->refine == b->refine &&
+ a->attribute == b->attribute)
+ {
+ int i;
+ for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
+ return (i == MAX_SLOTS);
+ }
+ return 0;
+}
+
+#ifndef TXT_ONLY
+int charsql_db_init(int method){
+
+ if(method == 1){ //'INIT / START'
+ ShowInfo("Connecting to 'character' Database... ");
+ mysql_init(&charsql_handle);
+
+ if(!mysql_real_connect(&charsql_handle, charsql_host, charsql_user, charsql_pass, charsql_db, charsql_port, (char *)NULL, 0)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ exit(1);
+ }else{
+ printf("success.\n");
+ if( strlen(default_codepage) > 0 ) {
+ sprintf( tmp_sql, "SET NAMES %s", default_codepage );
+ if (mysql_query(&charsql_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }else if(method == 0){ //'FINAL' / Shutdown
+ ShowInfo("Closing 'character' Database connection ... ");
+ mysql_close(&charsql_handle);
+ printf("done.\n");
+ }
+ return 0;
+}
+#endif
diff --git a/src/map/map.h b/src/map/map.h
new file mode 100644
index 000000000..c56e4c886
--- /dev/null
+++ b/src/map/map.h
@@ -0,0 +1,1409 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <stdarg.h>
+#include "../common/mmo.h"
+#include "../common/mapindex.h"
+#include "../common/db.h"
+
+//Uncomment to enable the Cell Stack Limit mod. (EXPERIMENTAL)
+//It's only config is the battle_config cell_stack_limit.
+//Only chars affected are those defined in BL_CHAR (mobs and players currently)
+//#define CELL_NOSTACK
+
+#define MAX_PC_CLASS 4050
+#define PC_CLASS_BASE 0
+#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001)
+#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22)
+#define MAX_NPC_PER_MAP 512
+#define BLOCK_SIZE 8
+#define AREA_SIZE battle_config.area_size
+#define LIFETIME_FLOORITEM 60
+#define DAMAGELOG_SIZE 30
+#define LOOTITEM_SIZE 10
+#define MAX_STATUSCHANGE 250
+//Quick defines to know which are the min-max common ailments. [Skotlex]
+//Because of the way the headers are included.. these must be replaced for actual values.
+//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently.
+#define SC_COMMON_MIN 0
+#define SC_COMMON_MAX 10
+
+#define MAX_SKILL_LEVEL 100
+#define MAX_SKILLUNITGROUP 32
+#define MAX_MOBSKILLUNITGROUP 8
+#define MAX_SKILLUNITGROUPTICKSET 32
+#define MAX_SKILLTIMERSKILL 32
+#define MAX_MOBSKILLTIMERSKILL 10
+#define MAX_MOBSKILL 32
+#define MAX_MOB_LIST_PER_MAP 128
+#define MAX_EVENTQUEUE 2
+#define MAX_EVENTTIMER 32
+#define NATURAL_HEAL_INTERVAL 500
+#define MAX_FLOORITEM 500000
+#define MAX_LEVEL 255
+#define MAX_WALKPATH 32
+#define MAX_DROP_PER_MAP 48
+#define MAX_IGNORE_LIST 80
+#define MAX_VENDING 12
+#define MOBID_EMPERIUM 1288
+
+#define MAX_PC_BONUS 10
+#define MAX_DUEL 1024
+
+//These mark the ID of the jobs, as expected by the client. [Skotlex]
+enum {
+ JOB_NOVICE,
+ JOB_SWORDMAN,
+ JOB_MAGE,
+ JOB_ARCHER,
+ JOB_ACOLYTE,
+ JOB_MERCHANT,
+ JOB_THIEF,
+ JOB_KNIGHT,
+ JOB_PRIEST,
+ JOB_WIZARD,
+ JOB_BLACKSMITH,
+ JOB_HUNTER,
+ JOB_ASSASSIN,
+ JOB_KNIGHT2,
+ JOB_CRUSADER,
+ JOB_MONK,
+ JOB_SAGE,
+ JOB_ROGUE,
+ JOB_ALCHEMIST,
+ JOB_BARD,
+ JOB_DANCER,
+ JOB_CRUSADER2,
+ JOB_WEDDING,
+ JOB_SUPER_NOVICE,
+ JOB_GUNSLINGER,
+ JOB_NINJA,
+ JOB_XMAS,
+
+ JOB_NOVICE_HIGH = 4001,
+ JOB_SWORDMAN_HIGH,
+ JOB_MAGE_HIGH,
+ JOB_ARCHER_HIGH,
+ JOB_ACOLYTE_HIGH,
+ JOB_MERCHANT_HIGH,
+ JOB_THIEF_HIGH,
+ JOB_LORD_KNIGHT,
+ JOB_HIGH_PRIEST,
+ JOB_HIGH_WIZARD,
+ JOB_WHITESMITH,
+ JOB_SNIPER,
+ JOB_ASSASSIN_CROSS,
+ JOB_LORD_KNIGHT2,
+ JOB_PALADIN,
+ JOB_CHAMPION,
+ JOB_PROFESSOR,
+ JOB_STALKER,
+ JOB_CREATOR,
+ JOB_CLOWN,
+ JOB_GYPSY,
+ JOB_PALADIN2,
+
+ JOB_BABY,
+ JOB_BABY_SWORDMAN,
+ JOB_BABY_MAGE,
+ JOB_BABY_ARCHER,
+ JOB_BABY_ACOLYTE,
+ JOB_BABY_MERCHANT,
+ JOB_BABY_THIEF,
+ JOB_BABY_KNIGHT,
+ JOB_BABY_PRIEST,
+ JOB_BABY_WIZARD,
+ JOB_BABY_BLACKSMITH,
+ JOB_BABY_HUNTER,
+ JOB_BABY_ASSASSIN,
+ JOB_BABY_KNIGHT2,
+ JOB_BABY_CRUSADER,
+ JOB_BABY_MONK,
+ JOB_BABY_SAGE,
+ JOB_BABY_ROGUE,
+ JOB_BABY_ALCHEMIST,
+ JOB_BABY_BARD,
+ JOB_BABY_DANCER,
+ JOB_BABY_CRUSADER2,
+ JOB_SUPER_BABY,
+
+ JOB_TAEKWON,
+ JOB_STAR_GLADIATOR,
+ JOB_STAR_GLADIATOR2,
+ JOB_SOUL_LINKER,
+};
+
+//The following system marks a different job ID system used by the map server,
+//which makes a lot more sense than the normal one. [Skotlex]
+//
+//These marks the "level" of the job.
+#define JOBL_2_1 0x100 //256
+#define JOBL_2_2 0x200 //512
+#define JOBL_2 0x300
+
+#define JOBL_UPPER 0x1000 //4096
+#define JOBL_BABY 0x2000 //8192
+
+//for filtering and quick checking.
+#define MAPID_UPPERMASK 0x0fff
+#define MAPID_BASEMASK 0x00ff
+//First Jobs
+//Note the oddity of the novice:
+//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too...
+enum {
+ MAPID_NOVICE = 0x0,
+ MAPID_SWORDMAN,
+ MAPID_MAGE,
+ MAPID_ARCHER,
+ MAPID_ACOLYTE,
+ MAPID_MERCHANT,
+ MAPID_THIEF,
+ MAPID_TAEKWON,
+ MAPID_WEDDING,
+ MAPID_XMAS, // [Valaris]
+//2_1 classes
+ MAPID_SUPER_NOVICE = JOBL_2_1|0x0,
+ MAPID_KNIGHT,
+ MAPID_WIZARD,
+ MAPID_HUNTER,
+ MAPID_PRIEST,
+ MAPID_BLACKSMITH,
+ MAPID_ASSASSIN,
+ MAPID_STAR_GLADIATOR,
+//2_2 classes
+ MAPID_CRUSADER = JOBL_2_2|0x1,
+ MAPID_SAGE,
+ MAPID_BARDDANCER,
+ MAPID_MONK,
+ MAPID_ALCHEMIST,
+ MAPID_ROGUE,
+ MAPID_SOUL_LINKER,
+//1-1, advanced
+ MAPID_NOVICE_HIGH = JOBL_UPPER|0x0,
+ MAPID_SWORDMAN_HIGH,
+ MAPID_MAGE_HIGH,
+ MAPID_ARCHER_HIGH,
+ MAPID_ACOLYTE_HIGH,
+ MAPID_MERCHANT_HIGH,
+ MAPID_THIEF_HIGH,
+//2_1 advanced
+ MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1,
+ MAPID_HIGH_WIZARD,
+ MAPID_SNIPER,
+ MAPID_HIGH_PRIEST,
+ MAPID_WHITESMITH,
+ MAPID_ASSASSIN_CROSS,
+//2_2 advanced
+ MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1,
+ MAPID_PROFESSOR,
+ MAPID_CLOWNGYPSY,
+ MAPID_CHAMPION,
+ MAPID_CREATOR,
+ MAPID_STALKER,
+//1-1 baby
+ MAPID_BABY = JOBL_BABY|0x0,
+ MAPID_BABY_SWORDMAN,
+ MAPID_BABY_MAGE,
+ MAPID_BABY_ARCHER,
+ MAPID_BABY_ACOLYTE,
+ MAPID_BABY_MERCHANT,
+ MAPID_BABY_THIEF,
+ MAPID_BABY_TAEKWON,
+//2_1 baby
+ MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0,
+ MAPID_BABY_KNIGHT,
+ MAPID_BABY_WIZARD,
+ MAPID_BABY_HUNTER,
+ MAPID_BABY_PRIEST,
+ MAPID_BABY_BLACKSMITH,
+ MAPID_BABY_ASSASSIN,
+ MAPID_BABY_STAR_GLADIATOR,
+//2_2 baby
+ MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1,
+ MAPID_BABY_SAGE,
+ MAPID_BABY_BARDDANCER,
+ MAPID_BABY_MONK,
+ MAPID_BABY_ALCHEMIST,
+ MAPID_BABY_ROGUE,
+ MAPID_BABY_SOUL_LINKER,
+};
+
+//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex]
+#define MESSAGE_SIZE 80
+
+#define DEFAULT_AUTOSAVE_INTERVAL 60*1000
+
+#define OPTION_SIGHT 0x0001
+#define OPTION_HIDE 0x0002
+#define OPTION_CLOAK 0x0004
+
+#define OPTION_FALCON 0x0010
+#define OPTION_RIDING 0x0020
+#define OPTION_INVISIBLE 0x0040
+#define OPTION_ORCISH 0x0800
+
+#define OPTION_WEDDING 0x1000
+#define OPTION_RUWACH 0x2000
+#define OPTION_CHASEWALK 0x4000
+
+#define OPTION_FLYING 0x8000
+
+//TODO: Get these Missing options...
+#define OPTION_SIGHTTRASHER 0x0001
+
+//Specifies maps where players may hit each other
+#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+//Specifies maps that have special GvG/WoE restrictions
+#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+
+//This stackable implementation does not means a BL can be more than one type at a time, but it's
+//meant to make it easier to check for multiple types at a time on invocations such as
+// map_foreach* calls [Skotlex]
+enum {
+ BL_NUL = 0x000,
+ BL_PC = 0x001,
+ BL_MOB = 0x002,
+ BL_PET = 0x004,
+ BL_ITEM = 0x008,
+ BL_SKILL = 0x010,
+ BL_NPC = 0x020,
+ BL_CHAT = 0x040
+};
+
+//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
+#define BL_CHAR (BL_PC|BL_MOB)
+#define BL_ALL 0xfff
+
+enum { WARP, SHOP, SCRIPT, MONS };
+
+struct block_list {
+ struct block_list *next,*prev;
+ int id;
+ short m,x,y;
+ unsigned char type;
+ unsigned char subtype;
+};
+
+struct walkpath_data {
+ unsigned char path_len,path_pos,path_half;
+ unsigned char path[MAX_WALKPATH];
+};
+struct shootpath_data {
+ int rx,ry,len;
+ int x[MAX_WALKPATH];
+ int y[MAX_WALKPATH];
+};
+
+struct script_reg {
+ int index;
+ int data;
+};
+struct script_regstr {
+ int index;
+ char data[256];
+};
+struct status_change {
+ int timer;
+ int val1,val2,val3,val4;
+};
+struct vending {
+ short index;
+ unsigned short amount;
+ unsigned int value;
+};
+
+struct weapon_data {
+ int atkmods[3];
+ // all the variables except atkmods get zero'ed in each call of status_calc_pc
+ // NOTE: if you want to add a non-zeroed variable, you need to update the memset call
+ // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex]
+ int watk;
+ int watk2;
+ int atk_ele;
+ int overrefine;
+ int star;
+ int ignore_def_ele;
+ int ignore_def_race;
+ int def_ratio_atk_ele;
+ int def_ratio_atk_race;
+ int addele[10];
+ int addrace[12];
+ int addrace2[12];
+ int addsize[3];
+
+ short ignore_def_mob;
+ short hp_drain_rate;
+ short hp_drain_per;
+ short hp_drain_value;
+ short sp_drain_rate;
+ short sp_drain_per;
+ short sp_drain_value;
+ short add_damage_classid[MAX_PC_BONUS];
+ int add_damage_classrate[MAX_PC_BONUS];
+ int add_damage_class_count;
+};
+
+struct skill_unit_group;
+struct skill_unit {
+ struct block_list bl;
+
+ struct skill_unit_group *group;
+
+ int limit;
+ int val1,val2;
+ short alive,range;
+};
+struct skill_unit_group {
+ int src_id;
+ int party_id;
+ int guild_id;
+ int map;
+ int target_flag; //Holds BCT_* flag for battle_check_target
+ int bl_flag; //Holds BL_* flag for map_foreachin* functions
+ unsigned int tick;
+ int limit,interval;
+
+ int skill_id,skill_lv;
+ int val1,val2,val3;
+ char *valstr;
+ int unit_id;
+ int group_id;
+ int unit_count,alive_count;
+ struct skill_unit *unit;
+};
+struct skill_unit_group_tickset {
+ unsigned int tick;
+ int id;
+};
+struct skill_timerskill {
+ int timer;
+ int src_id;
+ int target_id;
+ int map;
+ short x,y;
+ short skill_id,skill_lv;
+ int type;
+ int flag;
+};
+
+struct npc_data;
+struct pet_db;
+struct item_data;
+struct square;
+
+struct map_session_data {
+ struct block_list bl;
+ //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in
+ //status_calc_pc, while special_state is recalculated in each call. [Skotlex]
+ struct {
+ unsigned auth : 1;
+ unsigned change_walk_target : 1;
+ unsigned attack_continue : 1;
+ unsigned menu_or_input : 1;
+ unsigned dead_sit : 2;
+ unsigned skillcastcancel : 1;
+ unsigned waitingdisconnect : 1;
+ unsigned lr_flag : 2;
+ unsigned connect_new : 1;
+ unsigned arrow_atk : 1;
+ unsigned attack_type : 3;
+ unsigned skill_flag : 1;
+ unsigned gangsterparadise : 1;
+ unsigned rest : 1;
+ unsigned produce_flag : 1;
+ unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex]
+ unsigned snovice_flag : 4;
+ // originally by Qamera, adapted by celest
+ unsigned event_death : 1;
+ unsigned event_kill : 1;
+ unsigned event_disconnect : 1;
+ // Abracadabra bugfix by Aru
+ unsigned abra_flag : 1;
+ unsigned autotrade : 1; //By Fantik
+ unsigned perfect_hiding : 1; // [Valaris]
+ unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet)
+ unsigned showdelay :1;
+ unsigned showexp :1;
+ unsigned showzeny :1;
+ unsigned mainchat :1; //[LuzZza]
+ unsigned disguised :1; //[Valaris]
+ unsigned deal_locked :2;
+ unsigned party_sent :1;
+ unsigned guild_sent :1;
+ unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
+ unsigned size :2; // for tiny/large types
+ unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
+ unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex]
+ unsigned short autoloot;
+ struct guild *gmaster_flag;
+ } state;
+ struct {
+ unsigned killer : 1;
+ unsigned killable : 1;
+ unsigned restart_full_recover : 1;
+ unsigned no_castcancel : 1;
+ unsigned no_castcancel2 : 1;
+ unsigned no_sizefix : 1;
+ unsigned no_magic_damage : 1;
+ unsigned no_weapon_damage : 1;
+ unsigned no_gemstone : 1;
+ unsigned infinite_endure : 1;
+ unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
+ } special_state;
+ int char_id, login_id1, login_id2, sex;
+ unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex]
+
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
+ struct mmo_charstatus status;
+ struct registry save_reg;
+
+ struct item_data *inventory_data[MAX_INVENTORY];
+ short equip_index[11];
+ unsigned int weight,max_weight;
+ int cart_weight,cart_max_weight,cart_num,cart_max_num;
+ int fd;
+ unsigned short mapindex;
+ short to_x,to_y;
+ short speed,prev_speed;
+ short opt1,opt2,opt3;
+ unsigned char dir,head_dir;
+ unsigned int client_tick,server_tick;
+ struct walkpath_data walkpath;
+ int walktimer;
+ int npc_id,areanpc_id,npc_shopid;
+ int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse)
+ int npc_pos;
+ int npc_menu;
+ int npc_amount;
+ struct script_stack *stack;
+ unsigned char *npc_script,*npc_scriptroot;
+ int npc_scriptstate;
+ char npc_str[256];
+ unsigned int chatID;
+ time_t idletime;
+
+ struct{
+ char name[NAME_LENGTH];
+ } ignore[MAX_IGNORE_LIST];
+ int ignoreAll;
+
+ int attacktimer;
+
+ int attacktarget;
+ short attacktarget_lv;
+ unsigned int attackabletime;
+
+ int followtimer; // [MouseJstr]
+ int followtarget;
+
+ time_t emotionlasttime; // to limit flood with emotion packets
+
+ int skilltimer;
+ int skilltarget;
+ short skillx,skilly;
+ short skillid,skilllv;
+ short skillitem,skillitemlv;
+ short skillid_old,skilllv_old;
+ short skillid_dance,skilllv_dance;
+ struct skill_unit_group skillunit[MAX_SKILLUNITGROUP];
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
+ struct skill_timerskill skilltimerskill[MAX_SKILLTIMERSKILL];
+ char blockskill[MAX_SKILL]; // [celest]
+ //unsigned int skillstatictimer[MAX_SKILL];
+ unsigned short timerskill_count; // [celest]
+ int cloneskill_id;
+ struct map_session_data *repair_target;
+
+ int invincible_timer;
+ unsigned int canact_tick;
+ unsigned int canmove_tick;
+ unsigned int canlog_tick;
+ unsigned int canregen_tick;
+ unsigned int canuseitem_tick; // [Skotlex]
+ int hp_sub,sp_sub;
+ int inchealhptick,inchealsptick,inchealspirithptick,inchealspiritsptick;
+
+ short view_class;
+ short weapontype1,weapontype2;
+ short disguise; // [Valaris]
+
+ struct weapon_data right_weapon;
+ struct weapon_data left_weapon;
+
+ int paramc[6],paramcard[6];
+
+ // here start arrays to be globally zeroed at the beginning of status_calc_pc()
+
+ int paramb[6];
+ int parame[6];
+ int subele[10];
+ int subrace[12];
+ int subrace2[12];
+ int subsize[3];
+ int addeff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int addeff2[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int weapon_coma_ele[10];
+ int weapon_coma_race[12];
+ int weapon_atk[16];
+ int weapon_atk_rate[16];
+ int arrow_addele[10];
+ int arrow_addrace[12];
+ int arrow_addsize[3];
+ int arrow_addeff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int arrow_addeff2[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int magic_addele[10];
+ int magic_addrace[12];
+ int magic_addsize[3];
+ int critaddrace[12];
+ int expaddrace[12];
+ int itemhealrate[7];
+ int addeff3[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ short addeff3_type[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ short sp_gain_race[12];
+ short unequip_losehp[11];
+ short unequip_losesp[11];
+ // zeroed arrays end here.
+ // zeroed structures start here
+ struct {
+ short id, lv, rate;
+ } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS];
+ struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills.
+ short id, val;
+ } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS];
+ struct {
+ short class_, rate;
+ } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS],
+ add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS];
+ struct {
+ short id, group;
+ int race, rate;
+ } add_drop[MAX_PC_BONUS];
+ // zeroed structures end here
+ // zeroed vars start here.
+ int hit;
+ int flee, flee2;
+ int critical;
+ int aspd;
+ int def, def2;
+ int mdef, mdef2;
+ int def_ele;
+ int matk1, matk2;
+ int base_atk;
+ int arrow_atk,arrow_ele,arrow_cri,arrow_hit,arrow_range;
+ int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp;
+ int critical_def,double_rate;
+ int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex]
+ int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate;
+ int ignore_mdef_ele;
+ int ignore_mdef_race;
+ int perfect_hit;
+ int perfect_hit_add;
+ int get_zeny_rate;
+ int get_zeny_num; //Added Get Zeny Rate [Skotlex]
+ int double_add_rate;
+ int short_weapon_damage_return,long_weapon_damage_return;
+ int magic_damage_return; // AppleGirl Was Here
+ int random_attack_increase_add,random_attack_increase_per; // [Valaris]
+ int break_weapon_rate,break_armor_rate;
+ int crit_atk_rate;
+ int hp_loss_rate;
+ int sp_loss_rate;
+ int classchange; // [Valaris]
+ unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex]
+
+ short attackrange,attackrange_;
+ short splash_range, splash_add_range;
+ short add_steal_rate;
+ short hp_loss_value;
+ short sp_loss_value;
+ short hp_loss_type;
+ short sp_drain_type;
+ short sp_gain_value, hp_gain_value;
+ short add_drop_count;
+ unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest]
+ unsigned short unbreakable_equip; //100% break resistance on certain equipment
+ unsigned short unstripable_equip;
+ short no_regen;
+ short add_def_count,add_mdef_count;
+ short add_dmg_count,add_mdmg_count;
+
+ // zeroed vars end here.
+
+ int amotion,dmotion;
+ int castrate,delayrate,hprate,sprate,dsprate;
+ int atk_rate;
+ int aspd_rate,speed_rate,hprecov_rate,sprecov_rate;
+ int matk_rate;
+ int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate;
+ int speed_add_rate, aspd_add_rate;
+
+ int hp_loss_tick;
+ int sp_loss_tick;
+
+ int itemid;
+ short itemindex; //Used item's index in sd->inventory [Skotlex]
+
+ short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo]
+
+ short spiritball, spiritball_old;
+ int spirit_timer[MAX_SKILL_LEVEL];
+
+ int die_counter;
+ short doridori_counter;
+ char potion_success_counter;
+
+ int reg_num;
+ struct script_reg *reg;
+ int regstr_num;
+ struct script_regstr *regstr;
+
+ struct status_change sc_data[MAX_STATUSCHANGE];
+ short sc_count;
+ short mission_mobid; //Stores the target mob_id for TK_MISSION
+ short mission_count; //Stores the bounty kill count for TK_MISSION
+ int devotion[5]; //Stores the char IDs of chars devoted to.
+
+ int trade_partner;
+ struct {
+ struct {
+ int index, amount;
+ } item[10];
+ int zeny, weight;
+ } deal;
+
+ int party_invite,party_invite_account;
+ short party_x,party_y; // should be short [zzo]
+
+ int guild_invite,guild_invite_account;
+ int guild_emblem_id,guild_alliance,guild_alliance_account;
+ short guild_x,guild_y; // For guildmate position display. [Skotlex] should be short [zzo]
+ int guildspy; // [Syrus22]
+ int partyspy; // [Syrus22]
+
+ int vender_id;
+ int vend_num;
+ char message[MESSAGE_SIZE];
+ struct vending vending[MAX_VENDING];
+
+ struct s_pet pet;
+ struct pet_db *petDB;
+ struct pet_data *pd;
+ int pet_hungry_timer;
+
+ struct{
+ int m; //-1 - none, other: map index corresponding to map name.
+ unsigned short index; //map index
+ }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars
+ int feel_level;
+ short hate_mob[3];
+
+ unsigned int pvp_timer;
+ short pvp_point;
+ unsigned short pvp_rank, pvp_lastusers;
+ unsigned short pvp_won, pvp_lost;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ unsigned short eventcount; // [celest]
+
+ unsigned char change_level; // [celest]
+
+ char fakename[NAME_LENGTH]; // fake names [Valaris]
+
+#ifndef TXT_ONLY
+ int mail_counter; // mail counter for mail system [Valaris]
+#endif
+
+ int duel_group; // duel vars [LuzZza]
+ int duel_invite;
+
+ char away_message[128]; // [LuzZza]
+
+};
+
+struct {
+ int members_count;
+ int invites_count;
+ int max_players_limit;
+} duel_list[MAX_DUEL];
+
+int duel_count;
+
+struct npc_timerevent_list {
+ int timer,pos;
+};
+struct npc_label_list {
+ char name[NAME_LENGTH];
+ int pos;
+};
+struct npc_item_list {
+ unsigned int nameid,value;
+};
+struct npc_data {
+ struct block_list bl;
+ short n;
+ short class_,dir;
+ short speed;
+ unsigned char name[NAME_LENGTH];
+ unsigned char exname[NAME_LENGTH];
+ int chat_id;
+ short opt1,opt2,opt3,option;
+ short flag;
+ int walktimer; // [Valaris]
+ short to_x,to_y; // [Valaris]
+ struct walkpath_data walkpath;
+ unsigned int next_walktime;
+ unsigned int canmove_tick;
+
+ struct { // [Valaris]
+ unsigned state : 8;
+ unsigned change_walk_target : 1;
+ unsigned walk_easy : 1;
+ } state;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ short arenaflag;
+
+ void *chatdb;
+
+ union {
+ struct {
+ unsigned char *script;
+ short xs,ys;
+ int guild_id;
+ int timer,timerid,timeramount,nexttimer,rid;
+ unsigned int timertick;
+ struct npc_timerevent_list *timer_event;
+ int label_list_num;
+ struct npc_label_list *label_list;
+ int src_id;
+ } scr;
+ struct npc_item_list shop_item[1];
+ struct {
+ short xs,ys;
+ short x,y;
+ unsigned short mapindex;
+ } warp;
+ } u;
+ //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex]
+};
+
+//For quick linking to a guardian's info. [Skotlex]
+struct guardian_data {
+ int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium.
+ int guild_id;
+ int emblem_id;
+ int guardup_lv; //Level of GD_GUARDUP skill.
+ char guild_name[NAME_LENGTH];
+ struct guild_castle* castle;
+};
+
+struct mob_data {
+ struct block_list bl;
+ struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex]
+ char name[NAME_LENGTH];
+ struct {
+ unsigned size : 2; //Small/Big monsters.
+ unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
+ unsigned ai : 3; //Special ai for summoned monsters.
+ } special_state; //Special mob information that does not needs to be zero'ed on mob respawn.
+ struct {
+ unsigned state : 8;
+ unsigned skillstate : 8;
+ unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex]
+ unsigned targettype : 1;
+ unsigned steal_flag : 1;
+ unsigned steal_coin_flag : 1;
+ unsigned skillcastcancel : 1;
+ unsigned change_walk_target : 1;
+ unsigned walk_easy : 1;
+ unsigned soul_change_flag : 1; // Celest
+ unsigned alchemist: 1;
+ int provoke_flag; // Celest
+ } state;
+ struct status_change sc_data[MAX_STATUSCHANGE];
+ struct walkpath_data walkpath;
+ struct guardian_data* guardian_data;
+ struct item *lootitem;
+ struct {
+ int id;
+ int dmg;
+ } dmglog[DAMAGELOG_SIZE];
+ unsigned long tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]
+ short n;
+ short base_class,class_,dir,mode,level;
+ short m,x0,y0,xs,ys;
+ short to_x,to_y;
+ short target_dir;
+ short speed;
+ short attacked_count;
+ short target_lv;
+ int timer;
+ int hp, max_hp;
+ int target_id,attacked_id;
+ int spawndelay1,spawndelay2;
+ unsigned int attackabletime, canmove_tick, next_walktime;
+ unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime;
+ short move_fail_count;
+ short lootitem_count;
+ short sc_count;
+ short opt1,opt2,opt3,option;
+ short min_chase;
+
+ int deletetimer;
+ int skilltimer;
+ int skilltarget;
+ int def_ele;
+ int master_id,master_dist;
+
+ short skillx,skilly,skillid,skilllv,skillidx;
+ unsigned int skilldelay[MAX_MOBSKILL];
+ struct skill_timerskill skilltimerskill[MAX_MOBSKILLTIMERSKILL];
+ struct skill_unit_group skillunit[MAX_MOBSKILLUNITGROUP];
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
+ char npc_event[50];
+};
+
+struct pet_data {
+ struct block_list bl;
+ short n;
+ short class_,dir;
+ struct mob_db *db;
+ short speed;
+ char name[NAME_LENGTH];
+ struct {
+ unsigned state : 8 ;
+ unsigned skillstate : 8 ;
+ unsigned change_walk_target : 1 ;
+ unsigned casting_flag :1 ;//Skotlex: Used to identify when we are casting.
+ short skillbonus;
+ } state;
+ int timer;
+ short to_x,to_y;
+ short equip;
+ struct walkpath_data walkpath;
+ int target_id;
+ short target_lv;
+ int move_fail_count;
+ unsigned int attackabletime,next_walktime,last_thinktime;
+ short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex]
+ struct pet_status { //Pet Status data
+ short level;
+ short atk1,atk2;
+ short str,agi,vit,int_,dex,luk;
+ } *status; //[Skotlex]
+
+ struct pet_recovery { //Stat recovery
+ unsigned short type; //Status Change id
+ unsigned short delay; //How long before curing (secs).
+ int timer;
+ } *recovery; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_bonus {
+ unsigned short type; //bStr, bVit?
+ unsigned short val; //Qty
+ unsigned short duration; //in secs
+ unsigned short delay; //Time before recasting (secs)
+ int timer;
+ } *bonus; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_skill_attack { //Attack Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_.
+ unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks)
+ unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10%
+ } *a_skill; //[Skotlex]
+
+ struct pet_skill_support { //Support Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat)
+ unsigned short sp; //Max SP% for skill to trigger (100 = no check)
+ unsigned short delay; //Time (secs) between being able to recast.
+ int timer;
+ } *s_skill; //[Skotlex]
+
+ struct pet_loot {
+ struct item *item;
+ unsigned short count;
+ unsigned short weight;
+ unsigned short max;
+ int timer;
+ } *loot; //[Valaris] / Rewritten by [Skotlex]
+
+ struct skill_timerskill skilltimerskill[MAX_MOBSKILLTIMERSKILL]; // [Valaris]
+ struct skill_unit_group skillunit[MAX_MOBSKILLUNITGROUP]; // [Valaris]
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; // [Valaris]
+ struct map_session_data *msd;
+};
+
+enum { MS_IDLE,MS_WALK,MS_ATTACK,MS_DEAD,MS_DELAY };
+
+enum { NONE_ATTACKABLE,ATTACKABLE };
+
+enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // ˆÍ‚Ü‚êƒyƒiƒ‹ƒeƒBŒvŽZ—p
+
+// For equipment breaking/stripping effects
+enum {
+ EQP_WEAPON = 1, // Both weapons
+ EQP_ARMOR = 2, // Armor
+ EQP_SHIELD = 4, // Shield
+ EQP_HELM = 8, // Top-head headgear
+};
+
+// Mob List Held in memory for Dynamic Mobs [Wizputer]
+struct mob_list {
+ int m,x,y,xs,ys,class_,num,delay1,delay2,level;
+ char mobname[NAME_LENGTH],eventname[NAME_LENGTH];
+};
+
+struct map_data {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULL‚Ȃ牺‚Ìmap_data_other_server‚Æ‚µ‚Ĉµ‚¤
+ unsigned char *cell; //Contains temporary cell data that is set/unset on tiles.
+#ifdef CELL_NOSTACK
+ unsigned char *cell_bl; //Holds amount of bls in any given cell.
+#endif
+ char *alias; // [MouseJstr]
+ struct block_list **block;
+ struct block_list **block_mob;
+ int *block_count,*block_mob_count;
+ int m;
+ short xs,ys;
+ short bxs,bys;
+ int npc_num;
+ int users;
+ struct {
+ unsigned alias : 1;
+ unsigned nomemo : 1;
+ unsigned noteleport : 1;
+ unsigned noreturn : 1;
+ unsigned monster_noteleport : 1;
+ unsigned nosave : 1;
+ unsigned nobranch : 1;
+ unsigned nopenalty : 1;
+ unsigned pvp : 1;
+ unsigned pvp_noparty : 1;
+ unsigned pvp_noguild : 1;
+ unsigned pvp_nightmaredrop :1;
+ unsigned pvp_nocalcrank : 1;
+ unsigned gvg_castle : 1;
+ unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7
+ unsigned gvg_dungeon : 1; // Celest
+ unsigned gvg_noparty : 1;
+ unsigned nozenypenalty : 1;
+ unsigned notrade : 1;
+ unsigned noskill : 1;
+ unsigned nowarp : 1;
+ unsigned nowarpto : 1;
+ unsigned nopvp : 1; // [Valaris]
+ unsigned noicewall : 1; // [Valaris]
+ unsigned snow : 1; // [Valaris]
+ unsigned clouds : 1;
+ unsigned clouds2 : 1; // [Valaris]
+ unsigned fog : 1; // [Valaris]
+ unsigned fireworks : 1;
+ unsigned sakura : 1; // [Valaris]
+ unsigned leaves : 1; // [Valaris]
+ unsigned rain : 1; // [Valaris]
+ unsigned indoors : 1; // celest
+ unsigned nogo : 1; // [Valaris]
+ unsigned nobaseexp : 1; // [Lorky] added by Lupus
+ unsigned nojobexp : 1; // [Lorky]
+ unsigned nomobloot : 1; // [Lorky]
+ unsigned nomvploot : 1; // [Lorky]
+ unsigned nightenabled :1; //For night display. [Skotlex]
+ } flag;
+ struct point save;
+ struct npc_data *npc[MAX_NPC_PER_MAP];
+ struct {
+ int drop_id;
+ int drop_type;
+ int drop_per;
+ } drop_list[MAX_DROP_PER_MAP];
+ struct mob_list *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer]
+ int mob_delete_timer; // [Skotlex]
+};
+
+struct map_data_other_server {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULLŒÅ’è‚É‚µ‚Ä”»’f
+ unsigned long ip;
+ unsigned int port;
+};
+
+struct flooritem_data {
+ struct block_list bl;
+ unsigned char subx,suby;
+ int cleartimer;
+ int first_get_id,second_get_id,third_get_id;
+ unsigned int first_get_tick,second_get_tick,third_get_tick;
+ struct item item_data;
+};
+
+enum {
+ SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7
+ SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15
+ SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23
+ SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31
+ SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39
+ SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47
+ SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55
+ SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60
+ SP_CARTINFO=99, // 99
+
+ SP_BASEJOB=119, // 100+19 - celest
+ SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex]
+
+ // original 1000-
+ SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002
+ SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006
+ SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011
+ SP_ADDEFF, SP_RESEFF, // 1012-1013
+ SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018
+ SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021
+ SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025
+ SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027
+ SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030
+ SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032
+ SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034
+ SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037
+ SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042
+ SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046
+ SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050
+ SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057
+ SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062
+ SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066
+ SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070
+ SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076
+ SP_DISGUISE,SP_CLASSCHANGE, // 1077-1078
+ SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080
+ SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082
+ SP_DELAYRATE, // 1083
+
+ SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005
+ SP_NO_CASTCANCEL2,SP_INFINITE_ENDURE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010
+ SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012
+
+ SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017
+ SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020
+ SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025
+ SP_SUBSIZE, SP_DAMAGE_WHEN_UNEQUIP, SP_ADD_ITEM_HEAL_RATE, SP_LOSESP_WHEN_UNEQUIP, SP_EXP_ADDRACE, // 2026-2030
+ SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033
+ SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037
+ SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040
+ SP_ADD_SKILL_BLOW //2041
+};
+
+enum {
+ LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES
+};
+
+// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc)
+#define CELL_NPC 0x1
+#define CELL_REGEN 0x2
+#define CELL_PNEUMA 0x4
+#define CELL_SAFETYWALL 0x8
+#define CELL_LANDPROTECTOR 0x10
+#define CELL_BASILICA 0x20
+#define CELL_MOONLIT 0x40
+#define CELL_ICEWALL 0x80
+/*
+ * map_getcell()‚ÅŽg—p‚³‚ê‚éƒtƒ‰ƒO
+ */
+typedef enum {
+ CELL_CHKWALL=0, // •Ç(ƒZƒ‹ƒ^ƒCƒv1)
+ CELL_CHKWATER, // …ê(ƒZƒ‹ƒ^ƒCƒv3)
+ CELL_CHKGROUND, // ’n–ÊáŠQ•¨(ƒZƒ‹ƒ^ƒCƒv5)
+ CELL_CHKPASS, // ’ʉ߉”\(ƒZƒ‹ƒ^ƒCƒv1,5ˆÈŠO)
+ CELL_CHKNOPASS, // ’ʉߕs‰Â(ƒZƒ‹ƒ^ƒCƒv1,5)
+ CELL_GETTYPE, // ƒZƒ‹ƒ^ƒCƒv‚ð•Ô‚·
+ CELL_GETCELLTYPE,
+ CELL_CHKNPC=0x10, // ƒ^ƒbƒ`ƒ^ƒCƒv‚ÌNPC(ƒZƒ‹ƒ^ƒCƒv0x80ƒtƒ‰ƒO)
+ CELL_CHKREGEN, // cells that improve regeneration
+ CELL_CHKPNEUMA,
+ CELL_CHKSAFETYWALL,
+ CELL_CHKBASILICA, // ƒoƒWƒŠƒJ(ƒZƒ‹ƒ^ƒCƒv0x40ƒtƒ‰ƒO)
+ CELL_CHKLANDPROTECTOR,
+ CELL_CHKMOONLIT,
+ CELL_CHKICEWALL,
+} cell_t;
+// map_setcell()‚ÅŽg—p‚³‚ê‚éƒtƒ‰ƒO
+enum {
+ CELL_SETNPC=0x10, // ƒ^ƒbƒ`ƒ^ƒCƒv‚ÌNPC‚ðƒZƒbƒg
+ CELL_CLRNPC,
+ CELL_SETBASILICA, // ƒoƒWƒŠƒJ‚ðƒZƒbƒg
+ CELL_CLRBASILICA, // ƒoƒWƒŠƒJ‚ðƒNƒŠƒA
+ CELL_SETREGEN, // set regen cell
+ CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth
+ CELL_CLRLANDPROTECTOR,
+ CELL_SETPNEUMA,
+ CELL_CLRPNEUMA,
+ CELL_SETSAFETYWALL,
+ CELL_CLRSAFETYWALL,
+ CELL_SETMOONLIT,
+ CELL_CLRMOONLIT,
+ CELL_SETICEWALL,
+ CELL_CLRICEWALL,
+};
+
+struct chat_data {
+ struct block_list bl;
+
+ unsigned char pass[8]; /* password */
+ unsigned char title[61]; /* room title MAX 60 */
+ unsigned char limit; /* join limit */
+ unsigned char trigger;
+ unsigned char users; /* current users */
+ unsigned char pub; /* room attribute */
+ struct map_session_data *usersd[20];
+ struct block_list *owner_;
+ struct block_list **owner;
+ char npc_event[50];
+};
+
+extern struct map_data map[];
+extern int map_num;
+extern int autosave_interval;
+extern int agit_flag;
+extern int night_flag; // 0=day, 1=night [Yor]
+extern int kick_on_disconnect; //To allow inter-server reconnections without kicking players out [Skotlex]
+extern int enable_spy; //Determines if @spy commands are active.
+extern char db_path[256];
+
+// gat?Ö§
+int map_getcell(int,int,int,cell_t);
+int map_getcellp(struct map_data*,int,int,cell_t);
+void map_setcell(int,int,int,int);
+extern int map_read_flag; // 0: grf«Õ«¡«¤«E1: «­«ã«Ã«·«E2: «­«ã«Ã«·«E?õê)
+enum {
+ READ_FROM_GAT, READ_FROM_AFM,
+ READ_FROM_BITMAP, CREATE_BITMAP,
+ READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED
+};
+
+extern char motd_txt[];
+extern char help_txt[];
+extern char help2_txt[];
+extern char charhelp_txt[];
+
+extern char talkie_mes[];
+
+extern char wisp_server_name[];
+
+// ŽI‘S‘Ìî•ñ
+void map_setusers(int);
+int map_getusers(void);
+// block휊֘A
+int map_freeblock(struct block_list *bl);
+int map_freeblock_lock(void);
+int map_freeblock_unlock(void);
+// blockŠÖ˜A
+int map_addblock_sub(struct block_list *, int);
+int map_delblock_sub(struct block_list *, int);
+#define map_addblock(bl) map_addblock_sub(bl,1)
+#define map_delblock(bl) map_delblock_sub(bl,1)
+int map_moveblock(struct block_list *, int, int, unsigned int);
+int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...);
+// -- moonsoul (added map_foreachincell)
+int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
+int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
+int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest
+int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...);
+int map_countnearpc(int,int,int);
+//blockŠÖ˜A‚ɒljÁ
+int map_count_oncell(int m,int x,int y,int type);
+struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *);
+// ˆêŽž“IobjectŠÖ˜A
+int map_addobject(struct block_list *);
+int map_delobject(int);
+int map_delobjectnofree(int id);
+void map_foreachobject(int (*)(struct block_list*,va_list),int,...);
+//
+int map_quit(struct map_session_data *);
+// npc
+int map_addnpc(int,struct npc_data *);
+
+// °ƒAƒCƒeƒ€ŠÖ˜A
+int map_clearflooritem_timer(int,unsigned int,int,int);
+int map_removemobs_timer(int,unsigned int,int,int);
+#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1)
+int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int);
+int map_searchrandfreecell(int,int,int,int);
+
+// ƒLƒƒƒ‰id„ƒLƒƒƒ‰–¼ •ÏŠ·ŠÖ˜A
+void map_addchariddb(int charid,char *name);
+void map_delchariddb(int charid);
+int map_reqchariddb(struct map_session_data * sd,int charid);
+char * map_charid2nick(int);
+struct map_session_data * map_charid2sd(int);
+
+struct map_session_data * map_id2sd(int);
+struct block_list * map_id2bl(int);
+int map_mapindex2mapid(unsigned short mapindex);
+int map_mapname2mapid(char*);
+int map_mapname2ipport(unsigned short,int*,int*);
+int map_setipport(unsigned short map,unsigned long ip,int port);
+int map_eraseipport(unsigned short map,unsigned long ip,int port);
+int map_eraseallipport(void);
+void map_addiddb(struct block_list *);
+void map_deliddb(struct block_list *bl);
+struct map_session_data** map_getallusers(int *users);
+int map_foreachiddb(int (*)(DBKey,void*,va_list),...);
+void map_addnickdb(struct map_session_data *);
+struct map_session_data * map_nick2sd(char*);
+int compare_item(struct item *a, struct item *b);
+
+// ‚»‚Ì‘¼
+int map_check_dir(int s_dir,int t_dir);
+int map_calc_dir( struct block_list *src,int x,int y);
+int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex]
+
+// Water functions...
+//
+int map_setwaterheight(int m, char *mapname, int height);
+int map_waterheight(char *mapname);
+
+// path.c‚æ‚è
+int path_search(struct walkpath_data*,int,int,int,int,int,int);
+int path_search_long(struct shootpath_data *,int,int,int,int,int);
+int path_blownpos(int m,int x0,int y0,int dx,int dy,int count);
+
+// distance related functions [Skotlex]
+#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance)
+#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance)
+#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance)
+int check_distance(int dx, int dy, int distance);
+
+#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y)
+#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1))
+#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1))
+unsigned int distance(int dx, int dy);
+
+int cleanup_sub(struct block_list *bl, va_list ap);
+
+void map_helpscreen(int flag); // [Valaris]
+int map_delmap(char *mapname);
+
+struct mob_list* map_addmobtolist(unsigned short m); // [Wizputer]
+void map_spawnmobs(int); // [Wizputer]
+void map_removemobs(int); // [Wizputer]
+
+//Added for own save method
+int charsql_db_init(int method);
+
+extern char *INTER_CONF_NAME;
+extern char *LOG_CONF_NAME;
+extern char *MAP_CONF_NAME;
+extern char *BATTLE_CONF_FILENAME;
+extern char *ATCOMMAND_CONF_FILENAME;
+extern char *CHARCOMMAND_CONF_FILENAME;
+extern char *SCRIPT_CONF_NAME;
+extern char *MSG_CONF_NAME;
+extern char *GRF_PATH_FILENAME;
+
+
+extern int charsave_method; //needed ..
+
+#ifndef TXT_ONLY
+
+// MySQL
+#ifdef __WIN32
+#include <my_global.h>
+#include <my_sys.h>
+#endif
+#include <mysql.h>
+
+extern char tmp_sql[65535];
+
+extern int db_use_sqldbs;
+extern MYSQL mmysql_handle;
+extern MYSQL_RES* sql_res ;
+extern MYSQL_ROW sql_row ;
+
+extern MYSQL lmysql_handle;
+extern MYSQL_RES* lsql_res ;
+extern MYSQL_ROW lsql_row ;
+
+extern MYSQL charsql_handle;
+extern MYSQL_RES* charsql_res;
+extern MYSQL_ROW charsql_row;
+
+extern MYSQL logmysql_handle;
+extern MYSQL_RES* logsql_res ;
+extern MYSQL_ROW logsql_row ;
+
+extern int mail_server_enable;
+extern MYSQL mail_handle;
+extern MYSQL_RES* mail_res ;
+extern MYSQL_ROW mail_row ;
+
+extern char item_db_db[32];
+extern char item_db2_db[32];
+extern char mob_db_db[32];
+extern char mob_db2_db[32];
+extern char login_db[32];
+
+// SQL for databases not supported yet. [Valaris]
+extern int db_use_newsqldbs;
+
+extern char abra_sqldb[32];
+extern char attr_fix_sqldb[32];
+extern char cast_sqldb[32];
+extern char castle_sqldb[32];
+extern char create_arrow_sqldb[32];
+extern char exp_sqldb[32];
+extern char exp_guild_sqldb[32];
+extern char item_bluebox_sqldb[32];
+extern char item_cardalbum_sqldb[32];
+extern char item_giftbox_sqldb[32];
+extern char item_scroll_sqldb[32];
+extern char item_violetbox_sqldb[32];
+extern char job_sqldb1[32];
+extern char mob_boss_sqldb[32];
+extern char mob_branch_sqldb[32];
+extern char mob_poring_sqldb[32];
+extern char mob_skill_sqldb[32];
+extern char pet_sqldb[32];
+extern char produce_sqldb[32];
+extern char refine_sqldb[32];
+extern char size_fix_sqldb[32];
+extern char skill_sqldb[32];
+extern char skill_require_sqldb[32];
+extern char skill_tree_sqldb[32];
+// End [Valaris]
+
+extern char login_db_level[32];
+extern char login_db_account_id[32];
+
+extern char gm_db[32];
+extern char gm_db_level[32];
+extern char gm_db_account_id[32];
+
+extern int read_gm_interval;
+
+extern char char_db[32];
+
+#ifdef MAPREGSQL
+// [zBuffer] SQL Mapreg
+extern MYSQL mapregsql_handle;
+extern MYSQL_RES* mapregsql_res ;
+extern MYSQL_ROW mapregsql_row;
+#endif
+
+extern char mail_db[32];
+
+#endif /* not TXT_ONLY */
+
+extern int lowest_gm_level;
+extern char main_chat_nick[16];
+
+#endif
diff --git a/src/map/mercenary.c b/src/map/mercenary.c
new file mode 100644
index 000000000..704963649
--- /dev/null
+++ b/src/map/mercenary.c
@@ -0,0 +1,11 @@
+// Homunculus and future Mercenary system code go here [Celest]
+
+int do_init_merc (void)
+{
+ // read DB's
+}
+
+int do_final_merc (void)
+{
+ // clean up
+} \ No newline at end of file
diff --git a/src/map/mercenary.h b/src/map/mercenary.h
new file mode 100644
index 000000000..704963649
--- /dev/null
+++ b/src/map/mercenary.h
@@ -0,0 +1,11 @@
+// Homunculus and future Mercenary system code go here [Celest]
+
+int do_init_merc (void)
+{
+ // read DB's
+}
+
+int do_final_merc (void)
+{
+ // clean up
+} \ No newline at end of file
diff --git a/src/map/mob.c b/src/map/mob.c
new file mode 100644
index 000000000..b315c5c43
--- /dev/null
+++ b/src/map/mob.c
@@ -0,0 +1,5020 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+
+#include "timer.h"
+#include "socket.h"
+#include "db.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "map.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "guild.h"
+#include "itemdb.h"
+#include "skill.h"
+#include "battle.h"
+#include "party.h"
+#include "npc.h"
+#include "log.h"
+#include "showmsg.h"
+#include "script.h"
+#include "atcommand.h"
+#include "date.h"
+
+#define MIN_MOBTHINKTIME 100
+#define MIN_MOBLINKTIME 1000
+
+#define MOB_LAZYSKILLPERC 10 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute)
+#define MOB_LAZYMOVEPERC 50 // Move probability in the negligent mode MOB (rate of 1000 minute)
+#define MOB_LAZYWARPPERC 20 // Warp probability in the negligent mode MOB (rate of 1000 minute)
+
+//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex]
+struct mob_db *mob_db_data[MAX_MOB_DB+1];
+struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested.
+
+struct mob_db *mob_db(int index) { if (index < 0 || index > MAX_MOB_DB || mob_db_data[index] == NULL) return mob_dummy; return mob_db_data[index]; }
+
+#define CLASSCHANGE_BOSS_NUM 21
+
+/*==========================================
+ * Local prototype declaration (only required thing)
+ *------------------------------------------
+ */
+static int mob_makedummymobdb(int);
+static int mob_timer(int,unsigned int,int,int);
+static int mob_spawn_guardian_sub(int,unsigned int,int,int);
+int mobskill_use(struct mob_data *md,unsigned int tick,int event);
+int mobskill_deltimer(struct mob_data *md );
+int mob_skillid2skillidx(int class_,int skillid);
+int mobskill_use_id(struct mob_data *md,struct block_list *target,int skill_idx);
+
+/*==========================================
+ * Mob is searched with a name.
+ *------------------------------------------
+ */
+int mobdb_searchname(const char *str)
+{
+ int i;
+ struct mob_db* mob;
+ for(i=0;i<=MAX_MOB_DB;i++){
+ mob = mob_db(i);
+ if(mob == mob_dummy) //Skip dummy mobs.
+ continue;
+ if(strcmpi(mob->name,str)==0 || strcmp(mob->jname,str)==0 ||
+ memcmp(mob->name,str,NAME_LENGTH)==0 || memcmp(mob->jname,str,NAME_LENGTH)==0)
+ return i;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Id Mob is checked.
+ *------------------------------------------
+ */
+int mobdb_checkid(const int id)
+{
+ if (mob_db(id) == mob_dummy)
+ return 0;
+ if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question.
+ return 0;
+ return id;
+}
+
+/*==========================================
+ * The minimum data set for MOB spawning
+ *------------------------------------------
+ */
+int mob_spawn_dataset(struct mob_data *md,const char *mobname,int class_)
+{
+ nullpo_retr(0, md);
+
+ md->bl.prev=NULL;
+ md->bl.next=NULL;
+
+ md->base_class = md->class_ = class_;
+ md->db = mob_db(class_);
+
+ if(strcmp(mobname,"--en--")==0)
+ strncpy(md->name,md->db->name,NAME_LENGTH-1);
+ else if(strcmp(mobname,"--ja--")==0)
+ strncpy(md->name,md->db->jname,NAME_LENGTH-1);
+ else
+ strncpy(md->name,mobname,NAME_LENGTH-1);
+
+ md->n = 0;
+ md->bl.id= npc_get_new_npc_id();
+
+ memset(&md->state,0,sizeof(md->state));
+ md->timer = -1;
+ md->target_id=0;
+ md->attacked_id=0;
+ md->attacked_count=0;
+ md->speed=md->db->speed;
+
+ return 0;
+}
+
+/*==========================================
+ * Fetches a random mob_id [Skotlex]
+ * type: Where to fetch from:
+ * 0: dead branch list
+ * 1: poring list
+ * 2: bloody branch list
+ * flag:
+ * &1: Apply the summon success chance found in the list.
+ * &2: Apply a monster check level.
+ * lv: Mob level to check against
+ *------------------------------------------
+ */
+
+int mob_get_random_id(int type, int flag, int lv) {
+ struct mob_db *mob;
+ int i=0, k=0, class_;
+ if(type < 0 || type >= MAX_RANDOMMONSTER) {
+ if (battle_config.error_log)
+ ShowError("mob_get_random_id: Invalid type (%d) of random monster.\n", type);
+ return 0;
+ }
+ do {
+ class_ = rand() % MAX_MOB_DB;
+ if (flag&1)
+ k = rand() % 1000000;
+ mob = mob_db(class_);
+ } while ((mob == mob_dummy || mob->summonper[type] <= k ||
+ (flag&2 && lv < mob->lv)) && (i++) < MAX_MOB_DB);
+ if(i >= MAX_MOB_DB)
+ class_ = mob_db_data[0]->summonper[type];
+ return class_;
+}
+
+/*==========================================
+ * The MOB appearance for one time (for scripts)
+ *------------------------------------------
+ */
+int mob_once_spawn (struct map_session_data *sd, char *mapname,
+ int x, int y, const char *mobname, int class_, int amount, const char *event)
+{
+ struct mob_data *md = NULL;
+ int m, count, lv = 255;
+ int i, j;
+
+ if(sd) lv = sd->status.base_level;
+
+ if(sd && strcmp(mapname,"this")==0)
+ m = sd->bl.m;
+ else
+ m = map_mapname2mapid(mapname);
+
+ if (m < 0 || amount <= 0 || (class_ >= 0 && class_ <= 1000) || class_ > MAX_MOB_DB + 2*MAX_MOB_DB) // ’l‚ªˆÙí‚Ȃ碊«‚ðŽ~‚ß‚é
+ return 0;
+
+ if (sd) { //even if the coords were wrong, spawn mob anyways (but look for most suitable coords first) Got from Freya [Lupus]
+ if (x <= 0 || y <= 0) {
+ if (x <= 0) x = sd->bl.x + rand() % 3 - 1;
+ if (y <= 0) y = sd->bl.y + rand() % 3 - 1;
+ if (map_getcell(m, x, y, CELL_CHKNOPASS)) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+ }
+ } else if (x <= 0 || y <= 0) {
+ i = j = 0;
+ do {
+ x = rand() % (map[m].xs - 2) + 1;
+ y = rand() % (map[m].ys - 2) + 1;
+ } while ((i = map_getcell(m, x, y, CELL_CHKNOPASS)) && j++ < 64);
+ if (i) { // not solved?
+ x = 0;
+ y = 0;
+ }
+ }
+
+ for (count = 0; count < amount; count++) {
+ md = (struct mob_data *)aCalloc(1,sizeof(struct mob_data));
+
+ if (class_ > 2*MAX_MOB_DB) { // large/tiny mobs [Valaris]
+ md->special_state.size = 2;
+ class_ -= 2*MAX_MOB_DB;
+ } else if (class_ > MAX_MOB_DB) {
+ md->special_state.size = 1;
+ class_ -= MAX_MOB_DB;
+ }
+
+ if (class_ < 0) {
+ class_ = mob_get_random_id(-class_ -1, battle_config.random_monster_checklv?3:1, lv);
+ if (!class_) {
+ aFree(md);
+ return 0;
+ }
+ if (battle_config.dead_branch_active)
+ //Behold Aegis's masterful decisions yet again...
+ //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3
+ md->mode = mob_db(class_)->mode|MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE;
+ }
+
+
+ if(mob_db(class_)->mode & MD_LOOTER)
+ md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ mob_spawn_dataset (md, mobname, class_);
+ md->bl.m = m;
+ md->bl.x = x;
+ md->bl.y = y;
+ md->m = m;
+ md->x0 = x;
+ md->y0 = y;
+ //md->xs = 0;
+ //md->ys = 0;
+ md->spawndelay1 = -1; // ˆê“x‚̂݃tƒ‰ƒO
+ md->spawndelay2 = -1; // ˆê“x‚̂݃tƒ‰ƒO
+
+ //better safe than sorry, current md->npc_event has a size of 50
+ if (strlen(event) < 50)
+ memcpy(md->npc_event, event, strlen(event));
+
+ md->bl.type = BL_MOB;
+ map_addiddb (&md->bl);
+ mob_spawn (md->bl.id);
+
+ if(class_ == MOBID_EMPERIUM) { // emperium hp based on defense level [Valaris]
+ struct guild_castle *gc = guild_mapname2gc(map[md->bl.m].name);
+ struct guild *g = gc?guild_search(gc->guild_id):NULL;
+ if(gc) {
+ md->max_hp += 2000 * gc->defense;
+ md->hp = md->max_hp;
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->castle = gc;
+ md->guardian_data->number = MAX_GUARDIANS;
+ md->guardian_data->guild_id = gc->guild_id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ }
+ else if (gc->guild_id) //Guild not yet available, retry in 5.
+ add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id);
+ }
+ } // end addition [Valaris]
+ }
+ return (amount > 0) ? md->bl.id : 0;
+}
+/*==========================================
+ * The MOB appearance for one time (& area specification for scripts)
+ *------------------------------------------
+ */
+int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
+ int x0,int y0,int x1,int y1,
+ const char *mobname,int class_,int amount,const char *event)
+{
+ int x,y,i,max,lx=-1,ly=-1,id=0;
+ int m;
+
+ if(strcmp(mapname,"this")==0)
+ m=sd->bl.m;
+ else
+ m=map_mapname2mapid(mapname);
+
+ max=(y1-y0+1)*(x1-x0+1)*3;
+ if(max>1000)max=1000;
+
+ if(m<0 || amount<=0 || mob_db(class_) == mob_dummy) // A summon is stopped if a value is unusual
+ return 0;
+
+ for(i=0;i<amount;i++){
+ int j=0;
+ do{
+ x=rand()%(x1-x0+1)+x0;
+ y=rand()%(y1-y0+1)+y0;
+ } while (map_getcell(m,x,y,CELL_CHKNOPASS) && (++j)<max);
+ // freya }while( ( (c=map_getcell(m,x,y))==1 || c==5)&& (++j)<max );
+ if(j>=max){
+ if(lx>=0){ // Since reference went wrong, the place which boiled before is used.
+ x=lx;
+ y=ly;
+ }else
+ return 0; // Since reference of the place which boils first went wrong, it stops.
+ }
+ if(x==0||y==0) ShowWarning("mob_once_spawn_area: xory=0, x=%d,y=%d,x0=%d,y0=%d\n",x,y,x0,y0);
+ id=mob_once_spawn(sd,mapname,x,y,mobname,class_,1,event);
+ lx=x;
+ ly=y;
+ }
+ return id;
+}
+/*==========================================
+ * Set a Guardian's guild data [Skotlex]
+ *------------------------------------------
+ */
+static int mob_spawn_guardian_sub(int tid,unsigned int tick,int id,int data)
+{ //Needed because the guild_data may not be available at guardian spawn time.
+ struct block_list* bl = map_id2bl(id);
+ struct mob_data* md;
+ struct guild* g;
+
+ if (bl == NULL) //It is possible mob was already removed from map when the castle has no owner. [Skotlex]
+ return 0;
+
+ if (bl->type != BL_MOB || (md = (struct mob_data*)bl) == NULL)
+ {
+ ShowError("mob_spawn_guardian_sub: Block error!\n");
+ return 0;
+ }
+
+ nullpo_retr(0, md->guardian_data);
+ g = guild_search(data);
+
+ if (g == NULL)
+ { //Liberate castle, if the guild is not found this is an error! [Skotlex]
+ ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n",data);
+ if (md->class_ == MOBID_EMPERIUM)
+ { //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on.
+ md->guardian_data->guild_id = 0;
+ if (md->guardian_data->castle->guild_id) //Free castle up.
+ {
+ ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 1, 0);
+ }
+ } else {
+ if (md->guardian_data->castle->guardian[md->guardian_data->number].visible)
+ { //Safe removal of guardian.
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ mob_delete(md); //Remove guardian.
+ }
+ return 0;
+ }
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ return 0;
+}
+
+/*==========================================
+ * Summoning Guardians [Valaris]
+ *------------------------------------------
+ */
+int mob_spawn_guardian(struct map_session_data *sd,char *mapname,
+ int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian)
+{
+ struct mob_data *md=NULL;
+ struct guild *g=NULL;
+ struct guild_castle *gc;
+
+ int m,count=1;
+
+ if( sd && strcmp(mapname,"this")==0)
+ m=sd->bl.m;
+ else
+ m=map_mapname2mapid(mapname);
+
+ if(m<0 || amount<=0 || (class_>=0 && class_<=1000) || class_>MAX_MOB_DB) // Invalid monster classes
+ return 0;
+
+ if(class_<0)
+ return 0;
+
+ if(guardian < 0 || guardian >= MAX_GUARDIANS)
+ {
+ ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map[m].name);
+ return 0;
+ }
+ if (amount > 1)
+ ShowWarning("mob_spawn_guardian: Spawning %d guardians in position %d (castle map %s)\n", amount, map[m].name);
+
+ if(sd){
+ if(x<=0) x=sd->bl.x;
+ if(y<=0) y=sd->bl.y;
+ }
+ else if(x<=0 || y<=0)
+ ShowWarning("mob_spawn_guardian: Invalid coordinates (%d,%d)\n",x,y);
+
+ gc=guild_mapname2gc(map[m].name);
+ if (gc == NULL)
+ {
+ ShowError("mob_spawn_guardian: No castle set at map %s\n", map[m].name);
+ return 0;
+ }
+ if (!gc->guild_id)
+ ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map[m].name);
+ else
+ g = guild_search(gc->guild_id);
+
+ if (gc->guardian[guardian].id)
+ ShowWarning("mob_spawn_guardian: Spawning guardian in position %d which already has a guardian (castle map %s)\n", guardian, map[m].name);
+
+ for(count=0;count<amount;count++){
+ md=(struct mob_data *) aCalloc(1, sizeof(struct mob_data));
+ mob_spawn_dataset(md,mobname,class_);
+ md->bl.m=m;
+ md->bl.x=x;
+ md->bl.y=y;
+ md->m =m;
+ md->x0=x;
+ md->y0=y;
+ md->xs=0;
+ md->ys=0;
+ md->spawndelay1=-1; // Only once is a flag.
+ md->spawndelay2=-1; // Only once is a flag.
+
+ //better safe than sorry, current md->npc_event has a size of 50 [Skotlex]
+ if (strlen(event) < 50)
+ memcpy(md->npc_event, event, strlen(event));
+
+ md->bl.type=BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+
+ md->max_hp += 2000 * gc->defense;
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->number = guardian;
+ md->guardian_data->guild_id = gc->guild_id;
+ md->guardian_data->castle = gc;
+ md->hp = gc->guardian[guardian].hp;
+ gc->guardian[guardian].id = md->bl.id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ } else if (md->guardian_data->guild_id)
+ add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id);
+ }
+
+ return (amount>0)?md->bl.id:0;
+}
+
+/*==========================================
+ * Is MOB in the state in which the present movement is possible or not?
+ *------------------------------------------
+ */
+int mob_can_move(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ if(DIFF_TICK(md->canmove_tick, gettick()) > 0 || md->skilltimer != -1 || (md->opt1 > 0 && md->opt1 != OPT1_STONEWAIT) || md->option&OPTION_HIDE)
+ return 0;
+ // ƒAƒ“ƒNƒ‹’†‚Å“®‚¯‚È‚¢‚Æ‚©
+ if( md->sc_data[SC_ANKLE].timer != -1 || //ƒAƒ“ƒNƒ‹ƒXƒlƒA
+ md->sc_data[SC_AUTOCOUNTER].timer != -1 || //ƒI[ƒgƒJƒEƒ“ƒ^[
+ md->sc_data[SC_BLADESTOP].timer != -1 || //”’nŽæ‚è
+ md->sc_data[SC_SPIDERWEB].timer != -1 || //ƒXƒpƒCƒ_[ƒEƒFƒbƒu
+ (md->sc_data[SC_DANCING].timer !=-1 && md->sc_data[SC_DANCING].val1 == CG_HERMODE) || //cannot move while Hermod is active.
+ (md->sc_data[SC_GOSPEL].timer !=-1 && md->sc_data[SC_GOSPEL].val4 == BCT_SELF) || // cannot move while gospel is in effect
+ md->sc_data[SC_STOP].timer != -1 ||
+ md->sc_data[SC_CLOSECONFINE].timer != -1 ||
+ md->sc_data[SC_CLOSECONFINE2].timer != -1
+ )
+ return 0;
+
+ return 1;
+}
+
+/*==========================================
+ * Time calculation concerning one step next to mob
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ if(md->walkpath.path_pos>=md->walkpath.path_len)
+ return -1;
+ if(md->walkpath.path[md->walkpath.path_pos]&1)
+ return status_get_speed(&md->bl)*14/10;
+ return status_get_speed(&md->bl);
+}
+
+static int mob_walktoxy_sub(struct mob_data *md);
+
+/*==========================================
+ * Mob Walk processing
+ *------------------------------------------
+ */
+static int mob_walk(struct mob_data *md,unsigned int tick,int data)
+{
+ int moveblock;
+ int i;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+ int x,y,dx,dy;
+
+ nullpo_retr(0, md);
+
+ md->state.state=MS_IDLE;
+ if(md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_pos!=data)
+ return 0;
+
+ md->walkpath.path_half ^= 1;
+ if(md->walkpath.path_half==0){
+ md->walkpath.path_pos++;
+ if(md->state.change_walk_target){
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+ }
+ else {
+ if(md->walkpath.path[md->walkpath.path_pos]>=8)
+ return 1;
+
+ x = md->bl.x;
+ y = md->bl.y;
+#ifndef CELL_NOSTACK
+ if(map_getcell(md->bl.m,x,y,CELL_CHKNOPASS)) {
+ mob_stop_walking(md,1);
+ return 0;
+ }
+#endif
+ md->dir=md->walkpath.path[md->walkpath.path_pos];
+ dx = dirx[md->dir];
+ dy = diry[md->dir];
+
+ if (map_getcell(md->bl.m,x+dx,y+dy,CELL_CHKBASILICA) && !(status_get_mode(&md->bl)&MD_BOSS)) {
+ mob_stop_walking(md,1);
+ return 0;
+ }
+
+ if (map_getcell(md->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+
+ moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE);
+
+ md->state.state=MS_WALK;
+ map_foreachinmovearea(clif_moboutsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md);
+
+ if ( md->min_chase > md->db->range2)
+ md->min_chase--;
+
+ x += dx;
+ y += dy;
+ map_moveblock(&md->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_mobinsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,md);
+ md->state.state=MS_IDLE;
+
+ if(md->option&4)
+ skill_check_cloaking(&md->bl);
+ }
+ if((i=calc_next_walk_step(md))>0){
+ i = i>>1;
+ if(i < 1 && md->walkpath.path_half == 0)
+ i = 1;
+
+ if(md->walkpath.path_pos>=md->walkpath.path_len)
+ clif_fixmobpos(md); // ‚Æ‚Ü‚Á‚½‚Æ‚«‚Ɉʒu‚ÌÄ‘—M
+ else {
+ md->timer=add_timer(tick+i,mob_timer,md->bl.id,md->walkpath.path_pos);
+ md->state.state=MS_WALK;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Reachability to a Specification ID existence place
+ * state indicates type of 'seek' mob should do:
+ * - MSS_LOOT: Looking for item, path must be easy.
+ * - MSS_RUSH: Chasing attacking player, path is determined by mob_ai&1
+ * - MSS_FOLLOW: Initiative/support seek, path must be easy.
+ *------------------------------------------
+ */
+int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state)
+{
+ int dx,dy;
+ struct walkpath_data wpd;
+ int i, easy = 0;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, bl);
+ switch (state) {
+ case MSS_RUSH:
+ easy = (battle_config.mob_ai&1?0:1);
+ break;
+ case MSS_LOOT:
+ case MSS_FOLLOW:
+ default:
+ easy = 1;
+ break;
+ }
+
+ if( md->bl.m != bl->m) // ˆá‚¤ƒƒbƒv
+ return 0;
+
+ if( md->bl.x==bl->x && md->bl.y==bl->y ) // “¯‚¶ƒ}ƒX
+ return 1;
+
+ if( range>0 && !check_distance_bl(&md->bl, bl, range))
+ return 0;
+
+ dx=abs(bl->x - md->bl.x);
+ dy=abs(bl->y - md->bl.y);
+ // Obstacle judging
+ wpd.path_len=0;
+ wpd.path_pos=0;
+ wpd.path_half=0;
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x,bl->y,easy)!=-1)
+ return 1;
+
+ if(bl->type!=BL_PC && bl->type!=BL_MOB)
+ return 0;
+
+ // It judges whether it can adjoin or not.
+ dx=(dx>0)?1:((dx<0)?-1:0);
+ dy=(dy>0)?1:((dy<0)?-1:0);
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-dx,bl->y-dy,easy)!=-1)
+ return 1;
+ for(i=0;i<9;i++){
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-1+i/3,bl->y-1+i%3,easy)!=-1)
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Links nearby mobs (supportive mobs)
+ *------------------------------------------
+ */
+static int mob_linksearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ int class_;
+ struct block_list *target;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=(struct mob_data *)bl;
+ class_ = va_arg(ap, int);
+ target = va_arg(ap, struct block_list *);
+ tick=va_arg(ap, unsigned int);
+
+ if (md->class_ == class_ && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME
+ && (!md->target_id || md->state.targettype == NONE_ATTACKABLE))
+ {
+ md->last_linktime = tick;
+ if( mob_can_reach(md,target,md->db->range2, MSS_FOLLOW) ){ // Reachability judging
+ md->target_id = target->id;
+ md->attacked_count = 0;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range3;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Attack processing of mob
+ *------------------------------------------
+ */
+static int mob_attack(struct mob_data *md,unsigned int tick,int data)
+{
+ struct block_list *tbl=NULL;
+
+ int range;
+
+ nullpo_retr(0, md);
+
+ md->min_chase=md->db->range3;
+ md->state.state=MS_IDLE;
+ md->state.skillstate=MSS_IDLE;
+
+ if( md->skilltimer!=-1 ) // ƒXƒLƒ‹Žg—p’†
+ return 0;
+
+ if((tbl=map_id2bl(md->target_id))==NULL || !status_check_skilluse(&md->bl, tbl, 0, 0)){
+ md->target_id=0;
+ md->state.targettype = NONE_ATTACKABLE;
+ return 0;
+ }
+
+ if (!check_distance_bl(&md->bl, tbl, md->db->range3)){
+ mob_stopattack(md);
+ return 0;
+ }
+
+ range = md->db->range;
+ if (range <= 3)
+ range++; //Melee attackers get a bonus range cell when attacking.
+
+ /* It seems mobs always teleport the last two tiles when chasing players, so do not give them this bonus range tile.[Skotlex]
+ if(battle_iswalking(tbl)) range++;
+ */
+ if(!check_distance_bl(&md->bl, tbl, range))
+ return 0;
+ if(battle_config.monster_attack_direction_change)
+ md->dir=map_calc_dir(&md->bl, tbl->x,tbl->y ); // Œü‚«Ý’è
+
+ if (status_get_mode(&md->bl)&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
+ { // Link monsters nearby [Skotlex]
+ md->last_linktime = tick;
+ map_foreachinarea(mob_linksearch, md->bl.m,
+ md->bl.x-md->db->range2, md->bl.y-md->db->range2,
+ md->bl.x+md->db->range2, md->bl.y+md->db->range2,
+ BL_MOB, md->class_, tbl, tick);
+ }
+
+ md->state.skillstate=md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ if( mobskill_use(md,tick,-1) ) // ƒXƒLƒ‹Žg—p
+ return 0;
+
+ if(md->sc_data && md->sc_data[SC_WINKCHARM].timer != -1)
+ clif_emotion(&md->bl, 3);
+ else
+ md->target_lv = battle_weapon_attack(&md->bl,tbl,tick,0);
+
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+
+ //Mobs can't move if they can't attack neither.
+ //Use the attack delay for next can attack try
+ //But use the attack motion to know when it can start moving. [Skotlex]
+ md->attackabletime = tick + status_get_adelay(&md->bl);
+ md->canmove_tick = tick + status_get_amotion(&md->bl);
+
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ md->state.state=MS_ATTACK;
+
+ return 0;
+}
+
+/*==========================================
+ * The attack of PC which is attacking id is stopped.
+ * The callback function of clif_foreachclient
+ *------------------------------------------
+ */
+int mob_stopattacked(struct map_session_data *sd,va_list ap)
+{
+ int id;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ id=va_arg(ap,int);
+ if(sd->attacktarget==id)
+ pc_stopattack(sd);
+ return 0;
+}
+/*==========================================
+ * The timer in which the mob's states changes
+ *------------------------------------------
+ */
+int mob_changestate(struct mob_data *md,int state,int type)
+{
+ unsigned int tick;
+ int i;
+
+ nullpo_retr(0, md);
+
+ if(md->timer != -1)
+ delete_timer(md->timer,mob_timer);
+ md->timer=-1;
+ md->state.state=state;
+
+ switch(state){
+ case MS_WALK:
+ if((i=calc_next_walk_step(md))>0){
+ i = i>>2;
+ md->timer=add_timer(gettick()+i,mob_timer,md->bl.id,0);
+ }
+ else
+ md->state.state=MS_IDLE;
+ break;
+ case MS_ATTACK:
+ tick = gettick();
+ i=DIFF_TICK(md->attackabletime,tick);
+ if(i>0 && i<2000)
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ else if(type) {
+ md->attackabletime = tick + status_get_amotion(&md->bl);
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ }
+ else {
+ md->attackabletime = tick + 1;
+ md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0);
+ }
+ break;
+ case MS_DELAY:
+ md->timer=add_timer(gettick()+type,mob_timer,md->bl.id,0);
+ break;
+ case MS_DEAD:
+ skill_castcancel(&md->bl,0);
+// mobskill_deltimer(md);
+ md->state.skillstate=MSS_DEAD;
+ md->last_deadtime=gettick();
+ // Since it died, all aggressors' attack to this mob is stopped.
+ clif_foreachclient(mob_stopattacked,md->bl.id);
+ skill_unit_move(&md->bl,gettick(),4);
+ status_change_clear(&md->bl,2); // ƒXƒe[ƒ^ƒXˆÙí‚ð‰ðœ‚·‚é
+ skill_clear_unitgroup(&md->bl); // ‘S‚ẴXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹[ƒv‚ð휂·‚é
+ skill_cleartimerskill(&md->bl);
+ if(md->deletetimer!=-1)
+ delete_timer(md->deletetimer,mob_timer_delete);
+ md->deletetimer=-1;
+ md->hp = md->target_id = md->attacked_id = md->attacked_count = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * timer processing of mob (timer function)
+ * It branches to a walk and an attack.
+ *------------------------------------------
+ */
+static int mob_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if( (bl=map_id2bl(id)) == NULL ){ //UŒ‚‚µ‚Ä‚«‚½“G‚ª‚à‚¤‚¢‚È‚¢‚̂ͳí‚̂悤‚¾
+ return 1;
+ }
+
+ if(!bl || !bl->type || bl->type!=BL_MOB)
+ return 1;
+
+ nullpo_retr(1, md=(struct mob_data*)bl);
+
+ if(md->timer != tid){
+ if(battle_config.error_log)
+ ShowError("mob_timer %d != %d\n",md->timer,tid);
+ return 0;
+ }
+ md->timer=-1;
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ map_freeblock_lock();
+ switch(md->state.state){
+ case MS_WALK:
+ mob_walk(md,tick,data);
+ break;
+ case MS_ATTACK:
+ mob_attack(md,tick,data);
+ break;
+ case MS_DELAY:
+ mob_changestate(md,MS_IDLE,0);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("mob_timer : %d ?\n",md->state.state);
+ break;
+ }
+
+ if (md->timer == -1)
+ mob_changestate(md,MS_WALK,0);
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int mob_walktoxy_sub(struct mob_data *md)
+{
+ struct walkpath_data wpd;
+ int x,y;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+ nullpo_retr(0, md);
+
+ memset(&wpd, 0, sizeof(wpd));
+
+ if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,md->to_x,md->to_y,md->state.walk_easy))
+ return 1;
+ if (wpd.path[0] >= 8)
+ return 1;
+ x = md->bl.x+dirx[wpd.path[0]];
+ y = md->bl.y+diry[wpd.path[0]];
+ if (map_getcell(md->bl.m,x,y,CELL_CHKBASILICA) && !(status_get_mode(&md->bl)&MD_BOSS)) {
+ md->state.change_walk_target=0;
+ return 1;
+ }
+
+ memcpy(&md->walkpath,&wpd,sizeof(wpd));
+
+ md->state.change_walk_target=0;
+ mob_changestate(md,MS_WALK,0);
+ clif_movemob(md);
+
+ return 0;
+}
+
+/*==========================================
+ * mob move start
+ *------------------------------------------
+ */
+int mob_walktoxy(struct mob_data *md,int x,int y,int easy)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, md);
+
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD) //Just-in-case check to prevent dead mobs from moving. [Skotlex]
+ return 1;
+
+ if(md->state.state == MS_WALK && path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,x,y,easy) )
+ return 1;
+
+ md->state.walk_easy = easy;
+ md->to_x=x;
+ md->to_y=y;
+
+ if (md->sc_data[SC_CONFUSION].timer != -1) //Randomize target direction.
+ map_random_dir(&md->bl, &md->to_x, &md->to_y);
+
+ if(md->state.state == MS_WALK)
+ md->state.change_walk_target=1;
+ else
+ return mob_walktoxy_sub(md);
+
+ return 0;
+}
+
+/*==========================================
+ * mob spawn with delay (timer function)
+ *------------------------------------------
+ */
+static int mob_delayspawn(int tid, unsigned int tick, int m, int n)
+{
+ mob_spawn(m);
+ return 0;
+}
+
+/*==========================================
+ * spawn timing calculation
+ *------------------------------------------
+ */
+int mob_setdelayspawn(int id)
+{
+ unsigned int spawntime, spawntime1, spawntime2, spawntime3;
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if ((bl = map_id2bl(id)) == NULL || bl->type != BL_MOB)
+ return -1;
+ nullpo_retr(-1, md = (struct mob_data*)bl);
+
+ // Processing of MOB which is not revitalized
+ if (md->spawndelay1 == -1 && md->spawndelay2 == -1 && md->n == 0) {
+ if (md->lootitem) {
+ aFree(md->lootitem);
+ md->lootitem = NULL;
+ }
+ if (md->guardian_data)
+ {
+ if (md->guardian_data->number < MAX_GUARDIANS)
+ md->guardian_data->castle->guardian[md->guardian_data->number].id = 0;
+ aFree (md->guardian_data);
+ md->guardian_data = NULL;
+ }
+ map_deliddb(&md->bl);
+ map_delblock(bl); //In case it wasn't done before invoking the function.
+ map_freeblock(bl);
+ return 0;
+ }
+
+ spawntime1 = md->last_spawntime + md->spawndelay1;
+ spawntime2 = md->last_deadtime + md->spawndelay2;
+ spawntime3 = gettick() + 5000 + rand()%5000; //Lupus
+ // spawntime = max(spawntime1,spawntime2,spawntime3);
+ if (DIFF_TICK(spawntime1, spawntime2) > 0)
+ spawntime = spawntime1;
+ else
+ spawntime = spawntime2;
+ if (DIFF_TICK(spawntime3, spawntime) > 0)
+ spawntime = spawntime3;
+
+ add_timer(spawntime, mob_delayspawn, id, 0);
+ return 0;
+}
+
+static int mob_count_sub(struct block_list *bl,va_list ap)
+{
+ return 1;
+}
+
+/*==========================================
+ * Mob spawning. Initialization is also variously here.
+ *------------------------------------------
+ */
+int mob_spawn (int id)
+{
+ int x, y, i = 0;
+ unsigned int c, tick = gettick();
+ struct mob_data *md;
+ struct block_list *bl;
+
+ if ((bl = map_id2bl(id)) == NULL || bl->type != BL_MOB)
+ return -1;
+ nullpo_retr(-1, md = (struct mob_data*)bl);
+
+ md->last_spawntime = tick;
+ if (md->bl.prev != NULL)
+ map_delblock(&md->bl);
+ else {
+ if(md->class_ != md->base_class){ // ƒNƒ‰ƒXƒ`ƒFƒ“ƒW‚µ‚½Mob
+ md->class_ = md->base_class;
+ md->db = mob_db(md->base_class);
+ memcpy(md->name,md->db->jname,NAME_LENGTH);
+ md->speed=md->db->speed;
+ }
+ }
+ md->bl.m = md->m;
+ do {
+ if (md->x0 == 0 && md->y0 == 0) {
+ x = rand()%(map[md->bl.m].xs-2)+1;
+ y = rand()%(map[md->bl.m].ys-2)+1;
+ } else {
+ x = md->x0+rand()%(md->xs+1)-md->xs/2;
+ y = md->y0+rand()%(md->ys+1)-md->ys/2;
+ }
+ i++;
+ if (battle_config.no_spawn_on_player && i <= battle_config.no_spawn_on_player)
+ { //Avoid spawning on the view-range of players. [Skotlex]
+ if (map_foreachinarea(mob_count_sub, md->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ BL_PC) > 0)
+ continue;
+ }
+ } while(map_getcell(md->bl.m,x,y,CELL_CHKNOPASS) && i < 50);
+
+ if (i >= 50) {
+ if (md->spawndelay1 != -1 || md->spawndelay2 == -1)
+ // retry again later
+ add_timer(tick+5000,mob_delayspawn,id,0);
+ return 1;
+ }
+
+ md->to_x = md->bl.x = x;
+ md->to_y = md->bl.y = y;
+ md->dir = 0;
+ md->target_dir = 0;
+
+ memset(&md->state, 0, sizeof(md->state));
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ md->target_id = 0;
+ md->mode = 0;
+ md->move_fail_count = 0;
+
+ if (!md->speed)
+ md->speed = md->db->speed;
+ md->def_ele = md->db->element;
+
+ if (!md->level) // [Valaris]
+ md->level=md->db->lv;
+
+ md->master_id = 0;
+ md->master_dist = 0;
+
+ md->state.state = MS_IDLE;
+ md->state.skillstate = MSS_IDLE;
+ md->timer = -1;
+ md->last_thinktime = tick;
+ md->next_walktime = tick+rand()%50+5000;
+ md->attackabletime = tick;
+ md->canmove_tick = tick;
+ md->last_linktime = tick;
+
+ /* Guardians should be spawned using mob_spawn_guardian! [Skotlex]
+ * and the Emperium is spawned using mob_once_spawn.
+ md->guild_id = 0;
+ if (md->class_ >= 1285 && md->class_ <= 1288) {
+ struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name);
+ if(gc)
+ md->guild_id = gc->guild_id;
+ }
+ */
+
+ md->deletetimer = -1;
+
+ md->skilltimer = -1;
+ for (i = 0, c = tick-1000*3600*10; i < MAX_MOBSKILL; i++)
+ md->skilldelay[i] = c;
+ md->skillid = 0;
+ md->skilllv = 0;
+
+ memset(md->dmglog, 0, sizeof(md->dmglog));
+ md->tdmg = 0;
+ if (md->lootitem)
+ memset(md->lootitem, 0, sizeof(md->lootitem));
+ md->lootitem_count = 0;
+
+ for (i = 0; i < MAX_MOBSKILLTIMERSKILL; i++)
+ md->skilltimerskill[i].timer = -1;
+
+ for (i = 0; i < MAX_STATUSCHANGE; i++) {
+ md->sc_data[i].timer = -1;
+ md->sc_data[i].val1 = md->sc_data[i].val2 = md->sc_data[i].val3 = md->sc_data[i].val4 = 0;
+ }
+ md->sc_count = 0;
+ md->opt1 = md->opt2 = md->opt3 = md->option = 0;
+
+ if(md->db->option){ // Added for carts, falcons and pecos for cloned monsters. [Valaris]
+ if(md->db->option & 0x0008)
+ md->option |= 0x0008;
+ if(md->db->option & 0x0080)
+ md->option |= 0x0080;
+ if(md->db->option & 0x0100)
+ md->option |= 0x0100;
+ if(md->db->option & 0x0200)
+ md->option |= 0x0200;
+ if(md->db->option & 0x0400)
+ md->option |= 0x0400;
+ if(md->db->option & OPTION_FALCON)
+ md->option |= OPTION_FALCON;
+ if(md->db->option & OPTION_RIDING)
+ md->option |= OPTION_RIDING;
+ }
+
+ memset(md->skillunit, 0, sizeof(md->skillunit));
+ memset(md->skillunittick, 0, sizeof(md->skillunittick));
+
+ md->max_hp = md->db->max_hp;
+ if(md->special_state.size==1) // change for sized monsters [Valaris]
+ md->max_hp/=2;
+ else if(md->special_state.size==2)
+ md->max_hp*=2;
+ md->hp = md->max_hp;
+
+ map_addblock(&md->bl);
+ skill_unit_move(&md->bl,tick,1);
+
+ clif_spawnmob(md);
+
+ return 0;
+}
+
+/*==========================================
+ * The stop of MOB's attack
+ *------------------------------------------
+ */
+int mob_stopattack(struct mob_data *md)
+{
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ return 0;
+}
+/*==========================================
+ * The stop of MOB's walking
+ *------------------------------------------
+ */
+int mob_stop_walking(struct mob_data *md,int type)
+{
+ nullpo_retr(0, md);
+
+ if(md->state.state == MS_WALK || md->state.state == MS_IDLE) {
+ int dx=0,dy=0;
+
+ md->walkpath.path_len=0;
+ if(type&2 && mob_can_move(md)){
+ dx=md->to_x-md->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=md->to_y-md->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ }
+ md->to_x=md->bl.x+dx;
+ md->to_y=md->bl.y+dy;
+ if(dx!=0 || dy!=0){
+ mob_walktoxy_sub(md);
+ return 0;
+ }
+ mob_changestate(md,MS_IDLE,0);
+ }
+ if(type&0x01)
+ clif_fixmobpos(md);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Determines if the mob can change target. [Skotlex]
+ *------------------------------------------
+ */
+static int mob_can_changetarget(struct mob_data* md, struct block_list* target, int mode)
+{
+ switch (md->state.skillstate) {
+ case MSS_BERSERK: //Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking.
+ if (mode&(MD_ASSIST|MD_ANGRY) || (mode&(MD_AGGRESSIVE|MD_CASTSENSOR)) == (MD_AGGRESSIVE|MD_CASTSENSOR))
+ return (battle_config.mob_ai&4 || check_distance_bl(&md->bl, target, 3));
+ else
+ return 0;
+ case MSS_RUSH:
+ return (mode&MD_AGGRESSIVE);
+ case MSS_FOLLOW:
+ case MSS_ANGRY:
+ case MSS_IDLE:
+ case MSS_WALK:
+ case MSS_LOOT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+ * Determination for an attack of a monster
+ *------------------------------------------
+ */
+int mob_target(struct mob_data *md,struct block_list *bl,int dist)
+{
+ nullpo_retr(0, md);
+ nullpo_retr(0, bl);
+
+ // Nothing will be carried out if there is no mind of changing TAGE by TAGE ending.
+ if((md->target_id > 0 && md->state.targettype == ATTACKABLE) && !mob_can_changetarget(md, bl, status_get_mode(&md->bl)) &&
+ // if the monster was provoked ignore the above rule [celest]
+ !(md->state.provoke_flag && md->state.provoke_flag == bl->id))
+ return 0;
+
+ if(!status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ md->target_id = bl->id; // Since there was no disturbance, it locks on to target.
+ if(bl->type == BL_PC || bl->type == BL_MOB)
+ md->state.targettype = ATTACKABLE;
+ else
+ md->state.targettype = NONE_ATTACKABLE;
+ if (md->state.provoke_flag)
+ md->state.provoke_flag = 0;
+ md->min_chase=dist+md->db->range2;
+ if(md->min_chase>26)
+ md->min_chase=26;
+ return 0;
+}
+
+/*==========================================
+ * The ?? routine of an active monster
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list **target;
+ int dist;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=va_arg(ap,struct mob_data *);
+ target= va_arg(ap,struct block_list**);
+
+ //If can't seek yet, not an enemy, or you can't attack it, skip.
+ if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ case BL_MOB:
+ if((dist=distance_bl(&md->bl, bl)) < md->db->range2
+ && (md->db->range > 6 || mob_can_reach(md,bl,dist+1, MSS_FOLLOW))
+ && ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one.
+ ) {
+ (*target) = bl;
+ md->target_id=bl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase= md->db->range3;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * chase target-change routine.
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list **target;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ md=va_arg(ap,struct mob_data *);
+ target= va_arg(ap,struct block_list**);
+
+ //If can't seek yet, not an enemy, or you can't attack it, skip.
+ if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0))
+ return 0;
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ case BL_MOB:
+ if(check_distance_bl(&md->bl, bl, md->db->range) &&
+ battle_check_range (&md->bl, bl, md->db->range)
+ ) {
+ (*target) = bl;
+ md->target_id=bl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase= md->db->range3;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/*==========================================
+ * loot monster item search
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
+{
+ struct mob_data* md;
+ int dist,*itc;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data *));
+ nullpo_retr(0, itc=va_arg(ap,int *));
+
+ if(!md->lootitem || (battle_config.monster_loot_type == 1 && md->lootitem_count >= LOOTITEM_SIZE))
+ return 0;
+
+ if((dist=distance_bl(&md->bl, bl)) < md->db->range2 &&
+ mob_can_reach(md,bl,dist, MSS_LOOT) && rand()%1000<1000/(++(*itc)))
+ { // It is made a probability, such as within the limits PC.
+ md->target_id=bl->id;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->min_chase=md->db->range3;
+ md->next_walktime = gettick() + 500; //So that the mob may go after the item inmediately.
+ }
+ return 0;
+}
+
+/*==========================================
+ * Processing of slave monsters
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick)
+{
+ struct block_list *bl;
+ int old_dist;
+
+ nullpo_retr(0, md);
+
+ bl=map_id2bl(md->master_id);
+
+ if (!bl || status_isdead(bl)) { //Žå‚ªŽ€–S‚µ‚Ä‚¢‚é‚©Œ©‚‚©‚ç‚È‚¢
+ if(md->special_state.ai>0)
+ mob_timer_delete(0, 0, md->bl.id, 0);
+ else
+ mob_damage(NULL,md,md->hp,0);
+ return 0;
+ }
+
+ if(status_get_mode(&md->bl)&MD_CANMOVE)
+ { //If the mob can move, follow around. [Check by Skotlex]
+
+ if(bl->m != md->bl.m || md->master_dist > 30)
+ { // Since it is not in the same map (or is way to far), just warp it
+ mob_warp(md,bl->m,bl->x,bl->y,3);
+ return 0;
+ }
+
+ // Distance with between slave and master is measured.
+ old_dist=md->master_dist;
+ md->master_dist=distance_bl(&md->bl, bl);
+
+ // Since the master was in near immediately before, teleport is carried out and it pursues.
+ if(old_dist<10 && md->master_dist>18){
+ mob_warp(md,-1,bl->x,bl->y,3);
+ return 0;
+ }
+
+ // Although there is the master, since it is somewhat far, it approaches.
+ if((!md->target_id || md->state.targettype == NONE_ATTACKABLE) && mob_can_move(md) &&
+ md->master_dist<md->db->range3 && (md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_len==0)){
+ int i=0,dx,dy,ret;
+ if(md->master_dist>AREA_SIZE/2 && DIFF_TICK(md->next_walktime,tick)<3000) { //Allow it to cut down the walk time to chase back. [Skotlex]
+ do {
+ if(i<=5){
+ dx=bl->x - md->bl.x;
+ dy=bl->y - md->bl.y;
+ if(dx<0) dx+=(rand()%-dx)/2; //On the minimum, half the distance between slave/master. [Skotlex]
+ else if(dx>0) dx-=(rand()%dx)/2;
+ if(dy<0) dy+=(rand()%-dy)/2;
+ else if(dy>0) dy-=(rand()%dy)/2;
+ }else{
+ dx=bl->x - md->bl.x + rand()%11- 5;
+ dy=bl->y - md->bl.y + rand()%11- 5;
+ }
+
+ ret=mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0);
+ i++;
+ } while(ret && i<10);
+ md->next_walktime=tick+1000;
+ }
+ }
+ } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) {
+ //Delete the summoned mob if it's in a gvg ground and the master is elsewhere. [Skotlex]
+ if(md->special_state.ai>0)
+ mob_timer_delete(0, 0, md->bl.id, 0);
+ else
+ mob_damage(NULL,md,md->hp,0);
+ return 0;
+ }
+
+ //Avoid attempting to lock the master's target too often to avoid unnecessary overload. [Skotlex]
+ if (DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME && (!md->target_id || md->state.targettype == NONE_ATTACKABLE)) {
+ md->last_linktime = tick;
+ switch (bl->type) {
+ case BL_MOB:
+ {
+ struct mob_data *mmd= (struct mob_data*)bl;
+ struct block_list *tbl;
+ if(mmd->target_id>0 && mmd->state.targettype == ATTACKABLE &&
+ (tbl=map_id2bl(mmd->target_id)) && status_check_skilluse(&md->bl, tbl, 0, 0)
+ ) {
+ md->target_id=tbl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range2+distance_bl(&md->bl, tbl);
+ }
+ }
+ break;
+ case BL_PC:
+ {
+ struct map_session_data *msd = (struct map_session_data*)bl;
+ struct block_list *tbl = NULL;
+ if(msd->attacktarget)
+ tbl = map_id2bl(msd->attacktarget);
+ else if (msd->skilltarget)
+ tbl = map_id2bl(msd->skilltarget);
+ if(tbl && status_check_skilluse(&md->bl, tbl, 0, 0)) {
+ md->target_id=tbl->id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (status_get_mode(&md->bl)&MD_ANGRY)?1:0;
+ md->min_chase=md->db->range2+distance_bl(&md->bl, tbl);
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * A lock of target is stopped and mob moves to a standby state.
+ *------------------------------------------
+ */
+int mob_unlocktarget(struct mob_data *md,int tick)
+{
+ nullpo_retr(0, md);
+
+ md->target_id=0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->state.skillstate=MSS_IDLE;
+ md->next_walktime=tick+rand()%3000+3000;
+ return 0;
+}
+/*==========================================
+ * Random walk
+ *------------------------------------------
+ */
+int mob_randomwalk(struct mob_data *md,int tick)
+{
+ const int retrycount=20;
+ int speed;
+
+ nullpo_retr(0, md);
+
+ speed=status_get_speed(&md->bl);
+ if(DIFF_TICK(md->next_walktime,tick)<0){
+ int i,x,y,c,d=12-md->move_fail_count;
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ if(d<5) d=5;
+ for(i=0;i<retrycount;i++){ // Search of a movable place
+ int r=rand();
+ x=r%(d*2+1)-d;
+ y=r/(d*2+1)%(d*2+1)-d;
+ if (md->target_dir){
+ if (x<0) x=0-x;
+ if (y<0) y=0-y;
+ x *= mask[md->target_dir-1][0];
+ y *= mask[md->target_dir-1][1];
+ }
+ x+=md->bl.x;
+ y+=md->bl.y;
+
+ if((map_getcell(md->bl.m,x,y,CELL_CHKPASS)) && mob_walktoxy(md,x,y,1)==0){
+ md->move_fail_count=0;
+ break;
+ }
+ if(i+1>=retrycount){
+ md->move_fail_count++;
+ md->target_dir = 0;
+ if(md->move_fail_count>1000){
+ if(battle_config.error_log)
+ ShowWarning("MOB cant move. random spawn %d, class = %d\n",md->bl.id,md->class_);
+ md->move_fail_count=0;
+ mob_spawn(md->bl.id);
+ }
+ }
+ }
+ for(i=c=0;i<md->walkpath.path_len;i++){ // The next walk start time is calculated.
+ if(md->walkpath.path[i]&1)
+ c+=speed*14/10;
+ else
+ c+=speed;
+ }
+ md->next_walktime = tick+rand()%3000+3000+c;
+ md->state.skillstate=MSS_WALK;
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * AI of MOB whose is near a Player
+ *------------------------------------------
+ */
+static int mob_ai_sub_hard(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct block_list *tbl = NULL, *abl = NULL;
+ unsigned int tick;
+ int i, dx, dy, dist;
+ int attack_type = 0;
+ int mode;
+ int search_size = AREA_SIZE*2;
+ int blind_flag = 0;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ md = (struct mob_data*)bl;
+ tick = va_arg(ap, unsigned int);
+
+ if(md->bl.prev == NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ if (DIFF_TICK(tick, md->last_thinktime) < MIN_MOBTHINKTIME)
+ return 0;
+ md->last_thinktime = tick;
+
+ if (md->skilltimer != -1){ // Casting skill, or has died
+ if (DIFF_TICK (tick, md->next_walktime) > MIN_MOBTHINKTIME)
+ md->next_walktime = tick;
+ return 0;
+ }
+
+ // Abnormalities
+ if((md->opt1 > 0 && md->opt1 != OPT1_STONEWAIT) || md->state.state == MS_DELAY || md->sc_data[SC_BLADESTOP].timer != -1)
+ return 0;
+
+ if (md->sc_data && md->sc_data[SC_BLIND].timer != -1)
+ blind_flag = 1;
+
+ mode = status_get_mode(&md->bl);
+
+ if (md->target_id)
+ { //Check validity of current target. [Skotlex]
+ tbl = map_id2bl(md->target_id);
+ if (!tbl || tbl->m != md->bl.m || !status_check_skilluse(&md->bl, tbl, 0, 0))
+ { //Unlock current target.
+ if (md->state.state == MS_WALK && (battle_config.mob_ai&8 || !tbl)) //Inmediately stop chasing.
+ mob_stop_walking(md, 2);
+ mob_unlocktarget(md, tick-(battle_config.mob_ai&8?3000:0)); //Imediately do random walk.
+ tbl = NULL;
+ }
+ }
+
+ // Check for target change.
+ if (md->attacked_id && mode&MD_CANATTACK && md->attacked_id != md->target_id)
+ {
+ abl = map_id2bl(md->attacked_id);
+ if (abl && (!tbl || mob_can_changetarget(md, abl, mode))) {
+ if (md->bl.m != abl->m || abl->prev == NULL ||
+ (dist = distance_bl(&md->bl, abl)) >= 32 ||
+ battle_check_target(bl, abl, BCT_ENEMY) <= 0 ||
+ (battle_config.mob_ai&2 && !status_check_skilluse(bl, abl, 0, 0)) ||
+ !mob_can_reach(md, abl, dist+2, MSS_RUSH)) //Some more cells of grace...
+ { //Can't attack back
+ if (md->attacked_count++ > 3) {
+ if (mobskill_use(md, tick, MSC_RUDEATTACKED) == 0 &&
+ mode&MD_CANMOVE && mob_can_move(md))
+ {
+ int dist = rand() % 10 + 1;//Œã‘Þ‚·‚é‹——£
+ int dir = map_calc_dir(abl, bl->x, bl->y);
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ mob_walktoxy(md, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0);
+ md->next_walktime = tick + 500;
+ }
+ }
+ } else if (!(battle_config.mob_ai&2) && !status_check_skilluse(bl, abl, 0, 0)) {
+ //Can't attack back, but didn't invoke a rude attacked skill...
+ md->attacked_id = 0; //Simply unlock, shouldn't attempt to run away when in dumb_ai mode.
+ } else if (blind_flag && dist > 2 && DIFF_TICK(tick,md->next_walktime) < 0) { //Blinded, but can reach
+ if (!md->target_id)
+ { //Attempt to follow new target
+ if (mode&MD_CANMOVE && mob_can_move(md)) { // why is it moving to the target when the mob can't see the player? o.o
+ dx = abl->x - md->bl.x;
+ dy = abl->y - md->bl.y;
+ md->next_walktime = tick + 1000;
+ mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0);
+ }
+ }
+ } else { //Attackable
+ if (!tbl || dist < md->db->range || !check_distance_bl(&md->bl, tbl, dist)
+ || battle_gettarget(tbl) != md->bl.id)
+ { //Change if the new target is closer than the actual one
+ //or if the previous target is not attacking the mob. [Skotlex]
+ md->target_id = md->attacked_id; // set target
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = 0; //Retaliating.
+ attack_type = 1;
+ md->attacked_count = 0;
+ md->min_chase = dist + md->db->range2;
+ if (md->min_chase > 26)
+ md->min_chase = 26;
+ tbl = abl; //Set the new target
+ }
+ }
+ }
+ }
+ if (md->attacked_id) {
+ if (md->state.aggressive && md->attacked_id == md->target_id)
+ md->state.aggressive = 0; //No longer aggressive, change to retaliate AI.
+ md->attacked_id = 0; //Clear it since it's been checked for already.
+ }
+
+ // Processing of slave monster, is it needed when there's a target to deal with?
+ if (md->master_id > 0 && !tbl)
+ mob_ai_sub_hard_slavemob(md, tick);
+
+ // Scan area for targets
+ if ((mode&MD_AGGRESSIVE && battle_config.monster_active_enable && !tbl) ||
+ (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW)
+ ) {
+ search_size = (blind_flag) ? 3 : md->db->range2;
+ map_foreachinarea (mob_ai_sub_hard_activesearch, md->bl.m,
+ md->bl.x-search_size,md->bl.y-search_size,
+ md->bl.x+search_size,md->bl.y+search_size,
+ md->special_state.ai?BL_CHAR:BL_PC,
+ md, &tbl);
+ } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) {
+ search_size = (blind_flag && md->db->range>3) ? 3 : md->db->range;
+ map_foreachinarea (mob_ai_sub_hard_changechase, md->bl.m,
+ md->bl.x-search_size,md->bl.y-search_size,
+ md->bl.x+search_size,md->bl.y+search_size,
+ md->special_state.ai?BL_CHAR:BL_PC,
+ md, &tbl);
+ }
+
+ // Scan area for items to loot, avoid trying to loot of the mob is full and can't consume the items.
+ if (!md->target_id && mode&MD_LOOTER && md->lootitem &&
+ (md->lootitem_count < LOOTITEM_SIZE || battle_config.monster_loot_type != 1))
+ {
+ i = 0;
+ search_size = (blind_flag) ? 3 : md->db->range2;
+ map_foreachinarea (mob_ai_sub_hard_lootsearch, md->bl.m,
+ md->bl.x-search_size, md->bl.y-search_size,
+ md->bl.x+search_size, md->bl.y+search_size,
+ BL_ITEM, md, &i);
+ }
+
+ if (tbl)
+ { //Target exists, attack or loot as applicable.
+ if (tbl->type != BL_ITEM)
+ { //Attempt to attack.
+ //At this point we know the target is attackable, we just gotta check if the range matches.
+ if (blind_flag && DIFF_TICK(tick,md->next_walktime) < 0 && !check_distance_bl(&md->bl, tbl, 1))
+ { //Run towards the enemy when out of range?
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ if (!(mode & MD_CANMOVE) || !mob_can_move(md))
+ return 0;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ md->next_walktime = tick + 1000;
+ mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0);
+ return 0;
+ }
+ if (!battle_check_range (&md->bl, tbl, md->db->range))
+ { //Out of range...
+ if (!(mode & MD_CANMOVE))
+ { //Can't chase.
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ if (!mob_can_move(md)) //Wait until you can move?
+ return 0;
+ //Follow up
+ md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH;
+ mobskill_use (md, tick, -1);
+ if (md->timer != -1 && md->state.state != MS_ATTACK &&
+ (DIFF_TICK (md->next_walktime, tick) < 0 ||
+ !(battle_config.mob_ai&1) ||
+ check_distance_blxy(tbl, md->to_x, md->to_y, md->db->range)) //Current target tile is still within attack range.
+ ) {
+ return 0; //No need to follow, already doing it?
+ }
+ search_size = (blind_flag) ? 3 : ((md->min_chase > md->db->range2) ? md->min_chase : md->db->range2);
+ if (!mob_can_reach(md, tbl, search_size, MSS_RUSH))
+ { //Can't reach
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ //Target reachable. Locate suitable spot to move to.
+ i = 0;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ if (dx < 0) dx++;
+ else if (dx > 0) dx--;
+ if (dy < 0) dy++;
+ else if (dy > 0) dy--;
+ while (i < 5 && mob_walktoxy(md, md->bl.x + dx, md->bl.y + dy, 0))
+ { //Attempt to chase to nearby blocks
+ dx = tbl->x - md->bl.x + rand()%3 - 1;
+ dy = tbl->y - md->bl.y + rand()%3 - 1;
+ i++;
+ }
+ if (i==5)
+ { //Failed? Try going away from the target before retrying.
+ if (dx < 0) dx = 2;
+ else if (dx > 0) dx = -2;
+ if (dy < 0) dy = 2;
+ else if (dy > 0) dy = -2;
+ }
+ md->next_walktime = tick + 500;
+ mob_walktoxy (md, md->bl.x+dx, md->bl.y+dy, 0);
+ return 0;
+ }
+ //Target within range, engage
+ md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ if (md->state.state == MS_WALK)
+ mob_stop_walking (md, 1);
+ else if (md->state.state == MS_ATTACK)
+ return 0; //Ah, we are already attacking.
+ mob_changestate(md, MS_ATTACK, attack_type);
+ return 0;
+ } else { //Target is BL_ITEM, attempt loot.
+ struct flooritem_data *fitem;
+
+ if ((dist = distance_bl(&md->bl, tbl)) >= md->min_chase || (blind_flag && dist >= 4) || md->lootitem == NULL)
+ { //Can't loot...
+ mob_unlocktarget (md, tick);
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,0);
+ return 0;
+ }
+ if (dist)
+ { //Still not within loot range.
+ if (!(mode & MD_CANMOVE))
+ { //A looter that can't move? Real smart.
+ mob_unlocktarget(md,tick);
+ return 0;
+ }
+ if (!mob_can_move(md)) // “®‚¯‚È‚¢ó‘Ô‚É‚ ‚é
+ return 0;
+ md->state.skillstate = MSS_LOOT; // ƒ‹[ƒgŽžƒXƒLƒ‹Žg—p
+ mobskill_use(md, tick, -1);
+ if (md->timer != -1 && md->state.state != MS_ATTACK &&
+ (DIFF_TICK(md->next_walktime,tick) < 0 ||
+ check_distance_blxy(tbl, md->to_x, md->to_y, 0)))
+ { //Already on the way to looting.
+ return 0;
+ }
+ md->next_walktime = tick + 500;
+ dx = tbl->x - md->bl.x;
+ dy = tbl->y - md->bl.y;
+ if (mob_walktoxy(md, md->bl.x+dx, md->bl.y+dy, 0))
+ mob_unlocktarget(md, tick); //Can't loot...
+ return 0;
+ }
+ //Within looting range.
+ if (md->state.state == MS_ATTACK)
+ return 0; //Busy attacking?
+ if (md->state.state == MS_WALK)
+ mob_stop_walking(md,0);
+
+ fitem = (struct flooritem_data *)tbl;
+ if (md->lootitem_count < LOOTITEM_SIZE) {
+ memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0]));
+ if(log_config.pick > 0) //Logs items, taken by (L)ooter Mobs [Lupus]
+ log_pick((struct map_session_data*)md, "L", md->class_, md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]);
+ } else if (battle_config.monster_loot_type == 1) { //Can't loot, stuffed!
+ mob_unlocktarget(md,tick);
+ return 0;
+ } else { //Destroy first looted item...
+ if (md->lootitem[0].card[0] == (short)0xff00)
+ intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) );
+ for (i = 0; i < LOOTITEM_SIZE - 1; i++)
+ memcpy (&md->lootitem[i], &md->lootitem[i+1], sizeof(md->lootitem[0]));
+ memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0]));
+ }
+ //Clear item.
+ map_clearflooritem (tbl->id);
+ mob_unlocktarget (md,tick);
+ return 0;
+ }
+ }
+
+ // When there's no target, it is idling.
+ if (mobskill_use(md, tick, -1))
+ return 0;
+
+ // Nothing else to do... except random walking.
+ if (mode&MD_CANMOVE && mob_can_move(md))
+ {
+ if (DIFF_TICK(md->next_walktime, tick) > 7000 &&
+ (md->walkpath.path_len == 0 || md->walkpath.path_pos >= md->walkpath.path_len))
+ md->next_walktime = tick + 3000 * rand() % 2000;
+ // Random movement
+ if (mob_randomwalk(md,tick))
+ return 0;
+ }
+
+ // Since he has finished walking, it stands by.
+ if (md->walkpath.path_len == 0 || md->walkpath.path_pos >= md->walkpath.path_len)
+ md->state.skillstate = MSS_IDLE;
+ return 0;
+}
+
+/*==========================================
+ * Serious processing for mob in PC field of view (foreachclient)
+ *------------------------------------------
+ */
+static int mob_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
+{
+ unsigned int tick;
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ tick=va_arg(ap,unsigned int);
+ map_foreachinarea(mob_ai_sub_hard,sd->bl.m,
+ sd->bl.x-AREA_SIZE*2,sd->bl.y-AREA_SIZE*2,
+ sd->bl.x+AREA_SIZE*2,sd->bl.y+AREA_SIZE*2,
+ BL_MOB,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * Serious processing for mob in PC field of view (interval timer function)
+ *------------------------------------------
+ */
+static int mob_ai_hard(int tid,unsigned int tick,int id,int data)
+{
+ clif_foreachclient(mob_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * Negligent mode MOB AI (PC is not in near)
+ *------------------------------------------
+ */
+static int mob_ai_sub_lazy(DBKey key,void * data,va_list app)
+{
+ struct mob_data *md = (struct mob_data *)data;
+ va_list ap;
+ unsigned int tick;
+ int mode;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, app);
+
+ if(md->bl.type!=BL_MOB)
+ return 0;
+
+ ap = va_arg(app, va_list);
+ tick=va_arg(ap,unsigned int);
+
+ if(DIFF_TICK(tick,md->last_thinktime)<MIN_MOBTHINKTIME*10)
+ return 0;
+ md->last_thinktime=tick;
+
+ if (md->bl.prev==NULL || md->state.state == MS_DEAD)
+ return 1;
+
+ if(md->skilltimer!=-1){
+ if(DIFF_TICK(tick,md->next_walktime)>MIN_MOBTHINKTIME*10)
+ md->next_walktime=tick;
+ return 0;
+ }
+
+ // Žæ‚芪‚«ƒ‚ƒ“ƒXƒ^[‚̈—iŒÄ‚Ñ–ß‚µ‚³‚ꂽŽžj
+ if (md->master_id > 0) {
+ mob_ai_sub_hard_slavemob (md,tick);
+ return 0;
+ }
+
+ mode = status_get_mode(&md->bl);
+ if(DIFF_TICK(md->next_walktime,tick)<0 &&
+ (mode&MD_CANMOVE) && mob_can_move(md) ){
+
+ if( map[md->bl.m].users>0 ){
+ // Since PC is in the same map, somewhat better negligent processing is carried out.
+
+ // It sometimes moves.
+ if(rand()%1000<MOB_LAZYMOVEPERC)
+ mob_randomwalk(md,tick);
+ // MOB which is not not the summons MOB but BOSS, either sometimes reboils.
+ else if( rand()%1000<MOB_LAZYWARPPERC && md->x0<=0 && md->master_id!=0 &&
+ !(mode&MD_BOSS))
+ mob_spawn(md->bl.id);
+ else if(rand()%1000<MOB_LAZYSKILLPERC) //Chance to do a mob's idle skill.
+ mobskill_use(md, tick, -1);
+ }else{
+ // Since PC is not even in the same map, suitable processing is carried out even if it takes.
+
+ // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping
+ if( rand()%1000<MOB_LAZYWARPPERC && md->x0<=0 && md->master_id!=0 &&
+ !(mode&MD_BOSS))
+ mob_warp(md,-1,-1,-1,-1);
+ }
+
+ md->next_walktime = tick+rand()%10000+5000;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Negligent processing for mob outside PC field of view (interval timer function)
+ *------------------------------------------
+ */
+static int mob_ai_lazy(int tid,unsigned int tick,int id,int data)
+{
+ map_foreachiddb(mob_ai_sub_lazy,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * The structure object for item drop with delay
+ * Since it is only two being able to pass [ int ] a timer function
+ * Data is put in and passed to this structure object.
+ *------------------------------------------
+ */
+struct delay_item_drop {
+ int m,x,y;
+ struct item item_data;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+/*==========================================
+ * Initializes the delay drop structure for mob-dropped items.
+ *------------------------------------------
+ */
+static struct delay_item_drop* mob_setdropitem(int nameid, int qty, int m, int x, int y,
+ struct map_session_data* first_sd, struct map_session_data* second_sd, struct map_session_data* third_sd)
+{
+ struct delay_item_drop *drop = aCalloc(1, sizeof (struct delay_item_drop));
+ drop->item_data.nameid = nameid;
+ drop->item_data.amount = qty;
+ drop->item_data.identify = !itemdb_isequip3(nameid);
+ drop->m = m;
+ drop->x = x;
+ drop->y = y;
+ drop->first_sd = first_sd;
+ drop->second_sd = second_sd;
+ drop->third_sd = third_sd;
+ return drop;
+};
+
+/*==========================================
+ * Initializes the delay drop structure for mob-looted items.
+ *------------------------------------------
+ */
+static struct delay_item_drop* mob_setlootitem(struct item* item, int m, int x, int y,
+ struct map_session_data* first_sd, struct map_session_data* second_sd, struct map_session_data* third_sd)
+{
+ struct delay_item_drop *drop = aCalloc(1, sizeof (struct delay_item_drop));
+ memcpy(&drop->item_data, item, sizeof(struct item));
+ drop->m = m;
+ drop->x = x;
+ drop->y = y;
+ drop->first_sd = first_sd;
+ drop->second_sd = second_sd;
+ drop->third_sd = third_sd;
+ return drop;
+};
+
+/*==========================================
+ * item drop with delay (timer function)
+ *------------------------------------------
+ */
+static int mob_delay_item_drop(int tid,unsigned int tick,int id,int data)
+{
+ struct delay_item_drop *ditem;
+ ditem=(struct delay_item_drop *)id;
+
+ map_addflooritem(&ditem->item_data,1,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+ aFree(ditem);
+ return 0;
+}
+
+/*==========================================
+ * Sets a timer to drop an item on the ground
+ * Also performs logging and autoloot if enabled.
+ * rate is the drop-rate of the item, required for autoloot.
+ *------------------------------------------
+ * by [Skotlex]
+ */
+static void mob_item_drop(struct mob_data *md, unsigned int tick, struct delay_item_drop * ditem, int loot, int drop_rate)
+{
+ if(log_config.pick > 0)
+ { //Logs items, dropped by mobs [Lupus]
+ if (loot)
+ log_pick((struct map_session_data*)md, "L", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, &ditem->item_data);
+ else
+ log_pick((struct map_session_data*)md, "M", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, NULL);
+ }
+
+ if (ditem->first_sd && ditem->first_sd->state.autoloot &&
+ (drop_rate <= ditem->first_sd->state.autoloot ||
+ ditem->first_sd->state.autoloot >= 10000) //Fetch 100% drops
+ && pc_additem(ditem->first_sd,&ditem->item_data,ditem->item_data.amount) == 0)
+ { //Autolooted.
+ if(log_config.pick > 0)
+ log_pick(ditem->first_sd, "P", 0, ditem->item_data.nameid, ditem->item_data.amount, &ditem->item_data);
+ aFree(ditem);
+ } else
+ add_timer(tick, mob_delay_item_drop, (int)ditem, 0);
+}
+
+/*==========================================
+ * mob data is erased.
+ *------------------------------------------
+ */
+void mob_unload(struct mob_data *md)
+{
+ nullpo_retv(md);
+ mob_remove_map(md, 0);
+ map_deliddb(&md->bl);
+ map_freeblock((struct block_list*)md);
+}
+
+int mob_remove_map(struct mob_data *md, int type)
+{
+ nullpo_retr(1, md);
+
+ if(md->bl.prev == NULL)
+ return 1;
+ mob_changestate(md,MS_DEAD,0);
+ clif_clearchar_area(&md->bl,type);
+ map_delblock(&md->bl);
+ if (md->lootitem){
+ aFree(md->lootitem);
+ md->lootitem = NULL;
+ }
+ if (md->guardian_data)
+ {
+ aFree(md->guardian_data);
+ md->guardian_data = NULL;
+ }
+ return 0;
+}
+int mob_delete(struct mob_data *md)
+{
+ nullpo_retr(1, md);
+
+ mob_remove_map(md, 1);
+ if(pcdb_checkid(mob_get_viewclass(md->class_))) //Player mobs are not removed automatically by the client.
+ clif_clearchar_delay(gettick()+3000,&md->bl,0);
+ if(mob_is_clone(md->class_))
+ mob_clone_delete(md->class_);
+ mob_deleteslave(md);
+ mob_setdelayspawn(md->bl.id);
+ return 0;
+}
+int mob_timer_delete(int tid, unsigned int tick, int id, int data)
+{
+ struct mob_data *md=(struct mob_data *)map_id2bl(id);
+ nullpo_retr(0, md);
+
+//for Alchemist CANNIBALIZE [Lupus]
+ mob_remove_map(md, 3);
+ mob_setdelayspawn(md->bl.id);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int mob_deleteslave_sub(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ int id;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md = (struct mob_data *)bl);
+
+ id=va_arg(ap,int);
+ if(md->master_id > 0 && md->master_id == id )
+ mob_damage(NULL,md,md->hp,1);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int mob_deleteslave(struct mob_data *md)
+{
+ nullpo_retr(0, md);
+
+ map_foreachinmap(mob_deleteslave_sub, md->bl.m, BL_MOB,md->bl.id);
+ return 0;
+}
+
+/*==========================================
+ * It is the damage of sd to damage to md.
+ *------------------------------------------
+ */
+int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type)
+{
+ int i,count,minpos,mindmg;
+ struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE];
+ struct {
+ struct party *p;
+ int id,base_exp,job_exp,zeny;
+ } pt[DAMAGELOG_SIZE];
+ int pnum=0;
+ int mvp_damage,max_hp;
+ unsigned int tick = gettick();
+ struct map_session_data *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL;
+ struct block_list *master = NULL;
+ double temp;
+ struct item item;
+ int ret, mode;
+ int drop_rate;
+ int base_drop_delay;
+ int race;
+
+ nullpo_retr(0, md); //src‚ÍNULL‚ŌĂ΂ê‚éê‡‚à‚ ‚é‚Ì‚ÅA‘¼‚Ń`ƒFƒbƒN
+
+ max_hp = status_get_max_hp(&md->bl);
+ race = status_get_race(&md->bl);
+
+ if(src && src->type == BL_PC) {
+ sd = (struct map_session_data *)src;
+ mvp_sd = sd;
+ }
+
+ if(md->bl.prev==NULL){
+ if(battle_config.error_log==1)
+ ShowError("mob_damage : BlockError!!\n");
+ return 0;
+ }
+
+ if(md->state.state==MS_DEAD || md->hp<=0) {
+ if(md->bl.prev != NULL) {
+ mob_changestate(md,MS_DEAD,0);
+ mobskill_use(md,tick,-1); // It is skill at the time of death.
+ clif_clearchar_area(&md->bl,1);
+ map_delblock(&md->bl);
+ mob_setdelayspawn(md->bl.id);
+ }
+ return 0;
+ }
+
+/* The stop walking code is triggered in battle_walkdelay which is invoked from clif_damage after a timer.
+ * So the mob should stop walking in sync with the time the "attack" hits the mob. If this is bugged then the
+ * fault must be looked at in battle_walkdelay, not here. [Skotlex]
+ if(md->sc_data[SC_ENDURE].timer == -1) // Stop the walking [Lance]
+ mob_stop_walking(md,1);
+*/
+ if(damage > max_hp>>2)
+ skill_stop_dancing(&md->bl);
+
+ if(md->hp > max_hp)
+ md->hp = max_hp;
+
+ // The amount of overkill rounds to hp.
+ if(damage>md->hp)
+ damage=md->hp;
+ md->hp-=damage;
+ md->tdmg+=damage; //Store total damage...
+
+ if(!(type&2)) {
+ int id = 0;
+ if (src) {
+ switch (src->type) {
+ case BL_PC:
+ id = sd->status.char_id;
+ if(md->attacked_id <= 0)
+ md->attacked_id = sd->bl.id;
+ break;
+ case BL_PET:
+ {
+ struct pet_data *pd = (struct pet_data*)src;
+ if (battle_config.pet_attack_exp_to_master) {
+ id = pd->msd->status.char_id;
+ damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly.
+ }
+ //Let mobs retaliate against the pet's master [Skotlex]
+ if(md->attacked_id <= 0)
+ md->attacked_id = pd->msd->bl.id;
+ break;
+ }
+ case BL_MOB:
+ {
+ struct mob_data* md2 = (struct mob_data*)src;
+ if(md2->special_state.ai && md2->master_id) {
+ struct map_session_data* msd = map_id2sd(md2->master_id);
+ if (msd) id = msd->status.char_id;
+ }
+ if(md->attacked_id <= 0)
+ { //Let players decide whether to retaliate versus the master or the mob. [Skotlex]
+ if (md2->master_id && battle_config.retaliate_to_master)
+ md->attacked_id = md2->master_id;
+ else
+ md->attacked_id = md2->bl.id;
+ }
+ break;
+ }
+ }
+ }
+ //Log damage...
+ if (id && damage > 0) {
+ for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=0x7fffffff;i<DAMAGELOG_SIZE;i++){
+ if(md->dmglog[i].id==id)
+ break;
+ if(md->dmglog[i].id==0) { //Store data in first empty slot.
+ md->dmglog[i].id = id;
+ break;
+ }
+ if(md->dmglog[i].dmg<mindmg){
+ minpos=i;
+ mindmg=md->dmglog[i].dmg;
+ }
+ }
+ if(i<DAMAGELOG_SIZE)
+ md->dmglog[i].dmg+=damage;
+ else {
+ md->dmglog[minpos].id=id;
+ md->dmglog[minpos].dmg=damage;
+ }
+ }
+ }
+
+ if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex])
+ if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0)
+ {
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ } // end addition
+
+ if(md->option&OPTION_HIDE)
+ status_change_end(&md->bl, SC_HIDING, -1);
+ if(md->option&OPTION_CLOAK)
+ status_change_end(&md->bl, SC_CLOAKING, -1);
+
+ if(md->special_state.ai == 2 && //ƒXƒtƒBƒA[ƒ}ƒCƒ“
+ src && md->master_id == src->id)
+ {
+ md->state.alchemist = 1;
+ md->target_dir = map_calc_dir(src,md->bl.x,md->bl.y)+1;
+ mobskill_use(md, tick, MSC_ALCHEMIST);
+ }
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, &md->bl);
+
+ if(md->hp > 0)
+ return damage;
+
+ //Not the most correct way ever, but this is totally custom anyway.... [Skotlex]
+ if (md->sc_data[SC_KAIZEL].timer != -1) {
+ max_hp = status_get_max_hp(&md->bl);
+ mob_heal(md, 10*md->sc_data[SC_KAIZEL].val1*max_hp/100);
+ clif_resurrection(&md->bl, 1);
+ status_change_start(&md->bl,SkillStatusChangeTable[SL_KAIZEL],10,0,0,0,skill_get_time2(SL_KAIZEL, md->sc_data[SC_KAIZEL].val1),0);
+ status_change_end(&md->bl,SC_KAIZEL,-1);
+ return damage;
+ }
+
+ // ----- ‚±‚±‚©‚玀–Sˆ— -----
+
+ mode = status_get_mode(&md->bl); //Mode will be used for various checks regarding exp/drops.
+
+ //changestate will clear all status effects, so we need to know if RICHMANKIM is in effect before then. [Skotlex]
+ //I just recycled ret because it isn't used until much later and I didn't want to add a new variable for it.
+ ret = (md->sc_data[SC_RICHMANKIM].timer != -1)?(25 + 11*md->sc_data[SC_RICHMANKIM].val1):0;
+
+ map_freeblock_lock();
+ mob_changestate(md,MS_DEAD,0);
+ mobskill_use(md,tick,-1); // Ž€–SŽžƒXƒLƒ‹
+
+ memset(tmpsd,0,sizeof(tmpsd));
+ memset(pt,0,sizeof(pt));
+
+ max_hp = status_get_max_hp(&md->bl);
+
+ if(src && src->type == BL_MOB)
+ mob_unlocktarget((struct mob_data *)src,tick);
+
+ base_drop_delay = battle_config.delay_battle_damage?0:500;
+ if(sd) {
+ int sp = 0, hp = 0;
+ if (sd->state.attack_type == BF_MAGIC)
+ base_drop_delay = 500;
+ if (sd->state.attack_type == BF_MAGIC && sd->skilltarget == md->bl.id && (i=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ { //Soul Drain should only work on targetted spells [Skotlex]
+ if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
+ clif_skill_nodamage(src,&md->bl,HW_SOULDRAIN,i,1);
+ sp += (status_get_lv(&md->bl))*(65+15*i)/100;
+ }
+ sp += sd->sp_gain_value;
+ sp += sd->sp_gain_race[race];
+ sp += sd->sp_gain_race[mode&MD_BOSS?10:11];
+ hp += sd->hp_gain_value;
+ if (sp > 0) {
+ if(sd->status.sp + sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp += sp;
+ if (sp > 0 && battle_config.show_hp_sp_gain)
+ clif_heal(sd->fd,SP_SP,sp);
+ }
+ if (hp > 0) {
+ if(sd->status.hp + hp > sd->status.max_hp)
+ hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp += hp;
+ if (hp > 0 && battle_config.show_hp_sp_gain)
+ clif_heal(sd->fd,SP_HP,hp);
+ }
+ if (sd->mission_mobid == md->class_) { //TK_MISSION [Skotlex]
+ //Recycling hp for new random target id...
+ if (++sd->mission_count >= 100 && (hp = mob_get_random_id(0, 0, sd->status.base_level)))
+ {
+ pc_addfame(sd, 1);
+ sd->mission_mobid = hp;
+ pc_setglobalreg(sd,"TK_MISSION_ID", hp);
+ sd->mission_count = 0;
+ clif_mission_mob(sd, hp, 0);
+ }
+ pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count);
+ }
+ }
+
+ // mapŠO‚ÉÁ‚¦‚½l‚ÍŒvŽZ‚©‚眂­‚Ì‚Å
+ // overkill•ª‚Í–³‚¢‚¯‚Çsum‚Ímax_hp‚Ƃ͈Ⴄ
+
+ for(i=0,count=0,mvp_damage=0;i<DAMAGELOG_SIZE;i++){
+ if(md->dmglog[i].id==0)
+ break; //Reached end of log.
+ count++; //Count an attacker even if he is dead/logged-out.
+ tmpsd[i] = map_charid2sd(md->dmglog[i].id);
+ if(tmpsd[i] == NULL)
+ continue;
+ if(tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i]))
+ continue;
+
+ if(mvp_damage<md->dmglog[i].dmg){
+ third_sd = second_sd;
+ second_sd = mvp_sd;
+ mvp_sd=tmpsd[i];
+ mvp_damage=md->dmglog[i].dmg;
+ }
+ }
+
+ // [MouseJstr]
+ if((map[md->bl.m].flag.pvp == 0) || (battle_config.pvp_exp == 1)) {
+
+ // ŒoŒ±’l‚Ì•ª”z
+ for(i=0;i<DAMAGELOG_SIZE;i++){
+ int pid,flag=1,zeny=0;
+ unsigned long base_exp,job_exp;
+ double per;
+ struct party *p;
+ if(tmpsd[i]==NULL || tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i]))
+ continue;
+
+ if (battle_config.exp_calc_type) // eAthena's exp formula based on max hp.
+ per = (double)md->dmglog[i].dmg/(double)max_hp;
+ else //jAthena's exp formula based on total damage.
+ per = (double)md->dmglog[i].dmg/(double)md->tdmg;
+
+ if (count>1)
+ per *= (9.+(double)((count > 6)? 6:count))/10.; //attackers count bonus.
+
+ base_exp = (unsigned long)md->db->base_exp;
+ job_exp = (unsigned long)md->db->job_exp;
+
+ if (ret)
+ per += per*ret/100.; //SC_RICHMANKIM bonus. [Skotlex]
+
+ if(sd) {
+ if (sd->expaddrace[race])
+ per += per*sd->expaddrace[race]/100.;
+ per += per*sd->expaddrace[mode&MD_BOSS?10:11]/100.;
+ }
+ if (battle_config.pk_mode && (md->db->lv - tmpsd[i]->status.base_level >= 20))
+ per *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris]
+
+ //SG additional exp from Blessings [Komurka] - probably can be optimalized ^^;;
+ if(md->class_ == tmpsd[i]->hate_mob[0] && (battle_config.allow_skill_without_day || is_day_of_sun()))
+ per += per*10*pc_checkskill(tmpsd[i],SG_SUN_BLESS)/100.;
+ else if(md->class_ == tmpsd[i]->hate_mob[1] && (battle_config.allow_skill_without_day || is_day_of_moon()))
+ per += per*10*pc_checkskill(tmpsd[i],SG_MOON_BLESS)/100.;
+ else if(md->class_ == tmpsd[i]->hate_mob[2] && (battle_config.allow_skill_without_day || is_day_of_star()))
+ per += per*20*pc_checkskill(tmpsd[i],SG_STAR_BLESS)/100.;
+
+ if(md->special_state.size==1) // change experience for different sized monsters [Valaris]
+ per /=2.;
+ else if(md->special_state.size==2)
+ per *=2.;
+ if(md->master_id) {
+ if(((master = map_id2bl(md->master_id)) && status_get_mode(master)&MD_BOSS) || // check if its master is a boss (MVP's and minibosses)
+ md->special_state.ai) { // for summoned creatures [Valaris]
+ per = 0;
+ }
+ } else {
+ if(battle_config.zeny_from_mobs) {
+ if(md->level > 0) zeny=(int) ((md->level+rand()%md->level)*per); // zeny calculation moblv + random moblv [Valaris]
+ if(md->db->mexp > 0)
+ zeny*=rand()%250;
+ if(md->special_state.size==1 && zeny >=2) // change zeny for different sized monsters [Valaris]
+ zeny/=2;
+ else if(md->special_state.size==2 && zeny >1)
+ zeny*=2;
+ }
+ if(battle_config.mobs_level_up && md->level > md->db->lv) { // [Valaris]
+ base_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->base_exp))*(battle_config.mobs_level_up_exp_rate/100)));
+ job_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->job_exp))*(battle_config.mobs_level_up_exp_rate/100)));
+ }
+ }
+
+ if (per > 4) per = 4; //Limit gained exp to quadro the mob's exp. [3->4 Komurka]
+ base_exp = (unsigned long)(base_exp*per);
+ job_exp = (unsigned long)(job_exp*per);
+
+ if (base_exp > 0x7fffffff) base_exp = 0x7fffffff;
+ else if (base_exp < 1) base_exp = 1;
+
+ if (job_exp > 0x7fffffff) job_exp = 0x7fffffff;
+ else if (job_exp < 1) job_exp = 1;
+
+ //mapflags: noexp check [Lorky]
+ if (map[md->bl.m].flag.nobaseexp == 1) base_exp=0;
+ if (map[md->bl.m].flag.nojobexp == 1) job_exp=0;
+ //end added Lorky
+ if((pid=tmpsd[i]->status.party_id)>0){ // ƒp[ƒeƒB‚É“ü‚Á‚Ä‚¢‚é
+ int j;
+ for(j=0;j<pnum;j++) // Œö•½ƒp[ƒeƒBƒŠƒXƒg‚É‚¢‚é‚©‚Ç‚¤‚©
+ if(pt[j].id==pid)
+ break;
+ if(j==pnum){ // ‚¢‚È‚¢‚Æ‚«‚ÍŒö•½‚©‚Ç‚¤‚©Šm”F
+ if((p=party_search(pid))!=NULL && p->exp!=0){
+ pt[pnum].id=pid;
+ pt[pnum].p=p;
+ pt[pnum].base_exp=base_exp;
+ pt[pnum].job_exp=job_exp;
+ if(battle_config.zeny_from_mobs)
+ pt[pnum].zeny=zeny; // zeny share [Valaris]
+ pnum++;
+ flag=0;
+ }
+ }else{ // ‚¢‚é‚Æ‚«‚ÍŒö•½
+ if (pt[j].base_exp +base_exp < 0x7fffffff)
+ pt[j].base_exp+=base_exp;
+ else
+ pt[j].base_exp = 0x7fffffff;
+ if (pt[j].job_exp +job_exp < 0x7fffffff)
+ pt[j].job_exp+=job_exp;
+ else
+ pt[j].job_exp = 0x7fffffff;
+ if(battle_config.zeny_from_mobs)
+ pt[j].zeny+=zeny; // zeny share [Valaris]
+ flag=0;
+ }
+ }
+ if(flag) { // added zeny from mobs [Valaris]
+ if(base_exp > 0 || job_exp > 0)
+ pc_gainexp(tmpsd[i],base_exp,job_exp);
+ if (battle_config.zeny_from_mobs && zeny > 0) {
+ pc_getzeny(tmpsd[i],zeny); // zeny from mobs [Valaris]
+ }
+ }
+
+ }
+ // Œö•½•ª”z
+ for(i=0;i<pnum;i++)
+ party_exp_share(pt[i].p,md->bl.m,pt[i].base_exp,pt[i].job_exp,pt[i].zeny);
+
+ // item drop
+ if (!(type&1)) {
+ int drop_ore = -1, drop_items = 0; //slot N for DROP LOG, number of dropped items
+ int log_item[10]; //8 -> 10 Lupus
+ memset(&log_item,0,sizeof(log_item));
+ for (i = 0; i < 10; i++) { // 8 -> 10 Lupus
+ struct delay_item_drop *ditem;
+
+ if ((master && status_get_mode(master) & MD_BOSS) || // check if its master is a boss (MVP's and minibosses)
+ (md->special_state.ai &&
+ (battle_config.alchemist_summon_reward == 0 || //Noone gives items
+ (md->class_ != 1142 && battle_config.alchemist_summon_reward == 1) //Non Marine spheres don't drop items
+ ))) // Added [Valaris]
+ break; // End
+ //mapflag: noloot check [Lorky]
+ if (map[md->bl.m].flag.nomobloot) break;;
+ //end added [Lorky]
+
+ if (md->db->dropitem[i].nameid <= 0)
+ continue;
+ drop_rate = md->db->dropitem[i].p;
+ if (drop_rate <= 0 && !battle_config.drop_rate0item)
+ drop_rate = 1;
+ // change drops depending on monsters size [Valaris]
+ if(md->special_state.size==1 && drop_rate >= 2)
+ drop_rate/=2;
+ else if(md->special_state.size==2 && drop_rate > 0)
+ drop_rate*=2;
+ //Drops affected by luk as a fixed increase [Valaris]
+ if (src && battle_config.drops_by_luk > 0)
+ drop_rate += status_get_luk(src)*battle_config.drops_by_luk/100;
+ //Drops affected by luk as a % increase [Skotlex]
+ if (src && battle_config.drops_by_luk2 > 0)
+ drop_rate += (int)(0.5+drop_rate*status_get_luk(src)*battle_config.drops_by_luk2/10000.0);
+ if (sd && battle_config.pk_mode == 1 && (md->db->lv - sd->status.base_level >= 20))
+ drop_rate = (int)(drop_rate*1.25); // pk_mode increase drops if 20 level difference [Valaris]
+
+ if (drop_rate < rand() % 10000 + 1) { //fixed 0.01% impossible drops bug [Lupus]
+ drop_ore = i; //we remember an empty slot to put there ORE DISCOVERY drop later.
+ continue;
+ }
+ drop_items++; //we count if there were any drops
+
+ ditem = mob_setdropitem(md->db->dropitem[i].nameid, 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ log_item[i] = ditem->item_data.nameid;
+
+ //A Rare Drop Global Announce by Lupus
+ if(drop_rate<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(ditem->item_data.nameid);
+ sprintf (message, msg_txt(541), (mvp_sd?mvp_sd->status.name:"???"), md->db->jname, i_data->jname, (float)drop_rate/100);
+ //MSG: "'%s' won %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+ // Announce first, or else ditem will be freed. [Lance]
+ mob_item_drop(md, tick+base_drop_delay+i, ditem, 0, drop_rate);
+ }
+
+ // Ore Discovery [Celest]
+ if (sd == mvp_sd && map[md->bl.m].flag.nomobloot==0 && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) {
+ struct delay_item_drop *ditem;
+ ditem = mob_setdropitem(itemdb_searchrandomid(6), 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ if (drop_ore<0) drop_ore=8; //we have only 10 slots in LOG, there's a check to not overflow (9th item usually a card, so we use 8th slot)
+ log_item[drop_ore] = ditem->item_data.nameid; //it's for logging only
+ drop_items++; //we count if there were any drops
+ mob_item_drop(md, tick+base_drop_delay+drop_ore, ditem, 0, battle_config.finding_ore_rate/10);
+ }
+
+ //this drop log contains ALL dropped items + ORE (if there was ORE Recovery) [Lupus]
+ if(sd && log_config.drop > 0 && drop_items) //we check were there any drops.. and if not - don't write the log
+ log_drop(sd, md->class_, log_item); //mvp_sd
+
+ if(sd/* && sd->state.attack_type == BF_WEAPON*/) { //Player reports indicate this SHOULD work with all skills. [Skotlex]
+ int itemid = 0;
+ for (i = 0; i < sd->add_drop_count; i++) {
+ struct delay_item_drop *ditem;
+ if (sd->add_drop[i].id < 0)
+ continue;
+ if (sd->add_drop[i].race & (1<<race) ||
+ sd->add_drop[i].race & 1<<(mode&MD_BOSS?10:11))
+ {
+ //check if the bonus item drop rate should be multiplied with mob level/10 [Lupus]
+ if(sd->add_drop[i].rate<0)
+ //it's negative, then it should be multiplied. e.g. for Mimic,Myst Case Cards, etc
+ // rate = base_rate * (mob_level/10) + 1
+ drop_rate = -sd->add_drop[i].rate*(md->level/10)+1;
+ else
+ //it's positive, then it goes as it is
+ drop_rate = sd->add_drop[i].rate;
+ if (drop_rate < rand()%10000 +1)
+ continue;
+ itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id :
+ itemdb_searchrandomgroup(sd->add_drop[i].group);
+
+ ditem = mob_setdropitem(itemid, 1, md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ mob_item_drop(md, tick+base_drop_delay+20+i, ditem, 0, drop_rate);
+ }
+ }
+ if(sd->get_zeny_num && rand()%100 < sd->get_zeny_rate) //Gets get_zeny_num per level +/-10% [Skotlex]
+ pc_getzeny(sd,md->db->lv*sd->get_zeny_num*(90+rand()%21)/100);
+ }
+ if(md->lootitem) {
+ for(i=0;i<md->lootitem_count;i++) {
+ struct delay_item_drop *ditem;
+
+ ditem = mob_setlootitem(&md->lootitem[i], md->bl.m, md->bl.x, md->bl.y, mvp_sd, second_sd, third_sd);
+ mob_item_drop(md, tick+base_drop_delay+40+i, ditem, 1, 10000);
+ }
+ }
+ }
+
+ // mvpˆ—
+ if(mvp_sd && md->db->mexp > 0 && !md->special_state.ai){
+ int log_mvp[2] = {0};
+ int j;
+ int mexp;
+ temp = ((double)md->db->mexp * (9.+(double)count)/10.); //[Gengar]
+ mexp = (temp > 2147483647.)? 0x7fffffff:(int)temp;
+
+ //mapflag: noexp check [Lorky]
+ if (map[md->bl.m].flag.nobaseexp == 1 || map[md->bl.m].flag.nojobexp == 1) mexp=1;
+ //end added [Lorky]
+
+ if(mexp < 1) mexp = 1;
+ clif_mvp_effect(mvp_sd); // ƒGƒtƒFƒNƒg
+ clif_mvp_exp(mvp_sd,mexp);
+ pc_gainexp(mvp_sd,mexp,0);
+ log_mvp[1] = mexp;
+ for(j=0;j<3;j++){
+ i = rand() % 3;
+ //mapflag: noloot check [Lorky]
+ if (map[md->bl.m].flag.nomvploot == 1) break;
+ //end added Lorky
+
+ if(md->db->mvpitem[i].nameid <= 0)
+ continue;
+ drop_rate = md->db->mvpitem[i].p;
+ if(drop_rate <= 0 && !battle_config.drop_rate0item)
+ drop_rate = 1;
+ if(drop_rate <= rand()%10000+1) //if ==0, then it doesn't drop
+ continue;
+ memset(&item,0,sizeof(item));
+ item.nameid=md->db->mvpitem[i].nameid;
+ item.identify=!itemdb_isequip3(item.nameid);
+ clif_mvp_item(mvp_sd,item.nameid);
+ log_mvp[0] = item.nameid;
+ if(mvp_sd->weight*2 > mvp_sd->max_weight)
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ else if((ret = pc_additem(mvp_sd,&item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ }
+
+ //A Rare MVP Drop Global Announce by Lupus
+ if(drop_rate<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(item.nameid);
+ sprintf (message, msg_txt(541), mvp_sd?mvp_sd->status.name :"???", md->db->jname, i_data->jname, (float)drop_rate/100);
+ //MSG: "'%s' won %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+
+ if(log_config.pick > 0) {//Logs items, MVP prizes [Lupus]
+ log_pick((struct map_session_data*)md, "M", md->class_, item.nameid, -1, NULL);
+ log_pick(mvp_sd, "P", 0, item.nameid, 1, NULL);
+ }
+
+ if(log_config.mvpdrop > 0)
+ log_mvpdrop(mvp_sd, md->class_, log_mvp);
+
+ break;
+ }
+
+ }
+
+ } // [MouseJstr]
+
+ // <Agit> NPC Event [OnAgitBreak]
+ if(md->npc_event[0] && strcmp(((md->npc_event)+strlen(md->npc_event)-13),"::OnAgitBreak") == 0) {
+ ShowNotice("MOB.C: Run NPC_Event[OnAgitBreak].\n");
+ if (agit_flag == 1) //Call to Run NPC_Event[OnAgitBreak]
+ guild_agit_break(md);
+ }
+
+ // SCRIPTŽÀs
+ if(md->npc_event[0]){
+// if(battle_config.battle_log)
+// printf("mob_damage : run event : %s\n",md->npc_event);
+ if(src && src->type == BL_PET)
+ sd = ((struct pet_data *)src)->msd;
+ if(sd && battle_config.mob_npc_event_type)
+ npc_event(sd,md->npc_event,0);
+ else if(mvp_sd)
+ npc_event(mvp_sd,md->npc_event,0);
+
+ } else if (mvp_sd) {
+//lordalfa
+ pc_setglobalreg(mvp_sd,"killedrid",(md->class_));
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id("NPCKillEvent"))) {
+ run_script(npc->u.scr.script,0,mvp_sd->bl.id,npc->bl.id); // NPCKillNPC
+ ShowStatus("Event '"CL_WHITE"NPCKillEvent"CL_RESET"' executed.\n");
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id("NPCKillEvent", mvp_sd->bl.id), "NPCKillEvent");
+ }
+}
+//[lordalfa]
+ (battle_config.mob_clear_delay) ? clif_clearchar_delay(tick+battle_config.mob_clear_delay,&md->bl,1) : clif_clearchar_area(&md->bl,1);
+// clif_clearchar_area(&md->bl,1); //eh? Why send the same packet twice? [Skotlex]
+ if(md->level) md->level=0;
+ map_delblock(&md->bl);
+ if(pcdb_checkid(mob_get_viewclass(md->class_)))
+ clif_clearchar_delay(tick+3000,&md->bl,0);
+ if(mob_is_clone(md->class_))
+ mob_clone_delete(md->class_);
+ mob_deleteslave(md);
+ mob_setdelayspawn(md->bl.id);
+ map_freeblock_unlock();
+
+ return damage;
+}
+
+int mob_guardian_guildchange(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md;
+ struct guild* g;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, md = (struct mob_data *)bl);
+
+ if (!md->guardian_data)
+ return 0;
+
+ if (md->guardian_data->castle->guild_id == 0)
+ { //Castle with no owner? Delete the guardians.
+ if (md->class_ == MOBID_EMPERIUM)
+ { //But don't delete the emperium, just clear it's guild-data
+ md->guardian_data->guild_id = 0;
+ md->guardian_data->emblem_id = 0;
+ md->guardian_data->guild_name[0] = '\0';
+ } else {
+ if (md->guardian_data->castle->guardian[md->guardian_data->number].visible)
+ { //Safe removal of guardian.
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ mob_delete(md); //Remove guardian.
+ }
+ return 0;
+ }
+
+ g = guild_search(md->guardian_data->castle->guild_id);
+ if (g == NULL)
+ { //Properly remove guardian info from Castle data.
+ ShowError("mob_guardian_guildchange: New Guild (id %d) does not exists!\n", md->guardian_data->guild_id);
+ md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0;
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ mob_delete(md);
+ return 0;
+ }
+
+ md->guardian_data->guild_id = md->guardian_data->castle->guild_id;
+ md->guardian_data->emblem_id = g->emblem_id;
+ md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP);
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+
+ return 1;
+}
+
+/*==========================================
+ * Pick a random class for the mob
+ *------------------------------------------
+ */
+int mob_random_class (int *value, size_t count)
+{
+ nullpo_retr(0, value);
+
+ // no count specified, look into the array manually, but take only max 5 elements
+ if (count < 1) {
+ count = 0;
+ while(count < 5 && mobdb_checkid(value[count])) count++;
+ if(count < 1) // nothing found
+ return 0;
+ } else {
+ // check if at least the first value is valid
+ if(mobdb_checkid(value[0]) == 0)
+ return 0;
+ }
+ //Pick a random value, hoping it exists. [Skotlex]
+ return mobdb_checkid(value[rand()%count]);
+}
+
+/*==========================================
+ * Change mob base class
+ *------------------------------------------
+ */
+int mob_class_change (struct mob_data *md, int class_)
+{
+ unsigned int tick = gettick();
+ int i, c, hp_rate;
+
+ nullpo_retr(0, md);
+
+ if (md->bl.prev == NULL)
+ return 0;
+
+ hp_rate = md->hp*100/status_get_max_hp(&md->bl);
+ clif_mob_class_change(md,class_);
+ md->class_ = class_;
+ md->db = mob_db(class_);
+ md->max_hp = md->db->max_hp; //Update the mob's max HP
+ if (battle_config.monster_class_change_full_recover) {
+ md->hp = md->max_hp;
+ memset(md->dmglog, 0, sizeof(md->dmglog));
+ md->tdmg = 0;
+ } else
+ md->hp = md->max_hp*hp_rate/100;
+ if(md->hp > md->max_hp) md->hp = md->max_hp;
+ else if(md->hp < 1) md->hp = 1;
+
+ memcpy(md->name,md->db->jname,NAME_LENGTH-1);
+ memset(&md->state,0,sizeof(md->state));
+ md->attacked_id = 0;
+ md->target_id = 0;
+ md->move_fail_count = 0;
+
+ md->speed = md->db->speed;
+ md->def_ele = md->db->element;
+
+ mob_changestate(md,MS_IDLE,0);
+ skill_castcancel(&md->bl,0);
+ md->state.skillstate = MSS_IDLE;
+ md->last_thinktime = tick;
+ md->next_walktime = tick+rand()%50+5000;
+ md->attackabletime = tick;
+ md->canmove_tick = tick;
+ md->last_linktime = tick;
+
+ for(i=0,c=tick-1000*3600*10;i<MAX_MOBSKILL;i++)
+ md->skilldelay[i] = c;
+ md->skillid=0;
+ md->skilllv=0;
+
+ if(md->lootitem == NULL && md->db->mode&MD_LOOTER)
+ md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ skill_clear_unitgroup(&md->bl);
+ skill_cleartimerskill(&md->bl);
+
+ clif_clearchar_area(&md->bl,0);
+ clif_spawnmob(md);
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+/*==========================================
+ * mob‰ñ•œ
+ *------------------------------------------
+ */
+int mob_heal(struct mob_data *md,int heal)
+{
+ int max_hp;
+
+ nullpo_retr(0, md);
+ max_hp = status_get_max_hp(&md->bl);
+
+ md->hp += heal;
+ if( max_hp < md->hp )
+ md->hp = max_hp;
+
+ if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex])
+ if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0)
+ {
+ guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0);
+ guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0);
+ }
+ } // end addition
+
+ if (battle_config.show_mob_hp)
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Added by RoVeRT
+ *------------------------------------------
+ */
+int mob_warpslave_sub(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md=(struct mob_data *)bl;
+ struct block_list *master;
+ int x,y,range,i=0;
+ master = va_arg(ap, struct block_list*);
+ range = va_arg(ap, int);
+
+ if(md->master_id!=master->id)
+ return 0;
+
+ do {
+ x = master->x - range/2 + rand()%range;
+ y = master->y - range/2 + rand()%range;
+ } while (map_getcell(master->m,x,y,CELL_CHKNOPASS) && i<25);
+
+ if (i == 100)
+ mob_warp(md, master->m, master->x, master->y,2);
+ else
+ mob_warp(md, master->m, x, y,2);
+
+ return 1;
+}
+
+/*==========================================
+ * Added by RoVeRT
+ * Warps slaves. Range is the area around the master that they can
+ * appear in randomly.
+ *------------------------------------------
+ */
+int mob_warpslave(struct block_list *bl, int range)
+{
+ if (range < 1)
+ range = 1; //Min range needed to avoid crashes and stuff. [Skotlex]
+
+ return map_foreachinmap(mob_warpslave_sub, bl->m, BL_MOB, bl, range);
+}
+
+/*==========================================
+ * mobƒ[ƒv
+ *------------------------------------------
+ */
+int mob_warp(struct mob_data *md,int m,int x,int y,int type)
+{
+ int i=0,xs=0,ys=0,bx=x,by=y;
+ int tick = gettick();
+
+ nullpo_retr(0, md);
+
+ if( md->bl.prev==NULL )
+ return 0;
+
+ if( m<0 ) m=md->bl.m;
+
+ if(type >= 0) {
+ if(map[md->bl.m].flag.monster_noteleport)
+ return 0;
+ if(md->sc_count) { //Clear a few status changes (taken directly from pc_setpos). [Skotlex]
+ if(md->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&md->bl, SC_TRICKDEAD, -1);
+ if(md->sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end(&md->bl,SC_BLADESTOP,-1);
+ if(md->sc_data && md->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&md->bl,SC_RUN,-1);
+ if(md->sc_data[SC_DANCING].timer!=-1)
+ skill_stop_dancing(&md->bl);
+ if (md->sc_data[SC_DEVOTION].timer!=-1)
+ status_change_end(&md->bl,SC_DEVOTION,-1);
+ if (md->sc_data[SC_CLOSECONFINE].timer!=-1)
+ status_change_end(&md->bl,SC_CLOSECONFINE,-1);
+ if (md->sc_data[SC_CLOSECONFINE2].timer!=-1)
+ status_change_end(&md->bl,SC_CLOSECONFINE2,-1);
+ if (md->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&md->bl,SC_RUN,-1);
+ }
+ clif_clearchar_area(&md->bl,type);
+ }
+ skill_unit_move(&md->bl,tick,4);
+ map_delblock(&md->bl);
+
+ if(bx>0 && by>0){ // ˆÊ’uŽw’è‚ÌꇎüˆÍ‚XƒZƒ‹‚ð’Tõ
+ xs=ys=9;
+ }
+
+ while( ( x<0 || y<0 || map_getcell(m,x,y,CELL_CHKNOPASS)) && (i++)<1000 ){
+ if( xs>0 && ys>0 && i<250 ){ // Žw’èˆÊ’u•t‹ß‚Ì’Tõ
+ x=bx+rand()%xs-xs/2;
+ y=by+rand()%ys-ys/2;
+ }else{ // Š®‘Sƒ‰ƒ“ƒ_ƒ€’Tõ
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ }
+ }
+ md->dir=0;
+ if(i<1000){
+ md->bl.x=md->to_x=x;
+ md->bl.y=md->to_y=y;
+ md->bl.m=m;
+ }else {
+ m=md->bl.m;
+ if(battle_config.error_log==1)
+ ShowWarning("MOB %d warp failed, class = %d\n",md->bl.id,md->class_);
+ }
+
+ md->target_id=0; // ƒ^ƒQ‚ð‰ðœ‚·‚é
+ md->state.targettype=NONE_ATTACKABLE;
+ md->attacked_id=0;
+ if (md->master_id)
+ md->master_dist = 0; //Assume mob warped to leader. [Skotlex]
+ md->state.skillstate=MSS_IDLE;
+ mob_changestate(md,MS_IDLE,0);
+
+ if(type>0 && i==1000) {
+ if(battle_config.battle_log)
+ ShowInfo("MOB %d warp to (%d,%d), class = %d\n",md->bl.id,x,y,md->class_);
+ }
+
+ map_addblock(&md->bl);
+ skill_unit_move(&md->bl,tick,1);
+ if(type>0)
+ {
+ clif_spawnmob(md);
+ mob_warpslave(&md->bl,AREA_SIZE);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ‰æ–Ê“à‚ÌŽæ‚芪‚«‚Ì”ŒvŽZ—p(foreachinarea)
+ *------------------------------------------
+ */
+int mob_countslave_sub(struct block_list *bl,va_list ap)
+{
+ int id,*c;
+ struct mob_data *md;
+ id=va_arg(ap,int);
+
+ c=va_arg(ap,int *);
+ md = (struct mob_data *)bl;
+
+ if( md->master_id==id ) {
+ (*c)++;
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * ‰æ–Ê“à‚ÌŽæ‚芪‚«‚Ì”ŒvŽZ
+ *------------------------------------------
+ */
+int mob_countslave(struct block_list *bl)
+{
+ int c=0;
+ map_foreachinmap(mob_countslave_sub, bl->m, BL_MOB,bl->id,&c);
+ return c;
+}
+/*==========================================
+ * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex]
+ *------------------------------------------
+ */
+int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id)
+{
+ struct mob_data *md;
+ int bx,by,m,count = 0,class_,k;
+
+ nullpo_retr(0, md2);
+ nullpo_retr(0, value);
+
+ bx=md2->bl.x;
+ by=md2->bl.y;
+ m=md2->bl.m;
+
+ if(mobdb_checkid(value[0]) == 0)
+ return 0;
+
+ while(count < 5 && mobdb_checkid(value[count])) count++;
+ if(count < 1) return 0;
+
+ for(k=0;k<amount;k++) {
+ int x=0,y=0,i=0;
+ class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex]
+
+ if (mobdb_checkid(class_) == 0)
+ continue;
+
+ md=(struct mob_data *)aCalloc(1,sizeof(struct mob_data));
+ if(mob_db(class_)->mode&MD_LOOTER)
+ md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item));
+
+ while((x<=0 || y<=0 || map_getcell(m,x,y,CELL_CHKNOPASS)) && (i++)<100){
+ x=rand()%9-4+bx;
+ y=rand()%9-4+by;
+ }
+ if(i>=100){
+ x=bx;
+ y=by;
+ }
+
+ mob_spawn_dataset(md,"--ja--",class_);
+ md->bl.m=m;
+ md->bl.x=x;
+ md->bl.y=y;
+
+ md->m =m;
+ md->x0=x;
+ md->y0=y;
+ md->xs=0;
+ md->ys=0;
+ if (battle_config.slaves_inherit_speed && (skill_id != NPC_METAMORPHOSIS && skill_id != NPC_TRANSFORMATION))
+ md->speed=md2->speed;
+ md->special_state.cached= battle_config.dynamic_mobs; //[Skotlex]
+ md->spawndelay1=-1; // ˆê“x‚̂݃tƒ‰ƒO
+ md->spawndelay2=-1; // ˆê“x‚̂݃tƒ‰ƒO
+
+ if (!battle_config.monster_class_change_full_recover &&
+ (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS))
+ { //Scale HP
+ md->hp = (md->max_hp*md2->hp)/md2->max_hp;
+ }
+
+ memset(md->npc_event,0,sizeof(md->npc_event));
+ md->bl.type=BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+ clif_skill_nodamage(&md->bl,&md->bl,skill_id,amount,1);
+
+ if(skill_id == NPC_SUMMONSLAVE)
+ md->master_id=md2->bl.id;
+ }
+ return 0;
+}
+
+/*==========================================
+ *MOBskill‚©‚çŠY“–skillid‚Ìskillidx‚ð•Ô‚·
+ *------------------------------------------
+ */
+int mob_skillid2skillidx(int class_,int skillid)
+{
+ int i, max = mob_db(class_)->maxskill;
+ struct mob_skill *ms=mob_db(class_)->skill;
+
+ if(ms==NULL)
+ return -1;
+
+ for(i=0;i<max;i++){
+ if(ms[i].skill_id == skillid)
+ return i;
+ }
+ return -1;
+
+}
+
+//
+// MOBƒXƒLƒ‹
+//
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—pi‰r¥Š®—¹AIDŽw’èj
+ *------------------------------------------
+ */
+int mobskill_castend_id( int tid, unsigned int tick, int id,int data )
+{
+ struct mob_data* md=NULL;
+ struct block_list *bl;
+ int inf;
+//Code cleanup. Insert any code that should be executed if the skill fails here.
+#define skill_failed(md) { md->skillid = md->skilllv = -1; }
+
+ if((md = (struct mob_data*)map_id2bl(id)) == NULL ) //‰r¥‚µ‚½Mob‚ª‚à‚¤‚¢‚È‚¢‚Æ‚¢‚¤‚Ì‚Í—Ç‚­‚ ‚é³íˆ—
+ return 0;
+
+ if( md->bl.type!=BL_MOB || md->bl.prev==NULL )
+ return 0;
+
+ if( md->skilltimer != tid ) {
+ if (battle_config.error_log)
+ ShowError("mobskill_castend_id: Timer mismatch %d!=%d\n", md->skilltimer, tid);
+ md->skilltimer = -1;
+ return 0;
+ }
+
+ md->skilltimer=-1;
+
+ if((bl = map_id2bl(md->skilltarget)) == NULL || bl->prev==NULL || md->bl.m != bl->m) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(md->skillid != NPC_EMOTION)
+ //Set afterskill delay.
+ md->last_thinktime=tick + (tid==-1?status_get_adelay(&md->bl):status_get_amotion(&md->bl));
+
+ if(md->skillid == RG_BACKSTAP) {
+ int dir = map_calc_dir(&md->bl,bl->x,bl->y),t_dir = status_get_dir(bl);
+ if(bl->type != BL_SKILL && (check_distance_bl(&md->bl, bl, 0) || map_check_dir(dir,t_dir))) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+
+ inf = skill_get_inf(md->skillid);
+ if((inf&INF_ATTACK_SKILL ||
+ (inf&INF_SELF_SKILL && md->bl.id != bl->id && skill_get_nk(md->skillid) != NK_NO_DAMAGE))
+ && battle_check_target(&md->bl,bl, BCT_ENEMY)<=0 ) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(tid != -1)
+ {
+ if (md->skillid == -1 ||!status_check_skilluse(&md->bl, bl, md->skillid, 1)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(!check_distance_bl(&md->bl, bl, skill_get_range2(&md->bl, md->skillid,md->skilllv) + battle_config.mob_skill_add_range)) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+ md->skilldelay[md->skillidx]=tick;
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill castend skill=%d, class = %d\n",md->skillid,md->class_);
+// mob_stop_wShowInfo(md,0);
+
+ switch( skill_get_nk(md->skillid) )
+ {
+ case NK_NO_DAMAGE:// Žx‰‡Œn
+ skill_castend_nodamage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0);
+ break;
+ // UŒ‚Œn/‚«”ò‚΂µŒn
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0);
+ break;
+ }
+
+ if (md->sc_count && md->sc_data[SC_MAGICPOWER].timer != -1 && md->skillid != HW_MAGICPOWER)
+ status_change_end(&md->bl, SC_MAGICPOWER, -1);
+
+ if (md->db->skill[md->skillidx].emotion >= 0)
+ clif_emotion(&md->bl, md->db->skill[md->skillidx].emotion);
+
+ //TODO: This value is used for the "afterskill" mob condition, what could one do to clean it other than
+ //to use a "previous skill" struct variable?
+ //md->skillid = md->skilllv = -1; //Clean up skill-data for battle_getcurrentskill references. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—pi‰r¥Š®—¹Aꊎw’èj
+ *------------------------------------------
+ */
+int mobskill_castend_pos( int tid, unsigned int tick, int id,int data )
+{
+ struct mob_data* md=NULL;
+ int maxcount;
+
+//Code cleanup. Insert any code that should be executed if the skill fails here.
+#define skill_failed(md) { md->skillid = md->skilllv = -1; }
+
+ nullpo_retr(0, md=(struct mob_data *)map_id2bl(id));
+
+ if( md->bl.type!=BL_MOB || md->bl.prev==NULL )
+ return 0;
+
+ if( md->skilltimer != tid ) {
+ if (battle_config.error_log)
+ ShowError("mobskill_castend_pos: Timer mismatch %d!=%d\n", md->skilltimer, tid);
+ md->skilltimer = -1;
+ return 0;
+ }
+
+ md->skilltimer=-1;
+
+ if (tid != -1)
+ { //Avoid unnecessary checks for instant-cast skills. [Skotlex]
+ if (md->skillid == -1 || !status_check_skilluse(&md->bl, NULL, md->skillid, 1)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(!check_distance_blxy(&md->bl, md->skillx, md->skilly, skill_get_range2(&md->bl, md->skillid,md->skilllv) + battle_config.mob_skill_add_range)) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+
+ if (!battle_config.monster_skill_reiteration &&
+ skill_get_unit_flag (md->skillid) & UF_NOREITERATION &&
+ skill_check_unit_range (md->bl.m, md->skillx, md->skilly, md->skillid, md->skilllv)) {
+ skill_failed(md);
+ return 0;
+ }
+
+ if(battle_config.monster_skill_nofootset &&
+ skill_get_unit_flag (md->skillid) & UF_NOFOOTSET &&
+ skill_check_unit_range2(&md->bl, md->bl.m, md->skillx, md->skilly, md->skillid, md->skilllv)) {
+ skill_failed(md);
+ return 0;
+ }
+ if(battle_config.monster_land_skill_limit) {
+ maxcount = skill_get_maxcount(md->skillid);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_MOBSKILLUNITGROUP;i++) {
+ if(md->skillunit[i].alive_count > 0 && md->skillunit[i].skill_id == md->skillid)
+ c++;
+ }
+ if(c >= maxcount) {
+ skill_failed(md);
+ return 0;
+ }
+ }
+ }
+
+ md->skilldelay[md->skillidx]=tick;
+
+ //Set afterskill delay.
+ md->last_thinktime=tick + (tid==-1?status_get_adelay(&md->bl):status_get_amotion(&md->bl));
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill castend skill=%d, class = %d\n",md->skillid,md->class_);
+// mob_stop_walking(md,0);
+
+ skill_castend_pos2(&md->bl,md->skillx,md->skilly,md->skillid,md->skilllv,tick,0);
+
+ if (md->db->skill[md->skillidx].emotion >= 0)
+ clif_emotion(&md->bl, md->db->skill[md->skillidx].emotion);
+
+ //TODO: This value is used for the "afterskill" mob condition, what could one do to clean it other than
+ //to use a "previous skill" struct variable?
+ //md->skillid = md->skilllv = -1; //Clean up skill data for future references to battle_getcurrentskill [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * Skill use (an aria start, ID specification)
+ *------------------------------------------
+ */
+int mobskill_use_id(struct mob_data *md,struct block_list *target,int skill_idx)
+{
+ int casttime;
+ struct mob_skill *ms;
+ int skill_id, skill_lv, forcecast = 0;
+ int selfdestruct_flag = 0;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, ms=&md->db->skill[skill_idx]);
+
+ if( target==NULL && (target=map_id2bl(md->target_id))==NULL )
+ return 0;
+
+ if( target->prev==NULL || md->bl.prev==NULL )
+ return 0;
+
+ skill_id=ms->skill_id;
+ skill_lv=ms->skill_lv;
+
+ if(map_flag_gvg(md->bl.m) && skill_db[skill_id].nocast & 4)
+ return 0;
+ if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF && md->bl.id == target->id)
+ return 0;
+
+ if(!status_check_skilluse(&md->bl, target, skill_id, 0))
+ return 0;
+
+ if(md->sc_data && md->sc_data[SC_WINKCHARM].timer != -1 && target->type == BL_PC) {
+ clif_emotion(&md->bl, 3);
+ return 0;
+ }
+
+ // ŽË’ö‚ÆáŠQ•¨ƒ`ƒFƒbƒN
+ if(!battle_check_range(&md->bl,target,skill_get_range2(&md->bl,skill_id,skill_lv)))
+ return 0;
+
+// delay=skill_delayfix(&md->bl, skill_id, skill_lv, 0);
+
+ casttime=skill_castfix(&md->bl, skill_id, skill_lv, ms->casttime);
+ md->state.skillcastcancel=ms->cancel;
+ md->skilldelay[skill_idx]=gettick();
+
+ switch(skill_id){ /* ‰½‚©“ÁŽê‚Ȉ—‚ª•K—v */
+ case ALL_RESURRECTION: /* ƒŠƒUƒŒƒNƒVƒ‡ƒ“ */
+ if(target->type != BL_PC && battle_check_undead(status_get_race(target),status_get_elem_type(target))){ /* “G‚ªƒAƒ“ƒfƒbƒh‚È‚ç */
+ forcecast=1; /* ƒ^[ƒ“ƒAƒ“ƒfƒbƒg‚Æ“¯‚¶‰r¥ŽžŠÔ */
+ casttime=skill_castfix(&md->bl, PR_TURNUNDEAD,skill_lv, 0);
+ }
+ break;
+ case MO_EXTREMITYFIST: /*ˆ¢C—…”e–PŒ*/
+ case SA_MAGICROD:
+ case SA_SPELLBREAKER:
+ forcecast=1;
+ break;
+ case NPC_SUMMONSLAVE:
+ case NPC_SUMMONMONSTER:
+ if(md->master_id!=0)
+ return 0;
+ break;
+ case NPC_SELFDESTRUCTION:
+ if (casttime == 0 && md->special_state.ai == 2) {
+ casttime = skill_get_time(skill_id,skill_lv);
+ selfdestruct_flag = 1;
+ }
+ break;
+ }
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill use target_id=%d skill=%d lv=%d cast=%d, class = %d\n",target->id,skill_id,skill_lv,casttime,md->class_);
+
+ if (casttime || forcecast) { // ‰r¥‚ª•K—v
+ if (!selfdestruct_flag)
+ mob_stop_walking(md,1); // •às’âŽ~
+ clif_skillcasting(&md->bl, md->bl.id, target->id, 0,0, skill_id, casttime);
+ }
+
+ if (casttime <= 0) // ‰r¥‚Ì–³‚¢‚à‚̂̓Lƒƒƒ“ƒZƒ‹‚³‚ê‚È‚¢
+ md->state.skillcastcancel = 0;
+
+ md->skilltarget = target->id;
+ md->skillx = 0;
+ md->skilly = 0;
+ md->skillid = skill_id;
+ md->skilllv = skill_lv;
+ md->skillidx = skill_idx;
+
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1 && md->skillid != AS_CLOAKING)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+
+ if( casttime>0 ){
+ md->skilltimer =
+ add_timer( gettick()+casttime, mobskill_castend_id, md->bl.id, 0 );
+ }else{
+ md->skilltimer = -1;
+ mobskill_castend_id(md->skilltimer,gettick(),md->bl.id, 0);
+ }
+
+ return 1;
+}
+/*==========================================
+ * ƒXƒLƒ‹Žg—piꊎw’èj
+ *------------------------------------------
+ */
+int mobskill_use_pos( struct mob_data *md,
+ int skill_x, int skill_y, int skill_idx)
+{
+ int casttime=0;
+ struct mob_skill *ms;
+ struct block_list bl;
+ int skill_id, skill_lv;
+
+ nullpo_retr(0, md);
+ nullpo_retr(0, ms=&md->db->skill[skill_idx]);
+
+ if( md->bl.prev==NULL )
+ return 0;
+
+ skill_id=ms->skill_id;
+ skill_lv=ms->skill_lv;
+
+ if(map_flag_gvg(md->bl.m) && skill_db[skill_id].nocast & 4)
+ return 0;
+ if(!status_check_skilluse(&md->bl, NULL, skill_id, 0))
+ return 0;
+
+ // ŽË’ö‚ÆáŠQ•¨ƒ`ƒFƒbƒN
+ bl.type = BL_NUL;
+ bl.m = md->bl.m;
+ bl.x = skill_x;
+ bl.y = skill_y;
+ if(!battle_check_range(&md->bl,&bl,skill_get_range2(&md->bl,skill_id,skill_lv)))
+ return 0;
+
+// delay=skill_delayfix(&md->bl, skill_id, skill_lv, 0);
+ casttime=skill_castfix(&md->bl,skill_id, skill_lv, ms->casttime);
+ md->skilldelay[skill_idx]=gettick();
+ md->state.skillcastcancel=ms->cancel;
+
+ if(battle_config.mob_skill_log)
+ ShowInfo("MOB skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d, class = %d\n",
+ skill_x,skill_y,skill_id,skill_lv,casttime,md->class_);
+
+ if( casttime>0 ) { // A cast time is required.
+ mob_stop_walking(md,1); // •às’âŽ~
+ clif_skillcasting( &md->bl,
+ md->bl.id, 0, skill_x,skill_y, skill_id,casttime);
+ }
+
+ if( casttime<=0 ) // A skill without a cast time wont be cancelled.
+ md->state.skillcastcancel=0;
+
+
+ md->skillx = skill_x;
+ md->skilly = skill_y;
+ md->skilltarget = 0;
+ md->skillid = skill_id;
+ md->skilllv = skill_lv;
+ md->skillidx = skill_idx;
+ if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&md->bl,SC_CLOAKING,-1);
+ if( casttime>0 ){
+ md->skilltimer =
+ add_timer( gettick()+casttime, mobskill_castend_pos, md->bl.id, 0 );
+ }else{
+ md->skilltimer = -1;
+ mobskill_castend_pos(md->skilltimer,gettick(),md->bl.id, 0);
+ }
+
+ return 1;
+}
+
+
+/*==========================================
+ * Friendly Mob whose HP is decreasing by a nearby MOB is looked for.
+ *------------------------------------------
+ */
+int mob_getfriendhpltmaxrate_sub(struct block_list *bl,va_list ap)
+{
+ int rate;
+ struct block_list **fr;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=va_arg(ap,struct mob_data *));
+ rate=va_arg(ap,int);
+ fr=va_arg(ap,struct block_list **);
+
+ if( md->bl.id == bl->id )
+ return 0;
+
+ if ((*fr) != NULL) //A friend was already found.
+ return 0;
+
+ if (battle_check_target(&md->bl,bl,BCT_ENEMY)>0)
+ return 0;
+
+ if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100)
+ (*fr) = bl;
+ return 0;
+}
+struct block_list *mob_getfriendhpltmaxrate(struct mob_data *md,int rate)
+{
+ struct block_list *fr=NULL;
+ const int r=8;
+ int type = BL_MOB;
+
+ nullpo_retr(NULL, md);
+
+ if (md->special_state.ai) //Summoned creatures. [Skotlex]
+ type = BL_PC;
+
+ map_foreachinarea(mob_getfriendhpltmaxrate_sub, md->bl.m,
+ md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r,
+ type,md,rate,&fr);
+ return fr;
+}
+/*==========================================
+ * Check hp rate of its master
+ *------------------------------------------
+ */
+struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate)
+{
+ if (md && md->master_id > 0) {
+ struct block_list *bl = map_id2bl(md->master_id);
+ if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100)
+ return bl;
+ }
+
+ return NULL;
+}
+/*==========================================
+ * What a status state suits by nearby MOB is looked for.
+ *------------------------------------------
+ */
+int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
+{
+ int cond1,cond2;
+ struct mob_data **fr, *md, *mmd;
+ int flag=0;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data *)bl);
+ nullpo_retr(0, mmd=va_arg(ap,struct mob_data *));
+
+ if( mmd->bl.id == bl->id )
+ return 0;
+ if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0)
+ return 0;
+ cond1=va_arg(ap,int);
+ cond2=va_arg(ap,int);
+ fr=va_arg(ap,struct mob_data **);
+ if( cond2==-1 ){
+ int j;
+ for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){
+ if ((flag=(md->sc_data[j].timer!=-1))) //Once an effect was found, break out. [Skotlex]
+ break;
+ }
+ }else
+ flag=( md->sc_data[cond2].timer!=-1 );
+ if( flag^( cond1==MSC_FRIENDSTATUSOFF ) )
+ (*fr)=md;
+
+ return 0;
+}
+struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
+{
+ struct mob_data *fr=NULL;
+ const int r=8;
+
+ nullpo_retr(0, md);
+
+ map_foreachinarea(mob_getfriendstatus_sub, md->bl.m,
+ md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r,
+ BL_MOB,md,cond1,cond2,&fr);
+ return fr;
+}
+
+/*==========================================
+ * Skill use judging
+ *------------------------------------------
+ */
+int mobskill_use(struct mob_data *md, unsigned int tick, int event)
+{
+ struct mob_skill *ms;
+ struct block_list *fbl = NULL; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex]
+ struct mob_data *fmd = NULL;
+ int i;
+
+ nullpo_retr (0, md);
+ nullpo_retr (0, ms = md->db->skill);
+
+ if (battle_config.mob_skill_rate == 0 || md->skilltimer != -1)
+ return 0;
+
+ for (i = 0; i < md->db->maxskill; i++) {
+ int c2 = ms[i].cond2, flag = 0;
+
+ // ƒfƒBƒŒƒC’†
+ if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i].delay)
+ continue;
+
+ // ó‘Ô”»’è
+ if (ms[i].state >= 0 && ms[i].state != md->state.skillstate)
+ continue;
+
+ if (rand() % 10000 > ms[i].permillage) //Lupus (max value = 10000)
+ continue;
+
+ // ðŒ”»’è
+ flag = (event == ms[i].cond1);
+ //Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function
+ //in battle. The only exception is MSC_SKILLUSED which explicitly uses the event value to trigger. [Skotlex]
+ if (!flag && (event == -1 || event == MSC_SKILLUSED)){
+ switch (ms[i].cond1)
+ {
+ case MSC_ALWAYS:
+ flag = 1; break;
+ case MSC_MYHPLTMAXRATE: // HP< maxhp%
+ {
+ int max_hp = status_get_max_hp(&md->bl);
+ flag = (md->hp < max_hp * c2 / 100); break;
+ }
+ case MSC_MYSTATUSON: // status[num] on
+ case MSC_MYSTATUSOFF: // status[num] off
+ if (!md->sc_data) {
+ flag = 0;
+ } else if (ms[i].cond2 == -1) {
+ int j;
+ for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++)
+ if ((flag = (md->sc_data[j].timer != -1)) != 0)
+ break;
+ } else {
+ flag = (md->sc_data[ms[i].cond2].timer != -1);
+ }
+ flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); break;
+ case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp%
+ flag = ((fbl = mob_getfriendhpltmaxrate(md, ms[i].cond2)) != NULL); break;
+ case MSC_FRIENDSTATUSON: // friend status[num] on
+ case MSC_FRIENDSTATUSOFF: // friend status[num] off
+ flag = ((fmd = mob_getfriendstatus(md, ms[i].cond1, ms[i].cond2)) != NULL); break;
+ case MSC_SLAVELT: // slave < num
+ flag = (mob_countslave(&md->bl) < c2 ); break;
+ case MSC_ATTACKPCGT: // attack pc > num
+ flag = (battle_counttargeted(&md->bl, NULL, 0) > c2); break;
+ case MSC_SLAVELE: // slave <= num
+ flag = (mob_countslave(&md->bl) <= c2 ); break;
+ case MSC_ATTACKPCGE: // attack pc >= num
+ flag = (battle_counttargeted(&md->bl, NULL, 0) >= c2); break;
+ case MSC_AFTERSKILL:
+ flag = (md->skillid == c2); break;
+ case MSC_SKILLUSED: // specificated skill used
+ flag = ((event & 0xffff) == MSC_SKILLUSED && ((event >> 16) == c2 || c2 == 0)); break;
+ case MSC_RUDEATTACKED:
+ flag = (!md->attacked_id && md->attacked_count > 0);
+ if (flag) md->attacked_count = 0; //Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex]
+ break;
+ case MSC_MASTERHPLTMAXRATE:
+ flag = ((fbl = mob_getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break;
+ case MSC_MASTERATTACKED:
+ flag = (md->master_id > 0 && battle_counttargeted(map_id2bl(md->master_id), NULL, 0) > 0); break;
+ case MSC_ALCHEMIST:
+ flag = (md->state.alchemist);
+ break;
+ }
+ }
+
+ if (!flag)
+ continue; //Skill requisite failed to be fulfilled.
+
+ //Execute skill
+ if (skill_get_inf(ms[i].skill_id) & INF_GROUND_SKILL) {
+ // ꊎw’è
+ struct block_list *bl = NULL;
+ int x = 0, y = 0;
+ if (ms[i].target <= MST_AROUND) {
+ switch (ms[i].target) {
+ case MST_TARGET:
+ case MST_AROUND5:
+ case MST_AROUND6:
+ case MST_AROUND7:
+ case MST_AROUND8:
+ bl = map_id2bl(md->target_id);
+ break;
+ case MST_MASTER:
+ bl = &md->bl;
+ if (md->master_id)
+ bl = map_id2bl(md->master_id);
+ if (bl) //Otherwise, fall through.
+ break;
+ case MST_FRIEND:
+ if (fbl)
+ {
+ bl = fbl;
+ break;
+ } else if (fmd) {
+ bl= &fmd->bl;
+ break;
+ } // else fall through
+ default:
+ bl = &md->bl;
+ break;
+ }
+ if (bl != NULL) {
+ x = bl->x; y=bl->y;
+ }
+ }
+ if (x <= 0 || y <= 0)
+ continue;
+ // Ž©•ª‚ÌŽüˆÍ
+ if (ms[i].target >= MST_AROUND1) {
+ int bx = x, by = y, i = 0, m = bl->m, r = (ms[i].target-MST_AROUND1) +1;
+ do {
+ bx = x + rand() % (r*2+1) - r;
+ by = y + rand() % (r*2+1) - r;
+ } while (map_getcell(m, bx, by, CELL_CHKNOPASS) && (i++) < 1000);
+ if (i < 1000){
+ x = bx; y = by;
+ }
+ }
+ // ‘ŠŽè‚ÌŽüˆÍ
+ if (ms[i].target >= MST_AROUND5) {
+ int bx = x, by = y, i = 0, m = bl->m, r = (ms[i].target-MST_AROUND5) + 1;
+ do {
+ bx = x + rand() % (r*2+1) - r;
+ by = y + rand() % (r*2+1) - r;
+ } while (map_getcell(m, bx, by, CELL_CHKNOPASS) && (i++) < 1000);
+ if (i < 1000){
+ x = bx; y = by;
+ }
+ }
+ if (!mobskill_use_pos(md, x, y, i))
+ return 0;
+ } else {
+ // IDŽw’è
+ if (ms[i].target <= MST_MASTER) {
+ struct block_list *bl;
+ switch (ms[i].target) {
+ case MST_TARGET:
+ bl = map_id2bl(md->target_id);
+ break;
+ case MST_MASTER:
+ bl = &md->bl;
+ if (md->master_id)
+ bl = map_id2bl(md->master_id);
+ if (bl) //Otherwise, fall through.
+ break;
+ case MST_FRIEND:
+ if (fbl) {
+ bl = fbl;
+ break;
+ } else if (fmd) {
+ bl = &fmd->bl;
+ break;
+ } // else fall through
+ default:
+ bl = &md->bl;
+ break;
+ }
+ if (bl && !mobskill_use_id(md, bl, i))
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Skill use event processing
+ *------------------------------------------
+ */
+int mobskill_event(struct mob_data *md, int flag)
+{
+ int tick = gettick();
+ nullpo_retr(0, md);
+
+ if (flag == -1 && mobskill_use(md, tick, MSC_CASTTARGETED))
+ return 1;
+ if ((flag & BF_SHORT) && mobskill_use(md, tick, MSC_CLOSEDATTACKED))
+ return 1;
+ if ((flag & BF_LONG) && mobskill_use(md, tick, MSC_LONGRANGEATTACKED))
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹—pƒ^ƒCƒ}[íœ
+ *------------------------------------------
+ */
+int mobskill_deltimer(struct mob_data *md )
+{
+ nullpo_retr(0, md);
+
+ if( md->skilltimer!=-1 ){
+ if( skill_get_inf( md->skillid )& INF_GROUND_SKILL )
+ delete_timer( md->skilltimer, mobskill_castend_pos );
+ else
+ delete_timer( md->skilltimer, mobskill_castend_id );
+ md->skilltimer=-1;
+ }
+ return 0;
+}
+
+// Player cloned mobs. [Valaris]
+int mob_is_clone(int class_)
+{
+ if(class_ >= MOB_CLONE_START && class_ <= MOB_CLONE_END)
+ return 1;
+
+ return 0;
+}
+
+//Flag values:
+//&1: Set special ai (fight mobs, not players)
+//If mode is not passed, a default aggressive mode is used.
+//If master_id is passed, clone is attached to him.
+//Returns: ID of newly crafted copy.
+int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration)
+{
+ int class_;
+ int i,j,inf,skill_id;
+ struct mob_skill *ms;
+
+ nullpo_retr(0, sd);
+
+ for(class_=MOB_CLONE_START; class_<MOB_CLONE_END; class_++){
+ if(mob_db_data[class_]==NULL)
+ break;
+ }
+
+ if(class_>MOB_CLONE_END)
+ return 0;
+
+ mob_db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db));
+ mob_db_data[class_]->view_class=sd->status.class_;
+ sprintf(mob_db_data[class_]->name,sd->status.name);
+ sprintf(mob_db_data[class_]->jname,sd->status.name);
+ mob_db_data[class_]->lv=status_get_lv(&sd->bl);
+ mob_db_data[class_]->max_hp=status_get_max_hp(&sd->bl);
+ mob_db_data[class_]->max_sp=0;
+ mob_db_data[class_]->base_exp=1;
+ mob_db_data[class_]->job_exp=1;
+ mob_db_data[class_]->range=status_get_range(&sd->bl);
+ mob_db_data[class_]->atk1=status_get_batk(&sd->bl); //Base attack as minimum damage.
+ mob_db_data[class_]->atk2=mob_db_data[class_]->atk1 + status_get_atk(&sd->bl)+status_get_atk2(&sd->bl); //batk + weapon dmg
+ mob_db_data[class_]->def=status_get_def(&sd->bl);
+ mob_db_data[class_]->mdef=status_get_mdef(&sd->bl);
+ mob_db_data[class_]->str=status_get_str(&sd->bl);
+ mob_db_data[class_]->agi=status_get_agi(&sd->bl);
+ mob_db_data[class_]->vit=status_get_vit(&sd->bl);
+ mob_db_data[class_]->int_=status_get_int(&sd->bl);
+ mob_db_data[class_]->dex=status_get_dex(&sd->bl);
+ mob_db_data[class_]->luk=status_get_luk(&sd->bl);
+ mob_db_data[class_]->range2=AREA_SIZE*2/3; //Chase area of 2/3rds of a screen.
+ mob_db_data[class_]->range3=AREA_SIZE; //Let them have the same view-range as players.
+ mob_db_data[class_]->size=status_get_size(&sd->bl);
+ mob_db_data[class_]->race=status_get_race(&sd->bl);
+ mob_db_data[class_]->element=status_get_element(&sd->bl);
+ mob_db_data[class_]->mode=mode?mode:(MD_AGGRESSIVE|MD_ASSIST|MD_CANATTACK|MD_CANMOVE);
+ mob_db_data[class_]->speed=status_get_speed(&sd->bl);
+ mob_db_data[class_]->adelay=status_get_adelay(&sd->bl);
+ mob_db_data[class_]->amotion=status_get_amotion(&sd->bl);
+ mob_db_data[class_]->dmotion=status_get_dmotion(&sd->bl);
+ mob_db_data[class_]->sex=sd->status.sex;
+ mob_db_data[class_]->hair=sd->status.hair;
+ mob_db_data[class_]->hair_color=sd->status.hair_color;
+#if PACKETVER < 4
+ mob_db_data[class_]->weapon = sd->status.weapon;
+ mob_db_data[class_]->shield = sd->status.shield;
+#else
+ if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if (sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ mob_db_data[class_]->weapon=sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ mob_db_data[class_]->weapon=sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ mob_db_data[class_]->shield=0;
+ if (sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && sd->view_class != JOB_WEDDING && sd->view_class != JOB_XMAS) {
+ if (sd->inventory_data[sd->equip_index[8]]->view_id > 0)
+ mob_db_data[class_]->shield=sd->inventory_data[sd->equip_index[8]]->view_id;
+ else
+ mob_db_data[class_]->shield=sd->status.inventory[sd->equip_index[8]].nameid;
+ } else
+ mob_db_data[class_]->shield=0;
+#endif
+ mob_db_data[class_]->head_top=sd->status.head_top;
+ mob_db_data[class_]->head_mid=sd->status.head_mid;
+ mob_db_data[class_]->head_buttom=sd->status.head_bottom;
+ mob_db_data[class_]->option=sd->status.option;
+ mob_db_data[class_]->clothes_color=sd->status.clothes_color;
+
+ //Skill copy [Skotlex]
+ ms = &mob_db_data[class_]->skill[0];
+ //Go Backwards to give better priority to advanced skills.
+ for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) {
+ skill_id = skill_tree[sd->status.class_][j].id;
+ if (!skill_id || sd->status.skill[skill_id].lv < 1 || (skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)))
+ continue;
+ memset (&ms[i], 0, sizeof(struct mob_skill));
+ ms[i].skill_id = skill_id;
+ ms[i].skill_lv = sd->status.skill[skill_id].lv;
+ ms[i].state = -1;
+ ms[i].permillage = 500; //Default chance for moving/idle skills.
+ ms[i].emotion = -1;
+ ms[i].cancel = 0;
+ ms[i].delay = 5000+skill_delayfix(&sd->bl,skill_id, ms[i].skill_lv, 0);
+ ms[i].casttime = skill_castfix(&sd->bl,skill_id, ms[i].skill_lv, 0);
+
+ inf = skill_get_inf(skill_id);
+ if (inf&INF_ATTACK_SKILL) {
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ if (skill_get_range(skill_id, ms[i].skill_lv) > 3) {
+ ms[i].state = MSS_RUSH;
+ } else {
+ ms[i].state = MSS_BERSERK;
+ ms[i].permillage = 2500;
+ }
+ } else if(inf&INF_GROUND_SKILL) {
+ //Normal aggressive mob, disable skills that cannot help them fight
+ //against players (those with flags UF_NOMOB and UF_NOPC are specific
+ //to always aid players!) [Skotlex]
+ if (!(flag&1) && skill_get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC))
+ continue;
+ ms[i].permillage = 1000;
+ if (skill_get_inf2(skill_id)&INF2_TRAP) { //Traps!
+ ms[i].state = MSS_IDLE;
+ ms[i].target = MST_AROUND2;
+ ms[i].delay = 60000;
+ } else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ } else { //Target allies
+ ms[i].target = MST_FRIEND;
+ ms[i].cond1 = MSC_FRIENDHPLTMAXRATE;
+ ms[i].cond2 = 95;
+ }
+ } else if (inf&INF_SELF_SKILL) {
+ if (skill_get_nk(skill_id) != NK_NO_DAMAGE) { //Offensive skill
+ ms[i].target = MST_TARGET;
+ ms[i].state = MSS_BERSERK;
+ } else //Self skill
+ ms[i].target = MST_SELF;
+ ms[i].cond1 = MSC_MYHPLTMAXRATE;
+ ms[i].cond2 = 90;
+ ms[i].permillage = 2000;
+ //Delay: Remove the stock 5 secs and add half of the support time.
+ ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2;
+ if (ms[i].delay < 5000)
+ ms[i].delay = 5000; //With a minimum of 5 secs.
+ } else if (inf&INF_SUPPORT_SKILL) {
+ ms[i].target = MST_FRIEND;
+ ms[i].cond1 = MSC_FRIENDHPLTMAXRATE;
+ ms[i].cond2 = 90;
+ if (skill_id == AL_HEAL)
+ ms[i].permillage = 5000; //Higher skill rate usage for heal.
+ else if (skill_id == ALL_RESURRECTION)
+ ms[i].cond2 = 1;
+ //Delay: Remove the stock 5 secs and add half of the support time.
+ ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2;
+ if (ms[i].delay < 2000)
+ ms[i].delay = 2000; //With a minimum of 2 secs.
+
+ if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self.
+ memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill));
+ mob_db_data[class_]->maxskill = ++i;
+ ms[i].target = MST_SELF;
+ ms[i].cond1 = MSC_MYHPLTMAXRATE;
+ }
+ } else {
+ switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered.
+ case MO_TRIPLEATTACK:
+ case TF_DOUBLE:
+ ms[i].state = MSS_BERSERK;
+ ms[i].target = MST_TARGET;
+ ms[i].cond1 = MSC_ALWAYS;
+ ms[i].permillage = skill_id==TF_DOUBLE?(ms[i].skill_lv*500):(3000-ms[i].skill_lv*100);
+ ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits".
+ break;
+ default: //Untreated Skill
+ continue;
+ }
+ }
+ if (battle_config.mob_skill_rate!= 100)
+ ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100;
+ if (battle_config.mob_skill_delay != 100)
+ ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100;
+
+ mob_db_data[class_]->maxskill = ++i;
+ }
+ //Finally, spawn it.
+ i = mob_once_spawn(sd,(char*)map,x,y,"--en--",class_,1,event);
+ if ((master_id || flag || duration) && i) { //Further manipulate crafted char.
+ struct mob_data* md = (struct mob_data*)map_id2bl(i);
+ if (md && md->bl.type == BL_MOB) {
+ if (flag&1) //Friendly Character
+ md->special_state.ai = 1;
+ if (master_id) //Attach to Master
+ md->master_id = master_id;
+ if (duration) //Auto Delete after a while.
+ md->deletetimer = add_timer (gettick() + duration, mob_timer_delete, i, 0);
+ }
+#if 0
+ //I am playing with this for packet-research purposes, enable it if you want, but don't remove it :X [Skotlex]
+ //Guardian data
+ if (sd->status.guild_id) {
+ struct guild* g = guild_search(sd->status.guild_id);
+ md->guardian_data = aCalloc(1, sizeof(struct guardian_data));
+ md->guardian_data->castle = NULL;
+ md->guardian_data->number = MAX_GUARDIANS;
+ md->guardian_data->guild_id = sd->status.guild_id;
+ if (g)
+ {
+ md->guardian_data->emblem_id = g->emblem_id;
+ memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH);
+ }
+ }
+#endif
+ }
+
+ return i;
+}
+
+int mob_clone_delete(int class_)
+{
+ int i;
+ for(i=MOB_CLONE_START; i<MOB_CLONE_END; i++){
+ if(i==class_ && mob_db_data[i]!=NULL){
+ aFree(mob_db_data[i]);
+ mob_db_data[i]=NULL;
+ break;
+ }
+ }
+ return 0;
+}
+
+//
+// ‰Šú‰»
+//
+/*==========================================
+ * Since un-setting [ mob ] up was used, it is an initial provisional value setup.
+ *------------------------------------------
+ */
+static int mob_makedummymobdb(int class_)
+{
+ if (mob_dummy != NULL)
+ {
+ if (mob_db(class_) == mob_dummy)
+ return 1; //Using the mob_dummy data already. [Skotlex]
+ if (class_ > 0 && class_ <= MAX_MOB_DB)
+ { //Remove the mob data so that it uses the dummy data instead.
+ aFree(mob_db_data[class_]);
+ mob_db_data[class_] = NULL;
+ }
+ return 0;
+ }
+ //Initialize dummy data.
+ mob_dummy = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); //Initializing the dummy mob.
+ sprintf(mob_dummy->name,"DUMMY");
+ sprintf(mob_dummy->jname,"Dummy");
+ mob_dummy->lv=1;
+ mob_dummy->max_hp=1000;
+ mob_dummy->max_sp=1;
+ mob_dummy->base_exp=2;
+ mob_dummy->job_exp=1;
+ mob_dummy->range=1;
+ mob_dummy->atk1=7;
+ mob_dummy->atk2=10;
+ mob_dummy->def=0;
+ mob_dummy->mdef=0;
+ mob_dummy->str=1;
+ mob_dummy->agi=1;
+ mob_dummy->vit=1;
+ mob_dummy->int_=1;
+ mob_dummy->dex=6;
+ mob_dummy->luk=2;
+ mob_dummy->range2=10;
+ mob_dummy->range3=10;
+ mob_dummy->race=0;
+ mob_dummy->element=0;
+ mob_dummy->mode=0;
+ mob_dummy->speed=300;
+ mob_dummy->adelay=1000;
+ mob_dummy->amotion=500;
+ mob_dummy->dmotion=500;
+ return 0;
+}
+
+//Adjusts the drop rate of item according to the criteria given. [Skotlex]
+static int mob_drop_adjust(int rate, int rate_adjust, int rate_min, int rate_max)
+{
+ if (battle_config.logarithmic_drops && rate_adjust > 0) //Logarithmic drops equation by Ishizu-Chan
+ //Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5))
+ //x is the normal Droprate, y is the Modificator.
+ rate = (int)(rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5);
+ else //Classical linear rate adjustment.
+ rate = rate*rate_adjust/100;
+ return (rate>rate_max)?rate_max:((rate<rate_min)?rate_min:rate);
+}
+/*==========================================
+ * mob_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ char *filename[]={ "mob_db.txt","mob_db2.txt" };
+ int class_, i, fi;
+
+ for(fi=0;fi<2;fi++){
+ sprintf(line, "%s/%s", db_path, filename[fi]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(fi>0)
+ continue;
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ double exp, maxhp;
+ char *str[60], *p, *np; // 55->60 Lupus
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ for(i=0,p=line;i<60;i++){
+ if((np=strchr(p,','))!=NULL){
+ str[i]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[i]=p;
+ }
+
+ class_ = atoi(str[0]);
+ if (class_ == 0)
+ continue; //Leave blank lines alone... [Skotlex]
+
+ if (class_ <= 1000 || class_ > MAX_MOB_DB)
+ {
+ ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB);
+ continue;
+ } else if (pcdb_checkid(class_))
+ {
+ ShowWarning("Mob with ID: %d not loaded. That ID is reserved for player classes.\n");
+ continue;
+ }
+ if (mob_db_data[class_] == NULL)
+ mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data));
+
+ mob_db_data[class_]->view_class = class_;
+ memcpy(mob_db_data[class_]->name, str[1], NAME_LENGTH-1);
+ memcpy(mob_db_data[class_]->jname, str[2], NAME_LENGTH-1);
+ mob_db_data[class_]->lv = atoi(str[3]);
+ mob_db_data[class_]->max_hp = atoi(str[4]);
+ mob_db_data[class_]->max_sp = atoi(str[5]);
+
+ exp = (double)atoi(str[6]) * (double)battle_config.base_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->base_exp = (int)exp;
+
+ exp = (double)atoi(str[7]) * (double)battle_config.job_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->job_exp = (int)exp;
+
+ mob_db_data[class_]->range=atoi(str[8]);
+ mob_db_data[class_]->atk1=atoi(str[9]);
+ mob_db_data[class_]->atk2=atoi(str[10]);
+ mob_db_data[class_]->def=atoi(str[11]);
+ mob_db_data[class_]->mdef=atoi(str[12]);
+ mob_db_data[class_]->str=atoi(str[13]);
+ mob_db_data[class_]->agi=atoi(str[14]);
+ mob_db_data[class_]->vit=atoi(str[15]);
+ mob_db_data[class_]->int_=atoi(str[16]);
+ mob_db_data[class_]->dex=atoi(str[17]);
+ mob_db_data[class_]->luk=atoi(str[18]);
+ mob_db_data[class_]->range2=atoi(str[19]);
+ mob_db_data[class_]->range3=atoi(str[20]);
+ mob_db_data[class_]->size=atoi(str[21]);
+ mob_db_data[class_]->race=atoi(str[22]);
+ mob_db_data[class_]->element=atoi(str[23]);
+ mob_db_data[class_]->mode=atoi(str[24]);
+ mob_db_data[class_]->speed=atoi(str[25]);
+ mob_db_data[class_]->adelay=atoi(str[26]);
+ mob_db_data[class_]->amotion=atoi(str[27]);
+ mob_db_data[class_]->dmotion=atoi(str[28]);
+
+ for(i=0;i<10;i++){ // 8 -> 10 Lupus
+ int rate = 0,rate_adjust,type,ratemin,ratemax;
+ struct item_data *id;
+ mob_db_data[class_]->dropitem[i].nameid=atoi(str[29+i*2]);
+ type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid);
+ rate = atoi(str[30+i*2]);
+ if (class_ >= 1324 && class_ <= 1363)
+ { //Treasure box drop rates [Skotlex]
+ rate_adjust = battle_config.item_rate_treasure;
+ ratemin = battle_config.item_drop_treasure_min;
+ ratemax = battle_config.item_drop_treasure_max;
+ }
+ else switch (type)
+ {
+ case 0:
+ rate_adjust = battle_config.item_rate_heal;
+ ratemin = battle_config.item_drop_heal_min;
+ ratemax = battle_config.item_drop_heal_max;
+ break;
+ case 2:
+ rate_adjust = battle_config.item_rate_use;
+ ratemin = battle_config.item_drop_use_min;
+ ratemax = battle_config.item_drop_use_max;
+ break;
+ case 4:
+ case 5:
+ case 8: // Changed to include Pet Equip
+ rate_adjust = battle_config.item_rate_equip;
+ ratemin = battle_config.item_drop_equip_min;
+ ratemax = battle_config.item_drop_equip_max;
+ break;
+ case 6:
+ rate_adjust = battle_config.item_rate_card;
+ ratemin = battle_config.item_drop_card_min;
+ ratemax = battle_config.item_drop_card_max;
+ break;
+ default:
+ rate_adjust = battle_config.item_rate_common;
+ ratemin = battle_config.item_drop_common_min;
+ ratemax = battle_config.item_drop_common_max;
+ break;
+ }
+ mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
+
+ //calculate and store Max available drop chance of the item
+ id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
+ if (mob_db_data[class_]->dropitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->dropitem[i].p;
+ }
+ }
+ }
+ // MVP EXP Bonus, Chance: MEXP,ExpPer
+ mob_db_data[class_]->mexp=atoi(str[49])*battle_config.mvp_exp_rate/100;
+ mob_db_data[class_]->mexpper=atoi(str[50]);
+ //Now that we know if it is an mvp or not,
+ //apply battle_config modifiers [Skotlex]
+ maxhp = (double)mob_db_data[class_]->max_hp;
+ if (mob_db_data[class_]->mexp > 0)
+ { //Mvp
+ if (battle_config.mvp_hp_rate != 100)
+ maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.;
+ } else if (battle_config.monster_hp_rate != 100) //Normal mob
+ maxhp = maxhp * (double)battle_config.monster_hp_rate /100.;
+ if (maxhp < 1) maxhp = 1;
+ else if (maxhp > 0x7fffffff) maxhp = 0x7fffffff;
+ mob_db_data[class_]->max_hp = (int)maxhp;
+
+ // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
+ for(i=0;i<3;i++){
+ struct item_data *id;
+ int rate=atoi(str[52+i*2]);
+ mob_db_data[class_]->mvpitem[i].nameid=atoi(str[51+i*2]);
+ mob_db_data[class_]->mvpitem[i].p= mob_drop_adjust(rate, battle_config.item_rate_mvp,
+ battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
+
+ //calculate and store Max available drop chance of the MVP item
+ id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
+ if (mob_db_data[class_]->mvpitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate
+ }
+ }
+ }
+
+ if (mob_db_data[class_]->max_hp <= 0) {
+ ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->jname);
+ mob_makedummymobdb(class_);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[fi]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * MOB display graphic change data reading
+ *------------------------------------------
+ */
+static int mob_readdb_mobavail(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int class_,j,k;
+ char *str[20],*p,*np;
+
+ sprintf(line, "%s/mob_avail.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+
+ for(j=0,p=line;j<12;j++){
+ if((np=strchr(p,','))!=NULL){
+ str[j]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[j]=p;
+ }
+
+ if(str[0]==NULL)
+ continue;
+
+ class_=atoi(str[0]);
+ if (class_ == 0)
+ continue; //Leave blank lines alone... [Skotlex]
+
+ if(mob_db(class_) == mob_dummy) // ’l‚ªˆÙí‚Ȃ爗‚µ‚È‚¢B
+ continue;
+
+ k=atoi(str[1]);
+ if(k < 0)
+ continue;
+ if (j > 3 && k > 23 && k < 69)
+ k += 3977; // advanced job/baby class
+ mob_db_data[class_]->view_class=k;
+
+ //Player sprites
+ if(pcdb_checkid(k)) {
+ mob_db_data[class_]->sex=atoi(str[2]);
+ mob_db_data[class_]->hair=atoi(str[3]);
+ mob_db_data[class_]->hair_color=atoi(str[4]);
+ mob_db_data[class_]->weapon=atoi(str[5]);
+ mob_db_data[class_]->shield=atoi(str[6]);
+ mob_db_data[class_]->head_top=atoi(str[7]);
+ mob_db_data[class_]->head_mid=atoi(str[8]);
+ mob_db_data[class_]->head_buttom=atoi(str[9]);
+ mob_db_data[class_]->option=atoi(str[10])&~0x46;
+ mob_db_data[class_]->clothes_color=atoi(str[11]); // Monster player dye option - Valaris
+ }
+ else if(atoi(str[2]) > 0) mob_db_data[class_]->equip=atoi(str[2]); // mob equipment [Valaris]
+
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"mob_avail.txt");
+ return 0;
+}
+
+/*==========================================
+ * Reading of random monster data
+ *------------------------------------------
+ */
+static int mob_read_randommonster(void)
+{
+ FILE *fp;
+ char line[1024];
+ char *str[10],*p;
+ int i,j;
+
+ const char* mobfile[] = {
+ "mob_branch.txt",
+ "mob_poring.txt",
+ "mob_boss.txt" };
+
+ for(i=0;i<MAX_RANDOMMONSTER;i++){
+ mob_db_data[0]->summonper[i] = 1002; // ݒ肵–Y‚ꂽꇂ̓|ƒŠƒ“‚ªo‚é‚悤‚É‚µ‚Ä‚¨‚­
+ sprintf(line, "%s/%s", db_path, mobfile[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n",line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ int class_,per;
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<3 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ if(str[0]==NULL || str[2]==NULL)
+ continue;
+
+ class_ = atoi(str[0]);
+ per=atoi(str[2]);
+ if(mob_db(class_) != mob_dummy)
+ mob_db_data[class_]->summonper[i]=per;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",mobfile[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * mob_skill_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readskilldb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int i;
+
+ const struct {
+ char str[32];
+ int id;
+ } cond1[] = {
+ { "always", MSC_ALWAYS },
+ { "myhpltmaxrate", MSC_MYHPLTMAXRATE },
+ { "friendhpltmaxrate",MSC_FRIENDHPLTMAXRATE },
+ { "mystatuson", MSC_MYSTATUSON },
+ { "mystatusoff", MSC_MYSTATUSOFF },
+ { "friendstatuson", MSC_FRIENDSTATUSON },
+ { "friendstatusoff", MSC_FRIENDSTATUSOFF },
+ { "attackpcgt", MSC_ATTACKPCGT },
+ { "attackpcge", MSC_ATTACKPCGE },
+ { "slavelt", MSC_SLAVELT },
+ { "slavele", MSC_SLAVELE },
+ { "closedattacked", MSC_CLOSEDATTACKED },
+ { "longrangeattacked",MSC_LONGRANGEATTACKED },
+ { "skillused", MSC_SKILLUSED },
+ { "afterskill", MSC_AFTERSKILL },
+ { "casttargeted", MSC_CASTTARGETED },
+ { "rudeattacked", MSC_RUDEATTACKED },
+ { "masterhpltmaxrate",MSC_MASTERHPLTMAXRATE },
+ { "masterattacked", MSC_MASTERATTACKED },
+ { "alchemist", MSC_ALCHEMIST },
+ }, cond2[] ={
+ { "anybad", -1 },
+ { "stone", SC_STONE },
+ { "freeze", SC_FREEZE },
+ { "stan", SC_STAN },
+ { "sleep", SC_SLEEP },
+ { "poison", SC_POISON },
+ { "curse", SC_CURSE },
+ { "silence", SC_SILENCE },
+ { "confusion", SC_CONFUSION },
+ { "blind", SC_BLIND },
+ { "hiding", SC_HIDING },
+ { "sight", SC_SIGHT },
+ }, state[] = {
+ { "any", -1 },
+ { "idle", MSS_IDLE },
+ { "walk", MSS_WALK },
+ { "loot", MSS_LOOT },
+ { "dead", MSS_DEAD },
+ { "attack", MSS_BERSERK }, //Retaliating attack
+ { "angry", MSS_ANGRY }, //Preemptive attack (aggressive mobs)
+ { "chase", MSS_RUSH }, //Chase escaping target
+ { "follow", MSS_FOLLOW }, //Preemptive chase (aggressive mobs)
+ }, target[] = {
+ { "target", MST_TARGET },
+ { "self", MST_SELF },
+ { "friend", MST_FRIEND },
+ { "master", MST_MASTER },
+ { "around5", MST_AROUND5 },
+ { "around6", MST_AROUND6 },
+ { "around7", MST_AROUND7 },
+ { "around8", MST_AROUND8 },
+ { "around1", MST_AROUND1 },
+ { "around2", MST_AROUND2 },
+ { "around3", MST_AROUND3 },
+ { "around4", MST_AROUND4 },
+ { "around", MST_AROUND },
+ };
+
+ int x;
+ char *filename[]={ "mob_skill_db.txt","mob_skill_db2.txt" };
+
+ if (!battle_config.mob_skill_rate) {
+ ShowStatus("Mob skill use disabled. Not reading mob skills.\n");
+ return 0;
+ }
+ for(x=0;x<2;x++){
+ sprintf(line, "%s/%s", db_path, filename[x]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(x==0)
+ ShowError("can't read %s\n",line);
+ continue;
+ }
+ while(fgets(line,1020,fp)){
+ char *sp[20],*p;
+ int mob_id;
+ struct mob_skill *ms, gms;
+ int j=0;
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ memset(sp,0,sizeof(sp));
+ for(i=0,p=line;i<18 && p;i++){
+ sp[i]=p;
+ if((p=strchr(p,','))!=NULL)
+ *p++=0;
+ }
+ if(i == 0 || (mob_id=atoi(sp[0]))== 0 || (mob_id > 0 && mob_db(mob_id) == mob_dummy))
+ continue;
+ if(i < 18) {
+ ShowError("Insufficient number of fields for Mob Skill (Mob ID[%s], Name[%s], Skill:[%s/Lv%s])\n", sp[0], i>1?sp[1]:"?", i>3?sp[3]:"?", i>4?sp[4]:"?");
+ continue;
+ }
+ if( strcmp(sp[1],"clear")==0 ){
+ if (mob_id < 0)
+ continue;
+ memset(mob_db_data[mob_id]->skill,0,sizeof(struct mob_skill));
+ mob_db_data[mob_id]->maxskill=0;
+ continue;
+ }
+
+ if (mob_id < 0)
+ { //Prepare global skill. [Skotlex]
+ memset(&gms, 0, sizeof (struct mob_skill));
+ ms = &gms;
+ } else {
+ for(i=0;i<MAX_MOBSKILL;i++)
+ if( (ms=&mob_db_data[mob_id]->skill[i])->skill_id == 0)
+ break;
+ if(i==MAX_MOBSKILL){
+ ShowWarning("mob_skill: readdb: too many skill ! [%s] in %d[%s]\n",
+ sp[1],mob_id,mob_db_data[mob_id]->jname);
+ continue;
+ }
+ }
+
+ ms->state=atoi(sp[2]);
+ for(j=0;j<sizeof(state)/sizeof(state[0]);j++){
+ if( strcmp(sp[2],state[j].str)==0)
+ ms->state=state[j].id;
+ }
+
+ //Skill ID
+ j=atoi(sp[3]);
+ if (j<=0 || j>MAX_SKILL_DB) //fixed Lupus
+ {
+ if (mob_id < 0)
+ ShowWarning("Invalid Skill ID (%d) for all mobs\n", j);
+ else
+ ShowWarning("Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->jname);
+ continue;
+ }
+ ms->skill_id=j;
+ //Skill lvl
+ j= atoi(sp[4])<=0 ? 1 : atoi(sp[4]);
+ ms->skill_lv= j>battle_config.mob_max_skilllvl ? battle_config.mob_max_skilllvl : j; //we strip max skill level
+
+ //Apply battle_config modifiers to rate (permillage) and delay [Skotlex]
+ ms->permillage=atoi(sp[5]);
+ if (battle_config.mob_skill_rate != 100)
+ ms->permillage = ms->permillage*battle_config.mob_skill_rate/100;
+ ms->casttime=atoi(sp[6]);
+ ms->delay=atoi(sp[7]);
+ if (battle_config.mob_skill_delay != 100)
+ ms->delay = ms->delay*battle_config.mob_skill_delay/100;
+ ms->cancel=atoi(sp[8]);
+ if( strcmp(sp[8],"yes")==0 ) ms->cancel=1;
+ ms->target=atoi(sp[9]);
+ for(j=0;j<sizeof(target)/sizeof(target[0]);j++){
+ if( strcmp(sp[9],target[j].str)==0)
+ ms->target=target[j].id;
+ }
+ ms->cond1=-1;
+ for(j=0;j<sizeof(cond1)/sizeof(cond1[0]);j++){
+ if( strcmp(sp[10],cond1[j].str)==0)
+ ms->cond1=cond1[j].id;
+ }
+ ms->cond2=atoi(sp[11]);
+ for(j=0;j<sizeof(cond2)/sizeof(cond2[0]);j++){
+ if( strcmp(sp[11],cond2[j].str)==0)
+ ms->cond2=cond2[j].id;
+ }
+ ms->val[0]=atoi(sp[12]);
+ ms->val[1]=atoi(sp[13]);
+ ms->val[2]=atoi(sp[14]);
+ ms->val[3]=atoi(sp[15]);
+ ms->val[4]=atoi(sp[16]);
+ if(sp[17] != NULL && strlen(sp[17])>2)
+ ms->emotion=atoi(sp[17]);
+ else
+ ms->emotion=-1;
+ if (mob_id < 0)
+ { //Set this skill to ALL mobs. [Skotlex]
+ mob_id *= -1;
+ for (i = 1; i < MAX_MOB_DB; i++)
+ {
+ if (mob_db_data[i] == NULL)
+ continue;
+ if (mob_db_data[i]->mode&MD_BOSS)
+ {
+ if (!(mob_id&2)) //Skill not for bosses
+ continue;
+ } else
+ if (!(mob_id&1)) //Skill not for normal enemies.
+ continue;
+
+ for(j=0;j<MAX_MOBSKILL;j++)
+ if( mob_db_data[i]->skill[j].skill_id == 0)
+ break;
+ if(j==MAX_MOBSKILL)
+ continue;
+
+ memcpy (&mob_db_data[i]->skill[j], ms, sizeof(struct mob_skill));
+ mob_db_data[i]->maxskill=j+1;
+ }
+ } else //Skill set on a single mob.
+ mob_db_data[mob_id]->maxskill=i+1;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[x]);
+ }
+ return 0;
+}
+/*==========================================
+ * mob_race_db.txt reading
+ *------------------------------------------
+ */
+static int mob_readdb_race(void)
+{
+ FILE *fp;
+ char line[1024];
+ int race,j,k;
+ char *str[20],*p,*np;
+
+ sprintf(line, "%s/mob_race2_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+
+ for(j=0,p=line;j<12;j++){
+ if((np=strchr(p,','))!=NULL){
+ str[j]=p;
+ *np=0;
+ p=np+1;
+ } else
+ str[j]=p;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ race=atoi(str[0]);
+ if (race < 0 || race >= MAX_MOB_RACE_DB)
+ continue;
+
+ for (j=1; j<20; j++) {
+ if (!str[j])
+ break;
+ k=atoi(str[j]);
+ if (mob_db(k) == mob_dummy)
+ continue;
+ mob_db_data[k]->race2 = race;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","mob_race2_db.txt");
+ return 0;
+}
+
+#ifndef TXT_ONLY
+/*==========================================
+ * SQL reading
+ *------------------------------------------
+ */
+static int mob_read_sqldb(void)
+{
+ const char unknown_str[NAME_LENGTH] ="unknown";
+ int i, fi, class_;
+ double exp, maxhp;
+ long unsigned int ln = 0;
+ char *mob_db_name[] = { mob_db_db, mob_db2_db };
+
+ //For easier handling of converting. [Skotlex]
+#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a]))
+#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a])
+
+ for (fi = 0; fi < 2; fi++) {
+ sprintf (tmp_sql, "SELECT * FROM `%s`", mob_db_name[fi]);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", mob_db_name[fi], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ continue;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ class_ = TO_INT(0);
+ if (class_ <= 1000 || class_ > MAX_MOB_DB)
+ {
+ ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB);
+ continue;
+ } else if (pcdb_checkid(class_))
+ {
+ ShowWarning("Mob with ID: %d not loaded. That ID is reserved for Upper Classes.\n");
+ continue;
+ }
+ if (mob_db_data[class_] == NULL)
+ mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data));
+
+ ln++;
+
+ mob_db_data[class_]->view_class = class_;
+ memcpy(mob_db_data[class_]->name, TO_STR(1), NAME_LENGTH-1);
+ memcpy(mob_db_data[class_]->jname, TO_STR(2), NAME_LENGTH-1);
+ mob_db_data[class_]->lv = TO_INT(3);
+ mob_db_data[class_]->max_hp = TO_INT(4);
+ mob_db_data[class_]->max_sp = TO_INT(5);
+
+ exp = (double)TO_INT(6) * (double)battle_config.base_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->base_exp = (int)exp;
+
+ exp = (double)TO_INT(7) * (double)battle_config.job_exp_rate / 100.;
+ if (exp < 0) exp = 0;
+ else if (exp > 0x7fffffff) exp = 0x7fffffff;
+ mob_db_data[class_]->job_exp = (int)exp;
+
+ mob_db_data[class_]->range = TO_INT(8);
+ mob_db_data[class_]->atk1 = TO_INT(9);
+ mob_db_data[class_]->atk2 = TO_INT(10);
+ mob_db_data[class_]->def = TO_INT(11);
+ mob_db_data[class_]->mdef = TO_INT(12);
+ mob_db_data[class_]->str = TO_INT(13);
+ mob_db_data[class_]->agi = TO_INT(14);
+ mob_db_data[class_]->vit = TO_INT(15);
+ mob_db_data[class_]->int_ = TO_INT(16);
+ mob_db_data[class_]->dex = TO_INT(17);
+ mob_db_data[class_]->luk = TO_INT(18);
+ mob_db_data[class_]->range2 = TO_INT(19);
+ mob_db_data[class_]->range3 = TO_INT(20);
+ mob_db_data[class_]->size = TO_INT(21);
+ mob_db_data[class_]->race = TO_INT(22);
+ mob_db_data[class_]->element = TO_INT(23);
+ mob_db_data[class_]->mode = TO_INT(24);
+ mob_db_data[class_]->speed = TO_INT(25);
+ mob_db_data[class_]->adelay = TO_INT(26);
+ mob_db_data[class_]->amotion = TO_INT(27);
+ mob_db_data[class_]->dmotion = TO_INT(28);
+
+ for (i = 0; i < 10; i++){ // 8 -> 10 Lupus
+ int rate = 0, rate_adjust, type, ratemin, ratemax;
+ struct item_data *id;
+ mob_db_data[class_]->dropitem[i].nameid=TO_INT(29+i*2);
+ type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid);
+ rate = TO_INT(30+i*2);
+ if (class_ >= 1324 && class_ <= 1363)
+ { //Treasure box drop rates [Skotlex]
+ rate_adjust = battle_config.item_rate_treasure;
+ ratemin = battle_config.item_drop_treasure_min;
+ ratemax = battle_config.item_drop_treasure_max;
+ }
+ else switch(type)
+ {
+ case 0: // Added by Valaris
+ rate_adjust = battle_config.item_rate_heal;
+ ratemin = battle_config.item_drop_heal_min;
+ ratemax = battle_config.item_drop_heal_max;
+ break;
+ case 2:
+ rate_adjust = battle_config.item_rate_use;
+ ratemin = battle_config.item_drop_use_min;
+ ratemax = battle_config.item_drop_use_max; // End
+ break;
+ case 4:
+ case 5:
+ case 8: // Changed to include Pet Equip
+ rate_adjust = battle_config.item_rate_equip;
+ ratemin = battle_config.item_drop_equip_min;
+ ratemax = battle_config.item_drop_equip_max;
+ break;
+ case 6:
+ rate_adjust = battle_config.item_rate_card;
+ ratemin = battle_config.item_drop_card_min;
+ ratemax = battle_config.item_drop_card_max;
+ break;
+ default:
+ rate_adjust = battle_config.item_rate_common;
+ ratemin = battle_config.item_drop_common_min;
+ ratemax = battle_config.item_drop_common_max;
+ break;
+ }
+ mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
+
+ //calculate and store Max available drop chance of the item
+ id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
+ if (mob_db_data[class_]->dropitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->dropitem[i].p;
+ }
+ }
+ }
+ // MVP EXP Bonus, Chance: MEXP,ExpPer
+ mob_db_data[class_]->mexp = TO_INT(49) * battle_config.mvp_exp_rate / 100;
+ mob_db_data[class_]->mexpper = TO_INT(50);
+ //Now that we know if it is an mvp or not,
+ //apply battle_config modifiers [Skotlex]
+ maxhp = (double)mob_db_data[class_]->max_hp;
+ if (mob_db_data[class_]->mexp > 0)
+ { //Mvp
+ if (battle_config.mvp_hp_rate != 100)
+ maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.;
+ } else if (battle_config.monster_hp_rate != 100) //Normal mob
+ maxhp = maxhp * (double)battle_config.monster_hp_rate /100.;
+ if (maxhp < 0) maxhp = 1;
+ else if (maxhp > 0x7fffffff) maxhp = 0x7fffffff;
+ mob_db_data[class_]->max_hp = (int)maxhp;
+
+ // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
+ for (i=0; i<3; i++) {
+ struct item_data *id;
+ mob_db_data[class_]->mvpitem[i].nameid = TO_INT(51+i*2);
+ mob_db_data[class_]->mvpitem[i].p = mob_drop_adjust(TO_INT(52+i*2),
+ battle_config.item_rate_mvp, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
+
+ //calculate and store Max available drop chance of the MVP item
+ id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
+ if (mob_db_data[class_]->mvpitem[i].p) {
+ if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) {
+ //item has bigger drop chance or sold in shops
+ id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate
+ }
+ }
+ }
+ if (mob_db_data[class_]->max_hp <= 0) {
+ ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->jname);
+ mob_makedummymobdb(class_);
+ }
+ }
+
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, mob_db_name[fi]);
+ ln = 0;
+ }
+ }
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+void mob_reload(void)
+{
+ int i;
+#ifndef TXT_ONLY
+ if(db_use_sqldbs)
+ mob_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ mob_readdb();
+
+ mob_readdb_mobavail();
+ mob_read_randommonster();
+
+ //Mob skills need to be cleared before re-reading them. [Skotlex]
+ for (i = 0; i < MAX_MOB_DB; i++)
+ if (mob_db_data[i])
+ {
+ memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill));
+ mob_db_data[i]->maxskill=0;
+ }
+ mob_readskilldb();
+ mob_readdb_race();
+}
+
+/*==========================================
+ * Circumference initialization of mob
+ *------------------------------------------
+ */
+int do_init_mob(void)
+{ //Initialize the mob database
+ memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array
+ mob_db_data[0] = aCalloc(1, sizeof (struct mob_data)); //This mob is used for random spawns
+ mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob
+
+#ifndef TXT_ONLY
+ if(db_use_sqldbs)
+ mob_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ mob_readdb();
+
+ mob_readdb_mobavail();
+ mob_read_randommonster();
+ mob_readskilldb();
+ mob_readdb_race();
+
+ add_timer_func_list(mob_timer,"mob_timer");
+ add_timer_func_list(mob_delayspawn,"mob_delayspawn");
+ add_timer_func_list(mob_delay_item_drop,"mob_delay_item_drop");
+ add_timer_func_list(mob_ai_hard,"mob_ai_hard");
+ add_timer_func_list(mob_ai_lazy,"mob_ai_lazy");
+ add_timer_func_list(mobskill_castend_id,"mobskill_castend_id");
+ add_timer_func_list(mobskill_castend_pos,"mobskill_castend_pos");
+ add_timer_func_list(mob_timer_delete,"mob_timer_delete");
+ add_timer_func_list(mob_spawn_guardian_sub,"mob_spawn_guardian_sub");
+ add_timer_interval(gettick()+MIN_MOBTHINKTIME,mob_ai_hard,0,0,MIN_MOBTHINKTIME);
+ add_timer_interval(gettick()+MIN_MOBTHINKTIME*10,mob_ai_lazy,0,0,MIN_MOBTHINKTIME*10);
+
+ return 0;
+}
+
+/*==========================================
+ * Clean memory usage.
+ *------------------------------------------
+ */
+int do_final_mob(void)
+{
+ int i;
+ if (mob_dummy)
+ {
+ aFree(mob_dummy);
+ mob_dummy = NULL;
+ }
+ for (i = 0; i <= MAX_MOB_DB; i++)
+ {
+ if (mob_db_data[i] != NULL)
+ {
+ aFree(mob_db_data[i]);
+ mob_db_data[i] = NULL;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/map/mob.h b/src/map/mob.h
new file mode 100644
index 000000000..e126a3bfb
--- /dev/null
+++ b/src/map/mob.h
@@ -0,0 +1,173 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MOB_H_
+#define _MOB_H_
+
+#define MAX_RANDOMMONSTER 3
+#define MAX_MOB_RACE_DB 6
+#define MAX_MOB_DB 10000
+ /* Change this to increase the table size in your mob_db to accomodate
+ a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
+ */
+
+// These define the range of available IDs for clones. [Valaris]
+#define MOB_CLONE_START 9001
+#define MOB_CLONE_END 10000
+
+struct mob_skill {
+ short state;
+ short skill_id,skill_lv;
+ short permillage;
+ int casttime,delay;
+ short cancel;
+ short cond1,cond2;
+ short target;
+ int val[5];
+ short emotion;
+};
+
+struct mob_db {
+ char name[NAME_LENGTH],jname[NAME_LENGTH];
+ short lv;
+ int max_hp,max_sp;
+ int base_exp,job_exp;
+ int atk1,atk2;
+ int def,mdef;
+ int str,agi,vit,int_,dex,luk;
+ int range,range2,range3;
+ int size,race,element,mode;
+ short race2; // celest
+ int speed,adelay,amotion,dmotion;
+ int mexp,mexpper;
+ struct { int nameid,p; } dropitem[10]; //8 -> 10 Lupus
+ struct { int nameid,p; } mvpitem[3];
+ int view_class,sex;
+ short hair,hair_color,weapon,shield,head_top,head_mid,head_buttom,option,clothes_color; // [Valaris]
+ int equip; // [Valaris]
+ int summonper[MAX_RANDOMMONSTER];
+ int maxskill;
+ struct mob_skill skill[MAX_MOBSKILL];
+};
+
+enum {
+ MST_TARGET = 0,
+ MST_SELF,
+ MST_FRIEND,
+ MST_MASTER,
+ MST_AROUND5,
+ MST_AROUND6,
+ MST_AROUND7,
+ MST_AROUND8,
+ MST_AROUND1,
+ MST_AROUND2,
+ MST_AROUND3,
+ MST_AROUND4,
+ MST_AROUND = MST_AROUND4,
+
+ MSC_ALWAYS = 0x0000,
+ MSC_MYHPLTMAXRATE,
+ MSC_FRIENDHPLTMAXRATE,
+ MSC_MYSTATUSON,
+ MSC_MYSTATUSOFF,
+ MSC_FRIENDSTATUSON,
+ MSC_FRIENDSTATUSOFF,
+ MSC_ATTACKPCGT,
+ MSC_ATTACKPCGE,
+ MSC_SLAVELT,
+ MSC_SLAVELE,
+ MSC_CLOSEDATTACKED,
+ MSC_LONGRANGEATTACKED,
+ MSC_AFTERSKILL,
+ MSC_SKILLUSED ,
+ MSC_CASTTARGETED,
+ MSC_RUDEATTACKED,
+ MSC_MASTERHPLTMAXRATE,
+ MSC_MASTERATTACKED,
+ MSC_ALCHEMIST,
+};
+
+//Mob skill states.
+enum {
+ MSS_IDLE,
+ MSS_WALK,
+ MSS_LOOT,
+ MSS_DEAD,
+ MSS_BERSERK, //Aggressive mob attacking
+ MSS_ANGRY, //Mob retaliating from being attacked.
+ MSS_RUSH, //Mob following a player after being attacked.
+ MSS_FOLLOW, //Mob following a player without being attacked.
+};
+
+struct mob_db* mob_db(int class_);
+int mobdb_searchname(const char *str);
+int mobdb_checkid(const int id);
+int mob_once_spawn(struct map_session_data *sd,char *mapname,
+ int x,int y,const char *mobname,int class_,int amount,const char *event);
+int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
+ int x0,int y0,int x1,int y1,
+ const char *mobname,int class_,int amount,const char *event);
+
+int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris]
+ int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris]
+int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex]
+
+int mob_walktoxy(struct mob_data *md,int x,int y,int easy);
+int mob_randomwalk(struct mob_data *md,int tick);
+int mob_can_move(struct mob_data *md);
+
+int mob_target(struct mob_data *md,struct block_list *bl,int dist);
+int mob_unlocktarget(struct mob_data *md,int tick);
+int mob_stop_walking(struct mob_data *md,int type);
+int mob_stopattack(struct mob_data *);
+int mob_spawn(int);
+int mob_setdelayspawn(int);
+int mob_damage(struct block_list *,struct mob_data*,int,int);
+int mob_changestate(struct mob_data *md,int state,int type);
+int mob_heal(struct mob_data*,int);
+
+//Defines to speed up search.
+#define mob_get_viewclass(class_) mob_db(class_)->view_class
+#define mob_get_sex(class_) mob_db(class_)->sex
+#define mob_get_hair(class_) mob_db(class_)->hair
+#define mob_get_hair_color(class_) mob_db(class_)->hair_color
+#define mob_get_weapon(class_) mob_db(class_)->weapon
+#define mob_get_shield(class_) mob_db(class_)->shield
+#define mob_get_head_top(class_) mob_db(class_)->head_top
+#define mob_get_head_mid(class_) mob_db(class_)->head_mid
+#define mob_get_head_buttom(class_) mob_db(class_)->head_buttom
+#define mob_get_clothes_color(class_) mob_db(class_)->clothes_color
+#define mob_get_equip(class_) mob_db(class_)->equip
+
+int do_init_mob(void);
+int do_final_mob(void);
+
+void mob_unload(struct mob_data *md);
+int mob_remove_map(struct mob_data *md, int type);
+int mob_delete(struct mob_data *md);
+int mob_timer_delete(int tid, unsigned int tick, int id, int data);
+
+int mob_deleteslave(struct mob_data *md);
+
+
+int mob_random_class (int *value, size_t count);
+int mob_get_random_id(int type, int flag, int lv);
+int mob_class_change(struct mob_data *md,int class_);
+int mob_warp(struct mob_data *md,int m,int x,int y,int type);
+int mob_warpslave(struct block_list *bl, int range);
+
+int mobskill_use(struct mob_data *md,unsigned int tick,int event);
+int mobskill_event(struct mob_data *md,int flag);
+int mobskill_castend_id( int tid, unsigned int tick, int id,int data );
+int mobskill_castend_pos( int tid, unsigned int tick, int id,int data );
+int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id);
+int mob_countslave(struct block_list *bl);
+
+int mob_is_clone(int class_);
+
+int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration);
+int mob_clone_delete(int class_);
+
+void mob_reload(void);
+
+#endif
diff --git a/src/map/npc.c b/src/map/npc.c
new file mode 100644
index 000000000..7042ea9aa
--- /dev/null
+++ b/src/map/npc.c
@@ -0,0 +1,2826 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include "timer.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "map.h"
+#include "log.h"
+#include "npc.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "itemdb.h"
+#include "script.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "skill.h"
+#include "grfio.h"
+#include "showmsg.h"
+
+#ifdef _WIN32
+#undef isspace
+#define isspace(x) (x == ' ' || x == '\t')
+#endif
+
+struct npc_src_list {
+ struct npc_src_list * next;
+// struct npc_src_list * prev; //[Shinomori]
+ char name[4];
+};
+
+static struct npc_src_list *npc_src_first=NULL;
+static struct npc_src_list *npc_src_last=NULL;
+static int npc_id=START_NPC_NUM;
+static int npc_warp=0;
+static int npc_shop=0;
+static int npc_script=0;
+static int npc_mob=0;
+static int npc_delay_mob=0;
+static int npc_cache_mob=0;
+char *current_file = NULL;
+int npc_get_new_npc_id(void){ return npc_id++; }
+
+static struct dbt *ev_db;
+static struct dbt *npcname_db;
+
+struct event_data {
+ struct npc_data *nd;
+ int pos;
+};
+static struct tm ev_tm_b; // ŽžŒvƒCƒxƒ“ƒg—p
+
+static int npc_walktimer(int,unsigned int,int,int); // [Valaris]
+static int npc_walktoxy_sub(struct npc_data *nd); // [Valaris]
+
+/*==========================================
+ * NPC‚Ì–³Œø‰»/—LŒø‰»
+ * npc_enable
+ * npc_enable_sub —LŒøŽž‚ÉOnTouchƒCƒxƒ“ƒg‚ðŽÀs
+ *------------------------------------------
+ */
+int npc_enable_sub( struct block_list *bl, va_list ap )
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data *));
+ if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){
+ char name[50]; // need 24 + 9 for the "::OnTouch"
+
+ if (nd->flag&1) // –³Œø‰»‚³‚ê‚Ä‚¢‚é
+ return 1;
+
+ if(sd->areanpc_id==nd->bl.id)
+ return 1;
+ sd->areanpc_id=nd->bl.id;
+
+ snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance]
+ npc_event(sd,name,0);
+ }
+ //aFree(name);
+ return 0;
+}
+int npc_enable(const char *name,int flag)
+{
+ struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+
+ if (flag&1) { // —LŒø‰»
+ 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‚𖼑O‚Å’T‚·
+ *------------------------------------------
+ */
+struct npc_data* npc_name2id(const char *name)
+{
+ return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒLƒ…[‚̃Cƒxƒ“ƒgˆ—
+ *------------------------------------------
+ */
+int npc_event_dequeue(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ sd->npc_id=0;
+ if (sd->eventqueue[0][0]) { // ƒLƒ…[‚̃Cƒxƒ“ƒgˆ—
+ size_t ev;
+
+ // find an empty place in eventtimer list
+ for(ev=0;ev<MAX_EVENTTIMER;ev++)
+ if( sd->eventtimer[ev]==-1 )
+ break;
+ if(ev<MAX_EVENTTIMER)
+ { // generate and insert the timer
+ int i;
+ // copy the first event name
+ char *name=(char *)aMalloc(50*sizeof(char));
+ memcpy(name,sd->eventqueue[0],50);
+ // shift queued events down by one
+ for(i=1;i<MAX_EVENTQUEUE;i++)
+ memcpy(sd->eventqueue[i-1],sd->eventqueue[i],50);
+ // clear the last event
+ sd->eventqueue[MAX_EVENTQUEUE-1][0]=0;
+ // add the timer
+ sd->eventtimer[ev]=add_timer(gettick()+100,pc_eventtimer,sd->bl.id,(int)name);//!!todo!!
+
+ }else
+ ShowWarning("npc_event_dequeue: event timer is full !\n");
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒg‚Ì’x‰„ŽÀs
+ *------------------------------------------
+ */
+int npc_event_timer(int tid,unsigned int tick,int id,int data)
+{
+ unsigned char *eventname = (unsigned char *)data;
+ struct event_data *ev = strdb_get(ev_db,eventname);
+ struct npc_data *nd;
+ struct map_session_data *sd=map_id2sd(id);
+ size_t i;
+
+ if((ev==NULL || (nd=ev->nd)==NULL))
+ {
+ if(battle_config.error_log)
+ ShowWarning("npc_event: event not found [%s]\n",eventname);
+ }
+ else
+ {
+ for(i=0;i<MAX_EVENTTIMER;i++) {
+ if( nd->eventtimer[i]==tid ) {
+ nd->eventtimer[i]=-1;
+ npc_event(sd,eventname,0); // sd NULL check is within
+ break;
+ }
+ }
+ if(i==MAX_EVENTTIMER && battle_config.error_log)
+ ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname);
+ }
+
+ aFree(eventname);
+ return 0;
+}
+
+int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+// int xs,ys;
+
+ if((ev==NULL || (nd=ev->nd)==NULL)){
+ ShowWarning("npc_event: event not found [%s]\n",eventname);
+ return 0;
+ }
+
+ run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id);
+
+ return 0;
+}
+/*
+int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ char *p=(char *)key;
+ struct event_data *ev=(struct event_data *)data;
+ int *c=va_arg(ap,int *);
+ int tick=0,ctick=gettick();
+ char temp[10];
+ char event[100];
+
+ if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){
+ sscanf(&p[9],"%s",temp);
+ tick=atoi(temp);
+
+ strcpy( event, ev->nd->name);
+ strcat( event, p);
+
+ if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) {
+ npc_timer_event(event);
+ ev->nd->lastaction = ctick;
+ }
+ }
+ return 0;
+}
+
+int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ struct npc_data *nd=(struct npc_data*)data;
+
+ if(nd->timer == -1)
+ return 0;
+
+ sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id);
+
+ return 0;
+}
+
+int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT
+{
+ npcname_db->foreach(npcname_db,npc_timer_sub);
+
+ aFree((void*)data);
+ return 0;
+}*/
+/*==========================================
+ * ƒCƒxƒ“ƒg—pƒ‰ƒxƒ‹‚̃GƒNƒXƒ|[ƒg
+ * npc_parse_script->strdb_foreach‚©‚çŒÄ‚΂ê‚é
+ *------------------------------------------
+ */
+int npc_event_export(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+
+ if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) {
+ struct event_data *ev;
+ unsigned char buf[51];
+ char *p=strchr(lname,':');
+ // ƒGƒNƒXƒ|[ƒg‚³‚ê‚é
+ ev=(struct event_data *) aCalloc(sizeof(struct event_data), 1);
+ if (ev==NULL) {
+ ShowFatalError("npc_event_export: out of memory !\n");
+ exit(1);
+ }else if (p==NULL || (p-lname)>NAME_LENGTH) {
+ ShowFatalError("npc_event_export: label name error !\n");
+ exit(1);
+ }else{
+ ev->nd=nd;
+ ev->pos=pos;
+ *p='\0';
+ sprintf(buf,"%s::%s",nd->exname,lname);
+ *p=':';
+ strdb_put(ev_db,buf,ev);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * ‘S‚Ä‚ÌNPC‚ÌOn*ƒCƒxƒ“ƒgŽÀs
+ *------------------------------------------
+ */
+int npc_event_doall_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char*p = key.str;
+ struct event_data *ev;
+ int *c;
+ int rid;
+ unsigned char *name;
+
+ ev=(struct event_data *)data;
+ c=va_arg(ap,int *);
+ name=va_arg(ap,unsigned char *);
+ rid=va_arg(ap, int);
+
+ if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){
+ run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_doall(const unsigned char *name)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0);
+ return c;
+}
+int npc_event_doall_id(const unsigned char *name, int rid)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid);
+ return c;
+}
+
+int npc_event_do_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev;
+ int *c;
+ const unsigned char *name;
+
+ nullpo_retr(0, ev=(struct event_data *)data);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ name=va_arg(ap,const unsigned char *);
+
+ if (p && strcmpi(name,p)==0 ) {
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_do(const unsigned char *name)
+{
+ int c=0;
+
+ if (*name==':' && name[1]==':') {
+ return npc_event_doall(name+2);
+ }
+
+ ev_db->foreach(ev_db,npc_event_do_sub,&c,name);
+ return c;
+}
+
+/*==========================================
+ * ŽžŒvƒCƒxƒ“ƒgŽÀs
+ *------------------------------------------
+ */
+int npc_event_do_clock(int tid,unsigned int tick,int id,int data)
+{
+ time_t timer;
+ struct tm *t;
+ char buf[64];
+ char *day="";
+ int c=0;
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch (t->tm_wday) {
+ case 0: day = "Sun"; break;
+ case 1: day = "Mon"; break;
+ case 2: day = "Tue"; break;
+ case 3: day = "Wed"; break;
+ case 4: day = "Thu"; break;
+ case 5: day = "Fri"; break;
+ case 6: day = "Sat"; break;
+ }
+
+ if (t->tm_min != ev_tm_b.tm_min ) {
+ sprintf(buf,"OnMinute%02d",t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_hour!= ev_tm_b.tm_hour) {
+ sprintf(buf,"OnHour%02d",t->tm_hour);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_mday!= ev_tm_b.tm_mday) {
+ sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday);
+ c+=npc_event_doall(buf);
+ }
+ memcpy(&ev_tm_b,t,sizeof(ev_tm_b));
+ return c;
+}
+/*==========================================
+ * OnInitƒCƒxƒ“ƒgŽÀs(&ŽžŒvƒCƒxƒ“ƒgŠJŽn)
+ *------------------------------------------
+ */
+int npc_event_do_oninit(void)
+{
+// int c = npc_event_doall("OnInit");
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+
+ add_timer_interval(gettick()+100,
+ npc_event_do_clock,0,0,1000);
+
+ return 0;
+}
+/*==========================================
+ * OnTimer NPC event - by RoVeRT
+ *------------------------------------------
+ */
+int npc_addeventtimer(struct npc_data *nd,int tick,const char *name)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]==-1 )
+ break;
+ if(i<MAX_EVENTTIMER){
+ unsigned char *evname=(unsigned char *) aCallocA(NAME_LENGTH, sizeof(char));
+ if(evname==NULL){
+ ShowFatalError("npc_addeventtimer: out of memory !\n");exit(1);
+ }
+ memcpy(evname,name,NAME_LENGTH-1);
+ nd->eventtimer[i]=add_timer(gettick()+tick,
+ npc_event_timer,nd->bl.id,(int)evname);
+ }else
+ ShowWarning("npc_addtimer: event timer is full !\n");
+
+ return 0;
+}
+
+int npc_deleventtimer(struct npc_data *nd,const unsigned char *name)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 && strcmp(
+ (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ break;
+ }
+
+ return 0;
+}
+
+int npc_cleareventtimer(struct npc_data *nd)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ }
+
+ return 0;
+}
+
+int npc_do_ontimer_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev = (struct event_data *)data;
+ int *c = va_arg(ap,int *);
+// struct map_session_data *sd=va_arg(ap,struct map_session_data *);
+ int option = va_arg(ap,int);
+ int tick = 0;
+ char temp[10];
+ char event[50];
+
+ if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){
+ sscanf(&p[9], "%s", temp);
+ tick = atoi(temp);
+
+ strcpy(event, ev->nd->name);
+ strcat(event, p);
+
+ if (option!=0) {
+ npc_addeventtimer(ev->nd, tick, event);
+ } else {
+ npc_deleventtimer(ev->nd, event);
+ }
+ }
+ return 0;
+}
+int npc_do_ontimer(int npc_id, int option)
+{
+ ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option);
+ return 0;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[ƒCƒxƒ“ƒg—pƒ‰ƒxƒ‹‚ÌŽæ‚èž‚Ý
+ * npc_parse_script->strdb_foreach‚©‚çŒÄ‚΂ê‚é
+ *------------------------------------------
+ */
+int npc_timerevent_import(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+ int t=0,i=0;
+
+ if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') {
+ // ƒ^ƒCƒ}[ƒCƒxƒ“ƒg
+ struct npc_timerevent_list *te=nd->u.scr.timer_event;
+ int j,i=nd->u.scr.timeramount;
+ if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list));
+ else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
+ if(te==NULL){
+ ShowFatalError("npc_timerevent_import: out of memory !\n");
+ exit(1);
+ }
+ for(j=0;j<i;j++){
+ if(te[j].timer>t){
+ memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j));
+ break;
+ }
+ }
+ te[j].timer=t;
+ te[j].pos=pos;
+ nd->u.scr.timer_event=te;
+ nd->u.scr.timeramount=i+1;
+ }
+ return 0;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[ƒCƒxƒ“ƒgŽÀs
+ *------------------------------------------
+ */
+int npc_timerevent(int tid,unsigned int tick,int id,int data)
+{
+ int next,t;
+ struct npc_data* nd=(struct npc_data *)map_id2bl(id);
+ struct npc_timerevent_list *te;
+ if( nd==NULL || nd->u.scr.nexttimer<0 ){
+ ShowError("npc_timerevent: ??\n");
+ return 0;
+ }
+ nd->u.scr.timertick=tick;
+ te=nd->u.scr.timer_event+ nd->u.scr.nexttimer;
+ nd->u.scr.timerid = -1;
+
+ t = nd->u.scr.timer+=data;
+ nd->u.scr.nexttimer++;
+ if( nd->u.scr.timeramount>nd->u.scr.nexttimer ){
+ next= nd->u.scr.timer_event[ nd->u.scr.nexttimer ].timer - t;
+ nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,next);
+ }
+
+ run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
+ return 0;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[ƒCƒxƒ“ƒgŠJŽn
+ *------------------------------------------
+ */
+int npc_timerevent_start(struct npc_data *nd, int rid)
+{
+ int j,n, next;
+
+ nullpo_retr(0, nd);
+
+ n=nd->u.scr.timeramount;
+ if( nd->u.scr.nexttimer>=0 || n==0 )
+ return 0;
+
+ for(j=0;j<n;j++){
+ if( nd->u.scr.timer_event[j].timer > nd->u.scr.timer )
+ break;
+ }
+ if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori]
+ return 0;
+
+ nd->u.scr.nexttimer=j;
+ nd->u.scr.timertick=gettick();
+ if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori]
+ // if rid is less than 0 leave it unchanged [celest]
+
+ next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
+ nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,next);
+ return 0;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[ƒCƒxƒ“ƒgI—¹
+ *------------------------------------------
+ */
+int npc_timerevent_stop(struct npc_data *nd)
+{
+ nullpo_retr(0, nd);
+
+ if( nd->u.scr.nexttimer>=0 ){
+ nd->u.scr.nexttimer = -1;
+ nd->u.scr.timer += (int)(gettick() - nd->u.scr.timertick);
+ if(nd->u.scr.timerid!=-1)
+ delete_timer(nd->u.scr.timerid,npc_timerevent);
+ nd->u.scr.timerid = -1;
+ nd->u.scr.rid = 0;
+ }
+ return 0;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[’l‚ÌŠ“¾
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒ^ƒCƒ}[’l‚ÌÝ’è
+ *------------------------------------------
+ */
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer)
+{
+ int flag;
+
+ nullpo_retr(0, nd);
+
+ flag= nd->u.scr.nexttimer;
+
+ npc_timerevent_stop(nd);
+ nd->u.scr.timer=newtimer;
+ if(flag>=0)
+ npc_timerevent_start(nd, -1);
+ return 0;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgŒ^‚ÌNPCˆ—
+ *------------------------------------------
+ */
+int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill)
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+ int xs,ys;
+ unsigned char mobevent[100];
+
+ if (sd == NULL)
+ nullpo_info(NLP_MARK);
+
+ if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0)
+ return 1;
+
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (mob_kill) {
+ strcpy( mobevent, eventname);
+ strcat( mobevent, "::OnMyMobDead");
+ ev = strdb_get(ev_db, mobevent);
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (strnicmp(eventname, "GM_MONSTER",10) != 0)
+ ShowError("npc_event: event not found [%s]\n", mobevent);
+ return 0;
+ }
+ } else {
+ if (battle_config.error_log)
+ ShowError("npc_event: event not found [%s]\n", eventname);
+ return 0;
+ }
+ }
+
+ xs=nd->u.scr.xs;
+ ys=nd->u.scr.ys;
+ if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) ) {
+ if (nd->bl.m != sd->bl.m )
+ return 1;
+ if ( xs>0 && (sd->bl.x<nd->bl.x-xs/2 || nd->bl.x+xs/2<sd->bl.x) )
+ return 1;
+ if ( ys>0 && (sd->bl.y<nd->bl.y-ys/2 || nd->bl.y+ys/2<sd->bl.y) )
+ return 1;
+ }
+
+ if ( sd->npc_id!=0) {
+// if (battle_config.error_log)
+// printf("npc_event: npc_id != 0\n");
+ int i;
+ for(i=0;i<MAX_EVENTQUEUE;i++)
+ if (!sd->eventqueue[i][0])
+ break;
+ if (i==MAX_EVENTQUEUE) {
+ if (battle_config.error_log)
+ ShowWarning("npc_event: event queue is full !\n");
+ }else{
+// if (battle_config.etc_log)
+// printf("npc_event: enqueue\n");
+ memcpy(sd->eventqueue[i],eventname,50);
+ }
+ return 1;
+ }
+ if (nd->flag&1) { // –³Œø‰»‚³‚ê‚Ä‚¢‚é
+ npc_event_dequeue(sd);
+ return 0;
+ }
+
+ sd->npc_id=nd->bl.id;
+ sd->npc_pos=run_script(nd->u.scr.script,ev->pos,sd->bl.id,nd->bl.id);
+ return 0;
+}
+
+
+int npc_command_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,char *);
+ char *command=va_arg(ap,char *);
+ unsigned char temp[100];
+
+ if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){
+ sscanf(&p[11],"%s",temp);
+
+ if (strcmp(command,temp)==0)
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command)
+{
+ ev_db->foreach(ev_db,npc_command_sub,npcname,command);
+
+ return 0;
+}
+/*==========================================
+ * ÚGŒ^‚ÌNPCˆ—
+ *------------------------------------------
+ */
+int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
+{
+ int i,f=1;
+ int xs,ys;
+
+ nullpo_retr(1, sd);
+
+ if(sd->npc_id)
+ return 1;
+
+ for(i=0;i<map[m].npc_num;i++) {
+ if (map[m].npc[i]->flag&1) { // –³Œø‰»‚³‚ê‚Ä‚¢‚é
+ f=0;
+ continue;
+ }
+
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ xs=map[m].npc[i]->u.warp.xs;
+ ys=map[m].npc[i]->u.warp.ys;
+ break;
+ case SCRIPT:
+ xs=map[m].npc[i]->u.scr.xs;
+ ys=map[m].npc[i]->u.scr.ys;
+ break;
+ default:
+ continue;
+ }
+ if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs &&
+ y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys)
+ break;
+ }
+ if (i==map[m].npc_num) {
+ if (f) {
+ if (battle_config.error_log)
+ ShowError("npc_touch_areanpc : some bug \n");
+ }
+ return 1;
+ }
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ // hidden chars cannot use warps -- is it the same for scripts too?
+ if (sd->status.option&6 ||
+ (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza]
+ break;
+ skill_stop_dancing(&sd->bl);
+ pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0);
+ break;
+ case SCRIPT:
+ {
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+ char name[50]; // need 24 max + 9 for "::OnTouch"
+
+ if(sd->areanpc_id == map[m].npc[i]->bl.id)
+ return 1;
+ sd->areanpc_id = map[m].npc[i]->bl.id;
+
+ sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance]
+
+ if( npc_event(sd,name,0)>0 )
+ npc_click(sd,map[m].npc[i]->bl.id);
+ //aFree(name);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * ‹ß‚­‚©‚Ç‚¤‚©‚Ì”»’è
+ *------------------------------------------
+ */
+int npc_checknear(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(0, sd);
+
+ nd=(struct npc_data *)map_id2bl(id);
+ if (nd==NULL || nd->bl.type!=BL_NPC) {
+ if (battle_config.error_log)
+ ShowWarning("no such npc : %d\n",id);
+ return 1;
+ }
+
+ if (nd->class_<0) // ƒCƒxƒ“ƒgŒn‚Íí‚ÉOK
+ return 0;
+
+ // ƒGƒŠƒA”»’è
+ 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‚̃I[ƒvƒ“ƒ`ƒƒƒbƒg”­Œ¾
+ *------------------------------------------
+ */
+int npc_globalmessage(const char *name,char *mes)
+{
+ struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+ char temp[100];
+
+ if (!nd)
+ return 0;
+
+ snprintf(temp, sizeof temp ,"%s : %s",name,mes);
+ clif_GlobalMessage(&nd->bl,temp);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒNƒŠƒbƒNŽž‚ÌNPCˆ—
+ *------------------------------------------
+ */
+int npc_click(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (sd->npc_id != 0) {
+ if (battle_config.error_log)
+ ShowError("npc_click: npc_id != 0\n");
+ return 1;
+ }
+
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+
+ if (nd->flag&1) // –³Œø‰»‚³‚ê‚Ä‚¢‚é
+ return 1;
+
+ sd->npc_id=id;
+ switch(nd->bl.subtype) {
+ case SHOP:
+ clif_npcbuysell(sd,id);
+ npc_event_dequeue(sd);
+ break;
+ case SCRIPT:
+ sd->npc_pos=run_script(nd->u.scr.script,0,sd->bl.id,id);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_scriptcont(struct map_session_data *sd,int id)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (id!=sd->npc_id)
+ return 1;
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+
+ sd->npc_pos=run_script(nd->u.scr.script,sd->npc_pos,sd->bl.id,id);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buysellsel(struct map_session_data *sd,int id,int type)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if (npc_checknear(sd,id))
+ return 1;
+
+ nd=(struct npc_data *)map_id2bl(id);
+ if (nd->bl.subtype!=SHOP) {
+ if (battle_config.error_log)
+ ShowError("no such shop npc : %d\n",id);
+ sd->npc_id=0;
+ return 1;
+ }
+ if (nd->flag&1) // –³Œø‰»‚³‚ê‚Ä‚¢‚é
+ return 1;
+
+ sd->npc_shopid=id;
+ if (type==0) {
+ clif_buylist(sd,nd);
+ } else {
+ clif_selllist(sd);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buylist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ struct npc_data *nd;
+ double z;
+ int i,j,w,skill,itemamount=0,new_=0;
+
+ nullpo_retr(3, sd);
+ nullpo_retr(3, item_list);
+
+ if (npc_checknear(sd,sd->npc_shopid))
+ return 3;
+
+ nd=(struct npc_data*)map_id2bl(sd->npc_shopid);
+ if (nd->bl.subtype!=SHOP)
+ return 3;
+
+ for(i=0,w=0,z=0;i<n;i++) {
+ for(j=0;nd->u.shop_item[j].nameid;j++) {
+ if (nd->u.shop_item[j].nameid==item_list[i*2+1])
+ break;
+ }
+ if (nd->u.shop_item[j].nameid==0)
+ return 3;
+
+ if (itemdb_isequip3(nd->u.shop_item[j].nameid) && item_list[i*2] > 1)
+ { //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid);
+ item_list[i*2] = 1;
+ }
+ if (itemdb_value_notdc(nd->u.shop_item[j].nameid))
+ z+=(double)nd->u.shop_item[j].value * item_list[i*2];
+ else
+ z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2];
+ itemamount+=item_list[i*2];
+
+ switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return 2;
+ }
+
+ w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2];
+ }
+ if (z > (double)sd->status.zeny)
+ return 1; // zeny•s‘«
+ if (w+sd->weight > sd->max_weight)
+ return 2; // d—Ê’´‰ß
+ if (pc_inventoryblank(sd)<new_)
+ return 3; // Ží—Þ”’´‰ß
+
+ //Logs (S)hopping Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "S", sd, -(int)z);
+ //Logs
+
+ pc_payzeny(sd,(int)z);
+ for(i=0;i<n;i++) {
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = item_list[i*2+1];
+ item_tmp.identify = 1; // npc”Ì”„ƒAƒCƒeƒ€‚ÍŠÓ’èÏ‚Ý
+
+ pc_additem(sd,&item_tmp,item_list[i*2]);
+
+ //Logs items, Bought in NPC (S)hop [Lupus]
+ if(sd && log_config.pick > 0 )
+ log_pick(sd, "S", 0, item_tmp.nameid, item_list[i*2], NULL);
+ //Logs
+ }
+
+ //¤lŒoŒ±’l
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) {
+ if (sd->status.skill[MC_DISCOUNT].flag != 0)
+ skill = sd->status.skill[MC_DISCOUNT].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,0,(int)z);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ double z;
+ int i,skill,itemamount=0;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ if (npc_checknear(sd,sd->npc_shopid))
+ return 1;
+ for(i=0,z=0;i<n;i++) {
+ int nameid;
+ if (item_list[i*2]-2 <0 || item_list[i*2]-2 >=MAX_INVENTORY)
+ return 1;
+ nameid=sd->status.inventory[item_list[i*2]-2].nameid;
+ if (nameid == 0 ||
+ sd->status.inventory[item_list[i*2]-2].amount < item_list[i*2+1])
+ return 1;
+ if (itemdb_value_notoc(nameid))
+ z+=(double)itemdb_value_sell(nameid) * item_list[i*2+1];
+ else
+ z+=(double)pc_modifysellvalue(sd,itemdb_value_sell(nameid)) * item_list[i*2+1];
+ itemamount+=item_list[i*2+1];
+ }
+
+ if (z > MAX_ZENY) z = MAX_ZENY;
+
+ //Logs (S)hopping Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "S", sd, (int)z);
+ //Logs
+
+ pc_getzeny(sd,(int)z);
+ for(i=0;i<n;i++) {
+ int item_id=item_list[i*2]-2;
+ if( sd->status.inventory[item_id].nameid>0 && sd->inventory_data[item_id] != NULL &&
+ sd->inventory_data[item_id]->type==7 && sd->status.inventory[item_id].amount>0 &&
+ sd->status.inventory[item_id].card[0] == (short)0xff00)
+ if(search_petDB_index(sd->status.inventory[item_id].nameid, PET_EGG) >= 0)
+ intif_delete_petdata(MakeDWord(sd->status.inventory[item_id].card[1],sd->status.inventory[item_id].card[2]));
+
+ //Logs items, Sold to NPC (S)hop [Lupus]
+ if(sd && log_config.pick > 0 )
+ log_pick(sd, "S", 0, sd->status.inventory[item_id].nameid, -item_list[i*2+1], &sd->status.inventory[item_id]);
+ //Logs
+
+ pc_delitem(sd,item_id,item_list[i*2+1],0);
+ }
+
+ //¤lŒoŒ±’l
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) {
+ if (sd->status.skill[MC_OVERCHARGE].flag != 0)
+ skill = sd->status.skill[MC_OVERCHARGE].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,0,(int)z);
+ }
+ }
+
+ return 0;
+
+}
+
+// [Valaris] NPC Walking
+
+/*==========================================
+ * Time calculation concerning one step next to npc
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct npc_data *nd)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ return -1;
+ if(nd->walkpath.path[nd->walkpath.path_pos]&1)
+ return status_get_speed(&nd->bl)*14/10;
+ return status_get_speed(&nd->bl);
+}
+
+
+/*==========================================
+ * npc Walk processing
+ *------------------------------------------
+ */
+static int npc_walk(struct npc_data *nd,unsigned int tick,int data)
+{
+ int i;
+ static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+ static int diry[8]={1,1,0,-1,-1,-1,0,1};
+ int x,y,dx,dy;
+
+ nullpo_retr(0, nd);
+
+ nd->state.state=MS_IDLE;
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len || nd->walkpath.path_pos!=data)
+ return 0;
+
+ nd->walkpath.path_half ^= 1;
+ if(nd->walkpath.path_half==0){
+ nd->walkpath.path_pos++;
+ if(nd->state.change_walk_target){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ }
+ else {
+ if(nd->walkpath.path[nd->walkpath.path_pos]>=8)
+ return 1;
+
+ x = nd->bl.x;
+ y = nd->bl.y;
+ if(map_getcell(nd->bl.m,x,y,CELL_CHKNOPASS)) {
+ npc_stop_walking(nd,1);
+ return 0;
+ }
+ nd->dir=nd->walkpath.path[nd->walkpath.path_pos];
+ dx = dirx[nd->dir];
+ dy = diry[nd->dir];
+
+ if(map_getcell(nd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+
+ nd->state.state=MS_WALK;
+ map_foreachinmovearea(clif_npcoutsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,nd);
+
+ x += dx;
+ y += dy;
+ map_moveblock(&nd->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_npcinsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,nd);
+ nd->state.state=MS_IDLE;
+ }
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>1;
+ if(i < 1 && nd->walkpath.path_half == 0)
+ i = 1;
+ nd->walktimer=add_timer(tick+i,npc_walktimer,nd->bl.id,nd->walkpath.path_pos);
+ nd->state.state=MS_WALK;
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ clif_fixnpcpos(nd); // When npc stops, retransmission current of a position.
+
+ }
+ return 0;
+}
+
+int npc_changestate(struct npc_data *nd,int state,int type)
+{
+ int i;
+
+ nullpo_retr(0, nd);
+
+ if(nd->walktimer != -1)
+ delete_timer(nd->walktimer,npc_walktimer);
+ nd->walktimer=-1;
+ nd->state.state=state;
+
+ switch(state){
+ case MS_WALK:
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>2;
+ nd->walktimer=add_timer(gettick()+i,npc_walktimer,nd->bl.id,0);
+ }
+ else
+ nd->state.state=MS_IDLE;
+ break;
+ case MS_DELAY:
+ nd->walktimer=add_timer(gettick()+type,npc_walktimer,nd->bl.id,0);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int npc_walktimer(int tid,unsigned int tick,int id,int data)
+{
+ struct npc_data *nd;
+
+ nd=(struct npc_data*)map_id2bl(id);
+ if(nd == NULL || nd->bl.type != BL_NPC)
+ return 1;
+
+ if(nd->walktimer != tid){
+ return 0;
+ }
+
+ nd->walktimer=-1;
+
+ if(nd->bl.prev == NULL)
+ return 1;
+
+ switch(nd->state.state){
+ case MS_WALK:
+ npc_walk(nd,tick,data);
+ break;
+ case MS_DELAY:
+ npc_changestate(nd,MS_IDLE,0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int npc_walktoxy_sub(struct npc_data *nd)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y,nd->state.walk_easy))
+ return 1;
+ memcpy(&nd->walkpath,&wpd,sizeof(wpd));
+
+ nd->state.change_walk_target=0;
+ npc_changestate(nd,MS_WALK,0);
+
+ clif_movenpc(nd);
+
+ return 0;
+}
+
+int npc_walktoxy(struct npc_data *nd,int x,int y,int easy)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK && path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,x,y,0) )
+ return 1;
+
+ nd->state.walk_easy = easy;
+ nd->to_x=x;
+ nd->to_y=y;
+ if(nd->state.state == MS_WALK) {
+ nd->state.change_walk_target=1;
+ } else {
+ return npc_walktoxy_sub(nd);
+ }
+
+ return 0;
+}
+
+int npc_stop_walking(struct npc_data *nd,int type)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK || nd->state.state == MS_IDLE) {
+ int dx=0,dy=0;
+
+ nd->walkpath.path_len=0;
+ if(type&4){
+ dx=nd->to_x-nd->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=nd->to_y-nd->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ }
+ nd->to_x=nd->bl.x+dx;
+ nd->to_y=nd->bl.y+dy;
+ if(dx!=0 || dy!=0){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ npc_changestate(nd,MS_IDLE,0);
+ }
+ if(type&0x01)
+ clif_fixnpcpos(nd);
+ if(type&0x02) {
+ int delay=status_get_dmotion(&nd->bl);
+ unsigned int tick = gettick();
+ if(nd->canmove_tick < tick)
+ nd->canmove_tick = tick + delay;
+ }
+
+ return 0;
+}
+
+int npc_remove_map (struct npc_data *nd)
+{
+ int m,i;
+ nullpo_retr(1, nd);
+
+ if(nd->bl.prev == NULL || nd->bl.m < 0)
+ return 1; //Not assigned to a map.
+ m = nd->bl.m;
+#ifdef PCRE_SUPPORT
+ npc_chat_finalize(nd);
+#endif
+ clif_clearchar_area(&nd->bl,2);
+ strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname);
+ //Remove corresponding NPC CELLs
+ if (nd->bl.subtype == WARP) {
+ int j, xs, ys, x, y;
+ x = nd->bl.x;
+ y = nd->bl.y;
+ xs = nd->u.warp.xs;
+ ys = nd->u.warp.ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC))
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC);
+ }
+ }
+ }
+ map_delblock(&nd->bl);
+ map_deliddb(&nd->bl);
+ //Remove npc from map[].npc list. [Skotlex]
+ for(i=0;i<map[m].npc_num && map[m].npc[i] != nd;i++);
+ if (i >= map[m].npc_num) return 2; //failed to find it?
+
+ map[m].npc_num--;
+ for(; i<map[m].npc_num; i++)
+ map[m].npc[i]=map[m].npc[i+1];
+ return 0;
+}
+
+static int npc_unload_ev(DBKey key,void *data,va_list ap) {
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,unsigned char *);
+
+ if(strcmp(ev->nd->exname,npcname)==0){
+ db_remove(ev_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+int npc_unload (struct npc_data *nd)
+{
+ npc_remove_map (nd);
+
+ if (nd->chat_id) {
+ struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id);
+ if (cd) aFree (cd);
+ cd = NULL;
+ }
+ if (nd->bl.subtype == SCRIPT) {
+ ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
+ if (nd->u.scr.timerid != -1)
+ delete_timer(nd->u.scr.timerid, npc_timerevent);
+ npc_cleareventtimer (nd);
+ if (nd->u.scr.timer_event)
+ aFree(nd->u.scr.timer_event);
+ if (nd->u.scr.src_id == 0) {
+ if(nd->u.scr.script) {
+ aFree(nd->u.scr.script);
+ nd->u.scr.script = NULL;
+ }
+ if (nd->u.scr.label_list) {
+ aFree(nd->u.scr.label_list);
+ nd->u.scr.label_list = NULL;
+ }
+ }
+ }
+ aFree(nd);
+
+ return 0;
+}
+
+//
+// ‰Šú‰»ŠÖŒW
+//
+
+/*==========================================
+ * “Ç‚Ýž‚Þnpcƒtƒ@ƒCƒ‹‚̃NƒŠƒA
+ *------------------------------------------
+ */
+void npc_clearsrcfile (void)
+{
+ struct npc_src_list *p = npc_src_first, *p2;
+
+ while (p) {
+ p2 = p;
+ p = p->next;
+ aFree(p2);
+ }
+ npc_src_first = NULL;
+ npc_src_last = NULL;
+}
+/*==========================================
+ * “Ç‚Ýž‚Þnpcƒtƒ@ƒCƒ‹‚̒ljÁ
+ *------------------------------------------
+ */
+void npc_addsrcfile (char *name)
+{
+ struct npc_src_list *nsl;
+
+ if (strcmpi(name, "clear") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ // prevent multiple insert of source files
+ nsl = npc_src_first;
+ while (nsl)
+ { // found the file, no need to insert it again
+ if (0 == strcmp(name, nsl->name))
+ return;
+ nsl = nsl->next;
+ }
+
+ nsl = (struct npc_src_list *) aCalloc (1, sizeof(*nsl) + strlen(name));
+ nsl->next = NULL;
+ strncpy(nsl->name, name, strlen(name) + 1);
+ if (npc_src_first == NULL)
+ npc_src_first = nsl;
+ if (npc_src_last)
+ npc_src_last->next = nsl;
+ npc_src_last = nsl;
+}
+/*==========================================
+ * “Ç‚Ýž‚Þnpcƒtƒ@ƒCƒ‹‚Ìíœ
+ *------------------------------------------
+ */
+void npc_delsrcfile (char *name)
+{
+ struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first;
+
+ if (strcmpi(name, "all") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ while (p) {
+ if (strcmp(p->name, name) == 0) {
+ *lp = p->next;
+ if (npc_src_last == p)
+ npc_src_last = pp;
+ aFree(p);
+ break;
+ }
+ lp = &p->next;
+ pp = p;
+ p = p->next;
+ }
+}
+
+/*==========================================
+ * warps‰ðÍ
+ *------------------------------------------
+ */
+int npc_parse_warp (char *w1,char *w2,char *w3,char *w4)
+{
+ int x, y, xs, ys, to_x, to_y, m;
+ int i, j;
+ char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ // ˆø”‚̌”ƒ`ƒFƒbƒN
+ if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 ||
+ sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) {
+ ShowError("bad warp line : %s\n", w3);
+ return 1;
+ }
+
+ m = map_mapname2mapid(mapname);
+ i = mapindex_name2id(to_mapname);
+ if (!i) {
+ ShowError("bad warp line (destination map not found): %s\n", w3);
+ return 1;
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data));
+
+ nd->bl.id = npc_get_new_npc_id();
+ nd->n = map_addnpc(m, nd);
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+
+ if (!battle_config.warp_point_debug)
+ nd->class_ = WARP_CLASS;
+ else
+ nd->class_ = WARP_DEBUG_CLASS;
+ nd->speed = 200;
+
+ nd->u.warp.mapindex = (short)i;
+ xs += 2;
+ ys += 2;
+ nd->u.warp.x = to_x;
+ nd->u.warp.y = to_y;
+ nd->u.warp.xs = xs;
+ nd->u.warp.ys = ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC);
+ }
+ }
+
+ npc_warp++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = WARP;
+ map_addblock(&nd->bl);
+ clif_spawnnpc(nd);
+ strdb_put(npcname_db, nd->name, nd);
+
+ return 0;
+}
+
+/*==========================================
+ * shops‰ðÍ
+ *------------------------------------------
+ */
+static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4)
+{
+ #define MAX_SHOPITEM 100
+ char *p;
+ int x, y, dir, m, pos = 0;
+ char mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; dir = 0; m = -1;
+ } else {
+ // ˆø”‚̌”ƒ`ƒFƒbƒN
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ strchr(w4, ',') == NULL) {
+ ShowError("bad shop line : %s\n", w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) +
+ sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1));
+ p = strchr(w4, ',');
+
+ while (p && pos < MAX_SHOPITEM) {
+ int nameid, value;
+ struct item_data *id;
+ p++;
+ if (sscanf(p, "%d:%d", &nameid, &value) != 2)
+ break;
+ nd->u.shop_item[pos].nameid = nameid;
+ id = itemdb_search(nameid);
+ if (value < 0)
+ value = id->value_buy;
+ nd->u.shop_item[pos].value = value;
+ // check for bad prices that can possibly cause exploits
+ if (value*75/100 < id->value_sell*124/100) {
+ printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
+ ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d)\n",
+ id->name, id->nameid, value*75/100, id->value_sell*124/100);
+ }
+ //for logs filters, atcommands and iteminfo script command
+ if (id->maxchance<=0)
+ id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop
+
+ pos++;
+ p = strchr(p, ',');
+ }
+ if (pos == 0) {
+ aFree(nd);
+ return 1;
+ }
+ nd->u.shop_item[pos++].nameid = 0;
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ nd->dir = dir;
+ nd->flag = 0;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ nd->name[NAME_LENGTH-1] = '\0';
+ nd->class_ = m==-1?0:atoi(w4);
+ nd->speed = 200;
+
+ nd = (struct npc_data *)aRealloc(nd,
+ sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos);
+
+ npc_shop++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SHOP;
+ if (m >= 0) {
+ nd->n = map_addnpc(m,nd);
+ map_addblock(&nd->bl);
+ clif_spawnnpc(nd);
+ } else
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ strdb_put(npcname_db, nd->name,nd);
+
+ return 0;
+}
+
+/*==========================================
+ * NPC‚̃‰ƒxƒ‹ƒf[ƒ^ƒRƒ“ƒo[ƒg
+ *------------------------------------------
+ */
+int npc_convertlabel_db (DBKey key, void *data, va_list ap)
+{
+ unsigned char *lname = key.str;
+ int pos = (int)data;
+ struct npc_data *nd;
+ struct npc_label_list *lst;
+ int num;
+ char *p;
+ char c;
+
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd = va_arg(ap,struct npc_data *));
+
+ lst = nd->u.scr.label_list;
+ num = nd->u.scr.label_list_num;
+ if (!lst) {
+ lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
+ num = 0;
+ } else
+ lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1));
+
+ // In case of labels not terminated with ':', for user defined function support
+ p = lname;
+ while(isalnum(*(unsigned char*)p) || *p == '_') { p++; }
+ c = *p;
+ *p='\0';
+
+ // here we check if the label fit into the buffer
+ if (strlen(lname) > 23) {
+ ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file);
+ exit(1);
+ }
+ memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS
+
+ *p = c;
+ lst[num].pos = pos;
+ nd->u.scr.label_list = lst;
+ nd->u.scr.label_list_num = num+1;
+
+ return 0;
+}
+
+/*==========================================
+ * scripts‰ðÍ
+ *------------------------------------------
+ */
+static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) {
+ int i = strlen((char *)p),j;
+ int string_flag = 0;
+ static int comment_flag = 0;
+ for(j = 0; j < i ; j++) {
+ if(comment_flag) {
+ if(p[j] == '*' && p[j+1] == '/') {
+ // ƒ}ƒ‹ƒ`ƒ‰ƒCƒ“ƒRƒƒ“ƒgI—¹
+ j++;
+ (*curly_count)--;
+ comment_flag = 0;
+ }
+ } else if(string_flag) {
+ if(p[j] == '"') {
+ string_flag = 0;
+ } else if(p[j] == '\\' && p[j-1]<=0x7e) {
+ // ƒGƒXƒP[ƒv
+ j++;
+ }
+ } else {
+ if(p[j] == '"') {
+ string_flag = 1;
+ } else if(p[j] == '}') {
+ if(*curly_count == 0) {
+ break;
+ } else {
+ (*curly_count)--;
+ }
+ } else if(p[j] == '{') {
+ (*curly_count)++;
+ } else if(p[j] == '/' && p[j+1] == '/') {
+ // ƒRƒƒ“ƒg
+ break;
+ } else if(p[j] == '/' && p[j+1] == '*') {
+ // ƒ}ƒ‹ƒ`ƒ‰ƒCƒ“ƒRƒƒ“ƒg
+ j++;
+ (*curly_count)++;
+ comment_flag = 1;
+ }
+ }
+ }
+ if(string_flag) {
+ printf("Missing '\"' at file %s line %d\n",current_file,line);
+ exit(1);
+ }
+}
+
+// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex]
+static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
+{
+ unsigned char *srcbuf = NULL;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int curly_count = 0;
+
+ srcbuf = (unsigned char *)aCallocA(srcsize, sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0)
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ aFree(srcbuf);
+ return 0;
+}
+
+static int npc_parse_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
+{
+ int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov
+ char mapname[MAP_NAME_LENGTH];
+ unsigned char *srcbuf = NULL, *script;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int i;
+ struct npc_data *nd;
+ int evflag = 0;
+ struct dbt *label_db;
+ char *p;
+ struct npc_label_list *label_dup = NULL;
+ int label_dupnum = 0;
+ int src_id = 0;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; m = -1;
+ } else {
+ // ˆø”‚̌”ƒ`ƒFƒbƒN
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) {
+ ShowError("bad script line (in file %s): %s\n", current_file, w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ if (strcmp(w2, "script") == 0){
+ // parsing script with curly
+ int curly_count = 0;
+ srcbuf = (unsigned char *)aCallocA(srcsize, sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ script = NULL;
+ } else {
+ // printf("Ok line %d\n",*lines);
+ script = (unsigned char *) parse_script((unsigned char *) srcbuf, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+ } else {
+ // duplicate‚·‚é
+ char srcname[128];
+ struct npc_data *nd2;
+ if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) {
+ ShowError("bad duplicate name (in %s)! : %s", current_file, w2);
+ return 0;
+ }
+ if ((nd2 = npc_name2id(srcname)) == NULL) {
+ ShowError("bad duplicate name (in %s)! (not exist) : %s\n", current_file, srcname);
+ return 0;
+ }
+ script = (unsigned char *)nd2->u.scr.script;
+ label_dup = nd2->u.scr.label_list;
+ label_dupnum = nd2->u.scr.label_list_num;
+ src_id = nd2->bl.id;
+
+ }// end of ƒXƒNƒŠƒvƒg‰ðÍ
+
+ nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
+
+ if (m == -1){
+ // ƒXƒNƒŠƒvƒgƒRƒs[—p‚̃_ƒ~[NPC
+ } else if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) {
+ // ÚGŒ^NPC
+ int i, j;
+
+ if (xs >= 0) xs = xs * 2 + 1;
+ if (ys >= 0) ys = ys * 2 + 1;
+
+ if (class_ >= 0) {
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC);
+ }
+ }
+ }
+ nd->u.scr.xs = xs;
+ nd->u.scr.ys = ys;
+ } else {
+ // ƒNƒŠƒbƒNŒ^NPC
+ class_ = atoi(w4);
+ nd->u.scr.xs = 0;
+ nd->u.scr.ys = 0;
+ }
+
+ if (class_ < 0 && m >= 0) { // ƒCƒxƒ“ƒgŒ^NPC
+ evflag = 1;
+ }
+
+ while ((p = strchr(w3,':'))) {
+ if (p[1] == ':') break;
+ }
+ if (p) {
+ *p = 0;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, p+2, NAME_LENGTH-1);
+ } else {
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+ }
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ nd->dir = dir;
+// nd->flag = 0;
+ nd->class_ = class_;
+ nd->speed = 200;
+ nd->u.scr.script = script;
+ nd->u.scr.src_id = src_id;
+/* Cleaned up above with memset...
+ nd->chat_id = 0;
+ nd->option = 0;
+ nd->opt1 = 0;
+ nd->opt2 = 0;
+ nd->opt3 = 0;
+*/
+ nd->walktimer = -1;
+
+ npc_script++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SCRIPT;
+// Cleaned up above...
+// memset (nd->eventqueue, 0, sizeof(nd->eventqueue));
+ for (i = 0; i < MAX_EVENTTIMER; i++)
+ nd->eventtimer[i] = -1;
+ if (m >= 0) {
+ nd->n = map_addnpc(m, nd);
+ map_addblock(&nd->bl);
+
+ if (evflag) { // ƒCƒxƒ“ƒgŒ^
+ struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data));
+ ev->nd = nd;
+ ev->pos = 0;
+ strdb_put(ev_db, nd->exname, ev);
+ } else
+ clif_spawnnpc(nd);
+ } else {
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ }
+ strdb_put(npcname_db, nd->exname, nd);
+
+ //-----------------------------------------
+ // ƒ‰ƒxƒ‹ƒf[ƒ^‚Ì€”õ
+ if (srcbuf){
+ // script–{‘Ì‚ª‚ ‚éꇂ̈—
+ // ƒ‰ƒxƒ‹ƒf[ƒ^‚̃Rƒ“ƒo[ƒg
+ label_db = script_get_label_db();
+ label_db->foreach(label_db, npc_convertlabel_db, nd);
+
+ // ‚à‚¤Žg‚í‚È‚¢‚̂Ńoƒbƒtƒ@‰ð•ú
+ aFree(srcbuf);
+ } else {
+ // duplicate
+ nd->u.scr.label_list = label_dup; // ƒ‰ƒxƒ‹ƒf[ƒ^‹¤—L
+ nd->u.scr.label_list_num = label_dupnum;
+ }
+
+ //-----------------------------------------
+ // ƒCƒxƒ“ƒg—pƒ‰ƒxƒ‹ƒf[ƒ^‚̃GƒNƒXƒ|[ƒg
+ for (i = 0; i < nd->u.scr.label_list_num; i++){
+ char *lname = nd->u.scr.label_list[i].name;
+ int pos = nd->u.scr.label_list[i].pos;
+
+ if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) {
+ // this check is useless here because the buffer is only 24 chars
+ // and already overwritten if this is here is reached
+ // I leave the check anyway but place it correctly to npc_convertlabel_db
+ if (strlen(lname)>NAME_LENGTH-1) {
+ ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, current_file);
+ exit(1);
+ } else {
+ struct event_data *ev;
+ unsigned char buf[51];
+ // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS
+ sprintf(buf,"%s::%s",nd->exname,lname);
+
+ // remember the label is max 50 chars + eos; see the strdb_init below
+ // generate the data and insert it
+ ev=(struct event_data *)aCalloc(1,sizeof(struct event_data));
+ ev->nd=nd;
+ ev->pos=pos;
+ if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name?
+ ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, current_file);
+ }
+ }
+ }
+
+ //-----------------------------------------
+ // ƒ‰ƒxƒ‹ƒf[ƒ^‚©‚çƒ^ƒCƒ}[ƒCƒxƒ“ƒgŽæ‚èž‚Ý
+ 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') {
+ // ƒ^ƒCƒ}[ƒCƒxƒ“ƒg
+ struct npc_timerevent_list *te = nd->u.scr.timer_event;
+ int j, k = nd->u.scr.timeramount;
+ if (te == NULL)
+ te = (struct npc_timerevent_list *)aCallocA(1,sizeof(struct npc_timerevent_list));
+ else
+ te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) );
+ for (j = 0; j < k; j++){
+ if (te[j].timer > t){
+ memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j));
+ break;
+ }
+ }
+ te[j].timer = t;
+ te[j].pos = pos;
+ nd->u.scr.timer_event = te;
+ nd->u.scr.timeramount = k+1;
+ }
+ }
+ nd->u.scr.nexttimer = -1;
+ nd->u.scr.timerid = -1;
+
+ return 0;
+}
+
+/*==========================================
+ * functions‰ðÍ
+ *------------------------------------------
+ */
+static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines)
+{
+ unsigned char *srcbuf, *script, *p;
+ int srcsize = 65536;
+ int startline = 0;
+ char line[1024];
+ int curly_count = 0;
+ struct dbt *user_db;
+
+ // ƒXƒNƒŠƒvƒg‚̉ðÍ
+ srcbuf = (unsigned char *) aCallocA (srcsize, sizeof(char));
+ if (strchr(first_line,'{')) {
+ strcpy(srcbuf, strchr(first_line,'{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+
+ while (curly_count > 0) {
+ fgets(line, sizeof(line) - 1, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) {
+ srcsize += 65536;
+ srcbuf = (char *)aRealloc(srcbuf, srcsize);
+ memset(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0]!='{') {
+ if (strchr(line,'{')) {
+ strcpy(srcbuf, strchr(line,'{'));
+ startline = *lines;
+ }
+ } else
+ strcat(srcbuf,line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ script = NULL;
+ } else {
+ script = parse_script(srcbuf, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+
+ p = (char *) aCallocA (50, sizeof(char));
+ strncpy(p, w3, 50);
+
+ user_db = script_get_userfunc_db();
+ strdb_put(user_db, p, script);
+
+ // ‚à‚¤Žg‚í‚È‚¢‚̂Ńoƒbƒtƒ@‰ð•ú
+ aFree(srcbuf);
+
+// printf("function %s => %p\n",p,script);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Parse Mob 1 - Parse mob list into each map
+ * Parse Mob 2 - Actually Spawns Mob
+ * [Wizputer]
+ * If cached =1, it is a dynamic cached mob
+ *------------------------------------------
+ */
+int npc_parse_mob2 (struct mob_list *mob, int cached)
+{
+ int i;
+ struct mob_data *md;
+
+ for (i = 0; i < mob->num; i++) {
+ md = (struct mob_data *) aCalloc (1, sizeof(struct mob_data));
+
+ md->bl.prev = NULL;
+ md->bl.next = NULL;
+ md->bl.m = mob->m;
+ md->bl.x = mob->x;
+ md->bl.y = mob->y;
+ md->level = mob->level;
+ memcpy(md->name, mob->mobname, NAME_LENGTH-1);
+ md->n = i;
+ //FIXME: This implementation is not stable, npc scripts will stop working once MAX_MOB_DB changes value! [Skotlex]
+ if(mob->class_ > 2*MAX_MOB_DB){ // large/tiny mobs [Valaris]
+ md->special_state.size=2;
+ md->base_class = md->class_ = mob->class_-2*MAX_MOB_DB;
+ } else if (mob->class_ > MAX_MOB_DB) {
+ md->special_state.size=1;
+ md->base_class = md->class_ = mob->class_-MAX_MOB_DB;
+ } else
+ md->base_class = md->class_ = mob->class_;
+ md->bl.id = npc_get_new_npc_id();
+ md->db = mob_db(mob->class_);
+ md->m = mob->m;
+ md->x0 = mob->x;
+ md->y0 = mob->y;
+ md->xs = mob->xs;
+ md->ys = mob->ys;
+ md->spawndelay1 = mob->delay1;
+ md->spawndelay2 = mob->delay2;
+
+ md->special_state.cached = cached; //If cached, mob is dynamically removed
+ md->timer = -1;
+ md->speed = mob_db(mob->class_)->speed;
+
+ if (mob_db(mob->class_)->mode & MD_LOOTER)
+ md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE, sizeof(struct item));
+ else
+ md->lootitem = NULL;
+
+ if (strlen(mob->eventname) >= 4) {
+ memcpy(md->npc_event, mob->eventname, NAME_LENGTH-1);
+ } else if (strlen(mob->eventname) == 1) { //Portable monster big/small implementation. [Skotlex]
+ int size = atoi(mob->eventname);
+ if (size & 2)
+ md->special_state.size=1;
+ else if (size & 4)
+ md->special_state.size=2;
+ }
+
+ md->bl.type = BL_MOB;
+ map_addiddb(&md->bl);
+ mob_spawn(md->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
+{
+ int level, mode;
+ char mapname[MAP_NAME_LENGTH];
+ char mobname[NAME_LENGTH];
+ struct mob_list mob;
+
+ memset(&mob, 0, sizeof(struct mob_list));
+
+ // ˆø”‚̌”ƒ`ƒFƒbƒN
+ if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &mob.x, &mob.y, &mob.xs, &mob.ys) < 3 ||
+ sscanf(w4, "%d,%d,%d,%d,%23s", &mob.class_, &mob.num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) {
+ ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file);
+ return 1;
+ }
+
+ mob.m = map_mapname2mapid(mapname);
+ if (mob.m < 0) {
+ ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file);
+ return 1;
+ }
+
+ // check monster ID if exists!
+ if (mobdb_checkid(mob.class_)==0) {
+ ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ if (mob.num < 1 || mob.num>1000 ) {
+ ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+ if (mob.num > 1 && battle_config.mob_count_rate != 100) {
+ if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1)
+ mob.num = 1;
+ }
+
+ //Apply the spawn delay fix [Skotlex]
+ mode = mob_db(mob.class_)->mode;
+ if (mode & MD_BOSS) { //Bosses
+ if (battle_config.boss_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100;
+ }
+ } else if (mode&MD_PLANT) { //Plants
+ if (battle_config.plant_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100;
+ }
+ } else if (battle_config.mob_spawn_delay != 100)
+ { //Normal mobs
+ mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100;
+ }
+
+ // parse MOB_NAME,[MOB LEVEL]
+ if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1)
+ mob.level = level;
+
+ if (strcmp(mobname, "--en--") == 0)
+ memcpy(mob.mobname, mob_db(mob.class_)->name, NAME_LENGTH-1);
+ else if (strcmp(mobname, "--ja--") == 0)
+ memcpy(mob.mobname, mob_db(mob.class_)->jname, NAME_LENGTH-1);
+ else memcpy(mob.mobname, mobname, NAME_LENGTH-1);
+
+ if( mob.delay1<0 || mob.delay2<0 || mob.delay1>0xfffffff || mob.delay2>0xfffffff) {
+ ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) {
+ npc_parse_mob2(&mob,0);
+ npc_delay_mob += mob.num;
+ } else {
+ struct mob_list *dynmob = map_addmobtolist(mob.m);
+ if( dynmob ) {
+ memcpy(dynmob, &mob, sizeof(struct mob_list));
+ // check if target map has players
+ // (usually shouldn't occur when map server is just starting,
+ // but not the case when we do @reloadscript
+ if (map[mob.m].users > 0)
+ npc_parse_mob2(&mob,1);
+ npc_cache_mob += mob.num;
+ } else {
+ // mobcache is full
+ // create them as delayed with one second
+ mob.delay1 = 1000;
+ npc_parse_mob2(&mob,0);
+ npc_delay_mob += mob.num;
+ }
+ }
+
+ npc_mob++;
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ}ƒbƒvƒtƒ‰ƒOs‚̉ðÍ
+ *------------------------------------------
+ */
+static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
+{
+ int m;
+ char mapname[MAP_NAME_LENGTH];
+
+ // ˆø”‚̌”ƒ`ƒFƒbƒN
+ if (sscanf(w1, "%15[^,]",mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+
+//ƒ}ƒbƒvƒtƒ‰ƒO
+ if (strcmpi(w3, "nosave") == 0) {
+ char savemap[MAP_NAME_LENGTH];
+ int savex, savey;
+ if (strcmp(w4, "SavePoint") == 0) {
+ map[m].save.map = 0;
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) {
+ map[m].save.map = mapindex_name2id(savemap);
+ map[m].save.x = savex;
+ map[m].save.y = savey;
+ if (!map[m].save.map) {
+ ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file);
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ }
+ }
+ map[m].flag.nosave = 1;
+ }
+ else if (strcmpi(w3,"nomemo")==0) {
+ map[m].flag.nomemo=1;
+ }
+ else if (strcmpi(w3,"noteleport")==0) {
+ map[m].flag.noteleport=1;
+ }
+ else if (strcmpi(w3,"nowarp")==0) {
+ map[m].flag.nowarp=1;
+ }
+ else if (strcmpi(w3,"nowarpto")==0) {
+ map[m].flag.nowarpto=1;
+ }
+ else if (strcmpi(w3,"noreturn")==0) {
+ map[m].flag.noreturn=1;
+ }
+ else if (strcmpi(w3,"monster_noteleport")==0) {
+ map[m].flag.monster_noteleport=1;
+ }
+ else if (strcmpi(w3,"nobranch")==0) {
+ map[m].flag.nobranch=1;
+ }
+ else if (strcmpi(w3,"nopenalty")==0) {
+ map[m].flag.nopenalty=1;
+ }
+ else if (strcmpi(w3,"pvp")==0) {
+ map[m].flag.pvp=1;
+ }
+ else if (strcmpi(w3,"pvp_noparty")==0) {
+ map[m].flag.pvp_noparty=1;
+ }
+ else if (strcmpi(w3,"pvp_noguild")==0) {
+ map[m].flag.pvp_noguild=1;
+ }
+ else if (strcmpi(w3, "pvp_nightmaredrop") == 0) {
+ char drop_arg1[16], drop_arg2[16];
+ int drop_id = 0, drop_type = 0, drop_per = 0;
+ if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) {
+ int i;
+ if (strcmp(drop_arg1, "random") == 0)
+ drop_id = -1;
+ else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL)
+ drop_id = 0;
+ if (strcmp(drop_arg2, "inventory") == 0)
+ drop_type = 1;
+ else if (strcmp(drop_arg2,"equip") == 0)
+ drop_type = 2;
+ else if (strcmp(drop_arg2,"all") == 0)
+ drop_type = 3;
+
+ if (drop_id != 0){
+ for (i = 0; i < MAX_DROP_PER_MAP; i++) {
+ if (map[m].drop_list[i].drop_id == 0){
+ map[m].drop_list[i].drop_id = drop_id;
+ map[m].drop_list[i].drop_type = drop_type;
+ map[m].drop_list[i].drop_per = drop_per;
+ break;
+ }
+ }
+ map[m].flag.pvp_nightmaredrop = 1;
+ }
+ }
+ }
+ else if (strcmpi(w3,"pvp_nocalcrank")==0) {
+ map[m].flag.pvp_nocalcrank=1;
+ }
+ else if (strcmpi(w3,"gvg")==0) {
+ map[m].flag.gvg=1;
+ }
+ else if (strcmpi(w3,"gvg_noparty")==0) {
+ map[m].flag.gvg_noparty=1;
+ }
+ else if (strcmpi(w3,"gvg_dungeon")==0) {
+ map[m].flag.gvg_dungeon=1;
+ }
+ else if (strcmpi(w3,"gvg_castle")==0) {
+ map[m].flag.gvg_castle=1;
+ }
+ else if (strcmpi(w3,"nozenypenalty")==0) {
+ map[m].flag.nozenypenalty=1;
+ }
+ else if (strcmpi(w3,"notrade")==0) {
+ map[m].flag.notrade=1;
+ }
+ else if (strcmpi(w3,"noskill")==0) {
+ map[m].flag.noskill=1;
+ }
+ else if (battle_config.pk_mode && strcmpi(w3,"nopvp")==0) { // nopvp for pk mode [Valaris]
+ map[m].flag.nopvp=1;
+ map[m].flag.pvp=0;
+ }
+ else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris]
+ map[m].flag.noicewall=1;
+ }
+ else if (strcmpi(w3,"snow")==0) { // snow [Valaris]
+ map[m].flag.snow=1;
+ }
+ else if (strcmpi(w3,"clouds")==0) {
+ map[m].flag.clouds=1;
+ }
+ else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris]
+ map[m].flag.clouds2=1;
+ }
+ else if (strcmpi(w3,"fog")==0) { // fog [Valaris]
+ map[m].flag.fog=1;
+ }
+ else if (strcmpi(w3,"fireworks")==0) {
+ map[m].flag.fireworks=1;
+ }
+ else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris]
+ map[m].flag.sakura=1;
+ }
+ else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris]
+ map[m].flag.leaves=1;
+ }
+ else if (strcmpi(w3,"rain")==0) { // rain [Valaris]
+ map[m].flag.rain=1;
+ }
+ else if (strcmpi(w3,"indoors")==0) { // celest
+ map[m].flag.indoors=1;
+ }
+ else if (strcmpi(w3,"nightenabled")==0) { // Skotlex
+ map[m].flag.nightenabled=1;
+ }
+ else if (strcmpi(w3,"nogo")==0) { // celest
+ map[m].flag.nogo=1;
+ }
+ else if (strcmpi(w3,"noexp")==0) { // Lorky
+ map[m].flag.nobaseexp=1;
+ map[m].flag.nojobexp=1;
+ }
+ else if (strcmpi(w3,"nobaseexp")==0) { // Lorky
+ map[m].flag.nobaseexp=1;
+ }
+ else if (strcmpi(w3,"nojobexp")==0) { // Lorky
+ map[m].flag.nojobexp=1;
+ }
+ else if (strcmpi(w3,"noloot")==0) { // Lorky
+ map[m].flag.nomobloot=1;
+ map[m].flag.nomvploot=1;
+ }
+ else if (strcmpi(w3,"nomobloot")==0) { // Lorky
+ map[m].flag.nomobloot=1;
+ }
+ else if (strcmpi(w3,"nomvploot")==0) { // Lorky
+ map[m].flag.nomvploot=1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Setting up map cells
+ *------------------------------------------
+ */
+static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4)
+{
+ int m, cell, x, y, x0, y0, x1, y1;
+ char type[24], mapname[MAP_NAME_LENGTH];
+
+ if (sscanf(w1, "%15[^,]", mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+
+ if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) {
+ ShowError("Bad setcell line : %s\n",w3);
+ return 1;
+ }
+ cell = strtol(type, (char **)NULL, 0);
+ //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1);
+
+ if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
+ if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
+
+ for (x = x0; x <= x1; x++) {
+ for (y = y0; y <= y1; y++) {
+ map_setcell(m, x, y, cell);
+ //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y);
+ }
+ }
+
+ return 0;
+}
+
+void npc_parsesrcfile (char *name)
+{
+ int m, lines = 0;
+ char line[1024];
+
+ FILE *fp = fopen (name,"r");
+ if (fp == NULL) {
+ ShowError ("File not found : %s\n", name);
+ exit(1);
+ }
+ current_file = name;
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ char w1[1024], w2[1024], w3[1024], w4[1024], mapname[1024];
+ int i, j, w4pos, count;
+ lines++;
+
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ // •s—v‚ȃXƒy[ƒX‚âƒ^ƒu‚̘A‘±‚Í‹l‚ß‚é
+ for (i = j = 0; line[i]; i++) {
+ if (line[i]==' ') {
+ if (!((line[i+1] && (isspace((unsigned char)line[i+1]) || line[i+1]==',')) ||
+ (j && line[j-1]==',')))
+ line[j++]=' ';
+ } else if (line[i]=='\t') {
+ if (!(j && line[j-1]=='\t'))
+ line[j++]='\t';
+ } else
+ line[j++]=line[i];
+ }
+ line[j] = '\0'; //Forget to terminate the string. From [jA 1091]
+ // ʼn‚̓^ƒu‹æØ‚è‚Ń`ƒFƒbƒN‚µ‚Ä‚Ý‚ÄAƒ_ƒ‚È‚çƒXƒy[ƒX‹æØ‚è‚ÅŠm”F
+ 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;
+ }
+ // ƒ}ƒbƒv‚Ì‘¶ÝŠm”F
+ if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){
+ sscanf(w1,"%[^,]",mapname);
+ if (!mapindex_name2id(mapname)) { //Incorrect map
+ ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file);
+ if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info...
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ if ((m = map_mapname2mapid(mapname)) < 0) {
+ // "mapname" is not assigned to this server
+ // we must skip the script info...
+ if (strcmpi(w2,"script") == 0 && count > 3)
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ }
+ if (strcmpi(w2,"warp") == 0 && count > 3) {
+ npc_parse_warp(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"shop") == 0 && count > 3) {
+ npc_parse_shop(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"script") == 0 && count > 3) {
+ if (strcmpi(w1,"function") == 0) {
+ npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ } else {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ }
+ } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ } else if (strcmpi(w2,"monster") == 0 && count > 3) {
+ npc_parse_mob(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) {
+ npc_parse_mapflag(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"setcell") == 0 && count >= 3) {
+ npc_parse_mapcell(w1,w2,w3,w4);
+ } else {
+ ShowError("Probably TAB is missing: %s %s %s %s line '%i', file '%s'\n",w1,w2,w3,w4,lines,current_file); //Lupus
+ }
+ }
+ fclose(fp);
+
+ return;
+}
+
+static int npc_read_indoors (void)
+{
+ char *buf, *p;
+ int s, m;
+
+ buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+
+ for (p = buf; p - buf < s; ) {
+ char map_name[64];
+ if (sscanf(p, "%15[^#]#", map_name) == 1) {
+ size_t pos = strlen(map_name) - 4; // replace '.xxx' extension
+ memcpy(map_name+pos,".gat",4); // with '.gat'
+ if ((m = map_mapname2mapid(map_name)) >= 0)
+ map[m].flag.indoors = 1;
+ }
+
+ p = strchr(p, 10);
+ if (!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+static int npc_cleanup_sub (struct block_list *bl, va_list ap) {
+ nullpo_retr(0, bl);
+
+ switch(bl->type) {
+ case BL_NPC:
+ npc_unload((struct npc_data *)bl);
+ break;
+ case BL_MOB:
+ mob_unload((struct mob_data *)bl);
+ break;
+ }
+
+ return 0;
+}
+
+static int npc_cleanup_dbsub(DBKey key,void * data,va_list app) {
+ return npc_cleanup_sub((struct block_list*)data, 0);
+}
+
+int npc_reload (void)
+{
+ struct npc_src_list *nsl;
+ int m, i;
+ time_t last_time = time(0);
+ int busy = 0, npc_new_min = npc_id;
+ char c = '-';
+
+ for (m = 0; m < map_num; m++) {
+ map_foreachinmap(npc_cleanup_sub, m, 0);
+ if(battle_config.dynamic_mobs) { //dynamic check by [random]
+ for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++)
+ if (map[m].moblist[i]) aFree(map[m].moblist[i]);
+ memset (map[m].moblist, 0, sizeof(map[m].moblist));
+ }
+ map[m].npc_num = 0;
+ }
+ //Remove any npcs/mobs that weren't caught by the previous loop. [Skotlex]
+ map_foreachiddb(npc_cleanup_dbsub);
+
+ // anything else we should cleanup?
+ // Reloading npc's now
+ ev_db->clear(ev_db,NULL);
+ npcname_db->clear(npcname_db,NULL);
+ npc_warp = npc_shop = npc_script = 0;
+ npc_mob = npc_cache_mob = npc_delay_mob = 0;
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ if (script_config.verbose_mode) {
+ printf("\r");
+ ShowStatus("Loading NPCs... %-53s", nsl->name);
+ } else {
+ if (last_time != time(0)) {
+ printf("\r");
+ ShowStatus("Loading NPCs... Working: ");
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ printf("[%c]",c);
+ }
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ //Execute the OnInit event for freshly loaded npcs. [Skotlex]
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+ return 0;
+}
+
+/*==========================================
+ * I—¹
+ *------------------------------------------
+ */
+int do_final_npc(void)
+{
+ int i;
+ struct block_list *bl;
+ struct npc_data *nd;
+ struct mob_data *md;
+ struct pet_data *pd;
+
+ for (i = START_NPC_NUM; i < npc_id; i++){
+ if ((bl = map_id2bl(i))){
+ if (bl->type == BL_NPC && (nd = (struct npc_data *)bl)){
+ npc_unload(nd);
+ } else if (bl->type == BL_MOB && (md = (struct mob_data *)bl)){
+ if (md->lootitem)
+ aFree(md->lootitem);
+ aFree(md);
+ } else if (bl->type == BL_PET && (pd = (struct pet_data *)bl)){
+ aFree(pd);
+ }
+ }
+ }
+
+ ev_db->destroy(ev_db, NULL);
+ //There is no free function for npcname_db because at this point there shouldn't be any npcs left!
+ //So if there is anything remaining, let the memory manager catch it and report it.
+ npcname_db->destroy(npcname_db, NULL);
+
+ npc_clearsrcfile();
+
+ return 0;
+}
+
+/*==========================================
+ * npc‰Šú‰»
+ *------------------------------------------
+ */
+int do_init_npc(void)
+{
+ struct npc_src_list *nsl;
+ time_t last_time = time(0);
+ int busy = 0;
+ char c = '-';
+
+ // indoorrswtable.txt and etcinfo.txt [Celest]
+ if (battle_config.indoors_override_grffile)
+ npc_read_indoors();
+
+ // comparing only the first 24 chars of labels that are 50 chars long isn't that nice
+ // will cause "duplicated" labels where actually no dup is...
+ ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51);
+ npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH);
+
+ memset(&ev_tm_b, -1, sizeof(ev_tm_b));
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ current_file = NULL;
+ printf("\r");
+ if (script_config.verbose_mode)
+ ShowStatus ("Loading NPCs... %-53s", nsl->name);
+ else {
+ ShowStatus("Loading NPCs... Working: ");
+ if (last_time != time(0)) {
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ }
+ printf("[%c]",c);
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ add_timer_func_list(npc_walktimer,"npc_walktimer"); // [Valaris]
+ add_timer_func_list(npc_event_timer,"npc_event_timer");
+ add_timer_func_list(npc_event_do_clock,"npc_event_do_clock");
+ add_timer_func_list(npc_timerevent,"npc_timerevent");
+
+ return 0;
+}
+// [Lance]
+ int npc_changename(const char *name, const char *newname, short look){
+ struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+ npc_enable(name,0);
+ strcpy(nd->name,newname);
+ nd->class_ = look;
+ strdb_put(npcname_db,nd->name,nd);
+ npc_enable(newname,1);
+ return 0;
+}
diff --git a/src/map/npc.h b/src/map/npc.h
new file mode 100644
index 000000000..ad482d7d1
--- /dev/null
+++ b/src/map/npc.h
@@ -0,0 +1,71 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _NPC_H_
+#define _NPC_H_
+
+#define START_NPC_NUM 110000000
+
+#define WARP_CLASS 45
+#define WARP_DEBUG_CLASS 722
+#define INVISIBLE_CLASS 32767
+
+//Checks if a given id is a valid npc id. [Skotlex]
+//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001)
+#define npcdb_checkid(id) ((id >= 46 && id <= 125) || (id >= 700 && id <= 1000))
+
+#ifdef PCRE_SUPPORT
+void npc_chat_finalize(struct npc_data *nd);
+#endif
+int npc_chat_sub(struct block_list *bl, va_list ap);
+int npc_event_dequeue(struct map_session_data *sd);
+int npc_event_timer(int tid,unsigned int tick,int id,int data);
+int npc_event(struct map_session_data *sd,const unsigned char *npcname,int);
+int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command);
+int npc_touch_areanpc(struct map_session_data *,int,int,int);
+int npc_click(struct map_session_data *,int);
+int npc_scriptcont(struct map_session_data *,int);
+int npc_checknear(struct map_session_data *,int);
+int npc_buysellsel(struct map_session_data *,int,int);
+int npc_buylist(struct map_session_data *,int,unsigned short *);
+int npc_selllist(struct map_session_data *,int,unsigned short *);
+int npc_parse_mob(char *w1,char *w2,char *w3,char *w4);
+int npc_parse_mob2 (struct mob_list *, int cached); // [Wizputer]
+int npc_parse_warp(char *w1,char *w2,char *w3,char *w4);
+int npc_globalmessage(const char *name,char *mes);
+
+int npc_enable(const char *name,int flag);
+int npc_changename(const char *name, const char *newname, short look); // [Lance]
+struct npc_data* npc_name2id(const char *name);
+
+int npc_walktoxy(struct npc_data *nd,int x,int y,int easy); // npc walking [Valaris]
+int npc_stop_walking(struct npc_data *nd,int type);
+int npc_changestate(struct npc_data *nd,int state,int type);
+
+int npc_get_new_npc_id(void);
+
+void npc_addsrcfile(char *);
+void npc_delsrcfile(char *);
+void npc_parsesrcfile(char *);
+int do_final_npc(void);
+int do_init_npc(void);
+int npc_event_do_oninit(void);
+int npc_do_ontimer(int,int);
+
+int npc_event_doall(const unsigned char *name);
+int npc_event_do(const unsigned char *name);
+int npc_event_doall_id(const unsigned char *name, int id);
+
+int npc_timerevent_start(struct npc_data *nd, int rid);
+int npc_timerevent_stop(struct npc_data *nd);
+int npc_gettimerevent_tick(struct npc_data *nd);
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer);
+int npc_remove_map(struct npc_data *nd);
+int npc_unload(struct npc_data *nd);
+int npc_reload(void);
+
+extern char *current_file;
+
+#endif
+
diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c
new file mode 100644
index 000000000..6c5b513e8
--- /dev/null
+++ b/src/map/npc_chat.c
@@ -0,0 +1,519 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifdef PCRE_SUPPORT
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __WIN32
+#define __USE_W32_SOCKETS
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <time.h>
+
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "status.h"
+#include "npc.h"
+#include "chat.h"
+#include "script.h"
+#include "battle.h"
+
+#include "pcre.h"
+
+/**
+ * Written by MouseJstr in a vision... (2/21/2005)
+ *
+ * This allows you to make npc listen for spoken text (global
+ * messages) and pattern match against that spoken text using perl
+ * regular expressions.
+ *
+ * Please feel free to copy this code into your own personal ragnarok
+ * servers or distributions but please leave my name. Also, please
+ * wait until I've put it into the main eA branch which means I
+ * believe it is ready for distribution.
+ *
+ * So, how do people use this?
+ *
+ * The first and most important function is defpattern
+ *
+ * defpattern 1, "[^:]+: (.*) loves (.*)", "label";
+ *
+ * this defines a new pattern in set 1 using perl syntax
+ * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm)
+ * and tells it to jump to the supplied label when the pattern
+ * is matched.
+ *
+ * each of the matched Groups will result in a variable being
+ * set ($p1$ through $p9$ with $p0$ being the entire string)
+ * before the script gets executed.
+ *
+ * activatepset 1;
+ *
+ * This activates a set of patterns.. You can have many pattern
+ * sets defined and many active all at once. This feature allows
+ * you to set up "conversations" and ever changing expectations of
+ * the pattern matcher
+ *
+ * deactivatepset 1;
+ *
+ * turns off a pattern set;
+ *
+ * deactivatepset -1;
+ *
+ * turns off ALL pattern sets;
+ *
+ * deletepset 1;
+ *
+ * deletes a pset
+ */
+
+/* Structure containing all info associated with a single pattern
+ block */
+
+struct pcrematch_entry {
+ struct pcrematch_entry *next_;
+ char *pattern_;
+ pcre *pcre_;
+ pcre_extra *pcre_extra_;
+ char *label_;
+};
+
+/* A set of patterns that can be activated and deactived with a single
+ command */
+
+struct pcrematch_set {
+ struct pcrematch_set *next_, *prev_;
+ struct pcrematch_entry *head_;
+ int setid_;
+};
+
+/*
+ * Entire data structure hung off a NPC
+ *
+ * The reason I have done it this way (a void * in npc_data and then
+ * this) was to reduce the number of patches that needed to be applied
+ * to a ragnarok distribution to bring this code online. I
+ * also wanted people to be able to grab this one file to get updates
+ * without having to do a large number of changes.
+ */
+
+struct npc_parse {
+ struct pcrematch_set *active_;
+ struct pcrematch_set *inactive_;
+};
+
+
+/**
+ * delete everythign associated with a entry
+ *
+ * This does NOT do the list management
+ */
+
+void finalize_pcrematch_entry(struct pcrematch_entry *e) {
+//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex]
+#ifndef _WIN32
+ if (e->pcre_) {
+ free(e->pcre_);
+ e->pcre_ = NULL;
+ }
+#endif
+ if (e->pcre_extra_) {
+ free(e->pcre_extra_);
+ e->pcre_ = NULL;
+ }
+ aFree(e->pattern_);
+ aFree(e->label_);
+}
+
+/**
+ * Lookup (and possibly create) a new set of patterns by the set id
+ */
+static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
+{
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ nd->chatdb = npcParse = (struct npc_parse *)
+ aCalloc(sizeof(struct npc_parse), 1);
+
+ pcreset = npcParse->active_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ pcreset = npcParse->inactive_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+
+ if (pcreset == NULL) {
+ pcreset = (struct pcrematch_set *)
+ aCalloc(sizeof(struct pcrematch_set), 1);
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ pcreset->prev_ = 0;
+ npcParse->inactive_ = pcreset;
+ pcreset->setid_ = setid;
+ }
+
+ return pcreset;
+}
+
+/**
+ * activate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void activate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to activate...
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in inactive list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->active_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->active_ = pcreset;
+}
+
+/**
+ * deactivate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void deactivate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ if (setid == -1) {
+ while(npcParse->active_ != NULL)
+ deactivate_pcreset(nd, npcParse->active_->setid_);
+ return;
+ }
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in active list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->active_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->inactive_ = pcreset;
+}
+
+/**
+ * delete a set of patterns.
+ */
+static void delete_pcreset(struct npc_data *nd,int setid) {
+ int active = 1;
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL) {
+ active = 0;
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ }
+ if (pcreset == NULL)
+ return;
+
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+
+ if(active)
+ npcParse->active_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = NULL;
+
+ while (pcreset->head_) {
+ struct pcrematch_entry *n = pcreset->head_->next_;
+ finalize_pcrematch_entry(pcreset->head_);
+ aFree(pcreset->head_); // Cleanin' the last ones.. [Lance]
+ pcreset->head_ = n;
+ }
+
+ aFree(pcreset);
+}
+
+/**
+ * create a new pattern entry
+ */
+static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) {
+ struct pcrematch_entry * e = (struct pcrematch_entry *)
+ aCalloc(sizeof(struct pcrematch_entry), 1);
+ struct pcrematch_entry * last = set->head_;
+
+ // Normally we would have just stuck it at the end of the list but
+ // this doesn't sink up with peoples usage pattern. They wanted
+ // the items defined first to have a higher priority then the
+ // items defined later.. as a result, we have to do some work up
+ // front..
+
+ /* if we are the first pattern, stick us at the end */
+ if (last == NULL) {
+ set->head_ = e;
+ return e;
+ }
+
+ /* Look for the last entry */
+ while (last->next_ != NULL)
+ last = last->next_;
+
+ last->next_ = e;
+ e->next_ = NULL;
+
+ return e;
+}
+
+/**
+ * define/compile a new pattern
+ */
+
+void npc_chat_def_pattern(struct npc_data *nd, int setid,
+ const char *pattern, const char *label)
+{
+ const char *err;
+ int erroff;
+
+ struct pcrematch_set * s = lookup_pcreset(nd, setid);
+ struct pcrematch_entry *e = create_pcrematch_entry(s);
+ e->pattern_ = aStrdup(pattern);
+ e->label_ = aStrdup(label);
+ e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
+ e->pcre_extra_ = pcre_study(e->pcre_, 0, &err);
+}
+
+/**
+ * Delete everything associated with a NPC concerning the pattern
+ * matching code
+ *
+ * this could be more efficent but.. how often do you do this?
+ */
+void npc_chat_finalize(struct npc_data *nd)
+{
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return;
+
+ while(npcParse->active_)
+ delete_pcreset(nd, npcParse->active_->setid_);
+
+ while(npcParse->inactive_)
+ delete_pcreset(nd, npcParse->inactive_->setid_);
+
+ // Additional cleaning up [Lance]
+ aFree(npcParse);
+}
+
+/**
+ * Handler called whenever a global message is spoken in a NPC's area
+ */
+int npc_chat_sub(struct block_list *bl, va_list ap)
+{
+ struct npc_data *nd = (struct npc_data *)bl;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ unsigned char *msg;
+ int len, pos, i;
+ struct map_session_data *sd;
+ struct npc_label_list *lst;
+ struct pcrematch_set *pcreset;
+
+ // Not interested in anything you might have to say...
+ if (npcParse == NULL || npcParse->active_ == NULL)
+ return 0;
+
+ msg = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ sd = va_arg(ap,struct map_session_data *);
+
+ // grab the active list
+ pcreset = npcParse->active_;
+
+ // interate across all active sets
+ while (pcreset != NULL) {
+ struct pcrematch_entry *e = pcreset->head_;
+ // interate across all patterns in that set
+ while (e != NULL) {
+ int offsets[20];
+ char buf[255];
+ // perform pattern match
+ int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0,
+ 0, offsets, sizeof(offsets) / sizeof(offsets[0]));
+ if (r >= 0) {
+ // save out the matched strings
+ switch (r) {
+ case 10:
+ memcpy(buf, &msg[offsets[18]], offsets[19]);
+ buf[offsets[19]] = '\0';
+ set_var(sd, "$p9$", buf);
+ case 9:
+ memcpy(buf, &msg[offsets[16]], offsets[17]);
+ buf[offsets[17]] = '\0';
+ set_var(sd, "$p8$", buf);
+ case 8:
+ memcpy(buf, &msg[offsets[14]], offsets[15]);
+ buf[offsets[15]] = '\0';
+ set_var(sd, "$p7$", buf);
+ case 7:
+ memcpy(buf, &msg[offsets[12]], offsets[13]);
+ buf[offsets[13]] = '\0';
+ set_var(sd, "$p6$", buf);
+ case 6:
+ memcpy(buf, &msg[offsets[10]], offsets[11]);
+ buf[offsets[11]] = '\0';
+ set_var(sd, "$p5$", buf);
+ case 5:
+ memcpy(buf, &msg[offsets[8]], offsets[9]);
+ buf[offsets[9]] = '\0';
+ set_var(sd, "$p4$", buf);
+ case 4:
+ memcpy(buf, &msg[offsets[6]], offsets[7]);
+ buf[offsets[7]] = '\0';
+ set_var(sd, "$p3$", buf);
+ case 3:
+ memcpy(buf, &msg[offsets[4]], offsets[5]);
+ buf[offsets[5]] = '\0';
+ set_var(sd, "$p2$", buf);
+ case 2:
+ memcpy(buf, &msg[offsets[2]], offsets[3]);
+ buf[offsets[3]] = '\0';
+ set_var(sd, "$p1$", buf);
+ case 1:
+ memcpy(buf, &msg[offsets[0]], offsets[1]);
+ buf[offsets[1]] = '\0';
+ set_var(sd, "$p0$", buf);
+ }
+
+ // find the target label.. this sucks..
+ lst=nd->u.scr.label_list;
+ pos = -1;
+ for (i = 0; i < nd->u.scr.label_list_num; i++) {
+ if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) {
+ pos = lst[i].pos;
+ break;
+ }
+ }
+ if (pos == -1) {
+ ShowWarning("Unable to find label: %s", e->label_);
+ // unable to find label... do something..
+ return 0;
+ }
+ // run the npc script
+ run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id);
+ return 0;
+ }
+ e = e->next_;
+ }
+ pcreset = pcreset->next_;
+ }
+
+ return 0;
+}
+
+// Various script builtins used to support these functions
+
+int buildin_defpattern(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ char *label=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_chat_def_pattern(nd, setid, pattern, label);
+
+ return 0;
+}
+
+int buildin_activatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ activate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deactivatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ deactivate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deletepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ delete_pcreset(nd, setid);
+
+ return 0;
+}
+
+
+#endif
diff --git a/src/map/party.c b/src/map/party.c
new file mode 100644
index 000000000..e0b32df0f
--- /dev/null
+++ b/src/map/party.c
@@ -0,0 +1,742 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/timer.h"
+#include "../common/socket.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "party.h"
+#include "pc.h"
+#include "map.h"
+#include "battle.h"
+#include "intif.h"
+#include "clif.h"
+#include "log.h"
+#include "skill.h"
+#include "status.h"
+
+#define PARTY_SEND_XY_INVERVAL 1000 // À•W‚â‚g‚o‘—M‚ÌŠÔŠu
+
+static struct dbt* party_db;
+static struct party* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex]
+int party_share_level = 10;
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data);
+/*==========================================
+ * I—¹
+ *------------------------------------------
+ */
+void do_final_party(void)
+{
+ party_db->destroy(party_db,NULL);
+}
+// ‰Šú‰»
+void do_init_party(void)
+{
+ party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ add_timer_func_list(party_send_xy_timer,"party_send_xy_timer");
+ add_timer_interval(gettick()+PARTY_SEND_XY_INVERVAL,party_send_xy_timer,0,0,PARTY_SEND_XY_INVERVAL);
+}
+
+// ŒŸõ
+struct party *party_search(int party_id)
+{
+ if (party_cache && party_cache->party_id == party_id)
+ return party_cache;
+
+ party_cache = idb_get(party_db,party_id);
+ return party_cache;
+}
+int party_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct party *p=(struct party *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct party **);
+ if(strncmpi(p->name,str,NAME_LENGTH)==0)
+ *dst=p;
+ return 0;
+}
+// ƒp[ƒeƒB–¼ŒŸõ
+struct party* party_searchname(char *str)
+{
+ struct party *p=NULL;
+ party_db->foreach(party_db,party_searchname_sub,str,&p);
+ return p;
+}
+// 쬗v‹
+int party_create(struct map_session_data *sd,char *name,int item,int item2)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.party_id==0)
+ intif_create_party(sd,name,item,item2);
+ else
+ clif_party_created(sd,2);
+ return 0;
+}
+
+// 쬉”Û
+int party_created(int account_id,int char_id,int fail,int party_id,char *name)
+{
+ struct map_session_data *sd;
+ sd=map_id2sd(account_id);
+
+ nullpo_retr(0, sd);
+ if (sd->status.char_id != char_id)
+ return 0; //unlikely failure...
+
+ if(fail==0){
+ struct party *p;
+ sd->status.party_id=party_id;
+ if(idb_get(party_db,party_id)!=NULL){
+ ShowFatalError("party: id already exists!\n");
+ exit(1);
+ }
+ p=(struct party *)aCalloc(1,sizeof(struct party));
+ p->party_id=party_id;
+ memcpy(p->name, name, NAME_LENGTH);
+ idb_put(party_db,party_id,p);
+ clif_party_created(sd,0); //Success message
+ clif_charnameupdate(sd); //Update other people's display. [Skotlex]
+ }else{
+ clif_party_created(sd,1);
+ }
+ return 0;
+}
+
+// î•ñ—v‹
+int party_request_info(int party_id)
+{
+ return intif_request_partyinfo(party_id);
+}
+
+// Š‘®ƒLƒƒƒ‰‚ÌŠm”F
+int party_check_member(struct party *p)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, p);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if((sd = all_sd[i]) && sd->status.party_id==p->party_id)
+ {
+ int j,f=1;
+ for(j=0;j<MAX_PARTY;j++){
+ if(p->member[j].account_id==sd->status.account_id &&
+ p->member[j].char_id==sd->status.char_id)
+ {
+ f=0;
+ break;
+ }
+ }
+
+ if(f){
+ sd->status.party_id=0;
+ if(battle_config.error_log)
+ ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ return 0;
+}
+
+// î•ñŠ“¾Ž¸”si‚»‚ÌID‚̃Lƒƒƒ‰‚ð‘S•”–¢Š‘®‚É‚·‚éj
+int party_recv_noinfo(int party_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd = all_sd[i]) && sd->status.party_id==party_id)
+ sd->status.party_id=0;
+ }
+ return 0;
+}
+
+static void* create_party(DBKey key, va_list args) {
+ struct party *p;
+ p=(struct party *)aCalloc(1,sizeof(struct party));
+ return p;
+}
+// î•ñŠ“¾
+int party_recv_info(struct party *sp)
+{
+ struct map_session_data *sd;
+ struct party *p;
+ int i;
+
+ nullpo_retr(0, sp);
+
+ p= idb_ensure(party_db, sp->party_id, create_party);
+ if (!p->party_id) //party just received.
+ party_check_member(sp);
+ memcpy(p,sp,sizeof(struct party));
+
+ for(i=0;i<MAX_PARTY;i++){ // sd‚ÌÝ’è
+ if (!p->member[i].account_id) {
+ p->member[i].sd=NULL;
+ continue;
+ }
+ sd = map_id2sd(p->member[i].account_id);
+ p->member[i].sd = (sd!=NULL && sd->status.party_id==p->party_id && sd->status.char_id == p->member[i].char_id && !sd->state.waitingdisconnect)?sd:NULL;
+ }
+
+
+ for(i=0;i<MAX_PARTY;i++){ // Ý’èî•ñ‚Ì‘—M
+ sd = p->member[i].sd;
+ if(!sd)
+ continue;
+ // Refresh hp/xy state [LuzZza]
+ clif_party_hp(sd);
+ clif_party_xy(sd);
+ if(sd->state.party_sent==0){
+ clif_party_main_info(p,-1);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,-1);
+ sd->state.party_sent=1;
+ }
+ }
+
+ return 0;
+}
+
+// ƒp[ƒeƒB‚Ö‚ÌŠ©—U
+int party_invite(struct map_session_data *sd,int account_id)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+ struct party *p=party_search(sd->status.party_id);
+ int i,flag=0;
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || p==NULL)
+ return 0;
+ if(!battle_config.invite_request_check) {
+ if (tsd->guild_invite>0 || tsd->trade_partner) { // ‘ŠŽè‚ªŽæˆø’†‚©‚Ç‚¤‚©
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ }
+ if( tsd->status.party_id>0 || tsd->party_invite>0 ){ // ‘ŠŽè‚ÌŠ‘®Šm”F
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id == 0) //Room for a new member.
+ flag = 1;
+ if(p->member[i].account_id==account_id && p->member[i].char_id==tsd->status.char_id){
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ }
+ if (!flag) { //Full party.
+ clif_party_inviteack(sd,tsd->status.name,2);
+ return 0;
+ }
+
+ tsd->party_invite=sd->status.party_id;
+ tsd->party_invite_account=sd->status.account_id;
+
+ clif_party_invite(sd,tsd);
+ return 1;
+}
+// ƒp[ƒeƒBŠ©—U‚Ö‚Ì•Ô“š
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+
+ nullpo_retr(0, sd);
+
+ if(flag==1){ // ³‘ø
+ //interŽI‚֒ljÁ—v‹
+ intif_party_addmember( sd->party_invite, sd);
+ return 0;
+ }
+ else { // ‹‘”Û
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+ if(tsd==NULL)
+ return 0;
+ clif_party_inviteack(tsd,sd->status.name,1);
+ }
+ return 0;
+}
+// ƒp[ƒeƒB‚ª’ljÁ‚³‚ꂽ
+int party_member_added(int party_id,int account_id,int char_id, int flag)
+{
+ struct map_session_data *sd = map_id2sd(account_id),*sd2;
+
+ if(sd == NULL || sd->status.char_id != char_id){
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("party: member added error %d is not online\n",account_id);
+ intif_party_leave(party_id,account_id,char_id); // ƒLƒƒƒ‰‘¤‚‰“o˜^‚…‚«‚ˆ‚©‚‚½‚½‚Ÿ’E‘ž—v‹‚ào‚·
+ }
+ return 0;
+ }
+
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+
+ sd2=map_id2sd(sd->party_invite_account);
+ if (sd2)
+ clif_party_inviteack(sd2,sd->status.name,flag?2:0);
+ if(flag)
+ return 0;
+
+ sd->state.party_sent=0;
+ sd->status.party_id=party_id;
+
+ party_check_conflict(sd);
+ clif_charnameupdate(sd); //Update char name's display [Skotlex]
+ clif_party_hp(sd);
+ clif_party_xy(sd);
+ return 0;
+}
+// ƒp[ƒeƒBœ–¼—v‹
+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++){ // ƒŠ[ƒ_[‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ if(p->member[i].account_id==sd->status.account_id && p->member[i].char_id==sd->status.char_id) {
+ if(p->member[i].leader)
+ break;
+ return 0;
+ }
+ }
+ if (i >= MAX_PARTY) //Request from someone not in party? o.O
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){ // Š‘®‚µ‚Ä‚¢‚é‚©’²‚ׂé
+ if(p->member[i].account_id==account_id && strncmp(p->member[i].name,name,NAME_LENGTH)==0)
+ {
+ intif_party_leave(p->party_id,account_id,p->member[i].char_id);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// ƒp[ƒeƒB’E‘Þ—v‹
+int party_leave(struct map_session_data *sd)
+{
+ struct party *p;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (p = party_search(sd->status.party_id)) == NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){ // Š‘®‚µ‚Ä‚¢‚é‚©
+ if(p->member[i].account_id==sd->status.account_id && p->member[i].char_id==sd->status.char_id){
+ intif_party_leave(p->party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+ }
+ }
+ return 0;
+}
+// ƒp[ƒeƒBƒƒ“ƒo‚ª’E‘Þ‚µ‚½
+int party_member_leaved(int party_id,int account_id,int char_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct party *p=party_search(party_id);
+ if (sd && sd->status.char_id != char_id) //Wrong target
+ sd = NULL;
+ if(p!=NULL){
+ int i;
+ for(i=0;i<MAX_PARTY;i++)
+ if(p->member[i].account_id==account_id &&
+ p->member[i].char_id==char_id){
+ clif_party_leaved(p,sd,account_id,p->member[i].name,0x00);
+ p->member[i].account_id=0;
+ p->member[i].char_id=0;
+ p->member[i].sd=NULL;
+ }
+ }
+ if(sd!=NULL && sd->status.party_id==party_id){
+ sd->status.party_id=0;
+ sd->state.party_sent=0;
+ clif_charnameupdate(sd); //Update name display [Skotlex]
+ }
+ return 0;
+}
+// ƒp[ƒeƒB‰ðŽU’Ê’m
+int party_broken(int party_id)
+{
+ struct party *p;
+ int i;
+ if( (p=party_search(party_id))==NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].sd!=NULL){
+ clif_party_leaved(p,p->member[i].sd,
+ p->member[i].account_id,p->member[i].name,0x10);
+ p->member[i].sd->status.party_id=0;
+ p->member[i].sd->state.party_sent=0;
+ }
+ }
+ if (party_cache && party_cache->party_id == party_id)
+ party_cache = NULL;
+ idb_remove(party_db,party_id);
+ return 0;
+}
+// ƒp[ƒeƒB‚ÌÝ’è•ÏX—v‹
+int party_changeoption(struct map_session_data *sd,int exp,int flag)
+{
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0)
+ return 0;
+ intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,flag);
+ return 0;
+}
+// ƒp[ƒeƒB‚ÌÝ’è•ÏX’Ê’m
+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;
+}
+
+// ƒp[ƒeƒBƒƒ“ƒo‚̈ړ®’Ê’m
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv)
+{
+ struct party *p;
+ int i;
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ struct party_member *m=&p->member[i];
+ /* This can never happen...
+ if( m == NULL ){
+ ShowError("party_recv_movemap nullpo?\n");
+ return 0;
+ }
+ */
+ if(m->account_id==account_id && m->char_id==char_id){
+ m->map = map;
+ m->online=online;
+ m->lv=lv;
+ //Check if they still exist on this map server
+ sd = map_id2sd(m->account_id);
+ p->member[i].sd=(sd!=NULL && sd->status.party_id==p->party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL;
+ //if so, clear their coordinates as they just changed maps.
+ if (p->member[i].sd) {
+ sd->party_x=-1;
+ sd->party_y=-1;
+ }
+ break;
+ }
+ }
+ if(i==MAX_PARTY){
+ if(battle_config.error_log)
+ ShowError("party: not found member %d on %d[%s]",account_id,party_id,p->name);
+ return 0;
+ }
+
+ clif_party_info(p,-1);
+ return 0;
+}
+
+// ƒp[ƒeƒBƒƒ“ƒo‚̈ړ®
+int party_send_movemap(struct map_session_data *sd)
+{
+ struct party *p;
+
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0 )
+ return 0;
+ intif_party_changemap(sd,1);
+
+ if( sd->state.party_sent ) // ‚à‚¤ƒp[ƒeƒBƒf[ƒ^‚Í‘—MÏ‚Ý
+ return 0;
+
+ // ‹£‡Šm”F
+ party_check_conflict(sd);
+
+ // ‚ ‚é‚È‚çƒp[ƒeƒBî•ñ‘—M
+ if( (p=party_search(sd->status.party_id))!=NULL ){
+ party_check_member(p); // Š‘®‚ðŠm”F‚·‚é
+ if(sd->status.party_id==p->party_id){
+ clif_party_main_info(p,sd->fd);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,sd->fd);
+ sd->state.party_sent=1;
+ }
+ }
+
+ return 0;
+}
+// ƒp[ƒeƒBƒƒ“ƒo‚̃ƒOƒAƒEƒg
+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‚ª–³Œø‚É‚È‚é‚̂Ńp[ƒeƒBî•ñ‚©‚çíœ
+ 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;
+}
+// ƒp[ƒeƒBƒƒbƒZ[ƒW‘—M
+int party_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ if(sd->status.party_id==0)
+ return 0;
+ intif_party_message(sd->status.party_id,sd->status.account_id,mes,len);
+ party_recv_message(sd->status.party_id,sd->status.account_id,mes,len);
+ //Chat Logging support Type 'P'
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&4 //if Party bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+
+// ƒp[ƒeƒBƒƒbƒZ[ƒWŽóM
+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;
+}
+// ƒp[ƒeƒB‹£‡Šm”F
+int party_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv)
+{
+ struct party *p;
+ struct map_session_data *p_sd;
+ int i;
+
+ if(!party_id || (p=party_search(party_id))==NULL)
+ return 0;
+ for(i=0;i<MAX_PARTY;i++){
+ if ((p_sd = p->member[i].sd) == NULL)
+ continue;
+ switch(skillid) {
+ case TK_COUNTER: //Increase Triple Attack rate of Monks.
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK
+ && sd->bl.m == p_sd->bl.m
+ && pc_checkskill(p_sd,MO_TRIPLEATTACK)) {
+ int rate = 50 +50*skilllv; //+100/150/200% success rate
+ status_change_start(&p_sd->bl,SC_SKILLRATE_UP,MO_TRIPLEATTACK,rate,0,0,skill_get_time(SG_FRIEND, 1),0);
+ }
+ break;
+ case MO_TRIPLEATTACK: //Increase Counter rate of Star Gladiators
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR
+ && sd->bl.m == p_sd->bl.m
+ && pc_checkskill(p_sd,TK_COUNTER)) {
+ int rate = 50 +50*pc_checkskill(p_sd,TK_COUNTER); //+100/150/200% success rate
+ status_change_start(&p_sd->bl,SC_SKILLRATE_UP,TK_COUNTER,rate,0,0,skill_get_time(SG_FRIEND, 1),0);
+ }
+ break;
+ case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice
+ if ((p_sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE
+ && sd->bl.m == p_sd->bl.m)
+ return 1;
+ break;
+ case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon
+ if ((p_sd->class_&MAPID_NOVICE) == MAPID_TAEKWON
+ && sd->bl.m == p_sd->bl.m)
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+// ˆÊ’u‚â‚g‚o’Ê’m—p
+int party_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct party *p=(struct party *)data;
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ if((sd=p->member[i].sd)!=NULL){
+ // À•W’Ê’m
+ if(sd->party_x!=sd->bl.x || sd->party_y!=sd->bl.y){
+ clif_party_xy(sd);
+ sd->party_x=sd->bl.x;
+ sd->party_y=sd->bl.y;
+ }
+ }
+ }
+ return 0;
+}
+// ˆÊ’u‚â‚g‚o’Ê’m
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ party_db->foreach(party_db,party_send_xy_timer_sub,tick);
+ return 0;
+}
+
+// ˆÊ’u’Ê’mƒNƒŠƒA
+int party_send_xy_clear(struct party *p)
+{
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ if((sd=p->member[i].sd)!=NULL){
+ sd->party_x=-1;
+ sd->party_y=-1;
+ }
+ }
+ return 0;
+}
+
+// exp share and added zeny share [Valaris]
+int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny)
+{
+ struct map_session_data* sd[MAX_PARTY];
+ int i;
+ short c, bonus =100; // modified [Valaris]
+ unsigned long base, job;
+
+ nullpo_retr(0, p);
+
+ for (i = c = 0; i < MAX_PARTY; i++)
+ if ((sd[c] = p->member[i].sd)!=NULL && sd[c]->bl.m == map && !pc_isdead(sd[c])) {
+ if (battle_config.idle_no_share && (pc_issit(sd[c]) || sd[c]->chatID || (sd[c]->idletime < (last_tick - battle_config.idle_no_share))))
+ continue;
+ c++;
+ }
+ if (c < 1)
+ return 0;
+ if (battle_config.party_even_share_bonus) //Valaris's even share exp bonus equation.
+ bonus += (battle_config.party_even_share_bonus*c*(c-1)/10); //Changed Valaris's bonus switch to an equation [Skotlex]
+ else //Official kRO/iRO sites state that the even share bonus is 10% per additional party member.
+ bonus += (c-1)*10;
+ base = (unsigned long)(base_exp/c)*bonus/100;
+ job = (unsigned long)(job_exp/c)*bonus/100;
+ if (base > 0x7fffffff)
+ base = 0x7fffffff;
+ if (job > 0x7fffffff)
+ job = 0x7fffffff;
+ for (i = 0; i < c; i++)
+ {
+ pc_gainexp(sd[i], base, job);
+ if (battle_config.zeny_from_mobs) // zeny from mobs [Valaris]
+ pc_getzeny(sd[i],bonus*zeny/(c*100));
+ }
+ return 0;
+}
+
+int party_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.party_id)
+ clif_party_xy_remove(sd);
+ return 0;
+}
+
+// To use for Taekwon's "Fighting Chant"
+// int c = 0;
+// party_foreachsamemap(party_sub_count, sd, 0, &c);
+int party_sub_count(struct block_list *bl, va_list ap)
+{
+ int *c = va_arg(ap, int*);
+
+ (*c)++;
+ return 1;
+}
+
+// “¯‚¶ƒ}ƒbƒv‚̃p[ƒeƒBƒƒ“ƒo[‘S‘̂Ɉ—‚ð‚©‚¯‚é
+// type==0 “¯‚¶ƒ}ƒbƒv
+// !=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) // —LŒø‚©‚Ç‚¤‚©ƒ`ƒFƒbƒN
+ func(list[i],ap);
+
+ map_freeblock_unlock(); // ‰ð•ú‚ð‹–‰Â‚·‚é
+
+ va_end(ap);
+}
diff --git a/src/map/party.h b/src/map/party.h
new file mode 100644
index 000000000..4e9601d68
--- /dev/null
+++ b/src/map/party.h
@@ -0,0 +1,47 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PARTY_H_
+#define _PARTY_H_
+
+#include <stdarg.h>
+
+extern int party_share_level;
+struct party;
+struct map_session_data;
+struct block_list;
+
+void do_init_party(void);
+void do_final_party(void);
+struct party *party_search(int party_id);
+struct party* party_searchname(char *str);
+
+int party_create(struct map_session_data *sd,char *name, int item, int item2);
+int party_created(int account_id,int char_id,int fail,int party_id,char *name);
+int party_request_info(int party_id);
+int party_invite(struct map_session_data *sd,int account_id);
+int party_member_added(int party_id,int account_id,int char_id,int flag);
+int party_leave(struct map_session_data *sd);
+int party_removemember(struct map_session_data *sd,int account_id,char *name);
+int party_member_leaved(int party_id,int account_id,int char_id);
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag);
+int party_recv_noinfo(int party_id);
+int party_recv_info(struct party *sp);
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv);
+int party_broken(int party_id);
+int party_optionchanged(int party_id,int account_id,int exp,int item,int flag);
+int party_changeoption(struct map_session_data *sd,int exp,int item);
+int party_send_movemap(struct map_session_data *sd);
+int party_send_logout(struct map_session_data *sd);
+int party_send_message(struct map_session_data *sd,char *mes,int len);
+int party_recv_message(int party_id,int account_id,char *mes,int len);
+int party_check_conflict(struct map_session_data *sd);
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv);
+int party_send_xy_clear(struct party *p);
+int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny);
+int party_send_dot_remove(struct map_session_data *sd);
+int party_sub_count(struct block_list *bl, va_list ap);
+void party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...);
+
+
+#endif
diff --git a/src/map/path.c b/src/map/path.c
new file mode 100644
index 000000000..ad5c06f0f
--- /dev/null
+++ b/src/map/path.c
@@ -0,0 +1,483 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "map.h"
+#include "battle.h"
+#include "nullpo.h"
+#include "../common/showmsg.h"
+
+#define MAX_HEAP 150
+
+struct tmp_path { short x,y,dist,before,cost; char dir,flag;};
+#define calc_index(x,y) (((x)+(y)*MAX_WALKPATH) & (MAX_WALKPATH*MAX_WALKPATH-1))
+
+/*==========================================
+ * Œo˜H’Tõ•â•heap push
+ *------------------------------------------
+ */
+static void push_heap_path(int *heap,struct tmp_path *tp,int index)
+{
+ int i,h;
+
+ if( heap == NULL || tp == NULL ){
+ ShowError("push_heap_path nullpo\n");
+ return;
+ }
+
+ heap[0]++;
+
+ for(h=heap[0]-1,i=(h-1)/2;
+ h>0 && tp[index].cost<tp[heap[i+1]].cost;
+ i=(h-1)/2)
+ heap[h+1]=heap[i+1],h=i;
+ heap[h+1]=index;
+}
+
+/*==========================================
+ * Œo˜H’Tõ•â•heap update
+ * cost‚ªŒ¸‚Á‚½‚̂Ū‚Ì•û‚ÖˆÚ“®
+ *------------------------------------------
+ */
+static void update_heap_path(int *heap,struct tmp_path *tp,int index)
+{
+ int i,h;
+
+ nullpo_retv(heap);
+ nullpo_retv(tp);
+
+ for(h=0;h<heap[0];h++)
+ if(heap[h+1]==index)
+ break;
+ if(h==heap[0]){
+ ShowError("update_heap_path bug\n");
+ exit(1);
+ }
+ for(i=(h-1)/2;
+ h>0 && tp[index].cost<tp[heap[i+1]].cost;
+ i=(h-1)/2)
+ heap[h+1]=heap[i+1],h=i;
+ heap[h+1]=index;
+}
+
+/*==========================================
+ * Œo˜H’Tõ•â•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ŒvŽZ
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * •K—v‚È‚çpath‚ð’ljÁ/C³‚·‚é
+ *------------------------------------------
+ */
+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)‚ªˆÚ“®•s‰Â”\’n‘Ñ‚©‚Ç‚¤‚©
+ * flag 0x10000 ‰“‹——£UŒ‚”»’è
+ *------------------------------------------
+ */
+static int can_place(struct map_data *m,int x,int y,int flag)
+{
+ nullpo_retr(0, m);
+
+ if(map_getcellp(m,x,y,CELL_CHKPASS))
+ return 1;
+ if((flag&0x10000)&&map_getcellp(m,x,y,CELL_CHKGROUND))
+ return 1;
+ return 0;
+}
+
+/*==========================================
+ * (x0,y0)‚©‚ç(x1,y1)‚Ö1•à‚ňړ®‰Â”\‚©ŒvŽZ
+ *------------------------------------------
+ */
+static int can_move(struct map_data *m,int x0,int y0,int x1,int y1,int flag)
+{
+ nullpo_retr(0, m);
+
+ if(x0-x1<-1 || x0-x1>1 || y0-y1<-1 || y0-y1>1)
+ return 0;
+ if(x1<0 || y1<0 || x1>=m->xs || y1>=m->ys)
+ return 0;
+ if(flag&0x20000) //Flag to ignore everything, for use with Taekwon's Jump skill currently. [Skotlex]
+ return 1;
+#ifndef CELL_NOSTACK
+ //In no-stack mode, do not check current cell.
+ if(!can_place(m,x0,y0,flag))
+ return 0;
+#endif
+ if(!can_place(m,x1,y1,flag))
+ return 0;
+ if(x0==x1 || y0==y1)
+ return 1;
+ if(!can_place(m,x0,y1,flag) || !can_place(m,x1,y0,flag))
+ return 0;
+ return 1;
+}
+
+/*==========================================
+ * (x0,y0)‚©‚ç(dx,dy)•ûŒü‚ÖcountƒZƒ‹•ª
+ * ‚«”ò‚΂µ‚½‚ ‚Æ‚ÌÀ•W‚ðŠ“¾
+ *------------------------------------------
+ */
+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ƒ}ƒX‚ɧŒÀ
+ if(battle_config.error_log)
+ ShowWarning("path_blownpos: count too many %d !\n",count);
+ count=15;
+ }
+ if(dx>1 || dx<-1 || dy>1 || dy<-1){
+ if(battle_config.error_log)
+ ShowError("path_blownpos: illeagal dx=%d or dy=%d !\n",dx,dy);
+ dx=(dx>=0)?1:((dx<0)?-1:0);
+ dy=(dy>=0)?1:((dy<0)?-1:0);
+ }
+
+ while( (count--)>0 && (dx!=0 || dy!=0) ){
+ if( !can_move(md,x0,y0,x0+dx,y0+dy,0) ){
+ int fx=(dx!=0 && can_move(md,x0,y0,x0+dx,y0,0));
+ int fy=(dy!=0 && can_move(md,x0,y0,x0,y0+dy,0));
+ if( fx && fy ){
+ if(rand()&1) dx=0;
+ else dy=0;
+ }
+ if( !fx ) dx=0;
+ if( !fy ) dy=0;
+ }
+ x0+=dx;
+ y0+=dy;
+ }
+ return (x0<<16)|y0;
+}
+
+/*==========================================
+ * êÀËå×îÍô?ª¬Ê¦Òöª«ªÉª¦ª«ªòÚ÷ª¹
+ *------------------------------------------
+ */
+#define swap(x,y) { int t; t = x; x = y; y = t; }
+int path_search_long(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1)
+{
+ int dx, dy;
+ int wx = 0, wy = 0;
+ int weight;
+ struct map_data *md;
+
+ if (!map[m].gat)
+ return 0;
+ md = &map[m];
+
+ dx = (x1 - x0);
+ if (dx < 0) {
+ swap(x0, x1);
+ swap(y0, y1);
+ dx = -dx;
+ }
+ dy = (y1 - y0);
+
+ if (spd) {
+ spd->rx = spd->ry = 0;
+ spd->len = 1;
+ spd->x[0] = x0;
+ spd->y[0] = y0;
+ }
+
+ if (map_getcellp(md,x1,y1,CELL_CHKWALL))
+ return 0;
+
+ if (dx > abs(dy)) {
+ weight = dx;
+ if (spd)
+ spd->ry=1;
+ } else {
+ weight = abs(y1 - y0);
+ if (spd)
+ spd->rx=1;
+ }
+
+ while (x0 != x1 || y0 != y1) {
+ if (map_getcellp(md,x0,y0,CELL_CHKWALL))
+ return 0;
+ wx += dx;
+ wy += dy;
+ if (wx >= weight) {
+ wx -= weight;
+ x0 ++;
+ }
+ if (wy >= weight) {
+ wy -= weight;
+ y0 ++;
+ } else if (wy < 0) {
+ wy += weight;
+ y0 --;
+ }
+ if (spd && spd->len<MAX_WALKPATH) {
+ spd->x[spd->len] = x0;
+ spd->y[spd->len] = y0;
+ spd->len++;
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * path’Tõ (x0,y0)->(x1,y1)
+ *------------------------------------------
+ */
+int path_search(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag)
+{
+ int heap[MAX_HEAP+1];
+ struct tmp_path tp[MAX_WALKPATH*MAX_WALKPATH];
+ int i,rp,x,y;
+ struct map_data *md;
+ int dx,dy;
+
+ nullpo_retr(0, wpd);
+
+ if(!map[m].gat)
+ return -1;
+ md=&map[m];
+ if(x1<0 || x1>=md->xs || y1<0 || y1>=md->ys || map_getcellp(md,x1,y1,CELL_CHKNOPASS))
+ return -1;
+
+ // easy
+ dx = (x1-x0<0) ? -1 : 1;
+ dy = (y1-y0<0) ? -1 : 1;
+ for(x=x0,y=y0,i=0;x!=x1 || y!=y1;){
+ if(i>=sizeof(wpd->path))
+ return -1;
+ if(x!=x1 && y!=y1){
+ if(!can_move(md,x,y,x+dx,y+dy,flag))
+ break;
+ x+=dx;
+ y+=dy;
+ wpd->path[i++]=(dx<0) ? ((dy>0)? 1 : 3) : ((dy<0)? 5 : 7);
+ } else if(x!=x1){
+ if(!can_move(md,x,y,x+dx,y ,flag))
+ break;
+ x+=dx;
+ wpd->path[i++]=(dx<0) ? 2 : 6;
+ } else if(y!=y1){
+ if(!can_move(md,x,y,x ,y+dy,flag))
+ break;
+ y+=dy;
+ wpd->path[i++]=(dy>0) ? 0 : 4;
+ }
+ }
+ if (x==x1 && y==y1) { //easy path successful.
+ wpd->path_len=i;
+ wpd->path_pos=0;
+ wpd->path_half=0;
+ return 0;
+ }
+
+ if(flag&1)
+ return -1;
+
+ memset(tp,0,sizeof(tp));
+
+ i=calc_index(x0,y0);
+ tp[i].x=x0;
+ tp[i].y=y0;
+ tp[i].dist=0;
+ tp[i].dir=0;
+ tp[i].before=0;
+ tp[i].cost=calc_cost(&tp[i],x1,y1);
+ tp[i].flag=0;
+ heap[0]=0;
+ push_heap_path(heap,tp,calc_index(x0,y0));
+ while(1){
+ int e=0,fromdir;
+
+ if(heap[0]==0)
+ return -1;
+ rp=pop_heap_path(heap,tp);
+ x=tp[rp].x;
+ y=tp[rp].y;
+ if(x==x1 && y==y1){
+ int len,j;
+
+ for(len=0,i=rp;len<100 && i!=calc_index(x0,y0);i=tp[i].before,len++);
+ if(len==100 || len>=sizeof(wpd->path))
+ return -1;
+ wpd->path_len=len;
+ wpd->path_pos=0;
+ wpd->path_half=0;
+ for(i=rp,j=len-1;j>=0;i=tp[i].before,j--)
+ wpd->path[j]=tp[i].dir;
+
+ return 0;
+ }
+ fromdir=tp[rp].dir;
+ if(can_move(md,x,y,x+1,y-1,flag))
+ e+=add_path(heap,tp,x+1,y-1,tp[rp].dist+14,5,rp,x1,y1);
+ if(can_move(md,x,y,x+1,y ,flag))
+ e+=add_path(heap,tp,x+1,y ,tp[rp].dist+10,6,rp,x1,y1);
+ if(can_move(md,x,y,x+1,y+1,flag))
+ e+=add_path(heap,tp,x+1,y+1,tp[rp].dist+14,7,rp,x1,y1);
+ if(can_move(md,x,y,x ,y+1,flag))
+ e+=add_path(heap,tp,x ,y+1,tp[rp].dist+10,0,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y+1,flag))
+ e+=add_path(heap,tp,x-1,y+1,tp[rp].dist+14,1,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y ,flag))
+ e+=add_path(heap,tp,x-1,y ,tp[rp].dist+10,2,rp,x1,y1);
+ if(can_move(md,x,y,x-1,y-1,flag))
+ e+=add_path(heap,tp,x-1,y-1,tp[rp].dist+14,3,rp,x1,y1);
+ if(can_move(md,x,y,x ,y-1,flag))
+ e+=add_path(heap,tp,x ,y-1,tp[rp].dist+10,4,rp,x1,y1);
+ tp[rp].flag=1;
+ if(e || heap[0]>=MAX_HEAP-5)
+ return -1;
+ }
+ return -1;
+}
+
+/*==========================================
+
+ * path’Tõ (x0,y0)->(x1,y1)
+
+ *------------------------------------------
+
+ */
+
+#ifdef PATH_STANDALONETEST
+char gat[64][64]={
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,1,0,0,0,0,0},
+};
+struct map_data map[1];
+
+/*==========================================
+ * Œo˜H’Tõƒ‹[ƒ`ƒ“’P‘̃eƒXƒg—pmainŠÖ”
+ *------------------------------------------
+ */
+void main(int argc,char *argv[])
+{
+ struct walkpath_data wpd;
+
+ map[0].gat=gat;
+ map[0].xs=64;
+ map[0].ys=64;
+
+ path_search(&wpd,0,3,4,5,4);
+ path_search(&wpd,0,5,4,3,4);
+ path_search(&wpd,0,6,4,3,4);
+ path_search(&wpd,0,7,4,3,4);
+ path_search(&wpd,0,4,3,4,5);
+ path_search(&wpd,0,4,2,4,5);
+ path_search(&wpd,0,4,1,4,5);
+ path_search(&wpd,0,4,5,4,3);
+ path_search(&wpd,0,4,6,4,3);
+ path_search(&wpd,0,4,7,4,3);
+ path_search(&wpd,0,7,4,3,4);
+ path_search(&wpd,0,8,4,3,4);
+ path_search(&wpd,0,9,4,3,4);
+ path_search(&wpd,0,10,4,3,4);
+ path_search(&wpd,0,11,4,3,4);
+ path_search(&wpd,0,12,4,3,4);
+ path_search(&wpd,0,13,4,3,4);
+ path_search(&wpd,0,14,4,3,4);
+ path_search(&wpd,0,15,4,3,4);
+ path_search(&wpd,0,16,4,3,4);
+ path_search(&wpd,0,17,4,3,4);
+ path_search(&wpd,0,18,4,3,4);
+}
+#endif
diff --git a/src/map/pc.c b/src/map/pc.c
new file mode 100644
index 000000000..94aa4c602
--- /dev/null
+++ b/src/map/pc.c
@@ -0,0 +1,8305 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "socket.h" // [Valaris]
+#include "timer.h"
+
+#include "malloc.h"
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "mob.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "script.h"
+#include "battle.h"
+#include "skill.h"
+#include "party.h"
+#include "guild.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "vending.h"
+#include "nullpo.h"
+#include "atcommand.h"
+#include "log.h"
+#include "showmsg.h"
+#include "core.h"
+
+#ifndef TXT_ONLY // mail system [Valaris]
+#include "mail.h"
+#endif
+
+#define PVP_CALCRANK_INTERVAL 1000 // PVP‡ˆÊŒvŽZ‚ÌŠÔŠu
+static int exp_table[14][MAX_LEVEL];
+static short statp[MAX_LEVEL];
+
+// h-files are for declarations, not for implementations... [Shinomori]
+struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
+// timer for night.day implementation
+int day_timer_tid;
+int night_timer_tid;
+
+static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+struct fame_list smith_fame_list[10];
+struct fame_list chemist_fame_list[10];
+struct fame_list taekwon_fame_list[10];
+
+static unsigned int equip_pos[11]={0x0080,0x0008,0x0040,0x0004,0x0001,0x0200,0x0100,0x0010,0x0020,0x0002,0x8000};
+
+static struct gm_account *gm_account = NULL;
+static int GM_num = 0;
+
+#define MOTD_LINE_SIZE 128
+char motd_text[MOTD_LINE_SIZE][256]; // Message of the day buffer [Valaris]
+
+int pc_isGM(struct map_session_data *sd) {
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->bl.type!=BL_PC )
+ return 0;
+
+
+ //For console [Wizputer] //Unfortunately the console is "broken" because it shares fd 0 with disconnected players. [Skotlex]
+// if ( sd->fd == 0 )
+// return 99;
+
+ for(i = 0; i < GM_num; i++)
+ if (gm_account[i].account_id == sd->status.account_id)
+ return gm_account[i].level;
+ return 0;
+
+}
+
+int pc_iskiller(struct map_session_data *src, struct map_session_data *target) {
+ nullpo_retr(0, src);
+
+ if(src->bl.type!=BL_PC )
+ return 0;
+ if (src->special_state.killer)
+ return 1;
+
+ if(target->bl.type!=BL_PC )
+ return 0;
+
+ if (target->special_state.killable)
+ return 1;
+
+ return 0;
+}
+
+
+int pc_set_gm_level(int account_id, int level) {
+ int i;
+ for (i = 0; i < GM_num; i++) {
+ if (account_id == gm_account[i].account_id) {
+ gm_account[i].level = level;
+ return 0;
+ }
+ }
+
+ GM_num++;
+ gm_account = (struct gm_account *) aRealloc(gm_account, sizeof(struct gm_account) * GM_num);
+ gm_account[GM_num - 1].account_id = account_id;
+ gm_account[GM_num - 1].level = level;
+ return 0;
+}
+
+static int pc_invincible_timer(int tid,unsigned int tick,int id,int data) {
+ struct map_session_data *sd;
+
+ if( (sd=(struct map_session_data *)map_id2sd(id)) == NULL || sd->bl.type!=BL_PC )
+ return 1;
+
+ if(sd->invincible_timer != tid){
+ if(battle_config.error_log)
+ ShowError("invincible_timer %d != %d\n",sd->invincible_timer,tid);
+ return 0;
+ }
+ sd->invincible_timer=-1;
+ skill_unit_move(&sd->bl,tick,1);
+
+ return 0;
+}
+
+int pc_setinvincibletimer(struct map_session_data *sd,int val) {
+ nullpo_retr(0, sd);
+
+ if(sd->invincible_timer != -1)
+ delete_timer(sd->invincible_timer,pc_invincible_timer);
+ sd->invincible_timer = add_timer(gettick()+val,pc_invincible_timer,sd->bl.id,0);
+ return 0;
+}
+
+int pc_delinvincibletimer(struct map_session_data *sd) {
+ nullpo_retr(0, sd);
+
+ if(sd->invincible_timer != -1) {
+ delete_timer(sd->invincible_timer,pc_invincible_timer);
+ sd->invincible_timer = -1;
+ }
+ skill_unit_move(&sd->bl,gettick(),1);
+ return 0;
+}
+
+static int pc_spiritball_timer(int tid,unsigned int tick,int id,int data) {
+ struct map_session_data *sd;
+
+ if( (sd=(struct map_session_data *)map_id2sd(id)) == NULL || sd->bl.type!=BL_PC )
+ return 1;
+
+ if(sd->spirit_timer[0] != tid){
+ if(battle_config.error_log)
+ ShowError("spirit_timer %d != %d\n",sd->spirit_timer[0],tid);
+ return 0;
+ }
+
+ if(sd->spiritball <= 0) {
+ if(battle_config.error_log)
+ ShowError("Spiritballs are already 0 when pc_spiritball_timer gets called");
+ sd->spiritball = 0;
+ return 0;
+ }
+
+ sd->spiritball--;
+ // I leave this here as bad example [Shinomori]
+ //memcpy( &sd->spirit_timer[0], &sd->spirit_timer[1], sizeof(sd->spirit_timer[0]) * sd->spiritball );
+ memmove( sd->spirit_timer+0, sd->spirit_timer+1, (sd->spiritball)*sizeof(int) );
+ sd->spirit_timer[sd->spiritball]=-1;
+
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+int pc_addspiritball(struct map_session_data *sd,int interval,int max) {
+ nullpo_retr(0, sd);
+
+ if(max > MAX_SKILL_LEVEL)
+ max = MAX_SKILL_LEVEL;
+ if(sd->spiritball < 0)
+ sd->spiritball = 0;
+
+ if(sd->spiritball >= max) {
+ if(sd->spirit_timer[0] != -1)
+ delete_timer(sd->spirit_timer[0],pc_spiritball_timer);
+ // I leave this here as bad example [Shinomori]
+ //memcpy( &sd->spirit_timer[0], &sd->spirit_timer[1], sizeof(sd->spirit_timer[0]) * (sd->spiritball - 1));
+ memmove( sd->spirit_timer+0, sd->spirit_timer+1, (sd->spiritball - 1)*sizeof(int) );
+ //sd->spirit_timer[sd->spiritball-1] = -1; // intentionally, but will be overwritten
+ } else
+ sd->spiritball++;
+
+ sd->spirit_timer[sd->spiritball-1] = add_timer(gettick()+interval,pc_spiritball_timer,sd->bl.id,0);
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+int pc_delspiritball(struct map_session_data *sd,int count,int type) {
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->spiritball <= 0) {
+ sd->spiritball = 0;
+ return 0;
+ }
+
+ if(count > sd->spiritball)
+ count = sd->spiritball;
+ sd->spiritball -= count;
+ if(count > MAX_SKILL_LEVEL)
+ count = MAX_SKILL_LEVEL;
+
+ for(i=0;i<count;i++) {
+ if(sd->spirit_timer[i] != -1) {
+ delete_timer(sd->spirit_timer[i],pc_spiritball_timer);
+ sd->spirit_timer[i] = -1;
+ }
+ }
+ for(i=count;i<MAX_SKILL_LEVEL;i++) {
+ sd->spirit_timer[i-count] = sd->spirit_timer[i];
+ sd->spirit_timer[i] = -1;
+ }
+
+ if(!type)
+ clif_spiritball(sd);
+
+ return 0;
+}
+
+// Increases a player's and displays a notice to him
+void pc_addfame(struct map_session_data *sd,int count) {
+ nullpo_retv(sd);
+ sd->status.fame += count;
+ if(sd->status.fame > MAX_FAME)
+ sd->status.fame = MAX_FAME;
+ switch(sd->class_&MAPID_UPPERMASK){
+ case MAPID_BLACKSMITH: // Blacksmith
+ clif_fame_blacksmith(sd,count);
+ break;
+ case MAPID_ALCHEMIST: // Alchemist
+ clif_fame_alchemist(sd,count);
+ break;
+ case MAPID_TAEKWON: // Taekwon
+ clif_fame_taekwon(sd,count);
+ break;
+ }
+ //FIXME: Is this needed? It places unnecessary stress on the char server.... >.< [Skotlex]
+ chrif_save(sd,0); // Save to allow up-to-date fame list refresh
+ chrif_reqfamelist(); // Refresh the fame list
+}
+
+// Check whether a player ID is in the Top 10 fame list of its job
+int pc_istop10fame(int char_id,int job) {
+ int i;
+ switch(job){
+ case MAPID_BLACKSMITH: // Blacksmith
+ for(i=0;i<10;i++){
+ if(smith_fame_list[i].id==char_id)
+ return smith_fame_list[i].fame;
+ }
+ break;
+ case MAPID_ALCHEMIST: // Alchemist
+ for(i=0;i<10;i++){
+ if(chemist_fame_list[i].id==char_id)
+ return chemist_fame_list[i].fame;
+ }
+ break;
+ case MAPID_TAEKWON: // Taekwon
+ for(i=0;i<10;i++){
+ if(taekwon_fame_list[i].id==char_id)
+ return taekwon_fame_list[i].fame;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int pc_setrestartvalue(struct map_session_data *sd,int type) {
+ //?¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+
+ nullpo_retr(0, sd);
+
+ //-----------------------
+ // Ž€–S‚µ‚½
+ if(sd->special_state.restart_full_recover || // ƒIƒVƒŠƒXƒJ?ƒh
+ sd->state.snovice_flag == 4) { // [Celest]
+ sd->status.hp=sd->status.max_hp;
+ sd->status.sp=sd->status.max_sp;
+ if (sd->state.snovice_flag == 4) {
+ sd->state.snovice_flag = 0;
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_STEELBODY],1,0,0,0,skill_get_time(MO_STEELBODY,1),0 );
+ }
+ }
+ else {
+ if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2) && battle_config.restart_hp_rate < 50) { //ƒmƒr‚Í”¼•ª‰ñ•œ
+ sd->status.hp=(sd->status.max_hp)/2;
+ }
+ else {
+ if(battle_config.restart_hp_rate <= 0)
+ sd->status.hp = 1;
+ else {
+ sd->status.hp = sd->status.max_hp * battle_config.restart_hp_rate /100;
+ if(sd->status.hp <= 0)
+ sd->status.hp = 1;
+ }
+ }
+ if(battle_config.restart_sp_rate > 0) {
+ int sp = sd->status.max_sp * battle_config.restart_sp_rate /100;
+ if(sd->status.sp < sp)
+ sd->status.sp = sp;
+ }
+ }
+ if(type&1)
+ clif_updatestatus(sd,SP_HP);
+ if(type&1)
+ clif_updatestatus(sd,SP_SP);
+
+ /* removed exp penalty on spawn [Valaris] */
+
+ if(type&2 && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE && battle_config.zeny_penalty > 0 && !map[sd->bl.m].flag.nozenypenalty) {
+ int zeny = (int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.);
+ if(zeny < 1) zeny = 1;
+ sd->status.zeny -= zeny;
+ if(sd->status.zeny < 0) sd->status.zeny = 0;
+ clif_updatestatus(sd,SP_ZENY);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Determines if the player can move based on status changes. [Skotlex]
+ *------------------------------------------
+ */
+int pc_can_move(struct map_session_data *sd)
+{
+ if (sd->canmove_tick > gettick() || (sd->opt1 > 0 && sd->opt1 != OPT1_STONEWAIT) ||
+ sd->sc_data[SC_ANKLE].timer != -1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer !=-1 ||
+ sd->sc_data[SC_TRICKDEAD].timer !=-1 ||
+ sd->sc_data[SC_BLADESTOP].timer !=-1 ||
+ sd->sc_data[SC_SPIDERWEB].timer !=-1 ||
+ (sd->sc_data[SC_DANCING].timer !=-1 && sd->sc_data[SC_DANCING].val4 && sd->sc_data[SC_LONGING].timer == -1) ||
+ (sd->sc_data[SC_DANCING].timer !=-1 && sd->sc_data[SC_DANCING].val1 == CG_HERMODE) || //cannot move while Hermod is active.
+ (sd->sc_data[SC_GOSPEL].timer !=-1 && sd->sc_data[SC_GOSPEL].val4 == BCT_SELF) || // cannot move while gospel is in effect
+ sd->sc_data[SC_STOP].timer != -1 ||
+ sd->sc_data[SC_CLOSECONFINE].timer != -1 ||
+ sd->sc_data[SC_CLOSECONFINE2].timer != -1
+ )
+ return 0;
+
+ if ((sd->status.option & 2) && pc_checkskill(sd, RG_TUNNELDRIVE) <= 0)
+ return 0;
+
+ return 1;
+}
+
+/*==========================================
+ Determines if the GM can give / drop / trade / vend items [Lupus]
+ Args: GM Level (current player GM level)
+ * Returns
+ 1 = this GM can't do it
+ 0 = this one can do it
+ *------------------------------------------
+ */
+int pc_can_give_items(int level) {
+ return ( level >= battle_config.gm_cant_drop_min_lv
+ && level <= battle_config.gm_cant_drop_max_lv);
+}
+
+/*==========================================
+ * ƒ?ƒJƒ‹ƒvƒƒgƒ^ƒCƒv錾 (•K—v‚È•¨‚Ì‚Ý)
+ *------------------------------------------
+ */
+static int pc_walktoxy_sub(struct map_session_data *);
+
+/*==========================================
+ * save‚É•K—v‚ȃXƒe?ƒ^ƒXC³‚ðs‚È‚¤
+ *------------------------------------------
+ */
+int pc_makesavestatus(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->state.finalsave)
+ return 0; //Nothing to change.
+
+ // •bÌF‚ÍF?•¾ŠQ‚ª‘½‚¢‚Ì‚Å•Û‘¶?Û‚É‚Í‚µ‚È‚¢
+ if(!battle_config.save_clothcolor)
+ sd->status.clothes_color=0;
+
+ // Ž€–S?‘Ô‚¾‚Á‚½‚Ì‚Åhp‚ð1AˆÊ’u‚ðƒZ?ƒuꊂÉ?X
+ if(!sd->state.waitingdisconnect) {
+ if(pc_isdead(sd)){
+ pc_setrestartvalue(sd,0);
+ memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
+ } else {
+ sd->status.last_point.map = sd->mapindex;
+ sd->status.last_point.x = sd->bl.x;
+ sd->status.last_point.y = sd->bl.y;
+ }
+
+ // ƒZ?ƒu‹ÖŽ~ƒ}ƒbƒv‚¾‚Á‚½‚Ì‚ÅŽw’èˆÊ’u‚Ɉړ®
+ if(map[sd->bl.m].flag.nosave){
+ struct map_data *m=&map[sd->bl.m];
+ if(m->save.map)
+ memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point));
+ else
+ memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point));
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Ú?Žb̉Šú‰»
+ *------------------------------------------
+ */
+int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int login_id1, unsigned int client_tick, int sex, int fd) {
+ nullpo_retr(0, sd);
+
+ sd->bl.id = account_id;
+ sd->char_id = char_id;
+ sd->login_id1 = login_id1;
+ sd->login_id2 = 0; // at this point, we can not know the value :(
+ sd->client_tick = client_tick;
+ sd->sex = sex;
+ sd->state.auth = 0;
+ sd->bl.type = BL_PC;
+ sd->canact_tick = sd->canmove_tick = gettick();
+ sd->canlog_tick = gettick();
+ sd->state.waitingdisconnect = 0;
+
+ return 0;
+}
+
+int pc_equippoint(struct map_session_data *sd,int n)
+{
+ int ep = 0;
+ //?¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+
+ nullpo_retr(0, sd);
+
+ if(sd->inventory_data[n]) {
+ ep = sd->inventory_data[n]->equip;
+ if(sd->inventory_data[n]->look == 1 || sd->inventory_data[n]->look == 2 || sd->inventory_data[n]->look == 6) {
+ if(ep == 2 && (pc_checkskill(sd,AS_LEFT) > 0 || (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN))
+ return 34;
+ }
+ }
+ return ep;
+}
+
+int pc_setinventorydata(struct map_session_data *sd)
+{
+ int i,id;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_INVENTORY;i++) {
+ id = sd->status.inventory[i].nameid;
+ sd->inventory_data[i] = itemdb_search(id);
+ }
+ return 0;
+}
+
+int pc_calcweapontype(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->weapontype1 != 0 && sd->weapontype2 == 0)
+ sd->status.weapon = sd->weapontype1;
+ if(sd->weapontype1 == 0 && sd->weapontype2 != 0)// ¶Žè•Ší Only
+ sd->status.weapon = sd->weapontype2;
+ else if(sd->weapontype1 == 1 && sd->weapontype2 == 1)// ?’Z?
+ 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) ) // ’Z? - ?Žè?
+ sd->status.weapon = 0x14;
+ else if( (sd->weapontype1 == 1 && sd->weapontype2 == 6) ||
+ (sd->weapontype1 == 6 && sd->weapontype2 == 1) ) // ’Z? - •€
+ 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;
+ //?¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+
+ nullpo_retr(0, sd);
+
+ item = sd->inventory_data[n];
+
+ if( battle_config.gm_allequip>0 && pc_isGM(sd)>=battle_config.gm_allequip )
+ return 1;
+
+ if(item == NULL)
+ return 0;
+ if(item->elv && sd->status.base_level < (unsigned int)item->elv)
+ return 0;
+ if(item->sex != 2 && sd->status.sex != item->sex)
+ return 0;
+ if(map[sd->bl.m].flag.pvp && (item->flag.no_equip&1)) //optimized by Lupus
+ return 0;
+ if(map_flag_gvg(sd->bl.m) && (item->flag.no_equip>1)) //optimized by Lupus
+ return 0;
+ if((item->equip & 0x0002 || item->equip & 0x0020) && item->type == 4 && sd->sc_data[SC_STRIPWEAPON].timer != -1) // Also works with left-hand weapons [DracoRPG]
+ return 0;
+ if(item->equip & 0x0020 && item->type == 5 && sd->sc_data[SC_STRIPSHIELD].timer != -1) // Also works with left-hand weapons [DracoRPG]
+ return 0;
+ if(item->equip & 0x0010 && sd->sc_data[SC_STRIPARMOR].timer != -1)
+ return 0;
+ if(item->equip & 0x0100 && sd->sc_data[SC_STRIPHELM].timer != -1)
+ return 0;
+
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SUPERNOVICE) {
+ //Spirit of Super Novice equip bonuses. [Skotlex]
+ if (sd->status.base_level > 90 && item->equip & 0x301)
+ return 1; //Can equip all helms
+ if (sd->status.base_level > 96 && item->equip & 0x022 && item->type == 4)
+ switch(item->look) { //In weapons, the look determines type of weapon.
+ case 0x01: //Level 4 Knives are equippable.. this means all knives, I'd guess?
+ case 0x02: //All 1H swords
+ case 0x06: //All 1H Axes
+ case 0x08: //All Maces
+ case 0x0a: //All Staffs
+ return 1;
+ }
+ }
+ //Not equipable by class. [Skotlex]
+ if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)]))
+ return 0;
+
+ //Not equipable by upper class. [Skotlex]
+ if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper))
+ return 0;
+
+ return 1;
+}
+
+//‘•”õ”j‰ó
+int pc_break_equip(struct map_session_data *sd, unsigned short where)
+{
+ int i, j;
+
+ nullpo_retr(-1, sd);
+ if (sd->unbreakable_equip & where)
+ return 0;
+ if (sd->unbreakable >= rand()%100)
+ return 0;
+ if (where == EQP_WEAPON && (sd->status.weapon == 0 || //Bare fists should not break :P
+ sd->status.weapon == 6 || sd->status.weapon == 7 || sd->status.weapon == 8 || // Axes and Maces can't be broken [DracoRPG]
+ sd->status.weapon == 10 || sd->status.weapon == 15 //Rods and Books can't be broken [Skotlex]
+ ))
+ return 0;
+ switch (where) {
+ case EQP_WEAPON:
+ i = SC_CP_WEAPON;
+ break;
+ case EQP_ARMOR:
+ i = SC_CP_ARMOR;
+ break;
+ case EQP_SHIELD:
+ i = SC_CP_SHIELD;
+ break;
+ case EQP_HELM:
+ i = SC_CP_HELM;
+ break;
+ default:
+ return 0;
+ }
+ if (sd->sc_count && sd->sc_data[i].timer != -1)
+ return 0;
+
+ for (i = 0; i < 11; i++) {
+ if ((j = sd->equip_index[i]) > 0 && sd->status.inventory[j].attribute != 1 &&
+ ((where == EQP_HELM && i == 6) ||
+ (where == EQP_ARMOR && i == 7) ||
+ (where == EQP_WEAPON && (i == 8 || i == 9) && sd->inventory_data[j]->type == 4) ||
+ (where == EQP_SHIELD && i == 9 && sd->inventory_data[j]->type == 5)))
+ {
+ sd->status.inventory[j].attribute = 1;
+ pc_unequipitem(sd, j, 3);
+ clif_equiplist(sd);
+ return 1;
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * session id‚É–â‘è–³‚µ
+ * charŽI‚©‚ç‘—‚ç‚ê‚Ä‚«‚½ƒXƒe?ƒ^ƒX‚ðÝ’è
+ *------------------------------------------
+ */
+int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_time, struct mmo_charstatus *st)
+{
+ struct party *p;
+ struct guild *g;
+ int i;
+ unsigned long tick = gettick();
+
+ if (sd->state.auth) //Temporary debug. [Skotlex]
+ {
+ ShowDebug("pc_authok: Received auth ok for already authorized client (account id %d)!\n", sd->bl.id);
+ return 1;
+ }
+
+ sd->login_id2 = login_id2;
+ memcpy(&sd->status, st, sizeof(*st));
+
+ if (sd->status.sex != sd->sex) {
+ clif_authfail_fd(sd->fd, 0);
+ return 1;
+ }
+
+ //Set the map-server used job id. [Skotlex]
+ sd->class_ = pc_jobid2mapid((unsigned short) sd->status.class_);
+
+ //Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation.
+ // Šî–{“I‚ȉŠú‰»
+ sd->state.connect_new = 1;
+
+ sd->view_class = sd->status.class_;
+ sd->speed = DEFAULT_WALK_SPEED;
+ sd->walktimer = -1;
+ sd->attacktimer = -1;
+ sd->followtimer = -1; // [MouseJstr]
+ sd->skilltimer = -1;
+ sd->skillitem = -1;
+ sd->skillitemlv = -1;
+ sd->invincible_timer = -1;
+
+ sd->canact_tick = tick;
+ sd->canmove_tick = tick;
+ sd->canregen_tick = tick;
+ sd->attackabletime = tick;
+
+ for(i = 0; i < MAX_SKILL_LEVEL; i++)
+ sd->spirit_timer[i] = -1;
+ for(i = 0; i < MAX_SKILLTIMERSKILL; i++)
+ sd->skilltimerskill[i].timer = -1;
+
+
+ if (battle_config.item_auto_get)
+ sd->state.autoloot = 10000;
+
+ if (battle_config.disp_experience)
+ sd->state.showexp = 1;
+ if (battle_config.disp_zeny)
+ sd->state.showzeny = 1;
+
+ if (battle_config.display_delay_skill_fail)
+ sd->state.showdelay = 1;
+
+ // Request all registries.
+ intif_request_registry(sd,7);
+
+ // ƒAƒCƒeƒ€ƒ`ƒFƒbƒN
+ pc_setinventorydata(sd);
+ pc_checkitem(sd);
+
+ // pet
+ sd->pet_hungry_timer = -1;
+
+ // ƒXƒe?ƒ^ƒXˆÙí‚̉Šú‰»
+ for(i = 0; i < MAX_STATUSCHANGE; i++) {
+ sd->sc_data[i].timer=-1;
+ }
+ sd->sc_count=0;
+ if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) &&
+ (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide)))
+ sd->status.option &= (OPTION_MASK | OPTION_INVISIBLE);
+ else
+ sd->status.option &= OPTION_MASK;
+
+ // ƒp?ƒeƒB??ŒW‚̉Šú‰»
+ sd->party_x = -1;
+ sd->party_y = -1;
+ sd->guild_x = -1;
+ sd->guild_y = -1;
+
+ // ƒCƒxƒ“ƒg?ŒW‚̉Šú‰»
+ for(i = 0; i < MAX_EVENTTIMER; i++)
+ sd->eventtimer[i] = -1;
+
+ // Moved PVP timer initialisation before set_pos
+ sd->pvp_timer = -1;
+
+ // ˆÊ’u‚ÌÝ’è
+ if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, 0)) != 0) {
+ if(battle_config.error_log)
+ ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i);
+
+ // try warping to a default map instead (church graveyard)
+ if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, 0) != 0) {
+ // if we fail again
+ clif_authfail_fd(sd->fd, 0);
+ return 1;
+ }
+ }
+
+ // pet
+ if (sd->status.pet_id > 0)
+ intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id);
+
+ // ƒp?ƒeƒBAƒMƒ‹ƒhƒf?ƒ^‚Ì—v‹
+ if (sd->status.party_id > 0 && (p = party_search(sd->status.party_id)) == NULL)
+ party_request_info(sd->status.party_id);
+ if (sd->status.guild_id > 0)
+ {
+ if ((g = guild_search(sd->status.guild_id)) == NULL)
+ guild_request_info(sd->status.guild_id);
+ else if (strcmp(sd->status.name,g->master) == 0)
+ { //Block Guild Skills to prevent logout/login reuse exploiting. [Skotlex]
+ guild_block_skill(sd, 300000);
+ //Also set the Guild Master flag.
+ sd->state.gmaster_flag = g;
+ }
+ }
+
+ // ’Ê’m
+
+ clif_authok(sd);
+ map_addiddb(&sd->bl);
+ if (map_charid2nick(sd->status.char_id) == NULL)
+ map_addchariddb(sd->status.char_id, sd->status.name);
+
+ // Notify everyone that this char logged in [Skotlex].
+ clif_foreachclient(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
+
+ sd->feel_level=-1;
+ //Until the reg values arrive, set them to not require trigger...
+ sd->state.event_death = 1;
+ sd->state.event_kill = 1;
+ sd->state.event_disconnect = 1;
+
+ if (night_flag) {
+ char tmpstr[1024];
+ strcpy(tmpstr, msg_txt(500)); // Actually, it's the night...
+ clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1);
+ //Night packet is sent when it finishes loading the map. [Skotlex]
+ }
+
+ // ƒXƒe?ƒ^ƒX‰ŠúŒvŽZ‚È‚Ç
+ status_calc_pc(sd,1);
+
+ sd->state.auth = 1; //Do not auth him until the initial stats have been placed.
+ { //Add IP field
+ unsigned char *ip = (unsigned char *) &session[sd->fd]->client_addr.sin_addr;
+ if (pc_isGM(sd))
+ ShowInfo("GM Character '"CL_WHITE"%s"CL_RESET"' logged in. (Acc. ID: '"CL_WHITE"%d"CL_RESET"', Connection: '"CL_WHITE"%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"', GM Level '"CL_WHITE"%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->fd, sd->packet_ver, ip[0],ip[1],ip[2],ip[3], pc_isGM(sd));
+ else
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged in. (Account ID: '"CL_WHITE"%d"CL_RESET"', Connection: '"CL_WHITE"%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%d.%d.%d.%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->fd, sd->packet_ver, ip[0],ip[1],ip[2],ip[3]);
+ }
+
+ // Send friends list
+ clif_friendslist_send(sd);
+
+ if (battle_config.display_version == 1){
+ char buf[256];
+ sprintf(buf, "eAthena SVN version: %s", get_svn_revision());
+ clif_displaymessage(sd->fd, buf);
+ }
+
+ // Message of the Day [Valaris]
+ {
+ int ln;
+ for(ln=0; motd_text[ln][0] && ln < MOTD_LINE_SIZE; ln++) {
+ if (battle_config.motd_type)
+ clif_disp_onlyself(sd,motd_text[ln],strlen(motd_text[ln]));
+ else
+ clif_displaymessage(sd->fd, motd_text[ln]);
+ }
+ }
+
+#ifndef TXT_ONLY
+ if(mail_server_enable)
+ mail_check(sd,1); // check mail at login [Valaris]
+#endif
+
+ // message of the limited time of the account
+ if (connect_until_time != 0) { // don't display if it's unlimited or unknow value
+ char tmpstr[1024];
+ strftime(tmpstr, sizeof(tmpstr) - 1, msg_txt(501), localtime(&connect_until_time)); // "Your account time limit is: %d-%m-%Y %H:%M:%S."
+ clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Closes a connection because it failed to be authenticated from the char server.
+ *------------------------------------------
+ */
+int pc_authfail(struct map_session_data *sd) {
+
+ if (sd->state.auth) //Temporary debug. [Skotlex]
+ ShowDebug("pc_authfail: Received auth fail for already authentified client (account id %d)!\n", sd->bl.id);
+
+ if (!sd->fd)
+ ShowDebug("pc_authfail: Received auth fail for a player with no connection (account id %d)!\n", sd->bl.id);
+
+ clif_authfail_fd(sd->fd, 0);
+ return 0;
+}
+
+/*==========================================
+ * Invoked once after the char/account/account2 registry variables are received. [Skotlex]
+ *------------------------------------------
+ */
+int pc_reg_received(struct map_session_data *sd)
+{
+ int i,j;
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ char hate_var[3][NAME_LENGTH] = {"PC_HATE_MOB_SUN","PC_HATE_MOB_MOON","PC_HATE_MOB_STAR"};
+
+ sd->change_level = pc_readglobalreg(sd,"jobchange_level");
+ sd->die_counter = pc_readglobalreg(sd,"PC_DIE_COUNTER");
+
+ if (pc_checkskill(sd, TK_MISSION)) {
+ sd->mission_mobid = pc_readglobalreg(sd,"TK_MISSION_ID");
+ sd->mission_count = pc_readglobalreg(sd,"TK_MISSION_COUNT");
+ }
+
+ //SG map and mob read [Komurka]
+ for(i=0;i<3;i++) //for now - someone need to make reading from txt/sql
+ {
+ if ((j = pc_readglobalreg(sd,feel_var[i]))!=0) {
+ sd->feel_map[i].index = j;
+ sd->feel_map[i].m = map_mapindex2mapid(j);
+ } else {
+ sd->feel_map[i].index = 0;
+ sd->feel_map[i].m = -1;
+ }
+ sd->hate_mob[i] = pc_readglobalreg(sd,hate_var[i]) - 1;
+
+ }
+
+ if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) {
+ sd->cloneskill_id = pc_readglobalreg(sd,"CLONE_SKILL");
+ if (sd->cloneskill_id > 0) {
+ sd->status.skill[sd->cloneskill_id].id = sd->cloneskill_id;
+ sd->status.skill[sd->cloneskill_id].lv = pc_readglobalreg(sd,"CLONE_SKILL_LV");
+ if (i < sd->status.skill[sd->cloneskill_id].lv)
+ sd->status.skill[sd->cloneskill_id].lv = i;
+ sd->status.skill[sd->cloneskill_id].flag = 13; //cloneskill flag
+ clif_skillinfoblock(sd);
+ }
+ }
+
+ // Automated script events
+ if (script_config.event_requires_trigger) {
+ sd->state.event_death = pc_readglobalreg(sd, script_config.die_event_name);
+ sd->state.event_kill = pc_readglobalreg(sd, script_config.kill_event_name);
+ sd->state.event_disconnect = pc_readglobalreg(sd, script_config.logout_event_name);
+ // if script triggers are not required
+ } else {
+ sd->state.event_death = 1;
+ sd->state.event_kill = 1;
+ sd->state.event_disconnect = 1;
+ }
+
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.login_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLoginNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.login_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.login_event_name, sd->bl.id), script_config.login_event_name);
+ }
+
+ return 0;
+}
+
+static int pc_calc_skillpoint(struct map_session_data* sd)
+{
+ int i,skill,inf2,skill_point=0;
+
+ nullpo_retr(0, sd);
+
+ for(i=1;i<MAX_SKILL;i++){
+ if( (skill = pc_checkskill(sd,i)) > 0) {
+ inf2 = skill_get_inf2(i);
+ if((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) //Do not count wedding/link skills. [Skotlex]
+ ) {
+ if(!sd->status.skill[i].flag)
+ skill_point += skill;
+ else if(sd->status.skill[i].flag > 2 && sd->status.skill[i].flag != 13) {
+ skill_point += (sd->status.skill[i].flag - 2);
+ }
+ }
+ }
+ }
+
+ return skill_point;
+}
+
+
+/*==========================================
+ * ?‚¦‚ç‚ê‚éƒXƒLƒ‹‚ÌŒvŽZ
+ *------------------------------------------
+ */
+int pc_calc_skilltree(struct map_session_data *sd)
+{
+ int i,id=0,flag;
+ int c=0;
+
+ nullpo_retr(0, sd);
+
+ c = pc_mapid2jobid(pc_calc_skilltree_normalize_job(sd), sd->status.sex);
+
+ for(i=0;i<MAX_SKILL;i++){
+ if (sd->status.skill[i].flag != 13) //Don't touch plagiarized skills
+ sd->status.skill[i].id=0; //First clear skills.
+ }
+ for(i=0;i<MAX_SKILL;i++){
+ if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){
+ sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2;
+ sd->status.skill[i].flag=0;
+ }
+ else
+ if(sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_BARDDANCER && i >= DC_HUMMING && i<= DC_SERVICEFORYOU)
+ { //Enable Bard/Dancer spirit linked skills.
+ if (sd->status.sex) { //Link dancer skills to bard.
+ sd->status.skill[i].id=i;
+ sd->status.skill[i].lv=sd->status.skill[i-8].lv; // Set the level to the same as the linking skill
+ sd->status.skill[i].flag=1; // Tag it as a non-savable, non-uppable, bonus skill
+ } else { //Link bard skills to dancer.
+ sd->status.skill[i-8].id=i-8;
+ sd->status.skill[i-8].lv=sd->status.skill[i].lv; // Set the level to the same as the linking skill
+ sd->status.skill[i-8].flag=1; // Tag it as a non-savable, non-uppable, bonus skill
+ }
+ }
+ }
+
+ if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill){
+ for(i=0;i<MAX_SKILL;i++){
+ if (skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL)) //Only skills you can't have are npc/guild ones
+ continue;
+ if (skill_get_max(i) > 0)
+ sd->status.skill[i].id=i;
+ }
+ return 0;
+ }
+ do {
+ flag=0;
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0;i++){
+ int j,f=1;
+ if(!battle_config.skillfree) {
+ for(j=0;j<5;j++) {
+ if( skill_tree[c][i].need[j].id &&
+ pc_checkskill(sd,skill_tree[c][i].need[j].id) <
+ skill_tree[c][i].need[j].lv) {
+ f=0;
+ break;
+ }
+ }
+ if (sd->status.job_level < skill_tree[c][i].joblv)
+ f=0;
+ else if (pc_checkskill(sd, NV_BASIC) < 9 && id != NV_BASIC && !(skill_get_inf2(id)&INF2_QUEST_SKILL))
+ f=0; // Do not unlock normal skills when Basic Skills is not maxed out (can happen because of skill reset)
+ }
+ if(sd->status.skill[id].id==0 ){
+ if(sd->sc_data[SC_SPIRIT].timer != -1 && skill_get_inf2(id)&INF2_SPIRIT_SKILL) { //Enable Spirit Skills. [Skotlex]
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].lv=1;
+ sd->status.skill[id].flag=1; //So it is not saved, and tagged as a "bonus" skill.
+ flag=1;
+ } else if (f){
+ sd->status.skill[id].id=id;
+ flag=1;
+ }
+ }
+ }
+ } while(flag);
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON)) {
+ //Grant all Taekwon Tree, but only as bonus skills in case they drop from ranking. [Skotlex]
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0;i++){
+ if ((skill_get_inf2(id)&(INF2_QUEST_SKILL|INF2_WEDDING_SKILL)))
+ continue; //Do not include Quest/Wedding skills.
+ if(sd->status.skill[id].id==0 ){
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].flag=1; //So it is not saved, and tagged as a "bonus" skill.
+ } else
+ sd->status.skill[id].flag=sd->status.skill[id].lv+2;
+ sd->status.skill[id].lv= skill_tree_get_max(id, sd->status.class_);
+ }
+ }
+
+ return 0;
+}
+
+// Make sure all the skills are in the correct condition
+// before persisting to the backend.. [MouseJstr]
+int pc_clean_skilltree(struct map_session_data *sd) {
+ int i;
+ for (i = 0; i < MAX_SKILL; i++){
+ if (sd->status.skill[i].flag == 13){
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ sd->status.skill[i].flag = 0;
+ }
+ }
+
+ return 0;
+}
+
+int pc_calc_skilltree_normalize_job(struct map_session_data *sd) {
+ int skill_point;
+ int c = sd->class_;
+
+ if (!battle_config.skillup_limit || !(sd->class_&JOBL_2) || (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ return c; //Only Normalize non-first classes (and non-super novice)
+
+ skill_point = pc_calc_skillpoint(sd);
+ if(skill_point < 9)
+ c = MAPID_NOVICE;
+ else if (sd->status.skill_point >= (int)sd->status.job_level
+ && ((sd->change_level > 0 && skill_point < sd->change_level+8) || skill_point < 58)) {
+ //Send it to first class.
+ c &= MAPID_BASEMASK;
+ }
+ if (sd->class_&JOBL_UPPER) //Convert to Upper
+ c |= JOBL_UPPER;
+ else if (sd->class_&JOBL_BABY) //Convert to Baby
+ c |= JOBL_BABY;
+
+ return c;
+}
+
+/*==========================================
+ * d—ʃAƒCƒRƒ“‚ÌŠm”F
+ *------------------------------------------
+ */
+int pc_checkweighticon(struct map_session_data *sd)
+{
+ int flag=0;
+
+ nullpo_retr(0, sd);
+
+ //Consider the battle option 50% criteria....
+ if(sd->weight*100 >= sd->max_weight*battle_config.natural_heal_weight_rate)
+ flag=1;
+ if(sd->weight*10 >= sd->max_weight*9)
+ flag=2;
+
+ if(flag==1){
+ if(sd->sc_data[SC_WEIGHT50].timer==-1)
+ status_change_start(&sd->bl,SC_WEIGHT50,0,0,0,0,0,0);
+ }else{
+ if(sd->sc_data[SC_WEIGHT50].timer!=-1)
+ status_change_end(&sd->bl,SC_WEIGHT50,-1);
+ }
+ if(flag==2){
+ if(sd->sc_data[SC_WEIGHT90].timer==-1)
+ status_change_start(&sd->bl,SC_WEIGHT90,0,0,0,0,0,0);
+ }else{
+ if(sd->sc_data[SC_WEIGHT90].timer!=-1)
+ status_change_end(&sd->bl,SC_WEIGHT90,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? ”õ•i‚É‚æ‚é”\—Í“™‚̃{?ƒiƒXÝ’è
+ *------------------------------------------
+ */
+int pc_bonus(struct map_session_data *sd,int type,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_STR:
+ case SP_AGI:
+ case SP_VIT:
+ case SP_INT:
+ case SP_DEX:
+ case SP_LUK:
+ if(sd->state.lr_flag != 2)
+ sd->parame[type-SP_STR]+=val;
+ break;
+ case SP_ATK1:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.watk+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.watk+=val;
+ break;
+ case SP_ATK2:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.watk2+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.watk2+=val;
+ break;
+ case SP_BASE_ATK:
+ if(sd->state.lr_flag != 2)
+ sd->base_atk+=val;
+ break;
+ case SP_MATK1:
+ if(sd->state.lr_flag != 2)
+ sd->matk1 += val;
+ break;
+ case SP_MATK2:
+ if(sd->state.lr_flag != 2)
+ sd->matk2 += val;
+ break;
+ case SP_MATK:
+ if(sd->state.lr_flag != 2) {
+ sd->matk1 += val;
+ sd->matk2 += val;
+ }
+ break;
+ case SP_DEF1:
+ if(sd->state.lr_flag != 2)
+ sd->def+=val;
+ break;
+ case SP_DEF2:
+ if(sd->state.lr_flag != 2)
+ sd->def2+=val;
+ break;
+ case SP_MDEF1:
+ if(sd->state.lr_flag != 2)
+ sd->mdef+=val;
+ break;
+ case SP_MDEF2:
+ if(sd->state.lr_flag != 2)
+ sd->mdef+=val;
+ break;
+ case SP_HIT:
+ if(sd->state.lr_flag != 2)
+ sd->hit+=val;
+ else
+ sd->arrow_hit+=val;
+ break;
+ case SP_FLEE1:
+ if(sd->state.lr_flag != 2)
+ sd->flee+=val;
+ break;
+ case SP_FLEE2:
+ if(sd->state.lr_flag != 2)
+ sd->flee2+=val*10;
+ break;
+ case SP_CRITICAL:
+ if(sd->state.lr_flag != 2)
+ sd->critical+=val*10;
+ else
+ sd->arrow_cri += val*10;
+ break;
+ case SP_ATKELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.atk_ele=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.atk_ele=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_ele=val;
+ break;
+ case SP_DEFELE:
+ if(sd->state.lr_flag != 2)
+ sd->def_ele=val;
+ break;
+ case SP_MAXHP:
+ if(sd->state.lr_flag != 2)
+ sd->status.max_hp+=val;
+ break;
+ case SP_MAXSP:
+ if(sd->state.lr_flag != 2)
+ sd->status.max_sp+=val;
+ break;
+ case SP_CASTRATE:
+ if(sd->state.lr_flag != 2)
+ sd->castrate+=val;
+ break;
+ case SP_MAXHPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->hprate+=val;
+ break;
+ case SP_MAXSPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->sprate+=val;
+ break;
+ case SP_SPRATE:
+ if(sd->state.lr_flag != 2)
+ sd->dsprate+=val;
+ break;
+ case SP_ATTACKRANGE:
+ if(!sd->state.lr_flag)
+ sd->attackrange += val;
+ else if(sd->state.lr_flag == 1)
+ sd->attackrange_ += val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_range += val;
+ break;
+ case SP_ADD_SPEED: //Raw increase
+ if(sd->state.lr_flag != 2)
+ sd->speed -= val;
+ break;
+ case SP_SPEED_RATE: //Non stackable increase
+ if(sd->state.lr_flag != 2 && sd->speed_rate > 100-val)
+ sd->speed_rate = 100-val;
+ break;
+ case SP_SPEED_ADDRATE: //Stackable increase
+ if(sd->state.lr_flag != 2)
+ sd->speed_add_rate = sd->speed_add_rate * (100-val)/100;
+ break;
+ case SP_ASPD: //Raw increase
+ if(sd->state.lr_flag != 2)
+ sd->aspd -= val*10;
+ break;
+ case SP_ASPD_RATE: //Non stackable increase
+ if(sd->state.lr_flag != 2 && sd->aspd_rate > 100-val)
+ sd->aspd_rate = 100-val;
+ break;
+ case SP_ASPD_ADDRATE: //Stackable increase - Made it linear as per rodatazone
+ if(sd->state.lr_flag != 2)
+ sd->aspd_add_rate -= val;
+ break;
+ case SP_HP_RECOV_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->hprecov_rate += val;
+ break;
+ case SP_SP_RECOV_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->sprecov_rate += val;
+ break;
+ case SP_CRITICAL_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->critical_def += val;
+ break;
+ case SP_NEAR_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->near_attack_def_rate += val;
+ break;
+ case SP_LONG_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->long_attack_def_rate += val;
+ break;
+ case SP_DOUBLE_RATE:
+ if(sd->state.lr_flag == 0 && sd->double_rate < val)
+ sd->double_rate = val;
+ break;
+ case SP_DOUBLE_ADD_RATE:
+ if(sd->state.lr_flag == 0)
+ sd->double_add_rate += val;
+ break;
+ case SP_MATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->matk_rate += val;
+ break;
+ case SP_IGNORE_DEF_ELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_ele |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_ele |= 1<<val;
+ break;
+ case SP_IGNORE_DEF_RACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_race |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_race |= 1<<val;
+ break;
+ case SP_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->atk_rate += val;
+ break;
+ case SP_MAGIC_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->magic_def_rate += val;
+ break;
+ case SP_MISC_ATK_DEF:
+ if(sd->state.lr_flag != 2)
+ sd->misc_def_rate += val;
+ break;
+ case SP_IGNORE_MDEF_ELE:
+ if(sd->state.lr_flag != 2)
+ sd->ignore_mdef_ele |= 1<<val;
+ break;
+ case SP_IGNORE_MDEF_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->ignore_mdef_race |= 1<<val;
+ break;
+ case SP_PERFECT_HIT_RATE:
+ if(sd->state.lr_flag != 2 && sd->perfect_hit < val)
+ sd->perfect_hit = val;
+ break;
+ case SP_PERFECT_HIT_ADD_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->perfect_hit_add += val;
+ break;
+ case SP_CRITICAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->critical_rate+=val;
+ break;
+ case SP_DEF_RATIO_ATK_ELE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.def_ratio_atk_ele |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.def_ratio_atk_ele |= 1<<val;
+ break;
+ case SP_DEF_RATIO_ATK_RACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.def_ratio_atk_race |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.def_ratio_atk_race |= 1<<val;
+ break;
+ case SP_HIT_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->hit_rate += val;
+ break;
+ case SP_FLEE_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->flee_rate += val;
+ break;
+ case SP_FLEE2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->flee2_rate += val;
+ break;
+ case SP_DEF_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->def_rate += val;
+ break;
+ case SP_DEF2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->def2_rate += val;
+ break;
+ case SP_MDEF_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->mdef_rate += val;
+ break;
+ case SP_MDEF2_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->mdef2_rate += val;
+ break;
+ case SP_RESTART_FULL_RECOVER:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.restart_full_recover = 1;
+ break;
+ case SP_NO_CASTCANCEL:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_castcancel = 1;
+ break;
+ case SP_NO_CASTCANCEL2:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_castcancel2 = 1;
+ break;
+ case SP_NO_SIZEFIX:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_sizefix = 1;
+ break;
+ case SP_NO_MAGIC_DAMAGE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_magic_damage = 1;
+ break;
+ case SP_NO_WEAPON_DAMAGE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_weapon_damage = 1;
+ break;
+ case SP_NO_GEMSTONE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.no_gemstone = 1;
+ break;
+ case SP_INFINITE_ENDURE:
+ if(sd->state.lr_flag != 2)
+ sd->special_state.infinite_endure = 1;
+ break;
+ case SP_INTRAVISION: // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
+ if(sd->state.lr_flag != 2)
+ sd->special_state.intravision = 1;
+ break;
+ case SP_SPLASH_RANGE:
+ if(sd->state.lr_flag != 2 && sd->splash_range < val)
+ sd->splash_range = val;
+ break;
+ case SP_SPLASH_ADD_RANGE:
+ if(sd->state.lr_flag != 2)
+ sd->splash_add_range += val;
+ break;
+ case SP_SHORT_WEAPON_DAMAGE_RETURN:
+ if(sd->state.lr_flag != 2)
+ sd->short_weapon_damage_return += val;
+ break;
+ case SP_LONG_WEAPON_DAMAGE_RETURN:
+ if(sd->state.lr_flag != 2)
+ sd->long_weapon_damage_return += val;
+ break;
+ case SP_MAGIC_DAMAGE_RETURN: //AppleGirl Was Here
+ if(sd->state.lr_flag != 2)
+ sd->magic_damage_return += val;
+ break;
+ case SP_ALL_STATS: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_STR-SP_STR]+=val;
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_VIT-SP_STR]+=val;
+ sd->parame[SP_INT-SP_STR]+=val;
+ sd->parame[SP_DEX-SP_STR]+=val;
+ sd->parame[SP_LUK-SP_STR]+=val;
+ }
+ break;
+ case SP_AGI_VIT: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_VIT-SP_STR]+=val;
+ }
+ break;
+ case SP_AGI_DEX_STR: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->parame[SP_AGI-SP_STR]+=val;
+ sd->parame[SP_DEX-SP_STR]+=val;
+ sd->parame[SP_STR-SP_STR]+=val;
+ }
+ break;
+ case SP_PERFECT_HIDE: // [Valaris]
+ if(sd->state.lr_flag!=2) {
+ sd->state.perfect_hiding=1;
+ }
+ break;
+ case SP_DISGUISE: // Disguise script for items [Valaris]
+ if(sd->state.lr_flag!=2 && !sd->state.disguised && !pc_isriding(sd)) {
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise=val;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ }
+ break;
+ case SP_UNBREAKABLE:
+ if(sd->state.lr_flag!=2) {
+ sd->unbreakable += val;
+ }
+ break;
+ case SP_UNBREAKABLE_WEAPON:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_WEAPON;
+ break;
+ case SP_UNBREAKABLE_ARMOR:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_ARMOR;
+ break;
+ case SP_UNBREAKABLE_HELM:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_HELM;
+ break;
+ case SP_UNBREAKABLE_SHIELD:
+ if(sd->state.lr_flag != 2)
+ sd->unbreakable_equip |= EQP_SHIELD;
+ break;
+ case SP_CLASSCHANGE: // [Valaris]
+ if(sd->state.lr_flag !=2){
+ sd->classchange=val;
+ }
+ break;
+ case SP_LONG_ATK_RATE:
+ //if(sd->state.lr_flag != 2 && sd->long_attack_atk_rate < val)
+ // sd->long_attack_atk_rate = val;
+ if(sd->state.lr_flag != 2) //[Lupus] it should stack, too. As any other cards rate bonuses
+ sd->long_attack_atk_rate+=val;
+ break;
+ case SP_BREAK_WEAPON_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->break_weapon_rate+=val;
+ break;
+ case SP_BREAK_ARMOR_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->break_armor_rate+=val;
+ break;
+ case SP_ADD_STEAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->add_steal_rate+=val;
+ break;
+ case SP_DELAYRATE:
+ if(sd->state.lr_flag != 2)
+ sd->delayrate+=val;
+ break;
+ case SP_CRIT_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->crit_atk_rate += val;
+ break;
+ case SP_NO_REGEN:
+ if(sd->state.lr_flag != 2)
+ sd->no_regen = val;
+ break;
+ case SP_UNSTRIPABLE_WEAPON:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_WEAPON;
+ break;
+ case SP_UNSTRIPABLE:
+ case SP_UNSTRIPABLE_ARMOR:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_ARMOR;
+ break;
+ case SP_UNSTRIPABLE_HELM:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_HELM;
+ break;
+ case SP_UNSTRIPABLE_SHIELD:
+ if(sd->state.lr_flag != 2)
+ sd->unstripable_equip |= EQP_SHIELD;
+ break;
+ case SP_HP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_value += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_value += val;
+ }
+ break;
+ case SP_SP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_value += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_value += val;
+ }
+ break;
+ case SP_SP_GAIN_VALUE:
+ if(!sd->state.lr_flag)
+ sd->sp_gain_value += val;
+ break;
+ case SP_IGNORE_DEF_MOB: // 0:normal monsters only, 1:affects boss monsters as well
+ if(!sd->state.lr_flag)
+ sd->right_weapon.ignore_def_mob |= 1<<val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.ignore_def_mob |= 1<<val;
+ break;
+ case SP_HP_GAIN_VALUE:
+ if(!sd->state.lr_flag)
+ sd->hp_gain_value += val;
+ break;
+ case SP_DAMAGE_WHEN_UNEQUIP:
+ if(!sd->state.lr_flag) {
+ for (i=0; i<11; i++) {
+ //I think this one is bugged, notice how it uses the item_db info rather
+ // than inventory equipped position index [Skotlex]
+// if (sd->inventory_data[current_equip_item_index]->equip & equip_pos[i]) {
+ if(sd->status.inventory[current_equip_item_index].equip & equip_pos[i]) {
+ sd->unequip_losehp[i] += val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_LOSESP_WHEN_UNEQUIP:
+ if(!sd->state.lr_flag) {
+ for (i=0; i<11; i++) {
+// if (sd->inventory_data[current_equip_item_index]->equip & equip_pos[i]) {
+ if(sd->status.inventory[current_equip_item_index].equip & equip_pos[i]) {
+ sd->unequip_losesp[i] += val;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus: unknown type %d %d !\n",type,val);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? ”õ•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->right_weapon.addele[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addele[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addele[type2]+=val;
+ break;
+ case SP_ADDRACE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.addrace[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addrace[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addrace[type2]+=val;
+ break;
+ case SP_ADDSIZE:
+ if(!sd->state.lr_flag)
+ sd->right_weapon.addsize[type2]+=val;
+ else if(sd->state.lr_flag == 1)
+ sd->left_weapon.addsize[type2]+=val;
+ else if(sd->state.lr_flag == 2)
+ sd->arrow_addsize[type2]+=val;
+ break;
+ case SP_SUBELE:
+ if(sd->state.lr_flag != 2)
+ sd->subele[type2]+=val;
+ break;
+ case SP_SUBRACE:
+ if(sd->state.lr_flag != 2)
+ sd->subrace[type2]+=val;
+ break;
+ case SP_ADDEFF:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->addeff[type2-SC_COMMON_MIN]+=val;
+ else
+ sd->arrow_addeff[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_ADDEFF2:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect2): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->addeff2[type2-SC_COMMON_MIN]+=val;
+ else
+ sd->arrow_addeff2[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_RESEFF:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Resist Effect): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2)
+ sd->reseff[type2-SC_COMMON_MIN]+=val;
+ break;
+ case SP_MAGIC_ADDELE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addele[type2]+=val;
+ break;
+ case SP_MAGIC_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addrace[type2]+=val;
+ break;
+ case SP_MAGIC_ADDSIZE:
+ if(sd->state.lr_flag != 2)
+ sd->magic_addsize[type2]+=val;
+ break;
+ case SP_ADD_DAMAGE_CLASS:
+ if(!sd->state.lr_flag) {
+ for(i=0;i<sd->right_weapon.add_damage_class_count;i++) {
+ if(sd->right_weapon.add_damage_classid[i] == type2) {
+ sd->right_weapon.add_damage_classrate[i] += val;
+ break;
+ }
+ }
+ if(i >= sd->right_weapon.add_damage_class_count && sd->right_weapon.add_damage_class_count < 10) {
+ sd->right_weapon.add_damage_classid[sd->right_weapon.add_damage_class_count] = type2;
+ sd->right_weapon.add_damage_classrate[sd->right_weapon.add_damage_class_count] += val;
+ sd->right_weapon.add_damage_class_count++;
+ }
+ }
+ else if(sd->state.lr_flag == 1) {
+ for(i=0;i<sd->left_weapon.add_damage_class_count;i++) {
+ if(sd->left_weapon.add_damage_classid[i] == type2) {
+ sd->left_weapon.add_damage_classrate[i] += val;
+ break;
+ }
+ }
+ if(i >= sd->left_weapon.add_damage_class_count && sd->left_weapon.add_damage_class_count < 10) {
+ sd->left_weapon.add_damage_classid[sd->left_weapon.add_damage_class_count] = type2;
+ sd->left_weapon.add_damage_classrate[sd->left_weapon.add_damage_class_count] += val;
+ sd->left_weapon.add_damage_class_count++;
+ }
+ }
+ break;
+ case SP_ADD_MAGIC_DAMAGE_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_mdmg_count;i++) {
+ if(sd->add_mdmg[i].class_ == type2) {
+ sd->add_mdmg[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_mdmg_count && sd->add_mdmg_count < MAX_PC_BONUS) {
+ sd->add_mdmg[sd->add_mdmg_count].class_ = type2;
+ sd->add_mdmg[sd->add_mdmg_count].rate += val;
+ sd->add_mdmg_count++;
+ }
+ }
+ break;
+ case SP_ADD_DEF_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_def_count;i++) {
+ if(sd->add_def[i].class_ == type2) {
+ sd->add_def[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_def_count && sd->add_def_count < MAX_PC_BONUS) {
+ sd->add_def[sd->add_def_count].class_ = type2;
+ sd->add_def[sd->add_def_count].rate += val;
+ sd->add_def_count++;
+ }
+ }
+ break;
+ case SP_ADD_MDEF_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_mdef_count;i++) {
+ if(sd->add_mdef[i].class_ == type2) {
+ sd->add_mdef[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_mdef_count && sd->add_mdef_count < MAX_PC_BONUS) {
+ sd->add_mdef[sd->add_mdef_count].class_ = type2;
+ sd->add_mdef[sd->add_mdef_count].rate += val;
+ sd->add_mdef_count++;
+ }
+ }
+ break;
+ case SP_HP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_rate += type2;
+ sd->right_weapon.hp_drain_per += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_rate += type2;
+ sd->left_weapon.hp_drain_per += val;
+ }
+ break;
+ case SP_HP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.hp_drain_value += type2;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.hp_drain_value += type2;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_SP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_rate += type2;
+ sd->right_weapon.sp_drain_per += val;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_rate += type2;
+ sd->left_weapon.sp_drain_per += val;
+ }
+ sd->sp_drain_type = 0;
+ break;
+ case SP_SP_DRAIN_VALUE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_value += type2;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_value += type2;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_GET_ZENY_NUM:
+ if(sd->state.lr_flag != 2 && sd->get_zeny_rate < val)
+ {
+ sd->get_zeny_rate = val;
+ sd->get_zeny_num = type2;
+ }
+ break;
+ case SP_ADD_GET_ZENY_NUM:
+ if(sd->state.lr_flag != 2)
+ {
+ sd->get_zeny_rate += val;
+ sd->get_zeny_num += type2;
+ }
+ break;
+ case SP_WEAPON_COMA_ELE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_coma_ele[type2] += val;
+ break;
+ case SP_WEAPON_COMA_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_coma_race[type2] += val;
+ break;
+ case SP_RANDOM_ATTACK_INCREASE: // [Valaris]
+ if(sd->state.lr_flag !=2){
+ sd->random_attack_increase_add = type2;
+ sd->random_attack_increase_per += val;
+ }
+ break;
+ case SP_WEAPON_ATK:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_atk[type2]+=val;
+ break;
+ case SP_WEAPON_ATK_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->weapon_atk_rate[type2]+=val;
+ break;
+ case SP_CRITICAL_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->critaddrace[type2] += val*10;
+ break;
+ case SP_ADDEFF_WHENHIT:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect when hit): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ sd->addeff3[type2-SC_COMMON_MIN]+=val;
+ sd->addeff3_type[type2-SC_COMMON_MIN]=1;
+ }
+ break;
+ case SP_ADDEFF_WHENHIT_SHORT:
+ if (type2 < SC_COMMON_MIN || type2 > SC_COMMON_MAX) {
+ ShowWarning("pc_bonus2 (Add Effect when hit short): %d is not supported.\n", type2);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ sd->addeff3[type2-SC_COMMON_MIN]+=val;
+ sd->addeff3_type[type2-SC_COMMON_MIN]=0;
+ }
+ break;
+ case SP_SKILL_ATK:
+ for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != type2; i++);
+ if (i == MAX_PC_BONUS)
+ { //Better mention this so the array length can be updated. [Skotlex]
+ ShowDebug("run_script: bonus2 bSkillAtk reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n", MAX_PC_BONUS, type2, val);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ if (sd->skillatk[i].id == type2)
+ sd->skillatk[i].val += val;
+ else {
+ sd->skillatk[i].id = type2;
+ sd->skillatk[i].val = val;
+ }
+ }
+ break;
+ case SP_ADD_SKILL_BLOW:
+ for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != type2; i++);
+ if (i == MAX_PC_BONUS)
+ { //Better mention this so the array length can be updated. [Skotlex]
+ ShowDebug("run_script: bonus2 bSkillBlown reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n", MAX_PC_BONUS, type2, val);
+ break;
+ }
+ if(sd->state.lr_flag != 2) {
+ if (sd->skillblown[i].id == type2)
+ sd->skillblown[i].val += val;
+ else {
+ sd->skillblown[i].id = type2;
+ sd->skillblown[i].val = val;
+ }
+ }
+ break;
+ case SP_ADD_DAMAGE_BY_CLASS:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_dmg_count;i++) {
+ if(sd->add_dmg[i].class_ == type2) {
+ sd->add_dmg[i].rate += val;
+ break;
+ }
+ }
+ if(i >= sd->add_dmg_count && sd->add_dmg_count < MAX_PC_BONUS) {
+ sd->add_dmg[sd->add_dmg_count].class_ = type2;
+ sd->add_dmg[sd->add_dmg_count].rate += val;
+ sd->add_dmg_count++;
+ }
+ }
+ break;
+ case SP_HP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->hp_loss_value = type2;
+ sd->hp_loss_rate = val;
+ }
+ break;
+ case SP_ADDRACE2:
+ if (!(type2 > 0 && type2 < MAX_MOB_RACE_DB))
+ break;
+ if(sd->state.lr_flag != 2)
+ sd->right_weapon.addrace2[type2] += val;
+ else
+ sd->left_weapon.addrace2[type2] += val;
+ break;
+ case SP_SUBSIZE:
+ if(sd->state.lr_flag != 2)
+ sd->subsize[type2]+=val;
+ break;
+ case SP_SUBRACE2:
+ if(sd->state.lr_flag != 2)
+ sd->subrace2[type2]+=val;
+ break;
+ case SP_ADD_ITEM_HEAL_RATE:
+ if(sd->state.lr_flag != 2)
+ sd->itemhealrate[type2 - 1] += val;
+ break;
+ case SP_EXP_ADDRACE:
+ if(sd->state.lr_flag != 2)
+ sd->expaddrace[type2]+=val;
+ break;
+ case SP_SP_GAIN_RACE:
+ if(sd->state.lr_flag != 2)
+ sd->sp_gain_race[type2]+=val;
+ break;
+ case SP_ADD_MONSTER_DROP_ITEM:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].id == type2) {
+ sd->add_drop[i].race |= (1<<10)|(1<<11);
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].id = type2;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= (1<<10)|(1<<11);
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_ADD_MONSTER_DROP_ITEMGROUP:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].group == type2) {
+ sd->add_drop[i].id = 0;
+ sd->add_drop[i].race |= (1<<10)|(1<<11);
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].group = type2;
+ sd->add_drop[sd->add_drop_count].id = 0;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= (1<<10)|(1<<11);
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_SP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->sp_loss_value = type2;
+ sd->sp_loss_rate = val;
+ }
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus2: unknown type %d %d %d!\n",type,type2,val);
+ break;
+ }
+ return 0;
+}
+
+int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_ADD_MONSTER_DROP_ITEM:
+ if(sd->state.lr_flag != 2) {
+ for(i=0;i<sd->add_drop_count;i++) {
+ if(sd->add_drop[i].id == type2) {
+ sd->add_drop[i].race |= 1<<type3;
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < MAX_PC_BONUS) {
+ sd->add_drop[sd->add_drop_count].id = type2;
+ sd->add_drop[sd->add_drop_count].race |= 1<<type3;
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+ case SP_AUTOSPELL:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0 ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv < type3) ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv == type3 && sd->autospell[i].rate < val))
+ {
+ sd->autospell[i].id = type2;
+ sd->autospell[i].lv = type3;
+ sd->autospell[i].rate = val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_AUTOSPELL_WHENHIT:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell2[i].id == 0 ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv < type3) ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv == type3 && sd->autospell2[i].rate < val))
+ {
+ sd->autospell2[i].id = type2;
+ sd->autospell2[i].lv = type3;
+ sd->autospell2[i].rate = val;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_HP_LOSS_RATE:
+ if(sd->state.lr_flag != 2) {
+ sd->hp_loss_value = type2;
+ sd->hp_loss_rate = type3;
+ sd->hp_loss_type = val;
+ }
+ break;
+ case SP_SP_DRAIN_RATE:
+ if(!sd->state.lr_flag) {
+ sd->right_weapon.sp_drain_rate += type2;
+ sd->right_weapon.sp_drain_per += type3;
+ }
+ else if(sd->state.lr_flag == 1) {
+ sd->left_weapon.sp_drain_rate += type2;
+ sd->left_weapon.sp_drain_per += type3;
+ }
+ sd->sp_drain_type = val;
+ break;
+ case SP_ADD_MONSTER_DROP_ITEMGROUP:
+ if (sd->state.lr_flag != 2) {
+ for(i = 0; i < sd->add_drop_count; i++) {
+ if(sd->add_drop[i].group == type2) {
+ sd->add_drop[i].id = 0;
+ sd->add_drop[i].race |= 1<<type3;
+ if(sd->add_drop[i].rate < val)
+ sd->add_drop[i].rate = val;
+ break;
+ }
+ }
+ if(i >= sd->add_drop_count && sd->add_drop_count < 10) {
+ sd->add_drop[sd->add_drop_count].group = type2;
+ sd->add_drop[sd->add_drop_count].id = 0;
+ // all monsters, including boss and non boss monsters
+ sd->add_drop[sd->add_drop_count].race |= 1<<type3;
+ sd->add_drop[sd->add_drop_count].rate = val;
+ sd->add_drop_count++;
+ }
+ }
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus3: unknown type %d %d %d %d!\n",type,type2,type3,val);
+ break;
+ }
+
+ return 0;
+}
+
+int pc_bonus4(struct map_session_data *sd,int type,int type2,int type3,int type4,int val)
+{
+ int i;
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_AUTOSPELL:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0 ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv < type3) ||
+ (sd->autospell[i].id == type2 && sd->autospell[i].lv == type3 && sd->autospell[i].rate < type4))
+ {
+ sd->autospell[i].id = (val) ? type2 : -type2; // val = 0: self, 1: enemy
+ sd->autospell[i].lv = type3;
+ sd->autospell[i].rate = type4;
+ break;
+ }
+ }
+ }
+ break;
+ case SP_AUTOSPELL_WHENHIT:
+ if(sd->state.lr_flag != 2) {
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell2[i].id == 0 ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv < type3) ||
+ (sd->autospell2[i].id == type2 && sd->autospell2[i].lv == type3 && sd->autospell2[i].rate < type4))
+ {
+ sd->autospell2[i].id = (val) ? type2 : -type2; // val = 0: self, 1: enemy
+ sd->autospell2[i].lv = type3;
+ sd->autospell2[i].rate = type4;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowWarning("pc_bonus4: unknown type %d %d %d %d %d!\n",type,type2,type3,type4,val);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒg‚É‚æ‚éƒXƒLƒ‹Š“¾
+ *------------------------------------------
+ */
+int pc_skill(struct map_session_data *sd,int id,int level,int flag)
+{
+ nullpo_retr(0, sd);
+
+ if(level>MAX_SKILL_LEVEL){
+ if(battle_config.error_log)
+ ShowError("support card skill only!\n");
+ return 0;
+ }
+ if(!flag && (sd->status.skill[id].id == id || level == 0)){ // ƒNƒGƒXƒgŠ“¾‚Ȃ炱‚±‚Å?Œ‚ðŠm”F‚µ‚Ä‘—M‚·‚é
+ sd->status.skill[id].lv=level;
+ status_calc_pc(sd,0);
+ clif_skillinfoblock(sd);
+ }
+ else if(flag==2 && (sd->status.skill[id].id == id || level == 0)){ // ƒNƒGƒXƒgŠ“¾‚Ȃ炱‚±‚Å?Œ‚ðŠm”F‚µ‚Ä‘—M‚·‚é
+ sd->status.skill[id].lv+=level;
+ status_calc_pc(sd,0);
+ clif_skillinfoblock(sd);
+ }
+ else if(sd->status.skill[id].lv < level){ // ?‚¦‚ç‚ê‚邪lv‚ª¬‚³‚¢‚È‚ç
+ if(sd->status.skill[id].id==id)
+ sd->status.skill[id].flag=sd->status.skill[id].lv+2; // lv‚ð‹L‰¯
+ else {
+ sd->status.skill[id].id=id;
+ sd->status.skill[id].flag=1; // cardƒXƒLƒ‹‚Æ‚·‚é
+ }
+ sd->status.skill[id].lv=level;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_blockskill_end(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (sd) sd->blockskill[data] = 0;
+
+ return 1;
+}
+int pc_blockskill_start (struct map_session_data *sd, int skillid, int tick)
+{
+ nullpo_retr (-1, sd);
+
+ if (skillid >= 10000 && skillid < 10015)
+ skillid -= 9500;
+ else if (skillid < 1 || skillid > MAX_SKILL)
+ return -1;
+
+ sd->blockskill[skillid] = 1;
+ return add_timer(gettick()+tick,pc_blockskill_end,sd->bl.id,skillid);
+}
+
+/*==========================================
+ * ƒJ?ƒh?“ü
+ *------------------------------------------
+ */
+int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip)
+{
+ int i, ep;
+ int nameid, cardid;
+
+ nullpo_retr(0, sd);
+
+ if(idx_card < 0 || idx_card >= MAX_INVENTORY || !sd->inventory_data[idx_card])
+ return 0; //Invalid card index.
+
+ if(idx_equip < 0 || idx_equip >= MAX_INVENTORY || !sd->inventory_data[idx_equip])
+ return 0; //Invalid item index.
+
+ nameid=sd->status.inventory[idx_equip].nameid;
+ cardid=sd->status.inventory[idx_card].nameid;
+ ep=sd->inventory_data[idx_card]->equip;
+
+ if( nameid <= 0 || cardid <= 0 ||
+ (sd->inventory_data[idx_equip]->type!=4 && sd->inventory_data[idx_equip]->type!=5)|| // ? ”õ‚¶‚á‚È‚¢
+ sd->inventory_data[idx_card]->type!=6 || // Prevent Hack [Ancyker]
+ sd->status.inventory[idx_equip].identify==0 || // –¢ŠÓ’è
+ sd->status.inventory[idx_equip].card[0]==0x00ff || // »‘¢•Ší
+ sd->status.inventory[idx_equip].card[0]==0x00fe ||
+ sd->status.inventory[idx_equip].card[0]==(short)0xff00 ||
+ !(sd->inventory_data[idx_equip]->equip&ep) || // ? ”õŒÂŠˆá‚¢
+ (sd->inventory_data[idx_equip]->type==4 && ep==32) || // ? Žè•Ší‚Æ‚ƒJ?ƒh
+ 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){
+ // ‹ó‚«ƒXƒƒbƒg‚ª‚ ‚Á‚½‚Ì‚Å·‚µ?‚Þ
+ sd->status.inventory[idx_equip].card[i]=cardid;
+
+ // ƒJ?ƒh‚ÍŒ¸‚ç‚·
+ clif_insert_card(sd,idx_equip,idx_card,0);
+ pc_delitem(sd,idx_card,1,1);
+ return 0;
+ }
+ }
+ clif_insert_card(sd,idx_equip,idx_card,1);
+ return 0;
+}
+
+//
+// ƒAƒCƒeƒ€•¨
+//
+
+/*==========================================
+ * ƒXƒLƒ‹‚É‚æ‚锃‚¢’lC³
+ *------------------------------------------
+ */
+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) // ƒfƒBƒXƒJƒEƒ“ƒg
+ rate1 = 5+skill*2-((skill==10)? 1:0);
+ if((skill=pc_checkskill(sd,RG_COMPULSION))>0) // ƒRƒ€ƒpƒ‹ƒVƒ‡ƒ“ƒfƒBƒXƒJƒEƒ“ƒg
+ 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;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹‚É‚æ‚é?‚è’lC³
+ *------------------------------------------
+ */
+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) // ƒI?ƒo?ƒ`ƒƒ?ƒW
+ 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;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚𔃂Á‚½ŽbÉAV‚µ‚¢ƒAƒCƒeƒ€—“‚ðŽg‚¤‚©A
+ * 3–œŒÂ§ŒÀ‚É‚©‚©‚é‚©Šm”F
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ‹ó‚«ƒAƒCƒeƒ€—“‚ÌŒÂ?
+ *------------------------------------------
+ */
+int pc_inventoryblank(struct map_session_data *sd)
+{
+ int i,b;
+
+ nullpo_retr(0, sd);
+
+ for(i=0,b=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0)
+ b++;
+ }
+
+ return b;
+}
+
+/*==========================================
+ * ‚¨‹à‚ð?‚¤
+ *------------------------------------------
+ */
+int pc_payzeny(struct map_session_data *sd,int zeny)
+{
+ double z;
+
+ nullpo_retr(0, sd);
+
+ z = (double)sd->status.zeny;
+ if(sd->status.zeny<zeny || z - (double)zeny > MAX_ZENY)
+ return 1;
+ sd->status.zeny-=zeny;
+ clif_updatestatus(sd,SP_ZENY);
+
+ return 0;
+}
+
+/*==========================================
+ * ‚¨‹à‚𓾂é
+ *------------------------------------------
+ */
+int pc_getzeny(struct map_session_data *sd,int zeny)
+{
+ double z;
+
+ nullpo_retr(0, sd);
+
+ z = (double)sd->status.zeny;
+ if(z + (double)zeny > MAX_ZENY) {
+ zeny = 0;
+ sd->status.zeny = MAX_ZENY;
+ }
+ sd->status.zeny+=zeny;
+ clif_updatestatus(sd,SP_ZENY);
+ if(zeny > 0 && sd->state.showzeny){
+ char output[255];
+ sprintf(output, "Gained %dz.", zeny);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚ð’T‚µ‚ÄAƒCƒ“ƒfƒbƒNƒX‚ð•Ô‚·
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€’ljÁBŒÂ?‚Ì‚Ýitem\‘¢?‚Ì?Žš‚𖳎‹
+ *------------------------------------------
+ */
+int pc_additem(struct map_session_data *sd,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+ long w;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ data = itemdb_search(item_data->nameid);
+ w = data->weight*amount;
+ if(w + (long)sd->weight > (long)sd->max_weight || w + (long)sd->weight < 0) //Weight overflow check?
+ return 2;
+
+ i = MAX_INVENTORY;
+
+ if (!itemdb_isequip2(data)){
+ // ‘• ”õ•i‚Å‚Í‚È‚¢‚Ì‚ÅAŠùŠ—L•i‚È‚çŒÂ”‚̂ݕω»‚³‚¹‚é
+ for (i = 0; i < MAX_INVENTORY; i++)
+ if(sd->status.inventory[i].nameid == item_data->nameid &&
+ sd->status.inventory[i].card[0] == item_data->card[0] &&
+ sd->status.inventory[i].card[1] == item_data->card[1] &&
+ sd->status.inventory[i].card[2] == item_data->card[2] &&
+ sd->status.inventory[i].card[3] == item_data->card[3])
+ {
+ if (amount < 0 || amount > MAX_AMOUNT || sd->status.inventory[i].amount + amount > MAX_AMOUNT)
+ return 5;
+ sd->status.inventory[i].amount += amount;
+ clif_additem(sd,i,amount,0);
+ break;
+ }
+ }
+ if (i >= MAX_INVENTORY){
+ // ‘• ”õ•i‚©–¢Š—L•i‚¾‚Á‚½‚Ì‚Å‹ó‚«—“‚֒ljÁ
+ i = pc_search_inventory(sd,0);
+ if(i >= 0) {
+ // clear equips field first, just in case
+ if (item_data->equip != 0)
+ item_data->equip = 0;
+ memcpy(&sd->status.inventory[i], item_data, sizeof(sd->status.inventory[0]));
+ sd->status.inventory[i].amount = amount;
+ sd->inventory_data[i] = data;
+ clif_additem(sd,i,amount,0);
+ }
+ else return 4;
+ }
+ sd->weight += (int)w;
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚ðŒ¸‚ç‚·
+ *------------------------------------------
+ */
+int pc_delitem(struct map_session_data *sd,int n,int amount,int type)
+{
+ nullpo_retr(1, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ sd->status.inventory[n].amount -= amount;
+ sd->weight -= sd->inventory_data[n]->weight*amount ;
+ if(sd->status.inventory[n].amount<=0){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,3);
+ memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0]));
+ sd->inventory_data[n] = NULL;
+ }
+ if(!(type&1))
+ clif_delitem(sd,n,amount);
+ if(!(type&2))
+ clif_updatestatus(sd,SP_WEIGHT);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚ð—Ž‚·
+ *------------------------------------------
+ */
+int pc_dropitem(struct map_session_data *sd,int n,int amount)
+{
+ nullpo_retr(1, sd);
+
+ if(n < 0 || n >= MAX_INVENTORY)
+ return 1;
+
+ if(amount <= 0)
+ return 1;
+
+ if (sd->status.inventory[n].nameid <= 0 ||
+ sd->status.inventory[n].amount < amount ||
+ sd->trade_partner != 0 || sd->vender_id != 0 ||
+ sd->status.inventory[n].amount <= 0)
+ return 1;
+
+ if (!pc_candrop(sd,sd->status.inventory[n].nameid))
+ { //The client does not likes being silently ignored, so we send it a del of 0 qty
+ clif_delitem(sd,n,0);
+ clif_displaymessage (sd->fd, msg_txt(263));
+ return 1;
+ }
+
+ //Logs items, dropped by (P)layers [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(sd, "P", 0, sd->status.inventory[n].nameid, -amount, (struct item*)&sd->status.inventory[n]);
+ //Logs
+
+ if (map_addflooritem(&sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0) != 0)
+ pc_delitem(sd, n, amount, 0);
+ else
+ clif_delitem(sd,n,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚ðE‚¤
+ *------------------------------------------
+ */
+int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem)
+{
+ int flag=0;
+ unsigned int tick = gettick();
+ struct map_session_data *first_sd = NULL,*second_sd = NULL,*third_sd = NULL;
+ struct party *p=NULL;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, fitem);
+
+ if(!check_distance_bl(&fitem->bl, &sd->bl, 2) && sd->skillid!=BS_GREED)
+ return 0; // ‹——£‚ª‰“‚¢
+
+ if (sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if(fitem->first_get_id > 0 && fitem->first_get_id != sd->bl.id) {
+ first_sd = map_id2sd(fitem->first_get_id);
+ if(DIFF_TICK(tick,fitem->first_get_tick) < 0) {
+ if (!(p && p->item&1 &&
+ first_sd && first_sd->status.party_id == sd->status.party_id
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ else if(fitem->second_get_id > 0 && fitem->second_get_id != sd->bl.id) {
+ second_sd = map_id2sd(fitem->second_get_id);
+ if(DIFF_TICK(tick, fitem->second_get_tick) < 0) {
+ if(!(p && p->item&1 &&
+ ((first_sd && first_sd->status.party_id == sd->status.party_id) ||
+ (second_sd && second_sd->status.party_id == sd->status.party_id))
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ else if(fitem->third_get_id > 0 && fitem->third_get_id != sd->bl.id) {
+ third_sd = map_id2sd(fitem->third_get_id);
+ if(DIFF_TICK(tick,fitem->third_get_tick) < 0) {
+ if(!(p && p->item&1 &&
+ ((first_sd && first_sd->status.party_id == sd->status.party_id) ||
+ (second_sd && second_sd->status.party_id == sd->status.party_id) ||
+ (third_sd && third_sd->status.party_id == sd->status.party_id))
+ )) {
+ clif_additem(sd,0,0,6);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ first_sd = NULL; //First_sd will store who picked up the item.
+ if (p && p->item&2) { //item distribution to party members.
+ first_sd = NULL;
+ if (battle_config.party_share_type) { //Round Robin
+ int i;
+ i = p->itemc;
+ do {
+ i++;
+ if (i >= MAX_PARTY)
+ i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc"
+ if ((second_sd=p->member[i].sd)==NULL || sd->bl.m != second_sd->bl.m)
+ continue;
+
+ if (pc_additem(second_sd,&fitem->item_data,fitem->item_data.amount))
+ continue; //Chosen char can't pick up loot.
+ //Successful pick.
+ first_sd = second_sd;
+ break;
+ } while (i != p->itemc);
+ // Skip to the current receiver of an item, so the next pick should not go to him again.
+ p->itemc = i;
+ } else { //Random pick
+ struct map_session_data*psd[MAX_PARTY];
+ int i, count=0;
+ //Collect pick candidates
+ for (i = 0; i < MAX_PARTY; i++) {
+ if ((psd[count]=p->member[i].sd) && psd[count]->bl.m == sd->bl.m)
+ count++;
+ }
+ if (count > 0) { //Pick a random member.
+ do {
+ i = rand()%count;
+ if (pc_additem(psd[i],&fitem->item_data,fitem->item_data.amount))
+ { //Discard this receiver.
+ psd[i] = psd[count-1];
+ count--;
+ } else { //Successful pick.
+ first_sd = psd[i];
+ break;
+ }
+ } while (count > 0);
+ }
+ }
+ }
+ if (!first_sd) { //Noone has picked it up yet...
+ if ((flag = pc_additem(sd,&fitem->item_data,fitem->item_data.amount))) {
+ clif_additem(sd,0,0,flag);
+ return 1;
+ }
+ first_sd = sd;
+ }
+ if(log_config.pick) //Logs items, taken by (P)layers [Lupus]
+ log_pick(first_sd, "P", 0, fitem->item_data.nameid, fitem->item_data.amount, (struct item*)&fitem->item_data);
+ //Logs
+ if(battle_config.party_show_share_picker && first_sd != sd){
+ char output[80];
+ sprintf(output, "%s acquired the item.",first_sd->status.name);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ //Display pickup animation.
+ if(sd->attacktimer != -1)
+ pc_stopattack(sd);
+
+ clif_takeitem(&sd->bl,&fitem->bl);
+ map_clearflooritem(fitem->bl.id);
+ return 0;
+}
+
+int pc_isUseitem(struct map_session_data *sd,int n)
+{
+ struct item_data *item;
+ int nameid;
+
+ nullpo_retr(0, sd);
+
+ item = sd->inventory_data[n];
+ nameid = sd->status.inventory[n].nameid;
+
+ if(item == NULL)
+ return 0;
+ //Not consumable item
+ if(item->type != 0 && item->type != 2)
+ return 0;
+ //Anodyne (can't use Anodyne's Endure at GVG)
+ if((nameid == 605) && map_flag_gvg(sd->bl.m))
+ return 0;
+ //Fly Wing (can't use at GVG and when noteleport flag is on)
+ if(nameid == 601 && (map[sd->bl.m].flag.noteleport || map_flag_gvg(sd->bl.m))) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ //Fly Wing (can't use when you in duel) [LuzZza]
+ if(nameid == 601 && (!battle_config.duel_allow_teleport && sd->duel_group)) {
+ clif_displaymessage(sd->fd, "Duel: Can't use this item in duel.");
+ return 0;
+ }
+ //Butterfly Wing (can't use noreturn flag is on and if duel)
+ if(nameid == 602 && map[sd->bl.m].flag.noreturn)
+ return 0;
+ //BW (can't use when you in duel) [LuzZza]
+ if(nameid == 602 && (!battle_config.duel_allow_teleport && sd->duel_group)) {
+ clif_displaymessage(sd->fd, "Duel: Can't use this item in duel.");
+ return 0;
+ }
+ //Dead Branch & Bloody Branch & Porings Box (can't use at GVG and when nobranch flag is on)
+ if((nameid == 604 || nameid == 12103 || nameid == 12109) && (map[sd->bl.m].flag.nobranch || map_flag_gvg(sd->bl.m)))
+ return 0;
+ //Gender check
+ if(item->sex != 2 && sd->status.sex != item->sex)
+ return 0;
+ //Required level check
+ if(item->elv && sd->status.base_level < (unsigned int)item->elv)
+ return 0;
+
+ //Not equipable by class. [Skotlex]
+ if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)]))
+ return 0;
+
+ //Not usable by upper class. [Skotlex]
+ if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper))
+ return 0;
+
+ //Dead Branch & Bloody Branch & Porings Box
+ if((log_config.branch > 0) && (nameid == 604 || nameid == 12103 || nameid == 12109))
+ log_branch(sd);
+
+ return 1;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚ðŽg‚¤
+ *------------------------------------------
+ */
+int pc_useitem(struct map_session_data *sd,int n)
+{
+ int amount;
+
+ nullpo_retr(1, sd);
+
+ if(n >=0 && n < MAX_INVENTORY) {
+ unsigned char *script;
+ sd->itemid = sd->status.inventory[n].nameid;
+ sd->itemindex = n;
+ amount = sd->status.inventory[n].amount;
+ if(sd->status.inventory[n].nameid <= 0 ||
+ sd->status.inventory[n].amount <= 0 ||
+ gettick() < sd->canuseitem_tick || //Prevent mass item usage. [Skotlex]
+ sd->sc_data[SC_BERSERK].timer!=-1 ||
+ sd->sc_data[SC_MARIONETTE].timer!=-1 ||
+ sd->sc_data[SC_GRAVITATION].timer!=-1 ||
+ //Cannot use Potions/Healing items while under Gospel.
+ (sd->sc_data[SC_GOSPEL].timer!=-1 && sd->sc_data[SC_GOSPEL].val4 != BCT_SELF && sd->inventory_data[n]->type == 0) ||
+ (pc_issit(sd) && (sd->itemid == 605 || sd->itemid == 606)) ||
+ //added item_noequip.txt items check by Maya&[Lupus]
+ (map[sd->bl.m].flag.pvp && (sd->inventory_data[n]->flag.no_equip&1) ) || // PVP
+ (map_flag_gvg(sd->bl.m) && (sd->inventory_data[n]->flag.no_equip>1) ) || // GVG
+ !pc_isUseitem(sd,n) ) {
+ clif_useitemack(sd,n,0,0);
+ return 1;
+ }
+ script = sd->inventory_data[n]->script;
+ amount = sd->status.inventory[n].amount;
+ //Check if the item is to be consumed inmediately [Skotlex]
+ if (sd->inventory_data[n]->flag.delay_consume)
+ clif_useitemack(sd,n,amount,1);
+ else {
+ clif_useitemack(sd,n,amount-1,1);
+
+ //Logs (C)onsumable items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "C", 0, sd->status.inventory[n].nameid, -1, &sd->status.inventory[n]);
+ }
+ //Logs
+
+ pc_delitem(sd,n,1,1);
+ }
+ if(sd->status.inventory[n].card[0]==0x00fe && pc_istop10fame(MakeDWord(sd->status.inventory[n].card[2],sd->status.inventory[n].card[3]), MAPID_ALCHEMIST)) {
+ potion_flag = 2; // Famous player's potions have 50% more efficiency
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_ROGUE)
+ potion_flag = 3; //Even more effective potions.
+ }
+ sd->canuseitem_tick= gettick() + battle_config.item_use_interval; //Update item use time.
+ run_script(script,0,sd->bl.id,0);
+ potion_flag = 0;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJ?ƒgƒAƒCƒeƒ€’ljÁBŒÂ?‚Ì‚Ýitem\‘¢?‚Ì?Žš‚𖳎‹
+ *------------------------------------------
+ */
+int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i,w;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ data = itemdb_search(item_data->nameid);
+
+ if(!itemdb_cancartstore(item_data->nameid, pc_isGM(sd)))
+ { //Check item trade restrictions [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ if((w=data->weight*amount) + sd->cart_weight > sd->cart_max_weight)
+ return 1;
+
+ i=MAX_CART;
+ if(!itemdb_isequip2(data)){
+ // ‘• ”õ•i‚Å‚Í‚È‚¢‚Ì‚ÅAŠùŠ—L•i‚È‚çŒÂ”‚̂ݕω»‚³‚¹‚é
+ 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){
+ // ‘• ”õ•i‚©–¢Š—L•i‚¾‚Á‚½‚Ì‚Å‹ó‚«—“‚֒ljÁ
+ 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;
+}
+
+/*==========================================
+ * ƒJ?ƒgƒAƒCƒeƒ€‚ðŒ¸‚ç‚·
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ƒJ?ƒg‚ÖƒAƒCƒeƒ€ˆÚ“®
+ *------------------------------------------
+ */
+int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) {
+ struct item *item_data;
+
+ nullpo_retr(0, sd);
+
+ if (idx < 0 || idx >= MAX_CART) //Invalid index check [Skotlex]
+ return 1;
+
+ item_data = &sd->status.inventory[idx];
+
+ if (item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->vender_id)
+ return 1;
+
+ if (pc_cart_additem(sd,item_data,amount) == 0)
+ return pc_delitem(sd,idx,amount,0);
+
+ return 1;
+}
+
+/*==========================================
+ * ƒJ?ƒg?‚̃AƒCƒeƒ€?Šm”F(ŒÂ?‚Ì·•ª‚ð•Ô‚·)
+ *------------------------------------------
+ */
+int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount)
+{
+ struct item *item_data;
+
+ nullpo_retr(-1, sd);
+ nullpo_retr(-1, item_data=&sd->status.cart[idx]);
+
+ if( item_data->nameid==0 || !item_data->amount)
+ return -1;
+ return item_data->amount-amount;
+}
+/*==========================================
+ * ƒJ?ƒg‚©‚çƒAƒCƒeƒ€ˆÚ“®
+ *------------------------------------------
+ */
+
+int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount)
+{
+ struct item *item_data;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ if (idx < 0 || idx >= MAX_CART) //Invalid index check [Skotlex]
+ return 1;
+
+ item_data=&sd->status.cart[idx];
+
+ if( item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->vender_id )
+ return 1;
+ if((flag = pc_additem(sd,item_data,amount)) == 0)
+ return pc_cart_delitem(sd,idx,amount,0);
+
+ clif_additem(sd,0,0,flag);
+ return 1;
+}
+
+/*==========================================
+ * ƒXƒeƒBƒ‹•iŒöŠJ
+ *------------------------------------------
+ */
+int pc_show_steal(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ int itemid;
+ int type;
+
+ struct item_data *item=NULL;
+ char output[100];
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, sd=va_arg(ap,struct map_session_data *));
+
+ itemid=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+ if(!type){
+ if((item=itemdb_exists(itemid))==NULL)
+ sprintf(output,"%s stole an Unknown Item.",sd->status.name);
+ else
+ sprintf(output,"%s stole %s.",sd->status.name,item->jname);
+ clif_displaymessage( ((struct map_session_data *)bl)->fd, output);
+ }else{
+ sprintf(output,"%s has not stolen the item because of being overweight.",sd->status.name);
+ clif_displaymessage( ((struct map_session_data *)bl)->fd, output);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** pc.c: Small Steal Item fix by fritz
+int pc_steal_item(struct map_session_data *sd,struct block_list *bl)
+{
+ int i,j,skill,itemid,flag;
+ struct mob_data *md;
+ struct item tmp_item;
+
+ if(!sd || !bl || bl->type != BL_MOB)
+ return 0;
+
+ md=(struct mob_data *)bl;
+
+ if(md->state.steal_flag || status_get_mode(bl)&MD_BOSS || md->master_id ||
+ (md->class_>=1324 && md->class_<1364) || // prevent stealing from treasure boxes [Valaris]
+ map[md->bl.m].flag.nomobloot || // check noloot map flag [Lorky]
+ md->sc_data[SC_STONE].timer != -1 || md->sc_data[SC_FREEZE].timer != -1 //status change check
+ )
+ return 0;
+
+ skill = battle_config.skill_steal_type == 1
+ ? (sd->paramc[4] - md->db->dex)/2 + pc_checkskill(sd,TF_STEAL)*6 + 10
+ : sd->paramc[4] - md->db->dex + pc_checkskill(sd,TF_STEAL)*3 + 10;
+
+ if (skill < 1)
+ return 0;
+
+ j = i = rand()%10; //Pick one mobs drop slot.
+ do {
+ //if it's empty, we check one by one, till find an item
+ i--;
+ if(i<0)
+ i=9; //9th slot
+ itemid = md->db->dropitem[i].nameid;
+ //now try all 10 slots till success
+ if(itemid <= 0 || (itemdb_type(itemid) == 6 && pc_checkskill(sd,TF_STEAL) <= 5))
+ continue;
+ } while (i != j &&
+ rand() % 10000 > ((md->db->dropitem[i].p * skill) / 100 + sd->add_steal_rate)); //fixed rate. From Freya [Lupus]
+
+ if (i == j)
+ return 0;
+
+ md->state.steal_flag = 1;
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = itemid;
+ tmp_item.amount = 1;
+ tmp_item.identify = !itemdb_isequip3(itemid);
+ flag = pc_additem(sd,&tmp_item,1);
+
+ if(battle_config.show_steal_in_same_party)
+ party_foreachsamemap(pc_show_steal,sd,1,sd,tmp_item.nameid,flag?1:0);
+ if(flag)
+ clif_additem(sd,0,0,flag);
+ else
+ { //Only invoke logs if item was successfully added (otherwise logs lie about actual item transaction)
+ //Logs items, Stolen from mobs [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick((struct map_session_data*)md, "M", md->class_, itemid, -1, NULL);
+ log_pick(sd, "P", 0, itemid, 1, NULL);
+ }
+
+ if(log_config.steal) { //this drop log contains ALL stolen items [Lupus]
+ int log_item[10]; //for stolen items logging Lupus
+ memset(&log_item,0,sizeof(log_item));
+ log_item[i] = itemid; //i == monster's drop slot
+ log_drop(sd, md->class_, log_item);
+ }
+
+ //A Rare Steal Global Announce by Lupus
+ if(md->db->dropitem[i].p<=battle_config.rare_drop_announce) {
+ struct item_data *i_data;
+ char message[128];
+ i_data = itemdb_exists(itemid);
+ sprintf (message, msg_txt(542), (sd->status.name != NULL)?sd->status.name :"GM", md->db->jname, i_data->jname, (float)md->db->dropitem[i].p/100);
+ //MSG: "'%s' stole %s's %s (chance: %%%0.02f)"
+ intif_GMmessage(message,strlen(message)+1,0);
+ }
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_steal_coin(struct map_session_data *sd,struct block_list *bl)
+{
+ if(sd != NULL && bl != NULL && bl->type == BL_MOB) {
+ int rate,skill;
+ struct mob_data *md=(struct mob_data *)bl;
+ if(md && !md->state.steal_coin_flag) {
+ if (md->sc_data && (md->sc_data[SC_STONE].timer != -1 || md->sc_data[SC_FREEZE].timer != -1))
+ return 0;
+ skill = pc_checkskill(sd,RG_STEALCOIN)*10;
+ rate = skill + (sd->status.base_level - md->db->lv)*3 + sd->paramc[4]*2 + sd->paramc[5]*2;
+ if(rand()%1000 < rate) {
+ pc_getzeny(sd,md->db->lv*10 + rand()%100);
+ md->state.steal_coin_flag = 1;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+//
+//
+//
+/*==========================================
+ * Set's a player position.
+ * Return values:
+ * 0 - Success.
+ * 1 - Invalid map index.
+ * 2 - Map not in this map-server, and failed to locate alternate map-server.
+ * 3 - Failed to warp player because it was in transition between maps.
+ *------------------------------------------
+ */
+int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,int clrtype)
+{
+ int m;
+
+ nullpo_retr(0, sd);
+
+ if (!mapindex || !mapindex_id2name(mapindex)) {
+ ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", mapindex);
+ return 1;
+ }
+ if(sd->state.auth && sd->bl.prev == NULL)
+ { //Should NOT move a character while it is not in a map (changing between maps, for example)
+ //state.auth helps identifies if this is the initial setpos rather than a normal map-change set pos.
+ if (battle_config.etc_log)
+ ShowInfo("pc_setpos failed: Attempted to relocate player %s (%d:%d) while it is still between maps.\n", sd->status.name, sd->status.account_id, sd->status.char_id);
+ return 3;
+ }
+ if(sd->chatID) // ƒ`ƒƒƒbƒg‚©‚ço‚é
+ chat_leavechat(sd);
+ if(sd->trade_partner) // Žæˆø‚ð’†?‚·‚é
+ trade_tradecancel(sd);
+ if(sd->state.storage_flag == 1)
+ storage_storage_quit(sd,0); // ‘qŒÉ‚ðŠJ‚¢‚Ä‚é‚È‚ç•Û‘¶‚·‚é
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,0);
+
+ if(sd->party_invite>0) // ƒp?ƒeƒB?—U‚ð‹‘”Û‚·‚é
+ party_reply_invite(sd,sd->party_invite_account,0);
+ if(sd->guild_invite>0) // ƒMƒ‹ƒh?—U‚ð‹‘”Û‚·‚é
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0) // ƒMƒ‹ƒh“¯–¿?—U‚ð‹‘”Û‚·‚é
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+
+ // Delete timer before the player moved to hise repawn point
+ if (sd->pvp_timer != -1 && !battle_config.pk_mode) {
+ delete_timer(sd->pvp_timer, pc_calc_pvprank_timer);
+ sd->pvp_timer = -1;
+ }
+
+ skill_castcancel(&sd->bl,0); // ‰r¥’†?
+ pc_stop_walking(sd,0); // ?s’†?
+ pc_stopattack(sd); // U?’†?
+
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_gangsterparadise(sd,0);
+ }
+
+ m=map_mapindex2mapid(mapindex);
+
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl, SC_TRICKDEAD, -1);
+ if (sd->sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end(&sd->bl,SC_BLADESTOP,-1);
+ if (sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if (sd->sc_data[SC_DANCING].timer!=-1) // clear dance effect when warping [Valaris]
+ skill_stop_dancing(&sd->bl);
+ if (sd->sc_data[SC_DEVOTION].timer!=-1)
+ status_change_end(&sd->bl,SC_DEVOTION,-1);
+ if (sd->sc_data[SC_CLOSECONFINE].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOSECONFINE,-1);
+ if (sd->sc_data[SC_CLOSECONFINE2].timer!=-1)
+ status_change_end(&sd->bl,SC_CLOSECONFINE2,-1);
+ if (sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if (sd->bl.m != m) { //Cancel some map related stuff.
+ if (sd->sc_data[SC_WARM].timer != -1)
+ status_change_end(&sd->bl,SC_WARM,-1);
+ if (sd->sc_data[SC_SUN_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_SUN_COMFORT,-1);
+ if (sd->sc_data[SC_MOON_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_MOON_COMFORT,-1);
+ if (sd->sc_data[SC_STAR_COMFORT].timer != -1)
+ status_change_end(&sd->bl,SC_STAR_COMFORT,-1);
+ }
+ }
+
+ if(sd->status.option&OPTION_HIDE)
+ status_change_end(&sd->bl, SC_HIDING, -1);
+ if(pc_iscloaking(sd))
+ status_change_end(&sd->bl, SC_CLOAKING, -1);
+ if(pc_ischasewalk(sd))
+ status_change_end(&sd->bl, SC_CHASEWALK, -1);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ }
+
+ if(m<0){
+ if(sd->mapindex){
+ int ip,port;
+ if(map_mapname2ipport(mapindex,&ip,&port)==0){
+ skill_stop_dancing(&sd->bl);
+ skill_unit_move(&sd->bl,gettick(),4);
+ clif_clearchar_area(&sd->bl,clrtype&0xffff);
+ skill_gangsterparadise(sd,0);
+ map_delblock(&sd->bl);
+ if(sd->status.pet_id > 0 && sd->pd) {
+ if(sd->pd->bl.m != m && sd->pet.intimate <= 0) {
+ pet_remove_map(sd);
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ if(battle_config.pet_status_support)
+ status_calc_pc(sd,2);
+ }
+ else if(sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ clif_clearchar_area(&sd->pd->bl,clrtype&0xffff);
+ map_delblock(&sd->pd->bl);
+ }
+ }
+ sd->mapindex = mapindex;
+ sd->bl.x=x;
+ sd->bl.y=y;
+ sd->state.waitingdisconnect=1;
+ pc_clean_skilltree(sd);
+
+ if(sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ //The storage close routines save the char data. [Skotlex]
+ if (!sd->state.storage_flag)
+ chrif_save(sd,1);
+ else if (sd->state.storage_flag == 1) {
+ storage_storage_quit(sd,1);
+ } else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+
+ chrif_changemapserver(sd, mapindex, x, y, ip, (short)port);
+ return 0;
+ }
+ }
+ return 2;
+ }
+
+ if(x <0 || x >= map[m].xs || y <0 || y >= map[m].ys)
+ x=y=0;
+ if((x==0 && y==0) ||
+ (map_getcell(m,x,y,CELL_CHKNOPASS) && !map_getcell(m, x, y, CELL_CHKICEWALL))
+ ){ //We allow placing players on top of an ICEWALL tile to prevent force-warping players when an ice wall is placed
+ //at spawn points from warps and the like. [Skotlex]
+ if(x||y) {
+ if(battle_config.error_log)
+ ShowError("pc_setpos: attempt to place player on non-walkable tile (%s-%d,%d)\n",mapindex_id2name(mapindex),x,y);
+ }
+ do {
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ } while(map_getcell(m,x,y,CELL_CHKNOPASS));
+ }
+
+ if(sd->mapindex && sd->bl.prev != NULL){
+ skill_unit_move(&sd->bl,gettick(),4);
+ clif_clearchar_area(&sd->bl,clrtype&0xffff);
+ skill_gangsterparadise(sd,0);
+
+ map_delblock(&sd->bl);
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd) {
+ if(sd->pd->bl.m != m && sd->pet.intimate <= 0) {
+ pet_remove_map(sd);
+ intif_delete_petdata(sd->status.pet_id);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+ sd->petDB = NULL;
+ if(battle_config.pet_status_support)
+ status_calc_pc(sd,2);
+ pc_clean_skilltree(sd);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+ }
+ else if(sd->pet.intimate > 0) {
+ pet_stopattack(sd->pd);
+ pet_changestate(sd->pd,MS_IDLE,0);
+ clif_clearchar_area(&sd->pd->bl,clrtype&0xffff);
+ map_delblock(&sd->pd->bl);
+ }
+ }
+ clif_changemap(sd,map[m].index,x,y); // [MouseJstr]
+ }
+
+ if (sd->mapindex != mapindex) //minimap dot fix [Kevin]
+ {
+ party_send_dot_remove(sd);
+ guild_send_dot_remove(sd);
+ }
+
+ sd->mapindex = mapindex;
+ sd->bl.m = m;
+ sd->to_x = x;
+ sd->to_y = y;
+
+ // moved and changed dance effect stopping
+
+ sd->bl.x = x;
+ sd->bl.y = y;
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ sd->pd->bl.m = m;
+ sd->pd->bl.x = sd->pd->to_x = x;
+ sd->pd->bl.y = sd->pd->to_y = y;
+ sd->pd->dir = sd->dir;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * PC‚̃‰ƒ“ƒ_ƒ€ƒ?ƒv
+ *------------------------------------------
+ */
+int pc_randomwarp(struct map_session_data *sd, int type) {
+ int x,y,i=0;
+ int m;
+
+ nullpo_retr(0, sd);
+
+ m=sd->bl.m;
+
+ if (map[sd->bl.m].flag.noteleport) // ƒeƒŒƒ|?ƒg‹ÖŽ~
+ return 0;
+
+ do{
+ x=rand()%(map[m].xs-2)+1;
+ y=rand()%(map[m].ys-2)+1;
+ }while(map_getcell(m,x,y,CELL_CHKNOPASS) && (i++)<1000 );
+
+ if (i < 1000)
+ pc_setpos(sd,map[sd->bl.m].index,x,y,type);
+
+ return 0;
+}
+
+/*==========================================
+ * Œ»ÝˆÊ’u‚̃ƒ‚
+ *------------------------------------------
+ */
+int pc_memo(struct map_session_data *sd, int i) {
+ int skill;
+ int j;
+
+ nullpo_retr(0, sd);
+
+ skill = pc_checkskill(sd, AL_WARP);
+
+ if (i >= MIN_PORTAL_MEMO)
+ i -= MIN_PORTAL_MEMO;
+ else if (map[sd->bl.m].flag.nomemo || (map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd))) {
+ clif_skill_teleportmessage(sd, 1);
+ return 0;
+ }
+
+ if (skill < 1) {
+ clif_skill_memo(sd,2);
+ }
+
+ if (skill < 2 || i < -1 || i > 2) {
+ clif_skill_memo(sd, 1);
+ return 0;
+ }
+
+ for(j = 0 ; j < 3; j++) {
+ if (sd->status.memo_point[j].map == map[sd->bl.m].index) {
+ i = j;
+ break;
+ }
+ }
+
+ if (i == -1) {
+ for(i = skill - 3; i >= 0; i--) {
+ memcpy(&sd->status.memo_point[i+1],&sd->status.memo_point[i],
+ sizeof(struct point));
+ }
+ i = 0;
+ }
+ sd->status.memo_point[i].map = map[sd->bl.m].index;
+ sd->status.memo_point[i].x = sd->bl.x;
+ sd->status.memo_point[i].y = sd->bl.y;
+
+ clif_skill_memo(sd, 0);
+
+ return 1;
+}
+
+/*==========================================
+ * pc‹ì‚¯‘«—v‹
+ *------------------------------------------
+ */
+int pc_run(struct map_session_data *sd, int skilllv, int dir)
+{
+ int i,to_x,to_y,dir_x,dir_y;
+
+ nullpo_retr(0, sd);
+
+ if (!pc_can_move(sd)) {
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ return 0;
+ }
+
+ to_x = sd->bl.x;
+ to_y = sd->bl.y;
+ dir_x = dirx[dir];
+ dir_y = diry[dir];
+
+ for(i=0;i<AREA_SIZE;i++)
+ {
+ if(!map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
+ break;
+
+ to_x += dir_x;
+ to_y += dir_y;
+ }
+
+ //i‚ß‚È‚¢ê‡@‹ì‚¯‘«I—¹@áŠQ•¨‚ÅŽ~‚Ü‚Á‚½ê‡ƒXƒp[ƒgó‘Ô‰ðœ
+ if(to_x == sd->bl.x && to_y == sd->bl.y){
+ if(sd->sc_data[SC_RUN].timer!=-1)
+ status_change_end(&sd->bl,SC_RUN,-1);
+ } else
+ pc_walktoxy(sd, to_x, to_y);
+
+ return 1;
+}
+/*==========================================
+ * PC‚ÌŒü‹‚Ä‚¢‚é‚Ù‚¤‚Éstep•ª•à‚­
+ *------------------------------------------
+ */
+int pc_walktodir(struct map_session_data *sd,int step)
+{
+ int i,to_x,to_y,dir_x,dir_y;
+
+ nullpo_retr(0, sd);
+
+ to_x = sd->bl.x;
+ to_y = sd->bl.y;
+ dir_x = dirx[(int)sd->dir];
+ dir_y = diry[(int)sd->dir];
+
+ for(i=0;i<step;i++)
+ {
+ if(map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKNOPASS))
+ break;
+
+ if(map_getcell(sd->bl.m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS))
+ {
+ to_x += dir_x;
+ to_y += dir_y;
+ continue;
+ }
+ break;
+ }
+ pc_walktoxy(sd, sd->to_x, sd->to_y);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_can_reach(struct map_session_data *sd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, sd);
+
+ if( sd->bl.x==x && sd->bl.y==y ) // “¯‚¶ƒ}ƒX
+ return 1;
+
+ // áŠQ•¨”»’è
+ 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;
+}
+
+//
+// ? s•¨
+//
+/*==========================================
+ * ŽŸ‚Ì1?‚É‚©‚©‚éŽjÔ‚ðŒvŽZ
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ”¼?i‚Þ(timer??)
+ *------------------------------------------
+ */
+static int pc_walk(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ int i, x, y, dx, dy;
+
+ if ((sd = map_id2sd(id)) == NULL)
+ return 0;
+
+ if(sd->walktimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_walk %d != %d\n", sd->walktimer, tid);
+ return 0;
+ }
+
+ sd->walktimer = -1;
+
+ if (sd->walkpath.path_pos >= sd->walkpath.path_len ||
+ sd->walkpath.path_pos != data)
+ return 0;
+
+ //?‚¢‚½‚Ì‚Å‘§‚̃^ƒCƒ}?‚ð‰Šú‰»
+ sd->inchealspirithptick = 0;
+ sd->inchealspiritsptick = 0;
+
+ sd->walkpath.path_half ^= 1;
+ if (sd->walkpath.path_half == 0) { // ƒ}ƒX–Ú’†S‚Ö“r
+ sd->walkpath.path_pos++;
+ if (sd->state.change_walk_target) {
+ pc_walktoxy_sub(sd);
+ return 0;
+ }
+ } else { // ƒ}ƒX–Ú‹«ŠE‚Ö“r
+ if (sd->walkpath.path[sd->walkpath.path_pos] >= 8)
+ return 1;
+ x = sd->bl.x;
+ y = sd->bl.y;
+#ifndef CELL_NOSTACK
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKNOPASS)) {
+ pc_stop_walking(sd,1);
+ return 0;
+ }
+#endif
+ sd->dir = sd->head_dir = sd->walkpath.path[sd->walkpath.path_pos];
+ dx = dirx[(int)sd->dir];
+ dy = diry[(int)sd->dir];
+ if (map_getcell(sd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)) {
+ pc_walktoxy_sub(sd);
+ return 0;
+ }
+ sd->walktimer = 1; // temporarily set (so that in clif_set007x the player will still appear as walking)
+ map_foreachinmovearea(clif_pcoutsight, sd->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ dx, dy, BL_ALL, sd);
+
+ sd->walktimer = -1; // set back so not to disturb future pc_stop_walking calls
+ x += dx;
+ y += dy;
+ map_moveblock(&sd->bl, x, y, tick);
+ sd->walktimer = 1; // temporarily set (so that in clif_set007x the player will still appear as walking)
+
+ map_foreachinmovearea (clif_pcinsight, sd->bl.m,
+ x-AREA_SIZE, y-AREA_SIZE, x+AREA_SIZE, y+AREA_SIZE,
+ -dx, -dy, BL_ALL, sd);
+ sd->walktimer = -1; // set back so not to disturb future pc_stop_walking calls
+
+ if (pc_iscloaking(sd)) // ƒNƒ?ƒLƒ“ƒO‚ÌÁ–Å?¸
+ skill_check_cloaking(&sd->bl);
+ /* ”íƒfƒBƒ{?ƒVƒ‡ƒ“?¸ */
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_DANCING].timer != -1)
+ skill_unit_move_unit_group((struct skill_unit_group *)sd->sc_data[SC_DANCING].val2, sd->bl.m, dx, dy);
+ }
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,x,y);
+ else
+ sd->areanpc_id = 0;
+ }
+
+ if ((i = calc_next_walk_step(sd)) > 0) {
+ i = i>>1;
+ if (i < 1 && sd->walkpath.path_half == 0)
+ i = 1;
+ sd->walktimer = add_timer (tick+i, pc_walk, id, sd->walkpath.path_pos);
+ }
+ else if(sd->sc_data[SC_RUN].timer!=-1) //Keep trying to run.
+ pc_run(sd, sd->sc_data[SC_RUN].val1, sd->sc_data[SC_RUN].val2);
+ else { //Stopped walking. Update to_x and to_y to current location [Skotlex]
+ sd->to_x = sd->bl.x;
+ sd->to_y = sd->bl.y;
+ }
+ return 0;
+}
+
+/*==========================================
+ * ˆÚ“®‰Â”\‚©Šm”F‚µ‚ÄA‰Â”\‚È‚ç?sŠJŽn
+ *------------------------------------------
+ */
+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? s—v‹
+ *------------------------------------------
+ */
+int pc_walktoxy (struct map_session_data *sd, int x, int y)
+{
+ nullpo_retr(0, sd);
+
+ sd->to_x = x;
+ sd->to_y = y;
+ if (sd->sc_data[SC_CONFUSION].timer != -1) //Randomize the target position
+ map_random_dir(&sd->bl, &sd->to_x, &sd->to_y);
+
+ if (sd->walktimer != -1)
+ { //There was a timer-mismatch here. pc_walktoxy_sub does not clears previous pc_walk timers! [Skotlex]
+ sd->state.change_walk_target = 1;
+ } else {
+ pc_walktoxy_sub(sd);
+ }
+
+ if (sd->state.gmaster_flag) {
+ struct guild *g = sd->state.gmaster_flag;
+ int skill, guildflag = 0;
+ if ((skill = guild_checkskill(g, GD_LEADERSHIP)) > 0) guildflag |= skill<<12;
+ if ((skill = guild_checkskill(g, GD_GLORYWOUNDS)) > 0) guildflag |= skill<<8;
+ if ((skill = guild_checkskill(g, GD_SOULCOLD)) > 0) guildflag |= skill<<4;
+ if ((skill = guild_checkskill(g, GD_HAWKEYES)) > 0) guildflag |= skill;
+ if (guildflag)
+ map_foreachinarea (skill_guildaura_sub, sd->bl.m,
+ sd->bl.x-2, sd->bl.y-2, sd->bl.x+2, sd->bl.y+2, BL_PC,
+ sd->bl.id, sd->status.guild_id, &guildflag);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ? s’âŽ~
+ *------------------------------------------
+ */
+int pc_stop_walking (struct map_session_data *sd, int type)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->walktimer != -1) {
+ if(type&2 && pc_can_move(sd)){
+ int dx, dy;
+ dx=sd->to_x-sd->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=sd->to_y-sd->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ if(dx!=0 || dy!=0){
+ sd->to_x=sd->bl.x+dx;
+ sd->to_y=sd->bl.y+dy;
+ sd->state.change_walk_target = 1;
+ return 0;
+ }
+ }
+ delete_timer(sd->walktimer, pc_walk);
+ sd->walktimer = -1;
+ }
+ sd->walkpath.path_len = 0;
+ sd->to_x = sd->bl.x;
+ sd->to_y = sd->bl.y;
+ if (type & 0x01)
+ clif_fixpos(&sd->bl);
+ if (sd->sc_data[SC_RUN].timer != -1)
+ status_change_end(&sd->bl, SC_RUN, -1);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int pc_movepos(struct map_session_data *sd,int dst_x,int dst_y,int checkpath)
+{
+ int dx,dy;
+
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, sd);
+
+ if(checkpath && path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,dst_x,dst_y,0))
+ return 1;
+
+ sd->dir = sd->head_dir = map_calc_dir(&sd->bl, dst_x,dst_y);
+
+ dx = dst_x - sd->bl.x;
+ dy = dst_y - sd->bl.y;
+
+ map_foreachinmovearea(clif_pcoutsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,dx,dy,BL_ALL,sd);
+
+ map_moveblock(&sd->bl, dst_x, dst_y, gettick());
+
+ map_foreachinmovearea(clif_pcinsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,-dx,-dy,BL_ALL,sd);
+
+ if (pc_iscloaking(sd)) // ƒNƒ?ƒLƒ“ƒO‚ÌÁ–Å?¸
+ skill_check_cloaking(&sd->bl);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) {
+ struct pet_data *pd = sd->pd;
+ int flag = 0;
+
+ //Check if pet needs to be teleported. [Skotlex]
+ if (!checkpath && path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,dst_x,dst_y,0))
+ flag = 1;
+ else if (!check_distance_bl(&sd->bl, &pd->bl, AREA_SIZE)) //Too far, teleport.
+ flag = 2;
+ if (flag) {
+ pet_stopattack(pd);
+ pet_changestate(pd,MS_IDLE,0);
+ if (flag == 2) clif_clearchar_area(&pd->bl,3);
+ map_moveblock(&pd->bl, dst_x, dst_y, gettick());
+ pd->dir = sd->dir;
+ pd->to_x = dst_x;
+ pd->to_y = dst_y;
+ if (flag == 2) clif_fixpos(&pd->bl);
+ else clif_slide(&pd->bl,pd->bl.x,pd->bl.y);
+ }
+ }
+ if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
+ else
+ sd->areanpc_id=0;
+ return 0;
+}
+
+//
+// •Ší??
+//
+/*==========================================
+ * ƒXƒLƒ‹‚Ì?õ Š—L‚µ‚Ä‚¢‚½ê‡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;
+}
+
+/*==========================================
+ * •Ší?X‚É‚æ‚éƒXƒLƒ‹‚Ì??ƒ`ƒFƒbƒN
+ * ˆø?F
+ * struct map_session_data *sd ƒZƒbƒVƒ‡ƒ“ƒf?ƒ^
+ * int nameid ?”õ•iID
+ * •Ô‚è’lF
+ * 0 ?X‚È‚µ
+ * -1 ƒXƒLƒ‹‚ð‰ðœ
+ *------------------------------------------
+ */
+int pc_checkallowskill(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(!sd->sc_count)
+ return 0;
+
+ // Skills requiring specific weapon types
+ if(sd->sc_data[SC_TWOHANDQUICKEN].timer!=-1 && !(skill_get_weapontype(KN_TWOHANDQUICKEN)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_TWOHANDQUICKEN,-1);
+ if(sd->sc_data[SC_ONEHAND].timer!=-1 && !(skill_get_weapontype(KN_ONEHAND)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ONEHAND,-1);
+ if(sd->sc_data[SC_AURABLADE].timer!=-1 && !(skill_get_weapontype(LK_AURABLADE)&(1<<sd->status.weapon)))
+ // Aura Blade requires any weapon but bare fists
+ status_change_end(&sd->bl,SC_AURABLADE,-1);
+ if(sd->sc_data[SC_PARRYING].timer!=-1 && !(skill_get_weapontype(LK_PARRYING)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_PARRYING,-1);
+ if(sd->sc_data[SC_SPEARSQUICKEN].timer!=-1 && !(skill_get_weapontype(CR_SPEARQUICKEN)&(1<<sd->status.weapon)))
+ // Spear Quicken requires a Two-handed spear
+ status_change_end(&sd->bl,SC_SPEARSQUICKEN,-1);
+ if(sd->sc_data[SC_ADRENALINE].timer!=-1 && !(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ADRENALINE,-1);
+ if(sd->sc_data[SC_ADRENALINE2].timer!=-1 && !(skill_get_weapontype(BS_ADRENALINE2)&(1<<sd->status.weapon)))
+ status_change_end(&sd->bl,SC_ADRENALINE2,-1);
+ if( sd->sc_data[SC_SPURT].timer!=-1 && sd->status.weapon)
+ // Spurt requires bare hands (feet, in fact xD)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+
+ if(sd->status.shield <= 0) { // Skills requiring a shield
+ if(sd->sc_data[SC_AUTOGUARD].timer!=-1) // Guard
+ status_change_end(&sd->bl,SC_AUTOGUARD,-1);
+ if(sd->sc_data[SC_DEFENDER].timer!=-1) // Defending Aura
+ status_change_end(&sd->bl,SC_DEFENDER,-1);
+ if(sd->sc_data[SC_REFLECTSHIELD].timer!=-1) // Shield Reflect
+ status_change_end(&sd->bl,SC_REFLECTSHIELD,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ? ”õ•i‚̃`ƒFƒbƒN
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ?¶E‚â—{ŽqE‚ÌŒ³‚ÌE‹Æ‚ð•Ô‚·
+ *------------------------------------------
+ */
+struct pc_base_job pc_calc_base_job(int b_class)
+{
+ struct pc_base_job bj;
+ if(b_class < JOB_NOVICE_HIGH){
+ if (b_class == JOB_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_CRUSADER2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class;
+ bj.upper = 0;
+ }else if(b_class >= JOB_NOVICE_HIGH && b_class <= JOB_PALADIN2){ //High Jobs
+ if (b_class == JOB_LORD_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_PALADIN2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class - JOB_NOVICE_HIGH;
+ bj.upper = 1;
+ }else if(b_class >= JOB_TAEKWON && b_class <= JOB_SOUL_LINKER){
+ if (b_class == JOB_STAR_GLADIATOR2)
+ bj.job = 24 + JOB_STAR_GLADIATOR - JOB_TAEKWON;
+ else
+ bj.job = 24 + b_class - JOB_TAEKWON;
+ bj.upper = 0;
+ }else{ //Baby Classes
+ if (b_class == JOB_SUPER_BABY)
+ bj.job = JOB_SUPER_NOVICE;
+ else if (b_class == JOB_BABY_KNIGHT2)
+ bj.job = JOB_KNIGHT;
+ else if (b_class == JOB_BABY_CRUSADER2)
+ bj.job = JOB_CRUSADER;
+ else
+ bj.job = b_class - JOB_BABY;
+ bj.upper = 2;
+ }
+
+ if(bj.job == JOB_NOVICE){
+ bj.type = 0;
+ }else if(bj.job <= JOB_THIEF || bj.job == JOB_TAEKWON){
+ bj.type = 1;
+ }else{
+ bj.type = 2;
+ }
+
+ return bj;
+}
+
+/*==========================================
+ * For quick calculating [Celest]
+ *------------------------------------------
+ */
+int pc_calc_base_job2 (int b_class)
+{
+ if(b_class < JOB_NOVICE_HIGH)
+ {
+ if (b_class == JOB_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_CRUSADER2)
+ return JOB_CRUSADER;
+ return b_class;
+ }
+ if(b_class >= JOB_NOVICE_HIGH && b_class < JOB_BABY)
+ {
+ if (b_class == JOB_LORD_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_PALADIN2)
+ return JOB_CRUSADER;
+ return b_class - JOB_NOVICE_HIGH;
+ }
+ if(b_class >= JOB_TAEKWON && b_class <= JOB_SOUL_LINKER )
+ {
+ if (b_class == JOB_STAR_GLADIATOR2)
+ return 24 + JOB_STAR_GLADIATOR - JOB_TAEKWON;
+ return 24 + b_class - JOB_TAEKWON;
+ }
+ //Baby Classes
+ {
+ if (b_class == JOB_SUPER_BABY)
+ return JOB_SUPER_NOVICE;
+ if (b_class == JOB_BABY_KNIGHT2)
+ return JOB_KNIGHT;
+ if (b_class == JOB_BABY_CRUSADER2)
+ return JOB_CRUSADER;
+ return b_class - JOB_BABY;
+ }
+}
+
+/*==========================================
+ * Convert's from the client's lame Job ID system
+ * to the map server's 'makes sense' system. [Skotlex]
+ *------------------------------------------
+ */
+unsigned short pc_jobid2mapid(unsigned short b_class)
+{
+ int class_ = 0;
+ if (b_class >= JOB_BABY && b_class <= JOB_SUPER_BABY)
+ {
+ if (b_class == JOB_SUPER_BABY)
+ b_class = JOB_SUPER_NOVICE;
+ else
+ b_class -= JOB_BABY;
+ class_|= JOBL_BABY;
+ }
+ else if (b_class >= JOB_NOVICE_HIGH && b_class <= JOB_PALADIN2)
+ {
+ b_class -= JOB_NOVICE_HIGH;
+ class_|= JOBL_UPPER;
+ }
+ if (b_class >= JOB_KNIGHT && b_class <= JOB_KNIGHT2)
+ class_|= JOBL_2_1;
+ else if (b_class >= JOB_CRUSADER && b_class <= JOB_CRUSADER2)
+ class_|= JOBL_2_2;
+ switch (b_class)
+ {
+ case JOB_NOVICE:
+ case JOB_SWORDMAN:
+ case JOB_MAGE:
+ case JOB_ARCHER:
+ case JOB_ACOLYTE:
+ case JOB_MERCHANT:
+ case JOB_THIEF:
+ class_ |= b_class;
+ break;
+ case JOB_KNIGHT:
+ case JOB_KNIGHT2:
+ case JOB_CRUSADER:
+ case JOB_CRUSADER2:
+ class_ |= MAPID_SWORDMAN;
+ break;
+ case JOB_PRIEST:
+ case JOB_MONK:
+ class_ |= MAPID_ACOLYTE;
+ break;
+ case JOB_WIZARD:
+ case JOB_SAGE:
+ class_ |= MAPID_MAGE;
+ break;
+ case JOB_BLACKSMITH:
+ case JOB_ALCHEMIST:
+ class_ |= MAPID_MERCHANT;
+ break;
+ case JOB_HUNTER:
+ case JOB_BARD:
+ case JOB_DANCER:
+ class_ |= MAPID_ARCHER;
+ break;
+ case JOB_ASSASSIN:
+ case JOB_ROGUE:
+ class_ |= MAPID_THIEF;
+ break;
+
+ case JOB_STAR_GLADIATOR:
+ case JOB_STAR_GLADIATOR2:
+ class_ |= JOBL_2_1;
+ class_ |= MAPID_TAEKWON;
+ break;
+ case JOB_SOUL_LINKER:
+ class_ |= JOBL_2_2;
+ case JOB_TAEKWON:
+ class_ |= MAPID_TAEKWON;
+ break;
+ case JOB_WEDDING:
+ class_ = MAPID_WEDDING;
+ break;
+ case JOB_SUPER_NOVICE: //Super Novices are considered 2-1 novices. [Skotlex]
+ class_ |= JOBL_2_1;
+ break;
+ case JOB_XMAS:
+ class_ = MAPID_XMAS;
+ break;
+ default:
+ ShowError("pc_jobid2mapid: Unrecognized job %d!\n", b_class);
+ return 0;
+ }
+ return class_;
+}
+
+//Reverts the map-style class id to the client-style one.
+unsigned short pc_mapid2jobid(unsigned short class_, int sex) {
+ switch(class_) {
+ case MAPID_NOVICE:
+ return JOB_NOVICE;
+ case MAPID_SWORDMAN:
+ return JOB_SWORDMAN;
+ case MAPID_MAGE:
+ return JOB_MAGE;
+ case MAPID_ARCHER:
+ return JOB_ARCHER;
+ case MAPID_ACOLYTE:
+ return JOB_ACOLYTE;
+ case MAPID_MERCHANT:
+ return JOB_MERCHANT;
+ case MAPID_THIEF:
+ return JOB_THIEF;
+ case MAPID_TAEKWON:
+ return JOB_TAEKWON;
+ case MAPID_WEDDING:
+ return JOB_WEDDING;
+ case MAPID_XMAS: // [Valaris]
+ return JOB_XMAS;
+ //2_1 classes
+ case MAPID_SUPER_NOVICE:
+ return JOB_SUPER_NOVICE;
+ case MAPID_KNIGHT:
+ return JOB_KNIGHT;
+ case MAPID_WIZARD:
+ return JOB_WIZARD;
+ case MAPID_HUNTER:
+ return JOB_HUNTER;
+ case MAPID_PRIEST:
+ return JOB_PRIEST;
+ case MAPID_BLACKSMITH:
+ return JOB_BLACKSMITH;
+ case MAPID_ASSASSIN:
+ return JOB_ASSASSIN;
+ case MAPID_STAR_GLADIATOR:
+ return JOB_STAR_GLADIATOR;
+ //2_2 classes
+ case MAPID_CRUSADER:
+ return JOB_CRUSADER;
+ case MAPID_SAGE:
+ return JOB_SAGE;
+ case MAPID_BARDDANCER:
+ return sex?JOB_BARD:JOB_DANCER;
+ case MAPID_MONK:
+ return JOB_MONK;
+ case MAPID_ALCHEMIST:
+ return JOB_ALCHEMIST;
+ case MAPID_ROGUE:
+ return JOB_ROGUE;
+ case MAPID_SOUL_LINKER:
+ return JOB_SOUL_LINKER;
+ //1-1: advanced
+ case MAPID_NOVICE_HIGH:
+ return JOB_NOVICE_HIGH;
+ case MAPID_SWORDMAN_HIGH:
+ return JOB_SWORDMAN_HIGH;
+ case MAPID_MAGE_HIGH:
+ return JOB_MAGE_HIGH;
+ case MAPID_ARCHER_HIGH:
+ return JOB_ARCHER_HIGH;
+ case MAPID_ACOLYTE_HIGH:
+ return JOB_ACOLYTE_HIGH;
+ case MAPID_MERCHANT_HIGH:
+ return JOB_MERCHANT_HIGH;
+ case MAPID_THIEF_HIGH:
+ return JOB_THIEF_HIGH;
+ //2_1 advanced
+ case MAPID_LORD_KNIGHT:
+ return JOB_LORD_KNIGHT;
+ case MAPID_HIGH_WIZARD:
+ return JOB_HIGH_WIZARD;
+ case MAPID_SNIPER:
+ return JOB_SNIPER;
+ case MAPID_HIGH_PRIEST:
+ return JOB_HIGH_PRIEST;
+ case MAPID_WHITESMITH:
+ return JOB_WHITESMITH;
+ case MAPID_ASSASSIN_CROSS:
+ return JOB_ASSASSIN_CROSS;
+ //2_2 advanced
+ case MAPID_PALADIN:
+ return JOB_PALADIN;
+ case MAPID_PROFESSOR:
+ return JOB_PROFESSOR;
+ case MAPID_CLOWNGYPSY:
+ return sex?JOB_CLOWN:JOB_GYPSY;
+ case MAPID_CHAMPION:
+ return JOB_CHAMPION;
+ case MAPID_CREATOR:
+ return JOB_CREATOR;
+ case MAPID_STALKER:
+ return JOB_STALKER;
+ //1-1 baby
+ case MAPID_BABY:
+ return JOB_BABY;
+ case MAPID_BABY_SWORDMAN:
+ return JOB_BABY_SWORDMAN;
+ case MAPID_BABY_MAGE:
+ return JOB_BABY_MAGE;
+ case MAPID_BABY_ARCHER:
+ return JOB_BABY_ARCHER;
+ case MAPID_BABY_ACOLYTE:
+ return JOB_BABY_ACOLYTE;
+ case MAPID_BABY_MERCHANT:
+ return JOB_BABY_MERCHANT;
+ case MAPID_BABY_THIEF:
+ return JOB_BABY_THIEF;
+ //2_1 baby
+ case MAPID_SUPER_BABY:
+ return JOB_SUPER_BABY;
+ case MAPID_BABY_KNIGHT:
+ return JOB_BABY_KNIGHT;
+ case MAPID_BABY_WIZARD:
+ return JOB_BABY_WIZARD;
+ case MAPID_BABY_HUNTER:
+ return JOB_BABY_HUNTER;
+ case MAPID_BABY_PRIEST:
+ return JOB_BABY_PRIEST;
+ case MAPID_BABY_BLACKSMITH:
+ return JOB_BABY_BLACKSMITH;
+ case MAPID_BABY_ASSASSIN:
+ return JOB_BABY_ASSASSIN;
+ //2_2 baby
+ case MAPID_BABY_CRUSADER:
+ return JOB_BABY_CRUSADER;
+ case MAPID_BABY_SAGE:
+ return JOB_BABY_SAGE;
+ case MAPID_BABY_BARDDANCER:
+ return sex?JOB_BABY_BARD:JOB_BABY_DANCER;
+ case MAPID_BABY_MONK:
+ return JOB_BABY_MONK;
+ case MAPID_BABY_ALCHEMIST:
+ return JOB_BABY_ALCHEMIST;
+ case MAPID_BABY_ROGUE:
+ return JOB_BABY_ROGUE;
+ default:
+ ShowError("pc_mapid2jobid: Unrecognized job %d!\n", class_);
+ return 0;
+ }
+}
+
+/*====================================================
+ * This function return the name of the job (by [Yor])
+ *----------------------------------------------------
+ */
+char * job_name(int class_) {
+ switch (class_) {
+ case JOB_NOVICE:
+ case JOB_SWORDMAN:
+ case JOB_MAGE:
+ case JOB_ARCHER:
+ case JOB_ACOLYTE:
+ case JOB_MERCHANT:
+ case JOB_THIEF:
+ return msg_txt(550 - JOB_NOVICE+class_);
+
+ case JOB_KNIGHT:
+ case JOB_PRIEST:
+ case JOB_WIZARD:
+ case JOB_BLACKSMITH:
+ case JOB_HUNTER:
+ case JOB_ASSASSIN:
+ return msg_txt(557 - JOB_KNIGHT+class_);
+
+ case JOB_KNIGHT2:
+ return msg_txt(557);
+
+ case JOB_CRUSADER:
+ case JOB_MONK:
+ case JOB_SAGE:
+ case JOB_ROGUE:
+ case JOB_ALCHEMIST:
+ case JOB_BARD:
+ case JOB_DANCER:
+ return msg_txt(563 - JOB_CRUSADER+class_);
+
+ case JOB_CRUSADER2:
+ return msg_txt(563);
+
+ case JOB_WEDDING:
+ case JOB_SUPER_NOVICE:
+ case JOB_GUNSLINGER:
+ case JOB_NINJA:
+ case JOB_XMAS:
+ return msg_txt(570 - JOB_WEDDING+class_);
+
+ case JOB_NOVICE_HIGH:
+ case JOB_SWORDMAN_HIGH:
+ case JOB_MAGE_HIGH:
+ case JOB_ARCHER_HIGH:
+ case JOB_ACOLYTE_HIGH:
+ case JOB_MERCHANT_HIGH:
+ case JOB_THIEF_HIGH:
+ return msg_txt(575 - JOB_NOVICE_HIGH+class_);
+
+ case JOB_LORD_KNIGHT:
+ case JOB_HIGH_PRIEST:
+ case JOB_HIGH_WIZARD:
+ case JOB_WHITESMITH:
+ case JOB_SNIPER:
+ case JOB_ASSASSIN_CROSS:
+ return msg_txt(582 - JOB_LORD_KNIGHT+class_);
+
+ case JOB_LORD_KNIGHT2:
+ return msg_txt(582);
+
+ case JOB_PALADIN:
+ case JOB_CHAMPION:
+ case JOB_PROFESSOR:
+ case JOB_STALKER:
+ case JOB_CREATOR:
+ case JOB_CLOWN:
+ case JOB_GYPSY:
+ return msg_txt(588 - JOB_PALADIN + class_);
+
+ case JOB_PALADIN2:
+ return msg_txt(588);
+
+ case JOB_BABY:
+ case JOB_BABY_SWORDMAN:
+ case JOB_BABY_MAGE:
+ case JOB_BABY_ARCHER:
+ case JOB_BABY_ACOLYTE:
+ case JOB_BABY_MERCHANT:
+ case JOB_BABY_THIEF:
+ return msg_txt(595 - JOB_BABY + class_);
+
+ case JOB_BABY_KNIGHT:
+ case JOB_BABY_PRIEST:
+ case JOB_BABY_WIZARD:
+ case JOB_BABY_BLACKSMITH:
+ case JOB_BABY_HUNTER:
+ case JOB_BABY_ASSASSIN:
+ return msg_txt(602 - JOB_BABY_KNIGHT + class_);
+
+ case JOB_BABY_KNIGHT2:
+ return msg_txt(602);
+
+ case JOB_BABY_CRUSADER:
+ case JOB_BABY_MONK:
+ case JOB_BABY_SAGE:
+ case JOB_BABY_ROGUE:
+ case JOB_BABY_ALCHEMIST:
+ case JOB_BABY_BARD:
+ case JOB_BABY_DANCER:
+ return msg_txt(608 - JOB_BABY_CRUSADER +class_);
+
+ case JOB_BABY_CRUSADER2:
+ return msg_txt(608);
+
+ case JOB_SUPER_BABY:
+ return msg_txt(615);
+
+ case JOB_TAEKWON:
+ return msg_txt(616);
+ case JOB_STAR_GLADIATOR:
+ case JOB_STAR_GLADIATOR2:
+ return msg_txt(617);
+ case JOB_SOUL_LINKER:
+ return msg_txt(618);
+
+ default:
+ return msg_txt(650);
+ }
+}
+
+/*==========================================
+ * PC‚ÌU? (timer??)
+ *------------------------------------------
+ */
+int pc_attack_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ struct block_list *bl;
+ int skill,range;
+
+ sd=map_id2sd(id);
+ if(sd == NULL)
+ return 0;
+
+ //Should we disable this line? Ctrl+click and then going away "IS" idling... [Skotlex]
+ sd->idletime = last_tick;
+
+ if(sd->attacktimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_attack_timer %d != %d\n",sd->attacktimer,tid);
+ return 0;
+ }
+ sd->attacktimer=-1;
+
+ if(sd->bl.prev == NULL)
+ return 0;
+
+ bl=map_id2bl(sd->attacktarget);
+ if(bl==NULL || bl->prev == NULL)
+ return 0;
+
+ // “¯‚¶map‚Å‚È‚¢‚È‚çU?‚µ‚È‚¢
+ // PC‚ªŽ€‚ñ‚Å‚Ä‚àU?‚µ‚È‚¢
+ if(sd->bl.m != bl->m)
+ return 0;
+
+ if(!status_check_skilluse(&sd->bl, bl, 0, 0))
+ return 0;
+
+ if(sd->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST) <= 0)
+ return 0;
+
+ if(!battle_config.sdelay_attack_enable && DIFF_TICK(sd->canact_tick,tick) > 0 && pc_checkskill(sd,SA_FREECAST) <= 0)
+ {
+ if (tid == -1) { //player requested attack.
+ clif_skill_fail(sd,1,4,0);
+ return 0;
+ }
+ //Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex]
+ if(sd->state.attack_continue) {
+ sd->attackabletime = sd->canact_tick;
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+ }
+ return 0;
+ }
+
+ if(sd->status.weapon == 11 && sd->equip_index[10] < 0)
+ {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+
+ range = sd->attackrange;
+ if(sd->status.weapon != 11) range++;
+ if(battle_iswalking(bl)) range++;
+ if(!battle_check_range(&sd->bl,bl,range) ) {
+ if(pc_can_reach(sd,bl->x,bl->y))
+ clif_movetoattack(sd,bl);
+ return 0;
+ }
+
+ if(battle_config.pc_attack_direction_change)
+ sd->dir=sd->head_dir=map_calc_dir(&sd->bl, bl->x,bl->y ); // Œü‚«Ý’è
+
+ if(sd->walktimer != -1)
+ pc_stop_walking(sd,1);
+
+ if(DIFF_TICK(sd->attackabletime,tick) <= 0) {
+ map_freeblock_lock();
+ sd->attacktarget_lv = battle_weapon_attack(&sd->bl,bl,tick,0);
+
+ if(!(battle_config.pc_cloak_check_type&2) && sd->sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support)
+ pet_target_check(sd,bl,0);
+
+ map_freeblock_unlock();
+
+ if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // ƒtƒŠ?ƒLƒƒƒXƒg
+ sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
+ else
+ sd->attackabletime = tick + (sd->aspd<<1);
+ }
+
+ if(sd->state.attack_continue)
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * U?—v‹
+ * type‚ª1‚È‚ç??U?
+ *------------------------------------------
+ */
+int pc_attack(struct map_session_data *sd,int target_id,int type)
+{
+ struct block_list *bl;
+
+ nullpo_retr(0, sd);
+
+ bl=map_id2bl(target_id);
+ if(bl==NULL)
+ return 1;
+
+ if(bl->type==BL_NPC) { // monster npcs [Valaris]
+ npc_click(sd,target_id); // submitted by leinsirk10 [Celest]
+ return 0;
+ }
+
+ if(battle_check_target(&sd->bl,bl,BCT_ENEMY) <= 0 || !status_check_skilluse(&sd->bl, bl, 0, 0))
+ return 1;
+ if(sd->attacktimer != -1)
+ { //Just change target/type. [Skotlex]
+ sd->attacktarget=target_id;
+ sd->state.attack_continue=type;
+ return 0;
+ }
+
+ sd->attacktarget=target_id;
+ sd->state.attack_continue=type;
+
+ if(sd->attackabletime > gettick()){ //Do attack next time it is possible. [Skotlex]
+ sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
+ } else { //Attack NOW.
+ pc_attack_timer(-1,gettick(),sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ??U?’âŽ~
+ *------------------------------------------
+ */
+int pc_stopattack(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->attacktimer != -1) {
+ delete_timer(sd->attacktimer,pc_attack_timer);
+ sd->attacktimer=-1;
+ }
+ sd->attacktarget=0;
+ sd->state.attack_continue=0;
+
+ return 0;
+}
+
+int pc_follow_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd, *tsd;
+
+ sd = map_id2sd(id);
+ nullpo_retr(0, sd);
+
+ if (sd->followtimer != tid){
+ if(battle_config.error_log)
+ ShowError("pc_follow_timer %d != %d\n",sd->followtimer,tid);
+ sd->followtimer = -1;
+ return 0;
+ }
+
+ sd->followtimer = -1;
+ if (pc_isdead(sd))
+ return 0;
+
+ if ((tsd = map_id2sd(sd->followtarget)) != NULL)
+ {
+ if (pc_isdead(tsd))
+ return 0;
+
+ // either player or target is currently detached from map blocks (could be teleporting),
+ // but still connected to this map, so we'll just increment the timer and check back later
+ if (sd->bl.prev != NULL && tsd->bl.prev != NULL &&
+ sd->skilltimer == -1 && sd->attacktimer == -1 && sd->walktimer == -1)
+ {
+ if((sd->bl.m == tsd->bl.m) && pc_can_reach(sd,tsd->bl.x,tsd->bl.y)) {
+ if (!check_distance_bl(&sd->bl, &tsd->bl, 5) && pc_can_move(sd))
+ pc_walktoxy(sd,tsd->bl.x,tsd->bl.y);
+ } else
+ pc_setpos(sd, tsd->mapindex, tsd->bl.x, tsd->bl.y, 3);
+ }
+ sd->followtimer = add_timer(
+ tick + sd->aspd + rand() % 1000, // increase time a bit to loosen up map's load
+ pc_follow_timer, sd->bl.id, 0);
+ }
+ return 0;
+}
+
+int pc_stop_following (struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if (sd->followtimer != -1) {
+ delete_timer(sd->followtimer,pc_follow_timer);
+ sd->followtimer = -1;
+ }
+ sd->followtarget = -1;
+
+ return 0;
+}
+
+int pc_follow(struct map_session_data *sd,int target_id)
+{
+ struct block_list *bl = map_id2bl(target_id);
+ if (bl == NULL || bl->type != BL_PC)
+ return 1;
+ if (sd->followtimer != -1)
+ pc_stop_following(sd);
+
+ sd->followtarget = target_id;
+ pc_follow_timer(-1,gettick(),sd->bl.id,0);
+
+ return 0;
+}
+
+int pc_checkbaselevelup(struct map_session_data *sd)
+{
+ int next = pc_nextbaseexp(sd);
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_exp >= next && next > 0){
+
+ // base‘¤ƒŒƒxƒ‹ƒAƒbƒv?—
+ sd->status.base_exp -= next;
+
+ sd->status.base_level ++;
+ if (battle_config.pet_lv_rate && sd->pd) //<Skotlex> update pet's level
+ status_calc_pet(sd,0);
+ if (battle_config.use_statpoint_table)
+ { // Taken from pc_resetstate. [Skotlex]
+ int lv = sd->status.base_level;
+ if (lv >= MAX_LEVEL) lv = MAX_LEVEL - 1;
+ else if (lv < 1) lv = 1;
+ sd->status.status_point += statp[lv] - statp[lv-1];
+ } else //Estimated way.
+ sd->status.status_point += (sd->status.base_level+14) / 5 ;
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_BASELEVEL);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ status_calc_pc(sd,0);
+ pc_heal(sd,sd->status.max_hp,sd->status.max_sp);
+
+ //ƒXƒpƒmƒr‚̓LƒŠƒGAƒCƒ€ƒ|Aƒ}ƒjƒsAƒOƒAƒTƒtƒ‰Lv1‚ª‚©‚©‚é
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE || (sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON){
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],1,0,0,0,skill_get_time(PR_KYRIE,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_IMPOSITIO],1,0,0,0,skill_get_time(PR_IMPOSITIO,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],1,0,0,0,skill_get_time(PR_MAGNIFICAT,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_GLORIA],1,0,0,0,skill_get_time(PR_GLORIA,1),0 );
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_SUFFRAGIUM],1,0,0,0,skill_get_time(PR_SUFFRAGIUM,1),0 );
+ }
+
+ clif_misceffect(&sd->bl,0);
+ //LORDALFA - LVLUPEVENT
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.baselvup_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLvlUPNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n",script_config.baselvup_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.baselvup_event_name, sd->bl.id), script_config.baselvup_event_name);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int pc_checkjoblevelup(struct map_session_data *sd)
+{
+ int next = pc_nextjobexp(sd);
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_exp >= next && next > 0){
+ // job‘¤ƒŒƒxƒ‹ƒAƒbƒv?—
+ sd->status.job_exp -= next;
+ sd->status.job_level ++;
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ sd->status.skill_point ++;
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ status_calc_pc(sd,0);
+
+ clif_misceffect(&sd->bl,1);
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_change(&sd->bl,SI_DEVIL, 1); //Permanent blind effect from SG_DEVIL.
+
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.joblvup_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCLvlUPNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n",script_config.joblvup_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.joblvup_event_name, sd->bl.id), script_config.joblvup_event_name);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ??’lŽæ“¾
+ *------------------------------------------
+ */
+int pc_gainexp(struct map_session_data *sd,int base_exp,int job_exp)
+{
+ char output[256];
+ float nextbp=0, nextjp=0;
+ int nextb=0, nextj=0;
+ nullpo_retr(0, sd);
+
+ if(sd->bl.prev == NULL || pc_isdead(sd))
+ return 0;
+
+ if((battle_config.pvp_exp == 0) && map[sd->bl.m].flag.pvp) // [MouseJstr]
+ return 0; // no exp on pvp maps
+
+ if(sd->status.guild_id>0){ // ƒMƒ‹ƒh‚Éã”[
+ base_exp-=guild_payexp(sd,base_exp);
+ if(base_exp < 0)
+ base_exp = 0;
+ }
+
+ if(!battle_config.multi_level_up && pc_nextbaseafter(sd) && sd->status.base_exp+base_exp >= pc_nextbaseafter(sd)) {
+ base_exp = pc_nextbaseafter(sd) - sd->status.base_exp;
+ if (base_exp < 0)
+ base_exp = 0;
+ }
+ nextb = pc_nextbaseexp(sd);
+ nextj = pc_nextjobexp(sd);
+ if (nextb > 0)
+ nextbp = (float) base_exp / (float) nextb;
+ if (nextj > 0)
+ nextjp = (float) job_exp / (float) nextj;
+
+ sd->status.base_exp += base_exp;
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp = 0;
+
+ while(pc_checkbaselevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_BASEEXP);
+ if(!battle_config.multi_level_up && pc_nextjobafter(sd) && sd->status.job_exp+job_exp >= pc_nextjobafter(sd)) {
+ job_exp = pc_nextjobafter(sd) - sd->status.job_exp;
+ if (job_exp < 0)
+ job_exp = 0;
+ }
+
+ sd->status.job_exp += job_exp;
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp = 0;
+
+ while(pc_checkjoblevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_JOBEXP);
+
+ if(sd->state.showexp){
+ sprintf(output,
+ "Experienced Gained Base:%d (%.2f%%) Job:%d (%.2f%%)",base_exp,nextbp*(float)100,job_exp,nextjp*(float)100);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * base level‘¤•K—v??’lŒvŽZ
+ *------------------------------------------
+ */
+int pc_nextbaseexp(struct map_session_data *sd)
+{
+ int i =0;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_level>=MAX_LEVEL || sd->status.base_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?4:0; //4 is the base for upper, 0 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 3; //Super Novice/Super Baby
+ else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.base_level-1];
+}
+
+/*==========================================
+ * job level‘¤•K—v??’lŒvŽZ
+ *------------------------------------------
+ */
+int pc_nextjobexp(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?11:7; //11 is the base for upper, 7 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 10; //Super Novice/Super Baby
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR) {
+ i = 13; //Star Gladiator - slow JExp (as for 2nd class)
+ if (sd->status.job_level >= battle_config.max_job_level)
+ return 0; //Since SG aren't really an advanced class... [Skotlex]
+ } else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.job_level-1];
+}
+
+/*==========================================
+ * base level after next [Valaris]
+ *------------------------------------------
+ */
+int pc_nextbaseafter(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.base_level>=MAX_LEVEL || sd->status.base_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?4:0; //4 is the base for upper, 0 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 3; //Super Novice/Super Baby
+ else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.base_level];
+}
+
+/*==========================================
+ * job level after next [Valaris]
+ *------------------------------------------
+ */
+int pc_nextjobafter(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0)
+ return 0;
+
+ i = (sd->class_&JOBL_UPPER)?11:7; //11 is the base for upper, 7 for normal/baby ones.
+
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ ; //Add 0, it's novice.
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE)
+ i = 10; //Super Novice/Super Baby
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR) {
+ i = 13; //Star Gladiator - slow JExp (as for 2nd class)
+ if (sd->status.job_level >= battle_config.max_job_level)
+ return 0; //Since SG aren't really an advanced class... [Skotlex]
+ } else
+ i+= (sd->class_&JOBL_2)?2:1; //Add 2 for second classes, add 1 for first classes.
+
+ return exp_table[i][sd->status.job_level];
+}
+/*==========================================
+
+ * •K—vƒXƒe?ƒ^ƒXƒ|ƒCƒ“ƒgŒvŽZ
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ”\—Í’l¬’·
+ *------------------------------------------
+ */
+int pc_statusup(struct map_session_data *sd,int type)
+{
+ int max, need,val = 0;
+
+ nullpo_retr(0, sd);
+
+ max = pc_maxparameter(sd);
+
+ need=pc_need_status_point(sd,type);
+ if(type<SP_STR || type>SP_LUK || need<0 || need>sd->status.status_point){
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ switch(type){
+ case SP_STR:
+ if(sd->status.str >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.str;
+ break;
+ case SP_AGI:
+ if(sd->status.agi >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.agi;
+ break;
+ case SP_VIT:
+ if(sd->status.vit >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.vit;
+ break;
+ case SP_INT:
+ if(sd->status.int_ >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.int_;
+ break;
+ case SP_DEX:
+ if(sd->status.dex >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.dex;
+ break;
+ case SP_LUK:
+ if(sd->status.luk >= max) {
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ val= ++sd->status.luk;
+ break;
+ }
+ sd->status.status_point-=need;
+ if(need!=pc_need_status_point(sd,type)){
+ clif_updatestatus(sd,type-SP_STR+SP_USTR);
+ }
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,type);
+ status_calc_pc(sd,0);
+ clif_statusupack(sd,type,1,val);
+
+ return 0;
+}
+
+/*==========================================
+ * ”\—Í’l¬’·
+ *------------------------------------------
+ */
+int pc_statusup2(struct map_session_data *sd,int type,int val)
+{
+ int max;
+ nullpo_retr(0, sd);
+
+ max = pc_maxparameter(sd);
+
+ if(type<SP_STR || type>SP_LUK){
+ clif_statusupack(sd,type,0,0);
+ return 1;
+ }
+ switch(type){
+ case SP_STR:
+ if(sd->status.str + val >= max)
+ val = max;
+ else if(sd->status.str + val < 1)
+ val = 1;
+ else
+ val += sd->status.str;
+ sd->status.str = val;
+ break;
+ case SP_AGI:
+ if(sd->status.agi + val >= max)
+ val = max;
+ else if(sd->status.agi + val < 1)
+ val = 1;
+ else
+ val += sd->status.agi;
+ sd->status.agi = val;
+ break;
+ case SP_VIT:
+ if(sd->status.vit + val >= max)
+ val = max;
+ else if(sd->status.vit + val < 1)
+ val = 1;
+ else
+ val += sd->status.vit;
+ sd->status.vit = val;
+ break;
+ case SP_INT:
+ if(sd->status.int_ + val >= max)
+ val = max;
+ else if(sd->status.int_ + val < 1)
+ val = 1;
+ else
+ val += sd->status.int_;
+ sd->status.int_ = val;
+ break;
+ case SP_DEX:
+ if(sd->status.dex + val >= max)
+ val = max;
+ else if(sd->status.dex + val < 1)
+ val = 1;
+ else
+ val += sd->status.dex;
+ sd->status.dex = val;
+ break;
+ case SP_LUK:
+ if(sd->status.luk + val >= max)
+ val = max;
+ else if(sd->status.luk + val < 1)
+ val = 1;
+ else
+ val = sd->status.luk + val;
+ sd->status.luk = val;
+ break;
+ }
+ clif_updatestatus(sd,type-SP_STR+SP_USTR);
+ clif_updatestatus(sd,type);
+ status_calc_pc(sd,0);
+ clif_statusupack(sd,type,1,val);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ|ƒCƒ“ƒgŠ„‚èU‚è
+ *------------------------------------------
+ */
+int pc_skillup(struct map_session_data *sd,int skill_num)
+{
+ nullpo_retr(0, sd);
+
+ if( skill_num>=10000 ){
+ guild_skillup(sd,skill_num,0);
+ return 0;
+ }
+
+ if( sd->status.skill_point>0 &&
+ sd->status.skill[skill_num].id!=0 &&
+ //sd->status.skill[skill_num].lv < skill_get_max(skill_num) ) - celest
+ sd->status.skill[skill_num].lv < skill_tree_get_max(skill_num, sd->status.class_) )
+ {
+ sd->status.skill[skill_num].lv++;
+ sd->status.skill_point--;
+ status_calc_pc(sd,0);
+ clif_skillup(sd,skill_num);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_skillinfoblock(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * /allskill
+ *------------------------------------------
+ */
+int pc_allskillup(struct map_session_data *sd)
+{
+ int i,id;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_SKILL;i++){
+ sd->status.skill[i].id=0;
+ if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){ // cardƒXƒLƒ‹‚È‚çA
+ 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){
+ // ‘S‚ẴXƒLƒ‹
+ for(i=0;i<MAX_SKILL;i++){
+ if(!(skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL))) //Get ALL skills except npc/guild ones. [Skotlex]
+ if (i!=SG_DEVIL) //and except SG_DEVIL [Komurka]
+ sd->status.skill[i].lv=skill_get_max(i); //Nonexistant skills should return a max of 0 anyway.
+ }
+ }
+ else {
+ int inf2;
+ for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[sd->status.class_][i].id)>0;i++){
+ inf2 = skill_get_inf2(id);
+ if(sd->status.skill[id].id==0 &&
+ (!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) &&
+ (id!=SG_DEVIL))
+ {
+ sd->status.skill[id].id = id; // celest
+ sd->status.skill[id].lv = skill_tree_get_max(id, sd->status.class_); // celest
+ }
+ }
+ }
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetlvl
+ *------------------------------------------
+ */
+int pc_resetlvl(struct map_session_data* sd,int type)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=1;i<MAX_SKILL;i++){
+ sd->status.skill[i].lv = 0;
+ }
+
+ if(type == 1){
+ sd->status.skill_point=0;
+ sd->status.base_level=1;
+ sd->status.job_level=1;
+ sd->status.base_exp=sd->status.base_exp=0;
+ sd->status.job_exp=sd->status.job_exp=0;
+ if(sd->status.option !=0)
+ sd->status.option = 0;
+
+ sd->status.str=1;
+ sd->status.agi=1;
+ sd->status.vit=1;
+ sd->status.int_=1;
+ sd->status.dex=1;
+ sd->status.luk=1;
+ if(sd->status.class_ == JOB_NOVICE_HIGH)
+ sd->status.status_point=100; // not 88 [celest]
+ // give platinum skills upon changing
+ pc_skill(sd,142,1,0);
+ pc_skill(sd,143,1,0);
+ }
+
+ if(type == 2){
+ sd->status.skill_point=0;
+ sd->status.base_level=1;
+ sd->status.job_level=1;
+ sd->status.base_exp=0;
+ sd->status.job_exp=0;
+ }
+ if(type == 3){
+ sd->status.base_level=1;
+ sd->status.base_exp=0;
+ }
+ if(type == 4){
+ sd->status.job_level=1;
+ sd->status.job_exp=0;
+ }
+
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+ clif_updatestatus(sd,SP_BASELEVEL);
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+
+ clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris
+ clif_updatestatus(sd,SP_UAGI);
+ clif_updatestatus(sd,SP_UVIT);
+ clif_updatestatus(sd,SP_UINT);
+ clif_updatestatus(sd,SP_UDEX);
+ clif_updatestatus(sd,SP_ULUK); // End Addition
+
+ for(i=0;i<11;i++) { // unequip items that can't be equipped by base 1 [Valaris]
+ if(sd->equip_index[i] >= 0)
+ if(!pc_isequip(sd,sd->equip_index[i]))
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+
+ if ((type == 1 || type == 2 || type == 3) && sd->status.party_id) {
+ //Send map-change packet to do a level range check and break party settings. [Skotlex]
+ party_send_movemap(sd);
+ }
+ clif_skillinfoblock(sd);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+/*==========================================
+ * /resetstate
+ *------------------------------------------
+ */
+int pc_resetstate(struct map_session_data* sd)
+{
+ nullpo_retr(0, sd);
+
+ if (battle_config.use_statpoint_table)
+ { // New statpoint table used here - Dexity
+ int lv;
+ // allow it to just read the last entry [celest]
+ lv = sd->status.base_level < MAX_LEVEL ? sd->status.base_level : MAX_LEVEL - 1;
+
+ sd->status.status_point = statp[lv];
+ if (sd->class_&JOBL_UPPER)
+ sd->status.status_point+=52; // extra 52+48=100 stat points
+ } else { //Use new stat-calculating equation [Skotlex]
+#define sumsp(a) (((a-1)/10 +2)*(5*((a-1)/10 +1) + (a-1)%10) -10)
+ int add=0;
+ add += sumsp(sd->status.str);
+ add += sumsp(sd->status.agi);
+ add += sumsp(sd->status.vit);
+ add += sumsp(sd->status.int_);
+ add += sumsp(sd->status.dex);
+ add += sumsp(sd->status.luk);
+ sd->status.status_point+=add;
+ }
+
+ sd->status.str=1;
+ sd->status.agi=1;
+ sd->status.vit=1;
+ sd->status.int_=1;
+ sd->status.dex=1;
+ sd->status.luk=1;
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris
+ clif_updatestatus(sd,SP_UAGI);
+ clif_updatestatus(sd,SP_UVIT);
+ clif_updatestatus(sd,SP_UINT);
+ clif_updatestatus(sd,SP_UDEX);
+ clif_updatestatus(sd,SP_ULUK); // End Addition
+
+ clif_updatestatus(sd,SP_STATUSPOINT);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetskill
+ *------------------------------------------
+ */
+int pc_resetskill(struct map_session_data* sd)
+{
+ int i, skill, inf2;
+ nullpo_retr(0, sd);
+
+ if (pc_checkskill(sd, SG_DEVIL) && sd->status.job_level >= battle_config.max_job_level)
+ clif_status_load(&sd->bl, SI_DEVIL, 0); //Remove perma blindness due to skill-reset. [Skotlex]
+
+ for (i = 1; i < MAX_SKILL; i++) {
+ if ((skill = sd->status.skill[i].lv) > 0) {
+ inf2 = skill_get_inf2(i);
+ if ((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+ !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) //Avoid reseting wedding/linker skills.
+ {
+ if (!sd->status.skill[i].flag)
+ sd->status.skill_point += skill;
+ else if (sd->status.skill[i].flag > 2 && sd->status.skill[i].flag != 13)
+ sd->status.skill_point += (sd->status.skill[i].flag - 2);
+ sd->status.skill[i].lv = 0;
+ }
+ else if (battle_config.quest_skill_reset && (inf2&INF2_QUEST_SKILL))
+ {
+ sd->status.skill[i].lv = 0;
+ sd->status.skill[i].flag = 0;
+ }
+ } else {
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_skillinfoblock(sd);
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * /resetfeel [Komurka]
+ *------------------------------------------
+ */
+int pc_resetfeel(struct map_session_data* sd)
+{
+ int i;
+ char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"};
+ nullpo_retr(0, sd);
+
+ for (i=0; i<3; i++)
+ {
+ sd->feel_map[i].m = -1;
+ sd->feel_map[i].index = 0;
+ pc_setglobalreg(sd,feel_var[i],0);
+ }
+
+ return 0;
+}
+
+static int pc_respawn(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ if (sd && pc_isdead(sd))
+ { //Auto-respawn [Skotlex]
+ pc_setstand(sd);
+ pc_setrestartvalue(sd,3);
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,0);
+ }
+ return 0;
+}
+/*==========================================
+ * pc‚Ƀ_ƒ?ƒW‚ð?‚¦‚é
+ *------------------------------------------
+ */
+int pc_damage(struct block_list *src,struct map_session_data *sd,int damage)
+{
+ int i=0,j=0, resurrect_flag=0;
+
+ nullpo_retr(0, sd);
+
+ // ?‚ÉŽ€‚ñ‚Å‚¢‚½‚ç–³?
+ if(pc_isdead(sd))
+ return 0;
+ // À‚Á‚Ä‚½‚ç—§‚¿ã‚ª‚é
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_gangsterparadise(sd,0);
+ }
+
+ // ? ‚¢‚Ä‚¢‚½‚ç‘«‚ðŽ~‚ß‚é
+ if (sd->sc_data) {
+ if (sd->sc_data[SC_ENDURE].timer != -1 && (src != NULL && src->type == BL_MOB) && !map_flag_gvg(sd->bl.m)) {
+ if (!sd->special_state.infinite_endure && (--sd->sc_data[SC_ENDURE].val2) < 0)
+ status_change_end(&sd->bl, SC_ENDURE, -1);
+ }
+ if (sd->sc_data[SC_GRAVITATION].timer != -1 &&
+ sd->sc_data[SC_GRAVITATION].val3 == BCT_SELF) {
+ struct skill_unit_group *sg = (struct skill_unit_group *)sd->sc_data[SC_GRAVITATION].val4;
+ if (sg) {
+ skill_delunitgroup(sg);
+ status_change_end(&sd->bl, SC_GRAVITATION, -1);
+ }
+ }
+
+ }
+
+ // ‰‰‘t/ƒ_ƒ“ƒX‚Ì’†?
+ if(damage > sd->status.max_hp>>2)
+ skill_stop_dancing(&sd->bl);
+
+ if (damage < sd->status.hp)
+ sd->status.hp-=damage;
+ else
+ sd->status.hp = 0;
+
+ if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_damage_support)
+ pet_target_check(sd,src,1);
+
+ if (sd->sc_data[SC_TRICKDEAD].timer != -1)
+ status_change_end(&sd->bl, SC_TRICKDEAD, -1);
+ if(sd->status.option&OPTION_HIDE)
+ status_change_end(&sd->bl, SC_HIDING, -1);
+ if(pc_iscloaking(sd))
+ status_change_end(&sd->bl, SC_CLOAKING, -1);
+ if(pc_ischasewalk(sd))
+ status_change_end(&sd->bl, SC_CHASEWALK, -1);
+
+ clif_updatestatus(sd,SP_HP);
+
+ if(sd->status.hp>0){
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 ))
+ // ƒI?ƒgƒo?ƒT?ƒN?“®
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+
+ sd->canlog_tick = gettick();
+
+ return damage;
+ }
+
+ if(sd->vender_id)
+ vending_closevending(sd);
+
+ if(sd->status.pet_id > 0 && sd->pd &&
+ !map[sd->bl.m].flag.nopenalty) {
+ if(sd->petDB) {
+ sd->pet.intimate -= sd->petDB->die;
+ if(sd->pet.intimate < 0)
+ sd->pet.intimate = 0;
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ }
+ }
+
+ // Leave duel if you die [LuzZza]
+ if(battle_config.duel_autoleave_when_die) {
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
+ if(sd->duel_invite > 0)
+ duel_reject(sd->duel_invite, sd);
+ }
+
+ pc_stop_walking(sd,0);
+ skill_castcancel(&sd->bl,0); // ‰r¥‚Ì’†Ž~
+ skill_stop_dancing(&sd->bl); //You should stop dancing when dead... [Skotlex]
+ if (sd->sc_data[SC_GOSPEL].timer != -1 && sd->sc_data[SC_GOSPEL].val4 == BCT_SELF)
+ { //Remove Gospel [Skotlex]
+ struct skill_unit_group *sg = (struct skill_unit_group *)sd->sc_data[SC_GOSPEL].val3;
+ if (sg)
+ skill_delunitgroup(sg);
+ }
+ clif_clearchar_area(&sd->bl,1);
+
+ if (src && src->type == BL_PC) {
+ struct map_session_data *ssd = (struct map_session_data *)src;
+ if (ssd) {
+ if (sd->state.event_death)
+ pc_setglobalreg(sd,"killerrid",(ssd->status.account_id));
+ if (ssd->state.event_kill) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.kill_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCKillNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.kill_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.kill_event_name, sd->bl.id), script_config.kill_event_name);
+ }
+ }
+ if (battle_config.pk_mode && ssd->status.manner >= 0 && battle_config.manner_system) {
+ ssd->status.manner -= 5;
+ if(ssd->status.manner < 0)
+ status_change_start(src,SC_NOCHAT,0,0,0,0,0,0);
+
+ // PK/Karma system code (not enabled yet) [celest]
+ // originally from Kade Online, so i don't know if any of these is correct ^^;
+ // note: karma is measured REVERSE, so more karma = more 'evil' / less honourable,
+ // karma going down = more 'good' / more honourable.
+ // The Karma System way...
+ /*if (sd->status.karma > ssd->status.karma) { // If player killed was more evil
+ sd->status.karma--;
+ ssd->status.karma--;
+ }
+ else if (sd->status.karma < ssd->status.karma) // If player killed was more good
+ ssd->status.karma++;*/
+
+ // or the PK System way...
+ /* if (sd->status.karma > 0) // player killed is dishonourable?
+ ssd->status.karma--; // honour points earned
+ sd->status.karma++; // honour points lost */
+ // To-do: Receive exp on certain occasions
+ }
+ }
+ }
+
+ if (sd->state.event_death) {
+ if (script_config.event_script_type == 0) {
+ struct npc_data *npc;
+ if ((npc = npc_name2id(script_config.die_event_name))) {
+ run_script(npc->u.scr.script,0,sd->bl.id,npc->bl.id); // PCDeathNPC
+ ShowStatus("Event '"CL_WHITE"%s"CL_RESET"' executed.\n", script_config.die_event_name);
+ }
+ } else {
+ ShowStatus("%d '"CL_WHITE"%s"CL_RESET"' events executed.\n",
+ npc_event_doall_id(script_config.die_event_name, sd->bl.id), script_config.die_event_name);
+ }
+ }
+
+// PK/Karma system code (not enabled yet) [celest]
+ /*if(sd->status.karma > 0) {
+ int eq_num=0,eq_n[MAX_INVENTORY];
+ memset(eq_n,0,sizeof(eq_n));
+ for(i=0;i<MAX_INVENTORY;i++){
+ int k;
+ for(k=0;k<MAX_INVENTORY;k++){
+ if(eq_n[k] <= 0){
+ eq_n[k]=i;
+ break;
+ }
+ }
+ eq_num++;
+ }
+ if(eq_num > 0){
+ int n = eq_n[rand()%eq_num];
+ if(rand()%10000 < sd->status.karma){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,0);
+ pc_dropitem(sd,n,1);
+ }
+ }
+ }*/
+
+ if(battle_config.bone_drop==2
+ || (battle_config.bone_drop==1 && map[sd->bl.m].flag.pvp)){ // ƒhƒNƒƒhƒƒbƒv
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=7420; //PVP Skull item ID
+ item_tmp.identify=1;
+ item_tmp.card[0]=0x00fe;
+ item_tmp.card[1]=0;
+ item_tmp.card[2]=GetWord(sd->char_id,0); // CharId
+ item_tmp.card[3]=GetWord(sd->char_id,1);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ // activate Steel body if a super novice dies at 99+% exp [celest]
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
+ if ((i=pc_nextbaseexp(sd))<=0)
+ i=sd->status.base_exp;
+ if (i>0 && (j=sd->status.base_exp*1000/i)>=990 && j<1000)
+ sd->state.snovice_flag = 4;
+ }
+
+ for(i = 0; i < 5; i++)
+ if (sd->devotion[i]){
+ struct map_session_data *devsd = map_id2sd(sd->devotion[i]);
+ if (devsd) status_change_end(&devsd->bl,SC_DEVOTION,-1);
+ }
+
+ pc_setdead(sd);
+ skill_unit_move(&sd->bl,gettick(),4);
+
+ pc_setglobalreg(sd,"PC_DIE_COUNTER",++sd->die_counter); //Ž€‚ɃJƒEƒ“ƒ^?‘‚«?‚Ý
+ // changed penalty options, added death by player if pk_mode [Valaris]
+ if(battle_config.death_penalty_type && sd->state.snovice_flag != 4
+ && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE // only novices will receive no penalty
+ && !map[sd->bl.m].flag.nopenalty && !map_flag_gvg(sd->bl.m)
+ && !(sd->sc_count && sd->sc_data[SC_BABY].timer!=-1))
+ {
+ if(battle_config.death_penalty_type==1 && battle_config.death_penalty_base > 0)
+ sd->status.base_exp -= (int) ((double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.base_exp -= (int) ((double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
+ else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_base > 0) {
+ if(pc_nextbaseexp(sd) > 0)
+ sd->status.base_exp -= (int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.base_exp -= (int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
+ }
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp = 0;
+ clif_updatestatus(sd,SP_BASEEXP);
+
+ if(battle_config.death_penalty_type==1 && battle_config.death_penalty_job > 0)
+ sd->status.job_exp -= (int) ((double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.job_exp -= (int) ((double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
+ else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_job > 0) {
+ if(pc_nextjobexp(sd) > 0)
+ sd->status.job_exp -= (int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
+ if(battle_config.pk_mode && src && src->type==BL_PC)
+ sd->status.job_exp -= (int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
+ }
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ if(src && src->type==BL_MOB) {
+ struct mob_data *md=(struct mob_data *)src;
+ if(md && md->target_id != 0 && md->target_id==sd->bl.id) { // reset target id when player dies
+ md->target_id=0;
+ mob_changestate(md,MS_WALK,0);
+ }
+ if(battle_config.mobs_level_up && md && md->state.state!=MS_DEAD &&
+ md->level < battle_config.max_base_level && !md->guardian_data // Guardians should not level. [Skotlex]
+ ) { // monster level up [Valaris]
+ clif_misceffect(&md->bl,0);
+ md->level++;
+ md->hp+=(int) (sd->status.max_hp*.1);
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, &md->bl);
+ }
+ }
+ //Clear these data here so that SC_BABY check may work. [Skotlex]
+ resurrect_flag = (sd->sc_data[SC_KAIZEL].timer != -1)?sd->sc_data[SC_KAIZEL].val1:0; //Auto-resurrect later in the code.
+ status_change_clear(&sd->bl,0); // ƒXƒe?ƒ^ƒXˆÙí‚ð‰ðœ‚·‚é
+ clif_updatestatus(sd,SP_HP);
+ status_calc_pc(sd,0);
+ sd->canregen_tick = gettick();
+
+
+ //ƒiƒCƒgƒƒAƒ‚?ƒhƒAƒCƒeƒ€ƒhƒƒbƒv
+ 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){//ƒ‰ƒ“ƒ_ƒ€ƒhƒƒbƒv
+ int eq_num=0,eq_n[MAX_INVENTORY];
+ memset(eq_n,0,sizeof(eq_n));
+ //悸?”õ‚µ‚Ä‚¢‚éƒAƒCƒeƒ€?‚ðƒJƒEƒ“ƒg
+ 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‚ðŠi”[
+ for(k=0;k<MAX_INVENTORY;k++){
+ if(eq_n[k] <= 0){
+ eq_n[k]=i;
+ break;
+ }
+ }
+ eq_num++;
+ }
+ }
+ if(eq_num > 0){
+ int n = eq_n[rand()%eq_num];//ŠY?ƒAƒCƒeƒ€‚Ì’†‚©‚烉ƒ“ƒ_ƒ€
+ if(rand()%10000 < per){
+ if(sd->status.inventory[n].equip)
+ pc_unequipitem(sd,n,3);
+ pc_dropitem(sd,n,1);
+ }
+ }
+ }
+ else if(id > 0){
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid == id//ItemID‚ªˆê’v‚µ‚Ä‚¢‚Ä
+ && rand()%10000 < per//ƒhƒƒbƒv—¦”»’è‚àOK‚Å
+ && ((type == 1 && !sd->status.inventory[i].equip)//ƒ^ƒCƒv”»’è‚àOK‚È‚çƒhƒƒbƒv
+ || (type == 2 && sd->status.inventory[i].equip)
+ || type == 3) ){
+ if(sd->status.inventory[i].equip)
+ pc_unequipitem(sd,i,3);
+ pc_dropitem(sd,i,1);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // pvp
+ if( map[sd->bl.m].flag.pvp && !battle_config.pk_mode){ // disable certain pvp functions on pk_mode [Valaris]
+ //ƒ‰ƒ“ƒLƒ“ƒOŒvŽZ
+ if (!map[sd->bl.m].flag.pvp_nocalcrank) {
+ sd->pvp_point -= 5;
+ sd->pvp_lost++;
+ if (src && src->type == BL_PC) {
+ struct map_session_data *ssd = (struct map_session_data *)src;
+ if (ssd) { ssd->pvp_point++; ssd->pvp_won++; }
+ }
+ }
+ // ?§‘—ŠÒ
+ if( sd->pvp_point < 0 ){
+ sd->pvp_point=0;
+ add_timer(gettick()+1000, pc_respawn,sd->bl.id,0);
+ return damage;
+ }
+ }
+ //GvG
+ if(map_flag_gvg(sd->bl.m)){
+ add_timer(gettick()+1000, pc_respawn,sd->bl.id,0);
+ return damage;
+ }
+
+ if (sd->state.snovice_flag == 4 || resurrect_flag) {
+ if (sd->state.snovice_flag == 4 || sd->special_state.restart_full_recover) {
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ } else { //10% life per each level in Kaizel
+ sd->status.hp = 10*resurrect_flag*sd->status.max_hp/100;
+ }
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,1,1);
+ pc_setstand(sd);
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ clif_resurrection(&sd->bl, 1);
+ sd->state.snovice_flag = 0;
+ if(battle_config.pc_invincible_time)
+ pc_setinvincibletimer(sd, battle_config.pc_invincible_time);
+ if (resurrect_flag)
+ status_change_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],10,0,0,0,skill_get_time2(SL_KAIZEL, resurrect_flag),0);
+ else
+ status_change_start(&sd->bl,SkillStatusChangeTable[MO_STEELBODY],1,0,0,0,skill_get_time(MO_STEELBODY,1),0 );
+ return 0;
+ }
+
+ return damage;
+}
+
+//
+// script? ˜A
+//
+/*==========================================
+ * script—pPCƒXƒe?ƒ^ƒX?‚Ýo‚µ
+ *------------------------------------------
+ */
+int pc_readparam(struct map_session_data *sd,int type)
+{
+ int val=0;
+
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_SKILLPOINT:
+ val= sd->status.skill_point;
+ break;
+ case SP_STATUSPOINT:
+ val= sd->status.status_point;
+ break;
+ case SP_ZENY:
+ val= sd->status.zeny;
+ break;
+ case SP_BASELEVEL:
+ val= sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ val= sd->status.job_level;
+ break;
+ case SP_CLASS:
+ if(val>=24 && val < 45)
+ val+=3978;
+ else
+ val= sd->status.class_;
+ break;
+ case SP_BASEJOB: //Base job, extracting upper type.
+ val= pc_mapid2jobid(sd->class_&MAPID_UPPERMASK, sd->status.sex);
+ break;
+ case SP_UPPER:
+ val= sd->class_&JOBL_UPPER?1:(sd->class_&JOBL_BABY?2:0);
+ break;
+ case SP_BASECLASS: //Extract base class tree. [Skotlex]
+ val= pc_mapid2jobid(sd->class_&MAPID_BASEMASK, sd->status.sex);
+ break;
+ case SP_SEX:
+ val= sd->sex;
+ break;
+ case SP_WEIGHT:
+ val= sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ val= sd->max_weight;
+ break;
+ case SP_BASEEXP:
+ val= sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ val= sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ val= pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ val= pc_nextjobexp(sd);
+ break;
+ case SP_HP:
+ val= sd->status.hp;
+ break;
+ case SP_MAXHP:
+ val= sd->status.max_hp;
+ break;
+ case SP_SP:
+ val= sd->status.sp;
+ break;
+ case SP_MAXSP:
+ val= sd->status.max_sp;
+ break;
+ case SP_STR:
+ val= sd->status.str;
+ break;
+ case SP_AGI:
+ val= sd->status.agi;
+ break;
+ case SP_VIT:
+ val= sd->status.vit;
+ break;
+ case SP_INT:
+ val= sd->status.int_;
+ break;
+ case SP_DEX:
+ val= sd->status.dex;
+ break;
+ case SP_LUK:
+ val= sd->status.luk;
+ break;
+ case SP_KARMA: // celest
+ val = sd->status.karma;
+ break;
+ case SP_MANNER:
+ val = sd->status.manner;
+ break;
+ case SP_FAME:
+ val= sd->status.fame;
+ break;
+ }
+
+ return val;
+}
+
+/*==========================================
+ * script—pPCƒXƒe?ƒ^ƒXÝ’è
+ *------------------------------------------
+ */
+int pc_setparam(struct map_session_data *sd,int type,int val)
+{
+ int i = 0,up_level = battle_config.max_job_level;
+
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case SP_BASELEVEL:
+ if ((val+ sd->status.base_level) > battle_config.max_base_level) //Capping to max
+ val = battle_config.max_base_level - sd->status.base_level;
+ if (val > (int)sd->status.base_level) {
+ for (i = 1; i <= (val - (int)sd->status.base_level); i++)
+ sd->status.status_point += (sd->status.base_level + i + 14) / 5 ;
+ }
+ sd->status.base_level = val;
+ sd->status.base_exp = 0;
+ clif_updatestatus(sd, SP_BASELEVEL);
+ clif_updatestatus(sd, SP_NEXTBASEEXP);
+ clif_updatestatus(sd, SP_STATUSPOINT);
+ clif_updatestatus(sd, SP_BASEEXP);
+ status_calc_pc(sd, 0);
+ pc_heal(sd, sd->status.max_hp, sd->status.max_sp);
+ break;
+ case SP_JOBLEVEL:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE)
+ up_level = 10; //Novice & Baby Novice have 10 Job Levels only
+ else if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) //Super Novice & Super Baby can go up to 99
+ up_level = battle_config.max_sn_level;
+ else if (sd->class_&JOBL_UPPER && sd->class_&JOBL_2) //3rd Job has 70 Job Levels
+ up_level = battle_config.max_adv_level;
+ if (val >= (int)sd->status.job_level) {
+ if (val > up_level) val = up_level;
+ sd->status.skill_point += (val-sd->status.job_level);
+ sd->status.job_level = val;
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ clif_updatestatus(sd, SP_JOBEXP);
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ status_calc_pc(sd, 0);
+ clif_misceffect(&sd->bl, 1);
+ } else {
+ sd->status.job_level = val;
+ sd->status.job_exp = 0;
+ clif_updatestatus(sd, SP_JOBLEVEL);
+ clif_updatestatus(sd, SP_NEXTJOBEXP);
+ clif_updatestatus(sd, SP_JOBEXP);
+ status_calc_pc(sd, 0);
+ }
+ clif_updatestatus(sd,type);
+ break;
+ case SP_SKILLPOINT:
+ sd->status.skill_point = val;
+ break;
+ case SP_STATUSPOINT:
+ sd->status.status_point = val;
+ break;
+ case SP_ZENY:
+ if(val <= MAX_ZENY) {
+ // MAX_ZENY ˆÈ‰º‚È‚ç‘ã“ü
+ sd->status.zeny = val;
+ } else {
+ sd->status.zeny = MAX_ZENY;
+ /* Could someone explain the comments below? I have no idea what they are trying to do...
+ * if you want to give someone so much zeny, just set their zeny to the max. [Skotlex]
+ if(sd->status.zeny > val) {
+ // Zeny ‚ªŒ¸­‚µ‚Ä‚¢‚é‚È‚ç‘ã“ü
+ sd->status.zeny = val;
+ } else if(sd->status.zeny <= MAX_ZENY) {
+ // Zeny ‚ª‘‰Á‚µ‚Ä‚¢‚ÄAŒ»Ý‚Ì’l‚ªMAX_ZENY ˆÈ‰º‚È‚çMAX_ZENY
+ sd->status.zeny = MAX_ZENY;
+ } else {
+ // Zeny ‚ª‘‰Á‚µ‚Ä‚¢‚ÄAŒ»Ý‚Ì’l‚ªMAX_ZENY ‚æ‚艺‚Ȃ瑉Á•ª‚𖳎‹
+ ;
+ }
+ */
+ }
+ break;
+ case SP_BASEEXP:
+ if(pc_nextbaseexp(sd) > 0) {
+ sd->status.base_exp = val;
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp=0;
+ pc_checkbaselevelup(sd);
+ }
+ break;
+ case SP_JOBEXP:
+ if(pc_nextjobexp(sd) > 0) {
+ sd->status.job_exp = val;
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp=0;
+ pc_checkjoblevelup(sd);
+ }
+ break;
+ case SP_SEX:
+ sd->sex = val;
+ break;
+ case SP_WEIGHT:
+ sd->weight = val;
+ break;
+ case SP_MAXWEIGHT:
+ sd->max_weight = val;
+ break;
+ case SP_HP:
+ sd->status.hp = val;
+ break;
+ case SP_MAXHP:
+ sd->status.max_hp = val;
+ break;
+ case SP_SP:
+ sd->status.sp = val;
+ break;
+ case SP_MAXSP:
+ sd->status.max_sp = val;
+ break;
+ case SP_STR:
+ sd->status.str = val;
+ break;
+ case SP_AGI:
+ sd->status.agi = val;
+ break;
+ case SP_VIT:
+ sd->status.vit = val;
+ break;
+ case SP_INT:
+ sd->status.int_ = val;
+ break;
+ case SP_DEX:
+ sd->status.dex = val;
+ break;
+ case SP_LUK:
+ sd->status.luk = val;
+ break;
+ case SP_KARMA:
+ sd->status.karma = val;
+ break;
+ case SP_MANNER:
+ sd->status.manner = val;
+ break;
+ case SP_FAME:
+ sd->status.fame = val;
+ break;
+ }
+ clif_updatestatus(sd,type);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP‰ñ•œ
+ *------------------------------------------
+ */
+int pc_heal(struct map_session_data *sd,int hp,int sp)
+{
+// if(battle_config.battle_log)
+// printf("heal %d %d\n",hp,sp);
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1) //ƒo?ƒT?ƒN’†‚͉ñ•œ‚³‚¹‚È‚¢‚炵‚¢
+ return 0;
+
+ if(hp+sd->status.hp>sd->status.max_hp)
+ hp=sd->status.max_hp-sd->status.hp;
+ if(sp+sd->status.sp>sd->status.max_sp)
+ sp=sd->status.max_sp-sd->status.sp;
+ sd->status.hp+=hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ sd->status.sp+=sp;
+ if(sd->status.sp <= 0)
+ sd->status.sp = 0;
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ if(sd->status.hp>=sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer != -1 &&
+ (sd->sc_data[SC_PROVOKE].timer!=-1 && sd->sc_data[SC_PROVOKE].val2==1 ))
+ status_change_end(&sd->bl,SC_PROVOKE,-1); //End auto berserk.
+
+ return hp + sp;
+}
+
+/*==========================================
+ * HP/SP‰ñ•œ
+ *------------------------------------------
+ */
+int pc_itemheal(struct map_session_data *sd,int hp,int sp)
+{
+ int bonus, type;
+// if(battle_config.battle_log)
+// printf("heal %d %d\n",hp,sp);
+
+ nullpo_retr(0, sd);
+
+ if(sd->sc_count && sd->sc_data[SC_GOSPEL].timer!=-1) //ƒo?ƒT?ƒN’†‚͉ñ•œ‚³‚¹‚È‚¢‚炵‚¢
+ return 0;
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+
+ if(hp > 0) {
+ bonus = (sd->paramc[2]<<1) + 100 + pc_checkskill(sd,SM_RECOVERY)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5;
+ // A potion produced by an Alchemist in the Fame Top 10 gets +50% effect [DracoRPG]
+ bonus += (potion_flag==2)?50:(potion_flag==3?100:0);
+ if ((type = itemdb_group(sd->itemid)) > 0 && type <= 7)
+ bonus = bonus * (100+sd->itemhealrate[type - 1]) / 100;
+ if(bonus != 100)
+ hp = hp * bonus / 100;
+ }
+ if(sp > 0) {
+ bonus = (sd->paramc[3]<<1) + 100 + pc_checkskill(sd,MG_SRECOVERY)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5;
+ bonus += (potion_flag==2)?50:(potion_flag==3?100:0);
+ if(bonus != 100)
+ sp = sp * bonus / 100;
+ }
+ if(hp+sd->status.hp>sd->status.max_hp)
+ hp=sd->status.max_hp-sd->status.hp;
+ if(sp+sd->status.sp>sd->status.max_sp)
+ sp=sd->status.max_sp-sd->status.sp;
+ sd->status.hp+=hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ sd->status.sp+=sp;
+ if(sd->status.sp <= 0)
+ sd->status.sp = 0;
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP‰ñ•œ
+ *------------------------------------------
+ */
+int pc_percentheal(struct map_session_data *sd,int hp,int sp)
+{
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ if(hp > 0)
+ hp = 0;
+ }
+ if(pc_checkoversp(sd)) {
+ if(sp > 0)
+ sp = 0;
+ }
+ if(hp) {
+ if(hp >= 100) {
+ sd->status.hp = sd->status.max_hp;
+ }
+ else if(hp <= -100) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ }
+ else {
+ sd->status.hp += sd->status.max_hp*hp/100;
+ if(sd->status.hp > sd->status.max_hp)
+ sd->status.hp = sd->status.max_hp;
+ if(sd->status.hp <= 0) {
+ sd->status.hp = 0;
+ pc_damage(NULL,sd,1);
+ hp = 0;
+ }
+ }
+ }
+ if(sp) {
+ if(sp >= 100) {
+ sd->status.sp = sd->status.max_sp;
+ }
+ else if(sp <= -100) {
+ sd->status.sp = 0;
+ }
+ else {
+ sd->status.sp += sd->status.max_sp*sp/100;
+ if(sd->status.sp > sd->status.max_sp)
+ sd->status.sp = sd->status.max_sp;
+ if(sd->status.sp < 0)
+ sd->status.sp = 0;
+ }
+ }
+ if(hp)
+ clif_updatestatus(sd,SP_HP);
+ if(sp)
+ clif_updatestatus(sd,SP_SP);
+
+ return 0;
+}
+
+/*==========================================
+ * E?X
+ * ˆø? job E‹Æ 0`23
+ * upper ’Êí 0, ?¶ 1, —{Žq 2, ‚»‚Ì‚Ü‚Ü -1
+ * Rewrote to make it tidider [Celest]
+ *------------------------------------------
+ */
+int pc_jobchange(struct map_session_data *sd,int job, int upper)
+{
+ int i;
+ int b_class = 0;
+ //?¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+ struct pc_base_job s_class = pc_calc_base_job(sd->status.class_);
+
+ nullpo_retr(0, sd);
+
+ if (job < 0)
+ return 1;
+ if (upper < 0 || upper > 2) //Œ»Ý?¶‚©‚Ç‚¤‚©‚ð”»?‚·‚é
+ upper = s_class.upper;
+
+ b_class = job; //’ÊíE‚È‚çjob‚»‚Ì‚Ü‚ñ‚Ü
+ if (job < JOB_SUPER_NOVICE) {
+ if (upper == 1)
+ b_class += JOB_NOVICE_HIGH;
+ else if (upper == 2) //—{Žq‚ÉŒ‹¥‚Í‚È‚¢‚¯‚Ç‚Ç‚¤‚¹ŽŸ‚ÅR‚ç‚ê‚é‚©‚ç‚¢‚¢‚â
+ b_class += JOB_BABY;
+ } else if (job == JOB_SUPER_NOVICE) {
+ if (upper == 1) //?¶‚ɃXƒpƒmƒr‚Í‘¶Ý‚µ‚È‚¢‚Ì‚Å‚¨?‚è
+ return 1;
+ else if (upper == 2)
+ b_class = JOB_SUPER_BABY;
+ } else if (job < JOB_SUPER_BABY-JOB_NOVICE_HIGH+JOB_SUPER_NOVICE+2) {
+ // Min is SuperNovice +1 -> Becomes Novice High [Skotlex]
+ // Max is SuperBaby-NoviceHigh+1 -> Becomes Super Baby
+ b_class += JOB_NOVICE_HIGH - JOB_SUPER_NOVICE -1;
+ } else if (job >= JOB_TAEKWON && job <= JOB_SOUL_LINKER) {
+ if (upper > 0)
+ return 1;
+ } else if (job < JOB_NOVICE_HIGH || job > JOB_SOUL_LINKER) //Invalid value
+ return 1;
+
+ job = pc_calc_base_job2 (b_class); // check base class [celest]
+
+ if((sd->status.sex == 0 && job == JOB_BARD) || (sd->status.sex == 1 && job == JOB_DANCER))
+ return 1;
+
+ // check if we are changing from 1st to 2nd job
+ if ((job >= JOB_KNIGHT && job <= JOB_CRUSADER2) || (job >= JOB_STAR_GLADIATOR && job <= JOB_SOUL_LINKER)) {
+ if ((s_class.job > JOB_NOVICE && s_class.job < JOB_KNIGHT) || s_class.job == JOB_TAEKWON)
+ sd->change_level = sd->status.job_level;
+ else
+ sd->change_level = 40;
+ }
+ else
+ sd->change_level = 0;
+
+ pc_setglobalreg (sd, "jobchange_level", sd->change_level);
+
+ sd->status.class_ = sd->view_class = b_class;
+ sd->class_ = pc_jobid2mapid(sd->status.class_);
+ sd->status.job_level=1;
+ sd->status.job_exp=0;
+ clif_updatestatus(sd,SP_JOBLEVEL);
+ clif_updatestatus(sd,SP_JOBEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+
+ for(i=0;i<11;i++) {
+ if(sd->equip_index[i] >= 0)
+ if(!pc_isequip(sd,sd->equip_index[i]))
+ pc_unequipitem(sd,sd->equip_index[i],2); // ?”õŠO‚µ
+ }
+
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class); // move sprite update to prevent client crashes with incompatible equipment [Valaris]
+
+ if(battle_config.save_clothcolor &&
+ sd->status.clothes_color > 0 &&
+ ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) || (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ if(battle_config.muting_players && sd->status.manner < 0 && battle_config.manner_system)
+ clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner);
+
+ if(pc_isriding(sd)) { // remove peco status if changing into invalid class [Valaris]
+ if(!(pc_checkskill(sd,KN_RIDING)))
+ pc_setoption(sd,sd->status.option&~OPTION_RIDING);
+ else
+ pc_setriding(sd);
+ }
+
+ status_calc_pc(sd,0);
+ pc_checkallowskill(sd);
+ pc_equiplookall(sd);
+ clif_equiplist(sd);
+ chrif_save(sd,0); //Why are we saving it?
+ chrif_reqfamelist();
+
+ return 0;
+}
+
+/*==========================================
+ * Œ©‚½–Ú?X
+ *------------------------------------------
+ */
+int pc_equiplookall(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+#endif
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+
+ return 0;
+}
+
+/*==========================================
+ * Œ©‚½–Ú?X
+ *------------------------------------------
+ */
+int pc_changelook(struct map_session_data *sd,int type,int val)
+{
+ nullpo_retr(0, sd);
+
+ switch(type){
+ case LOOK_HAIR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_hair_style)
+ val = battle_config.min_hair_style;
+ else if (val > battle_config.max_hair_style)
+ val = battle_config.max_hair_style;
+ if (sd->status.hair != val)
+ {
+ sd->status.hair=val;
+ if (sd->status.guild_id) //Update Guild Window. [Skotlex]
+ intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
+ GMI_HAIR,&sd->status.hair,sizeof(sd->status.hair));
+ }
+ break;
+ case LOOK_WEAPON:
+ sd->status.weapon=val;
+ break;
+ case LOOK_HEAD_BOTTOM:
+ sd->status.head_bottom=val;
+ break;
+ case LOOK_HEAD_TOP:
+ sd->status.head_top=val;
+ break;
+ case LOOK_HEAD_MID:
+ sd->status.head_mid=val;
+ break;
+ case LOOK_HAIR_COLOR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_hair_color)
+ val = battle_config.min_hair_color;
+ else if (val > battle_config.max_hair_color)
+ val = battle_config.max_hair_color;
+ if (sd->status.hair_color != val)
+ {
+ sd->status.hair_color=val;
+ if (sd->status.guild_id) //Update Guild Window. [Skotlex]
+ intif_guild_change_memberinfo(sd->status.guild_id,sd->status.account_id,sd->status.char_id,
+ GMI_HAIR_COLOR,&sd->status.hair_color,sizeof(sd->status.hair_color));
+ }
+ break;
+ case LOOK_CLOTHES_COLOR: //Use the battle_config limits! [Skotlex]
+ if (val < battle_config.min_cloth_color)
+ val = battle_config.min_cloth_color;
+ else if (val > battle_config.max_cloth_color)
+ val = battle_config.max_cloth_color;
+ sd->status.clothes_color=val;
+ break;
+ case LOOK_SHIELD:
+ sd->status.shield=val;
+ break;
+ case LOOK_SHOES:
+ break;
+ }
+
+ if((type==LOOK_CLOTHES_COLOR) && ((sd->view_class==JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && battle_config.xmas_ignorepalette)))
+ return 0;
+
+ clif_changelook(&sd->bl,type,val);
+
+ return 0;
+}
+
+/*==========================================
+ * •t?•i(‘é,ƒyƒR,ƒJ?ƒg)Ý’è
+ *------------------------------------------
+ */
+int pc_setoption(struct map_session_data *sd,int type)
+{
+ nullpo_retr(0, sd);
+ if (type&OPTION_RIDING && !(sd->status.option&OPTION_RIDING) && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN)
+ { //We are going to mount. [Skotlex]
+ switch (sd->status.class_)
+ {
+ case JOB_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_KNIGHT2;
+ break;
+ case JOB_CRUSADER:
+ sd->status.class_ = sd->view_class = JOB_CRUSADER2;
+ break;
+ case JOB_LORD_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_LORD_KNIGHT2;
+ break;
+ case JOB_PALADIN:
+ sd->status.class_ = sd->view_class = JOB_PALADIN2;
+ break;
+ case JOB_BABY_KNIGHT:
+ sd->status.class_ = sd->view_class = JOB_BABY_KNIGHT2;
+ break;
+ case JOB_BABY_CRUSADER:
+ sd->status.class_ = sd->view_class = JOB_BABY_CRUSADER2;
+ break;
+ }
+ clif_status_load(&sd->bl,SI_RIDING,1);
+ status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds.
+ }
+ else if (!(type&OPTION_RIDING) && sd->status.option&OPTION_RIDING && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN)
+ { //We are going to dismount.
+ switch (sd->status.class_)
+ {
+ case JOB_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_KNIGHT;
+ break;
+ case JOB_CRUSADER2:
+ sd->status.class_ = sd->view_class = JOB_CRUSADER;
+ break;
+ case JOB_LORD_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_LORD_KNIGHT;
+ break;
+ case JOB_PALADIN2:
+ sd->status.class_ = sd->view_class = JOB_PALADIN;
+ break;
+ case JOB_BABY_KNIGHT2:
+ sd->status.class_ = sd->view_class = JOB_BABY_KNIGHT;
+ break;
+ case JOB_BABY_CRUSADER2:
+ sd->status.class_ = sd->view_class = JOB_BABY_CRUSADER;
+ break;
+ }
+ clif_status_load(&sd->bl,SI_RIDING,0);
+ status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds.
+ }
+ if (type&OPTION_FALCON && !(sd->status.option&OPTION_FALCON)) //Falcon ON
+ clif_status_load(&sd->bl,SI_FALCON,1);
+ else if (!(type&OPTION_FALCON) && sd->status.option&OPTION_FALCON) //Falcon OFF
+ clif_status_load(&sd->bl,SI_FALCON,0);
+
+ //SG flying [Komurka]
+ if (type&OPTION_FLYING && !(sd->status.option&OPTION_FLYING)) //Flying ON
+ {
+ if (sd->status.class_==JOB_STAR_GLADIATOR) sd->status.class_ = sd->view_class = JOB_STAR_GLADIATOR2;
+ }
+ else if (!(type&OPTION_FLYING) && sd->status.option&OPTION_FLYING) //Flying OFF
+ {
+ if (sd->status.class_==JOB_STAR_GLADIATOR2) sd->status.class_ = sd->view_class = JOB_STAR_GLADIATOR;
+ }
+
+ sd->status.option=type;
+ clif_changeoption(&sd->bl);
+ status_calc_pc(sd,0);
+ return 0;
+}
+
+/*==========================================
+ * ƒJ?ƒgÝ’è
+ *------------------------------------------
+ */
+int pc_setcart(struct map_session_data *sd,int type)
+{
+ int cart[6]={0x0000,0x0008,0x0080,0x0100,0x0200,0x0400};
+ int option, i;
+ nullpo_retr(0, sd);
+
+ if (type < 0 || type > 5)
+ return 0; //Never trust the values sent by the client! [Skotlex]
+
+ option = sd->status.option;
+ for (i = 0; i < 6; i++)
+ { //This should preserve the current option, only modifying the cart bit.
+ if (i == type)
+ option |= cart[i];
+ else
+ option &= ~cart[i];
+ }
+ if(pc_checkskill(sd,MC_PUSHCART)>0){ // ƒvƒbƒVƒ…ƒJ?ƒgƒXƒLƒ‹ŠŽ
+ if(!pc_iscarton(sd)){ // ƒJ?ƒg‚ð•t‚¯‚Ä‚¢‚È‚¢
+ pc_setoption(sd,option);
+ clif_cart_itemlist(sd);
+ clif_cart_equiplist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ clif_status_change(&sd->bl,SI_INCREASEAGI,0); //0x0c is 12, Increase Agi??
+ }
+ else{
+ pc_setoption(sd,option);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ‘éÝ’è
+ *------------------------------------------
+ */
+int pc_setfalcon(struct map_session_data *sd)
+{
+ if(pc_checkskill(sd,HT_FALCON)>0){ // ƒtƒ@ƒ‹ƒRƒ“ƒ}ƒXƒ^ƒŠ?ƒXƒLƒ‹ŠŽ
+ pc_setoption(sd,sd->status.option|0x0010);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒyƒRƒyƒRÝ’è
+ *------------------------------------------
+ */
+int pc_setriding(struct map_session_data *sd)
+{
+ if((pc_checkskill(sd,KN_RIDING)>0)){ // ƒ‰ƒCƒfƒBƒ“ƒOƒXƒLƒ‹ŠŽ
+ pc_setoption(sd,sd->status.option|OPTION_RIDING);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€ƒhƒƒbƒv‰Â•s‰Â”»’è
+ *------------------------------------------
+ */
+int pc_candrop(struct map_session_data *sd,int item_id)
+{
+ int level = pc_isGM(sd);
+ if ( pc_can_give_items(level) ) //check if this GM level can drop items
+ return 0;
+ return (itemdb_isdropable(item_id, level));
+}
+
+/*==========================================
+ * script—p??‚Ì’l‚ð?‚Þ
+ *------------------------------------------
+ */
+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—p??‚Ì’l‚ðÝ’è
+ *------------------------------------------
+ */
+int pc_setreg(struct map_session_data *sd,int reg,int val)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for (i = 0; i < sd->reg_num; i++) {
+ if (sd->reg[i].index == reg){
+ sd->reg[i].data = val;
+ return 0;
+ }
+ }
+ sd->reg_num++;
+ sd->reg = (struct script_reg *) aRealloc(sd->reg, sizeof(*(sd->reg)) * sd->reg_num);
+ memset(sd->reg + (sd->reg_num - 1), 0, sizeof(struct script_reg));
+ sd->reg[i].index = reg;
+ sd->reg[i].data = val;
+
+ return 0;
+}
+
+/*==========================================
+ * script—p•¶Žš—ñ??‚Ì’l‚ð?‚Þ
+ *------------------------------------------
+ */
+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—p•¶Žš—ñ??‚Ì’l‚ðÝ’è
+ *------------------------------------------
+ */
+int pc_setregstr(struct map_session_data *sd,int reg,char *str)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(strlen(str)+1 >= sizeof(sd->regstr[0].data)){
+ ShowWarning("pc_setregstr: string too long !\n");
+ return 0;
+ }
+
+ for(i=0;i<sd->regstr_num;i++)
+ if(sd->regstr[i].index==reg){
+ strcpy(sd->regstr[i].data,str);
+ return 0;
+ }
+
+ sd->regstr_num++;
+ sd->regstr = (struct script_regstr *) aRealloc(sd->regstr, sizeof(sd->regstr[0]) * sd->regstr_num);
+ if(sd->regstr==NULL){
+ ShowFatalError("out of memory : pc_setreg\n");
+ exit(1);
+ }
+ memset(sd->regstr + (sd->regstr_num - 1), 0, sizeof(struct script_regstr));
+ sd->regstr[i].index = reg;
+ strcpy(sd->regstr[i].data, str);
+
+ return 0;
+}
+
+int pc_readregistry(struct map_session_data *sd,char *reg,int type) {
+ struct global_reg *sd_reg;
+ int i,max;
+
+ nullpo_retr(0, sd);
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = sd->save_reg.global_num;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = sd->save_reg.account_num;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = sd->save_reg.account2_num;
+ break;
+ default:
+ return 0;
+ }
+ if (max == -1) {
+ if (battle_config.error_log)
+ ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type);
+ //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
+ intif_request_registry(sd,type==3?4:type);
+ return 0;
+ }
+ for(i=0;i<max;i++){
+ if(strcmp(sd_reg[i].str,reg)==0)
+ return atoi(sd_reg[i].value);
+ }
+ return 0;
+}
+
+char* pc_readregistry_str(struct map_session_data *sd,char *reg,int type) {
+ struct global_reg *sd_reg;
+ int i,max;
+
+ nullpo_retr(0, sd);
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = sd->save_reg.global_num;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = sd->save_reg.account_num;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = sd->save_reg.account2_num;
+ break;
+ default:
+ return NULL;
+ }
+ if (max == -1) {
+ if (battle_config.error_log)
+ ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type);
+ //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
+ intif_request_registry(sd,type==3?4:type);
+ return NULL;
+ }
+ for(i=0;i<max;i++){
+ if(strcmp(sd_reg[i].str,reg)==0)
+ return sd_reg[i].value;
+ }
+ return NULL;
+}
+
+int pc_setregistry(struct map_session_data *sd,char *reg,int val,int type) {
+ struct global_reg *sd_reg;
+ int i,*max, regmax;
+
+ nullpo_retr(0, sd);
+ if (type == 3) { //Some special character reg values...
+ if(strcmp(reg,"PC_DIE_COUNTER") == 0 && sd->die_counter != val){
+ sd->die_counter = val;
+ // status_calc_pc(sd,0); //I doubt this is needed....
+ } else if(strcmp(reg,script_config.die_event_name) == 0){
+ sd->state.event_death = val;
+ } else if(strcmp(reg,script_config.kill_event_name) == 0){
+ sd->state.event_kill = val;
+ } else if(strcmp(reg,script_config.logout_event_name) == 0){
+ sd->state.event_disconnect = val;
+ }
+ }
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = &sd->save_reg.global_num;
+ regmax = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = &sd->save_reg.account_num;
+ regmax = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = &sd->save_reg.account2_num;
+ regmax = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ return 0;
+ }
+ if (*max == -1) {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : refusing to set %s (type %d) until vars are received.\n", reg, type);
+ return 1;
+ }
+
+ // delete reg
+ if (val == 0) {
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ if (i != *max - 1)
+ memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg));
+ memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg));
+ (*max)--;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ break;
+ }
+ }
+ return 0;
+ }
+ // change value if found
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ sprintf(sd_reg[i].value, "%d", val);
+ sd->state.reg_dirty |= 1<<(type-1);
+ return 0;
+ }
+ }
+
+ // add value if not found
+ if (i < regmax) {
+ memset(&sd_reg[i], 0, sizeof(struct global_reg));
+ strncpy(sd_reg[i].str, reg, 32);
+ sprintf(sd_reg[i].value, "%d", val);
+ (*max)++;
+ sd->state.reg_dirty |= 1<<(type-1);
+ return 0;
+ }
+
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
+
+ return 1;
+}
+
+int pc_setregistry_str(struct map_session_data *sd,char *reg,char *val,int type) {
+ struct global_reg *sd_reg;
+ int i,*max, regmax;
+
+ nullpo_retr(0, sd);
+ if (reg[strlen(reg)-1] != '$') {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry_str : reg %s must be string (end in '$') to use this!\n", reg);
+ return 1;
+ }
+
+ switch (type) {
+ case 3: //Char reg
+ sd_reg = sd->save_reg.global;
+ max = &sd->save_reg.global_num;
+ regmax = GLOBAL_REG_NUM;
+ break;
+ case 2: //Account reg
+ sd_reg = sd->save_reg.account;
+ max = &sd->save_reg.account_num;
+ regmax = ACCOUNT_REG_NUM;
+ break;
+ case 1: //Account2 reg
+ sd_reg = sd->save_reg.account2;
+ max = &sd->save_reg.account2_num;
+ regmax = ACCOUNT_REG2_NUM;
+ break;
+ default:
+ return 0;
+ }
+ if (*max == -1) {
+ if(battle_config.error_log)
+ ShowError("pc_setregistry_str : refusing to set %s (type %d) until vars are received.\n", reg, type);
+ return 1;
+ }
+
+ // delete reg
+ if (strcmp(val,"")==0) {
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ if (i != *max - 1)
+ memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg));
+ memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg));
+ (*max)--;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ break;
+ }
+ }
+ return 0;
+ }
+ // change value if found
+ for(i = 0; i < *max; i++) {
+ if (strcmp(sd_reg[i].str, reg) == 0) {
+ strncpy(sd_reg[i].value, val, 256);
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ return 0;
+ }
+ }
+
+ // add value if not found
+ if (i < regmax) {
+ memset(&sd_reg[i], 0, sizeof(struct global_reg));
+ strncpy(sd_reg[i].str, reg, 32);
+ strncpy(sd_reg[i].value, val, 256);
+ (*max)++;
+ sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved"
+ if (type!=3) intif_saveregistry(sd,type);
+ return 0;
+ }
+
+ if(battle_config.error_log)
+ ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
+
+ return 1;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}??—
+ *------------------------------------------
+ */
+int pc_eventtimer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ char *p = (char *)data;
+ int i;
+ if(sd==NULL)
+ return 0;
+
+ for(i=0;i < MAX_EVENTTIMER;i++){
+ if( sd->eventtimer[i]==tid ){
+ sd->eventtimer[i]=-1;
+ npc_event(sd,p,0);
+ break;
+ }
+ }
+ if (p) aFree(p);
+ if(i==MAX_EVENTTIMER) {
+ if(battle_config.error_log)
+ ShowError("pc_eventtimer: no such event timer\n");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}?’ljÁ
+ *------------------------------------------
+ */
+int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]==-1 )
+ break;
+ if(i<MAX_EVENTTIMER){
+ char *evname = aStrdup(name);
+ //char *evname=(char *)aMallocA((strlen(name)+1)*sizeof(char));
+ //memcpy(evname,name,(strlen(name)+1));
+ sd->eventtimer[i]=add_timer(gettick()+tick,
+ pc_eventtimer,sd->bl.id,(int)evname);
+ sd->eventcount++;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}?íœ
+ *------------------------------------------
+ */
+int pc_deleventtimer(struct map_session_data *sd,const char *name)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if (sd->eventcount <= 0)
+ return 0;
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]!=-1 ) {
+ char *p = (char *)(get_timer(sd->eventtimer[i])->data);
+ if(p && strcmp(p, name)==0) {
+ delete_timer(sd->eventtimer[i],pc_eventtimer);
+ sd->eventtimer[i]=-1;
+ sd->eventcount--;
+ aFree(p);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}?ƒJƒEƒ“ƒg’l’ljÁ
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}?‘Síœ
+ *------------------------------------------
+ */
+int pc_cleareventtimer(struct map_session_data *sd)
+{
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if (sd->eventcount <= 0)
+ return 0;
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( sd->eventtimer[i]!=-1 ){
+ char *p = (char *)(get_timer(sd->eventtimer[i])->data);
+ delete_timer(sd->eventtimer[i],pc_eventtimer);
+ sd->eventtimer[i]=-1;
+ if (p) aFree(p);
+ }
+
+ return 0;
+}
+
+//
+// ? ”õ•¨
+//
+/*==========================================
+ * ƒAƒCƒeƒ€‚ð?”õ‚·‚é
+ *------------------------------------------
+ */
+int pc_equipitem(struct map_session_data *sd,int n,int pos)
+{
+ int i,nameid, arrow;
+ struct item_data *id;
+ //?¶‚â—{Žq‚Ìꇂ̌³‚ÌE‹Æ‚ðŽZo‚·‚é
+
+ nullpo_retr(0, sd);
+
+ nameid = sd->status.inventory[n].nameid;
+ id = sd->inventory_data[n];
+ pos = pc_equippoint(sd,n);
+ if(battle_config.battle_log)
+ ShowInfo("equip %d(%d) %x:%x\n",nameid,n,id->equip,pos);
+ if(!pc_isequip(sd,n) || !pos || sd->status.inventory[n].attribute==1 ) { // [Valaris]
+ clif_equipitemack(sd,n,0,0); // fail
+ return 0;
+ }
+
+// -- moonsoul (if player is berserk then cannot equip)
+//
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1){
+ clif_equipitemack(sd,n,0,0); // fail
+ return 0;
+ }
+
+ if(pos==0x88){ // ƒAƒNƒZƒTƒŠ—p—áŠO?—
+ 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) // ˆê?A?”õ—v‹‰ÓŠ‚ª“ñ“—¬•Ší‚©ƒ`ƒFƒbƒN‚·‚é
+ && (id->equip==2) // ? Žè•Ší
+ && (pc_checkskill(sd, AS_LEFT) > 0 || (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN) ) // ¶ŽèC?—L
+ {
+ int tpos=0;
+ if(sd->equip_index[8] >= 0)
+ tpos |= sd->status.inventory[sd->equip_index[8]].equip;
+ if(sd->equip_index[9] >= 0)
+ tpos |= sd->status.inventory[sd->equip_index[9]].equip;
+ tpos &= 0x02;
+ pos = tpos == 0x02 ? 0x20 : 0x02;
+ }
+
+ arrow=pc_search_inventory(sd,pc_checkequip(sd,9)); // Added by RoVeRT
+ for(i=0;i<11;i++) {
+ if(sd->equip_index[i] >= 0 && sd->status.inventory[sd->equip_index[i]].equip&pos) {
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+ }
+ // ‹|–î?”õ
+ if(pos==0x8000){
+ clif_arrowequip(sd,n);
+ clif_arrow_fail(sd,3); // 3=–?”õ‚Å‚«‚Ü‚µ‚½
+ }
+ else
+ clif_equipitemack(sd,n,pos,1);
+
+ for(i=0;i<11;i++) {
+ if(pos & equip_pos[i])
+ sd->equip_index[i] = n;
+ }
+ sd->status.inventory[n].equip=pos;
+
+ if(sd->status.inventory[n].equip & 0x0002) {
+ if(sd->inventory_data[n])
+ sd->weapontype1 = sd->inventory_data[n]->look;
+ else
+ sd->weapontype1 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ }
+ if(sd->status.inventory[n].equip & 0x0020) {
+ if(sd->inventory_data[n]) {
+ if(sd->inventory_data[n]->type == 4) {
+ sd->status.shield = 0;
+ if(sd->status.inventory[n].equip == 0x0020)
+ sd->weapontype2 = sd->inventory_data[n]->look;
+ else
+ sd->weapontype2 = 0;
+ }
+ else if(sd->inventory_data[n]->type == 5) {
+ sd->status.shield = sd->inventory_data[n]->look;
+ sd->weapontype2 = 0;
+ }
+ }
+ else
+ sd->status.shield = sd->weapontype2 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+ }
+ if(sd->status.inventory[n].equip & 0x0001) {
+ if(sd->inventory_data[n])
+ sd->status.head_bottom = sd->inventory_data[n]->look;
+ else
+ sd->status.head_bottom = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ }
+ if(sd->status.inventory[n].equip & 0x0100) {
+ if(sd->inventory_data[n])
+ sd->status.head_top = sd->inventory_data[n]->look;
+ else
+ sd->status.head_top = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ }
+ if(sd->status.inventory[n].equip & 0x0200) {
+ if(sd->inventory_data[n])
+ sd->status.head_mid = sd->inventory_data[n]->look;
+ else
+ sd->status.head_mid = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+ }
+ if(sd->status.inventory[n].equip & 0x0040)
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+
+ pc_checkallowskill(sd); // ?”õ•i‚ŃXƒLƒ‹‚©‰ðœ‚³‚ê‚é‚©ƒ`ƒFƒbƒN
+ if (itemdb_look(sd->status.inventory[n].nameid) == 11 && (arrow >= 0)){ // Added by RoVeRT
+ clif_arrowequip(sd,arrow);
+ sd->status.inventory[arrow].equip=32768;
+ }
+ status_calc_pc(sd,0);
+
+ if(sd->special_state.infinite_endure) {
+ if(sd->sc_data[SC_ENDURE].timer == -1)
+ status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0);
+ }
+ else {
+ if(sd->sc_count && sd->sc_data[SC_ENDURE].timer != -1 && sd->sc_data[SC_ENDURE].val2)
+ status_change_end(&sd->bl,SC_ENDURE,-1);
+ }
+
+ if(sd->sc_count) {
+ if (sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ? ”õ‚µ‚½•¨‚ðŠO‚·
+ * type:
+ * 0 - only unequip
+ * 1 - calculate status after unequipping
+ * 2 - force unequip
+ *------------------------------------------
+ */
+int pc_unequipitem(struct map_session_data *sd,int n,int flag)
+{
+ short hp = 0, sp = 0;
+ nullpo_retr(0, sd);
+
+// -- moonsoul (if player is berserk then cannot unequip)
+//
+ if(!(flag&2) && sd->sc_count && (sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1)){
+ clif_unequipitemack(sd,n,0,0);
+ return 0;
+ }
+
+ if(battle_config.battle_log)
+ ShowInfo("unequip %d %x:%x\n",n,pc_equippoint(sd,n),sd->status.inventory[n].equip);
+ if(sd->status.inventory[n].equip){
+ int i;
+ for(i=0;i<11;i++) {
+ if(sd->status.inventory[n].equip & equip_pos[i]) {
+ sd->equip_index[i] = -1;
+ if(sd->unequip_losehp[i] > 0) {
+ hp += sd->unequip_losehp[i];
+ sd->unequip_losehp[i] = 0;
+ }
+ if(sd->unequip_losesp[i] > 0) {
+ sp += sd->unequip_losesp[i];
+ sd->unequip_losesp[i] = 0;
+ }
+ }
+ }
+ if(sd->status.inventory[n].equip & 0x0002) {
+ sd->weapontype1 = 0;
+ sd->status.weapon = sd->weapontype2;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ if(sd->sc_data[SC_DANCING].timer!=-1) //When unequipping, stop dancing. [Skotlex]
+ skill_stop_dancing(&sd->bl);
+ }
+ if(sd->status.inventory[n].equip & 0x0020) {
+ sd->status.shield = sd->weapontype2 = 0;
+ pc_calcweapontype(sd);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+ }
+ if(sd->status.inventory[n].equip & 0x0001) {
+ sd->status.head_bottom = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
+ }
+ if(sd->status.inventory[n].equip & 0x0100) {
+ sd->status.head_top = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
+ }
+ if(sd->status.inventory[n].equip & 0x0200) {
+ sd->status.head_mid = 0;
+ clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid);
+ }
+ if(sd->status.inventory[n].equip & 0x0040)
+ clif_changelook(&sd->bl,LOOK_SHOES,0);
+
+ clif_unequipitemack(sd,n,sd->status.inventory[n].equip,1);
+ sd->status.inventory[n].equip=0;
+ if(flag&1)
+ pc_checkallowskill(sd);
+ if(sd->weapontype1 == 0 && sd->weapontype2 == 0)
+ skill_enchant_elemental_end(&sd->bl,-1); //•ŠíŽ‚¿¾‚¦‚Í–³?Œ‚Å?«•t?‰ðœ
+ } else {
+ clif_unequipitemack(sd,n,0,0);
+ }
+
+ if(flag&1) {
+ status_calc_pc(sd,0);
+ if(sd->sc_count && sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele))
+ status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1);
+ }
+
+ if (hp > 0 || sp > 0) {
+ if (hp > sd->status.hp)
+ hp = sd->status.hp;
+ if (sp > sd->status.sp)
+ sp = sd->status.sp;
+ pc_heal(sd,-hp,-sp);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚Ìindex”Ô?‚ð‹l‚ß‚½‚è
+ * ? ”õ•i‚Ì?”õ‰Â”\ƒ`ƒFƒbƒN‚ðs‚È‚¤
+ *------------------------------------------
+ */
+int pc_checkitem(struct map_session_data *sd)
+{
+ int i,j,k,id,calc_flag = 0;
+ struct item_data *it=NULL;
+
+ nullpo_retr(0, sd);
+
+ if (sd->vender_id) //Avoid reorganizing items when we are vending, as that leads to exploits (pointed out by End of Exam)
+ return 0;
+
+ // ŠŽ•i‹ó‚«‹l‚ß
+ for(i=j=0;i<MAX_INVENTORY;i++){
+ if( (id=sd->status.inventory[i].nameid)==0)
+ continue;
+ if( battle_config.item_check && !itemdb_available(id) ){
+ if(battle_config.error_log)
+ ShowWarning("illegal item id %d in %d[%s] inventory.\n",id,sd->bl.id,sd->status.name);
+ pc_delitem(sd,i,sd->status.inventory[i].amount,3);
+ continue;
+ }
+ if(i>j){
+ memcpy(&sd->status.inventory[j],&sd->status.inventory[i],sizeof(struct item));
+ sd->inventory_data[j] = sd->inventory_data[i];
+ }
+ j++;
+ }
+ if(j < MAX_INVENTORY)
+ memset(&sd->status.inventory[j],0,sizeof(struct item)*(MAX_INVENTORY-j));
+ for(k=j;k<MAX_INVENTORY;k++)
+ sd->inventory_data[k] = NULL;
+
+ // ƒJ?ƒg?‹ó‚«‹l‚ß
+ for(i=j=0;i<MAX_CART;i++){
+ if( (id=sd->status.cart[i].nameid)==0 )
+ continue;
+ if( battle_config.item_check && !itemdb_available(id) ){
+ if(battle_config.error_log)
+ ShowWarning("illeagal item id %d in %d[%s] cart.\n",id,sd->bl.id,sd->status.name);
+ pc_cart_delitem(sd,i,sd->status.cart[i].amount,1);
+ continue;
+ }
+ if(i>j){
+ memcpy(&sd->status.cart[j],&sd->status.cart[i],sizeof(struct item));
+ }
+ j++;
+ }
+ if(j < MAX_CART)
+ memset(&sd->status.cart[j],0,sizeof(struct item)*(MAX_CART-j));
+
+ // ? ”õˆÊ’uƒ`ƒFƒbƒN
+
+ 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;
+ }
+ //?”õ§ŒÀƒ`ƒFƒbƒN
+ if(sd->status.inventory[i].equip && (map[sd->bl.m].flag.pvp||map[sd->bl.m].flag.gvg) && (it->flag.no_equip&1)){//PVP check for forbiden items. optimized by [Lupus]
+ sd->status.inventory[i].equip=0;
+ calc_flag = 1;
+ }else if(sd->status.inventory[i].equip && map_flag_gvg(sd->bl.m) && (it->flag.no_equip>1)){//GvG optimized by [Lupus]
+ sd->status.inventory[i].equip=0;
+ calc_flag = 1;
+ }
+ }
+
+ pc_setequipindex(sd);
+ if(calc_flag)
+ status_calc_pc(sd,2);
+
+ return 0;
+}
+
+int pc_checkoverhp(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.hp == sd->status.max_hp)
+ return 1;
+ if(sd->status.hp > sd->status.max_hp) {
+ sd->status.hp = sd->status.max_hp;
+ clif_updatestatus(sd,SP_HP);
+ return 2;
+ }
+
+ return 0;
+}
+
+int pc_checkoversp(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.sp == sd->status.max_sp)
+ return 1;
+ if(sd->status.sp > sd->status.max_sp) {
+ sd->status.sp = sd->status.max_sp;
+ clif_updatestatus(sd,SP_SP);
+ return 2;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * PVP‡ˆÊŒvŽZ—p(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‡ˆÊŒvŽZ
+ *------------------------------------------
+ */
+int pc_calc_pvprank(struct map_session_data *sd)
+{
+ int old;
+ struct map_data *m;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, m=&map[sd->bl.m]);
+
+ old=sd->pvp_rank;
+
+ if( !(m->flag.pvp) )
+ return 0;
+ sd->pvp_rank=1;
+ map_foreachinmap(pc_calc_pvprank_sub,sd->bl.m,BL_PC,sd);
+ if(old!=sd->pvp_rank || sd->pvp_lastusers!=m->users)
+ clif_pvpset(sd,sd->pvp_rank,sd->pvp_lastusers=m->users,0);
+ return sd->pvp_rank;
+}
+/*==========================================
+ * PVP‡ˆÊŒvŽZ(timer)
+ *------------------------------------------
+ */
+int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=NULL;
+ if(battle_config.pk_mode) // disable pvp ranking if pk_mode on [Valaris]
+ return 0;
+
+ sd=map_id2sd(id);
+ if(sd==NULL)
+ return 0;
+ sd->pvp_timer=-1;
+ if( pc_calc_pvprank(sd)>0 )
+ sd->pvp_timer=add_timer(
+ gettick()+PVP_CALCRANK_INTERVAL,
+ pc_calc_pvprank_timer,id,data);
+ return 0;
+}
+
+/*==========================================
+ * sd‚ÍŒ‹¥‚µ‚Ä‚¢‚é‚©(?¥‚Ìꇂ͑Š•û‚Ìchar_id‚ð•Ô‚·)
+ *------------------------------------------
+ */
+int pc_ismarried(struct map_session_data *sd)
+{
+ if(sd == NULL)
+ return -1;
+ if(sd->status.partner_id > 0)
+ return sd->status.partner_id;
+ else
+ return 0;
+}
+/*==========================================
+ * sd‚ªdstsd‚ÆŒ‹¥(dstsd¨sd‚ÌŒ‹¥?—‚à“¯ŽbÉs‚¤)
+ *------------------------------------------
+ */
+int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd)
+{
+ if(sd == NULL || dstsd == NULL ||
+ sd->status.partner_id > 0 || dstsd->status.partner_id > 0 ||
+ sd->class_&JOBL_BABY)
+ return -1;
+ sd->status.partner_id = dstsd->status.char_id;
+ dstsd->status.partner_id = sd->status.char_id;
+ return 0;
+}
+
+/*==========================================
+ * sd‚ª—£¥(‘ŠŽè‚Ísd->status.partner_id‚Ɉ˂é)(‘ŠŽè‚à“¯ŽbÉ—£¥?Œ‹¥Žw—ÖŽ©“®?’D)
+ *------------------------------------------
+ */
+int pc_divorce(struct map_session_data *sd)
+{
+ struct map_session_data *p_sd;
+ if (sd == NULL || !pc_ismarried(sd))
+ return -1;
+
+ if ((p_sd = map_charid2sd(sd->status.partner_id)) != NULL) {
+ int i;
+ if (p_sd->status.partner_id != sd->status.char_id || sd->status.partner_id != p_sd->status.char_id) {
+ ShowWarning("pc_divorce: Illegal partner_id sd=%d p_sd=%d\n", sd->status.partner_id, p_sd->status.partner_id);
+ return -1;
+ }
+ sd->status.partner_id = 0;
+ p_sd->status.partner_id = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(sd, i, 1, 0);
+ if (p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(p_sd, i, 1, 0);
+ }
+ clif_divorced(sd, p_sd->status.name);
+ clif_divorced(p_sd, sd->status.name);
+ } else {
+ ShowError("pc_divorce: p_sd nullpo\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * sd - father dstsd - mother jasd - child
+ */
+int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd, struct map_session_data *jasd)
+{
+ int j;
+ if (sd == NULL || dstsd == NULL || jasd == NULL ||
+ sd->status.partner_id <= 0 || dstsd->status.partner_id <= 0 ||
+ sd->status.partner_id != dstsd->status.char_id || dstsd->status.partner_id != sd->status.char_id ||
+ sd->status.child > 0 || dstsd->status.child || jasd->status.father > 0 || jasd->status.mother > 0)
+ return -1;
+ jasd->status.father = sd->status.char_id;
+ jasd->status.mother = dstsd->status.char_id;
+ sd->status.child = jasd->status.char_id;
+ dstsd->status.child = jasd->status.char_id;
+
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(jasd->status.inventory[j].nameid>0 && jasd->status.inventory[j].equip!=0)
+ pc_unequipitem(jasd, j, 3);
+ }
+ if (pc_jobchange(jasd, 4023, 0) == 0)
+ { //Success, and give Junior the Baby skills. [Skotlex]
+ pc_skill(jasd,WE_BABY,1,0);
+ pc_skill(jasd,WE_CALLPARENT,1,0);
+ clif_displaymessage(jasd->fd, msg_txt(12)); // Your job has been changed.
+ //We should also grant the parent skills to the parents [Skotlex]
+ pc_skill(sd,WE_CALLBABY,1,0);
+ pc_skill(dstsd,WE_CALLBABY,1,0);
+ } else {
+ clif_displaymessage(jasd->fd, msg_txt(155)); // Impossible to change your job.
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * sd‚Ì‘Š•û‚Ìmap_session_data‚ð•Ô‚·
+ *------------------------------------------
+ */
+struct map_session_data *pc_get_partner(struct map_session_data *sd)
+{
+ //struct map_session_data *p_sd = NULL;
+ //char *nick;
+ //if(sd == NULL || !pc_ismarried(sd))
+ // return NULL;
+ //nick=map_charid2nick(sd->status.partner_id);
+ //if (nick==NULL)
+ // return NULL;
+ //if((p_sd=map_nick2sd(nick)) == NULL )
+ // return NULL;
+
+ if (sd && pc_ismarried(sd))
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.partner_id);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_father (struct map_session_data *sd)
+{
+ if (sd && sd->class_&JOBL_BABY && sd->status.father > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.father);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_mother (struct map_session_data *sd)
+{
+ if (sd && sd->class_&JOBL_BABY && sd->status.mother > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.mother);
+
+ return NULL;
+}
+
+struct map_session_data *pc_get_child (struct map_session_data *sd)
+{
+ if (sd && pc_ismarried(sd) && sd->status.child > 0)
+ // charid2sd returns NULL if not found
+ return map_charid2sd(sd->status.child);
+
+ return NULL;
+}
+
+//
+// Ž©‘R‰ñ•œ•¨
+//
+/*==========================================
+ * SP‰ñ•œ—ÊŒvŽZ
+ *------------------------------------------
+ */
+static int natural_heal_tick,natural_heal_prev_tick,natural_heal_diff_tick;
+static int pc_spheal(struct map_session_data *sd)
+{
+ int a = natural_heal_diff_tick;
+
+ nullpo_retr(0, sd);
+
+ if(pc_issit(sd))
+ a += a;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MAGNIFICAT].timer!=-1) // ƒ}ƒOƒjƒtƒBƒJ?ƒg
+ a += a;
+ if (sd->sc_data[SC_REGENERATION].timer != -1)
+ a *= sd->sc_data[SC_REGENERATION].val1;
+ }
+ // Re-added back to status_calc
+ //if((skill = pc_checkskill(sd,HP_MEDITATIO)) > 0) //Increase natural SP regen with Meditatio [DracoRPG]
+ //a += a*skill*3/100;
+
+ if (sd->status.guild_id > 0) {
+ struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); // Increased guild castle regen [Valaris]
+ if(gc) {
+ struct guild *g = guild_search(sd->status.guild_id);
+ if(g && g->guild_id == gc->guild_id)
+ a += a;
+ } // end addition [Valaris]
+ }
+
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKREGEN))
+ a += a;
+
+ return a;
+}
+
+/*==========================================
+ * HP‰ñ•œ—ÊŒvŽZ
+ *------------------------------------------
+ */
+static int pc_hpheal(struct map_session_data *sd)
+{
+ int a = natural_heal_diff_tick;
+
+ nullpo_retr(0, sd);
+
+ if(pc_issit(sd))
+ a += a;
+ if (sd->sc_count) {
+ if (sd->sc_data[SC_MAGNIFICAT].timer != -1) // Modified by RoVeRT
+ a += a;
+ if (sd->sc_data[SC_REGENERATION].timer != -1)
+ a *= sd->sc_data[SC_REGENERATION].val1;
+ }
+ if (sd->status.guild_id > 0) {
+ struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); // Increased guild castle regen [Valaris]
+ if(gc) {
+ struct guild *g = guild_search(sd->status.guild_id);
+ if(g && g->guild_id == gc->guild_id)
+ a += a;
+ } // end addition [Valaris]
+ }
+
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKREGEN))
+ a += a;
+
+ return a;
+}
+
+static int pc_natural_heal_hp(struct map_session_data *sd)
+{
+ int bhp;
+ int inc_num,bonus,hp_flag;
+
+ nullpo_retr(0, sd);
+
+ if (sd->no_regen & 1)
+ return 0;
+
+ if(pc_checkoverhp(sd)) {
+ sd->hp_sub = sd->inchealhptick = 0;
+ return 0;
+ }
+
+ bhp=sd->status.hp;
+ hp_flag = (pc_checkskill(sd,SM_MOVINGRECOVERY) > 0 && sd->walktimer != -1);
+
+ if(sd->walktimer == -1) {
+ inc_num = pc_hpheal(sd);
+ if(sd->sc_data[SC_TENSIONRELAX].timer!=-1 ){ // ƒeƒ“ƒVƒ‡ƒ“ƒŠƒ‰ƒbƒNƒX
+ sd->hp_sub += 2*inc_num;
+ sd->inchealhptick += 3*natural_heal_diff_tick;
+ } else {
+ sd->hp_sub += inc_num;
+ sd->inchealhptick += natural_heal_diff_tick;
+ }
+ }
+ else if(hp_flag) {
+ inc_num = pc_hpheal(sd);
+ sd->hp_sub += inc_num;
+ sd->inchealhptick = 0;
+ }
+ else {
+ sd->hp_sub = sd->inchealhptick = 0;
+ return 0;
+ }
+
+ if(sd->hp_sub >= battle_config.natural_healhp_interval) {
+ bonus = sd->nhealhp;
+ if(hp_flag) {
+ bonus >>= 2;
+ if(bonus <= 0) bonus = 1;
+ }
+ while(sd->hp_sub >= battle_config.natural_healhp_interval) {
+ sd->hp_sub -= battle_config.natural_healhp_interval;
+ if(sd->status.hp + bonus <= sd->status.max_hp)
+ sd->status.hp += bonus;
+ else {
+ sd->status.hp = sd->status.max_hp;
+ sd->hp_sub = sd->inchealhptick = 0;
+ }
+ }
+ }
+ if(bhp!=sd->status.hp)
+ clif_updatestatus(sd,SP_HP);
+
+ if(sd->nshealhp > 0) {
+ if(sd->inchealhptick >= battle_config.natural_heal_skill_interval && sd->status.hp < sd->status.max_hp) {
+ bonus = sd->nshealhp;
+ while(sd->inchealhptick >= battle_config.natural_heal_skill_interval) {
+ sd->inchealhptick -= battle_config.natural_heal_skill_interval;
+ if(sd->status.hp + bonus <= sd->status.max_hp)
+ sd->status.hp += bonus;
+ else {
+ bonus = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ sd->hp_sub = sd->inchealhptick = 0;
+ }
+ clif_heal(sd->fd,SP_HP,bonus);
+ }
+ }
+ }
+ else sd->inchealhptick = 0;
+
+ return 0;
+}
+
+static int pc_natural_heal_sp(struct map_session_data *sd)
+{
+ int bsp;
+ int inc_num,bonus;
+
+ nullpo_retr(0, sd);
+
+ if (sd->no_regen & 2)
+ return 0;
+
+ if(pc_checkoversp(sd)) {
+ sd->sp_sub = sd->inchealsptick = 0;
+ return 0;
+ }
+
+ bsp=sd->status.sp;
+
+ inc_num = pc_spheal(sd);
+ if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1 || (sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_MONK))
+ sd->sp_sub += inc_num;
+ if(sd->walktimer == -1)
+ sd->inchealsptick += natural_heal_diff_tick;
+ else sd->inchealsptick = 0;
+
+ if(sd->sp_sub >= battle_config.natural_healsp_interval){
+ bonus = sd->nhealsp;;
+ while(sd->sp_sub >= battle_config.natural_healsp_interval){
+ sd->sp_sub -= battle_config.natural_healsp_interval;
+ if(sd->status.sp + bonus <= sd->status.max_sp)
+ sd->status.sp += bonus;
+ else {
+ sd->status.sp = sd->status.max_sp;
+ sd->sp_sub = sd->inchealsptick = 0;
+ }
+ }
+ }
+
+ if(bsp != sd->status.sp)
+ clif_updatestatus(sd,SP_SP);
+
+ if(sd->nshealsp > 0) {
+ if(sd->inchealsptick >= battle_config.natural_heal_skill_interval && sd->status.sp < sd->status.max_sp) {
+ if(sd->doridori_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
+ bonus = sd->nshealsp*2;
+ sd->doridori_counter = 0;
+ } else
+ bonus = sd->nshealsp;
+ while(sd->inchealsptick >= battle_config.natural_heal_skill_interval) {
+ sd->inchealsptick -= battle_config.natural_heal_skill_interval;
+ if(sd->status.sp + bonus <= sd->status.max_sp)
+ sd->status.sp += bonus;
+ else {
+ bonus = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ sd->sp_sub = sd->inchealsptick = 0;
+ }
+ clif_heal(sd->fd,SP_SP,bonus);
+ }
+ }
+ }
+ else sd->inchealsptick = 0;
+
+ return 0;
+}
+
+static int pc_spirit_heal_hp(struct map_session_data *sd)
+{
+ int bonus_hp,interval = battle_config.natural_heal_skill_interval;
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoverhp(sd)) {
+ sd->inchealspirithptick = 0;
+ return 0;
+ }
+
+ sd->inchealspirithptick += natural_heal_diff_tick;
+
+ if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
+ interval += interval;
+
+ if(sd->inchealspirithptick >= interval) {
+ bonus_hp = sd->nsshealhp;
+ if(sd->doridori_counter && pc_checkskill(sd,TK_HPTIME) > 0) {
+ //TK_HPTIME doridori provided bonus [Dralnu]
+ bonus_hp += sd->nsshealhp;
+ if (!sd->nsshealsp) //If there's sp regen, this gets clear in the next function. [Skotlex]
+ sd->doridori_counter = 0;
+ }
+ while(sd->inchealspirithptick >= interval) {
+ if(pc_issit(sd)) {
+ sd->inchealspirithptick -= interval;
+ if(sd->status.hp < sd->status.max_hp) {
+ if(sd->status.hp + bonus_hp <= sd->status.max_hp)
+ sd->status.hp += bonus_hp;
+ else {
+ bonus_hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ }
+ clif_heal(sd->fd,SP_HP,bonus_hp);
+ sd->inchealspirithptick = 0;
+ }
+ }else{
+ sd->inchealspirithptick -= natural_heal_diff_tick;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+static int pc_spirit_heal_sp(struct map_session_data *sd)
+{
+ int bonus_sp,interval = battle_config.natural_heal_skill_interval;
+
+ nullpo_retr(0, sd);
+
+ if(pc_checkoversp(sd)) {
+ sd->inchealspiritsptick = 0;
+ return 0;
+ }
+
+ sd->inchealspiritsptick += natural_heal_diff_tick;
+
+ if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
+ interval += interval;
+
+ if(sd->inchealspiritsptick >= interval) {
+ bonus_sp = sd->nsshealsp;
+ if(sd->doridori_counter && pc_checkskill(sd,TK_SPTIME) > 0) {
+ //TK_SPTIME doridori provided bonus [Dralnu]
+ bonus_sp += sd->nsshealsp;
+ sd->doridori_counter = 0;
+ }
+ while(sd->inchealspiritsptick >= interval) {
+ if(pc_issit(sd)) {
+ sd->inchealspiritsptick -= interval;
+ if(sd->status.sp < sd->status.max_sp) {
+ if(sd->status.sp + bonus_sp <= sd->status.max_sp)
+ sd->status.sp += bonus_sp;
+ else {
+ bonus_sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ }
+ clif_heal(sd->fd,SP_SP,bonus_sp);
+ sd->inchealspiritsptick = 0;
+ }
+ }else{
+ sd->inchealspiritsptick -= natural_heal_diff_tick;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int pc_bleeding (struct map_session_data *sd)
+{
+ int hp = 0, sp = 0;
+ nullpo_retr(0, sd);
+
+ if (sd->hp_loss_value > 0) {
+ sd->hp_loss_tick += natural_heal_diff_tick;
+ if (sd->hp_loss_tick >= sd->hp_loss_rate) {
+ do {
+ hp += sd->hp_loss_value;
+ sd->hp_loss_tick -= sd->hp_loss_rate;
+ } while (sd->hp_loss_tick >= sd->hp_loss_rate);
+ sd->hp_loss_tick = 0;
+ }
+ }
+
+ if (sd->sp_loss_value > 0) {
+ sd->sp_loss_tick += natural_heal_diff_tick;
+ if (sd->sp_loss_tick >= sd->sp_loss_rate) {
+ do {
+ sp += sd->sp_loss_value;
+ sd->sp_loss_tick -= sd->sp_loss_rate;
+ } while (sd->sp_loss_tick >= sd->sp_loss_rate);
+ sd->sp_loss_tick = 0;
+ }
+ }
+
+ if (hp > 0 || sp > 0)
+ pc_heal(sd,-hp,-sp);
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SP Ž©‘R‰ñ•œ ŠeƒNƒ‰ƒCƒAƒ“ƒg
+ *------------------------------------------
+ */
+
+static int pc_natural_heal_sub(struct map_session_data *sd,va_list ap) {
+ int tick;
+
+ nullpo_retr(0, sd);
+ tick = va_arg(ap,int);
+
+// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status)
+ if (pc_isdead(sd) || pc_ishiding(sd) ||
+ //-- cannot regen for 5 minutes after using Berserk --- [Celest]
+ (sd->sc_count && (
+ (sd->sc_data[SC_POISON].timer != -1 && sd->sc_data[SC_SLOWPOISON].timer == -1) ||
+ (sd->sc_data[SC_DPOISON].timer != -1 && sd->sc_data[SC_SLOWPOISON].timer == -1) ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ sd->sc_data[SC_TRICKDEAD].timer != -1
+ ))
+ ) { //Cannot heal neither natural or special.
+ sd->hp_sub = sd->inchealhptick = sd->inchealspirithptick = 0;
+ sd->sp_sub = sd->inchealsptick = sd->inchealspiritsptick = 0;
+ } else {
+ if (DIFF_TICK (tick, sd->canregen_tick)<0 ||
+ sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) { //Cannot heal natural HP/SP
+ sd->hp_sub = sd->inchealhptick = 0;
+ sd->sp_sub = sd->inchealsptick = 0;
+ } else { //natural heal
+ pc_natural_heal_hp(sd);
+ if(sd->sc_count && (
+ sd->sc_data[SC_EXTREMITYFIST].timer != -1 ||
+ sd->sc_data[SC_DANCING].timer != -1 ||
+ sd->sc_data[SC_BLEEDING].timer != -1
+ )) //No SP natural heal.
+ sd->sp_sub = sd->inchealsptick = 0;
+ else
+ pc_natural_heal_sp(sd);
+ sd->canregen_tick = tick;
+ }
+ //Sitting Healing
+ if (sd->nsshealhp)
+ pc_spirit_heal_hp(sd);
+ if (sd->nsshealsp)
+ pc_spirit_heal_sp(sd);
+ }
+ if (sd->hp_loss_value > 0 || sd->sp_loss_value > 0)
+ pc_bleeding(sd);
+ else
+ sd->hp_loss_tick = sd->sp_loss_tick = 0;
+
+ return 0;
+}
+
+/*==========================================
+ * HP/SPŽ©‘R‰ñ•œ (interval timer??)
+ *------------------------------------------
+ */
+int pc_natural_heal(int tid,unsigned int tick,int id,int data)
+{
+ natural_heal_tick = tick;
+ natural_heal_diff_tick = DIFF_TICK(natural_heal_tick,natural_heal_prev_tick);
+ clif_foreachclient(pc_natural_heal_sub, tick);
+
+ natural_heal_prev_tick = tick;
+ return 0;
+}
+
+/*==========================================
+ * ƒZ?ƒuƒ|ƒCƒ“ƒg‚Ì•Û‘¶
+ *------------------------------------------
+ */
+int pc_setsavepoint(struct map_session_data *sd, short mapindex,int x,int y)
+{
+ nullpo_retr(0, sd);
+
+ sd->status.save_point.map = mapindex;
+ sd->status.save_point.x = x;
+ sd->status.save_point.y = y;
+
+ return 0;
+}
+
+/*==========================================
+ * Ž©“®ƒZ?ƒu ŠeƒNƒ‰ƒCƒAƒ“ƒg
+ *------------------------------------------
+ */
+static int last_save_fd,save_flag;
+static int pc_autosave_sub(struct map_session_data *sd,va_list ap)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(save_flag==0 && sd->fd>last_save_fd && !sd->state.waitingdisconnect)
+ {
+ // pet
+ if(sd->status.pet_id > 0 && sd->pd)
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+
+ chrif_save(sd,0);
+ save_flag=1;
+ last_save_fd = sd->fd;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Ž©“®ƒZ?ƒu (timer??)
+ *------------------------------------------
+ */
+int pc_autosave(int tid,unsigned int tick,int id,int data)
+{
+ int interval;
+
+ save_flag=0;
+ clif_foreachclient(pc_autosave_sub);
+ if(save_flag==0)
+ last_save_fd=0;
+
+ interval = autosave_interval/(clif_countusers()+1);
+ if(interval <= 0)
+ interval = 1;
+ add_timer(gettick()+interval,pc_autosave,0,0);
+
+ return 0;
+}
+
+int pc_read_gm_account(int fd)
+{
+#ifdef TXT_ONLY
+ int i = 0;
+ RFIFOHEAD(fd);
+#endif
+ if (gm_account != NULL)
+ aFree(gm_account);
+ GM_num = 0;
+#ifdef TXT_ONLY
+ gm_account = (struct gm_account *) aCallocA(((RFIFOW(fd,2) - 4) / 5), sizeof(struct gm_account));
+ for (i = 4; i < RFIFOW(fd,2); i = i + 5) {
+ gm_account[GM_num].account_id = RFIFOL(fd,i);
+ gm_account[GM_num].level = (int)RFIFOB(fd,i+4);
+ //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level);
+ GM_num++;
+ }
+#else
+ sprintf (tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'",gm_db_account_id,gm_db_level,gm_db,gm_db_level,lowest_gm_level);
+ if(mysql_query(&lmysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ lsql_res = mysql_store_result(&lmysql_handle);
+ if (lsql_res) {
+ gm_account = (struct gm_account *) aCallocA((size_t)mysql_num_rows(lsql_res), sizeof(struct gm_account));
+ while ((lsql_row = mysql_fetch_row(lsql_res))) {
+ gm_account[GM_num].account_id = atoi(lsql_row[0]);
+ gm_account[GM_num].level = atoi(lsql_row[1]);
+ ShowNotice("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level);
+ GM_num++;
+ }
+ }
+
+ mysql_free_result(lsql_res);
+#endif /* TXT_ONLY */
+ return GM_num;
+}
+
+/*================================================
+ * timer to do the day [Yor]
+ * data: 0 = called by timer, 1 = gmcommand/script
+ *------------------------------------------------
+ */
+int map_day_timer(int tid, unsigned int tick, int id, int data)
+{
+ char tmp_soutput[1024];
+ struct map_session_data *pl_sd;
+
+ if (data == 0 && battle_config.day_duration <= 0) // if we want a day
+ return 0;
+
+ if (night_flag != 0) {
+ int i;
+ night_flag = 0; // 0=day, 1=night [Yor]
+
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth && pl_sd->fd)
+ {
+ if (pl_sd->state.night) {
+ clif_status_load(&pl_sd->bl, SI_NIGHT, 0); //New night effect by dynamix [Skotlex]
+ pl_sd->state.night = 0;
+ }
+ }
+ }
+
+ strcpy(tmp_soutput, (data == 0) ? msg_txt(502) : msg_txt(60)); // The day has arrived!
+ intif_GMmessage(tmp_soutput, strlen(tmp_soutput) + 1, 0);
+ }
+
+ return 0;
+}
+
+/*================================================
+ * timer to do the night [Yor]
+ * data: 0 = called by timer, 1 = gmcommand/script
+ *------------------------------------------------
+ */
+int map_night_timer(int tid, unsigned int tick, int id, int data)
+{
+ char tmp_soutput[1024];
+ struct map_session_data *pl_sd;
+
+ if (data == 0 && battle_config.night_duration <= 0) // if we want a night
+ return 0;
+
+ if (night_flag == 0) {
+ int i;
+ night_flag = 1; // 0=day, 1=night [Yor]
+ for(i = 0; i < fd_max; i++) {
+ if (session[i] && (pl_sd = (struct map_session_data *) session[i]->session_data) && pl_sd->state.auth && pl_sd->fd)
+ {
+ if (!pl_sd->state.night && map[pl_sd->bl.m].flag.nightenabled) {
+ clif_status_load(&pl_sd->bl, SI_NIGHT, 1); //New night effect by dynamix [Skotlex]
+ pl_sd->state.night = 1;
+ }
+ }
+ }
+ strcpy(tmp_soutput, (data == 0) ? msg_txt(503) : msg_txt(59)); // The night has fallen...
+ intif_GMmessage(tmp_soutput, strlen(tmp_soutput) + 1, 0);
+ }
+
+ return 0;
+}
+
+void pc_setstand(struct map_session_data *sd){
+ nullpo_retv(sd);
+
+ if(sd->sc_count && sd->sc_data[SC_TENSIONRELAX].timer!=-1)
+ status_change_end(&sd->bl,SC_TENSIONRELAX,-1);
+
+ sd->state.dead_sit = 0;
+}
+
+//
+// ‰Šú‰»•¨
+//
+/*==========================================
+ * Ý’èƒtƒ@ƒCƒ‹?‚Ý?‚Þ
+ * exp.txt •K—v??’l
+ * job_db1.txt d—Ê,hp,sp,U?‘¬“x
+ * job_db2.txt job”\—Í’lƒ{?ƒiƒX
+ * skill_tree.txt ŠeE?‚̃XƒLƒ‹ƒcƒŠ?
+ * attr_fix.txt ?«C³ƒe?ƒuƒ‹
+ * size_fix.txt ƒTƒCƒY•â³ƒe?ƒuƒ‹
+ * refine_db.txt ¸?ƒf?ƒ^ƒe?ƒuƒ‹
+ *------------------------------------------
+ */
+int pc_readdb(void)
+{
+ int i,j,k;
+ FILE *fp;
+ char line[1024],*p;
+
+ // •K—v??’l?‚Ý?‚Ý
+ memset(exp_table,0,sizeof(exp_table));
+ sprintf(line, "%s/exp.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ int bn,b1,b2,b3,b4,b5,b6,jn,j1,j2,j3,j4,j5,j6;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(sscanf(line,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",&bn,&b1,&b2,&b3,&b4,&b5,&b6,&jn,&j1,&j2,&j3,&j4,&j5,&j6)!=14)
+ continue;
+ exp_table[0][i]=bn;
+ exp_table[1][i]=b1;
+ exp_table[2][i]=b2;
+ exp_table[3][i]=b3;
+ exp_table[4][i]=b4;
+ exp_table[5][i]=b5;
+ exp_table[6][i]=b6;
+ exp_table[7][i]=jn;
+ exp_table[8][i]=j1;
+ exp_table[9][i]=j2;
+ exp_table[10][i]=j3;
+ exp_table[11][i]=j4;
+ exp_table[12][i]=j5;
+ exp_table[13][i]=j6;
+ i++;
+ }
+ if (i > battle_config.max_base_level)
+ { //Empty Base level columns
+ for (j = battle_config.max_base_level-1; j < i && exp_table[0][j]>0; j++)
+ {
+ exp_table[0][j]=0;
+ exp_table[1][j]=0;
+ exp_table[2][j]=0;
+ exp_table[3][j]=0;
+ exp_table[4][j]=0;
+ exp_table[5][j]=0;
+ exp_table[6][j]=0;
+ }
+ }
+ if (i > battle_config.max_sn_level)
+ { //Empty SN job exp columns
+ for (j = battle_config.max_sn_level-1; j < i && exp_table[10][j]>0; j++)
+ exp_table[10][j]=0;
+ }
+ if (i > battle_config.max_adv_level)
+ { //Empty Adv Jobs columns
+ for (j = battle_config.max_adv_level-1; j < i && exp_table[13][j]>0; j++)
+ exp_table[13][j]=0;
+ }
+ if (i > battle_config.max_job_level)
+ { //Empty normal Job columns
+ for (j = battle_config.max_job_level-1; j < i &&
+ (exp_table[8][j]>0 || exp_table[9][j]>0 || exp_table[12][j]>0); j++)
+ {
+ exp_table[8][j]=0; //1st Job
+ exp_table[9][j]=0; //2nd Job
+ exp_table[12][j]=0; //Adv 1st Job
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","exp.txt");
+
+ // ƒXƒLƒ‹ƒcƒŠ?
+ memset(skill_tree,0,sizeof(skill_tree));
+ sprintf(line, "%s/skill_tree.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[50];
+ int f=0, m=3;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<14 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(j<13)
+ continue;
+ if (j == 14) {
+ f=1; // MinJobLvl has been added
+ m++;
+ }
+ // check for bounds [celest]
+ if (atoi(split[0]) >= MAX_PC_CLASS)
+ continue;
+ k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
+ for(j = 0; j < MAX_SKILL_TREE && skill_tree[atoi(split[0])][j].id && skill_tree[atoi(split[0])][j].id != k; j++);
+ if (j == MAX_SKILL_TREE)
+ {
+ ShowWarning("Unable to load skill %d into job %d's tree. Maximum number of skills per class has been reached.\n", k, atoi(split[0]));
+ continue;
+ }
+ skill_tree[atoi(split[0])][j].id=k;
+ skill_tree[atoi(split[0])][j].max=atoi(split[2]);
+ if (f) skill_tree[atoi(split[0])][j].joblv=atoi(split[3]);
+
+ for(k=0;k<5;k++){
+ skill_tree[atoi(split[0])][j].need[k].id=atoi(split[k*2+m]);
+ skill_tree[atoi(split[0])][j].need[k].lv=atoi(split[k*2+m+1]);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","skill_tree.txt");
+
+ // ?«C³ƒe?ƒuƒ‹
+ for(i=0;i<4;i++)
+ for(j=0;j<10;j++)
+ for(k=0;k<10;k++)
+ attr_fix_table[i][j][k]=100;
+
+ sprintf(line, "%s/attr_fix.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[10];
+ int lv,n;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<3 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ lv=atoi(split[0]);
+ n=atoi(split[1]);
+
+ for(i=0;i<n;){
+ if( !fgets(line, sizeof(line)-1, fp) )
+ break;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+
+ for(j=0,p=line;j<n && p;j++){
+ while(*p==32 && *p>0)
+ p++;
+ attr_fix_table[lv-1][i][j]=atoi(p);
+ if(battle_config.attr_recover == 0 && attr_fix_table[lv-1][i][j] < 0)
+ attr_fix_table[lv-1][i][j] = 0;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+
+ i++;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","attr_fix.txt");
+
+ // ƒXƒLƒ‹ƒcƒŠ?
+ memset(statp,0,sizeof(statp));
+ i=1;
+ j=45; // base points
+ sprintf(line, "%s/statpoint.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp == NULL){
+ ShowStatus("Can't read '"CL_WHITE"%s"CL_RESET"'... Generating DB.\n",line);
+ //return 1;
+ } else {
+ while(fgets(line, sizeof(line)-1, fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if ((j=atoi(line))<0)
+ j=0;
+ if (i >= MAX_LEVEL)
+ break;
+ statp[i]=j;
+ i++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt");
+ }
+ // generate the remaining parts of the db if necessary
+ for (; i < MAX_LEVEL; i++) {
+ j += (i+15)/5;
+ statp[i] = j;
+ }
+
+ return 0;
+}
+
+// Read MOTD on startup. [Valaris]
+int pc_read_motd(void) {
+ FILE *fp;
+ int ln=0,i=0;
+
+ memset(motd_text,0,sizeof(motd_text));
+ if ((fp = fopen(motd_txt, "r")) != NULL) {
+ while ((ln < MOTD_LINE_SIZE) && fgets(motd_text[ln], sizeof(motd_text[ln])-1, fp) != NULL) {
+ if(motd_text[ln][0] == '/' && motd_text[ln][1] == '/')
+ continue;
+ for(i=0; motd_text[ln][i]; i++) {
+ if (motd_text[ln][i] == '\r' || motd_text[ln][i]== '\n') {
+ if(i)
+ motd_text[ln][i]=0;
+ else
+ motd_text[ln][0]=' ';
+ ln++;
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ }
+ else if(battle_config.error_log)
+ ShowWarning("In function pc_read_motd() -> File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt);
+
+ return 0;
+}
+
+/*==========================================
+ * pc? ŒW‰Šú‰»
+ *------------------------------------------
+ */
+void do_final_pc(void) {
+ if (gm_account)
+ aFree(gm_account);
+ return;
+}
+int do_init_pc(void) {
+ pc_readdb();
+ pc_read_motd(); // Read MOTD [Valaris]
+
+ add_timer_func_list(pc_walk, "pc_walk");
+ add_timer_func_list(pc_attack_timer, "pc_attack_timer");
+ add_timer_func_list(pc_natural_heal, "pc_natural_heal");
+ add_timer_func_list(pc_invincible_timer, "pc_invincible_timer");
+ add_timer_func_list(pc_eventtimer, "pc_eventtimer");
+ add_timer_func_list(pc_calc_pvprank_timer, "pc_calc_pvprank_timer");
+ add_timer_func_list(pc_autosave, "pc_autosave");
+ add_timer_func_list(pc_spiritball_timer, "pc_spiritball_timer");
+ add_timer_func_list(pc_blockskill_end, "pc_blockskill_end");
+ add_timer_func_list(pc_follow_timer, "pc_follow_timer");
+ add_timer_interval((natural_heal_prev_tick = gettick() + NATURAL_HEAL_INTERVAL), pc_natural_heal, 0, 0, NATURAL_HEAL_INTERVAL);
+ add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
+#ifndef TXT_ONLY
+ pc_read_gm_account(0);
+#endif /* not TXT_ONLY */
+
+ if (battle_config.day_duration > 0 && battle_config.night_duration > 0) {
+ int day_duration = battle_config.day_duration;
+ int night_duration = battle_config.night_duration;
+ // add night/day timer (by [yor])
+ add_timer_func_list(map_day_timer, "map_day_timer"); // by [yor]
+ add_timer_func_list(map_night_timer, "map_night_timer"); // by [yor]
+
+ if (!battle_config.night_at_start) {
+ night_flag = 0; // 0=day, 1=night [Yor]
+ day_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_day_timer, 0, 0, day_duration + night_duration);
+ night_timer_tid = add_timer_interval(gettick() + day_duration, map_night_timer, 0, 0, day_duration + night_duration);
+ } else {
+ night_flag = 1; // 0=day, 1=night [Yor]
+ day_timer_tid = add_timer_interval(gettick() + night_duration, map_day_timer, 0, 0, day_duration + night_duration);
+ night_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_night_timer, 0, 0, day_duration + night_duration);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/map/pc.h b/src/map/pc.h
new file mode 100644
index 000000000..e7a9c85c5
--- /dev/null
+++ b/src/map/pc.h
@@ -0,0 +1,251 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PC_H_
+#define _PC_H_
+
+#include "map.h"
+
+#define OPTION_MASK 0xd7b8
+#define CART_MASK 0x788
+
+//Update this max as necessary. 53 is the value needed for Super Baby currently
+#define MAX_SKILL_TREE 53
+
+#define pc_setdead(sd) ((sd)->state.dead_sit = 1)
+#define pc_setsit(sd) ((sd)->state.dead_sit = 2)
+#define pc_isdead(sd) ((sd)->state.dead_sit == 1)
+#define pc_issit(sd) ((sd)->state.dead_sit == 2)
+#define pc_setdir(sd,b,h) ((sd)->dir = (b) ,(sd)->head_dir = (h) )
+#define pc_setchatid(sd,n) ((sd)->chatID = n)
+#define pc_ishiding(sd) ((sd)->status.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK))
+#define pc_iscloaking(sd) (!((sd)->status.option&OPTION_CHASEWALK) && ((sd)->status.option&OPTION_CLOAK))
+#define pc_ischasewalk(sd) ((sd)->status.option&OPTION_CHASEWALK)
+#define pc_iscarton(sd) ((sd)->status.option&CART_MASK)
+#define pc_isfalcon(sd) ((sd)->status.option&OPTION_FALCON)
+#define pc_isriding(sd) ((sd)->status.option&OPTION_RIDING)
+#define pc_isinvisible(sd) ((sd)->status.option&OPTION_INVISIBLE)
+#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight)
+#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9)
+#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter)
+//Checks if the given class value corresponds to a player class. [Skotlex]
+#define pcdb_checkid(class_) ((class_ >= JOB_NOVICE && class_ <= JOB_XMAS) || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER))
+
+int pc_isGM(struct map_session_data *sd);
+int pc_iskiller(struct map_session_data *src, struct map_session_data *target); // [MouseJstr]
+int pc_getrefinebonus(int lv,int type);
+int pc_can_move(struct map_session_data *sd); //[Skotlex]
+int pc_can_give_items(int level); //[Lupus]
+
+int pc_setrestartvalue(struct map_session_data *sd,int type);
+int pc_makesavestatus(struct map_session_data *);
+int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int);
+int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
+int pc_authfail(struct map_session_data *);
+int pc_reg_received(struct map_session_data *sd);
+
+int pc_isequip(struct map_session_data *sd,int n);
+int pc_equippoint(struct map_session_data *sd,int n);
+
+int pc_break_equip(struct map_session_data *, unsigned short);
+#define pc_breakweapon(sd) (pc_break_equip(sd, EQP_WEAPON))
+#define pc_breakarmor(sd) (pc_break_equip(sd, EQP_ARMOR))
+#define pc_breakshield(sd) (pc_break_equip(sd, EQP_SHIELD))
+#define pc_breakhelm(sd) (pc_break_equip(sd, EQP_HELM))
+
+int pc_checkskill(struct map_session_data *sd,int skill_id);
+int pc_checkallowskill(struct map_session_data *sd);
+int pc_checkequip(struct map_session_data *sd,int pos);
+
+int pc_calc_skilltree(struct map_session_data *sd);
+int pc_calc_skilltree_normalize_job(struct map_session_data *sd);
+int pc_clean_skilltree(struct map_session_data *sd);
+
+int pc_checkoverhp(struct map_session_data*);
+int pc_checkoversp(struct map_session_data*);
+
+int pc_can_reach(struct map_session_data*,int,int);
+int pc_walktoxy(struct map_session_data*,int,int);
+int pc_stop_walking(struct map_session_data*,int);
+int pc_movepos(struct map_session_data*,int,int,int);
+int pc_setpos(struct map_session_data*,unsigned short,int,int,int);
+int pc_setsavepoint(struct map_session_data*,short,int,int);
+int pc_randomwarp(struct map_session_data *sd,int type);
+int pc_memo(struct map_session_data *sd,int i);
+int pc_remove_map(struct map_session_data *sd,int clrtype);
+
+int pc_checkadditem(struct map_session_data*,int,int);
+int pc_inventoryblank(struct map_session_data*);
+int pc_search_inventory(struct map_session_data *sd,int item_id);
+int pc_payzeny(struct map_session_data*,int);
+int pc_additem(struct map_session_data*,struct item*,int);
+int pc_getzeny(struct map_session_data*,int);
+int pc_delitem(struct map_session_data*,int,int,int);
+int pc_checkitem(struct map_session_data*);
+
+int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount);
+int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type);
+int pc_putitemtocart(struct map_session_data *sd,int idx,int amount);
+int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount);
+int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount);
+
+int pc_takeitem(struct map_session_data*,struct flooritem_data*);
+int pc_dropitem(struct map_session_data*,int,int);
+
+int pc_checkweighticon(struct map_session_data *sd);
+
+int pc_bonus(struct map_session_data*,int,int);
+int pc_bonus2(struct map_session_data *sd,int,int,int);
+int pc_bonus3(struct map_session_data *sd,int,int,int,int);
+int pc_bonus4(struct map_session_data *sd,int,int,int,int,int);
+int pc_skill(struct map_session_data*,int,int,int);
+
+int pc_blockskill_start (struct map_session_data*,int,int); // [celest]
+
+int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip);
+
+int pc_steal_item(struct map_session_data *sd,struct block_list *bl);
+int pc_steal_coin(struct map_session_data *sd,struct block_list *bl);
+
+int pc_modifybuyvalue(struct map_session_data*,int);
+int pc_modifysellvalue(struct map_session_data*,int);
+
+int pc_attack(struct map_session_data*,int,int);
+int pc_stopattack(struct map_session_data*);
+
+int pc_follow(struct map_session_data*, int); // [MouseJstr]
+int pc_stop_following(struct map_session_data*);
+
+int pc_checkbaselevelup(struct map_session_data *sd);
+int pc_checkjoblevelup(struct map_session_data *sd);
+int pc_gainexp(struct map_session_data*,int,int);
+int pc_nextbaseexp(struct map_session_data *);
+int pc_nextbaseafter(struct map_session_data *); // [Valaris]
+int pc_nextjobexp(struct map_session_data *);
+int pc_nextjobafter(struct map_session_data *); // [Valaris]
+int pc_need_status_point(struct map_session_data *,int);
+int pc_statusup(struct map_session_data*,int);
+int pc_statusup2(struct map_session_data*,int,int);
+int pc_skillup(struct map_session_data*,int);
+int pc_allskillup(struct map_session_data*);
+int pc_resetlvl(struct map_session_data*,int type);
+int pc_resetstate(struct map_session_data*);
+int pc_resetskill(struct map_session_data*);
+int pc_resetfeel(struct map_session_data*);
+int pc_equipitem(struct map_session_data*,int,int);
+int pc_unequipitem(struct map_session_data*,int,int);
+int pc_checkitem(struct map_session_data*);
+int pc_useitem(struct map_session_data*,int);
+
+int pc_damage(struct block_list *,struct map_session_data*,int);
+int pc_heal(struct map_session_data *,int,int);
+int pc_itemheal(struct map_session_data *sd,int hp,int sp);
+int pc_percentheal(struct map_session_data *sd,int,int);
+int pc_jobchange(struct map_session_data *,int, int);
+int pc_setoption(struct map_session_data *,int);
+int pc_setcart(struct map_session_data *sd,int type);
+int pc_setfalcon(struct map_session_data *sd);
+int pc_setriding(struct map_session_data *sd);
+int pc_changelook(struct map_session_data *,int,int);
+int pc_equiplookall(struct map_session_data *sd);
+
+int pc_readparam(struct map_session_data*,int);
+int pc_setparam(struct map_session_data*,int,int);
+int pc_readreg(struct map_session_data*,int);
+int pc_setreg(struct map_session_data*,int,int);
+char *pc_readregstr(struct map_session_data *sd,int reg);
+int pc_setregstr(struct map_session_data *sd,int reg,char *str);
+
+#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3)
+#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3)
+#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3)
+#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3)
+#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2)
+#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2)
+#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2)
+#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2)
+#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1)
+#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1)
+#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1)
+#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1)
+int pc_readregistry(struct map_session_data*,char*,int);
+int pc_setregistry(struct map_session_data*,char*,int,int);
+char *pc_readregistry_str(struct map_session_data*,char*,int);
+int pc_setregistry_str(struct map_session_data*,char*,char*,int);
+
+int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name);
+int pc_deleventtimer(struct map_session_data *sd,const char *name);
+int pc_cleareventtimer(struct map_session_data *sd);
+int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick);
+
+int pc_calc_pvprank(struct map_session_data *sd);
+int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data);
+
+int pc_ismarried(struct map_session_data *sd);
+int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd);
+int pc_divorce(struct map_session_data *sd);
+int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd);
+struct map_session_data *pc_get_partner(struct map_session_data *sd);
+struct map_session_data *pc_get_father(struct map_session_data *sd);
+struct map_session_data *pc_get_mother(struct map_session_data *sd);
+struct map_session_data *pc_get_child(struct map_session_data *sd);
+
+int pc_set_gm_level(int account_id, int level);
+void pc_setstand(struct map_session_data *sd);
+int pc_break_equip(struct map_session_data *sd, unsigned short where);
+int pc_candrop(struct map_session_data *sd,int item_id);
+
+struct pc_base_job{
+ int job; //E‹ÆA‚½‚¾‚µ“]¶E‚â—{ŽqE‚Ìꇂ͌³‚ÌE‹Æ‚ð•Ô‚·(”pƒvƒŠ¨ƒvƒŠ)
+ int type; //ƒmƒr 0, ˆêŽŸE 1, “ñŽŸE 2, ƒXƒpƒmƒr 3
+ int upper; //’Êí 0, “]¶ 1, —{Žq 2
+};
+
+struct pc_base_job pc_calc_base_job(int b_class);//“]¶‚â—{ŽqE‚ÌŒ³‚ÌE‹Æ‚ð•Ô‚·
+int pc_calc_base_job2(int b_class); // Celest
+unsigned short pc_jobid2mapid(unsigned short b_class); // Skotlex
+unsigned short pc_mapid2jobid(unsigned short class_, int sex); // Skotlex
+
+char * job_name(int class_);
+
+struct skill_tree_entry {
+ short id;
+ unsigned char max;
+ unsigned char joblv;
+ struct {
+ short id;
+ unsigned char lv;
+ } need[5];
+}; // Celest
+extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
+
+int pc_read_gm_account(int fd);
+int pc_setinvincibletimer(struct map_session_data *sd,int);
+int pc_delinvincibletimer(struct map_session_data *sd);
+int pc_addspiritball(struct map_session_data *sd,int,int);
+int pc_delspiritball(struct map_session_data *sd,int,int);
+void pc_addfame(struct map_session_data *sd,int count);
+int pc_istop10fame(int char_id, int job);
+int pc_eventtimer(int tid,unsigned int tick,int id,int data); // for npc_dequeue
+
+int pc_run(struct map_session_data *sd, int skilllv, int dir);
+
+extern struct fame_list smith_fame_list[10];
+extern struct fame_list chemist_fame_list[10];
+extern struct fame_list taekwon_fame_list[10];
+
+int pc_readdb(void);
+int do_init_pc(void);
+void do_final_pc(void);
+
+enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT};
+
+// timer for night.day
+extern int day_timer_tid;
+extern int night_timer_tid;
+int map_day_timer(int,unsigned int,int,int); // by [yor]
+int map_night_timer(int,unsigned int,int,int); // by [yor]
+
+int pc_read_motd(void); // [Valaris]
+
+#endif
diff --git a/src/map/pcre.h b/src/map/pcre.h
new file mode 100644
index 000000000..244e55e0d
--- /dev/null
+++ b/src/map/pcre.h
@@ -0,0 +1,258 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+ Copyright (c) 1997-2005 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR 6
+#define PCRE_MINOR 3
+#define PCRE_DATE 15-Aug-2005
+
+/* Win32 uses DLL by default; it needs special stuff for exported functions. */
+
+#ifdef _WIN32
+# ifdef PCRE_DEFINITION
+# ifdef DLL_EXPORT
+# define PCRE_DATA_SCOPE __declspec(dllexport)
+# endif
+# else
+# ifndef PCRE_STATIC
+# define PCRE_DATA_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+#endif
+
+/* For other operating systems, we use the standard "extern". */
+
+#ifndef PCRE_DATA_SCOPE
+# ifdef __cplusplus
+# define PCRE_DATA_SCOPE extern "C"
+# else
+# define PCRE_DATA_SCOPE extern
+# endif
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x00000001
+#define PCRE_MULTILINE 0x00000002
+#define PCRE_DOTALL 0x00000004
+#define PCRE_EXTENDED 0x00000008
+#define PCRE_ANCHORED 0x00000010
+#define PCRE_DOLLAR_ENDONLY 0x00000020
+#define PCRE_EXTRA 0x00000040
+#define PCRE_NOTBOL 0x00000080
+#define PCRE_NOTEOL 0x00000100
+#define PCRE_UNGREEDY 0x00000200
+#define PCRE_NOTEMPTY 0x00000400
+#define PCRE_UTF8 0x00000800
+#define PCRE_NO_AUTO_CAPTURE 0x00001000
+#define PCRE_NO_UTF8_CHECK 0x00002000
+#define PCRE_AUTO_CALLOUT 0x00004000
+#define PCRE_PARTIAL 0x00008000
+#define PCRE_DFA_SHORTEST 0x00010000
+#define PCRE_DFA_RESTART 0x00020000
+#define PCRE_FIRSTLINE 0x00040000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL (-12)
+#define PCRE_ERROR_BADPARTIAL (-13)
+#define PCRE_ERROR_INTERNAL (-14)
+#define PCRE_ERROR_BADCOUNT (-15)
+#define PCRE_ERROR_DFA_UITEM (-16)
+#define PCRE_ERROR_DFA_UCOND (-17)
+#define PCRE_ERROR_DFA_UMLIMIT (-18)
+#define PCRE_ERROR_DFA_WSSIZE (-19)
+#define PCRE_ERROR_DFA_RECURSE (-20)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+#define PCRE_INFO_DEFAULT_TABLES 11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+#define PCRE_CONFIG_UNICODE_PROPERTIES 6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+#define PCRE_EXTRA_TABLES 0x0008
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+ const unsigned char *tables; /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ const char *subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are in the subject */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------- Added for Version 1 -------------------------- */
+ int pattern_position; /* Offset to next item in the pattern */
+ int next_item_length; /* Length of next item in the pattern */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. For Virtual Pascal, these definitions
+have to take another form. */
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+PCRE_DATA_SCOPE void *pcre_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_free(void *);
+PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_stack_free(void *);
+PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *,
+ const unsigned char *);
+PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **,
+ int *, const unsigned char *);
+PCRE_DATA_SCOPE int pcre_config(int, void *);
+PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *,
+ int);
+PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int , int *, int);
+PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *,
+ int, int, int, int *, int);
+PCRE_DATA_SCOPE void pcre_free_substring(const char *);
+PCRE_DATA_SCOPE void pcre_free_substring_list(const char **);
+PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *);
+PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int,
+ const char **);
+PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *);
+PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void);
+PCRE_DATA_SCOPE int pcre_refcount(pcre *, int);
+PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **);
+PCRE_DATA_SCOPE const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/src/map/pet.c b/src/map/pet.c
new file mode 100644
index 000000000..48e62cfb4
--- /dev/null
+++ b/src/map/pet.c
@@ -0,0 +1,1973 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "db.h"
+#include "timer.h"
+#include "socket.h"
+#include "nullpo.h"
+#include "malloc.h"
+#include "pc.h"
+#include "status.h"
+#include "map.h"
+#include "intif.h"
+#include "clif.h"
+#include "chrif.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "mob.h"
+#include "npc.h"
+#include "script.h"
+#include "skill.h"
+#include "showmsg.h"
+
+#define MIN_PETTHINKTIME 100
+
+struct pet_db pet_db[MAX_PET_DB];
+
+static int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+static int pet_timer(int tid,unsigned int tick,int id,int data);
+static int pet_walktoxy_sub(struct pet_data *pd);
+
+static int calc_next_walk_step(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->walkpath.path_pos>=pd->walkpath.path_len)
+ return -1;
+ if(pd->walkpath.path[pd->walkpath.path_pos]&1)
+ return pd->speed*14/10;
+ return pd->speed;
+}
+
+static int pet_performance_val(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet.intimate > 900)
+ return (sd->petDB->s_perfor > 0)? 4:3;
+ else if(sd->pet.intimate > 750)
+ return 2;
+ else
+ return 1;
+}
+
+int pet_hungry_val(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet.hungry > 90)
+ return 4;
+ else if(sd->pet.hungry > 75)
+ return 3;
+ else if(sd->pet.hungry > 25)
+ return 2;
+ else if(sd->pet.hungry > 10)
+ return 1;
+ else
+ return 0;
+}
+
+static int pet_can_reach(struct pet_data *pd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if( pd->bl.x==x && pd->bl.y==y ) // “¯‚¶ƒ}ƒX
+ return 1;
+
+ // áŠQ•¨”»’è
+ wpd.path_len=0;
+ wpd.path_pos=0;
+ wpd.path_half=0;
+ return (path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,x,y,0)!=-1)?1:0;
+}
+
+static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir)
+{
+ int x,y,dx,dy;
+ int i,j=0,k;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ pd->to_x = tx;
+ pd->to_y = ty;
+
+ if(dir >= 0 && dir < 8) {
+ dx = -dirx[dir]*2;
+ dy = -diry[dir]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if(!(j=pet_can_reach(pd,x,y))) {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if(!(j=pet_can_reach(pd,x,y))) {
+ for(i=0;i<12;i++) {
+ k = rand()%8;
+ dx = -dirx[k]*2;
+ dy = -diry[k]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if((j=pet_can_reach(pd,x,y)))
+ break;
+ else {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if((j=pet_can_reach(pd,x,y)))
+ break;
+ }
+ }
+ if(!j) {
+ x = tx;
+ y = ty;
+ if(!pet_can_reach(pd,x,y))
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ return 1;
+
+ pd->to_x = x;
+ pd->to_y = y;
+ return 0;
+}
+
+static int pet_unlocktarget(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ pd->target_id=0;
+
+ return 0;
+}
+
+static int pet_attack(struct pet_data *pd,unsigned int tick,int data)
+{
+ struct block_list *target;
+
+ short range;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ target= map_id2bl(pd->target_id);
+
+ if(!status_check_skilluse(&pd->bl, target, 0, 0))
+ return 0;
+
+ if(target == NULL || pd->bl.m != target->m || target->prev == NULL ||
+ !check_distance_bl(&pd->bl, target, pd->db->range3))
+ {
+ pet_unlocktarget(pd);
+ return 0;
+ }
+
+ range = pd->db->range;
+ if (battle_iswalking(&pd->bl)) range++;
+ if (battle_iswalking(target)) range++;
+ if(!check_distance_bl(&pd->bl, target, range))
+ return 0;
+ if(battle_config.monster_attack_direction_change)
+ pd->dir=map_calc_dir(&pd->bl, target->x,target->y );
+
+ clif_fixpetpos(pd);
+
+ pd->target_lv = battle_weapon_attack(&pd->bl,target,tick,0);
+
+ pd->attackabletime = tick + status_get_adelay(&pd->bl);
+
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ pd->state.state=MS_ATTACK;
+ return 0;
+}
+
+static int petskill_castend(struct pet_data *pd,unsigned int tick,int data);
+static int petskill_castend2(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, short skill_x, short skill_y, unsigned int tick);
+
+/*==========================================
+ * Pet Attack Skill [Skotlex]
+ *------------------------------------------
+ */
+static int pet_attackskill(struct pet_data *pd, unsigned int tick, int data)
+{
+
+ struct block_list *bl;
+
+ nullpo_retr(0, pd);
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ bl=map_id2bl(pd->target_id);
+ if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range3))
+ {
+ pet_unlocktarget(pd);
+ return 0;
+ }
+
+ petskill_use(pd, bl, pd->a_skill->id, pd->a_skill->lv, tick);
+ return 0;
+}
+
+struct castend_delay { //[Skotlex] For passing skill info after casting
+ struct pet_data *src;
+ int target;
+ short id;
+ short lv;
+ short x,y;
+};
+
+/*==========================================
+ * Pet Skill Use [Skotlex]
+ *------------------------------------------
+ */
+int petskill_use(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, unsigned int tick)
+{
+ int casttime;
+ struct castend_delay *dat;
+
+ nullpo_retr(0, pd);
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.casting_flag)
+ return 1; //Will not interrupt an already casting skill.
+
+ if(!status_check_skilluse(&pd->bl, target, skill_id, 0))
+ return 0; //Cannot target....
+
+ if(pd->timer != -1) //Cancel whatever else the pet is doing.
+ delete_timer(pd->timer, pet_timer);
+
+ if(battle_config.monster_attack_direction_change)
+ pd->dir=map_calc_dir(&pd->bl, target->x, target->y );
+ clif_fixpetpos(pd);
+
+ //Casting time
+ casttime=skill_castfix(&pd->bl, skill_id, skill_lv, 0);
+
+ pet_stop_walking(pd,1);
+ pd->attackabletime = tick;
+ pd->state.state=MS_ATTACK;
+
+ if (casttime > 0)
+ {
+ pd->attackabletime += casttime;
+
+ dat = (struct castend_delay *)aCalloc(1, sizeof(struct castend_delay));
+ dat->src = pd;
+ dat->target = target->id;
+ dat->id = skill_id;
+ dat->lv = skill_lv;
+ dat->x = target->x;
+ dat->y = target->y;
+
+ pd->state.casting_flag = 1;
+ if (skill_get_inf(skill_id) & INF_GROUND_SKILL)
+ clif_skillcasting( &pd->bl, pd->bl.id, 0, dat->x, dat->y, skill_id,casttime);
+ else
+ clif_skillcasting( &pd->bl, pd->bl.id, dat->target, 0,0, skill_id,casttime);
+
+ pd->timer = add_timer(pd->attackabletime,pet_timer,pd->bl.id,(int)dat);
+ } else {
+ petskill_castend2(pd, target, skill_id, skill_lv, target->x, target->y, tick);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Pet Attack Cast End [Skotlex]
+ *------------------------------------------
+ */
+static int petskill_castend(struct pet_data *pd,unsigned int tick,int data)
+{
+ struct castend_delay *dat = (struct castend_delay *)data;
+ struct block_list *target = map_id2bl(dat->target);
+ pd->state.casting_flag = 0;
+ if (dat && pd == dat->src)
+ petskill_castend2(pd, target, dat->id, dat->lv, dat->x, dat->y, tick);
+ aFree(dat);
+ return 0;
+}
+
+/*==========================================
+ * Pet Attack Cast End2 [Skotlex]
+ *------------------------------------------
+ */
+static int petskill_castend2(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, short skill_x, short skill_y, unsigned int tick)
+{ //Invoked after the casting time has passed.
+ short delaytime =0;
+
+ nullpo_retr(0, pd);
+
+ pd->state.state=MS_IDLE;
+
+ if (skill_get_inf(skill_id) & INF_GROUND_SKILL)
+ { //Area skill
+ skill_castend_pos2(&pd->bl, skill_x, skill_y, skill_id, skill_lv, tick,0);
+ } else { //Targeted Skill
+ if (!target || !status_check_skilluse(&pd->bl, target, skill_id, 1))
+ return 0;
+ //Skills with inf = 4 (cast on self) have view range (assumed party skills)
+ if(!check_distance_bl(&pd->bl, target,
+ (skill_get_inf(skill_id) & INF_SELF_SKILL?battle_config.area_size:skill_get_range2(&pd->bl, skill_id, skill_lv))))
+ return 0;
+ switch( skill_get_nk(skill_id) )
+ {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(&pd->bl,target, skill_id, skill_lv,tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&pd->bl,target,skill_id,skill_lv,tick,0);
+ break;
+ }
+ }
+
+ if (pd->timer != -1) //The above skill casting could had changed the state (Abracadabra?)
+ return 0;
+
+ delaytime = skill_delayfix(&pd->bl,skill_id, skill_lv, 0);
+ if (delaytime < MIN_PETTHINKTIME)
+ delaytime = status_get_adelay(&pd->bl);
+ pd->attackabletime = tick + delaytime;
+ if (pd->target_id)
+ { //Resume attacking
+ pd->state.state=MS_ATTACK;
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int pet_walk(struct pet_data *pd,unsigned int tick,int data)
+{
+ int i;
+ int x,y,dx,dy;
+
+ nullpo_retr(0, pd);
+
+ pd->state.state=MS_IDLE;
+ if(pd->walkpath.path_pos >= pd->walkpath.path_len || pd->walkpath.path_pos != data)
+ return 0;
+
+ pd->walkpath.path_half ^= 1;
+ if(pd->walkpath.path_half==0){
+ pd->walkpath.path_pos++;
+ if(pd->state.change_walk_target){
+ pet_walktoxy_sub(pd);
+ return 0;
+ }
+ }
+ else {
+ if(pd->walkpath.path[pd->walkpath.path_pos] >= 8)
+ return 1;
+
+ x = pd->bl.x;
+ y = pd->bl.y;
+
+ pd->dir=pd->walkpath.path[pd->walkpath.path_pos];
+ dx = dirx[pd->dir];
+ dy = diry[pd->dir];
+
+ if(map_getcell(pd->bl.m,x+dx,y+dy,CELL_CHKNOPASS)){
+ pet_walktoxy_sub(pd);
+ return 0;
+ }
+
+ pd->state.state=MS_WALK;
+ map_foreachinmovearea(clif_petoutsight,pd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,pd);
+
+ x += dx;
+ y += dy;
+ map_moveblock(&pd->bl, x, y, tick);
+
+ map_foreachinmovearea(clif_petinsight,pd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,pd);
+ pd->state.state=MS_IDLE;
+ }
+ if((i=calc_next_walk_step(pd))>0){
+ i = i>>1;
+ if(i < 1 && pd->walkpath.path_half == 0)
+ i = 1;
+ pd->timer=add_timer(tick+i,pet_timer,pd->bl.id,pd->walkpath.path_pos);
+ pd->state.state=MS_WALK;
+
+ if(pd->walkpath.path_pos >= pd->walkpath.path_len)
+ clif_fixpetpos(pd);
+ }
+ return 0;
+}
+
+int pet_stopattack(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ pd->target_id=0;
+ if(pd->state.state == MS_ATTACK)
+ pet_changestate(pd,MS_IDLE,0);
+
+ return 0;
+}
+
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type)
+{
+ struct pet_data *pd;
+ int rate;
+
+ pd = sd->pd;
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL ||
+ sd->pet.intimate < battle_config.pet_support_min_friendly ||
+ sd->pet.hungry < 1 ||
+ pd->class_ == status_get_class(bl) ||
+ pd->state.state == MS_DELAY)
+ return 0;
+
+ if(pd->bl.m != bl->m ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range2))
+ return 0;
+
+ if (!status_check_skilluse(&pd->bl, bl, 0, 0))
+ return 0;
+
+ if(!type) {
+ rate = sd->petDB->attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(sd->petDB->attack_rate > 0 && rate <= 0)
+ rate = 1;
+ } else {
+ rate = sd->petDB->defence_attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(sd->petDB->defence_attack_rate > 0 && rate <= 0)
+ rate = 1;
+ }
+ if(rand()%10000 < rate)
+ {
+ if(pd->target_id == 0 || rand()%10000 < sd->petDB->change_target_rate)
+ pd->target_id = bl->id;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Pet SC Check [Skotlex]
+ *------------------------------------------
+ */
+int pet_sc_check(struct map_session_data *sd, int type)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ pd = sd->pd;
+
+ if (pd == NULL ||
+ (battle_config.pet_equip_required && pd->equip == 0) ||
+ pd->recovery == NULL ||
+ pd->recovery->timer != -1 ||
+ pd->recovery->type != type)
+ return 1;
+
+ pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+int pet_changestate(struct pet_data *pd,int state,int type)
+{
+ unsigned int tick;
+ int i;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if (pd->state.casting_flag)
+ skill_castcancel(&pd->bl, 0);
+ if(pd->timer != -1)
+ delete_timer(pd->timer,pet_timer);
+ pd->timer=-1;
+ pd->state.state=state;
+
+ switch(state) {
+ case MS_WALK:
+ if((i=calc_next_walk_step(pd)) > 0){
+ i = i>>2;
+ pd->timer=add_timer(gettick()+i,pet_timer,pd->bl.id,0);
+ } else
+ pd->state.state=MS_IDLE;
+ break;
+ case MS_ATTACK:
+ tick = gettick();
+ i=DIFF_TICK(pd->attackabletime,tick);
+ if(i>0 && i<2000)
+ pd->timer=add_timer(pd->attackabletime,pet_timer,pd->bl.id,0);
+ else
+ pd->timer=add_timer(tick+1,pet_timer,pd->bl.id,0);
+ break;
+ case MS_DELAY:
+ pd->timer=add_timer(gettick()+type,pet_timer,pd->bl.id,0);
+ break;
+ }
+
+ return 0;
+}
+
+static int pet_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct pet_data *pd;
+
+ pd=(struct pet_data*)map_id2bl(id);
+ if(pd == NULL || pd->bl.type != BL_PET)
+ return 1;
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->timer != tid){
+ if(battle_config.error_log)
+ ShowError("pet_timer %d != %d\n",pd->timer,tid);
+ return 0;
+ }
+ pd->timer=-1;
+
+ if(pd->bl.prev == NULL)
+ return 1;
+
+ switch(pd->state.state){
+ case MS_WALK:
+ pet_walk(pd,tick,data);
+ break;
+ case MS_ATTACK:
+ if (pd->msd == NULL) //Is this even possible?
+ break;
+ if (pc_isdead(pd->msd))
+ { //Stop attacking when master died.
+ pet_stopattack(pd);
+ break;
+ }
+ if (pd->state.casting_flag)
+ { //There is a skill being cast.
+ petskill_castend(pd, tick, data);
+ break;
+ }
+ if (battle_config.pet_status_support &&
+ pd->a_skill &&
+ (!battle_config.pet_equip_required || pd->equip > 0) &&
+ (rand()%100 < (pd->a_skill->rate +pd->msd->pet.intimate*pd->a_skill->bonusrate/1000))
+ )
+ { //Skotlex: Use pet's skill
+ pet_attackskill(pd,tick,data);
+ break;
+ }
+ pet_attack(pd,tick,data);
+ break;
+ case MS_DELAY:
+ pet_changestate(pd,MS_IDLE,0);
+ break;
+ default:
+ if(battle_config.error_log)
+ ShowError("pet_timer : %d ?\n",pd->state.state);
+ break;
+ }
+
+ return 0;
+}
+
+static int pet_walktoxy_sub(struct pet_data *pd)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,pd->to_x,pd->to_y,0))
+ return 1;
+ memcpy(&pd->walkpath,&wpd,sizeof(wpd));
+
+ pd->state.change_walk_target=0;
+ pet_changestate(pd,MS_WALK,0);
+ clif_movepet(pd);
+// if(battle_config.etc_log)
+// printf("walkstart\n");
+
+ return 0;
+}
+
+int pet_walktoxy(struct pet_data *pd,int x,int y)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.state == MS_WALK && path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,x,y,0))
+ return 1;
+
+ pd->to_x=x;
+ pd->to_y=y;
+
+ if(pd->state.state == MS_WALK) {
+ pd->state.change_walk_target=1;
+ } else {
+ return pet_walktoxy_sub(pd);
+ }
+
+ return 0;
+}
+
+int pet_stop_walking(struct pet_data *pd,int type)
+{
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(pd->state.state == MS_WALK || pd->state.state == MS_IDLE) {
+ pd->walkpath.path_len=0;
+ pd->to_x=pd->bl.x;
+ pd->to_y=pd->bl.y;
+ }
+ if(type&0x01)
+ clif_fixpetpos(pd);
+ if(type&~0xff)
+ pet_changestate(pd,MS_DELAY,type>>8);
+ else
+ pet_changestate(pd,MS_IDLE,0);
+
+ return 0;
+}
+
+static int pet_hungry(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ int interval,t;
+
+
+ sd=map_id2sd(id);
+ if(sd==NULL)
+ return 1;
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->pet_hungry_timer != tid){
+ if(battle_config.error_log)
+ ShowError("pet_hungry_timer %d != %d\n",sd->pet_hungry_timer,tid);
+ return 0;
+ }
+ sd->pet_hungry_timer = -1;
+ if(!sd->status.pet_id || !sd->pd || !sd->petDB)
+ return 1;
+
+ sd->pet.hungry--;
+ t = sd->pet.intimate;
+ if(sd->pet.hungry < 0) {
+ if(sd->pd->target_id > 0)
+ pet_stopattack(sd->pd);
+ sd->pet.hungry = 0;
+ sd->pet.intimate -= battle_config.pet_hungry_friendly_decrease;
+ if(sd->pet.intimate <= 0) {
+ sd->pet.intimate = 0;
+ if(battle_config.pet_status_support && t > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+ }
+ status_calc_pet(sd, 0);
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ }
+ clif_send_petdata(sd,2,sd->pet.hungry);
+
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = sd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ sd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0);
+
+ return 0;
+}
+
+int search_petDB_index(int key,int type)
+{
+ int i;
+
+ for(i=0;i<MAX_PET_DB;i++) {
+ if(pet_db[i].class_ <= 0)
+ continue;
+ switch(type) {
+ case PET_CLASS:
+ if(pet_db[i].class_ == key)
+ return i;
+ break;
+ case PET_CATCH:
+ if(pet_db[i].itemID == key)
+ return i;
+ break;
+ case PET_EGG:
+ if(pet_db[i].EggID == key)
+ return i;
+ break;
+ case PET_EQUIP:
+ if(pet_db[i].AcceID == key)
+ return i;
+ break;
+ case PET_FOOD:
+ if(pet_db[i].FoodID == key)
+ return i;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+int pet_hungry_timer_delete(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->pet_hungry_timer != -1) {
+ delete_timer(sd->pet_hungry_timer,pet_hungry);
+ sd->pet_hungry_timer = -1;
+ }
+
+ return 0;
+}
+
+int pet_remove_map(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pd) {
+
+ struct pet_data *pd=sd->pd; // [Valaris]
+ skill_cleartimerskill(&pd->bl); //Just in case pets get a timer-based skill.
+ //[Skotlex] clear bonus data
+ if (pd->status)
+ {
+ aFree(pd->status);
+ pd->status = NULL;
+ }
+ if (pd->a_skill)
+ {
+ aFree(pd->a_skill);
+ pd->a_skill = NULL;
+ }
+ if (pd->s_skill)
+ {
+ if (pd->s_skill->timer != -1)
+ {
+ if (sd->pd->s_skill->id)
+ delete_timer(sd->pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(sd->pd->s_skill->timer, pet_heal_timer);
+ }
+ aFree(pd->s_skill);
+ pd->s_skill = NULL;
+ }
+ if(pd->recovery)
+ {
+ if(pd->recovery->timer != -1)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ aFree(pd->recovery);
+ pd->recovery = NULL;
+ }
+ if(pd->bonus)
+ {
+ if (pd->bonus->timer != -1)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ aFree(pd->bonus);
+ pd->bonus = NULL;
+ }
+ if (pd->loot)
+ {
+ if (pd->loot->item)
+ aFree(pd->loot->item);
+ // if (pd->loot->timer != -1)
+ // delete_timer(pd->loot->timer, pet_loot_timer);
+ aFree (pd->loot);
+ pd->loot = NULL;
+ }
+ pd->state.skillbonus=-1;
+ if(sd->state.perfect_hiding) sd->state.perfect_hiding=0; // end additions
+
+ pet_changestate(sd->pd,MS_IDLE,0);
+ if(sd->pet_hungry_timer != -1)
+ pet_hungry_timer_delete(sd);
+ clif_clearchar_area(&sd->pd->bl,0);
+ map_delblock(&sd->pd->bl);
+ map_deliddb(&sd->pd->bl);
+ aFree(sd->pd);
+ sd->pd = NULL;
+ }
+ return 0;
+}
+struct delay_item_drop {
+ int m,x,y;
+ int nameid,amount;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+struct delay_item_drop2 {
+ int m,x,y;
+ struct item item_data;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+};
+
+int pet_performance(struct map_session_data *sd)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, pd=sd->pd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ pet_stop_walking(pd,2000<<8);
+ clif_pet_performance(&pd->bl,rand()%pet_performance_val(sd) + 1);
+ // ƒ‹[ƒg‚µ‚½Item‚ð—Ž‚Æ‚³‚¹‚é
+ pet_lootitem_drop(pd,NULL);
+
+ return 0;
+}
+
+int pet_return_egg(struct map_session_data *sd)
+{
+ struct item tmp_item;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pd) {
+ // ƒ‹[ƒg‚µ‚½Item‚ð—Ž‚Æ‚³‚¹‚é
+ pet_lootitem_drop(sd->pd,sd);
+ pet_remove_map(sd);
+ sd->status.pet_id = 0;
+ sd->pd = NULL;
+
+ if(sd->petDB == NULL)
+ return 1;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = sd->petDB->EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = (short)0xff00;
+ tmp_item.card[1] = GetWord(sd->pet.pet_id,0);
+ tmp_item.card[2] = GetWord(sd->pet.pet_id,1);
+ tmp_item.card[3] = sd->pet.rename_flag;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ sd->pet.incuvate = 1;
+ if(battle_config.pet_status_support && sd->pet.intimate > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ chrif_save(sd,0); //FIXME: Do we really need to save the char when returning to pet? Seems like a waste, and unexploitable as the pet data is just moved to an item in the inventory. [Skotlex]
+
+ sd->pet.rename_flag = 0; //Prevents future captured pets from starting as "beloved" [Skotlex]
+ sd->petDB = NULL;
+ }
+
+ return 0;
+}
+
+int pet_data_init(struct map_session_data *sd)
+{
+ struct pet_data *pd;
+ int i=0,interval=0;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.account_id != sd->pet.account_id || sd->status.char_id != sd->pet.char_id ||
+ sd->status.pet_id != sd->pet.pet_id) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ i = search_petDB_index(sd->pet.class_,PET_CLASS);
+ if(i < 0) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ sd->petDB = &pet_db[i];
+ sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data));
+ pd->bl.m = sd->bl.m;
+ pd->bl.x = pd->to_x = sd->bl.x;
+ pd->bl.y = pd->to_y = sd->bl.y;
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ pd->bl.x = pd->to_x;
+ pd->bl.y = pd->to_y;
+ pd->bl.id = npc_get_new_npc_id();
+ memcpy(pd->name, sd->pet.name, NAME_LENGTH-1);
+ pd->class_ = sd->pet.class_;
+ pd->db = mob_db(pd->class_);
+ pd->equip = sd->pet.equip;
+ pd->dir = sd->dir;
+ pd->speed = sd->petDB->speed;
+ pd->bl.subtype = MONS;
+ pd->bl.type = BL_PET;
+ pd->state.state = MS_IDLE;
+ pd->timer = -1;
+ pd->next_walktime = pd->attackabletime = pd->last_thinktime = gettick();
+ pd->msd = sd;
+
+ map_addiddb(&pd->bl);
+
+ // initialise
+ if (battle_config.pet_lv_rate) //[Skotlex]
+ pd->status = (struct pet_status *) aCalloc(1,sizeof(struct pet_status));
+
+ status_calc_pet(sd,1);
+
+ pd->state.skillbonus = -1;
+ if (battle_config.pet_status_support) //Skotlex
+ run_script(pet_db[i].script,0,sd->bl.id,0);
+
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++)
+ pd->skilltimerskill[i].timer = -1;
+
+ if(sd->pet_hungry_timer != -1)
+ pet_hungry_timer_delete(sd);
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = sd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ sd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0);
+ return 0;
+}
+
+int pet_birth_process(struct map_session_data *sd)
+{
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && sd->pet.incuvate == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ sd->pet.incuvate = 0;
+ sd->pet.account_id = sd->status.account_id;
+ sd->pet.char_id = sd->status.char_id;
+ sd->status.pet_id = sd->pet.pet_id;
+ if(pet_data_init(sd)) {
+ sd->status.pet_id = 0;
+ sd->pet.incuvate = 1;
+ sd->pet.account_id = 0;
+ sd->pet.char_id = 0;
+ return 1;
+ }
+
+ intif_save_petdata(sd->status.account_id,&sd->pet);
+ chrif_save(sd,0); //FIXME: As before, is it REALLY Needed to save the char for hatching a pet? [Skotlex]
+
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ return 0;
+}
+
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag)
+{
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 1;
+ if(flag == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ memcpy(&sd->pet,p,sizeof(struct s_pet));
+ if(sd->pet.incuvate == 1)
+ pet_birth_process(sd);
+ else {
+ pet_data_init(sd);
+ if(sd->pd && sd->bl.prev != NULL) {
+ map_addblock(&sd->pd->bl);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+// clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+ }
+ }
+ if(battle_config.pet_status_support && sd->pet.intimate > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+
+ return 0;
+}
+
+int pet_select_egg(struct map_session_data *sd,short egg_index)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[egg_index].card[0] == (short)0xff00)
+ intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) );
+ else {
+ if(battle_config.error_log)
+ ShowError("wrong egg item inventory %d\n",egg_index);
+ }
+ pc_delitem(sd,egg_index,1,0);
+
+ return 0;
+}
+
+int pet_catch_process1(struct map_session_data *sd,int target_class)
+{
+ nullpo_retr(0, sd);
+
+ sd->catch_target_class = target_class;
+ clif_catch_process(sd);
+
+ return 0;
+}
+
+int pet_catch_process2(struct map_session_data *sd,int target_id)
+{
+ struct mob_data *md;
+ int i=0,pet_catch_rate=0;
+
+ nullpo_retr(1, sd);
+
+ if (sd->itemid > 0)
+ { //Consume the pet lure [Skotlex]
+ if ((i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, items moved or they tried an exploit.
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+ //Delete the item
+ sd->itemid = sd->itemindex = -1;
+ pc_delitem(sd,i,1,0);
+ }
+
+ md=(struct mob_data*)map_id2bl(target_id);
+ if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ i = search_petDB_index(md->class_,PET_CLASS);
+ //catch_target_class == 0 is used for universal lures. [Skotlex]
+ //for now universal lures do not include bosses.
+ if (sd->catch_target_class == 0 && !(md->db->mode&0x20))
+ sd->catch_target_class = md->class_;
+ if(i < 0 || sd->catch_target_class != md->class_) {
+ clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them.
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ //target_id‚É‚æ‚é“G¨—‘”»’è
+// if(battle_config.etc_log)
+// printf("mob_id = %d, mob_class = %d\n",md->bl.id,md->class_);
+ //¬Œ÷‚Ìê‡
+ pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->db->lv)*30 + sd->paramc[5]*20)*(200 - md->hp*100/md->db->max_hp)/100;
+ if(pet_catch_rate < 1) pet_catch_rate = 1;
+ if(battle_config.pet_catch_rate != 100)
+ pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100;
+
+ if(rand()%10000 < pet_catch_rate) {
+ mob_remove_map(md,0);
+ mob_setdelayspawn(md->bl.id);
+ clif_pet_rulet(sd,1);
+// if(battle_config.etc_log)
+// printf("rulet success %d\n",target_id);
+ intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv,
+ pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname);
+ }
+ else
+ {
+ sd->catch_target_class = -1;
+ clif_pet_rulet(sd,0);
+ }
+
+ return 0;
+}
+
+int pet_get_egg(int account_id,int pet_id,int flag)
+{ //This function is invoked when a new pet has been created, and at no other time!
+ struct map_session_data *sd;
+ struct item tmp_item;
+ int i=0,ret=0;
+
+ if(!flag) {
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 1;
+
+ i = search_petDB_index(sd->catch_target_class,PET_CLASS);
+ sd->catch_target_class = -1;
+
+ if(i >= 0) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = pet_db[i].EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = (short)0xff00;
+ tmp_item.card[1] = GetWord(pet_id,0);
+ tmp_item.card[2] = GetWord(pet_id,1);
+ tmp_item.card[3] = 0; //New pets are not named.
+ if((ret = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ else
+ intif_delete_petdata(pet_id);
+ }
+
+ return 0;
+}
+
+int pet_menu(struct map_session_data *sd,int menunum)
+{
+ nullpo_retr(0, sd);
+ if (sd->pd == NULL)
+ return 1;
+
+ switch(menunum) {
+ case 0:
+ clif_send_petstatus(sd);
+ break;
+ case 1:
+ pet_food(sd);
+ break;
+ case 2:
+ pet_performance(sd);
+ break;
+ case 3:
+ pet_return_egg(sd);
+ break;
+ case 4:
+ pet_unequipitem(sd);
+ break;
+ }
+ return 0;
+}
+
+int pet_change_name(struct map_session_data *sd,char *name)
+{
+ int i;
+
+ nullpo_retr(1, sd);
+
+ if((sd->pd == NULL) || (sd->pet.rename_flag == 1 && battle_config.pet_rename == 0))
+ return 1;
+
+ for(i=0;i<NAME_LENGTH && name[i];i++){
+ if( !(name[i]&0xe0) || name[i]==0x7f)
+ return 1;
+ }
+
+ pet_stop_walking(sd->pd,1);
+
+ memcpy(sd->pet.name, name, NAME_LENGTH-1);
+ memcpy(sd->pd->name, name, NAME_LENGTH-1);
+
+ clif_clearchar_area(&sd->pd->bl,0);
+ clif_spawnpet(sd->pd);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ sd->pet.rename_flag = 1;
+ clif_pet_equip(sd->pd,sd->pet.equip);
+ clif_send_petstatus(sd);
+
+ return 0;
+}
+
+int pet_equipitem(struct map_session_data *sd,int index)
+{
+ int nameid;
+
+ nullpo_retr(1, sd);
+
+ nameid = sd->status.inventory[index].nameid;
+ if(sd->petDB == NULL)
+ return 1;
+ if(sd->petDB->AcceID == 0 || nameid != sd->petDB->AcceID || sd->pet.equip != 0) {
+ clif_equipitemack(sd,0,0,0);
+ return 1;
+ }
+ else {
+ pc_delitem(sd,index,1,0);
+ sd->pet.equip = sd->pd->equip = nameid;
+ status_calc_pc(sd,0);
+ clif_pet_equip(sd->pd,nameid);
+ if (battle_config.pet_equip_required)
+ { //Skotlex: start support timers if needd
+ if (sd->pd->s_skill && sd->pd->s_skill->timer == -1)
+ {
+ if (sd->pd->s_skill->id)
+ sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0);
+ else
+ sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0);
+ }
+ if (sd->pd->bonus && sd->pd->bonus->timer == -1)
+ sd->pd->bonus->timer=add_timer(gettick()+sd->pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+ }
+ }
+
+ return 0;
+}
+
+int pet_unequipitem(struct map_session_data *sd)
+{
+ struct item tmp_item;
+ int nameid,flag;
+
+ nullpo_retr(1, sd);
+
+ if(sd->petDB == NULL)
+ return 1;
+ if(sd->pet.equip == 0)
+ return 1;
+
+ nameid = sd->pet.equip;
+ sd->pet.equip = sd->pd->equip = 0;
+ status_calc_pc(sd,0);
+ clif_pet_equip(sd->pd,0);
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = nameid;
+ tmp_item.identify = 1;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ if (battle_config.pet_equip_required)
+ { //Skotlex: halt support timers if needed
+ if (sd->pd->s_skill && sd->pd->s_skill->timer != -1)
+ {
+ if (sd->pd->s_skill->id)
+ delete_timer(sd->pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(sd->pd->s_skill->timer, pet_heal_timer);
+ sd->pd->s_skill->timer = -1;
+ }
+ if (sd->pd->bonus && sd->pd->bonus->timer != -1)
+ {
+ delete_timer(sd->pd->bonus->timer, pet_skill_bonus_timer);
+ sd->pd->bonus->timer = -1;
+ }
+ }
+
+ return 0;
+}
+
+int pet_food(struct map_session_data *sd)
+{
+ int i,k,t;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->petDB == NULL)
+ return 1;
+ i=pc_search_inventory(sd,sd->petDB->FoodID);
+ if(i < 0) {
+ clif_pet_food(sd,sd->petDB->FoodID,0);
+ return 1;
+ }
+ pc_delitem(sd,i,1,0);
+ t = sd->pet.intimate;
+ if(sd->pet.hungry > 90)
+ sd->pet.intimate -= sd->petDB->r_full;
+ else if(sd->pet.hungry > 75) {
+ if(battle_config.pet_friendly_rate != 100)
+ k = (sd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
+ else
+ k = sd->petDB->r_hungry;
+ k = k >> 1;
+ if(k <= 0)
+ k = 1;
+ sd->pet.intimate += k;
+ }
+ else {
+ if(battle_config.pet_friendly_rate != 100)
+ k = (sd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
+ else
+ k = sd->petDB->r_hungry;
+ sd->pet.intimate += k;
+ }
+ if(sd->pet.intimate <= 0) {
+ sd->pet.intimate = 0;
+ if(battle_config.pet_status_support && t > 0) {
+ if(sd->bl.prev != NULL)
+ status_calc_pc(sd,0);
+ else
+ status_calc_pc(sd,2);
+ }
+ }
+ else if(sd->pet.intimate > 1000)
+ sd->pet.intimate = 1000;
+ status_calc_pet(sd, 0);
+ sd->pet.hungry += sd->petDB->fullness;
+ if(sd->pet.hungry > 100)
+ sd->pet.hungry = 100;
+
+ clif_send_petdata(sd,2,sd->pet.hungry);
+ clif_send_petdata(sd,1,sd->pet.intimate);
+ clif_pet_food(sd,sd->petDB->FoodID,1);
+
+ return 0;
+}
+
+static int pet_randomwalk(struct pet_data *pd,int tick)
+{
+ const int retrycount=20;
+ int speed;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ speed = status_get_speed(&pd->bl);
+
+ if(DIFF_TICK(pd->next_walktime,tick) < 0){
+ int i,x,y,c,d=12-pd->move_fail_count;
+ if(d<5) d=5;
+ for(i=0;i<retrycount;i++){
+ int r=rand();
+ x=pd->bl.x+r%(d*2+1)-d;
+ y=pd->bl.y+r/(d*2+1)%(d*2+1)-d;
+ if((map_getcell(pd->bl.m,x,y,CELL_CHKPASS))&&( pet_walktoxy(pd,x,y)==0)){
+ pd->move_fail_count=0;
+ break;
+ }
+ if(i+1>=retrycount){
+ pd->move_fail_count++;
+ if(pd->move_fail_count>1000){
+ if(battle_config.error_log)
+ ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->class_);
+ pd->move_fail_count=0;
+ pet_changestate(pd,MS_DELAY,60000);
+ return 0;
+ }
+ }
+ }
+ for(i=c=0;i<pd->walkpath.path_len;i++){
+ if(pd->walkpath.path[i]&1)
+ c+=speed*14/10;
+ else
+ c+=speed;
+ }
+ pd->next_walktime = tick+rand()%3000+3000+c;
+
+ return 1;
+ }
+ return 0;
+}
+
+static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick)
+{
+ struct map_session_data *sd = pd->msd;
+ struct block_list *bl = NULL;
+ int dist,i=0,dx,dy,ret;
+ int mode,race;
+
+ nullpo_retr(0, pd);
+
+ sd = pd->msd;
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL)
+ return 0;
+
+ if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME)
+ return 0;
+ pd->last_thinktime=tick;
+
+ if(pd->state.state == MS_DELAY || pd->bl.m != sd->bl.m)
+ return 0;
+ // ƒyƒbƒg‚É‚æ‚郋[ƒg
+ if(!pd->target_id && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(gettick(),pd->loot->timer)>0)
+ map_foreachinarea(pet_ai_sub_hard_lootsearch,pd->bl.m,
+ pd->bl.x-6,pd->bl.y-6, //If pet_ai_sub_hard_lootsearch limits itself to a range of 5, WHY use AREA_SIZE here? o.O [Skotlex]
+ pd->bl.x+6,pd->bl.y+6,
+// pd->bl.x-AREA_SIZE*2,pd->bl.y-AREA_SIZE*2,
+// pd->bl.x+AREA_SIZE*2,pd->bl.y+AREA_SIZE*2,
+ BL_ITEM,pd,&i);
+
+ if(sd->pet.intimate > 0) {
+ dist = distance_bl(&sd->bl, &pd->bl);
+ if(dist > 12) {
+ if(pd->target_id > 0)
+ pet_unlocktarget(pd);
+ if(pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(&sd->bl, pd->to_x, pd->to_y, 3))
+ return 0;
+ pd->speed = (sd->speed>>1);
+ if(pd->speed <= 0)
+ pd->speed = 1;
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ if(pet_walktoxy(pd,pd->to_x,pd->to_y))
+ pet_randomwalk(pd,tick);
+ }
+ else if(pd->target_id - MAX_FLOORITEM > 0) { //Mob targeted
+ mode=pd->db->mode;
+ race=pd->db->race;
+ bl= map_id2bl(pd->target_id);
+ if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range3))
+ pet_unlocktarget(pd);
+ else if(!battle_check_range(&pd->bl,bl,pd->db->range) && !pd->state.casting_flag){ //Skotlex Don't interrupt a casting spell when targed moved
+ if(pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(bl, pd->to_x, pd->to_y, 2))
+ return 0;
+ if(!pet_can_reach(pd, bl->x, bl->y))
+ pet_unlocktarget(pd);
+ else {
+ i=0;
+ pd->speed = status_get_speed(&pd->bl);
+ do {
+ if(i==0) { // ʼn‚ÍAEGIS‚Æ“¯‚¶•û–@‚ÅŒŸõ
+ dx=bl->x - pd->bl.x;
+ dy=bl->y - pd->bl.y;
+ if(dx<0) dx++;
+ else if(dx>0) dx--;
+ if(dy<0) dy++;
+ else if(dy>0) dy--;
+ }
+ else { // ‚¾‚ß‚È‚çAthenaŽ®(ƒ‰ƒ“ƒ_ƒ€)
+ dx=bl->x - pd->bl.x + rand()%3 - 1;
+ dy=bl->y - pd->bl.y + rand()%3 - 1;
+ }
+ ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ i++;
+ } while(ret && i<5);
+
+ if(ret) { // ˆÚ“®•s‰Â”\‚ÈŠ‚©‚ç‚ÌUŒ‚‚È‚ç2•à‰º‚é
+ if(dx<0) dx=2;
+ else if(dx>0) dx=-2;
+ if(dy<0) dy=2;
+ else if(dy>0) dy=-2;
+ pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ }
+ }
+ }
+ else {
+ if(pd->state.state==MS_WALK)
+ pet_stop_walking(pd,1);
+ if(pd->state.state==MS_ATTACK)
+ return 0;
+ pet_changestate(pd,MS_ATTACK,0);
+ }
+ }
+ else if(pd->target_id > 0 && pd->loot){ //Item Targeted, attempt loot
+ struct block_list *bl_item;
+ struct flooritem_data *fitem;
+
+ bl_item = map_id2bl(pd->target_id);
+ if(bl_item == NULL || bl_item->type != BL_ITEM ||bl_item->m != pd->bl.m ||
+ (dist=distance_bl(&pd->bl, bl_item))>=5){
+ // ‰“‚·‚¬‚é‚©ƒAƒCƒeƒ€‚ª‚È‚­‚È‚Á‚½
+ pet_unlocktarget(pd);
+ }
+ else if(dist){
+ if(pd->timer != -1 && pd->state.state!=MS_ATTACK && (DIFF_TICK(pd->next_walktime,tick)<0 || !check_distance_blxy(bl_item, pd->to_x, pd->to_y, 0)))
+ return 0; // Šù‚Ɉړ®’†
+
+ pd->next_walktime=tick+500;
+ dx=bl_item->x - pd->bl.x;
+ dy=bl_item->y - pd->bl.y;
+
+ ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy);
+ }
+ else{ // ƒAƒCƒeƒ€‚Ü‚Å‚½‚Ç‚è’…‚¢‚½
+ fitem = (struct flooritem_data *)bl_item;
+ if(pd->state.state==MS_ATTACK)
+ return 0; // UŒ‚’†
+ if(pd->state.state==MS_WALK){ // •às’†‚È‚ç’âŽ~
+ pet_stop_walking(pd,1);
+ }
+ if(pd->loot->count < pd->loot->max){
+ memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0]));
+ pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount;
+ map_clearflooritem(bl_item->id);
+ pet_unlocktarget(pd);
+ }
+ else { //Maxed out on carried items
+ pet_unlocktarget(pd);
+ return 0;
+ }
+ }
+ }
+ else {
+ if(dist <= 3 || pd->state.casting_flag || (pd->timer != -1 && pd->state.state == MS_WALK && check_distance_blxy(&sd->bl, pd->to_x,pd->to_y, 3)))
+ return 0;
+ pd->speed = status_get_speed(&pd->bl);
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->dir);
+ if(pet_walktoxy(pd,pd->to_x,pd->to_y))
+ pet_randomwalk(pd,tick);
+ }
+ }
+ else {
+ pd->speed = status_get_speed(&pd->bl);
+ if(pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ pet_randomwalk(pd,tick);
+ }
+
+ return 0;
+}
+
+static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
+{
+ unsigned int tick;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, ap);
+
+ tick=va_arg(ap,unsigned int);
+ if(sd->status.pet_id && sd->pd && sd->petDB)
+ pet_ai_sub_hard(sd->pd,tick);
+
+ return 0;
+}
+
+static int pet_ai_hard(int tid,unsigned int tick,int id,int data)
+{
+ clif_foreachclient(pet_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
+{
+ struct pet_data* pd;
+ int *itc;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, pd=va_arg(ap,struct pet_data *));
+ nullpo_retr(0, itc=va_arg(ap,int *));
+
+ if(!pd->target_id){
+ struct flooritem_data *fitem = (struct flooritem_data *)bl;
+ struct map_session_data *sd = NULL;
+ // ƒ‹[ƒgŒ –³‚µ
+ if(fitem && fitem->first_get_id>0)
+ sd = map_id2sd(fitem->first_get_id);
+ // Removed [Valaris]
+ //if((pd->lootitem_weight + (itemdb_search(fitem->item_data.))->weight * fitem->item_data.amount) > battle_config.pet_weight)
+ // return 0;
+
+ if(pd->loot == NULL || pd->loot->item == NULL || (pd->loot->count >= pd->loot->max) || (sd && sd->pd != pd))
+ return 0;
+ if(bl->m == pd->bl.m && check_distance_bl(&pd->bl, bl, 5)){
+ if( pet_can_reach(pd,bl->x,bl->y) // “ž’B‰Â”\«”»’è
+ && rand()%1000<1000/(++(*itc)) ){ // ”͈͓àPC‚Å“™Šm—¦‚É‚·‚é
+ pd->target_id=bl->id;
+ }
+ }
+ }
+ return 0;
+}
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd)
+{
+ int i,flag=0;
+
+ if(pd){
+ if(pd->loot) {
+ for(i=0;i<pd->loot->count;i++) {
+ struct delay_item_drop2 *ditem;
+
+ ditem=(struct delay_item_drop2 *)aCalloc(1,sizeof(struct delay_item_drop2));
+ memcpy(&ditem->item_data,&pd->loot->item[i],sizeof(pd->loot->item[0]));
+ ditem->m = pd->bl.m;
+ ditem->x = pd->bl.x;
+ ditem->y = pd->bl.y;
+ ditem->first_sd = 0;
+ ditem->second_sd = 0;
+ ditem->third_sd = 0;
+ // —Ž‚Æ‚³‚È‚¢‚Å’¼ÚPC‚ÌItem—“‚Ö
+ if(sd){
+ if((flag = pc_additem(sd,&ditem->item_data,ditem->item_data.amount))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+ }
+ aFree(ditem);
+ }
+ else
+ add_timer(gettick()+540+i,pet_delay_item_drop2,(int)ditem,0);
+ }
+ //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori)
+ memset(pd->loot->item,0,pd->loot->max * sizeof(struct item));
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+ pd->loot->timer = gettick()+10000; // 10*1000ms‚ÌŠÔE‚í‚È‚¢
+ }
+ }
+ return 1;
+}
+
+int pet_delay_item_drop2(int tid,unsigned int tick,int id,int data)
+{
+ struct delay_item_drop2 *ditem;
+
+ ditem=(struct delay_item_drop2 *)id;
+
+ map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
+
+ aFree(ditem);
+ return 0;
+}
+
+/*==========================================
+ * pet bonus giving skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct pet_data *pd;
+ int timer = 0;
+
+ if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->bonus->timer != tid) {
+ if(battle_config.error_log)
+ {
+ ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid);
+ pd->bonus->timer = -1;
+ }
+ return 0;
+ }
+
+ // determine the time for the next timer
+ if (pd->state.skillbonus == 0) {
+ // pet bonuses are not active at the moment, so,
+ pd->state.skillbonus = 1;
+ timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect
+ } else if (pd->state.skillbonus == 1) {
+ // pet bonuses are already active, so,
+ pd->state.skillbonus = 0;
+ timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again
+ if (timer <= 0) //Always active bonus
+ timer = MIN_PETTHINKTIME;
+ }
+
+ // add/remove our bonuses
+ status_calc_pc(sd, 0);
+
+ // wait for the next timer
+ pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet recovery skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+
+ if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->recovery == NULL || pd->recovery->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->recovery)
+ ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid);
+ else
+ ShowError("pet_recovery_timer called with no recovery skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(sd->sc_data && sd->sc_data[pd->recovery->type].timer != -1)
+ { //Display a heal animation?
+ //Detoxify is chosen for now.
+ clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1);
+ status_change_end(&sd->bl,pd->recovery->type,-1);
+ clif_emotion(&pd->bl, 33);
+ }
+
+ pd->recovery->timer = -1;
+
+ return 0;
+}
+
+int pet_heal_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+ short rate = 100;
+
+ if(sd==NULL || sd->bl.type!=BL_PC || sd->pd == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill == NULL || pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->s_skill)
+ ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid);
+ else
+ ShowError("pet_heal_timer called with no support skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(pc_isdead(sd) ||
+ (rate = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp ||
+ (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp ||
+ (rate = pd->state.casting_flag) || //Another skill is in effect
+ (rate = pd->state.state) == MS_WALK) //Better wait until the pet stops moving (MS_WALK is 2)
+ { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ if (pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1);
+ pc_heal(sd,pd->s_skill->lv,0);
+
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_support_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=(struct map_session_data*)map_id2bl(id);
+ struct pet_data *pd;
+ short rate = 100;
+ if(sd==NULL || sd->bl.type!=BL_PC || sd->pd == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill == NULL || pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ {
+ if (pd->s_skill)
+ ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid);
+ else
+ ShowError("pet_skill_support_timer called with no support skill defined (tid=%d)\n",tid);
+ }
+ return 0;
+ }
+
+ if(pc_isdead(sd) ||
+ (rate = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp ||
+ (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp ||
+ (rate = pd->state.casting_flag) || //Another skill is in effect
+ (rate = pd->state.state) == MS_WALK) //Better wait until the pet stops moving (MS_WALK is 2)
+ { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ if (pd->state.state == MS_ATTACK)
+ pet_stopattack(pd);
+ petskill_use(pd, &sd->bl, pd->s_skill->id, pd->s_skill->lv, tick);
+
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ *ƒyƒbƒgƒf[ƒ^“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+int read_petdb()
+{
+ FILE *fp;
+ char line[1024];
+ int nameid,i,k;
+ int j=0;
+ int lines;
+ char *filename[]={"pet_db.txt","pet_db2.txt"};
+ char *str[32],*p,*np;
+
+
+//Remove any previous scripts in case reloaddb was invoked.
+ for(j =0; j < MAX_PET_DB; j++)
+ if (pet_db[j].script) {
+ aFree(pet_db[j].script);
+ pet_db[j].script = NULL;
+ }
+ j = 0;
+ memset(pet_db,0,sizeof(pet_db));
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowError("can't read %s\n",line);
+ return -1;
+ }
+ lines = 0;
+ while(fgets(line,1020,fp) && j < MAX_PET_DB){
+
+ lines++;
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ for(k=0,p=line;k<20;k++){
+ if((np=strchr(p,','))!=NULL){
+ str[k]=p;
+ *np=0;
+ p=np+1;
+ } else {
+ str[k]=p;
+ p+=strlen(p);
+ }
+ }
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || nameid>2000)
+ continue;
+
+ //MobID,Name,JName,ItemID,EggID,AcceID,FoodID,"Fullness (1‰ñ‚̉a‚Å‚Ì–ž• “x‘‰Á—¦%)","HungryDeray (/min)","R_Hungry (‹ó• Žž‰a‚â‚èe–§“x‘‰Á—¦%)","R_Full (‚Æ‚Ä‚à–ž• Žž‰a‚â‚èe–§“xŒ¸­—¦%)","Intimate (•ßŠlŽže–§“x%)","Die (Ž€–SŽže–§“xŒ¸­—¦%)","Capture (•ßŠl—¦%)",(Name)
+ pet_db[j].class_ = nameid;
+ memcpy(pet_db[j].name,str[1],NAME_LENGTH-1);
+ memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1);
+ pet_db[j].itemID=atoi(str[3]);
+ pet_db[j].EggID=atoi(str[4]);
+ pet_db[j].AcceID=atoi(str[5]);
+ pet_db[j].FoodID=atoi(str[6]);
+ pet_db[j].fullness=atoi(str[7]);
+ pet_db[j].hungry_delay=atoi(str[8])*1000;
+ pet_db[j].r_hungry=atoi(str[9]);
+ if(pet_db[j].r_hungry <= 0)
+ pet_db[j].r_hungry=1;
+ pet_db[j].r_full=atoi(str[10]);
+ pet_db[j].intimate=atoi(str[11]);
+ pet_db[j].die=atoi(str[12]);
+ pet_db[j].capture=atoi(str[13]);
+ pet_db[j].speed=atoi(str[14]);
+ pet_db[j].s_perfor=(char)atoi(str[15]);
+ pet_db[j].talk_convert_class=atoi(str[16]);
+ pet_db[j].attack_rate=atoi(str[17]);
+ pet_db[j].defence_attack_rate=atoi(str[18]);
+ pet_db[j].change_target_rate=atoi(str[19]);
+ pet_db[j].script = NULL;
+ if((np=strchr(p,'{'))==NULL)
+ continue;
+ pet_db[j].script = parse_script((unsigned char *) np,lines);
+ j++;
+ }
+ if (j >= MAX_PET_DB)
+ ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB);
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ŠÖŒW‰Šú‰»ˆ—
+ *------------------------------------------
+ */
+int do_init_pet(void)
+{
+ memset(pet_db,0,sizeof(pet_db));
+ read_petdb();
+
+ add_timer_func_list(pet_timer,"pet_timer");
+ add_timer_func_list(pet_hungry,"pet_hungry");
+ add_timer_func_list(pet_ai_hard,"pet_ai_hard");
+ add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris]
+ add_timer_func_list(pet_delay_item_drop2,"pet_delay_item_drop2");
+ add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex]
+ add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris]
+ add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris]
+ add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME);
+
+ return 0;
+}
+
+int do_final_pet(void) {
+ int i;
+ for(i = 0;i < MAX_PET_DB; i++) {
+ if(pet_db[i].script) {
+ aFree(pet_db[i].script);
+ pet_db[i].script = NULL;
+ }
+ }
+ return 0;
+}
diff --git a/src/map/pet.h b/src/map/pet.h
new file mode 100644
index 000000000..feadafdf3
--- /dev/null
+++ b/src/map/pet.h
@@ -0,0 +1,73 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PET_H_
+#define _PET_H_
+
+#define MAX_PET_DB 300
+#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex]
+
+struct pet_db {
+ short class_;
+ char name[NAME_LENGTH],jname[NAME_LENGTH];
+ short itemID;
+ short EggID;
+ short AcceID;
+ short FoodID;
+ int fullness;
+ int hungry_delay;
+ int r_hungry;
+ int r_full;
+ int intimate;
+ int die;
+ int capture;
+ int speed;
+ char s_perfor;
+ int talk_convert_class;
+ int attack_rate;
+ int defence_attack_rate;
+ int change_target_rate;
+ unsigned char *script;
+};
+extern struct pet_db pet_db[MAX_PET_DB];
+
+enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD };
+
+int pet_hungry_val(struct map_session_data *sd);
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type);
+int pet_sc_check(struct map_session_data *sd, int type); //Skotlex
+int pet_stopattack(struct pet_data *pd);
+int pet_changestate(struct pet_data *pd,int state,int type);
+int pet_walktoxy(struct pet_data *pd,int x,int y);
+int pet_stop_walking(struct pet_data *pd,int type);
+int search_petDB_index(int key,int type);
+int pet_hungry_timer_delete(struct map_session_data *sd);
+int pet_remove_map(struct map_session_data *sd);
+int pet_data_init(struct map_session_data *sd);
+int pet_birth_process(struct map_session_data *sd);
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag);
+int pet_select_egg(struct map_session_data *sd,short egg_index);
+int pet_catch_process1(struct map_session_data *sd,int target_class);
+int pet_catch_process2(struct map_session_data *sd,int target_id);
+int pet_get_egg(int account_id,int pet_id,int flag);
+int pet_menu(struct map_session_data *sd,int menunum);
+int pet_change_name(struct map_session_data *sd,char *name);
+int pet_equipitem(struct map_session_data *sd,int index);
+int pet_unequipitem(struct map_session_data *sd);
+int pet_food(struct map_session_data *sd);
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd);
+int pet_delay_item_drop2(int tid,unsigned int tick,int id,int data);
+int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap);
+int petskill_use(struct pet_data *pd, struct block_list *target, short skill_id, short skill_lv, unsigned int tick); // [Skotlex]
+int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex]
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_skillsupport_timer(int tid,unsigned int tick,int id,int data); // [Skotlex]
+
+int read_petdb(void);
+int do_init_pet(void);
+int do_final_pet(void);
+
+#endif
+
diff --git a/src/map/script.c b/src/map/script.c
new file mode 100644
index 000000000..a2a3f1a28
--- /dev/null
+++ b/src/map/script.c
@@ -0,0 +1,10736 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+//#define DEBUG_FUNCIN
+//#define DEBUG_DISP
+//#define DEBUG_RUN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/lock.h"
+#include "../common/nullpo.h"
+
+#include "map.h"
+#include "clif.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "pc.h"
+#include "status.h"
+#include "script.h"
+#include "storage.h"
+#include "mob.h"
+#include "npc.h"
+#include "pet.h"
+#include "intif.h"
+#include "skill.h"
+#include "chat.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "charcommand.h"
+#include "log.h"
+#include "showmsg.h"
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+#include "strlib.h"
+#endif
+
+#define SCRIPT_BLOCK_SIZE 256
+
+#define FETCH(n, t) \
+ if(st->end>st->start+(n)) \
+ (t)=conv_num(st,&(st->stack->stack_data[st->start+(n)]));
+
+enum { LABEL_NEXTLINE=1,LABEL_START };
+static unsigned char * script_buf = NULL;
+static int script_pos,script_size;
+
+char *str_buf;
+int str_pos,str_size;
+static struct str_data_struct {
+ int type;
+ int str;
+ int backpatch;
+ int label;
+ int (*func)(struct script_state *);
+ int val;
+ int next;
+} *str_data = NULL;
+int str_num=LABEL_START,str_data_size;
+int str_hash[16];
+
+static struct dbt *mapreg_db=NULL;
+static struct dbt *mapregstr_db=NULL;
+static int mapreg_dirty=-1;
+char mapreg_txt[256]="save/mapreg.txt";
+#define MAPREG_AUTOSAVE_INTERVAL (10*1000)
+
+static struct dbt *scriptlabel_db=NULL;
+static struct dbt *userfunc_db=NULL;
+
+struct dbt* script_get_label_db(){ return scriptlabel_db; }
+struct dbt* script_get_userfunc_db(){ return userfunc_db; }
+
+static char pos[11][100] = {"“ª","‘Ì","¶Žè","‰EŽè","ƒ[ƒu","ŒC","ƒAƒNƒZƒTƒŠ[1","ƒAƒNƒZƒTƒŠ[2","“ª2","“ª3","‘•’…‚µ‚Ä‚¢‚È‚¢"};
+
+struct Script_Config script_config;
+
+static int parse_cmd;
+
+// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc )
+// [Eoe / jA 1080, 1081, 1094, 1164]
+enum { TYPE_NULL = 0 , TYPE_IF , TYPE_SWITCH , TYPE_WHILE , TYPE_FOR , TYPE_DO , TYPE_USERFUNC};
+static struct {
+ struct {
+ int type;
+ int index;
+ int count;
+ int flag;
+ } curly[256]; // ‰EƒJƒbƒR‚Ìî•ñ
+ int curly_count; // ‰EƒJƒbƒR‚Ì”
+ int index; // ƒXƒNƒŠƒvƒg“à‚ÅŽg—p‚µ‚½\•¶‚Ì”
+} syntax;
+unsigned char* parse_curly_close(unsigned char *p);
+unsigned char* parse_syntax_close(unsigned char *p);
+unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag);
+unsigned char* parse_syntax(unsigned char *p);
+static int parse_syntax_for_flag = 0;
+
+extern int current_equip_item_index; //for New CARS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
+int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
+int potion_target=0;
+
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+// [zBuffer] SQL Mapreg Saving/Loading Database Declaration
+char mapregsql_db[32] = "mapreg";
+char mapregsql_db_varname[32] = "varname";
+char mapregsql_db_index[32] = "index";
+char mapregsql_db_value[32] = "value";
+char tmp_sql[65535];
+// --------------------------------------------------------
+#endif
+
+/*==========================================
+ * ƒ[ƒJƒ‹ƒvƒƒgƒ^ƒCƒv錾 (•K—v‚È•¨‚Ì‚Ý)
+ *------------------------------------------
+ */
+unsigned char* parse_subexpr(unsigned char *,int);
+#ifndef TXT_ONLY
+int buildin_query_sql(struct script_state *st);
+#endif
+int buildin_atoi(struct script_state *st);
+int buildin_axtoi(struct script_state *st);
+int buildin_mes(struct script_state *st);
+int buildin_goto(struct script_state *st);
+int buildin_callsub(struct script_state *st);
+int buildin_callfunc(struct script_state *st);
+int buildin_return(struct script_state *st);
+int buildin_getarg(struct script_state *st);
+int buildin_next(struct script_state *st);
+int buildin_close(struct script_state *st);
+int buildin_close2(struct script_state *st);
+int buildin_menu(struct script_state *st);
+int buildin_rand(struct script_state *st);
+int buildin_warp(struct script_state *st);
+int buildin_areawarp(struct script_state *st);
+int buildin_warpchar(struct script_state *st); // [LuzZza]
+int buildin_warpparty(struct script_state *st); //[Fredzilla]
+int buildin_warpguild(struct script_state *st); //[Fredzilla]
+int buildin_heal(struct script_state *st);
+int buildin_itemheal(struct script_state *st);
+int buildin_percentheal(struct script_state *st);
+int buildin_jobchange(struct script_state *st);
+int buildin_input(struct script_state *st);
+int buildin_setlook(struct script_state *st);
+int buildin_set(struct script_state *st);
+int buildin_setarray(struct script_state *st);
+int buildin_cleararray(struct script_state *st);
+int buildin_copyarray(struct script_state *st);
+int buildin_getarraysize(struct script_state *st);
+int buildin_deletearray(struct script_state *st);
+int buildin_getelementofarray(struct script_state *st);
+int buildin_getitem(struct script_state *st);
+int buildin_getitem2(struct script_state *st);
+int buildin_getnameditem(struct script_state *st);
+int buildin_grouprandomitem(struct script_state *st);
+int buildin_makeitem(struct script_state *st);
+int buildin_delitem(struct script_state *st);
+int buildin_delitem2(struct script_state *st);
+int buildin_enableitemuse(struct script_state *st);
+int buildin_disableitemuse(struct script_state *st);
+int buildin_viewpoint(struct script_state *st);
+int buildin_countitem(struct script_state *st);
+int buildin_countitem2(struct script_state *st);
+int buildin_checkweight(struct script_state *st);
+int buildin_readparam(struct script_state *st);
+int buildin_getcharid(struct script_state *st);
+int buildin_getpartyname(struct script_state *st);
+int buildin_getpartymember(struct script_state *st);
+int buildin_getguildname(struct script_state *st);
+int buildin_getguildmaster(struct script_state *st);
+int buildin_getguildmasterid(struct script_state *st);
+int buildin_strcharinfo(struct script_state *st);
+int buildin_getequipid(struct script_state *st);
+int buildin_getequipname(struct script_state *st);
+int buildin_getbrokenid(struct script_state *st); // [Valaris]
+int buildin_repair(struct script_state *st); // [Valaris]
+int buildin_getequipisequiped(struct script_state *st);
+int buildin_getequipisenableref(struct script_state *st);
+int buildin_getequipisidentify(struct script_state *st);
+int buildin_getequiprefinerycnt(struct script_state *st);
+int buildin_getequipweaponlv(struct script_state *st);
+int buildin_getequippercentrefinery(struct script_state *st);
+int buildin_successrefitem(struct script_state *st);
+int buildin_failedrefitem(struct script_state *st);
+int buildin_cutin(struct script_state *st);
+int buildin_cutincard(struct script_state *st);
+int buildin_statusup(struct script_state *st);
+int buildin_statusup2(struct script_state *st);
+int buildin_bonus(struct script_state *st);
+int buildin_bonus2(struct script_state *st);
+int buildin_bonus3(struct script_state *st);
+int buildin_bonus4(struct script_state *st);
+int buildin_skill(struct script_state *st);
+int buildin_addtoskill(struct script_state *st); // [Valaris]
+int buildin_guildskill(struct script_state *st);
+int buildin_getskilllv(struct script_state *st);
+int buildin_getgdskilllv(struct script_state *st);
+int buildin_basicskillcheck(struct script_state *st);
+int buildin_getgmlevel(struct script_state *st);
+int buildin_end(struct script_state *st);
+int buildin_checkoption(struct script_state *st);
+int buildin_setoption(struct script_state *st);
+int buildin_setcart(struct script_state *st);
+int buildin_checkcart(struct script_state *st); // check cart [Valaris]
+int buildin_setfalcon(struct script_state *st);
+int buildin_checkfalcon(struct script_state *st); // check falcon [Valaris]
+int buildin_setriding(struct script_state *st);
+int buildin_checkriding(struct script_state *st); // check for pecopeco [Valaris]
+int buildin_savepoint(struct script_state *st);
+int buildin_gettimetick(struct script_state *st);
+int buildin_gettime(struct script_state *st);
+int buildin_gettimestr(struct script_state *st);
+int buildin_openstorage(struct script_state *st);
+int buildin_guildopenstorage(struct script_state *st);
+int buildin_itemskill(struct script_state *st);
+int buildin_produce(struct script_state *st);
+int buildin_monster(struct script_state *st);
+int buildin_areamonster(struct script_state *st);
+int buildin_killmonster(struct script_state *st);
+int buildin_killmonsterall(struct script_state *st);
+int buildin_clone(struct script_state *st);
+int buildin_doevent(struct script_state *st);
+int buildin_donpcevent(struct script_state *st);
+int buildin_addtimer(struct script_state *st);
+int buildin_deltimer(struct script_state *st);
+int buildin_addtimercount(struct script_state *st);
+int buildin_initnpctimer(struct script_state *st);
+int buildin_stopnpctimer(struct script_state *st);
+int buildin_startnpctimer(struct script_state *st);
+int buildin_setnpctimer(struct script_state *st);
+int buildin_getnpctimer(struct script_state *st);
+int buildin_attachnpctimer(struct script_state *st); // [celest]
+int buildin_detachnpctimer(struct script_state *st); // [celest]
+int buildin_playerattached(struct script_state *st); // [Skotlex]
+int buildin_announce(struct script_state *st);
+int buildin_mapannounce(struct script_state *st);
+int buildin_areaannounce(struct script_state *st);
+int buildin_getusers(struct script_state *st);
+int buildin_getmapusers(struct script_state *st);
+int buildin_getareausers(struct script_state *st);
+int buildin_getareadropitem(struct script_state *st);
+int buildin_enablenpc(struct script_state *st);
+int buildin_disablenpc(struct script_state *st);
+int buildin_enablearena(struct script_state *st); // Added by RoVeRT
+int buildin_disablearena(struct script_state *st); // Added by RoVeRT
+int buildin_hideoffnpc(struct script_state *st);
+int buildin_hideonnpc(struct script_state *st);
+int buildin_sc_start(struct script_state *st);
+int buildin_sc_start2(struct script_state *st);
+int buildin_sc_start4(struct script_state *st);
+int buildin_sc_end(struct script_state *st);
+int buildin_getscrate(struct script_state *st);
+int buildin_debugmes(struct script_state *st);
+int buildin_catchpet(struct script_state *st);
+int buildin_birthpet(struct script_state *st);
+int buildin_resetlvl(struct script_state *st);
+int buildin_resetstatus(struct script_state *st);
+int buildin_resetskill(struct script_state *st);
+int buildin_changebase(struct script_state *st);
+int buildin_changesex(struct script_state *st);
+int buildin_waitingroom(struct script_state *st);
+int buildin_delwaitingroom(struct script_state *st);
+int buildin_enablewaitingroomevent(struct script_state *st);
+int buildin_disablewaitingroomevent(struct script_state *st);
+int buildin_getwaitingroomstate(struct script_state *st);
+int buildin_warpwaitingpc(struct script_state *st);
+int buildin_attachrid(struct script_state *st);
+int buildin_detachrid(struct script_state *st);
+int buildin_isloggedin(struct script_state *st);
+int buildin_setmapflagnosave(struct script_state *st);
+int buildin_setmapflag(struct script_state *st);
+int buildin_removemapflag(struct script_state *st);
+int buildin_pvpon(struct script_state *st);
+int buildin_pvpoff(struct script_state *st);
+int buildin_gvgon(struct script_state *st);
+int buildin_gvgoff(struct script_state *st);
+int buildin_emotion(struct script_state *st);
+int buildin_maprespawnguildid(struct script_state *st);
+int buildin_agitstart(struct script_state *st); // <Agit>
+int buildin_agitend(struct script_state *st);
+int buildin_agitcheck(struct script_state *st); // <Agitcheck>
+int buildin_flagemblem(struct script_state *st); // Flag Emblem
+int buildin_getcastlename(struct script_state *st);
+int buildin_getcastledata(struct script_state *st);
+int buildin_setcastledata(struct script_state *st);
+int buildin_requestguildinfo(struct script_state *st);
+int buildin_getequipcardcnt(struct script_state *st);
+int buildin_successremovecards(struct script_state *st);
+int buildin_failedremovecards(struct script_state *st);
+int buildin_marriage(struct script_state *st);
+int buildin_wedding_effect(struct script_state *st);
+int buildin_divorce(struct script_state *st);
+int buildin_ispartneron(struct script_state *st); // MouseJstr
+int buildin_getpartnerid(struct script_state *st); // MouseJstr
+int buildin_getchildid(struct script_state *st); // Skotlex
+int buildin_getmotherid(struct script_state *st); // Lupus
+int buildin_getfatherid(struct script_state *st); // Lupus
+int buildin_warppartner(struct script_state *st); // MouseJstr
+int buildin_getitemname(struct script_state *st);
+int buildin_getitemslots(struct script_state *st);
+int buildin_makepet(struct script_state *st);
+int buildin_getexp(struct script_state *st);
+int buildin_getinventorylist(struct script_state *st);
+int buildin_getskilllist(struct script_state *st);
+int buildin_clearitem(struct script_state *st);
+int buildin_classchange(struct script_state *st);
+int buildin_misceffect(struct script_state *st);
+int buildin_soundeffect(struct script_state *st);
+int buildin_soundeffectall(struct script_state *st);
+int buildin_setcastledata(struct script_state *st);
+int buildin_mapwarp(struct script_state *st);
+int buildin_inittimer(struct script_state *st);
+int buildin_stoptimer(struct script_state *st);
+int buildin_cmdothernpc(struct script_state *st);
+int buildin_mobcount(struct script_state *st);
+int buildin_strmobinfo(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_guardian(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_guardianinfo(struct script_state *st); // Script for displaying mob info [Valaris]
+int buildin_petskillbonus(struct script_state *st); // petskillbonus [Valaris]
+int buildin_petrecovery(struct script_state *st); // pet skill for curing status [Valaris]
+int buildin_petloot(struct script_state *st); // pet looting [Valaris]
+int buildin_petheal(struct script_state *st); // pet healing [Valaris]
+//int buildin_petmag(struct script_state *st); // pet magnificat [Valaris]
+int buildin_petskillattack(struct script_state *st); // pet skill attacks [Skotlex]
+int buildin_petskillattack2(struct script_state *st); // pet skill attacks [Skotlex]
+int buildin_petskillsupport(struct script_state *st); // pet support skill [Valaris]
+int buildin_skilleffect(struct script_state *st); // skill effects [Celest]
+int buildin_npcskilleffect(struct script_state *st); // skill effects for npcs [Valaris]
+int buildin_specialeffect(struct script_state *st); // special effect script [Valaris]
+int buildin_specialeffect2(struct script_state *st); // special effect script [Valaris]
+int buildin_nude(struct script_state *st); // nude [Valaris]
+int buildin_atcommand(struct script_state *st); // [MouseJstr]
+int buildin_charcommand(struct script_state *st); // [MouseJstr]
+int buildin_movenpc(struct script_state *st); // [MouseJstr]
+int buildin_message(struct script_state *st); // [MouseJstr]
+int buildin_npctalk(struct script_state *st); // [Valaris]
+int buildin_hasitems(struct script_state *st); // [Valaris]
+int buildin_getlook(struct script_state *st); //Lorky [Lupus]
+int buildin_getsavepoint(struct script_state *st); //Lorky [Lupus]
+int buildin_npcspeed(struct script_state *st); // [Valaris]
+int buildin_npcwalkto(struct script_state *st); // [Valaris]
+int buildin_npcstop(struct script_state *st); // [Valaris]
+int buildin_getmapxy(struct script_state *st); //get map position for player/npc/pet/mob by Lorky [Lupus]
+int buildin_checkoption1(struct script_state *st); // [celest]
+int buildin_checkoption2(struct script_state *st); // [celest]
+int buildin_guildgetexp(struct script_state *st); // [celest]
+int buildin_guildchangegm(struct script_state *st); // [Skotlex]
+int buildin_skilluseid(struct script_state *st); // originally by Qamera [celest]
+int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest]
+int buildin_logmes(struct script_state *st); // [Lupus]
+int buildin_summon(struct script_state *st); // [celest]
+int buildin_isnight(struct script_state *st); // [celest]
+int buildin_isday(struct script_state *st); // [celest]
+int buildin_isequipped(struct script_state *st); // [celest]
+int buildin_isequippedcnt(struct script_state *st); // [celest]
+int buildin_cardscnt(struct script_state *st); // [Lupus]
+int buildin_getrefine(struct script_state *st); // [celest]
+int buildin_adopt(struct script_state *st);
+int buildin_night(struct script_state *st);
+int buildin_day(struct script_state *st);
+int buildin_getusersname(struct script_state *st); //jA commands added [Lupus]
+int buildin_dispbottom(struct script_state *st);
+int buildin_recovery(struct script_state *st);
+int buildin_getpetinfo(struct script_state *st);
+int buildin_checkequipedcard(struct script_state *st);
+int buildin_globalmes(struct script_state *st);
+int buildin_jump_zero(struct script_state *st);
+int buildin_select(struct script_state *st);
+int buildin_getmapmobs(struct script_state *st); //jA addition end
+int buildin_unequip(struct script_state *st); // unequip [Spectre]
+int buildin_getstrlen(struct script_state *st); //strlen [valaris]
+int buildin_charisalpha(struct script_state *st);//isalpha [valaris]
+int buildin_fakenpcname(struct script_state *st); // [Lance]
+int buildin_compare(struct script_state *st); // Lordalfa, to bring strstr to Scripting Engine
+int buildin_getiteminfo(struct script_state *st); //[Lupus] returns Items Buy / sell Price, etc info
+int buildin_getequipcardid(struct script_state *st); //[Lupus] returns card id from quipped item card slot N
+// [zBuffer] List of mathematics commands --->
+int buildin_sqrt(struct script_state *st);
+int buildin_pow(struct script_state *st);
+int buildin_distance(struct script_state *st);
+// <--- [zBuffer] List of mathematics commands
+// [zBuffer] List of dynamic var commands --->
+int buildin_getd(struct script_state *st);
+int buildin_setd(struct script_state *st);
+// <--- [zBuffer] List of dynamic var commands
+int buildin_petstat(struct script_state *st); // [Lance] Pet Stat Rq: Dubby
+int buildin_callshop(struct script_state *st); // [Skotlex]
+void push_val(struct script_stack *stack,int type,int val);
+int run_func(struct script_state *st);
+
+int mapreg_setreg(int num,int val);
+int mapreg_setregstr(int num,const char *str);
+
+int buildin_setitemscript(struct script_state *st);
+int buildin_disguise(struct script_state *st);
+int buildin_undisguise(struct script_state *st);
+
+#ifdef PCRE_SUPPORT
+int buildin_defpattern(struct script_state *st); // MouseJstr
+int buildin_activatepset(struct script_state *st); // MouseJstr
+int buildin_deactivatepset(struct script_state *st); // MouseJstr
+int buildin_deletepset(struct script_state *st); // MouseJstr
+#endif
+
+struct {
+ int (*func)(struct script_state *);
+ char *name;
+ char *arg;
+} buildin_func[]={
+ {buildin_axtoi,"axtoi","s"},
+#ifndef TXT_ONLY
+ {buildin_query_sql, "query_sql", "s*"},
+#endif
+ {buildin_atoi,"atoi","s"},
+ {buildin_mes,"mes","s"},
+ {buildin_next,"next",""},
+ {buildin_close,"close",""},
+ {buildin_close2,"close2",""},
+ {buildin_menu,"menu","*"},
+ {buildin_goto,"goto","l"},
+ {buildin_callsub,"callsub","i*"},
+ {buildin_callfunc,"callfunc","s*"},
+ {buildin_return,"return","*"},
+ {buildin_getarg,"getarg","i"},
+ {buildin_jobchange,"jobchange","i*"},
+ {buildin_input,"input","*"},
+ {buildin_warp,"warp","sii"},
+ {buildin_areawarp,"areawarp","siiiisii"},
+ {buildin_warpchar,"warpchar","siii"}, // [LuzZza]
+ {buildin_warpparty,"warpparty","siii"}, // [Fredzilla]
+ {buildin_warpguild,"warpguild","siii"}, // [Fredzilla]
+ {buildin_setlook,"setlook","ii"},
+ {buildin_set,"set","ii"},
+ {buildin_setarray,"setarray","ii*"},
+ {buildin_cleararray,"cleararray","iii"},
+ {buildin_copyarray,"copyarray","iii"},
+ {buildin_getarraysize,"getarraysize","i"},
+ {buildin_deletearray,"deletearray","ii"},
+ {buildin_getelementofarray,"getelementofarray","ii"},
+ {buildin_getitem,"getitem","ii**"},
+ {buildin_getitem2,"getitem2","iiiiiiiii*"},
+ {buildin_getnameditem,"getnameditem","is"},
+ {buildin_grouprandomitem,"groupranditem","i"},
+ {buildin_makeitem,"makeitem","iisii"},
+ {buildin_delitem,"delitem","ii"},
+ {buildin_delitem2,"delitem2","iiiiiiiii"},
+ {buildin_enableitemuse,"enable_items",""},
+ {buildin_disableitemuse,"disable_items",""},
+ {buildin_cutin,"cutin","si"},
+ {buildin_cutincard,"cutincard","i"},
+ {buildin_viewpoint,"viewpoint","iiiii"},
+ {buildin_heal,"heal","ii"},
+ {buildin_itemheal,"itemheal","ii"},
+ {buildin_percentheal,"percentheal","ii"},
+ {buildin_rand,"rand","i*"},
+ {buildin_countitem,"countitem","i"},
+ {buildin_countitem2,"countitem2","iiiiiiii"},
+ {buildin_checkweight,"checkweight","ii"},
+ {buildin_readparam,"readparam","i*"},
+ {buildin_getcharid,"getcharid","i*"},
+ {buildin_getpartyname,"getpartyname","i"},
+ {buildin_getpartymember,"getpartymember","i"},
+ {buildin_getguildname,"getguildname","i"},
+ {buildin_getguildmaster,"getguildmaster","i"},
+ {buildin_getguildmasterid,"getguildmasterid","i"},
+ {buildin_strcharinfo,"strcharinfo","i"},
+ {buildin_getequipid,"getequipid","i"},
+ {buildin_getequipname,"getequipname","i"},
+ {buildin_getbrokenid,"getbrokenid","i"}, // [Valaris]
+ {buildin_repair,"repair","i"}, // [Valaris]
+ {buildin_getequipisequiped,"getequipisequiped","i"},
+ {buildin_getequipisenableref,"getequipisenableref","i"},
+ {buildin_getequipisidentify,"getequipisidentify","i"},
+ {buildin_getequiprefinerycnt,"getequiprefinerycnt","i"},
+ {buildin_getequipweaponlv,"getequipweaponlv","i"},
+ {buildin_getequippercentrefinery,"getequippercentrefinery","i"},
+ {buildin_successrefitem,"successrefitem","i"},
+ {buildin_failedrefitem,"failedrefitem","i"},
+ {buildin_statusup,"statusup","i"},
+ {buildin_statusup2,"statusup2","ii"},
+ {buildin_bonus,"bonus","ii"},
+ {buildin_bonus2,"bonus2","iii"},
+ {buildin_bonus3,"bonus3","iiii"},
+ {buildin_bonus4,"bonus4","iiiii"},
+ {buildin_skill,"skill","ii*"},
+ {buildin_addtoskill,"addtoskill","ii*"}, // [Valaris]
+ {buildin_guildskill,"guildskill","ii"},
+ {buildin_getskilllv,"getskilllv","i"},
+ {buildin_getgdskilllv,"getgdskilllv","ii"},
+ {buildin_basicskillcheck,"basicskillcheck","*"},
+ {buildin_getgmlevel,"getgmlevel","*"},
+ {buildin_end,"end",""},
+// {buildin_end,"break",""}, this might confuse advanced scripting support [Eoe]
+ {buildin_checkoption,"checkoption","i"},
+ {buildin_setoption,"setoption","i"},
+ {buildin_setcart,"setcart",""},
+ {buildin_checkcart,"checkcart","*"}, //fixed by Lupus (added '*')
+ {buildin_setfalcon,"setfalcon",""},
+ {buildin_checkfalcon,"checkfalcon","*"}, //fixed by Lupus (fixed wrong pointer, added '*')
+ {buildin_setriding,"setriding",""},
+ {buildin_checkriding,"checkriding","*"}, //fixed by Lupus (fixed wrong pointer, added '*')
+ {buildin_savepoint,"save","sii"},
+ {buildin_savepoint,"savepoint","sii"},
+ {buildin_gettimetick,"gettimetick","i"},
+ {buildin_gettime,"gettime","i"},
+ {buildin_gettimestr,"gettimestr","si"},
+ {buildin_openstorage,"openstorage",""},
+ {buildin_guildopenstorage,"guildopenstorage","*"},
+ {buildin_itemskill,"itemskill","iis"},
+ {buildin_produce,"produce","i"},
+ {buildin_monster,"monster","siisii*"},
+ {buildin_areamonster,"areamonster","siiiisii*"},
+ {buildin_killmonster,"killmonster","ss"},
+ {buildin_killmonsterall,"killmonsterall","s"},
+ {buildin_clone,"clone","siisi*"},
+ {buildin_doevent,"doevent","s"},
+ {buildin_donpcevent,"donpcevent","s"},
+ {buildin_addtimer,"addtimer","is"},
+ {buildin_deltimer,"deltimer","s"},
+ {buildin_addtimercount,"addtimercount","si"},
+ {buildin_initnpctimer,"initnpctimer","*"},
+ {buildin_stopnpctimer,"stopnpctimer","*"},
+ {buildin_startnpctimer,"startnpctimer","*"},
+ {buildin_setnpctimer,"setnpctimer","*"},
+ {buildin_getnpctimer,"getnpctimer","i*"},
+ {buildin_attachnpctimer,"attachnpctimer","*"}, // attached the player id to the npc timer [Celest]
+ {buildin_detachnpctimer,"detachnpctimer","*"}, // detached the player id from the npc timer [Celest]
+ {buildin_playerattached,"playerattached",""}, // returns id of the current attached player. [Skotlex]
+ {buildin_announce,"announce","si*"},
+ {buildin_mapannounce,"mapannounce","ssi*"},
+ {buildin_areaannounce,"areaannounce","siiiisi*"},
+ {buildin_getusers,"getusers","i"},
+ {buildin_getmapusers,"getmapusers","s"},
+ {buildin_getareausers,"getareausers","siiii"},
+ {buildin_getareadropitem,"getareadropitem","siiiii"},
+ {buildin_enablenpc,"enablenpc","s"},
+ {buildin_disablenpc,"disablenpc","s"},
+ {buildin_enablearena,"enablearena",""}, // Added by RoVeRT
+ {buildin_disablearena,"disablearena",""}, // Added by RoVeRT
+ {buildin_hideoffnpc,"hideoffnpc","s"},
+ {buildin_hideonnpc,"hideonnpc","s"},
+ {buildin_sc_start,"sc_start","iii*"},
+ {buildin_sc_start2,"sc_start2","iiii*"},
+ {buildin_sc_start4,"sc_start4","iiiiii*"},
+ {buildin_sc_end,"sc_end","i"},
+ {buildin_getscrate,"getscrate","ii*"},
+ {buildin_debugmes,"debugmes","s"},
+ {buildin_catchpet,"pet","i"},
+ {buildin_birthpet,"bpet",""},
+ {buildin_resetlvl,"resetlvl","i"},
+ {buildin_resetstatus,"resetstatus",""},
+ {buildin_resetskill,"resetskill",""},
+ {buildin_changebase,"changebase","i"},
+ {buildin_changesex,"changesex",""},
+ {buildin_waitingroom,"waitingroom","si*"},
+ {buildin_warpwaitingpc,"warpwaitingpc","sii"},
+ {buildin_delwaitingroom,"delwaitingroom","*"},
+ {buildin_enablewaitingroomevent,"enablewaitingroomevent","*"},
+ {buildin_disablewaitingroomevent,"disablewaitingroomevent","*"},
+ {buildin_getwaitingroomstate,"getwaitingroomstate","i*"},
+ {buildin_warpwaitingpc,"warpwaitingpc","sii*"},
+ {buildin_attachrid,"attachrid","i"},
+ {buildin_detachrid,"detachrid",""},
+ {buildin_isloggedin,"isloggedin","i"},
+ {buildin_setmapflagnosave,"setmapflagnosave","ssii"},
+ {buildin_setmapflag,"setmapflag","si"},
+ {buildin_removemapflag,"removemapflag","si"},
+ {buildin_pvpon,"pvpon","s"},
+ {buildin_pvpoff,"pvpoff","s"},
+ {buildin_gvgon,"gvgon","s"},
+ {buildin_gvgoff,"gvgoff","s"},
+ {buildin_emotion,"emotion","i*"},
+ {buildin_maprespawnguildid,"maprespawnguildid","sii"},
+ {buildin_agitstart,"agitstart",""}, // <Agit>
+ {buildin_agitend,"agitend",""},
+ {buildin_agitcheck,"agitcheck","i"}, // <Agitcheck>
+ {buildin_flagemblem,"flagemblem","i"}, // Flag Emblem
+ {buildin_getcastlename,"getcastlename","s"},
+ {buildin_getcastledata,"getcastledata","si*"},
+ {buildin_setcastledata,"setcastledata","sii"},
+ {buildin_requestguildinfo,"requestguildinfo","i*"},
+ {buildin_getequipcardcnt,"getequipcardcnt","i"},
+ {buildin_successremovecards,"successremovecards","i"},
+ {buildin_failedremovecards,"failedremovecards","ii"},
+ {buildin_marriage,"marriage","s"},
+ {buildin_wedding_effect,"wedding",""},
+ {buildin_divorce,"divorce",""},
+ {buildin_ispartneron,"ispartneron",""},
+ {buildin_getpartnerid,"getpartnerid",""},
+ {buildin_getchildid,"getchildid",""},
+ {buildin_getmotherid,"getmotherid",""},
+ {buildin_getfatherid,"getfatherid",""},
+ {buildin_warppartner,"warppartner","sii"},
+ {buildin_getitemname,"getitemname","i"},
+ {buildin_getitemslots,"getitemslots","i"},
+ {buildin_makepet,"makepet","i"},
+ {buildin_getexp,"getexp","ii"},
+ {buildin_getinventorylist,"getinventorylist",""},
+ {buildin_getskilllist,"getskilllist",""},
+ {buildin_clearitem,"clearitem",""},
+ {buildin_classchange,"classchange","ii"},
+ {buildin_misceffect,"misceffect","i"},
+ {buildin_soundeffect,"soundeffect","si"},
+ {buildin_soundeffectall,"soundeffectall","si"}, // SoundEffectAll [Codemaster]
+ {buildin_strmobinfo,"strmobinfo","ii"}, // display mob data [Valaris]
+ {buildin_guardian,"guardian","siisii*i"}, // summon guardians
+ {buildin_guardianinfo,"guardianinfo","i"}, // display guardian data [Valaris]
+ {buildin_petskillbonus,"petskillbonus","iiii"}, // [Valaris]
+ {buildin_petrecovery,"petrecovery","ii"}, // [Valaris]
+ {buildin_petloot,"petloot","i"}, // [Valaris]
+ {buildin_petheal,"petheal","iiii"}, // [Valaris]
+// {buildin_petmag,"petmag","iiii"}, // [Valaris]
+ {buildin_petskillattack,"petskillattack","iiii"}, // [Skotlex]
+ {buildin_petskillattack2,"petskillattack2","iiiii"}, // [Valaris]
+ {buildin_petskillsupport,"petskillsupport","iiiii"}, // [Skotlex]
+ {buildin_skilleffect,"skilleffect","ii"}, // skill effect [Celest]
+ {buildin_npcskilleffect,"npcskilleffect","iiii"}, // npc skill effect [Valaris]
+ {buildin_specialeffect,"specialeffect","i"}, // npc skill effect [Valaris]
+ {buildin_specialeffect2,"specialeffect2","i"}, // skill effect on players[Valaris]
+ {buildin_nude,"nude",""}, // nude command [Valaris]
+ {buildin_mapwarp,"mapwarp","ssii"}, // Added by RoVeRT
+ {buildin_inittimer,"inittimer",""},
+ {buildin_stoptimer,"stoptimer",""},
+ {buildin_cmdothernpc,"cmdothernpc","ss"},
+ {buildin_atcommand,"atcommand","*"}, // [MouseJstr]
+ {buildin_charcommand,"charcommand","*"}, // [MouseJstr]
+// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr]
+ {buildin_message,"message","s*"}, // [MouseJstr]
+ {buildin_npctalk,"npctalk","*"}, // [Valaris]
+ {buildin_hasitems,"hasitems","*"}, // [Valaris]
+ {buildin_mobcount,"mobcount","ss"},
+ {buildin_getlook,"getlook","i"},
+ {buildin_getsavepoint,"getsavepoint","i"},
+ {buildin_npcspeed,"npcspeed","i"}, // [Valaris]
+ {buildin_npcwalkto,"npcwalkto","ii"}, // [Valaris]
+ {buildin_npcstop,"npcstop",""}, // [Valaris]
+ {buildin_getmapxy,"getmapxy","siii*"}, //by Lorky [Lupus]
+ {buildin_checkoption1,"checkoption1","i"},
+ {buildin_checkoption2,"checkoption2","i"},
+ {buildin_guildgetexp,"guildgetexp","i"},
+ {buildin_guildchangegm,"guildchangegm","is"},
+ {buildin_skilluseid,"skilluseid","ii"}, // originally by Qamera [Celest]
+ {buildin_skilluseid,"doskill","ii"}, // since a lot of scripts would already use 'doskill'...
+ {buildin_skillusepos,"skillusepos","iiii"}, // [Celest]
+ {buildin_logmes,"logmes","s"}, //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus]
+ {buildin_summon,"summon","si*"}, // summons a slave monster [Celest]
+ {buildin_isnight,"isnight",""}, // check whether it is night time [Celest]
+ {buildin_isday,"isday",""}, // check whether it is day time [Celest]
+ {buildin_isequipped,"isequipped","i*"}, // check whether another item/card has been equipped [Celest]
+ {buildin_isequippedcnt,"isequippedcnt","i*"}, // check how many items/cards are being equipped [Celest]
+ {buildin_cardscnt,"cardscnt","i*"}, // check how many items/cards are being equipped in the same arm [Lupus]
+ {buildin_getrefine,"getrefine","*"}, // returns the refined number of the current item, or an item with index specified [celest]
+ {buildin_adopt,"adopt","sss"}, // allows 2 parents to adopt a child
+ {buildin_night,"night",""}, // sets the server to night time
+ {buildin_day,"day",""}, // sets the server to day time
+#ifdef PCRE_SUPPORT
+ {buildin_defpattern, "defpattern", "iss"}, // Define pattern to listen for [MouseJstr]
+ {buildin_activatepset, "activatepset", "i"}, // Activate a pattern set [MouseJstr]
+ {buildin_deactivatepset, "deactivatepset", "i"}, // Deactive a pattern set [MouseJstr]
+ {buildin_deletepset, "deletepset", "i"}, // Delete a pattern set [MouseJstr]
+#endif
+ {buildin_dispbottom,"dispbottom","s"}, //added from jA [Lupus]
+ {buildin_getusersname,"getusersname","*"},
+ {buildin_recovery,"recovery",""},
+ {buildin_getpetinfo,"getpetinfo","i"},
+ {buildin_checkequipedcard,"checkequipedcard","i"},
+ {buildin_jump_zero,"jump_zero","ii"}, //for future jA script compatibility
+ {buildin_select,"select","*"}, //for future jA script compatibility
+ {buildin_globalmes,"globalmes","s*"},
+ {buildin_getmapmobs,"getmapmobs","s"}, //end jA addition
+ {buildin_unequip,"unequip","i"}, // unequip command [Spectre]
+ {buildin_getstrlen,"getstrlen","s"}, //strlen [Valaris]
+ {buildin_charisalpha,"charisalpha","si"}, //isalpha [Valaris]
+ {buildin_fakenpcname,"fakenpcname","ssi"}, // [Lance]
+ {buildin_compare,"compare","ss"}, // Lordalfa - To bring strstr to scripting Engine.
+ {buildin_getiteminfo,"getiteminfo","ii"}, //[Lupus] returns Items Buy / sell Price, etc info
+ {buildin_getequipcardid,"getequipcardid","ii"}, //[Lupus] returns CARD ID or other info from CARD slot N of equipped item
+ // [zBuffer] List of mathematics commands --->
+ {buildin_sqrt,"sqrt","i"},
+ {buildin_pow,"pow","ii"},
+ {buildin_distance,"distance","iiii"},
+ // <--- [zBuffer] List of mathematics commands
+ // [zBuffer] List of dynamic var commands --->
+ {buildin_getd,"getd","*"},
+ {buildin_setd,"setd","*"},
+ // <--- [zBuffer] List of dynamic var commands
+ {buildin_petstat,"petstat","i"},
+ {buildin_callshop,"callshop","si"}, // [Skotlex]
+ {buildin_setitemscript,"setitemscript","is"}, //Set NEW item bonus script. Lupus
+ {buildin_disguise,"disguise","i"}, //disguise player. Lupus
+ {buildin_undisguise,"undisguise","i"}, //undisguise player. Lupus
+ {NULL,NULL,NULL},
+};
+
+enum {
+ C_NOP,C_POS,C_INT,C_PARAM,C_FUNC,C_STR,C_CONSTSTR,C_ARG,
+ C_NAME,C_EOL, C_RETINFO,
+ C_USERFUNC, C_USERFUNC_POS, // user defined functions
+
+ C_LOR,C_LAND,C_LE,C_LT,C_GE,C_GT,C_EQ,C_NE, //operator
+ C_XOR,C_OR,C_AND,C_ADD,C_SUB,C_MUL,C_DIV,C_MOD,C_NEG,C_LNOT,C_NOT,C_R_SHIFT,C_L_SHIFT
+};
+
+//Reports on the console the src of an script error.
+static void report_src(struct script_state *st) {
+ struct block_list *bl;
+ if (!st->oid) return; //Can't report source.
+ bl = map_id2bl(st->oid);
+ if (!bl) return;
+ switch (bl->type) {
+ case BL_NPC:
+ ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, mapindex_id2name(bl->m), bl->x, bl->y);
+ break;
+ default:
+ ShowDebug("Source (Non-NPC): %s (%d,%d)\n", mapindex_id2name(bl->m), bl->x, bl->y);
+ break;
+ }
+}
+/*==========================================
+ * •¶Žš—ñ‚̃nƒbƒVƒ…‚ðŒvŽZ
+ *------------------------------------------
+ */
+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‚Ì’†‚É–¼‘O‚ª‚ ‚é‚©ŒŸõ‚·‚é
+ *------------------------------------------
+ */
+// Šù‘¶‚Ì‚Å‚ ‚ê‚ΔԆA–³‚¯‚ê‚Î-1
+static int search_str(const unsigned char *p)
+{
+ int i;
+ i=str_hash[calc_hash(p)];
+ while(i){
+ if(strcmp(str_buf+str_data[i].str,(char *) p)==0){
+ return i;
+ }
+ i=str_data[i].next;
+ }
+ return -1;
+}
+
+/*==========================================
+ * str_data‚É–¼‘O‚ð“o˜^
+ *------------------------------------------
+ */
+// Šù‘¶‚Ì‚Å‚ ‚ê‚ΔԆA–³‚¯‚ê‚Γo˜^‚µ‚ÄV‹K”Ô†
+static int add_str(const unsigned char *p)
+{
+ int i;
+ char *lowcase;
+
+ lowcase=aStrdup((char *) p);
+ for(i=0;lowcase[i];i++)
+ lowcase[i]=tolower(lowcase[i]);
+ if((i=search_str((unsigned char *) lowcase))>=0){
+ aFree(lowcase);
+ return i;
+ }
+ aFree(lowcase);
+
+ i=calc_hash(p);
+ if(str_hash[i]==0){
+ str_hash[i]=str_num;
+ } else {
+ i=str_hash[i];
+ for(;;){
+ if(strcmp(str_buf+str_data[i].str,(char *) p)==0){
+ return i;
+ }
+ if(str_data[i].next==0)
+ break;
+ i=str_data[i].next;
+ }
+ str_data[i].next=str_num;
+ }
+ if(str_num>=str_data_size){
+ str_data_size+=128;
+ str_data=(struct str_data_struct *) aRealloc(str_data,sizeof(str_data[0])*str_data_size);
+ memset(str_data + (str_data_size - 128), '\0', 128);
+ }
+ while(str_pos+(int)strlen((char *) p)+1>=str_size){
+ str_size+=256;
+ str_buf=(char *)aRealloc(str_buf,str_size);
+ memset(str_buf + (str_size - 256), '\0', 256);
+ }
+ strcpy(str_buf+str_pos, (char *) p);
+ str_data[str_num].type=C_NOP;
+ str_data[str_num].str=str_pos;
+ str_data[str_num].next=0;
+ str_data[str_num].func=NULL;
+ str_data[str_num].backpatch=-1;
+ str_data[str_num].label=-1;
+ str_pos+=(int)strlen( (char *) p)+1;
+ return str_num++;
+}
+
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒgƒoƒbƒtƒ@ƒTƒCƒY‚ÌŠm”F‚ÆŠg’£
+ *------------------------------------------
+ */
+static void check_script_buf(int size)
+{
+ if(script_pos+size>=script_size){
+ script_size+=SCRIPT_BLOCK_SIZE;
+ script_buf=(unsigned char *)aRealloc(script_buf,script_size);
+ memset(script_buf + script_size - SCRIPT_BLOCK_SIZE, '\0',
+ SCRIPT_BLOCK_SIZE);
+ }
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒgƒoƒbƒtƒ@‚É‚PƒoƒCƒg‘‚«ž‚Þ
+ *------------------------------------------
+ */
+static void add_scriptb(int a)
+{
+ check_script_buf(1);
+ script_buf[script_pos++]=a;
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒgƒoƒbƒtƒ@‚Ƀf[ƒ^ƒ^ƒCƒv‚ð‘‚«ž‚Þ
+ *------------------------------------------
+ */
+static void add_scriptc(int a)
+{
+ while(a>=0x40){
+ add_scriptb((a&0x3f)|0x40);
+ a=(a-0x40)>>6;
+ }
+ add_scriptb(a&0x3f);
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒgƒoƒbƒtƒ@‚É®”‚ð‘‚«ž‚Þ
+ *------------------------------------------
+ */
+static void add_scripti(int a)
+{
+ while(a>=0x40){
+ add_scriptb(a|0xc0);
+ a=(a-0x40)>>6;
+ }
+ add_scriptb(a|0x80);
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒgƒoƒbƒtƒ@‚Ƀ‰ƒxƒ‹/•Ï”/ŠÖ”‚ð‘‚«ž‚Þ
+ *------------------------------------------
+ */
+// Å‘å16M‚Ü‚Å
+static void add_scriptl(int l)
+{
+ int backpatch = str_data[l].backpatch;
+
+ switch(str_data[l].type){
+ case C_POS:
+ case C_USERFUNC_POS:
+ add_scriptc(C_POS);
+ add_scriptb(str_data[l].label);
+ add_scriptb(str_data[l].label>>8);
+ add_scriptb(str_data[l].label>>16);
+ break;
+ case C_NOP:
+ case C_USERFUNC:
+ // ƒ‰ƒxƒ‹‚̉”\«‚ª‚ ‚é‚Ì‚Åbackpatch—pƒf[ƒ^–„‚ßž‚Ý
+ 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:
+ // ‚à‚¤‘¼‚Ì—p“r‚ÆŠm’肵‚Ä‚é‚Ì‚Å”Žš‚ð‚»‚Ì‚Ü‚Ü
+ add_scriptc(C_NAME);
+ add_scriptb(l);
+ add_scriptb(l>>8);
+ add_scriptb(l>>16);
+ break;
+ }
+}
+
+/*==========================================
+ * ƒ‰ƒxƒ‹‚ð‰ðŒˆ‚·‚é
+ *------------------------------------------
+ */
+void set_label(int l,int pos)
+{
+ int i,next;
+
+ str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ str_data[l].label=pos;
+ for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){
+ next=(*(int*)(script_buf+i)) & 0x00ffffff;
+ script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ script_buf[i]=pos;
+ script_buf[i+1]=pos>>8;
+ script_buf[i+2]=pos>>16;
+ i=next;
+ }
+}
+
+/*==========================================
+ * ƒXƒy[ƒX/ƒRƒƒ“ƒg“Ç‚Ý”ò‚΂µ
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ‚P’PŒêƒXƒLƒbƒv
+ *------------------------------------------
+ */
+static unsigned char *skip_word(unsigned char *p)
+{
+ // prefix
+ if(*p=='$') p++; // MAPŽI“à‹¤—L•Ï”—p
+ if(*p=='@') p++; // ˆêŽž“I•Ï”—p(like weiss)
+ if(*p=='#') p++; // account•Ï”—p
+ if(*p=='#') p++; // ƒ[ƒ‹ƒhaccount•Ï”—p
+
+ 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;
+
+/*==========================================
+ * ƒGƒ‰[ƒƒbƒZ[ƒWo—Í
+ *------------------------------------------
+ */
+static void disp_error_message(const char *mes,const unsigned char *pos)
+{
+ int line,c=0,i;
+ unsigned char *p,*linestart,*lineend;
+
+ for(line=startline,p=startptr;p && *p;line++){
+ linestart=p;
+ lineend=(unsigned char *) strchr((char *) p,'\n');
+ if(lineend){
+ c=*lineend;
+ *lineend=0;
+ }
+ if(lineend==NULL || pos<lineend){
+ fprintf(stderr, "\r"); //To not printout the error next to the spinner...
+ ShowError(" "); //Better error display [Skotlex]
+ if (current_file) {
+ printf("%s in "CL_WHITE"\'%s\'"CL_RESET" line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, current_file, line);
+ } else {
+ printf("%s line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, line);
+ }
+ for(i=0;(linestart[i]!='\r') && (linestart[i]!='\n') && linestart[i];i++){
+ if(linestart+i!=pos)
+ printf("%c",linestart[i]);
+ else
+ printf("\'%c\'",linestart[i]);
+ }
+ printf("\a\n");
+ if(lineend)
+ *lineend=c;
+ return;
+ }
+ *lineend=c;
+ p=lineend+1;
+ }
+}
+
+/*==========================================
+ * €‚̉ðÍ
+ *------------------------------------------
+ */
+unsigned char* parse_simpleexpr(unsigned char *p)
+{
+ int i;
+ p=skip_space(p);
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_simpleexpr %s\n",p);
+#endif
+ if(*p==';' || *p==','){
+ disp_error_message("unexpected expr end",p);
+ exit(1);
+ }
+ if(*p=='('){
+
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if((*p++)!=')'){
+ disp_error_message("unmatch ')'",p);
+ exit(1);
+ }
+ } else if(isdigit(*p) || ((*p=='-' || *p=='+') && isdigit(p[1]))){
+ char *np;
+ i=strtoul((char *) p,&np,0);
+ add_scripti(i);
+ p=(unsigned char *) np;
+ } else if(*p=='"'){
+ add_scriptc(C_STR);
+ p++;
+ while(*p && *p!='"'){
+ if(p[-1]<=0x7e && *p=='\\')
+ p++;
+ else if(*p=='\n'){
+ disp_error_message("unexpected newline @ string",p);
+ exit(1);
+ }
+ add_scriptb(*p++);
+ }
+ if(!*p){
+ disp_error_message("unexpected eof @ string",p);
+ exit(1);
+ }
+ add_scriptb(0);
+ p++; //'"'
+ } else {
+ int c,l;
+ char *p2;
+ // label , register , function etc
+ if(skip_word(p)==p && !(*p==')' && p[-1]=='(')){
+ disp_error_message("unexpected character",p);
+ exit(1);
+ }
+
+ p2=(char *) skip_word(p);
+ c=*p2; *p2=0; // –¼‘O‚ðadd_str‚·‚é
+ l=add_str(p);
+
+ parse_cmd=l; // warn_*_mismatch_paramnum‚Ì‚½‚ß‚É•K—v
+
+ *p2=c;
+ p=(unsigned char *) p2;
+
+ if(str_data[l].type!=C_FUNC && c=='['){
+ // array(name[i] => getelementofarray(name,i) )
+ add_scriptl(search_str((unsigned char *) "getelementofarray"));
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if((*p++)!=']'){
+ disp_error_message("unmatch ']'",p);
+ exit(1);
+ }
+ add_scriptc(C_FUNC);
+ } else if(str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) {
+ add_scriptl(search_str((unsigned char*)"callsub"));
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+ }else
+ add_scriptl(l);
+
+ }
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_simpleexpr end %s\n",p);
+#endif
+ return p;
+}
+
+/*==========================================
+ * Ž®‚̉ðÍ
+ *------------------------------------------
+ */
+unsigned char* parse_subexpr(unsigned char *p,int limit)
+{
+ int op,opl,len;
+ char *tmpp;
+
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_subexpr %s\n",p);
+#endif
+ p=skip_space(p);
+
+ if(*p=='-'){
+ tmpp=(char *) skip_space((unsigned char *) (p+1));
+ if(*tmpp==';' || *tmpp==','){
+ add_scriptl(LABEL_NEXTLINE);
+ p++;
+ return p;
+ }
+ }
+ tmpp=(char *) p;
+ if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
+ p=parse_subexpr(p+1,100);
+ add_scriptc(op);
+ } else
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+ while(((op=C_ADD,opl=6,len=1,*p=='+') ||
+ (op=C_SUB,opl=6,len=1,*p=='-') ||
+ (op=C_MUL,opl=7,len=1,*p=='*') ||
+ (op=C_DIV,opl=7,len=1,*p=='/') ||
+ (op=C_MOD,opl=7,len=1,*p=='%') ||
+ (op=C_FUNC,opl=8,len=1,*p=='(') ||
+ (op=C_LAND,opl=1,len=2,*p=='&' && p[1]=='&') ||
+ (op=C_AND,opl=5,len=1,*p=='&') ||
+ (op=C_LOR,opl=0,len=2,*p=='|' && p[1]=='|') ||
+ (op=C_OR,opl=4,len=1,*p=='|') ||
+ (op=C_XOR,opl=3,len=1,*p=='^') ||
+ (op=C_EQ,opl=2,len=2,*p=='=' && p[1]=='=') ||
+ (op=C_NE,opl=2,len=2,*p=='!' && p[1]=='=') ||
+ (op=C_R_SHIFT,opl=5,len=2,*p=='>' && p[1]=='>') ||
+ (op=C_GE,opl=2,len=2,*p=='>' && p[1]=='=') ||
+ (op=C_GT,opl=2,len=1,*p=='>') ||
+ (op=C_L_SHIFT,opl=5,len=2,*p=='<' && p[1]=='<') ||
+ (op=C_LE,opl=2,len=2,*p=='<' && p[1]=='=') ||
+ (op=C_LT,opl=2,len=1,*p=='<')) && opl>limit){
+ p+=len;
+ if(op==C_FUNC){
+ int i=0,func=parse_cmd;
+ const char *plist[128];
+
+ if(str_data[parse_cmd].type == C_FUNC){
+ // ’Êí‚ÌŠÖ”
+ add_scriptc(C_ARG);
+ } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) {
+ // ƒ†[ƒU[’è‹`ŠÖ”ŒÄ‚Ño‚µ
+ parse_cmd = search_str((unsigned char*)"callsub");
+ i++;
+ } else {
+ disp_error_message(
+ "expect command, missing function name or calling undeclared function",(unsigned char *) tmpp
+ );
+ exit(0);
+ }
+ func=parse_cmd;
+
+ do {
+ plist[i]=(char *) p;
+ p=parse_subexpr(p,-1);
+ p=skip_space(p);
+ if(*p==',') p++;
+ else if(*p!=')' && script_config.warn_func_no_comma){
+ disp_error_message("expect ',' or ')' at func params",p);
+ }
+ p=skip_space(p);
+ i++;
+ } while(*p && *p!=')' && i<128);
+ plist[i]=(char *) p;
+ if(*(p++)!=')'){
+ disp_error_message("func request '(' ')'",p);
+ exit(1);
+ }
+
+ if (str_data[func].type == C_FUNC && script_config.warn_func_mismatch_paramnum) {
+ const char *arg = buildin_func[str_data[func].val].arg;
+ int j = 0;
+ for (; arg[j]; j++) if (arg[j] == '*') break;
+ if (!(i <= 1 && j == 0) && ((arg[j] == 0 && i != j) || (arg[j] == '*' && i < j))) {
+ disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j]));
+ }
+ }
+ } else {
+ p=parse_subexpr(p,opl);
+ }
+ add_scriptc(op);
+ p=skip_space(p);
+ }
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_subexpr end %s\n",p);
+#endif
+ return p; /* return first untreated operator */
+}
+
+/*==========================================
+ * Ž®‚Ì•]‰¿
+ *------------------------------------------
+ */
+unsigned char* parse_expr(unsigned char *p)
+{
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_expr %s\n",p);
+#endif
+ switch(*p){
+ case ')': case ';': case ':': case '[': case ']':
+ case '}':
+ disp_error_message("unexpected char",p);
+ exit(1);
+ }
+ p=parse_subexpr(p,-1);
+#ifdef DEBUG_FUNCIN
+ if(battle_config.etc_log)
+ ShowDebug("parse_expr end %s\n",p);
+#endif
+ return p;
+}
+
+/*==========================================
+ * s‚̉ðÍ
+ *------------------------------------------
+ */
+unsigned char* parse_line(unsigned char *p)
+{
+ int i=0,cmd;
+ const char *plist[128];
+ unsigned char *p2;
+ char end;
+
+ p=skip_space(p);
+ if(*p==';')
+ return p + 1;
+
+ p = skip_space(p);
+ if(p[0] == '{') {
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ syntax.curly[syntax.curly_count].count = -1;
+ syntax.curly[syntax.curly_count].index = -1;
+ syntax.curly_count++;
+ return p + 1;
+ } else if(p[0] == '}') {
+ return parse_curly_close(p);
+ }
+
+ // \•¶ŠÖ˜A‚̈—
+ p2 = parse_syntax(p);
+ if(p2 != NULL) { return p2; }
+
+ // ʼn‚ÍŠÖ”–¼
+ p2=(char *) p;
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+
+ if(str_data[parse_cmd].type == C_FUNC){
+ // ’Êí‚ÌŠÖ”
+ add_scriptc(C_ARG);
+ } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) {
+ // ƒ†[ƒU[’è‹`ŠÖ”ŒÄ‚Ño‚µ
+ parse_cmd = search_str((unsigned char*)"callsub");
+ i++;
+ } else {
+ disp_error_message(
+ "expect command, missing function name or calling undeclared function", (unsigned char *)p2
+ );
+// exit(0);
+ }
+ cmd=parse_cmd;
+
+ if(parse_syntax_for_flag) {
+ end = ')';
+ } else {
+ end = ';';
+ }
+ while(p && *p && *p!=end && i<128){
+ plist[i]=(char *) p;
+
+ p=parse_expr(p);
+ p=skip_space(p);
+ // ˆø”‹æØ‚è‚Ì,ˆ—
+ if(*p==',') p++;
+ else if(*p!=end && script_config.warn_cmd_no_comma && 0 <= i ){
+ if(parse_syntax_for_flag) {
+ disp_error_message("expect ',' or ')' at cmd params",p);
+ } else {
+ disp_error_message("expect ',' or ';' at cmd params",p);
+ }
+ }
+ p=skip_space(p);
+ i++;
+ }
+ plist[i]=(char *) p;
+ if(!p || *(p++)!=end){
+ if(parse_syntax_for_flag) {
+ disp_error_message("need ')'",p);
+ } else {
+ disp_error_message("need ';'",p);
+ }
+ exit(1);
+ }
+ add_scriptc(C_FUNC);
+
+ // if, for , while ‚̕‚¶”»’è
+ p = parse_syntax_close(p);
+
+ if( str_data[cmd].type==C_FUNC && script_config.warn_cmd_mismatch_paramnum){
+ const char *arg=buildin_func[str_data[cmd].val].arg;
+ int j=0;
+ for(j=0;arg[j];j++) if(arg[j]=='*')break;
+ if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){
+ disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j]));
+ }
+ }
+
+
+ return p;
+}
+
+
+// { ... } ‚̕‚¶ˆ—
+unsigned char* parse_curly_close(unsigned char *p) {
+ if(syntax.curly_count <= 0) {
+ disp_error_message("unexpected string",p);
+ return p + 1;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
+ syntax.curly_count--;
+ // if, for , while ‚̕‚¶”»’è
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
+ // switch() •Â‚¶”»’è
+ int pos = syntax.curly_count-1;
+ unsigned char label[256];
+ int l;
+ // ˆêŽž•Ï”‚ðÁ‚·
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // –³ðŒ‚ÅI—¹ƒ|ƒCƒ“ƒ^‚Ɉړ®
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Œ»Ý’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ if(syntax.curly[pos].flag) {
+ // default ‚ª‘¶Ý‚·‚é
+ sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+
+ // I—¹ƒ‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__SW%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly_count--;
+ return p+1;
+ } else {
+ disp_error_message("unexpected string",p);
+ return p + 1;
+ }
+}
+
+// \•¶ŠÖ˜A‚̈—
+// break, case, continue, default, do, for, function,
+// if, switch, while ‚ð‚±‚Ì“à•”‚ň—‚µ‚Ü‚·B
+unsigned char* parse_syntax(unsigned char *p) {
+ switch(p[0]) {
+ case 'b':
+ if(!strncmp(p,"break",5) && !isalpha(*(p + 5))) {
+ // break ‚̈—
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_SWITCH) {
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("unexpected 'break'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_word(p);
+ p++;
+ // if, for , while ‚̕‚¶”»’è
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'c':
+ if(!strncmp(p,"case",4) && !isalpha(*(p + 4))) {
+ // case ‚̈—
+ if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) {
+ disp_error_message("unexpected 'case' ",p);
+ return p+1;
+ } else {
+ char *p2;
+ char label[256];
+ int l;
+ int pos = syntax.curly_count-1;
+ if(syntax.curly[pos].count != 1) {
+ // FALLTHRU —p‚̃Wƒƒƒ“ƒv
+ sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Œ»Ý’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+ // switch ”»’蕶
+ p = skip_word(p);
+ p = skip_space(p);
+ p2 = p;
+ p = skip_word(p);
+ p = skip_space(p);
+ if(*p != ':') {
+ disp_error_message("expect ':'",p);
+ exit(1);
+ }
+ *p = 0;
+ sprintf(label,"if(%s != $@__SW%x_VAL) goto __SW%x_%x;",
+ p2,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ *p = ':';
+ // ‚Q‰ñparse ‚µ‚È‚¢‚ƃ_ƒ
+ p2 = parse_line(label);
+ parse_line(p2);
+ syntax.curly_count--;
+ if(syntax.curly[pos].count != 1) {
+ // FALLTHRU I—¹Œã‚̃‰ƒxƒ‹
+ sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+ // ˆêŽž•Ï”‚ðÁ‚·
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(!strncmp(p,"continue",8) && !isalpha(*(p + 8))) {
+ // continue ‚̈—
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[pos].flag = 1; // continue —p‚̃Šƒ“ƒN’£‚éƒtƒ‰ƒO
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("unexpected 'continue'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_word(p);
+ p++;
+ // if, for , while ‚̕‚¶”»’è
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'd':
+ if(!strncmp(p,"default",7) && !isalpha(*(p + 7))) {
+ // switch - default ‚̈—
+ if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) {
+ disp_error_message("unexpected 'delault'",p);
+ return p+1;
+ } else if(syntax.curly[syntax.curly_count - 1].flag) {
+ disp_error_message("dup 'delault'",p);
+ return p+1;
+ } else {
+ char label[256];
+ int l;
+ int pos = syntax.curly_count-1;
+ // Œ»Ý’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ p = skip_word(p);
+ p = skip_space(p);
+ if(*p != ':') {
+ disp_error_message("need ':'",p);
+ }
+ p++;
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // –³ðŒ‚ÅŽŸ‚̃Šƒ“ƒN‚É”ò‚΂·
+ sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // default ‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__SW%x_DEF",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly[syntax.curly_count - 1].flag = 1;
+ syntax.curly[pos].count++;
+
+ p = skip_word(p);
+ return p + 1;
+ }
+ } else if(!strncmp(p,"do",2) && !isalpha(*(p + 2))) {
+ int l;
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_DO;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // Œ»Ý’n‚̃‰ƒxƒ‹Œ`¬‚·‚é
+ sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ case 'f':
+ if(!strncmp(p,"for",3) && !isalpha(*(p + 3))) {
+ int l;
+ unsigned char label[256];
+ int pos = syntax.curly_count;
+ syntax.curly[syntax.curly_count].type = TYPE_FOR;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ p=skip_word(p);
+ p=skip_space(p);
+
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ return p+1;
+ }
+ p++;
+
+ // ‰Šú‰»•¶‚ðŽÀs‚·‚é
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+
+ // ðŒ”»’fŠJŽn‚̃‰ƒxƒ‹Œ`¬‚·‚é
+ sprintf(label,"__FR%x_J",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ if(*p == ';') {
+ // for(;;) ‚̃pƒ^[ƒ“‚È‚Ì‚Å•K‚¸^
+ ;
+ } else {
+ // ðŒ‚ª‹U‚È‚çI—¹’n“_‚É”ò‚΂·
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ }
+ if(*p != ';') {
+ disp_error_message("need ';'",p);
+ return p+1;
+ }
+ p++;
+
+ // ƒ‹[ƒvŠJŽn‚É”ò‚΂·
+ sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // ŽŸ‚̃‹[ƒv‚ւ̃‰ƒxƒ‹Œ`¬‚·‚é
+ sprintf(label,"__FR%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // ŽŸ‚̃‹[ƒv‚É“ü‚鎞‚̈—
+ // for ÅŒã‚Ì '(' ‚ð ';' ‚Æ‚µ‚Ĉµ‚¤ƒtƒ‰ƒO
+ parse_syntax_for_flag = 1;
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+ parse_syntax_for_flag = 0;
+
+ // ðŒ”»’舗‚É”ò‚΂·
+ sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // ƒ‹[ƒvŠJŽn‚̃‰ƒxƒ‹•t‚¯
+ sprintf(label,"__FR%x_BGN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ return p;
+ } else if(!strncmp(p,"function",8) && !isalpha(*(p + 8))) {
+ unsigned char *func_name;
+ // function
+ p=skip_word(p);
+ p=skip_space(p);
+ // function - name
+ func_name = p;
+ p=skip_word(p);
+ if(*skip_space(p) == ';') {
+ // ŠÖ”‚Ì錾 - –¼‘O‚ð“o˜^‚µ‚ÄI‚í‚è
+ unsigned char c = *p;
+ int l;
+ *p = 0;
+ l=add_str(func_name);
+ *p = c;
+ if(str_data[l].type == C_NOP) {
+ str_data[l].type = C_USERFUNC;
+ }
+ return skip_space(p) + 1;
+ } else {
+ // ŠÖ”‚Ì’†g
+ char label[256];
+ unsigned char c = *p;
+ int l;
+ syntax.curly[syntax.curly_count].type = TYPE_USERFUNC;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ // ŠÖ”I—¹‚Ü‚Å”ò‚΂·
+ sprintf(label,"goto __FN%x_FIN;",syntax.curly[syntax.curly_count-1].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // ŠÖ”–¼‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ *p = 0;
+ l=add_str(func_name);
+ if(str_data[l].type == C_NOP) {
+ str_data[l].type = C_USERFUNC;
+ }
+ if(str_data[l].label!=-1){
+ *p=c;
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ strdb_put(scriptlabel_db,func_name,(void*)script_pos); // ŠO•”—plabel db“o˜^
+ *p = c;
+ return skip_space(p);
+ }
+ }
+ break;
+ case 'i':
+ if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) {
+ // if() ‚̈—
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_IF;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ case 's':
+ if(!strncmp(p,"switch",6) && !isalpha(*(p + 6))) {
+ // switch() ‚̈—
+ char label[256];
+ syntax.curly[syntax.curly_count].type = TYPE_SWITCH;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str((unsigned char*)"set"));
+ add_scriptc(C_ARG);
+ add_scriptl(add_str(label));
+ p=skip_word(p);
+ p=skip_space(p);
+ p=parse_expr(p);
+ p=skip_space(p);
+ if(*p != '{') {
+ disp_error_message("need '{'",p);
+ }
+ add_scriptc(C_FUNC);
+ return p + 1;
+ }
+ break;
+ case 'w':
+ if(!strncmp(p,"while",5) && !isalpha(*(p + 5))) {
+ int l;
+ char label[256];
+ p=skip_word(p);
+ p=skip_space(p);
+
+ syntax.curly[syntax.curly_count].type = TYPE_WHILE;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // ðŒ”»’fŠJŽn‚̃‰ƒxƒ‹Œ`¬‚·‚é
+ sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ // ðŒ‚ª‹U‚È‚çI—¹’n“_‚É”ò‚΂·
+ sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ }
+ return NULL;
+}
+
+unsigned char* parse_syntax_close(unsigned char *p) {
+ // if(...) for(...) hoge(); ‚̂悤‚ÉA‚P“x•Â‚¶‚ç‚ꂽ‚çÄ“x•Â‚¶‚ç‚ê‚é‚©Šm”F‚·‚é
+ int flag;
+
+ do {
+ p = parse_syntax_close_sub(p,&flag);
+ } while(flag);
+ return p;
+}
+
+// if, for , while , do ‚̕‚¶”»’è
+// flag == 1 : •Â‚¶‚ç‚ꂽ
+// flag == 0 : •Â‚¶‚ç‚ê‚È‚¢
+unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag) {
+ unsigned char label[256];
+ int pos = syntax.curly_count - 1;
+ int l;
+ *flag = 1;
+
+ if(syntax.curly_count <= 0) {
+ *flag = 0;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_IF) {
+ char *p2 = p;
+ // if ÅIꊂ֔ò‚΂·
+ sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Œ»Ý’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+
+ syntax.curly[pos].count++;
+ p = skip_space(p);
+ if(!syntax.curly[pos].flag && !strncmp(p,"else",4) && !isalpha(*(p + 4))) {
+ // else or else - if
+ p = skip_word(p);
+ p = skip_space(p);
+ if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) {
+ // else - if
+ p=skip_word(p);
+ p=skip_space(p);
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ *flag = 0;
+ return p;
+ } else {
+ // else
+ if(!syntax.curly[pos].flag) {
+ syntax.curly[pos].flag = 1;
+ *flag = 0;
+ return p;
+ }
+ }
+ }
+ // if •Â‚¶
+ syntax.curly_count--;
+ // ÅI’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__IF%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ if(syntax.curly[pos].flag == 1) {
+ // ‚±‚Ìif‚ɑ΂·‚éelse‚¶‚á‚È‚¢‚̂Ń|ƒCƒ“ƒ^‚̈ʒu‚Í“¯‚¶
+ return p2;
+ }
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_DO) {
+ int l;
+ char label[256];
+ unsigned char *p2;
+
+ if(syntax.curly[pos].flag) {
+ // Œ»Ý’n‚̃‰ƒxƒ‹Œ`¬‚·‚é(continue ‚Å‚±‚±‚É—ˆ‚é)
+ sprintf(label,"__DO%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ }
+
+ // ðŒ‚ª‹U‚È‚çI—¹’n“_‚É”ò‚΂·
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(p2 - p != 5 || strncmp("while",p,5)) {
+ disp_error_message("need 'while'",p);
+ }
+ p = p2;
+
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+
+ // ŠJŽn’n“_‚É”ò‚΂·
+ sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // ðŒI—¹’n“_‚̃‰ƒxƒ‹Œ`¬‚·‚é
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ p = skip_space(p);
+ if(*p != ';') {
+ disp_error_message("need ';'",p);
+ return p+1;
+ }
+ p++;
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ // ŽŸ‚̃‹[ƒv‚É”ò‚΂·
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // for I—¹‚̃‰ƒxƒ‹•t‚¯
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ // while ðŒ”»’f‚Ö”ò‚΂·
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // while I—¹‚̃‰ƒxƒ‹•t‚¯
+ sprintf(label,"__WL%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) {
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // –ß‚·
+ sprintf(label,"return;");
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Œ»Ý’n‚̃‰ƒxƒ‹‚ð•t‚¯‚é
+ sprintf(label,"__FN%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ if(str_data[l].label!=-1){
+ disp_error_message("dup label ",p);
+ exit(1);
+ }
+ set_label(l,script_pos);
+ syntax.curly_count--;
+ return p + 1;
+ } else {
+ *flag = 0;
+ return p;
+ }
+}
+
+/*==========================================
+ * ‘g‚Ýž‚ÝŠÖ”‚̒ljÁ
+ *------------------------------------------
+ */
+static void add_buildin_func(void)
+{
+ int i,n;
+ for(i=0;buildin_func[i].func;i++){
+ n=add_str((unsigned char *) buildin_func[i].name);
+ str_data[n].type=C_FUNC;
+ str_data[n].val=i;
+ str_data[n].func=buildin_func[i].func;
+ }
+}
+
+/*==========================================
+ * ’蔃f[ƒ^ƒx[ƒX‚Ì“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static void read_constdb(void)
+{
+ FILE *fp;
+ char line[1024],name[1024];
+ int val,n,i,type;
+
+ sprintf(line, "%s/const.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return ;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ type=0;
+ if(sscanf(line,"%[A-Za-z0-9_],%d,%d",name,&val,&type)>=2 ||
+ sscanf(line,"%[A-Za-z0-9_] %d %d",name,&val,&type)>=2){
+ for(i=0;name[i];i++)
+ name[i]=tolower(name[i]);
+ n=add_str((const unsigned char *) name);
+ if(type==0)
+ str_data[n].type=C_INT;
+ else
+ str_data[n].type=C_PARAM;
+ str_data[n].val=val;
+ }
+ }
+ fclose(fp);
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒg‚̉ðÍ
+ *------------------------------------------
+ */
+unsigned char* parse_script(unsigned char *src,int line)
+{
+ unsigned char *p, *tmpp;
+ int i;
+ static int first = 1;
+
+ if (first) {
+ add_buildin_func();
+ read_constdb();
+ }
+ first = 0;
+
+//////////////////////////////////////////////
+// additional check on the input to filter empty scripts ("{}" and "{ }")
+ p = src;
+ p = skip_space(p);
+ if (*p != '{') {
+ disp_error_message("not found '{'", p);
+ return NULL;
+ }
+ p++;
+ p = skip_space(p);
+ if (*p == '}') {
+ // an empty function, just return
+ return NULL;
+ }
+ script_buf = (unsigned char *) aCallocA(SCRIPT_BLOCK_SIZE, sizeof(unsigned char));
+ script_pos = 0;
+ script_size = SCRIPT_BLOCK_SIZE;
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+ for (i = LABEL_START; i < str_num; i++) {
+ if (
+ str_data[i].type == C_POS || str_data[i].type == C_NAME ||
+ str_data[i].type == C_USERFUNC || str_data[i].type == C_USERFUNC_POS
+ ) {
+ str_data[i].type = C_NOP;
+ str_data[i].backpatch = -1;
+ str_data[i].label = -1;
+ }
+ }
+
+ //Labels must be reparsed for the script....
+ scriptlabel_db->clear(scriptlabel_db, NULL);
+
+ // for error message
+ startptr = src;
+ startline = line;
+
+ while (p && *p && (*p != '}' || syntax.curly_count != 0)) {
+ p = skip_space(p);
+ // label‚¾‚¯“ÁŽêˆ—
+ tmpp = skip_space(skip_word(p));
+ if (*tmpp == ':' && !(!strncmp(p,"default",7) && !isalpha(*(p + 7)))) {
+ int l, c;
+ c = *skip_word(p);
+ *skip_word(p) = 0;
+ l = add_str(p);
+ if (str_data[l].label != -1) {
+ *skip_word(p) = c;
+ disp_error_message("dup label ", p);
+ exit(1);
+ }
+ set_label(l, script_pos);
+ strdb_put(scriptlabel_db, p, (void*)script_pos); // ŠO•”—plabel db“o˜^
+ *skip_word(p) = c;
+ p = tmpp + 1;
+ continue;
+ }
+
+ // ‘¼‚Í‘S•”ˆê‚­‚½
+ p = parse_line(p);
+ p = skip_space(p);
+ add_scriptc(C_EOL);
+
+ set_label(LABEL_NEXTLINE, script_pos);
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+ }
+
+ add_scriptc(C_NOP);
+
+ script_size = script_pos;
+ script_buf = (unsigned char *)aRealloc(script_buf, script_pos + 1);
+
+ // –¢‰ðŒˆ‚̃‰ƒxƒ‹‚ð‰ðŒˆ
+ for (i = LABEL_START; i < str_num; i++) {
+ if (str_data[i].type == C_NOP) {
+ int j, next;
+ str_data[i].type = C_NAME;
+ str_data[i].label = i;
+ for (j = str_data[i].backpatch; j >= 0 && j != 0x00ffffff; ) {
+ next = (*(int*)(script_buf+j)) & 0x00ffffff;
+ script_buf[j] = i;
+ script_buf[j+1] = i>>8;
+ script_buf[j+2] = i>>16;
+ j = next;
+ }
+ }
+ }
+
+#ifdef DEBUG_DISP
+ for (i = 0; i < script_pos; i++) {
+ if ((i & 15) == 0) printf("%04x : ", i);
+ printf("%02x ", script_buf[i]);
+ if((i&15) == 15) printf("\n");
+ }
+ printf("\n");
+#endif
+
+ startptr = NULL; //Clear pointer to prevent future references to a src that may be free'd. [Skotlex]
+ return (unsigned char *) script_buf;
+}
+
+//
+// ŽÀsŒn
+//
+enum {RUN = 0,STOP,END,RERUNLINE,GOTO,RETFUNC};
+
+/*==========================================
+ * rid‚©‚çsd‚ւ̉ðŒˆ
+ *------------------------------------------
+ */
+struct map_session_data *script_rid2sd(struct script_state *st)
+{
+ struct map_session_data *sd=map_id2sd(st->rid);
+ if(!sd){
+ ShowError("script_rid2sd: fatal error ! player not attached!\n");
+ report_src(st);
+ }
+ return sd;
+}
+
+
+/*==========================================
+ * •Ï”‚Ì“Ç‚ÝŽæ‚è
+ *------------------------------------------
+ */
+int get_val(struct script_state*st,struct script_data* data)
+{
+ struct map_session_data *sd=NULL;
+ if(data->type==C_NAME){
+ char *name=str_buf+str_data[data->u.num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if(prefix!='$'){
+ if((sd=script_rid2sd(st))==NULL)
+ ShowError("get_val error name?:%s\n",name);
+ }
+ if(postfix=='$'){
+
+ data->type=C_CONSTSTR;
+ if( prefix=='@'){
+ if(sd)
+ data->u.str = pc_readregstr(sd,data->u.num);
+ }else if(prefix=='$'){
+ data->u.str = (char *)idb_get(mapregstr_db,data->u.num);
+ }else if(prefix=='#'){
+ if( name[1]=='#'){
+ if(sd)
+ data->u.str = pc_readaccountreg2str(sd,name);
+ }else{
+ if(sd)
+ data->u.str = pc_readaccountregstr(sd,name);
+ }
+ }else{
+ if(sd)
+ data->u.str = pc_readglobalreg_str(sd,name);
+ } // [zBuffer]
+ /*else{
+ ShowWarning("script: get_val: illegal scope string variable.\n");
+ data->u.str = "!!ERROR!!";
+ }*/
+ if( data->u.str == NULL )
+ data->u.str ="";
+
+ }else{
+
+ data->type=C_INT;
+ if(str_data[data->u.num&0x00ffffff].type==C_INT){
+ data->u.num = str_data[data->u.num&0x00ffffff].val;
+ }else if(str_data[data->u.num&0x00ffffff].type==C_PARAM){
+ if(sd)
+ data->u.num = pc_readparam(sd,str_data[data->u.num&0x00ffffff].val);
+ }else if(prefix=='@'){
+ if(sd)
+ data->u.num = pc_readreg(sd,data->u.num);
+ }else if(prefix=='$'){
+ data->u.num = (int)idb_get(mapreg_db,data->u.num);
+ }else if(prefix=='#'){
+ if( name[1]=='#'){
+ if(sd)
+ data->u.num = pc_readaccountreg2(sd,name);
+ }else{
+ if(sd)
+ data->u.num = pc_readaccountreg(sd,name);
+ }
+ }else{
+ if(sd)
+ data->u.num = pc_readglobalreg(sd,name);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * •Ï”‚Ì“Ç‚ÝŽæ‚è2
+ *------------------------------------------
+ */
+void* get_val2(struct script_state*st,int num)
+{
+ struct script_data dat;
+ dat.type=C_NAME;
+ dat.u.num=num;
+ get_val(st,&dat);
+ if( dat.type==C_INT ) return (void*)dat.u.num;
+ else return (void*)dat.u.str;
+}
+
+/*==========================================
+ * •Ï”Ý’è—p
+ *------------------------------------------
+ */
+static int set_reg(struct map_session_data *sd,int num,char *name,void *v)
+{
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( postfix=='$' ){
+ char *str=(char*)v;
+ if( prefix=='@'){
+ pc_setregstr(sd,num,str);
+ }else if(prefix=='$') {
+ mapreg_setregstr(num,str);
+ }else if(prefix=='#') {
+ if( name[1]=='#' )
+ pc_setaccountreg2str(sd,name,str);
+ else
+ pc_setaccountregstr(sd,name,str);
+ }else{
+ pc_setglobalreg_str(sd,name,str);
+ } // [zBuffer]
+
+ /*else{
+ ShowWarning("script: set_reg: illegal scope string variable !");
+ }*/
+ }else{
+ // ”’l
+ int val = (int)v;
+ if(str_data[num&0x00ffffff].type==C_PARAM){
+ pc_setparam(sd,str_data[num&0x00ffffff].val,val);
+ }else if(prefix=='@') {
+ pc_setreg(sd,num,val);
+ }else if(prefix=='$') {
+ mapreg_setreg(num,val);
+ }else if(prefix=='#') {
+ if( name[1]=='#' )
+ pc_setaccountreg2(sd,name,val);
+ else
+ pc_setaccountreg(sd,name,val);
+ }else{
+ pc_setglobalreg(sd,name,val);
+ }
+ }
+ return 0;
+}
+
+int set_var(struct map_session_data *sd, char *name, void *val)
+{
+ return set_reg(sd, add_str((unsigned char *) name), name, val);
+}
+
+/*==========================================
+ * •¶Žš—ñ‚Ö‚Ì•ÏŠ·
+ *------------------------------------------
+ */
+char* conv_str(struct script_state *st,struct script_data *data)
+{
+ get_val(st,data);
+ if(data->type==C_INT){
+ char *buf;
+ buf=(char *)aCallocA(ITEM_NAME_LENGTH,sizeof(char));
+ snprintf(buf,ITEM_NAME_LENGTH, "%d",data->u.num);
+ data->type=C_STR;
+ data->u.str=buf;
+#if 1
+ } else if(data->type==C_NAME){
+ // ƒeƒ“ƒ|ƒ‰ƒŠB–{—ˆ–³‚¢‚Í‚¸
+ data->type=C_CONSTSTR;
+ data->u.str=str_buf+str_data[data->u.num].str;
+#endif
+ }
+ return data->u.str;
+}
+
+/*==========================================
+ * ”’l‚Ö•ÏŠ·
+ *------------------------------------------
+ */
+int conv_num(struct script_state *st,struct script_data *data)
+{
+ char *p;
+ get_val(st,data);
+ if(data->type==C_STR || data->type==C_CONSTSTR){
+ p=data->u.str;
+ data->u.num = atoi(p);
+ if(data->type==C_STR)
+ aFree(p);
+ data->type=C_INT;
+ }
+ return data->u.num;
+}
+
+/*==========================================
+ * ƒXƒ^ƒbƒN‚Ö”’l‚ðƒvƒbƒVƒ…
+ *------------------------------------------
+ */
+void push_val(struct script_stack *stack,int type,int val)
+{
+ if(stack->sp >= stack->sp_max){
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data *)aRealloc(stack->stack_data,
+ sizeof(stack->stack_data[0]) * stack->sp_max);
+ memset(stack->stack_data + (stack->sp_max - 64), 0,
+ 64 * sizeof(*(stack->stack_data)));
+ }
+// if(battle_config.etc_log)
+// printf("push (%d,%d)-> %d\n",type,val,stack->sp);
+ stack->stack_data[stack->sp].type=type;
+ stack->stack_data[stack->sp].u.num=val;
+ stack->sp++;
+}
+
+/*==========================================
+ * ƒXƒ^ƒbƒN‚Ö•¶Žš—ñ‚ðƒvƒbƒVƒ…
+ *------------------------------------------
+ */
+void push_str(struct script_stack *stack,int type,unsigned char *str)
+{
+ if(stack->sp>=stack->sp_max){
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data *)aRealloc(stack->stack_data,
+ sizeof(stack->stack_data[0]) * stack->sp_max);
+ memset(stack->stack_data + (stack->sp_max - 64), '\0',
+ 64 * sizeof(*(stack->stack_data)));
+ }
+// if(battle_config.etc_log)
+// printf("push (%d,%x)-> %d\n",type,str,stack->sp);
+ stack->stack_data[stack->sp].type=type;
+ stack->stack_data[stack->sp].u.str=(char *) str;
+ stack->sp++;
+}
+
+/*==========================================
+ * ƒXƒ^ƒbƒN‚Ö•¡»‚ðƒvƒbƒVƒ…
+ *------------------------------------------
+ */
+void push_copy(struct script_stack *stack,int pos)
+{
+ switch(stack->stack_data[pos].type){
+ case C_CONSTSTR:
+ push_str(stack,C_CONSTSTR,(unsigned char *) stack->stack_data[pos].u.str);
+ break;
+ case C_STR:
+ push_str(stack,C_STR,(unsigned char *) aStrdup(stack->stack_data[pos].u.str));
+ break;
+ default:
+ push_val(stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num);
+ break;
+ }
+}
+
+/*==========================================
+ * ƒXƒ^ƒbƒN‚©‚çƒ|ƒbƒv
+ *------------------------------------------
+ */
+void pop_stack(struct script_stack* stack,int start,int end)
+{
+ int i;
+ for(i=start;i<end;i++){
+ if(stack->stack_data[i].type==C_STR){
+ aFree(stack->stack_data[i].u.str);
+ stack->stack_data[i].type=C_INT; //Might not be correct, but it's done in case to prevent pointer errors later on. [Skotlex]
+ }
+ }
+ if(stack->sp>end){
+ memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end));
+ }
+ stack->sp-=end-start;
+}
+
+/*==========================================
+ * Free's the whole stack. Invoked when clearing a character. [Skotlex]
+ *------------------------------------------
+ */
+void script_free_stack(struct script_stack* stack)
+{
+ int i;
+ for (i = 0; i < stack->sp; i++)
+ {
+ if(stack->stack_data[i].type==C_STR)
+ {
+ //ShowDebug ("script_free_stack: freeing %p at sp=%d.\n", stack->stack_data[i].u.str, i);
+ aFree(stack->stack_data[i].u.str);
+ stack->stack_data[i].type = C_INT;
+ }
+ }
+ aFree (stack->stack_data);
+ aFree (stack);
+}
+
+int axtoi(char *hexStg) {
+ int n = 0; // position in string
+ int m = 0; // position in digit[] to shift
+ int count; // loop index
+ int intValue = 0; // integer value of hex string
+ int digit[11]; // hold values to convert
+ while (n < 10) {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count) {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+// [Lance] Hex string to integer converter
+int buildin_axtoi(struct script_state *st)
+{
+ char *hex = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, axtoi(hex));
+ return 0;
+}
+
+//
+// –„‚ßž‚ÝŠÖ”
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_mes(struct script_state *st)
+{
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ clif_scriptmes(script_rid2sd(st),st->oid,st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_goto(struct script_state *st)
+{
+ int pos;
+
+ if (st->stack->stack_data[st->start+2].type != C_POS){
+ int func = st->stack->stack_data[st->start+2].u.num;
+ ShowMessage("script: goto '"CL_WHITE"%s"CL_RESET"': not label!\n", str_buf + str_data[func].str);
+ st->state = END;
+ return 1;
+ }
+
+ pos = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ st->pos = pos;
+ st->state = GOTO;
+ return 0;
+}
+
+/*==========================================
+ * ƒ†[ƒU[’è‹`ŠÖ”‚̌ĂÑo‚µ
+ *------------------------------------------
+ */
+int buildin_callfunc(struct script_state *st)
+{
+ char *scr;
+ char *str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if( (scr=(char *) strdb_get(userfunc_db,(unsigned char*)str)) ){
+ int i,j;
+ for(i=st->start+3,j=0;i<st->end;i++,j++)
+ push_copy(st->stack,i);
+
+ push_val(st->stack,C_INT,j); // ˆø”‚Ì”‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_INT,st->stack->defsp); // Œ»Ý‚̊Xƒ^ƒbƒNƒ|ƒCƒ“ƒ^‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_INT,(int)st->script); // Œ»Ý‚̃XƒNƒŠƒvƒg‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_RETINFO,st->pos); // Œ»Ý‚̃XƒNƒŠƒvƒgˆÊ’u‚ðƒvƒbƒVƒ…
+
+ st->pos=0;
+ st->script=scr;
+ st->stack->defsp=st->start+4+j;
+ st->state=GOTO;
+ }else{
+ ShowWarning("script:callfunc: function not found! [%s]\n",str);
+ st->state=END;
+ return 1;
+ }
+ return 0;
+}
+/*==========================================
+ * ƒTƒuƒ‹[ƒeƒBƒ“‚̌ĂÑo‚µ
+ *------------------------------------------
+ */
+int buildin_callsub(struct script_state *st)
+{
+ int pos=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int i,j;
+ if(st->stack->stack_data[st->start+2].type != C_POS && st->stack->stack_data[st->start+2].type != C_USERFUNC_POS) {
+ ShowError("script: callsub: not label !\n");
+ st->state=END;
+ return 1;
+ } else {
+ for(i=st->start+3,j=0;i<st->end;i++,j++)
+ push_copy(st->stack,i);
+
+ push_val(st->stack,C_INT,j); // ˆø”‚Ì”‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_INT,st->stack->defsp); // Œ»Ý‚̊Xƒ^ƒbƒNƒ|ƒCƒ“ƒ^‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_INT,(int)st->script); // Œ»Ý‚̃XƒNƒŠƒvƒg‚ðƒvƒbƒVƒ…
+ push_val(st->stack,C_RETINFO,st->pos); // Œ»Ý‚̃XƒNƒŠƒvƒgˆÊ’u‚ðƒvƒbƒVƒ…
+
+ st->pos=pos;
+ st->stack->defsp=st->start+4+j;
+ st->state=GOTO;
+ }
+ return 0;
+}
+
+/*==========================================
+ * ˆø”‚ÌŠ“¾
+ *------------------------------------------
+ */
+int buildin_getarg(struct script_state *st)
+{
+ int num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int max,stsp;
+ if( st->stack->defsp<4 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO ){
+ ShowWarning("script:getarg without callfunc or callsub!\n");
+ st->state=END;
+ return 1;
+ }
+ max=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4]));
+ stsp=st->stack->defsp - max -4;
+ if( num >= max ){
+ ShowWarning("script:getarg arg1(%d) out of range(%d) !\n",num,max);
+ st->state=END;
+ return 1;
+ }
+ push_copy(st->stack,stsp+num);
+ return 0;
+}
+
+/*==========================================
+ * ƒTƒuƒ‹[ƒ`ƒ“/ƒ†[ƒU[’è‹`ŠÖ”‚ÌI—¹
+ *------------------------------------------
+ */
+int buildin_return(struct script_state *st)
+{
+ if(st->end>st->start+2){ // –ß‚è’l—L‚è
+ push_copy(st->stack,st->start+2);
+ }
+ st->state=RETFUNC;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_next(struct script_state *st)
+{
+ st->state=STOP;
+ clif_scriptnext(script_rid2sd(st),st->oid);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_close(struct script_state *st)
+{
+ st->state=END;
+ clif_scriptclose(script_rid2sd(st),st->oid);
+ return 0;
+}
+int buildin_close2(struct script_state *st)
+{
+ st->state=STOP;
+ clif_scriptclose(script_rid2sd(st),st->oid);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_menu(struct script_state *st)
+{
+ char *buf;
+ int len,i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(sd->state.menu_or_input==0){
+ st->state=RERUNLINE;
+ sd->state.menu_or_input=1;
+ for(i=st->start+2,len=16;i<st->end;i+=2){
+ conv_str(st,& (st->stack->stack_data[i]));
+ len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
+ }
+ buf=(char *)aCallocA(len+1,sizeof(char));
+ buf[0]=0;
+ for(i=st->start+2,len=0;i<st->end;i+=2){
+ strcat(buf,st->stack->stack_data[i].u.str);
+ strcat(buf,":");
+ }
+ clif_scriptmenu(script_rid2sd(st),st->oid,buf);
+ aFree(buf);
+ } else if(sd->npc_menu==0xff){ // cansel
+ sd->state.menu_or_input=0;
+ st->state=END;
+ } else { // goto“®ì
+ sd->state.menu_or_input=0;
+ if(sd->npc_menu>0){
+ //Skip empty menu entries which weren't displayed on the client (blackhole89)
+ for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2)
+ {
+ if((int)strlen(st->stack->stack_data[i].u.str) < 1)
+ sd->npc_menu++; //Empty selection which wasn't displayed on the client.
+ }
+ if(sd->npc_menu >= (st->end-st->start)/2) {
+ //Invalid selection.
+ st->state=END;
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+sd->npc_menu*2+1].type!=C_POS ){
+ ShowError("script: menu: not label !\n");
+ st->state=END;
+ return 1;
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu);
+ st->pos= conv_num(st,& (st->stack->stack_data[st->start+sd->npc_menu*2+1]));
+ st->state=GOTO;
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_rand(struct script_state *st)
+{
+ int range;
+
+ if (st->end > st->start+3){
+ int min, max;
+ min = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ max = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if (max == min){ //Why would someone do this?
+ push_val(st->stack,C_INT,min);
+ return 0;
+ }
+ if (max < min){
+ int tmp = min;
+ min = max;
+ max = tmp;
+ }
+ range = max - min + 1;
+ if (range == 0) range = 1;
+ push_val(st->stack,C_INT,rand()%range+min);
+ } else {
+ range = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if (range == 0) range = 1;
+ push_val(st->stack,C_INT,rand()%range);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_warp(struct script_state *st)
+{
+ int x,y;
+ char *str;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(strcmp(str,"Random")==0)
+ pc_randomwarp(sd,3);
+ else if(strcmp(str,"SavePoint")==0){
+ if(map[sd->bl.m].flag.noreturn) // ’±‹ÖŽ~
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else if(strcmp(str,"Save")==0){
+ if(map[sd->bl.m].flag.noreturn) // ’±‹ÖŽ~
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else
+ pc_setpos(sd,mapindex_name2id(str),x,y,0);
+ return 0;
+}
+/*==========================================
+ * ƒGƒŠƒAŽw’èƒ[ƒv
+ *------------------------------------------
+ */
+int buildin_areawarp_sub(struct block_list *bl,va_list ap)
+{
+ int x,y;
+ unsigned int map;
+ map=va_arg(ap, unsigned int);
+ x=va_arg(ap,int);
+ y=va_arg(ap,int);
+ if(map == 0)
+ pc_randomwarp((struct map_session_data *)bl,3);
+ else
+ pc_setpos((struct map_session_data *)bl,map,x,y,0);
+ return 0;
+}
+int buildin_areawarp(struct script_state *st)
+{
+ int x,y,m;
+ unsigned int index;
+ char *str;
+ char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+7]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ if(strcmp(str,"Random")==0)
+ index = 0;
+ else if(!(index=mapindex_name2id(str)))
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub,
+ m,x0,y0,x1,y1,BL_PC, index,x,y );
+ return 0;
+}
+
+/*==========================================
+ * warpchar [LuzZza]
+ * Useful for warp one player from
+ * another player npc-session.
+ * Using: warpchar "mapname.gat",x,y,Char_ID;
+ *------------------------------------------
+ */
+int buildin_warpchar(struct script_state *st)
+{
+ int x,y,a,i;
+ char *str;
+ struct map_session_data *sd, **pl_allsd;
+ int users;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ a=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0; i<users; i++) {
+ sd = pl_allsd[i];
+ if(sd->status.char_id == a) {
+
+ if(strcmp(str, "Random") == 0)
+ pc_randomwarp(sd, 3);
+
+ else if(strcmp(str, "SavePoint") == 0)
+ pc_setpos(sd, sd->status.save_point.map,
+ sd->status.save_point.x, sd->status.save_point.y, 3);
+
+ else
+ pc_setpos(sd, mapindex_name2id(str), x, y, 3);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Warpparty - [Fredzilla]
+ * Syntax: warpparty "mapname.gat",x,y,Party_ID;
+ *------------------------------------------
+ */
+int buildin_warpparty(struct script_state *st)
+{
+ int x,y;
+ char *str;
+ int p;
+ int i;
+ unsigned short mapindex;
+ struct map_session_data *pl_sd, **pl_allsd;
+ struct map_session_data *sd;
+ int users;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ p=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+ if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto)
+ return 0;
+
+ if(p < 1)
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ if(strcmp(str,"Random")==0)
+ {
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_randomwarp(pl_sd,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePointAll")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePoint")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ mapindex=sd->status.save_point.map;
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ else
+ {
+ mapindex = mapindex_name2id(str);
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.party_id == p)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * Warpguild - [Fredzilla]
+ * Syntax: warpguild "mapname.gat",x,y,Guild_ID;
+ *------------------------------------------
+ */
+int buildin_warpguild(struct script_state *st)
+{
+ int x,y;
+ unsigned short mapindex;
+ char *str;
+ int g;
+ int i;
+ struct map_session_data *pl_sd, **pl_allsd;
+ int users;
+ struct map_session_data *sd;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ g=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+
+ if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto)
+ return 0;
+
+ if(g < 1)
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ if(strcmp(str,"Random")==0)
+ {
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_randomwarp(pl_sd,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePointAll")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3);
+ }
+ }
+ }
+ else if(strcmp(str,"SavePoint")==0)
+ {
+ if(map[sd->bl.m].flag.noreturn)
+ return 0;
+
+ mapindex=sd->status.save_point.map;
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ else
+ {
+ mapindex = mapindex_name2id(str);
+ for (i = 0; i < users; i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g)
+ {
+ if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp)
+ continue;
+ pc_setpos(pl_sd,mapindex,x,y,3);
+ }
+ }
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_heal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pc_heal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_itemheal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(potion_flag==1) {
+ potion_hp = hp;
+ potion_sp = sp;
+ return 0;
+ }
+
+ pc_itemheal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_percentheal(struct script_state *st)
+{
+ int hp,sp;
+
+ hp=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sp=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(potion_flag==1) {
+ potion_per_hp = hp;
+ potion_per_sp = sp;
+ return 0;
+ }
+
+ pc_percentheal(script_rid2sd(st),hp,sp);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_jobchange(struct script_state *st)
+{
+ int job, upper=-1;
+
+ job=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ upper=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if ((job >= 0 && job < MAX_PC_CLASS))
+ pc_jobchange(script_rid2sd(st),job, upper);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_input(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=(st->end>st->start+2)?st->stack->stack_data[st->start+2].u.num:0;
+ char *name=(char *) ((st->end>st->start+2)?str_buf+str_data[num&0x00ffffff].str:"");
+// char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ sd=script_rid2sd(st);
+ if(sd->state.menu_or_input){
+ sd->state.menu_or_input=0;
+ if( postfix=='$' ){
+ // •¶Žš—ñ
+ if(st->end>st->start+2){ // ˆø”1ŒÂ
+ set_reg(sd,num,name,(void*)sd->npc_str);
+ }else{
+ ShowError("buildin_input: string discarded !!\n");
+ return 1;
+ }
+ return 0;
+ }
+ // commented by Lupus (check Value Number Input fix in clif.c)
+ // readded by Yor: set ammount to 0 instead of cancel trade.
+ // ** Fix by fritz :X keeps people from abusing old input bugs
+ if (sd->npc_amount < 0) { //** If input amount is less then 0
+// clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris
+// buildin_close(st); // ** close
+ sd->npc_amount = 0;
+ } else if ((unsigned int)sd->npc_amount > battle_config.vending_max_value) // new fix by Yor
+ sd->npc_amount = battle_config.vending_max_value;
+
+ // ”’l
+ if(st->end>st->start+2){ // ˆø”1ŒÂ
+ set_reg(sd,num,name,(void*)sd->npc_amount);
+ } else {
+ // ragemuŒÝŠ·‚Ì‚½‚ß
+ pc_setreg(sd,add_str((unsigned char *) "l14"),sd->npc_amount);
+ }
+ return 0;
+ }
+ //state.menu_or_input = 0
+ st->state=RERUNLINE;
+ if(postfix=='$')
+ clif_scriptinputstr(sd,st->oid);
+ else
+ clif_scriptinput(sd,st->oid);
+ sd->state.menu_or_input=1;
+ return 0;
+}
+
+/*==========================================
+ * •Ï”Ý’è
+ *------------------------------------------
+ */
+int buildin_set(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( st->stack->stack_data[st->start+2].type!=C_NAME ){
+ ShowError("script: buildin_set: not name\n");
+ return 1;
+ }
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+
+ if( postfix=='$' ){
+ // •¶Žš—ñ
+ char *str = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ set_reg(sd,num,name,(void*)str);
+ }else{
+ // ”’l
+ int val = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ set_reg(sd,num,name,(void*)val);
+ }
+
+ return 0;
+}
+/*==========================================
+ * ”z—ñ•Ï”Ý’è
+ *------------------------------------------
+ */
+int buildin_setarray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int i,j;
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_setarray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ for(j=0,i=st->start+3; i<st->end && j<128;i++,j++){
+ void *v;
+ if( postfix=='$' )
+ v=(void*)conv_str(st,& (st->stack->stack_data[i]));
+ else
+ v=(void*)conv_num(st,& (st->stack->stack_data[i]));
+ set_reg( sd, num+(j<<24), name, v);
+ }
+ return 0;
+}
+/*==========================================
+ * ”z—ñ•Ï”ƒNƒŠƒA
+ *------------------------------------------
+ */
+int buildin_cleararray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int sz=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int i;
+ void *v;
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_cleararray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ if( postfix=='$' )
+ v=(void*)conv_str(st,& (st->stack->stack_data[st->start+3]));
+ else
+ v=(void*)conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ for(i=0;i<sz;i++)
+ set_reg(sd,num+(i<<24),name,v);
+ return 0;
+}
+/*==========================================
+ * ”z—ñ•Ï”ƒRƒs[
+ *------------------------------------------
+ */
+int buildin_copyarray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int num2=st->stack->stack_data[st->start+3].u.num;
+ char *name2=str_buf+str_data[num2&0x00ffffff].str;
+ char prefix2=*name2;
+ char postfix2=name2[strlen(name2)-1];
+ int sz=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int i;
+
+ if( prefix!='$' && prefix!='@' && prefix2!='$' && prefix2!='@' ){
+ ShowWarning("buildin_copyarray: illegal scope !\n");
+ return 1;
+ }
+ if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){
+ ShowError("buildin_copyarray: type mismatch !\n");
+ return 1;
+ }
+ if( prefix!='$' || prefix2!='$' )
+ sd=script_rid2sd(st);
+
+ // if two array is the same and (num > num2), bottom-up copy is required [Eoe / jA 1116]
+ if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) {
+ for(i=sz-1;i>=0;i--)
+ set_reg(sd,num+(i<<24),name, get_val2(st,num2+(i<<24)) );
+ } else {
+ for(i=0;i<sz;i++)
+ set_reg(sd,num+(i<<24),name, get_val2(st,num2+(i<<24)) );
+ }
+
+ return 0;
+}
+/*==========================================
+ * ”z—ñ•Ï”‚̃TƒCƒYŠ“¾
+ *------------------------------------------
+ */
+static int getarraysize(struct script_state *st,int num,int postfix)
+{
+ int i=(num>>24),c=-1; // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance]
+ for(;i<128;i++){
+ // num must be the first elements of array [Eoe / jA 1127]
+ void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24));
+ if(postfix=='$' && *((char*)v) ) c=i;
+ if(postfix!='$' && (int)v )c=i;
+ }
+ return c+1;
+}
+int buildin_getarraysize(struct script_state *st)
+{
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_copyarray: illegal scope !\n");
+ return 1;
+ }
+
+ push_val(st->stack,C_INT,getarraysize(st,num,postfix) );
+ return 0;
+}
+/*==========================================
+ * ”z—ñ•Ï”‚©‚ç—v‘fíœ
+ *------------------------------------------
+ */
+int buildin_deletearray(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int num=st->stack->stack_data[st->start+2].u.num;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char prefix=*name;
+ char postfix=name[strlen(name)-1];
+ int count=1;
+ int i,sz=getarraysize(st,num,postfix)-(num>>24)-count+1;
+
+
+ if( (st->end > st->start+3) )
+ count=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if( prefix!='$' && prefix!='@' ){
+ ShowWarning("buildin_deletearray: illegal scope !\n");
+ return 1;
+ }
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+
+ for(i=0;i<sz;i++){
+ set_reg(sd,num+(i<<24),name, get_val2(st,num+((i+count)<<24) ) );
+ }
+ for(;i<(128-(num>>24));i++){
+ if( postfix!='$' ) set_reg(sd,num+(i<<24),name, 0);
+ if( postfix=='$' ) set_reg(sd,num+(i<<24),name, (void *) "");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Žw’è—v‘f‚ð•\‚·’l(ƒL[)‚ðŠ“¾‚·‚é
+ *------------------------------------------
+ */
+int buildin_getelementofarray(struct script_state *st)
+{
+ if( st->stack->stack_data[st->start+2].type==C_NAME ){
+ int i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(i>127 || i<0){
+ ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }else{
+ push_val(st->stack,C_NAME,
+ (i<<24) | st->stack->stack_data[st->start+2].u.num );
+ }
+ }else{
+ ShowError("script: getelementofarray (operator[]): param1 not name !\n");
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_setlook(struct script_state *st)
+{
+ int type,val;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ pc_changelook(script_rid2sd(st),type,val);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_cutin(struct script_state *st)
+{
+ int type;
+
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ clif_cutin(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str,type);
+
+ return 0;
+}
+/*==========================================
+ * ƒJ[ƒh‚̃Cƒ‰ƒXƒg‚ð•\Ž¦‚·‚é
+ *------------------------------------------
+ */
+int buildin_cutincard(struct script_state *st)
+{
+ int itemid;
+ struct item_data *i_data;
+
+ itemid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ i_data = itemdb_exists(itemid);
+ if (i_data)
+ clif_cutin(script_rid2sd(st),i_data->cardillustname,4);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_viewpoint(struct script_state *st)
+{
+ int type,x,y,id,color;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ id=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ color=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_countitem(struct script_state *st)
+{
+ int nameid=0,count=0,i;
+ struct map_session_data *sd;
+
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data;
+ if( (item_data = itemdb_searchname(name)) != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ if (nameid>=500) //if no such ID then skip this iteration
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==nameid)
+ count+=sd->status.inventory[i].amount;
+ }
+ else{
+ if(battle_config.error_log)
+ ShowError("wrong item ID : countitem(%i)\n",nameid);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+ push_val(st->stack,C_INT,count);
+ return 0;
+}
+
+/*==========================================
+ * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus]
+ * returns number of items that met the conditions
+ *------------------------------------------
+ */
+int buildin_countitem2(struct script_state *st)
+{
+ int nameid=0,count=0,i;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct map_session_data *sd;
+
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data;
+ if( (item_data = itemdb_searchname(name)) != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ iden=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if (nameid>=500) //if no such ID then skip this iteration
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ||
+ sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref ||
+ sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 ||
+ sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 ||
+ sd->status.inventory[i].card[3]!=c4)
+ continue;
+
+ count+=sd->status.inventory[i].amount;
+ }
+ else{
+ if(battle_config.error_log)
+ ShowError("wrong item ID : countitem2(%i)\n",nameid);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+ push_val(st->stack,C_INT,count);
+
+ return 0;
+}
+
+/*==========================================
+ * d—ʃ`ƒFƒbƒN
+ *------------------------------------------
+ */
+int buildin_checkweight(struct script_state *st)
+{
+ int nameid=0,amount,i;
+ unsigned long weight;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items
+ push_val(st->stack,C_INT,0);
+ ShowError("buildin_checkweight: Wrong item ID or amount.\n");
+ return 1;
+ }
+
+ weight = itemdb_weight(nameid)*amount;
+ if(amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight){
+ push_val(st->stack,C_INT,0);
+ } else {
+ //Check if the inventory ain't full.
+ //TODO: Currently does not checks if you can just stack it on top of another item you already have....
+
+ i = pc_search_inventory(sd,0);
+ if (i >= 0) //Empty slot available.
+ push_val(st->stack,C_INT,1);
+ else //Inventory full
+ push_val(st->stack,C_INT,0);
+
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getitem(struct script_state *st)
+{
+ int nameid,nameidsrc,amount,flag = 0;
+ struct item item_tmp;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple item ID
+ if( item_data != NULL)
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ if ( ( amount=conv_num(st,& (st->stack->stack_data[st->start+3])) ) <= 0) {
+ return 0; //return if amount <=0, skip the useles iteration
+ }
+ //Violet Box, Blue Box, etc - random item pick
+ if((nameidsrc = nameid)<0) { // Save real ID of the source Box [Lupus]
+ nameid=itemdb_searchrandomid(-nameid);
+
+ if(log_config.present > 0)
+ log_present(sd, -nameidsrc, nameid); //fixed missing ID by Lupus
+
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=!itemdb_isequip3(nameid);
+ if( st->end>st->start+5 ) //ƒAƒCƒeƒ€‚ðŽw’肵‚½ID‚É“n‚·
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+5])));
+ if(sd == NULL) //ƒAƒCƒeƒ€‚ð“n‚·‘ŠŽè‚ª‚¢‚È‚©‚Á‚½‚炨‹A‚è
+ return 0;
+ if((flag = pc_additem(sd,&item_tmp,amount))) {
+ clif_additem(sd,0,0,flag);
+ if(pc_candrop(sd,nameid))
+ map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, nameid, amount, NULL);
+ }
+ //Logs
+
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getitem2(struct script_state *st)
+{
+ int nameid,amount,flag = 0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct item_data *item_data;
+ struct item item_tmp;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple item ID
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ iden=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+10]));
+ if( st->end>st->start+11 ) //ƒAƒCƒeƒ€‚ðŽw’肵‚½ID‚É“n‚·
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+11])));
+ if(sd == NULL) //ƒAƒCƒeƒ€‚ð“n‚·‘ŠŽè‚ª‚¢‚È‚©‚Á‚½‚炨‹A‚è
+ return 0;
+
+ if(nameid<0) { // ƒ‰ƒ“ƒ_ƒ€
+ nameid=itemdb_searchrandomid(-nameid);
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_data=itemdb_exists(nameid);
+ if (item_data == NULL)
+ return -1;
+ if(item_data->type==4 || item_data->type==5){
+ if(ref > 10) ref = 10;
+ }
+ else if(item_data->type==7) {
+ iden = 1;
+ ref = 0;
+ }
+ else {
+ iden = 1;
+ ref = attr = 0;
+ }
+
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=iden;
+ else if(item_data->type==4 || item_data->type==5)
+ item_tmp.identify=0;
+ item_tmp.refine=ref;
+ item_tmp.attribute=attr;
+ item_tmp.card[0]=c1;
+ item_tmp.card[1]=c2;
+ item_tmp.card[2]=c3;
+ item_tmp.card[3]=c4;
+ if((flag = pc_additem(sd,&item_tmp,amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, nameid, amount, &item_tmp);
+ }
+ //Logs
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * gets an item with someone's name inscribed [Skotlex]
+ * getinscribeditem item_num, character_name
+ * Returned Qty is always 1, only works on equip-able
+ * equipment
+ *------------------------------------------
+ */
+int buildin_getnameditem(struct script_state *st)
+{
+ int nameid;
+ struct item item_tmp;
+ struct map_session_data *sd, *tsd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ { //Player not attached!
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL)
+ { //Failed
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ nameid = item_data->nameid;
+ }else
+ nameid = conv_num(st,data);
+
+ if(!itemdb_exists(nameid) || !itemdb_isequip3(nameid))
+ { //We don't allow non-equipable/stackable items to be named
+ //to avoid any qty exploits that could happen because of it.
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ data=&(st->stack->stack_data[st->start+3]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ) //Char Name
+ tsd=map_nick2sd(conv_str(st,data));
+ else //Char Id was given
+ tsd=map_charid2sd(conv_num(st,data));
+
+ if( tsd == NULL )
+ { //Failed
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ item_tmp.amount=1;
+ item_tmp.identify=1;
+ item_tmp.card[0]=254; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus]
+ item_tmp.card[2]=tsd->status.char_id;
+ item_tmp.card[3]=tsd->status.char_id >> 16;
+ if(pc_additem(sd,&item_tmp,1)) {
+ push_val(st->stack,C_INT,0);
+ return 0; //Failed to add item, we will not drop if they don't fit
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, item_tmp.amount, &item_tmp);
+ }
+ //Logs
+
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+/*==========================================
+ * gets a random item ID from an item group [Skotlex]
+ * groupranditem group_num
+ *------------------------------------------
+ */
+int buildin_grouprandomitem(struct script_state *st)
+{
+ int group;
+
+ group = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, itemdb_searchrandomgroup(group));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_makeitem(struct script_state *st)
+{
+ int nameid,amount,flag = 0;
+ int x,y,m;
+ char *mapname;
+ struct item item_tmp;
+ struct script_data *data;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ nameid=512; //Apple Item ID
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ mapname =conv_str(st,& (st->stack->stack_data[st->start+4]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ if(strcmp(mapname,"this")==0)
+ {
+ struct map_session_data *sd;
+ sd = script_rid2sd(st);
+ if (!sd) return 0; //Failed...
+ m=sd->bl.m;
+ } else
+ m=map_mapname2mapid(mapname);
+
+ if(nameid<0) { // ƒ‰ƒ“ƒ_ƒ€
+ nameid=itemdb_searchrandomid(-nameid);
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=!itemdb_isequip3(nameid);
+
+ map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ * script DELITEM command (fixed 2 bugs by Lupus, added deletion priority by Lupus)
+ *------------------------------------------
+ */
+int buildin_delitem(struct script_state *st)
+{
+ int nameid=0,amount,i,important_item=0;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ //nameid=512;
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0
+ //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount);
+ return 0;
+ }
+ //1st pass
+ //here we won't delete items with CARDS, named items but we count them
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item or equipped item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid )
+ continue;
+ //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
+ if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){
+ intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) );
+ //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^)
+ sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0;
+ //now this egg'll be deleted as a common unimportant item
+ }
+ //is this item important? does it have cards? or Player's name? or Refined/Upgraded
+ if( sd->status.inventory[i].card[0] || sd->status.inventory[i].card[1] ||
+ sd->status.inventory[i].card[2] || sd->status.inventory[i].card[3] || sd->status.inventory[i].refine) {
+ //this is important item, count it
+ important_item++;
+ continue;
+ }
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+ //2nd pass
+ //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally
+ if (important_item>0 && amount>0)
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid )
+ continue;
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+* advanced version of delitem [modified by Mihilion]
+*------------------------------------------
+*/
+int buildin_delitem2(struct script_state *st)
+{
+ int nameid=0,amount,i=0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct map_session_data *sd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ //nameid=512;
+ if( item_data )
+ nameid=item_data->nameid;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ iden=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ref=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ attr=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ c1=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ c2=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ c3=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ c4=conv_num(st,& (st->stack->stack_data[st->start+10]));
+
+ if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0
+ //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount);
+ return 0;
+ }
+
+ for(i=0;i<MAX_INVENTORY;i++){
+ //we don't delete wrong item or equipped item
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ||
+ sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref ||
+ sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 ||
+ sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 ||
+ sd->status.inventory[i].card[3]!=c4)
+ continue;
+ //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
+ if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){
+ intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) );
+ //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^)
+ sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0;
+ //now this egg'll be deleted as a common unimportant item
+ }
+
+ if(sd->status.inventory[i].amount>=amount){
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,amount,0);
+ return 0; //we deleted exact amount of items. now exit
+ } else {
+ amount-=sd->status.inventory[i].amount;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,sd->status.inventory[i].amount,0);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Enables/Disables use of items while in an NPC [Skotlex]
+ *------------------------------------------
+ */
+int buildin_enableitemuse(struct script_state *st) {
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = st->oid;
+ return 0;
+}
+
+int buildin_disableitemuse(struct script_state *st) {
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ *ƒLƒƒƒ‰ŠÖŒW‚̃pƒ‰ƒ[ƒ^Žæ“¾
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ *ƒLƒƒƒ‰ŠÖŒW‚Ì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;
+}
+/*==========================================
+ *Žw’èID‚ÌPT–¼Žæ“¾
+ *------------------------------------------
+ */
+char *buildin_getpartyname_sub(int party_id)
+{
+ struct party *p;
+
+ p=NULL;
+ p=party_search(party_id);
+
+ if(p!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, p->name, NAME_LENGTH-1);
+ return buf;
+ }
+
+ return 0;
+}
+int buildin_getpartyname(struct script_state *st)
+{
+ char *name;
+ int party_id;
+
+ party_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name=buildin_getpartyname_sub(party_id);
+ if(name != NULL)
+ push_str(st->stack,C_STR,(unsigned char *)name);
+ else
+ push_str(st->stack,C_CONSTSTR, (unsigned char *) "null");
+
+ return 0;
+}
+/*==========================================
+ *Žw’èID‚ÌPTl”‚ƃƒ“ƒo[IDŽæ“¾
+ *------------------------------------------
+ */
+int buildin_getpartymember(struct script_state *st)
+{
+ struct party *p;
+ int i,j=0;
+
+ p=NULL;
+ p=party_search(conv_num(st,& (st->stack->stack_data[st->start+2])));
+
+ if(p!=NULL){
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->member[i].account_id){
+// printf("name:%s %d\n",p->member[i].name,i);
+ mapreg_setregstr(add_str((unsigned char *) "$@partymembername$")+(i<<24),p->member[i].name);
+ j++;
+ }
+ }
+ }
+ mapreg_setreg(add_str((unsigned char *) "$@partymembercount"),j);
+
+ return 0;
+}
+/*==========================================
+ *Žw’èID‚̃Mƒ‹ƒh–¼Žæ“¾
+ *------------------------------------------
+ */
+char *buildin_getguildname_sub(int guild_id)
+{
+ struct guild *g=NULL;
+ g=guild_search(guild_id);
+
+ if(g!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, g->name, NAME_LENGTH-1);
+ return buf;
+ }
+ return NULL;
+}
+int buildin_getguildname(struct script_state *st)
+{
+ char *name;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name=buildin_getguildname_sub(guild_id);
+ if(name != NULL)
+ push_str(st->stack,C_STR,(unsigned char *) name);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+}
+
+/*==========================================
+ *Žw’èID‚ÌGuildMaster–¼Žæ“¾
+ *------------------------------------------
+ */
+char *buildin_getguildmaster_sub(int guild_id)
+{
+ struct guild *g=NULL;
+ g=guild_search(guild_id);
+
+ if(g!=NULL){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, g->master, NAME_LENGTH-1);
+ return buf;
+ }
+
+ return 0;
+}
+int buildin_getguildmaster(struct script_state *st)
+{
+ char *master;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ master=buildin_getguildmaster_sub(guild_id);
+ if(master!=0)
+ push_str(st->stack,C_STR,(unsigned char *) master);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+}
+
+int buildin_getguildmasterid(struct script_state *st)
+{
+ char *master;
+ struct map_session_data *sd=NULL;
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ master=buildin_getguildmaster_sub(guild_id);
+ if(master!=0){
+ if((sd=map_nick2sd(master)) == NULL){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,sd->status.char_id);
+ }else{
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒLƒƒƒ‰ƒNƒ^‚Ì–¼‘O
+ *------------------------------------------
+ */
+int buildin_strcharinfo(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int num;
+
+ sd=script_rid2sd(st);
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(num==0){
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, sd->status.name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ }
+ if(num==1){
+ char *buf;
+ buf=buildin_getpartyname_sub(sd->status.party_id);
+ if(buf!=0)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ }
+ if(num==2){
+ char *buf;
+ buf=buildin_getguildname_sub(sd->status.guild_id);
+ if(buf != NULL)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ }
+
+ return 0;
+}
+
+unsigned int equip[10]={0x0100,0x0010,0x0020,0x0002,0x0004,0x0040,0x0008,0x0080,0x0200,0x0001};
+
+/*==========================================
+ * GetEquipID(Pos); Pos: 1-10
+ *------------------------------------------
+ */
+int buildin_getequipid(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ struct item_data* item;
+
+ sd=script_rid2sd(st);
+ if(sd == NULL)
+ {
+ ShowError("getequipid: sd == NULL\n");
+ return 0;
+ }
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0){
+ item=sd->inventory_data[i];
+ if(item)
+ push_val(st->stack,C_INT,item->nameid);
+ else
+ push_val(st->stack,C_INT,0);
+ }else{
+ push_val(st->stack,C_INT,-1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ‘•”õ–¼•¶Žš—ñi¸˜Bƒƒjƒ…[—pj
+ *------------------------------------------
+ */
+int buildin_getequipname(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ struct item_data* item;
+ char *buf;
+
+ buf=(char *)aCallocA(64,sizeof(char));
+ sd=script_rid2sd(st);
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0){
+ item=sd->inventory_data[i];
+ if(item)
+ sprintf(buf,"%s-[%s]",pos[num-1],item->jname);
+ else
+ sprintf(buf,"%s-[%s]",pos[num-1],pos[10]);
+ }else{
+ sprintf(buf,"%s-[%s]",pos[num-1],pos[10]);
+ }
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+
+ return 0;
+}
+
+/*==========================================
+ * getbrokenid [Valaris]
+ *------------------------------------------
+ */
+int buildin_getbrokenid(struct script_state *st)
+{
+ int i,num,id=0,brokencounter=0;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute==1){
+ brokencounter++;
+ if(num==brokencounter){
+ id=sd->status.inventory[i].nameid;
+ break;
+ }
+ }
+ }
+
+ push_val(st->stack,C_INT,id);
+
+ return 0;
+}
+
+/*==========================================
+ * repair [Valaris]
+ *------------------------------------------
+ */
+int buildin_repair(struct script_state *st)
+{
+ int i,num;
+ int repaircounter=0;
+ struct map_session_data *sd;
+
+
+ sd=script_rid2sd(st);
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute==1){
+ repaircounter++;
+ if(num==repaircounter){
+ sd->status.inventory[i].attribute=0;
+ clif_equiplist(sd);
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ clif_misceffect(&sd->bl, 3);
+ clif_displaymessage(sd->fd,"Item has been repaired.");
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ‘•”õƒ`ƒFƒbƒN
+ *------------------------------------------
+ */
+int buildin_getequipisequiped(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if ((num - 1) >= (sizeof(equip) / sizeof(equip[0])))
+ i = -1;
+ else
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if(i >= 0)
+ push_val(st->stack,C_INT,1);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ‘•”õ•i¸˜B‰Â”\ƒ`ƒFƒbƒN
+ *------------------------------------------
+ */
+int buildin_getequipisenableref(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && num<7 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine)
+ {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ‘•”õ•iŠÓ’èƒ`ƒFƒbƒN
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ‘•”õ•i¸˜B“x
+ *------------------------------------------
+ */
+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;
+}
+
+/*==========================================
+ * ‘•”õ•i•Ší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;
+}
+
+/*==========================================
+ * ‘•”õ•i¸˜B¬Œ÷—¦
+ *------------------------------------------
+ */
+int buildin_getequippercentrefinery(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE)
+ push_val(st->stack,C_INT,percentrefinery[itemdb_wlv(sd->status.inventory[i].nameid)][(int)sd->status.inventory[i].refine]);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * ¸˜B¬Œ÷
+ *------------------------------------------
+ */
+int buildin_successrefitem(struct script_state *st)
+{
+ int i,num,ep;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep=sd->status.inventory[i].equip;
+
+ if(log_config.refine > 0)
+ log_refine(sd, i, 1);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2);
+
+ clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine);
+ clif_delitem(sd,i,1);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, 1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,3);
+ if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == 0x00ff && sd->char_id == MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])){ // Fame point system [DracoRPG]
+ switch (sd->inventory_data[i]->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ¸˜BŽ¸”s
+ *------------------------------------------
+ */
+int buildin_failedrefitem(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ if(log_config.refine > 0)
+ log_refine(sd, i, 0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ sd->status.inventory[i].refine = 0;
+ pc_unequipitem(sd,i,3);
+ // ¸˜BŽ¸”sƒGƒtƒFƒNƒg‚̃pƒPƒbƒg
+ clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine);
+
+ pc_delitem(sd,i,1,0);
+ // ‘¼‚Ìl‚É‚àŽ¸”s‚ð’Ê’m
+ 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;
+}
+/*==========================================
+ * ‘•”õ•i‚É‚æ‚é”\—Í’lƒ{[ƒiƒX
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ‘•”õ•i‚É‚æ‚é”\—Í’lƒ{[ƒiƒX
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ‘•”õ•i‚É‚æ‚é”\—Í’lƒ{[ƒiƒX
+ *------------------------------------------
+ */
+int buildin_bonus3(struct script_state *st)
+{
+ int type,type2,type3,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type2=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ type3=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ sd=script_rid2sd(st);
+ pc_bonus3(sd,type,type2,type3,val);
+
+ return 0;
+}
+
+int buildin_bonus4(struct script_state *st)
+{
+ int type,type2,type3,type4,val;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type2=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ type3=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ type4=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ val=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ sd=script_rid2sd(st);
+ pc_bonus4(sd,type,type2,type3,type4,val);
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒLƒ‹Š“¾
+ *------------------------------------------
+ */
+int buildin_skill(struct script_state *st)
+{
+ int id,level,flag=1;
+ struct map_session_data *sd;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ pc_skill(sd,id,level,flag);
+
+ return 0;
+}
+
+// add x levels of skill (stackable) [Valaris]
+int buildin_addtoskill(struct script_state *st)
+{
+ int id,level,flag=2;
+ struct map_session_data *sd;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ pc_skill(sd,id,level,flag);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒ‹ƒhƒXƒLƒ‹Žæ“¾
+ *------------------------------------------
+ */
+int buildin_guildskill(struct script_state *st)
+{
+ int id,level,flag=0;
+ struct map_session_data *sd;
+ int i=0;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ for(i=0;i<level;i++)
+ guild_skillup(sd,id,flag);
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒLƒ‹ƒŒƒxƒ‹Š“¾
+ *------------------------------------------
+ */
+int buildin_getskilllv(struct script_state *st)
+{
+ int id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) );
+ return 0;
+}
+/*==========================================
+ * getgdskilllv(Guild_ID, Skill_ID);
+ * skill_id = 10000 : GD_APPROVAL
+ * 10001 : GD_KAFRACONTRACT
+ * 10002 : GD_GUARDIANRESEARCH
+ * 10003 : GD_GUARDUP
+ * 10004 : GD_EXTENSION
+ *------------------------------------------
+ */
+int buildin_getgdskilllv(struct script_state *st)
+{
+ int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skill_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ struct guild *g=guild_search(guild_id);
+ push_val(st->stack,C_INT, (g==NULL)?-1:guild_checkskill(g,skill_id) );
+ return 0;
+/*
+ struct map_session_data *sd=NULL;
+ struct guild *g=NULL;
+ int skill_id;
+
+ skill_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ if(sd && sd->status.guild_id > 0) g=guild_search(sd->status.guild_id);
+ if(sd && g) {
+ push_val(st->stack,C_INT, guild_checkskill(g,skill_id+9999) );
+ } else {
+ push_val(st->stack,C_INT,-1);
+ }
+ return 0;
+*/
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_basicskillcheck(struct script_state *st)
+{
+ push_val(st->stack,C_INT, battle_config.basic_skill_check);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_getgmlevel(struct script_state *st)
+{
+ push_val(st->stack,C_INT, pc_isGM(script_rid2sd(st)));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_end(struct script_state *st)
+{
+ st->state = END;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->status.option & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption1(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->opt1 & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_checkoption2(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+
+ if(sd->opt2 & type){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_setoption(struct script_state *st)
+{
+ int type;
+ struct map_session_data *sd;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ pc_setoption(sd,type);
+
+ return 0;
+}
+
+/*==========================================
+ * Checkcart [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_checkcart(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(pc_iscarton(sd)){
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒJ[ƒg‚ð•t‚¯‚é
+ *------------------------------------------
+ */
+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;
+}
+
+
+/*==========================================
+ * ‘é‚ð•t‚¯‚é
+ *------------------------------------------
+ */
+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;
+}
+
+
+/*==========================================
+ * ƒyƒRƒyƒRæ‚è
+ *------------------------------------------
+ */
+int buildin_setriding(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+ pc_setriding(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒZ[ƒuƒ|ƒCƒ“ƒg‚Ì•Û‘¶
+ *------------------------------------------
+ */
+int buildin_savepoint(struct script_state *st)
+{
+ int x,y;
+ short map;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ map = mapindex_name2id(str);
+ if (map)
+ pc_setsavepoint(script_rid2sd(st),map,x,y);
+ return 0;
+}
+
+/*==========================================
+ * GetTimeTick(0: System Tick, 1: Time Second Tick)
+ *------------------------------------------
+ */
+int buildin_gettimetick(struct script_state *st) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ switch(type){
+ case 2:
+ //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC
+ // from the system clock.)
+ push_val(st->stack,C_INT,(int)time(NULL));
+ break;
+ case 1:
+ //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59)
+ time(&timer);
+ t=localtime(&timer);
+ push_val(st->stack,C_INT,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec));
+ break;
+ case 0:
+ default:
+ //type 0:(System Ticks)
+ push_val(st->stack,C_INT,gettick());
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTime(Type);
+ * 1: Sec 2: Min 3: Hour
+ * 4: WeekDay 5: MonthDay 6: Month
+ * 7: Year
+ *------------------------------------------
+ */
+int buildin_gettime(struct script_state *st) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch(type){
+ case 1://Sec(0~59)
+ push_val(st->stack,C_INT,t->tm_sec);
+ break;
+ case 2://Min(0~59)
+ push_val(st->stack,C_INT,t->tm_min);
+ break;
+ case 3://Hour(0~23)
+ push_val(st->stack,C_INT,t->tm_hour);
+ break;
+ case 4://WeekDay(0~6)
+ push_val(st->stack,C_INT,t->tm_wday);
+ break;
+ case 5://MonthDay(01~31)
+ push_val(st->stack,C_INT,t->tm_mday);
+ break;
+ case 6://Month(01~12)
+ push_val(st->stack,C_INT,t->tm_mon+1);
+ break;
+ case 7://Year(20xx)
+ push_val(st->stack,C_INT,t->tm_year+1900);
+ break;
+ default://(format error)
+ push_val(st->stack,C_INT,-1);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTimeStr("TimeFMT", Length);
+ *------------------------------------------
+ */
+int buildin_gettimestr(struct script_state *st)
+{
+ char *tmpstr;
+ char *fmtstr;
+ int maxlen;
+ time_t now = time(NULL);
+
+ fmtstr=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ maxlen=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ tmpstr=(char *)aCallocA(maxlen+1,sizeof(char));
+ strftime(tmpstr,maxlen,fmtstr,localtime(&now));
+ tmpstr[maxlen]='\0';
+
+ push_str(st->stack,C_STR,(unsigned char *) tmpstr);
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ðŠJ‚­
+ *------------------------------------------
+ */
+int buildin_openstorage(struct script_state *st)
+{
+ storage_storageopen(script_rid2sd(st));
+ return 0;
+}
+
+int buildin_guildopenstorage(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int ret;
+ ret = storage_guild_storageopen(sd);
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€‚É‚æ‚éƒXƒLƒ‹”­“®
+ *------------------------------------------
+ */
+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]));
+
+ // ‰r¥’†‚ɃXƒLƒ‹ƒAƒCƒeƒ€‚ÍŽg—p‚Å‚«‚È‚¢
+ if(sd->skilltimer != -1)
+ return 0;
+
+ sd->skillitem=id;
+ sd->skillitemlv=lv;
+ clif_item_skill(sd,id,lv,str);
+ return 0;
+}
+/*==========================================
+ * ƒAƒCƒeƒ€ì¬
+ *------------------------------------------
+ */
+int buildin_produce(struct script_state *st)
+{
+ int trigger;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if( sd->state.produce_flag == 1) return 0;
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ clif_skill_produce_mix_list(sd,trigger);
+ return 0;
+}
+/*==========================================
+ * NPC‚Ńyƒbƒgì‚é
+ *------------------------------------------
+ */
+int buildin_makepet(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ struct script_data *data;
+ int id,pet_id;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+
+ id=conv_num(st,data);
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0 && sd) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ }
+
+ return 0;
+}
+/*==========================================
+ * NPC‚ÅŒoŒ±’lã‚°‚é
+ *------------------------------------------
+ */
+int buildin_getexp(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int base=0,job=0;
+
+ base=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ job =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(base<0 || job<0)
+ return 0;
+ if(sd)
+ pc_gainexp(sd,base,job);
+
+ return 0;
+}
+
+/*==========================================
+ * Gain guild exp [Celest]
+ *------------------------------------------
+ */
+int buildin_guildgetexp(struct script_state *st)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int exp;
+
+ exp = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(exp < 0)
+ return 0;
+ if(sd && sd->status.guild_id > 0)
+ guild_getexp (sd, exp);
+
+ return 0;
+}
+
+/*==========================================
+ * Changes the guild master of a guild [Skotlex]
+ *------------------------------------------
+ */
+int buildin_guildchangegm(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int guild_id;
+ char *name;
+
+ guild_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ name = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ sd=map_nick2sd(name);
+
+ if (!sd)
+ push_val(st->stack,C_INT,0);
+ else
+ push_val(st->stack,C_INT,guild_gm_change(guild_id, sd));
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ‚ƒ“ƒXƒ^[”­¶
+ *------------------------------------------
+ */
+int buildin_monster(struct script_state *st)
+{
+ int class_,amount,x,y;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+5]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ if( st->end>st->start+8 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+8]));
+
+ if (class_ >= 0 && !mobdb_checkid(class_)) {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_);
+ return 1;
+ }
+ mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,amount,event);
+ return 0;
+}
+/*==========================================
+ * ƒ‚ƒ“ƒXƒ^[”­¶
+ *------------------------------------------
+ */
+int buildin_areamonster(struct script_state *st)
+{
+ int class_,amount,x0,y0,x1,y1;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0 =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0 =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1 =conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1 =conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+7]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+9]));
+ if( st->end>st->start+10 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+10]));
+
+ mob_once_spawn_area(map_id2sd(st->rid),map,x0,y0,x1,y1,str,class_,amount,event);
+ return 0;
+}
+/*==========================================
+ * ƒ‚ƒ“ƒXƒ^[íœ
+ *------------------------------------------
+ */
+int buildin_killmonster_sub(struct block_list *bl,va_list ap)
+{
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ if(!allflag){
+ if(strcmp(event,((struct mob_data *)bl)->npc_event)==0)
+ mob_delete((struct mob_data *)bl);
+ return 0;
+ }else if(allflag){
+ if(((struct mob_data *)bl)->spawndelay1==-1 && ((struct mob_data *)bl)->spawndelay2==-1)
+ mob_delete((struct mob_data *)bl);
+ return 0;
+ }
+ return 0;
+}
+int buildin_killmonster(struct script_state *st)
+{
+ char *mapname,*event;
+ int m,allflag=0;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ if(strcmp(event,"All")==0)
+ allflag = 1;
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+ map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag);
+ return 0;
+}
+
+int buildin_killmonsterall_sub(struct block_list *bl,va_list ap)
+{
+ mob_delete((struct mob_data *)bl);
+ return 0;
+}
+int buildin_killmonsterall(struct script_state *st)
+{
+ char *mapname;
+ int m;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+ map_foreachinmap(buildin_killmonsterall_sub,
+ m,BL_MOB);
+ return 0;
+}
+
+/*==========================================
+ * Creates a clone of a player.
+ * clone map, x, y, event, char_id, master_id, mode, flag, duration
+ *------------------------------------------
+ */
+int buildin_clone(struct script_state *st) {
+ struct map_session_data *sd, *msd=NULL;
+ int char_id,master_id=0,x,y, mode = 0, flag = 0;
+ unsigned int duration = 0;
+ char *map,*event="";
+
+ map=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+5]));
+ char_id=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ if( st->end>st->start+7 )
+ master_id=conv_num(st,& (st->stack->stack_data[st->start+7]));
+
+ if( st->end>st->start+8 )
+ mode=conv_num(st,& (st->stack->stack_data[st->start+8]));
+
+ if( st->end>st->start+9 )
+ flag=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ if( st->end>st->start+10 )
+ duration=conv_num(st,& (st->stack->stack_data[st->start+10]));
+
+ sd = map_charid2sd(char_id);
+ if (master_id) {
+ msd = map_charid2sd(master_id);
+ if (msd)
+ master_id = msd->bl.id;
+ else
+ master_id = 0;
+ }
+ if (sd) //Return ID of newly crafted clone.
+ push_val(st->stack,C_INT,mob_clone_spawn(sd, map, x, y, event, master_id, mode, flag, 1000*duration));
+ else //Failed to create clone.
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+/*==========================================
+ * ƒCƒxƒ“ƒgŽÀs
+ *------------------------------------------
+ */
+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Žå‘̃Cƒxƒ“ƒgŽÀs
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}[’ljÁ
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}[íœ
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒCƒxƒ“ƒgƒ^ƒCƒ}[‚̃JƒEƒ“ƒg’l’ljÁ
+ *------------------------------------------
+ */
+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ƒ^ƒCƒ}[‰Šú‰»
+ *------------------------------------------
+ */
+int buildin_initnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_settimerevent_tick(nd,0);
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ * NPCƒ^ƒCƒ}[ŠJŽn
+ *------------------------------------------
+ */
+int buildin_startnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ * NPCƒ^ƒCƒ}[’âŽ~
+ *------------------------------------------
+ */
+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ƒ^ƒCƒ}[î•ñŠ“¾
+ *------------------------------------------
+ */
+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ƒ^ƒCƒ}[’lÝ’è
+ *------------------------------------------
+ */
+int buildin_setnpctimer(struct script_state *st)
+{
+ int tick;
+ struct npc_data *nd;
+ tick=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end > st->start+3 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_settimerevent_tick(nd,tick);
+ return 0;
+}
+
+/*==========================================
+ * attaches the player rid to the timer [Celest]
+ *------------------------------------------
+ */
+int buildin_attachnpctimer(struct script_state *st)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nd=(struct npc_data *)map_id2bl(st->oid);
+ if( st->end > st->start+2 ) {
+ char *name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ sd=map_nick2sd(name);
+ } else {
+ sd = script_rid2sd(st);
+ }
+
+ if (sd==NULL)
+ return 0;
+
+ nd->u.scr.rid = sd->bl.id;
+ return 0;
+}
+
+/*==========================================
+ * detaches a player rid from the timer [Celest]
+ *------------------------------------------
+ */
+int buildin_detachnpctimer(struct script_state *st)
+{
+ struct npc_data *nd;
+ if( st->end > st->start+2 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ nd->u.scr.rid = 0;
+ return 0;
+}
+
+/*==========================================
+ * To avoid "player not attached" script errors, this function is provided,
+ * it checks if there is a player attached to the current script. [Skotlex]
+ * If no, returns 0, if yes, returns the char_id of the attached player.
+ *------------------------------------------
+ */
+int buildin_playerattached(struct script_state *st)
+{
+ struct map_session_data *sd;
+ if (st->rid == 0 || (sd = map_id2sd(st->rid)) == NULL)
+ push_val(st->stack,C_INT,0);
+ else
+ push_val(st->stack,C_INT,st->rid);
+ return 0;
+}
+
+/*==========================================
+ * “V‚̺ƒAƒiƒEƒ“ƒX
+ *------------------------------------------
+ */
+int buildin_announce(struct script_state *st)
+{
+ char *str, *color=NULL;
+ int flag;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if (st->end>st->start+4)
+ color=conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ if(flag&0x0f){
+ struct block_list *bl=(flag&0x08)? map_id2bl(st->oid) :
+ (struct block_list *)script_rid2sd(st);
+ if (color)
+ clif_announce(bl,str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0),flag);
+ else
+ clif_GMmessage(bl,str,(int)strlen(str)+1,flag);
+ }else {
+ if (color)
+ intif_announce(str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0), flag);
+ else
+ intif_GMmessage(str,(int)strlen(str)+1,flag);
+ }
+ return 0;
+}
+/*==========================================
+ * “V‚̺ƒAƒiƒEƒ“ƒXi“Á’èƒ}ƒbƒvj
+ *------------------------------------------
+ */
+int buildin_mapannounce_sub(struct block_list *bl,va_list ap)
+{
+ char *str, *color;
+ int len,flag;
+ str=va_arg(ap,char *);
+ len=va_arg(ap,int);
+ flag=va_arg(ap,int);
+ color=va_arg(ap,char *);
+ if (color)
+ clif_announce(bl,str,len, strtol(color, (char **)NULL, 0), flag|3);
+ else
+ clif_GMmessage(bl,str,len,flag|3);
+ return 0;
+}
+int buildin_mapannounce(struct script_state *st)
+{
+ char *mapname,*str, *color=NULL;
+ int flag,m;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if (st->end>st->start+5)
+ color=conv_str(st,& (st->stack->stack_data[st->start+5]));
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ map_foreachinmap(buildin_mapannounce_sub,
+ m, BL_PC, str,strlen(str)+1,flag&0x10, color);
+ return 0;
+}
+/*==========================================
+ * “V‚̺ƒAƒiƒEƒ“ƒXi“Á’èƒGƒŠƒAj
+ *------------------------------------------
+ */
+int buildin_areaannounce(struct script_state *st)
+{
+ char *map,*str,*color=NULL;
+ int flag,m;
+ int x0,y0,x1,y1;
+
+ map=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ str=conv_str(st,& (st->stack->stack_data[st->start+7]));
+ flag=conv_num(st,& (st->stack->stack_data[st->start+8]));
+ if (st->end>st->start+9)
+ color=conv_str(st,& (st->stack->stack_data[st->start+9]));
+
+ if( (m=map_mapname2mapid(map))<0 )
+ return 0;
+
+ map_foreachinarea(buildin_mapannounce_sub,
+ m,x0,y0,x1,y1,BL_PC, str,strlen(str)+1,flag&0x10, color);
+ return 0;
+}
+
+/*==========================================
+ * ƒ†[ƒU[”Š“¾
+ *------------------------------------------
+ */
+int buildin_getusers(struct script_state *st)
+{
+ int flag=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct block_list *bl=map_id2bl((flag&0x08)?st->oid:st->rid);
+ int val=0;
+ switch(flag&0x07){
+ case 0: val=map[bl->m].users; break;
+ case 1: val=map_getusers(); break;
+ }
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+/*==========================================
+ * Works like @WHO - displays all online users names in window
+ *------------------------------------------
+ */
+int buildin_getusersname(struct script_state *st)
+{
+ struct map_session_data *pl_sd = NULL, **pl_allsd;
+ int i=0,disp_num=1, users;
+
+ pl_allsd = map_getallusers(&users);
+
+ for (i=0;i<users;i++)
+ {
+ pl_sd = pl_allsd[i];
+ if( !(battle_config.hide_GM_session && pc_isGM(pl_sd)) )
+ {
+ if((disp_num++)%10==0)
+ clif_scriptnext(script_rid2sd(st),st->oid);
+ clif_scriptmes(script_rid2sd(st),st->oid,pl_sd->status.name);
+ }
+ }
+ return 0;
+}
+/*==========================================
+ * ƒ}ƒbƒvŽw’胆[ƒU[”Š“¾
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ƒGƒŠƒAŽw’胆[ƒU[”Š“¾
+ *------------------------------------------
+ */
+int buildin_getareausers_sub(struct block_list *bl,va_list ap)
+{
+ int *users=va_arg(ap,int *);
+ (*users)++;
+ return 0;
+}
+int buildin_getareausers(struct script_state *st)
+{
+ char *str;
+ int m,x0,y0,x1,y1,users=0;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ if( (m=map_mapname2mapid(str))< 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareausers_sub,
+ m,x0,y0,x1,y1,BL_PC,&users);
+ push_val(st->stack,C_INT,users);
+ return 0;
+}
+
+/*==========================================
+ * ƒGƒŠƒAŽw’èƒhƒƒbƒvƒAƒCƒeƒ€”Š“¾
+ *------------------------------------------
+ */
+int buildin_getareadropitem_sub(struct block_list *bl,va_list ap)
+{
+ int item=va_arg(ap,int);
+ int *amount=va_arg(ap,int *);
+ struct flooritem_data *drop=(struct flooritem_data *)bl;
+
+ if(drop->item_data.nameid==item)
+ (*amount)+=drop->item_data.amount;
+
+ return 0;
+}
+int buildin_getareadropitem(struct script_state *st)
+{
+ char *str;
+ int m,x0,y0,x1,y1,item,amount=0;
+ struct script_data *data;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y0=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ x1=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ y1=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ data=&(st->stack->stack_data[st->start+7]);
+ get_val(st,data);
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ item=512;
+ if( item_data )
+ item=item_data->nameid;
+ }else
+ item=conv_num(st,data);
+
+ if( (m=map_mapname2mapid(str))< 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareadropitem_sub,
+ m,x0,y0,x1,y1,BL_ITEM,item,&amount);
+ push_val(st->stack,C_INT,amount);
+ return 0;
+}
+/*==========================================
+ * NPC‚Ì—LŒø‰»
+ *------------------------------------------
+ */
+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;
+}
+/*==========================================
+ * ‰B‚ê‚Ä‚¢‚é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‚ðƒnƒCƒfƒBƒ“ƒO
+ *------------------------------------------
+ */
+int buildin_hideonnpc(struct script_state *st)
+{
+ char *str;
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ npc_enable(str,4);
+ return 0;
+}
+/*==========================================
+ * ó‘ÔˆÙí‚É‚©‚©‚é
+ *------------------------------------------
+ */
+int buildin_sc_start(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val4=0;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if( st->end>st->start+5 ) //Žw’肵‚½ƒLƒƒƒ‰‚ðó‘ÔˆÙí‚É‚·‚é
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+5])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2; //Thrown potions only last half.
+ val4 = 1; //Mark that this was a thrown sc_effect
+ }
+ if (bl)
+ status_change_start(bl,type,val1,0,0,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * ó‘ÔˆÙí‚É‚©‚©‚é(Šm—¦Žw’è)
+ *------------------------------------------
+ */
+int buildin_sc_start2(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val4=0,per;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ per=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ if( st->end>st->start+6 ) //Žw’肵‚½ƒLƒƒƒ‰‚ðó‘ÔˆÙí‚É‚·‚é
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2;
+ val4 = 1;
+ }
+
+ if(bl && rand()%10000 < per)
+ status_change_start(bl,type,val1,0,0,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * Starts a SC_ change with the four values passed. [Skotlex]
+ * Final optional argument is the ID of player to affect.
+ * sc_start4 type, duration, val1, val2, val3, val4, <id>;
+ *------------------------------------------
+ */
+int buildin_sc_start4(struct script_state *st)
+{
+ struct block_list *bl;
+ int type,tick,val1,val2,val3,val4;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ tick=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ val1=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ val2=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ val3=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ val4=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ if( st->end>st->start+8 )
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+8])));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target) {
+ bl = map_id2bl(potion_target);
+ tick/=2;
+ }
+ if (bl)
+ status_change_start(bl,type,val1,val2,val3,val4,tick,0);
+ return 0;
+}
+
+/*==========================================
+ * ó‘ÔˆÙ킪’¼‚é
+ *------------------------------------------
+ */
+int buildin_sc_end(struct script_state *st)
+{
+ struct block_list *bl;
+ int type;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag==1 && potion_target)
+ bl = map_id2bl(potion_target);
+
+ if (bl)
+ status_change_end(bl,type,-1);
+ return 0;
+}
+/*==========================================
+ * ó‘ÔˆÙí‘Ï«‚ðŒvŽZ‚µ‚½Šm—¦‚ð•Ô‚·
+ *------------------------------------------
+ */
+int buildin_getscrate(struct script_state *st)
+{
+ struct block_list *bl;
+ int sc_def,type,rate;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ rate=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 ) //Žw’肵‚½ƒLƒƒƒ‰‚Ì‘Ï«‚ðŒvŽZ‚·‚é
+ bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6])));
+ else
+ bl = map_id2bl(st->rid);
+
+ sc_def = status_get_sc_def(bl,type);
+
+ rate = rate * sc_def / 100;
+ push_val(st->stack,C_INT,rate);
+
+ return 0;
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_debugmes(struct script_state *st)
+{
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+/*==========================================
+ *•ßŠlƒAƒCƒeƒ€Žg—p
+ *------------------------------------------
+ */
+int buildin_catchpet(struct script_state *st)
+{
+ int pet_id;
+ struct map_session_data *sd;
+ pet_id= conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ pet_catch_process1(sd,pet_id);
+ return 0;
+}
+
+/*==========================================
+ *Œg‘Ñ—‘›z‰»‹@Žg—p
+ *------------------------------------------
+ */
+int buildin_birthpet(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ clif_sendegg(sd);
+ return 0;
+}
+
+/*==========================================
+ * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes)
+ *------------------------------------------
+ */
+int buildin_resetlvl(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ sd=script_rid2sd(st);
+ pc_resetlvl(sd,type);
+ return 0;
+}
+/*==========================================
+ * ƒXƒe[ƒ^ƒXƒŠƒZƒbƒg
+ *------------------------------------------
+ */
+int buildin_resetstatus(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ pc_resetstate(sd);
+ return 0;
+}
+
+/*==========================================
+ * script command resetskill
+ *------------------------------------------
+ */
+int buildin_resetskill(struct script_state *st)
+{
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+ pc_resetskill(sd);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int buildin_changebase(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ int vclass;
+
+ if( st->end>st->start+3 )
+ sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+3])));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd == NULL)
+ return 0;
+
+ vclass = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(vclass == JOB_WEDDING)
+ {
+ if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites
+ sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex]
+ )
+ return 0;
+ }
+
+// if(vclass==22) {
+// pc_unequipitem(sd,sd->equip_index[9],0); // ‘•”õŠO
+// }
+
+ sd->view_class = vclass;
+
+ return 0;
+}
+
+/*==========================================
+ * «•Ê•ÏŠ·
+ *------------------------------------------
+ */
+int buildin_changesex(struct script_state *st) {
+ struct map_session_data *sd = NULL;
+ sd = script_rid2sd(st);
+
+ if (sd->status.sex == 0) {
+ sd->status.sex = 1;
+ sd->sex = 1;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER)
+ sd->status.class_ -= 1;
+ } else if (sd->status.sex == 1) {
+ sd->status.sex = 0;
+ sd->sex = 0;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER)
+ sd->status.class_ += 1;
+ }
+ chrif_char_ask_name(-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
+ chrif_save(sd,0);
+ return 0;
+}
+
+/*==========================================
+ * npcƒ`ƒƒƒbƒgì¬
+ *------------------------------------------
+ */
+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){
+ // VAthenaŽd—l(‹ŒAthenaŽd—l‚ƌ݊·«‚ ‚è)
+ ev=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ }else{
+ // eathenaŽd—l
+ trigger=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ ev=conv_str(st,& (st->stack->stack_data[st->start+5]));
+ }
+ }else{
+ // ‹ŒAthenaŽd—l
+ if( st->end > st->start+4 )
+ ev=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ }
+ chat_createnpcchat( (struct npc_data *)map_id2bl(st->oid),
+ limit,pub,trigger,name,(int)strlen(name)+1,ev);
+ return 0;
+}
+/*==========================================
+ * Works like 'announce' but outputs in the common chat window
+ *------------------------------------------
+ */
+int buildin_globalmes(struct script_state *st)
+{
+ struct block_list *bl = map_id2bl(st->oid);
+ struct npc_data *nd = (struct npc_data *)bl;
+ char *name=NULL,*mes;
+
+ mes=conv_str(st,& (st->stack->stack_data[st->start+2])); // ƒƒbƒZ[ƒW‚̎擾
+ if(mes==NULL) return 0;
+
+ if(st->end>st->start+3){ // NPC–¼‚̎擾(123#456)
+ name=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ } else {
+ name=nd->name;
+ }
+
+ npc_globalmessage(name,mes); // ƒOƒ[ƒoƒ‹ƒƒbƒZ[ƒW‘—M
+
+ return 0;
+}
+/*==========================================
+ * npcƒ`ƒƒƒbƒgíœ
+ *------------------------------------------
+ */
+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ƒ`ƒƒƒbƒg‘SˆõR‚èo‚·
+ *------------------------------------------
+ */
+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ƒ`ƒƒƒbƒgƒCƒxƒ“ƒg—LŒø‰»
+ *------------------------------------------
+ */
+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ƒ`ƒƒƒbƒgƒCƒxƒ“ƒg–³Œø‰»
+ *------------------------------------------
+ */
+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ƒ`ƒƒƒbƒgó‘ÔŠ“¾
+ *------------------------------------------
+ */
+int buildin_getwaitingroomstate(struct script_state *st)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ int val=0,type;
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if( st->end > st->start+3 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ switch(type){
+ case 0: val=cd->users; break;
+ case 1: val=cd->limit; break;
+ case 2: val=cd->trigger&0x7f; break;
+ case 3: val=((cd->trigger&0x80)>0); break;
+ case 32: val=(cd->users >= cd->limit); break;
+ case 33: val=(cd->users >= cd->trigger); break;
+
+ case 4:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->title);
+ return 0;
+ case 5:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->pass);
+ return 0;
+ case 16:
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->npc_event);
+ return 0;
+ }
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+/*==========================================
+ * ƒ`ƒƒƒbƒgƒƒ“ƒo[(‹K’èl”)ƒ[ƒv
+ *------------------------------------------
+ */
+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]; // ƒŠƒXƒg擪‚ÌPC‚ðŽŸX‚ÉB
+
+ mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpc")+(i<<24),sd->bl.id);
+
+ if(strcmp(str,"Random")==0)
+ pc_randomwarp(sd,3);
+ else if(strcmp(str,"SavePoint")==0){
+ if(map[sd->bl.m].flag.noteleport) // ƒeƒŒƒ|‹ÖŽ~
+ return 0;
+
+ pc_setpos(sd,sd->status.save_point.map,
+ sd->status.save_point.x,sd->status.save_point.y,3);
+ }else
+ pc_setpos(sd,mapindex_name2id(str),x,y,0);
+ }
+ mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpcnum"),n);
+ return 0;
+}
+/*==========================================
+ * RID‚̃Aƒ^ƒbƒ`
+ *------------------------------------------
+ */
+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‚̃fƒ^ƒbƒ`
+ *------------------------------------------
+ */
+int buildin_detachrid(struct script_state *st)
+{
+ st->rid=0;
+ return 0;
+}
+/*==========================================
+ * ‘¶Ýƒ`ƒFƒbƒN
+ *------------------------------------------
+ */
+int buildin_isloggedin(struct script_state *st)
+{
+ push_val(st->stack,C_INT, map_id2sd(
+ conv_num(st,& (st->stack->stack_data[st->start+2])) )!=NULL );
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+enum { MF_NOMEMO,MF_NOTELEPORT,MF_NOSAVE,MF_NOBRANCH,MF_NOPENALTY,MF_NOZENYPENALTY,
+ MF_PVP,MF_PVP_NOPARTY,MF_PVP_NOGUILD,MF_GVG,MF_GVG_NOPARTY,MF_NOTRADE,MF_NOSKILL,
+ MF_NOWARP,MF_NOPVP,MF_NOICEWALL,MF_SNOW,MF_FOG,MF_SAKURA,MF_LEAVES,MF_RAIN,
+ MF_INDOORS,MF_NOGO,MF_CLOUDS,MF_CLOUDS2,MF_FIREWORKS,MF_GVG_CASTLE,MF_GVG_DUNGEON,MF_NIGHTENABLED,
+ MF_NOBASEEXP, MF_NOJOBEXP, MF_NOMOBLOOT, MF_NOMVPLOOT, MF_NORETURN, MF_NOWARPTO, MF_NIGHTMAREDROP
+ };
+
+int buildin_setmapflagnosave(struct script_state *st)
+{
+ int m,x,y;
+ unsigned short mapindex;
+ char *str,*str2;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ str2=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ m = map_mapname2mapid(str);
+ mapindex = mapindex_name2id(str2);
+
+ if(m >= 0 && mapindex) {
+ map[m].flag.nosave=1;
+ map[m].save.map=mapindex;
+ map[m].save.x=x;
+ map[m].save.y=y;
+ }
+
+ return 0;
+}
+
+int buildin_setmapflag(struct script_state *st)
+{
+ int m,i;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO:
+ map[m].flag.nomemo=1;
+ break;
+ case MF_NOTELEPORT:
+ map[m].flag.noteleport=1;
+ break;
+ case MF_NOBRANCH:
+ map[m].flag.nobranch=1;
+ break;
+ case MF_NOPENALTY:
+ map[m].flag.nopenalty=1;
+ break;
+ case MF_NOZENYPENALTY:
+ map[m].flag.nozenypenalty=1;
+ break;
+ case MF_PVP:
+ map[m].flag.pvp=1;
+ break;
+ case MF_PVP_NOPARTY:
+ map[m].flag.pvp_noparty=1;
+ break;
+ case MF_PVP_NOGUILD:
+ map[m].flag.pvp_noguild=1;
+ break;
+ case MF_GVG:
+ map[m].flag.gvg=1;
+ break;
+ case MF_GVG_NOPARTY:
+ map[m].flag.gvg_noparty=1;
+ break;
+ case MF_GVG_DUNGEON:
+ map[m].flag.gvg_dungeon=1;
+ break;
+ case MF_GVG_CASTLE:
+ map[m].flag.gvg_castle=1;
+ break;
+ case MF_NOTRADE:
+ map[m].flag.notrade=1;
+ break;
+ case MF_NOSKILL:
+ map[m].flag.noskill=1;
+ break;
+ case MF_NOWARP:
+ map[m].flag.nowarp=1;
+ break;
+ case MF_NOPVP:
+ map[m].flag.nopvp=1;
+ break;
+ case MF_NOICEWALL: // [Valaris]
+ map[m].flag.noicewall=1;
+ break;
+ case MF_SNOW: // [Valaris]
+ map[m].flag.snow=1;
+ break;
+ case MF_CLOUDS:
+ map[m].flag.clouds=1;
+ break;
+ case MF_CLOUDS2: // [Valaris]
+ map[m].flag.clouds2=1;
+ break;
+ case MF_FOG: // [Valaris]
+ map[m].flag.fog=1;
+ break;
+ case MF_FIREWORKS:
+ map[m].flag.fireworks=1;
+ break;
+ case MF_SAKURA: // [Valaris]
+ map[m].flag.sakura=1;
+ break;
+ case MF_LEAVES: // [Valaris]
+ map[m].flag.leaves=1;
+ break;
+ case MF_RAIN: // [Valaris]
+ map[m].flag.rain=1;
+ break;
+ case MF_INDOORS: // celest
+ map[m].flag.indoors=1;
+ break;
+ case MF_NIGHTENABLED:
+ map[m].flag.nightenabled=1;
+ break;
+ case MF_NOGO: // celest
+ map[m].flag.nogo=1;
+ break;
+ case MF_NOBASEEXP:
+ map[m].flag.nobaseexp=1;
+ break;
+ case MF_NOJOBEXP:
+ map[m].flag.nojobexp=1;
+ break;
+ case MF_NOMOBLOOT:
+ map[m].flag.nomobloot=1;
+ break;
+ case MF_NOMVPLOOT:
+ map[m].flag.nomvploot=1;
+ break;
+ case MF_NORETURN:
+ map[m].flag.noreturn=1;
+ break;
+ case MF_NOWARPTO:
+ map[m].flag.nowarpto=1;
+ break;
+ case MF_NIGHTMAREDROP:
+ map[m].flag.pvp_nightmaredrop=1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int buildin_removemapflag(struct script_state *st)
+{
+ int m,i;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ i=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO:
+ map[m].flag.nomemo=0;
+ break;
+ case MF_NOTELEPORT:
+ map[m].flag.noteleport=0;
+ break;
+ case MF_NOSAVE:
+ map[m].flag.nosave=0;
+ break;
+ case MF_NOBRANCH:
+ map[m].flag.nobranch=0;
+ break;
+ case MF_NOPENALTY:
+ map[m].flag.nopenalty=0;
+ break;
+ case MF_PVP:
+ map[m].flag.pvp=0;
+ break;
+ case MF_PVP_NOPARTY:
+ map[m].flag.pvp_noparty=0;
+ break;
+ case MF_PVP_NOGUILD:
+ map[m].flag.pvp_noguild=0;
+ break;
+ case MF_GVG:
+ map[m].flag.gvg=0;
+ break;
+ case MF_GVG_NOPARTY:
+ map[m].flag.gvg_noparty=0;
+ break;
+ case MF_GVG_DUNGEON:
+ map[m].flag.gvg_dungeon=0;
+ break;
+ case MF_GVG_CASTLE:
+ map[m].flag.gvg_castle=0;
+ break;
+ case MF_NOZENYPENALTY:
+ map[m].flag.nozenypenalty=0;
+ break;
+ case MF_NOSKILL:
+ map[m].flag.noskill=0;
+ break;
+ case MF_NOWARP:
+ map[m].flag.nowarp=0;
+ break;
+ case MF_NOPVP:
+ map[m].flag.nopvp=0;
+ break;
+ case MF_NOICEWALL: // [Valaris]
+ map[m].flag.noicewall=0;
+ break;
+ case MF_SNOW: // [Valaris]
+ map[m].flag.snow=0;
+ break;
+ case MF_CLOUDS:
+ map[m].flag.clouds=0;
+ break;
+ case MF_CLOUDS2: // [Valaris]
+ map[m].flag.clouds2=0;
+ break;
+ case MF_FOG: // [Valaris]
+ map[m].flag.fog=0;
+ break;
+ case MF_FIREWORKS:
+ map[m].flag.fireworks=0;
+ break;
+ case MF_SAKURA: // [Valaris]
+ map[m].flag.sakura=0;
+ break;
+ case MF_LEAVES: // [Valaris]
+ map[m].flag.leaves=0;
+ break;
+ case MF_RAIN: // [Valaris]
+ map[m].flag.rain=0;
+ break;
+ case MF_INDOORS: // celest
+ map[m].flag.indoors=0;
+ break;
+ case MF_NIGHTENABLED:
+ map[m].flag.nightenabled=0;
+ break;
+ case MF_NOGO: // celest
+ map[m].flag.nogo=0;
+ break;
+ case MF_NOBASEEXP:
+ map[m].flag.nobaseexp=0;
+ break;
+ case MF_NOJOBEXP:
+ map[m].flag.nojobexp=0;
+ break;
+ case MF_NOMOBLOOT:
+ map[m].flag.nomobloot=0;
+ break;
+ case MF_NOMVPLOOT:
+ map[m].flag.nomvploot=0;
+ break;
+ case MF_NORETURN:
+ map[m].flag.noreturn=0;
+ break;
+ case MF_NOWARPTO:
+ map[m].flag.nowarpto=0;
+ break;
+ case MF_NIGHTMAREDROP:
+ map[m].flag.pvp_nightmaredrop=0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int buildin_pvpon(struct script_state *st)
+{
+ int m,i,users;
+ char *str;
+ struct map_session_data *pl_sd=NULL, **pl_allsd;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.pvp && !map[m].flag.nopvp) {
+ map[m].flag.pvp = 1;
+ clif_send0199(m,1);
+
+ if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if ((pl_sd = pl_allsd[i]) && m == pl_sd->bl.m && pl_sd->pvp_timer == -1)
+ {
+ pl_sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,pl_sd->bl.id,0);
+ pl_sd->pvp_rank=0;
+ pl_sd->pvp_lastusers=0;
+ pl_sd->pvp_point=5;
+ pl_sd->pvp_won = 0;
+ pl_sd->pvp_lost = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+int buildin_pvpoff(struct script_state *st)
+{
+ int m,i,users;
+ char *str;
+ struct map_session_data *pl_sd=NULL, **pl_allsd;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.pvp && !map[m].flag.nopvp) { //fixed Lupus
+ map[m].flag.pvp = 0;
+ clif_send0199(m,0);
+
+ if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
+ return 0;
+
+ pl_allsd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if((pl_sd=pl_allsd[i]) && m == pl_sd->bl.m)
+ {
+ clif_pvpset(pl_sd,0,0,2);
+ if(pl_sd->pvp_timer != -1) {
+ delete_timer(pl_sd->pvp_timer,pc_calc_pvprank_timer);
+ pl_sd->pvp_timer = -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int buildin_gvgon(struct script_state *st)
+{
+ int m;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.gvg) {
+ map[m].flag.gvg = 1;
+ clif_send0199(m,3);
+ }
+
+ return 0;
+}
+int buildin_gvgoff(struct script_state *st)
+{
+ int m;
+ char *str;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.gvg) {
+ map[m].flag.gvg = 0;
+ clif_send0199(m,0);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Shows an emoticon on top of the player/npc
+ * emotion emotion#, <target: 0 - NPC, 1 - PC>
+ *------------------------------------------
+ */
+//Optional second parameter added by [Skotlex]
+int buildin_emotion(struct script_state *st)
+{
+ int type;
+ int player=0;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(type < 0 || type > 100)
+ return 0;
+
+ if( st->end>st->start+3 )
+ player=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if (player) {
+ struct map_session_data *sd = script_rid2sd(st);
+ if (sd)
+ clif_emotion(&sd->bl,type);
+ } else
+ clif_emotion(map_id2bl(st->oid),type);
+ return 0;
+}
+
+int buildin_maprespawnguildid_sub(struct block_list *bl,va_list ap)
+{
+ int g_id=va_arg(ap,int);
+ int flag=va_arg(ap,int);
+ struct map_session_data *sd=NULL;
+ struct mob_data *md=NULL;
+
+ if(bl->type == BL_PC)
+ sd=(struct map_session_data*)bl;
+ if(bl->type == BL_MOB)
+ md=(struct mob_data *)bl;
+
+ if(sd){
+ if((sd->status.guild_id == g_id) && (flag&1))
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3);
+ else if((sd->status.guild_id != g_id) && (flag&2))
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3);
+ else if (sd->status.guild_id == 0) // Warp out players not in guild [Valaris]
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); // end addition [Valaris]
+ }
+ if(md && flag&4){
+ if(!md->guardian_data && md->class_ != MOBID_EMPERIUM)
+ mob_delete(md);
+ }
+ return 0;
+}
+int buildin_maprespawnguildid(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int g_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int flag=conv_num(st,& (st->stack->stack_data[st->start+4]));
+
+ int m=map_mapname2mapid(mapname);
+
+ if(m) map_foreachinmap(buildin_maprespawnguildid_sub,m,BL_CHAR,g_id,flag);
+ return 0;
+}
+
+int buildin_agitstart(struct script_state *st)
+{
+ if(agit_flag==1) return 0; // Agit already Start.
+ agit_flag=1;
+ guild_agit_start();
+ return 0;
+}
+
+int buildin_agitend(struct script_state *st)
+{
+ if(agit_flag==0) return 0; // Agit already End.
+ agit_flag=0;
+ guild_agit_end();
+ return 0;
+}
+/*==========================================
+ * agitcheck 1; // choice script
+ * if(@agit_flag == 1) goto agit;
+ * if(agitcheck(0) == 1) goto agit;
+ *------------------------------------------
+ */
+int buildin_agitcheck(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int cond;
+
+ cond=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(cond == 0) {
+ if (agit_flag==1) push_val(st->stack,C_INT,1);
+ if (agit_flag==0) push_val(st->stack,C_INT,0);
+ } else {
+ sd=script_rid2sd(st);
+ if (agit_flag==1) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),1);
+ if (agit_flag==0) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),0);
+ }
+ return 0;
+}
+int buildin_flagemblem(struct script_state *st)
+{
+ int g_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(g_id < 0) return 0;
+
+// printf("Script.c: [FlagEmblem] GuildID=%d, Emblem=%d.\n", g->guild_id, g->emblem_id);
+ ((struct npc_data *)map_id2bl(st->oid))->u.scr.guild_id = g_id;
+ return 0;
+}
+
+int buildin_getcastlename(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ struct guild_castle *gc;
+ int i;
+ char *buf=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, gc->castle_name, NAME_LENGTH-1);
+ break;
+ }
+ }
+ }
+ if(buf)
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ else
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "");
+ return 0;
+}
+
+int buildin_getcastledata(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int index=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ char *event=NULL;
+ struct guild_castle *gc;
+ int i,j;
+
+ if( st->end>st->start+4 && index==0){
+ for(i=0,j=-1;i<MAX_GUILDCASTLE;i++)
+ if( (gc=guild_castle_search(i)) != NULL &&
+ strcmp(mapname,gc->map_name)==0 )
+ j=i;
+ if(j>=0){
+ event=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ guild_addcastleinfoevent(j,17,event);
+ }
+ }
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ switch(index){
+ case 0: for(j=1;j<26;j++) guild_castledataload(gc->castle_id,j); break; // Initialize[AgitInit]
+ case 1: push_val(st->stack,C_INT,gc->guild_id); break;
+ case 2: push_val(st->stack,C_INT,gc->economy); break;
+ case 3: push_val(st->stack,C_INT,gc->defense); break;
+ case 4: push_val(st->stack,C_INT,gc->triggerE); break;
+ case 5: push_val(st->stack,C_INT,gc->triggerD); break;
+ case 6: push_val(st->stack,C_INT,gc->nextTime); break;
+ case 7: push_val(st->stack,C_INT,gc->payTime); break;
+ case 8: push_val(st->stack,C_INT,gc->createTime); break;
+ case 9: push_val(st->stack,C_INT,gc->visibleC); break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ push_val(st->stack,C_INT,gc->guardian[index-10].visible); break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ push_val(st->stack,C_INT,gc->guardian[index-18].hp); break;
+ default:
+ push_val(st->stack,C_INT,0); break;
+ }
+ return 0;
+ }
+ }
+ }
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+int buildin_setcastledata(struct script_state *st)
+{
+ char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int index=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int value=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ struct guild_castle *gc;
+ int i;
+
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(strcmp(mapname,gc->map_name)==0){
+ // Save Data byself First
+ switch(index){
+ case 1: gc->guild_id = value; break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default: return 0;
+ }
+ guild_castledatasave(gc->castle_id,index,value);
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/* =====================================================================
+ * ƒMƒ‹ƒhî•ñ‚ð—v‹‚·‚é
+ * ---------------------------------------------------------------------
+ */
+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;
+}
+
+/* =====================================================================
+ * ƒJ[ƒh‚Ì”‚𓾂é
+ * ---------------------------------------------------------------------
+ */
+int buildin_getequipcardcnt(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0] == 0x00ff){ // »‘¢•Ší‚̓J[ƒh‚È‚µ
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+ push_val(st->stack,C_INT,(c));
+ return 0;
+ }
+ }while(c--);
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/* ================================================================
+ * ƒJ[ƒhŽæ‚èŠO‚µ¬Œ÷
+ * ----------------------------------------------------------------
+ */
+int buildin_successremovecards(struct script_state *st)
+{
+ int i,j,num,cardflag=0,flag;
+ struct map_session_data *sd;
+ struct item item_tmp;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0]==0x00ff){ // »‘¢•Ší‚͈—‚µ‚È‚¢
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+
+ cardflag = 1;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1];
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0;
+ item_tmp.attribute=0;
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){ // Ž‚Ä‚È‚¢‚È‚çƒhƒƒbƒv
+ 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){ // ƒJ[ƒh‚ðŽæ‚蜂¢‚½ƒAƒCƒeƒ€Š“¾
+ flag=0;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid;
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine;
+ item_tmp.attribute=sd->status.inventory[i].attribute;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+ pc_delitem(sd,i,1,0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){ // ‚à‚Ä‚È‚¢‚È‚çƒhƒƒbƒv
+ 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;
+}
+
+/* ================================================================
+ * ƒJ[ƒhŽæ‚èŠO‚µŽ¸”s slot,type
+ * type=0: —¼•û‘¹Ž¸A1:ƒJ[ƒh‘¹Ž¸A2:•‹ï‘¹Ž¸A3:‘¹Ž¸–³‚µ
+ * ----------------------------------------------------------------
+ */
+int buildin_failedremovecards(struct script_state *st)
+{
+ int i,j,num,cardflag=0,flag,typefail;
+ struct map_session_data *sd;
+ struct item item_tmp;
+ int c=MAX_SLOTS;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ typefail=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(sd->status.inventory[i].card[0]==0x00ff){ // »‘¢•Ší‚͈—‚µ‚È‚¢
+ return 0;
+ }
+ do{
+ if( (sd->status.inventory[i].card[c-1] > 4000 &&
+ sd->status.inventory[i].card[c-1] < 5000) ||
+ itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest]
+
+ cardflag = 1;
+
+ if(typefail == 2){ // •‹ï‚Ì‚Ý‘¹Ž¸‚È‚çAƒJ[ƒh‚͎󂯎æ‚点‚é
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1];
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0;
+ item_tmp.attribute=0;
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ }while(c--);
+
+ if(cardflag == 1){
+
+ if(typefail == 0 || typefail == 2){ // •‹ï‘¹Ž¸
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd,i,1,0);
+ clif_misceffect(&sd->bl,2);
+ return 0;
+ }
+ if(typefail == 1){ // ƒJ[ƒh‚Ì‚Ý‘¹Ž¸i•‹ï‚ð•Ô‚·j
+ flag=0;
+ item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid;
+ item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine;
+ item_tmp.attribute=sd->status.inventory[i].attribute;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=0;
+ pc_delitem(sd,i,1,0);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp);
+ }
+ //Logs
+
+ if((flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ clif_misceffect(&sd->bl,2);
+ return 0;
+ }
+ return 0;
+}
+
+int buildin_mapwarp(struct script_state *st) // Added by RoVeRT
+{
+ int x,y,m;
+ char *str;
+ char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x0=0;
+ y0=0;
+ x1=map[map_mapname2mapid(mapname)].xs;
+ y1=map[map_mapname2mapid(mapname)].ys;
+ str=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub,
+ m,x0,y0,x1,y1,BL_PC, str,x,y );
+ return 0;
+}
+
+int buildin_cmdothernpc(struct script_state *st) // Added by RoVeRT
+{
+ char *npc,*command;
+
+ npc=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ command=conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ npc_command(map_id2sd(st->rid),npc,command);
+ return 0;
+}
+
+int buildin_inittimer(struct script_state *st) // Added by RoVeRT
+{
+// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid);
+// nd->lastaction=nd->timer=gettick();
+
+ npc_do_ontimer(st->oid, 1);
+
+ return 0;
+}
+
+int buildin_stoptimer(struct script_state *st) // Added by RoVeRT
+{
+// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid);
+// nd->lastaction=nd->timer=-1;
+
+ npc_do_ontimer(st->oid, 0);
+
+ return 0;
+}
+
+int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT
+{
+ char *event=va_arg(ap,char *);
+ int *c=va_arg(ap,int *);
+
+ if(strcmp(event,((struct mob_data *)bl)->npc_event)==0)
+ (*c)++;
+ return 0;
+}
+
+int buildin_mobcount(struct script_state *st) // Added by RoVeRT
+{
+ char *mapname,*event;
+ int m,c=0;
+ mapname=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ if( (m=map_mapname2mapid(mapname))<0 ) {
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event,&c );
+
+ push_val(st->stack,C_INT, (c));
+
+ return 0;
+}
+int buildin_marriage(struct script_state *st)
+{
+ char *partner=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=map_nick2sd(partner);
+
+ if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+int buildin_wedding_effect(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ struct block_list *bl;
+
+ if(sd==NULL) {
+ bl=map_id2bl(st->oid);
+ } else
+ bl=&sd->bl;
+ clif_wedding_effect(bl);
+ return 0;
+}
+int buildin_divorce(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd==NULL || pc_divorce(sd) < 0){
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+int buildin_ispartneron(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+
+int buildin_getpartnerid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.partner_id);
+ return 0;
+}
+
+int buildin_getchildid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.child);
+ return 0;
+}
+
+int buildin_getmotherid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.mother);
+ return 0;
+}
+
+int buildin_getfatherid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ push_val(st->stack,C_INT,sd->status.father);
+ return 0;
+}
+
+int buildin_warppartner(struct script_state *st)
+{
+ int x,y;
+ unsigned short mapindex;
+ char *str;
+ struct map_session_data *sd=script_rid2sd(st);
+ struct map_session_data *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+4]));
+
+ mapindex = mapindex_name2id(str);
+ if (mapindex) {
+ pc_setpos(p_sd,mapindex,x,y,0);
+ push_val(st->stack,C_INT,1);
+ } else
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying MOB Information [Valaris]
+ *------------------------------------------------
+ */
+int buildin_strmobinfo(struct script_state *st)
+{
+
+ int num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int class_=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if((class_>=0 && class_<=1000) || class_ >2000)
+ return 0;
+
+ switch (num) {
+ case 1:
+ {
+ char *buf;
+ buf=(char *) aCallocA(NAME_LENGTH, sizeof(char));
+// buf=mob_db(class_)->name;
+// for string assignments you would need to go for c++ [Shinomori]
+ memcpy(buf, mob_db(class_)->name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ break;
+ }
+ case 2:
+ {
+ char *buf;
+ buf=(char *) aCallocA(NAME_LENGTH, sizeof(char));
+// buf=mob_db(class_).jname;
+// for string assignments you would need to go for c++ [Shinomori]
+ memcpy(buf,mob_db(class_)->jname, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ break;
+ }
+ case 3:
+ push_val(st->stack,C_INT,mob_db(class_)->lv);
+ break;
+ case 4:
+ push_val(st->stack,C_INT,mob_db(class_)->max_hp);
+ break;
+ case 5:
+ push_val(st->stack,C_INT,mob_db(class_)->max_sp);
+ break;
+ case 6:
+ push_val(st->stack,C_INT,mob_db(class_)->base_exp);
+ break;
+ case 7:
+ push_val(st->stack,C_INT,mob_db(class_)->job_exp);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Summon guardians [Valaris]
+ *------------------------------------------
+ */
+int buildin_guardian(struct script_state *st)
+{
+ int class_=0,amount=1,x=0,y=0,guardian=0;
+ char *str,*map,*event="";
+
+ map =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x =conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y =conv_num(st,& (st->stack->stack_data[st->start+4]));
+ str =conv_str(st,& (st->stack->stack_data[st->start+5]));
+ class_=conv_num(st,& (st->stack->stack_data[st->start+6]));
+ amount=conv_num(st,& (st->stack->stack_data[st->start+7]));
+ event=conv_str(st,& (st->stack->stack_data[st->start+8]));
+ if( st->end>st->start+9 )
+ guardian=conv_num(st,& (st->stack->stack_data[st->start+9]));
+
+ mob_spawn_guardian(map_id2sd(st->rid),map,x,y,str,class_,amount,event,guardian);
+
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying Guardian Info [Valaris]
+ *------------------------------------------------
+ */
+int buildin_guardianinfo(struct script_state *st)
+{
+ int guardian=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct map_session_data *sd=script_rid2sd(st);
+ struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name);
+
+ if (guardian < 0 || guardian >= MAX_GUARDIANS || gc==NULL)
+ {
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ if(gc->guardian[guardian].visible)
+ push_val(st->stack,C_INT,gc->guardian[guardian].hp);
+ else push_val(st->stack,C_INT,-1);
+
+ return 0;
+}
+/*==========================================
+ * ID‚©‚çItem–¼
+ *------------------------------------------
+ */
+int buildin_getitemname(struct script_state *st)
+{
+ int item_id=0;
+ struct item_data *i_data;
+ char *item_name;
+ struct script_data *data;
+
+ data=&(st->stack->stack_data[st->start+2]);
+ get_val(st,data);
+
+ if( data->type==C_STR || data->type==C_CONSTSTR ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ item_id=item_data->nameid;
+ }else
+ item_id=conv_num(st,data);
+
+ i_data = itemdb_exists(item_id);
+ if (i_data == NULL)
+ {
+ push_str(st->stack,C_CONSTSTR,(unsigned char *) "null");
+ return 0;
+ }
+ item_name=(char *)aCallocA(ITEM_NAME_LENGTH,sizeof(char));
+
+ memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) item_name);
+ return 0;
+}
+/*==========================================
+ * Returns number of slots an item has. [Skotlex]
+ *------------------------------------------
+ */
+int buildin_getitemslots(struct script_state *st)
+{
+ int item_id;
+ struct item_data *i_data;
+
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ i_data = itemdb_exists(item_id);
+
+ if (i_data)
+ push_val(st->stack,C_INT,i_data->slot);
+ else
+ push_val(st->stack,C_INT,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ getiteminfo(itemID,n), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = 10000, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ *------------------------------------------
+ */
+int buildin_getiteminfo(struct script_state *st)
+{
+ int item_id,n;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ n = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<14) {
+ item_arr = (int*)&i_data->value_buy;
+ push_val(st->stack,C_INT,item_arr[n]);
+ } else
+ push_val(st->stack,C_INT,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns value from equipped item slot n [Lupus]
+ getequipcardid(num,slot)
+ where
+ num = eqip position slot
+ slot = 0,1,2,3 (Card Slot N)
+
+ This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced)
+ it's useful when you want to check item cards or if it's signed
+ Useful for such quests as "Sign this refined item with players name" etc
+ Hat[0] +4 -> Player's Hat[0] +4
+ *------------------------------------------
+ */
+int buildin_getequipcardid(struct script_state *st)
+{
+ int i,num,slot;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ slot=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && slot>=0 && slot<4)
+ push_val(st->stack,C_INT,sd->status.inventory[i].card[slot]);
+ else
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+
+/*==========================================
+ * petskillbonus [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+
+int buildin_petskillbonus(struct script_state *st)
+{
+ struct pet_data *pd;
+
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->bonus)
+ { //Clear previous bonus
+ if (pd->bonus->timer != -1)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ } else //init
+ pd->bonus = (struct pet_bonus *) aCalloc(1, sizeof(struct pet_bonus));
+
+ pd->bonus->type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->bonus->val=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->bonus->duration=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->bonus->delay=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ if (pd->state.skillbonus == -1)
+ pd->state.skillbonus=0; // waiting state
+
+ // wait for timer to start
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->bonus->timer=-1;
+ else
+ pd->bonus->timer=add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet looting [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petloot(struct script_state *st)
+{
+ int max;
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ max=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(max < 1)
+ max = 1; //Let'em loot at least 1 item.
+ else if (max > MAX_PETLOOT_SIZE)
+ max = MAX_PETLOOT_SIZE;
+
+ pd = sd->pd;
+ if (pd->loot != NULL)
+ { //Release whatever was there already and reallocate memory
+ pet_lootitem_drop(pd, pd->msd);
+ aFree(pd->loot->item);
+ }
+ else
+ pd->loot = (struct pet_loot *)aCalloc(1, sizeof(struct pet_loot));
+
+ pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item));
+ memset(pd->loot->item,0,max * sizeof(struct item));
+
+ pd->loot->max=max;
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+ pd->loot->timer = gettick();
+
+ return 0;
+}
+/*==========================================
+ * PC‚ÌŠŽ•iî•ñ“Ç‚ÝŽæ‚è
+ *------------------------------------------
+ */
+int buildin_getinventorylist(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ unsigned char card_var[NAME_LENGTH];
+
+ int i,j=0,k;
+ if(!sd) return 0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_id")+(j<<24),sd->status.inventory[i].nameid);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_amount")+(j<<24),sd->status.inventory[i].amount);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_equip")+(j<<24),sd->status.inventory[i].equip);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_refine")+(j<<24),sd->status.inventory[i].refine);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_identify")+(j<<24),sd->status.inventory[i].identify);
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_attribute")+(j<<24),sd->status.inventory[i].attribute);
+ for (k = 0; k < MAX_SLOTS; k++)
+ {
+ sprintf(card_var, "@inventorylist_card%d",k+1);
+ pc_setreg(sd,add_str(card_var)+(j<<24),sd->status.inventory[i].card[k]);
+ }
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@inventorylist_count"),j);
+ return 0;
+}
+
+int buildin_getskilllist(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i,j=0;
+ if(!sd) return 0;
+ for(i=0;i<MAX_SKILL;i++){
+ if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){
+ pc_setreg(sd,add_str((unsigned char *) "@skilllist_id")+(j<<24),sd->status.skill[i].id);
+ pc_setreg(sd,add_str((unsigned char *)"@skilllist_lv")+(j<<24),sd->status.skill[i].lv);
+ pc_setreg(sd,add_str((unsigned char *)"@skilllist_flag")+(j<<24),sd->status.skill[i].flag);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str((unsigned char *) "@skilllist_count"),j);
+ return 0;
+}
+
+int buildin_clearitem(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i;
+ if(sd==NULL) return 0;
+ for (i=0; i<MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ }
+ //Logs
+
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ Disguise Player (returns Mob/NPC ID if success, 0 on fail) [Lupus]
+ *------------------------------------------
+ */
+int buildin_disguise(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int id;
+
+ id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if (!mobdb_checkid(id) && !npcdb_checkid(id)) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = id;
+ sd->state.disguised = 1; // set to override items with disguise script [Valaris]
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+
+ push_val(st->stack,C_INT,id);
+ return 0;
+}
+
+/*==========================================
+ Undisguise Player (returns 1 if success, 0 on fail) [Lupus]
+ *------------------------------------------
+ */
+int buildin_undisguise(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if (sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise = 0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ push_val(st->stack,C_INT,0);
+ } else {
+ push_val(st->stack,C_INT,1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * NPCƒNƒ‰ƒXƒ`ƒFƒ“ƒW
+ * class‚Í•Ï‚í‚肽‚¢class
+ * type‚Í’Êí0‚È‚Ì‚©‚ÈH
+ *------------------------------------------
+ */
+int buildin_classchange(struct script_state *st)
+{
+ int _class,type;
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL) return 0;
+
+ _class=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ clif_class_change(bl,_class,type);
+ return 0;
+}
+
+/*==========================================
+ * NPC‚©‚ç”­¶‚·‚éƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+int buildin_misceffect(struct script_state *st)
+{
+ int type;
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(st->oid)
+ clif_misceffect2(map_id2bl(st->oid),type);
+ else{
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd)
+ clif_misceffect2(&sd->bl,type);
+ }
+ return 0;
+}
+/*==========================================
+ * ƒTƒEƒ“ƒhƒGƒtƒFƒNƒg
+ *------------------------------------------
+ */
+int buildin_soundeffect(struct script_state *st)
+{
+
+ // Redundn
+ struct map_session_data *sd=script_rid2sd(st);
+ char *name;
+ int type=0;
+
+
+ name=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(sd){
+ if(st->oid)
+ clif_soundeffect(sd,map_id2bl(st->oid),name,type);
+ else{
+ clif_soundeffect(sd,&sd->bl,name,type);
+ }
+ }
+ return 0;
+}
+
+int buildin_soundeffectall(struct script_state *st)
+{
+ // [Lance] - Improved.
+ struct map_session_data *sd=NULL;
+ char *name;
+ int type=0;
+
+ name=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ type=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ //if(sd)
+ //{
+ if(st->oid)
+ clif_soundeffectall(map_id2bl(st->oid),name,type);
+ else
+ if((sd=script_rid2sd(st)))
+ clif_soundeffectall(&sd->bl,name,type);
+ //}
+ return 0;
+}
+/*==========================================
+ * pet status recovery [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petrecovery(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+
+ if (pd->recovery)
+ { //Halt previous bonus
+ if (pd->recovery->timer != -1)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ } else //Init
+ pd->recovery = (struct pet_recovery *)aCalloc(1, sizeof(struct pet_recovery));
+
+ pd->recovery->type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->recovery->delay=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ pd->recovery->timer=-1;
+
+ return 0;
+}
+
+/*==========================================
+ * pet healing [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petheal(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != -1)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aCalloc(1, sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport
+ //Use the lv as the amount to heal
+ pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->s_skill->timer=-1;
+ else
+ pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petskillattack(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aCalloc(1, sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->a_skill->div_ = 0;
+ pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris]
+ *------------------------------------------
+ */
+int buildin_petskillattack2(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aCalloc(1, sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->a_skill->div_ = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------
+ */
+int buildin_petskillsupport(struct script_state *st)
+{
+ struct pet_data *pd;
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != -1)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aCalloc(1, sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+5]));
+ pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+6]));
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->equip == 0)
+ pd->s_skill->timer=-1;
+ else
+ pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Scripted skill effects [Celest]
+ *------------------------------------------
+ */
+int buildin_skilleffect(struct script_state *st)
+{
+ struct map_session_data *sd;
+
+ int skillid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,skillid,skilllv,1);
+
+ return 0;
+}
+
+/*==========================================
+ * NPC skill effects [Valaris]
+ *------------------------------------------
+ */
+int buildin_npcskilleffect(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ int skillid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ int x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ int y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ clif_skill_poseffect(&nd->bl,skillid,skilllv,x,y,gettick());
+
+ return 0;
+}
+
+/*==========================================
+ * Special effects [Valaris]
+ *------------------------------------------
+ */
+int buildin_specialeffect(struct script_state *st)
+{
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL)
+ return 0;
+
+ clif_specialeffect(bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0);
+
+ return 0;
+}
+
+int buildin_specialeffect2(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+
+ if(sd==NULL)
+ return 0;
+
+ clif_specialeffect(&sd->bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0);
+
+ return 0;
+}
+
+/*==========================================
+ * Nude [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_nude(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int i,calcflag=0;
+
+ if(sd==NULL)
+ return 0;
+
+ for(i=0;i<11;i++)
+ if(sd->equip_index[i] >= 0) {
+ if(!calcflag)
+ calcflag=1;
+ pc_unequipitem(sd,sd->equip_index[i],2);
+ }
+
+ if(calcflag)
+ status_calc_pc(sd,1);
+
+ return 0;
+}
+
+/*==========================================
+ * gmcommand [MouseJstr]
+ *
+ * suggested on the forums...
+ * splitted into atcommand & charcommand by [Skotlex]
+ *------------------------------------------
+ */
+
+int buildin_atcommand(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *cmd;
+
+ sd = script_rid2sd(st);
+ if (!sd)
+ return 0;
+ cmd = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ is_atcommand(sd->fd, sd, cmd, 99);
+
+ return 0;
+}
+
+int buildin_charcommand(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *cmd;
+
+ sd = script_rid2sd(st);
+ if (!sd)
+ return 0;
+ cmd = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ is_charcommand(sd->fd, sd, cmd, 99);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Displays a message for the player only (like system messages like "you got an apple" )
+ *------------------------------------------
+ */
+int buildin_dispbottom(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ char *message;
+ message=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ if(sd)
+ clif_disp_onlyself(sd,message,(int)strlen(message));
+ return 0;
+}
+
+/*==========================================
+ * All The Players Full Recovery
+ (HP/SP full restore and resurrect if need)
+ *------------------------------------------
+ */
+int buildin_recovery(struct script_state *st)
+{
+ struct map_session_data *sd, **all_sd;
+ int i = 0, users;
+
+ all_sd = map_getallusers(&users);
+
+ for (i = 0; i < users; i++)
+ {
+ sd = all_sd[i];
+ sd->status.hp = sd->status.max_hp;
+ sd->status.sp = sd->status.max_sp;
+ clif_updatestatus(sd, SP_HP);
+ clif_updatestatus(sd, SP_SP);
+ if(pc_isdead(sd)){
+ pc_setstand(sd);
+ clif_resurrection(&sd->bl, 1);
+ }
+ clif_displaymessage(sd->fd,"You have been recovered!");
+ }
+ return 0;
+}
+/*==========================================
+ * Get your pet info: getpetinfo(n)
+ * n -> 0:pet_id 1:pet_class 2:pet_name
+ 3:friendly 4:hungry
+ *------------------------------------------
+ */
+int buildin_getpetinfo(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(sd && sd->status.pet_id){
+ switch(type){
+ case 0:
+ push_val(st->stack,C_INT,sd->status.pet_id);
+ break;
+ case 1:
+ push_val(st->stack,C_INT,sd->pet.class_);
+ break;
+ case 2:
+ if(sd->pet.name)
+ { //Shamelessly copied from strcharinfo() [Skotlex]
+ char *buf;
+ buf=(char *)aCallocA(NAME_LENGTH,sizeof(char));
+ memcpy(buf, sd->pet.name, NAME_LENGTH-1);
+ push_str(st->stack,C_STR,(unsigned char *) buf);
+ }
+ else
+ push_str(st->stack,C_CONSTSTR, (unsigned char *) "null");
+ break;
+ case 3:
+ //if(sd->pet.intimate)
+ push_val(st->stack,C_INT,sd->pet.intimate);
+ break;
+ case 4:
+ //if(sd->pet.hungry)
+ push_val(st->stack,C_INT,sd->pet.hungry);
+ break;
+ default:
+ push_val(st->stack,C_INT,0);
+ break;
+ }
+ }else{
+ push_val(st->stack,C_INT,0);
+ }
+ return 0;
+}
+/*==========================================
+ * Shows wether your inventory(and equips) contain
+ selected card or not.
+ checkequipedcard(4001);
+ *------------------------------------------
+ */
+int buildin_checkequipedcard(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+ int n,i,c=0;
+ c=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(sd){
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount){
+ for(n=0;n<MAX_SLOTS;n++){
+ if(sd->status.inventory[i].card[n]==c){
+ push_val(st->stack,C_INT,1);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+int buildin_jump_zero(struct script_state *st) {
+ int sel;
+ sel=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ if(!sel) {
+ int pos;
+ if( st->stack->stack_data[st->start+3].type!=C_POS ){
+ ShowError("script: jump_zero: not label !\n");
+ st->state=END;
+ return 0;
+ }
+
+ pos=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ st->pos=pos;
+ st->state=GOTO;
+ // printf("script: jump_zero: jumpto : %d\n",pos);
+ } else {
+ // printf("script: jump_zero: fail\n");
+ }
+ return 0;
+}
+
+int buildin_select(struct script_state *st)
+{
+ char *buf;
+ int len,i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ if(sd->state.menu_or_input==0){
+ st->state=RERUNLINE;
+ sd->state.menu_or_input=1;
+ for(i=st->start+2,len=16;i<st->end;i++){
+ conv_str(st,& (st->stack->stack_data[i]));
+ len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
+ }
+ buf=(char *)aCalloc(len+1,sizeof(char));
+ buf[0]=0;
+ for(i=st->start+2,len=0;i<st->end;i++){
+ strcat(buf,st->stack->stack_data[i].u.str);
+ strcat(buf,":");
+ }
+ clif_scriptmenu(script_rid2sd(st),st->oid,buf);
+ aFree(buf);
+ } else if(sd->npc_menu==0xff){ // cansel
+ sd->state.menu_or_input=0;
+ st->state=END;
+ } else {
+// pc_setreg(sd,add_str((unsigned char *) "l15"),sd->npc_menu);
+ pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu);
+ sd->state.menu_or_input=0;
+ push_val(st->stack,C_INT,sd->npc_menu);
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetMapMobs
+ returns mob counts on a set map:
+ e.g. GetMapMobs("prontera.gat")
+ use "this" - for player's map
+ *------------------------------------------
+ */
+int buildin_getmapmobs(struct script_state *st)
+{
+ char *str=NULL;
+ int m=-1,bx,by,i;
+ int count=0,c;
+ struct block_list *bl;
+
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if(strcmp(str,"this")==0){
+ struct map_session_data *sd=script_rid2sd(st);
+ if(sd)
+ m=sd->bl.m;
+ else{
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ }else
+ m=map_mapname2mapid(str);
+
+ if(m < 0){
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ for(by=0;by<=(map[m].ys-1)/BLOCK_SIZE;by++){
+ for(bx=0;bx<=(map[m].xs-1)/BLOCK_SIZE;bx++){
+ bl = map[m].block_mob[bx+by*map[m].bxs];
+ c = map[m].block_mob_count[bx+by*map[m].bxs];
+ for(i=0;i<c && bl;i++,bl=bl->next){
+ if(bl->x>=0 && bl->x<=map[m].xs-1 && bl->y>=0 && bl->y<=map[m].ys-1)
+ count++;
+ }
+ }
+ }
+ push_val(st->stack,C_INT,count);
+ return 0;
+}
+
+/*==========================================
+ * movenpc [MouseJstr]
+ *------------------------------------------
+ */
+
+int buildin_movenpc(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *map,*npc;
+ int x,y;
+
+ sd = script_rid2sd(st);
+
+ map = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ x = conv_num(st,& (st->stack->stack_data[st->start+3]));
+ y = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ npc = conv_str(st,& (st->stack->stack_data[st->start+5]));
+
+ return 0;
+}
+
+/*==========================================
+ * message [MouseJstr]
+ *------------------------------------------
+ */
+
+int buildin_message(struct script_state *st)
+{
+ struct map_session_data *sd;
+ char *msg,*player;
+ struct map_session_data *pl_sd = NULL;
+
+ sd = script_rid2sd(st);
+
+ player = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ msg = conv_str(st,& (st->stack->stack_data[st->start+3]));
+
+ if((pl_sd=map_nick2sd((char *) player)) == NULL)
+ return 0;
+ clif_displaymessage(pl_sd->fd, msg);
+
+ return 0;
+}
+
+/*==========================================
+ * npctalk (sends message to surrounding
+ * area) [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_npctalk(struct script_state *st)
+{
+ char *str;
+ char message[255];
+
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ if(nd) {
+ memcpy(message, nd->name, NAME_LENGTH);
+ strcat(message," : ");
+ strncat(message,str, 254); //Prevent overflow possibility. [Skotlex]
+ clif_message(&(nd->bl), message);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * hasitems (checks to see if player has any
+ * items on them, if so will return a 1)
+ * [Valaris]
+ *------------------------------------------
+ */
+
+int buildin_hasitems(struct script_state *st)
+{
+ int i;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].amount && sd->status.inventory[i].nameid!=2364 && sd->status.inventory[i].nameid!=2365) {
+ push_val(st->stack,C_INT,1);
+ return 0;
+ }
+ }
+
+ push_val(st->stack,C_INT,0);
+
+ return 0;
+}
+// change npc walkspeed [Valaris]
+int buildin_npcspeed(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(nd) {
+ nd->speed=x;
+ }
+
+ return 0;
+}
+// make an npc walk to a position [Valaris]
+int buildin_npcwalkto(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0,y=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(nd) {
+ npc_walktoxy(nd,x,y,0);
+ }
+
+ return 0;
+}
+// stop an npc's movement [Valaris]
+int buildin_npcstop(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd) {
+ if(nd->state.state==MS_WALK)
+ npc_stop_walking(nd,1);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------
+ */
+int buildin_getlook(struct script_state *st){
+ int type,val;
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=-1;
+ switch(type){
+ case LOOK_HAIR: //1
+ val=sd->status.hair;
+ break;
+ case LOOK_WEAPON: //2
+ val=sd->status.weapon;
+ break;
+ case LOOK_HEAD_BOTTOM: //3
+ val=sd->status.head_bottom;
+ break;
+ case LOOK_HEAD_TOP: //4
+ val=sd->status.head_top;
+ break;
+ case LOOK_HEAD_MID: //5
+ val=sd->status.head_mid;
+ break;
+ case LOOK_HAIR_COLOR: //6
+ val=sd->status.hair_color;
+ break;
+ case LOOK_CLOTHES_COLOR: //7
+ val=sd->status.clothes_color;
+ break;
+ case LOOK_SHIELD: //8
+ val=sd->status.shield;
+ break;
+ case LOOK_SHOES: //9
+ break;
+ }
+
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------
+*/
+int buildin_getsavepoint(struct script_state *st)
+{
+ int x,y,type;
+ char *mapname;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ x=sd->status.save_point.x;
+ y=sd->status.save_point.y;
+ switch(type){
+ case 0:
+ mapname=(char *) aCallocA(MAP_NAME_LENGTH+1, sizeof(char));
+ memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH);
+ mapname[MAP_NAME_LENGTH]='\0';
+ push_str(st->stack,C_STR,(unsigned char *) mapname);
+ break;
+ case 1:
+ push_val(st->stack,C_INT,x);
+ break;
+ case 2:
+ push_val(st->stack,C_INT,y);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get position for char/npc/pet/mob objects. Added by Lorky
+ *
+ * int getMapXY(MapName$,MaxX,MapY,type,[CharName$]);
+ * where type:
+ * MapName$ - String variable for output map name
+ * MapX - Integer variable for output coord X
+ * MapY - Integer variable for output coord Y
+ * type - type of object
+ * 0 - Character coord
+ * 1 - NPC coord
+ * 2 - Pet coord
+ * 3 - Mob coord (not released)
+ * CharName$ - Name object. If miss or "this" the current object
+ *
+ * Return:
+ * 0 - success
+ * -1 - some error, MapName$,MapX,MapY contains unknown value.
+ *------------------------------------------
+*/
+int buildin_getmapxy(struct script_state *st){
+ struct map_session_data *sd=NULL;
+ struct npc_data *nd;
+ struct pet_data *pd;
+
+ int num;
+ char *name;
+ char prefix;
+
+ int x,y,type;
+ char mapname[MAP_NAME_LENGTH+1];
+ memset(mapname, 0, sizeof(mapname));
+
+ if( st->stack->stack_data[st->start+2].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapname variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+3].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapx variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ if( st->stack->stack_data[st->start+4].type!=C_NAME ){
+ ShowWarning("script: buildin_getmapxy: not mapy variable\n");
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+//??????????? >>> Possible needly check function parameters on C_STR,C_INT,C_INT <<< ???????????//
+ type=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ switch (type){
+ case 0: //Get Character Position
+ if( st->end>st->start+6 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ sd=script_rid2sd(st);
+
+ if ( sd==NULL ) { //wrong char name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+
+ x=sd->bl.x;
+ y=sd->bl.y;
+ memcpy(mapname,mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH);
+ break;
+ case 1: //Get NPC Position
+ if( st->end > st->start+6 )
+ nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if ( nd==NULL ) { //wrong npc name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ x=nd->bl.x;
+ y=nd->bl.y;
+ memcpy(mapname, map[nd->bl.m].name, MAP_NAME_LENGTH);
+ break;
+ case 2: //Get Pet Position
+ if( st->end>st->start+6 )
+ sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6])));
+ else
+ sd=script_rid2sd(st);
+
+ if ( sd==NULL ) { //wrong char name or char offline
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ pd=sd->pd;
+
+ if(pd==NULL){ //pet data not found
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+ x=pd->bl.x;
+ y=pd->bl.y;
+ memcpy(mapname, map[pd->bl.m].name, MAP_NAME_LENGTH);
+ break;
+
+ case 3: //Get Mob Position
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ default: //Wrong type parameter
+ push_val(st->stack,C_INT,-1);
+ return 0;
+ }
+
+ //Set MapName$
+ num=st->stack->stack_data[st->start+2].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+
+ set_reg(sd,num,name,(void*)mapname);
+
+ //Set MapX
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(sd,num,name,(void*)x);
+
+
+ //Set MapY
+ num=st->stack->stack_data[st->start+4].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ prefix=*name;
+
+ if( prefix!='$' )
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+
+ set_reg(sd,num,name,(void*)y);
+
+ //Return Success value
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/*=====================================================
+ * Allows players to use a skill - by Qamera
+ *-----------------------------------------------------
+ */
+int buildin_skilluseid (struct script_state *st)
+{
+ int skid,sklv;
+ struct map_session_data *sd;
+
+ skid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sklv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ sd=script_rid2sd(st);
+ skill_use_id(sd,sd->status.account_id,skid,sklv);
+
+ return 0;
+}
+
+/*=====================================================
+ * Allows players to use a skill on a position [Celest]
+ *-----------------------------------------------------
+ */
+int buildin_skillusepos(struct script_state *st)
+{
+ int skid,sklv,x,y;
+ struct map_session_data *sd;
+
+ skid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sklv=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ x=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+5]));
+
+ sd=script_rid2sd(st);
+ skill_use_pos(sd,x,y,skid,sklv);
+
+ return 0;
+}
+
+/*==========================================
+ * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus]
+ *------------------------------------------
+ */
+int buildin_logmes(struct script_state *st)
+{
+ if (log_config.npc <= 0 ) return 0;
+ conv_str(st,& (st->stack->stack_data[st->start+2]));
+ log_npc(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str);
+ return 0;
+}
+
+int buildin_summon(struct script_state *st)
+{
+ int _class, id;
+ char *str,*event="";
+ struct map_session_data *sd;
+ struct mob_data *md;
+
+ sd=script_rid2sd(st);
+ if (sd) {
+ int tick = gettick();
+ str =conv_str(st,& (st->stack->stack_data[st->start+2]));
+ _class=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ event=conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ id=mob_once_spawn(sd, "this", 0, 0, str,_class,1,event);
+ if((md=(struct mob_data *)map_id2bl(id))){
+ md->master_id=sd->bl.id;
+ md->special_state.ai=1;
+ md->mode=mob_db(md->class_)->mode|0x04;
+ md->deletetimer=add_timer(tick+60000,mob_timer_delete,id,0);
+ clif_misceffect2(&md->bl,344);
+ }
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Checks whether it is daytime/nighttime
+ *------------------------------------------
+ */
+int buildin_isnight(struct script_state *st)
+{
+ push_val(st->stack,C_INT, (night_flag == 1));
+ return 0;
+}
+
+int buildin_isday(struct script_state *st)
+{
+ push_val(st->stack,C_INT, (night_flag == 0));
+ return 0;
+}
+
+/*================================================
+ * Check whether another item/card has been
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------
+ */
+// leave this here, just in case
+#if 0
+int buildin_isequipped(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int ret = -1;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ int flag = 0;
+
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<10; j++) {
+ int index, type;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ flag = 1;
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ flag = 1;
+ break;
+ }
+ }
+ }
+ if (flag) break;
+ }
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+#endif
+
+/*================================================
+ * Check how many items/cards in the list are
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------
+ */
+int buildin_isequippedcnt(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int ret = 0;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<10; j++) {
+ int index, type;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret++; //[Lupus]
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ ret++; //[Lupus]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*================================================
+ * Check whether another card has been
+ * equipped - used for 2/15's cards patch [celest]
+ * -- Items checked cannot be reused in another
+ * card set to prevent exploits
+ *------------------------------------------------
+ */
+int buildin_isequipped(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, j, k, id = 1;
+ int index, type, flag;
+ int ret = -1;
+
+ sd = script_rid2sd(st);
+
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+
+ for (i=0; id!=0; i++)
+ {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ type = itemdb_type(id);
+ flag = 0;
+ for (j=0; j<10; j++)
+ {
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == 9 && sd->equip_index[8] == index) continue;
+ if(j == 5 && sd->equip_index[4] == index) continue;
+ if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ switch (type)
+ {
+ case 4:
+ case 5:
+ if (sd->inventory_data[index]->nameid == id)
+ flag = 1;
+ break;
+ case 6:
+ if (
+ sd->inventory_data[index]->slot == 0 ||
+ sd->status.inventory[index].card[0] == 0x00ff ||
+ sd->status.inventory[index].card[0] == 0x00fe ||
+ sd->status.inventory[index].card[0] == (short)0xff00)
+ continue;
+
+ for (k = 0; k < sd->inventory_data[index]->slot; k++)
+ { //New hash system which should support up to 4 slots on any equipment. [Skotlex]
+ unsigned int hash = 0;
+ if (sd->status.inventory[index].card[k] != id)
+ continue;
+
+ hash = 1<<((j<5?j:j-5)*4 + k);
+ // check if card is already used by another set
+ if ((j<5?sd->setitem_hash:sd->setitem_hash2) & hash)
+ continue;
+
+ // We have found a match
+ flag = 1;
+ // Set hash so this card cannot be used by another
+ if (j<5)
+ sd->setitem_hash |= hash;
+ else
+ sd->setitem_hash2 |= hash;
+ break;
+ }
+ //Case 6 end
+ break;
+ }
+ if (flag) break;
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+
+ push_val(st->stack,C_INT,ret);
+ return 0;
+}
+
+/*================================================
+ * Check how many given inserted cards in the CURRENT
+ * weapon - used for 2/15's cards patch [Lupus]
+ *------------------------------------------------
+ */
+int buildin_cardscnt(struct script_state *st)
+{
+ struct map_session_data *sd;
+ int i, k, id = 1;
+ int ret = 0;
+ int index, type;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus]
+ if(index < 0) continue;
+
+ type = itemdb_type(id);
+
+ if(sd->inventory_data[index]) {
+ if (type == 4 || type == 5) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret++;
+ } else if (type == 6) {
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[0]!=0x00ff &&
+ sd->status.inventory[index].card[0]!=0x00fe &&
+ sd->status.inventory[index].card[0]!=(short)0xff00 &&
+ sd->status.inventory[index].card[k] == id) {
+ ret++;
+ }
+ }
+ }
+ }
+ }
+ push_val(st->stack,C_INT,ret);
+// push_val(st->stack,C_INT,current_equip_item_index);
+ return 0;
+}
+
+/*=======================================================
+ * Returns the refined number of the current item, or an
+ * item with inventory index specified
+ *-------------------------------------------------------
+ */
+int buildin_getrefine(struct script_state *st)
+{
+ struct map_session_data *sd;
+ if ((sd = script_rid2sd(st))!= NULL)
+ push_val(st->stack, C_INT, sd->status.inventory[current_equip_item_index].refine);
+ return 0;
+}
+
+/*=======================================================
+ * Allows 2 Parents to adopt a character as a Baby
+ *-------------------------------------------------------
+ */
+int buildin_adopt(struct script_state *st)
+{
+ int ret;
+
+ char *parent1 = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ char *parent2 = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ char *child = conv_str(st,& (st->stack->stack_data[st->start+4]));
+
+ struct map_session_data *p1_sd = map_nick2sd(parent1);
+ struct map_session_data *p2_sd = map_nick2sd(parent2);
+ struct map_session_data *c_sd = map_nick2sd(child);
+
+ if (!p1_sd || !p2_sd || !c_sd ||
+ p1_sd->status.base_level < 70 ||
+ p2_sd->status.base_level < 70)
+ return 0;
+
+ ret = pc_adoption(p1_sd, p2_sd, c_sd);
+ push_val(st->stack, C_INT, ret);
+
+ return 0;
+}
+
+/*=======================================================
+ * Day/Night controls
+ *-------------------------------------------------------
+ */
+int buildin_night(struct script_state *st)
+{
+ if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1);
+ return 0;
+}
+int buildin_day(struct script_state *st)
+{
+ if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1);
+ return 0;
+}
+
+//=======================================================
+// Unequip [Spectre]
+//-------------------------------------------------------
+int buildin_unequip(struct script_state *st)
+{
+ int i;
+ size_t num;
+ struct map_session_data *sd;
+
+ num = conv_num(st,& (st->stack->stack_data[st->start+2])) - 1;
+ sd=script_rid2sd(st);
+ if(sd!=NULL && num<10)
+ {
+ i=pc_checkequip(sd,equip[num]);
+ pc_unequipitem(sd,i,2);
+ return 0;
+ }
+ return 0;
+}
+
+//=======================================================
+// strlen [Valaris]
+//-------------------------------------------------------
+int buildin_getstrlen(struct script_state *st) {
+
+ char *str = str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int len = (str) ? (int)strlen(str) : 0;
+
+ push_val(st->stack,C_INT,len);
+ return 0;
+}
+
+//=======================================================
+// isalpha [Valaris]
+//-------------------------------------------------------
+int buildin_charisalpha(struct script_state *st) {
+
+ char *str=conv_str(st,& (st->stack->stack_data[st->start+2]));
+ int pos=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ int val = ( str && pos>0 && (unsigned int)pos<strlen(str) ) ? isalpha( str[pos] ) : 0;
+
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+// [Lance]
+int buildin_fakenpcname(struct script_state *st)
+{
+ char *name;
+ char *newname;
+ int look;
+ name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ newname = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ look = conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(look > 32767 || look < -32768) {
+ ShowError("buildin_fakenpcname: Invalid look value %d\n",look);
+ return 1; // Safety measure to prevent runtime errors
+ }
+ npc_changename(name,newname,(short)look);
+ return 0;
+}
+
+int buildin_atoi(struct script_state *st) {
+ char *value;
+ value = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack, C_INT, atoi(value));
+ return 0;
+}
+
+//-----------------------------------------------------------------------//
+// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA START //
+//-----------------------------------------------------------------------//
+int buildin_compare(struct script_state *st) {
+ char *message;
+ char *cmpstring;
+ int j;
+ message = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ cmpstring = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ for (j = 0; message[j]; j++)
+ message[j] = tolower(message[j]);
+ for (j = 0; cmpstring[j]; j++)
+ cmpstring[j] = tolower(cmpstring[j]);
+ push_val(st->stack,C_INT,(strstr(message,cmpstring) != NULL));
+ return 0;
+}
+
+//-----------------------------------------------------------------------//
+// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA END //
+//-----------------------------------------------------------------------//
+// [zBuffer] List of mathematics commands --->
+int buildin_sqrt(struct script_state *st){
+ double i, a;
+ i = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ a = sqrt(i);
+ push_val(st->stack, C_INT, (int)a);
+ return 0;
+}
+
+int buildin_pow(struct script_state *st){
+ double i, a, b;
+ a = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ b = conv_num(st, &(st->stack->stack_data[st->start+3]));
+ i = pow(a,b);
+ push_val(st->stack, C_INT, (int)i);
+ return 0;
+}
+int buildin_distance(struct script_state *st){
+ int x0, y0, x1, y1;
+
+ x0 = conv_num(st, &(st->stack->stack_data[st->start+2]));
+ y0 = conv_num(st, &(st->stack->stack_data[st->start+3]));
+ x1 = conv_num(st, &(st->stack->stack_data[st->start+4]));
+ y1 = conv_num(st, &(st->stack->stack_data[st->start+5]));
+
+ push_val(st->stack, C_INT, distance(x0-x1, y0-y1));
+ return 0;
+}
+
+// <--- [zBuffer] List of mathematics commands
+// [zBuffer] List of dynamic var commands --->
+void setd_sub(struct map_session_data *sd, char *varname, int elem, void *value)
+{
+ set_reg(sd, add_str((unsigned char *) varname)+(elem<<24), varname, value);
+ return;
+}
+
+int buildin_setd(struct script_state *st)
+{
+ struct map_session_data *sd=NULL;
+ char varname[100], *buffer;
+ char *value;
+ int elem;
+ buffer = conv_str(st, & (st->stack->stack_data[st->start+2]));
+ value = conv_str(st, & (st->stack->stack_data[st->start+3]));
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ if(st->rid)
+ sd = script_rid2sd(st);
+
+ if(varname[strlen(varname)-1] != '$') {
+ setd_sub(sd, varname, elem, (void *)atoi(value));
+ } else {
+ setd_sub(sd, varname, elem, (void *)value);
+ }
+
+ return 0;
+}
+
+#ifndef TXT_ONLY
+int buildin_query_sql(struct script_state *st) {
+ char *name, *query;
+ int num, i = 0;
+ struct map_session_data *sd = (st->rid)? script_rid2sd(st) : NULL;
+
+ query = conv_str(st,& (st->stack->stack_data[st->start+2]));
+ strcpy(tmp_sql, query);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+
+ if(st->end > st->start+3) {
+ if(st->stack->stack_data[st->start+3].type != C_NAME){
+ ShowWarning("buildin_query_sql: 2nd parameter is not a variable!\n");
+ } else {
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=(char *)(str_buf+str_data[num&0x00ffffff].str);
+ if((sql_res = mysql_store_result(&mmysql_handle))){
+ if(name[strlen(name)-1] != '$') {
+ while(i<128 && (sql_row = mysql_fetch_row(sql_res))){
+ setd_sub(sd, name, i, (void *)atoi(sql_row[0]));
+ i++;
+ }
+ } else {
+ while(i<128 && (sql_row = mysql_fetch_row(sql_res))){
+ setd_sub(sd, name, i, (void *)sql_row[0]);
+ i++;
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+int buildin_getd (struct script_state *st)
+{
+ char varname[100], *buffer;
+ //struct script_data dat;
+ int elem;
+
+ buffer = conv_str(st, & (st->stack->stack_data[st->start+2]));
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ /*dat.type=C_NAME;
+ dat.u.num=add_str((unsigned char *) varname)+(elem<<24);
+ get_val(st,&dat);
+
+ if(dat.type == C_INT)
+ push_val(st->stack, C_INT, dat.u.num);
+ else if(dat.type == C_CONSTSTR){
+ buffer = aStrdup((char *)dat.u.str);
+ // dat.u.str holds the actual pointer to the data, must be duplicated.
+ // It will be freed later. Tested.
+ push_str(st->stack, C_STR, buffer);
+ }*/
+
+ // Push the 'pointer' so it's more flexible [Lance]
+ push_val(st->stack,C_NAME,
+ (elem<<24) | add_str((unsigned char *) varname));
+
+ return 0;
+}
+
+// <--- [zBuffer] List of dynamic var commands
+// Pet stat [Lance]
+int buildin_petstat(struct script_state *st){
+ struct map_session_data *sd = NULL;
+ char *tmp;
+ int flag = conv_num(st, & (st->stack->stack_data[st->start+2]));
+ sd = script_rid2sd(st);
+ if(!sd || !sd->pet.pet_id){
+ if(flag == 2)
+ push_str(st->stack, C_CONSTSTR, "");
+ else
+ push_val(st->stack, C_INT, 0);
+ }
+ else {
+ switch(flag){
+ case 1:
+ push_val(st->stack, C_INT, (int)sd->pet.class_);
+ break;
+ case 2:
+ tmp = aStrdup(sd->pet.name);
+ push_str(st->stack, C_STR, tmp);
+ break;
+ case 3:
+ push_val(st->stack, C_INT, (int)sd->pet.level);
+ break;
+ case 4:
+ push_val(st->stack, C_INT, (int)sd->pet.hungry);
+ break;
+ case 5:
+ push_val(st->stack, C_INT, (int)sd->pet.intimate);
+ break;
+ default:
+ push_val(st->stack, C_INT, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+int buildin_callshop(struct script_state *st)
+{
+ struct map_session_data *sd = NULL;
+ struct npc_data *nd;
+ char *shopname;
+ int flag = 0;
+ sd = script_rid2sd(st);
+ if (!sd) {
+ push_val(st->stack,C_INT,0);
+ return 0;
+ }
+ shopname = conv_str(st, & (st->stack->stack_data[st->start+2]));
+ if( st->end>st->start+3 )
+ flag = conv_num(st, & (st->stack->stack_data[st->start+3]));
+ nd = npc_name2id(shopname);
+ if (!nd || nd->bl.type!=BL_NPC || nd->bl.subtype!=SHOP) {
+ ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)", shopname);
+ push_val(st->stack,C_INT,0);
+ return 1;
+ }
+
+ switch (flag) {
+ case 1: //Buy window
+ npc_buysellsel(sd,nd->bl.id,0);
+ break;
+ case 2: //Sell window
+ npc_buysellsel(sd,nd->bl.id,1);
+ break;
+ default: //Show menu
+ clif_npcbuysell(sd,nd->bl.id);
+ break;
+ }
+ sd->npc_shopid = nd->bl.id;
+ push_val(st->stack,C_INT,1);
+ return 0;
+}
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ setiteminfo(itemID,"{new item bonus script}");
+ *------------------------------------------
+ */
+int buildin_setitemscript(struct script_state *st)
+{
+ int item_id;
+ char *script;
+ struct item_data *i_data;
+
+ item_id = conv_num(st,& (st->stack->stack_data[st->start+2]));
+ script = conv_str(st,& (st->stack->stack_data[st->start+3]));
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && script!=NULL && script[0]=='{') {
+ if(i_data->script!=NULL)
+ aFree(i_data->script);
+ i_data->script = parse_script((unsigned char *) script, 0);
+ push_val(st->stack,C_INT,1);
+ } else
+ push_val(st->stack,C_INT,0);
+ return 0;
+}
+
+/* Work In Progress [Lupus]
+int buildin_addmonsterdrop(struct script_state *st)
+{
+ int class_,item_id,chance;
+ class_=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ chance=conv_num(st,& (st->stack->stack_data[st->start+4]));
+ if(class_>1000 && item_id>500 && chance>0) {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+}
+
+int buildin_delmonsterdrop(struct script_state *st)
+{
+ int class_,item_id;
+ class_=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ item_id=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if(class_>1000 && item_id>500) {
+ push_val(st->stack,C_INT,1);
+ } else {
+ push_val(st->stack,C_INT,0);
+ }
+}
+*/
+
+//
+// ŽÀs•”main
+//
+/*==========================================
+ * ƒRƒ}ƒ“ƒh‚Ì“Ç‚ÝŽæ‚è
+ *------------------------------------------
+ */
+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);
+}
+
+/*==========================================
+ * ƒRƒ}ƒ“ƒh‚̃vƒbƒVƒ…ƒoƒbƒN
+ *------------------------------------------
+ */
+void unget_com(int c)
+{
+ if(unget_com_data!=-1){
+ if(battle_config.error_log)
+ ShowError("unget_com can back only 1 data\n");
+ }
+ unget_com_data=c;
+}
+
+/*==========================================
+ * ”’l‚ÌŠ“¾
+ *------------------------------------------
+ */
+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);
+}
+
+/*==========================================
+ * ƒXƒ^ƒbƒN‚©‚ç’l‚ðŽæ‚èo‚·
+ *------------------------------------------
+ */
+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)
+
+/*==========================================
+ * ‰ÁŽZ‰‰ŽZŽq
+ *------------------------------------------
+ */
+void op_add(struct script_state* st)
+{
+ st->stack->sp--;
+ get_val(st,&(st->stack->stack_data[st->stack->sp]));
+ get_val(st,&(st->stack->stack_data[st->stack->sp-1]));
+
+ if(isstr(st->stack->stack_data[st->stack->sp]) || isstr(st->stack->stack_data[st->stack->sp-1])){
+ conv_str(st,&(st->stack->stack_data[st->stack->sp]));
+ conv_str(st,&(st->stack->stack_data[st->stack->sp-1]));
+ }
+ if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii
+ st->stack->stack_data[st->stack->sp-1].u.num += st->stack->stack_data[st->stack->sp].u.num;
+ } else { // ss‚Ì—\’è
+ char *buf;
+ buf=(char *)aCallocA(strlen(st->stack->stack_data[st->stack->sp-1].u.str)+
+ strlen(st->stack->stack_data[st->stack->sp].u.str)+1,sizeof(char));
+ strcpy(buf,st->stack->stack_data[st->stack->sp-1].u.str);
+ strcat(buf,st->stack->stack_data[st->stack->sp].u.str);
+ if(st->stack->stack_data[st->stack->sp-1].type==C_STR)
+ {
+ aFree(st->stack->stack_data[st->stack->sp-1].u.str);
+ st->stack->stack_data[st->stack->sp-1].type=C_INT;
+ }
+ if(st->stack->stack_data[st->stack->sp].type==C_STR)
+ {
+ aFree(st->stack->stack_data[st->stack->sp].u.str);
+ st->stack->stack_data[st->stack->sp].type=C_INT;
+ }
+ st->stack->stack_data[st->stack->sp-1].type=C_STR;
+ st->stack->stack_data[st->stack->sp-1].u.str=buf;
+ }
+}
+
+/*==========================================
+ * “ñ€‰‰ŽZŽq(•¶Žš—ñ)
+ *------------------------------------------
+ */
+void op_2str(struct script_state *st,int op,int sp1,int sp2)
+{
+ char *s1=st->stack->stack_data[sp1].u.str,
+ *s2=st->stack->stack_data[sp2].u.str;
+ int a=0;
+
+ switch(op){
+ case C_EQ:
+ a= (strcmp(s1,s2)==0);
+ break;
+ case C_NE:
+ a= (strcmp(s1,s2)!=0);
+ break;
+ case C_GT:
+ a= (strcmp(s1,s2)> 0);
+ break;
+ case C_GE:
+ a= (strcmp(s1,s2)>=0);
+ break;
+ case C_LT:
+ a= (strcmp(s1,s2)< 0);
+ break;
+ case C_LE:
+ a= (strcmp(s1,s2)<=0);
+ break;
+ default:
+ ShowWarning("script: illegal string operator\n");
+ break;
+ }
+
+ push_val(st->stack,C_INT,a);
+
+ if(st->stack->stack_data[sp1].type==C_STR)
+ {
+ aFree(s1);
+ st->stack->stack_data[sp1].type=C_INT;
+ }
+ if(st->stack->stack_data[sp2].type==C_STR)
+ {
+ aFree(s2);
+ st->stack->stack_data[sp2].type=C_INT;
+ }
+}
+/*==========================================
+ * “ñ€‰‰ŽZŽq(”’l)
+ *------------------------------------------
+ */
+void op_2num(struct script_state *st,int op,int i1,int i2)
+{
+ switch(op){
+ case C_SUB:
+ i1-=i2;
+ break;
+ case C_MUL:
+ {
+ #ifndef _MSC_VER
+ long long res = i1 * i2;
+ #else
+ __int64 res = i1 * i2;
+ #endif
+ if (res > 2147483647 )
+ i1 = 2147483647;
+ else
+ i1*=i2;
+ }
+ break;
+ case C_DIV:
+ if (i2 != 0)
+ i1/=i2;
+ else
+ ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_DIV)!\n");
+ break;
+ case C_MOD:
+ if (i2 != 0)
+ i1%=i2;
+ else
+ ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_MOD)!\n");
+ break;
+ case C_AND:
+ i1&=i2;
+ break;
+ case C_OR:
+ i1|=i2;
+ break;
+ case C_XOR:
+ i1^=i2;
+ break;
+ case C_LAND:
+ i1=i1&&i2;
+ break;
+ case C_LOR:
+ i1=i1||i2;
+ break;
+ case C_EQ:
+ i1=i1==i2;
+ break;
+ case C_NE:
+ i1=i1!=i2;
+ break;
+ case C_GT:
+ i1=i1>i2;
+ break;
+ case C_GE:
+ i1=i1>=i2;
+ break;
+ case C_LT:
+ i1=i1<i2;
+ break;
+ case C_LE:
+ i1=i1<=i2;
+ break;
+ case C_R_SHIFT:
+ i1=i1>>i2;
+ break;
+ case C_L_SHIFT:
+ i1=i1<<i2;
+ break;
+ }
+ push_val(st->stack,C_INT,i1);
+}
+/*==========================================
+ * “ñ€‰‰ŽZŽq
+ *------------------------------------------
+ */
+void op_2(struct script_state *st,int op)
+{
+ int i1,i2;
+ char *s1=NULL,*s2=NULL;
+
+ i2=pop_val(st);
+ if( isstr(st->stack->stack_data[st->stack->sp]) )
+ s2=st->stack->stack_data[st->stack->sp].u.str;
+
+ i1=pop_val(st);
+ if( isstr(st->stack->stack_data[st->stack->sp]) )
+ s1=st->stack->stack_data[st->stack->sp].u.str;
+
+ if( s1!=NULL && s2!=NULL ){
+ // ss => op_2str
+ op_2str(st,op,st->stack->sp,st->stack->sp+1);
+ }else if( s1==NULL && s2==NULL ){
+ // ii => op_2num
+ op_2num(st,op,i1,i2);
+ }else{
+ // si,is => error
+ ShowWarning("script: op_2: int&str, str&int not allow.");
+ push_val(st->stack,C_INT,0);
+ }
+}
+
+/*==========================================
+ * ’P€‰‰ŽZŽq
+ *------------------------------------------
+ */
+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);
+}
+
+
+/*==========================================
+ * ŠÖ”‚ÌŽÀs
+ *------------------------------------------
+ */
+int run_func(struct script_state *st)
+{
+ int i,start_sp,end_sp,func;
+
+ end_sp=st->stack->sp;
+ for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--);
+ if(i==0){
+ if(battle_config.error_log)
+ ShowError("function not found\n");
+// st->stack->sp=0;
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+ start_sp=i-1;
+ st->start=i-1;
+ st->end=end_sp;
+
+ func=st->stack->stack_data[st->start].u.num;
+ if( st->stack->stack_data[st->start].type!=C_NAME || str_data[func].type!=C_FUNC ){
+ ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n",
+ str_buf + str_data[func].str, str_data[func].type);
+// st->stack->sp=0;
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+#ifdef DEBUG_RUN
+ if(battle_config.etc_log) {
+ ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n",str_buf+str_data[func].str, func, str_data[func].type, st->stack->sp, st->start, st->end);
+ ShowDebug("stack dump :");
+ for(i=0;i<end_sp;i++){
+ switch(st->stack->stack_data[i].type){
+ case C_INT:
+ printf(" int(%d)",st->stack->stack_data[i].u.num);
+ break;
+ case C_NAME:
+ printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num].str);
+ break;
+ case C_ARG:
+ printf(" arg");
+ break;
+ case C_POS:
+ printf(" pos(%d)",st->stack->stack_data[i].u.num);
+ break;
+ case C_STR:
+ printf(" str(%s)",st->stack->stack_data[i].u.str);
+ break;
+ case C_CONSTSTR:
+ printf(" cstr(%s)",st->stack->stack_data[i].u.str);
+ break;
+ default:
+ printf(" %d,%d",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num);
+ }
+ }
+ printf("\n");
+ }
+#endif
+ if(str_data[func].func){
+ if (str_data[func].func(st)) //Report error
+ report_src(st);
+ } else {
+ if(battle_config.error_log)
+ ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type);
+ push_val(st->stack,C_INT,0);
+ report_src(st);
+ }
+
+ // Stack's datum are used when re-run functions [Eoe]
+ if(st->state != RERUNLINE) {
+ pop_stack(st->stack,start_sp,end_sp);
+ }
+
+ if(st->state==RETFUNC){
+ // ƒ†[ƒU[’è‹`ŠÖ”‚©‚ç‚Ì•œ‹A
+ int olddefsp=st->stack->defsp;
+ int i;
+
+ pop_stack(st->stack,st->stack->defsp,start_sp); // •œ‹A‚Ɏז‚‚ȃXƒ^ƒbƒNíœ
+ if(st->stack->defsp<4 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){
+ ShowWarning("script:run_func(return) return without callfunc or callsub!\n");
+ st->state=END;
+ report_src(st);
+ return 1;
+ }
+ i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-4])); // ˆø”‚Ì”Š“¾
+ st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1])); // ƒXƒNƒŠƒvƒgˆÊ’u‚Ì•œŒ³
+ st->script=(char*)conv_num(st,& (st->stack->stack_data[st->stack->defsp-2])); // ƒXƒNƒŠƒvƒg‚𕜌³
+ st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-3])); // Šî€ƒXƒ^ƒbƒNƒ|ƒCƒ“ƒ^‚𕜌³
+
+ pop_stack(st->stack,olddefsp-4-i,olddefsp); // —v‚ç‚È‚­‚È‚Á‚½ƒXƒ^ƒbƒN(ˆø”‚Æ•œ‹A—pƒf[ƒ^)íœ
+
+ st->state=GOTO;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒg‚ÌŽÀsƒƒCƒ“•”•ª
+ *------------------------------------------
+ */
+int run_script_main(struct script_state *st)
+{
+ int c/*,rerun_pos*/;
+ int cmdcount=script_config.check_cmdcount;
+ int gotocount=script_config.check_gotocount;
+ struct script_stack *stack=st->stack;
+
+ if(st->state == RERUNLINE) {
+ st->state = RUN;
+ run_func(st);
+ if(st->state == GOTO){
+ st->state = RUN;
+ }
+ } else {
+ st->state = RUN;
+ }
+ while( st->state == RUN) {
+ c= get_com((unsigned char *) st->script,&st->pos);
+ switch(c){
+ case C_EOL:
+ if(stack->sp!=stack->defsp){
+ if(stack->sp > stack->defsp)
+ { //sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex]
+ //Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded.
+ if (battle_config.etc_log)
+ ShowWarning("Clearing unused stack stack.sp(%d) -> default(%d)\n",stack->sp,stack->defsp);
+ pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section.
+ } else if(battle_config.error_log)
+ ShowError("stack.sp(%d) != default(%d)\n",stack->sp,stack->defsp);
+ stack->sp=stack->defsp;
+ }
+ // rerun_pos=st->pos;
+ break;
+ case C_INT:
+ push_val(stack,C_INT,get_num((unsigned char *) st->script,&st->pos));
+ break;
+ case C_POS:
+ case C_NAME:
+ push_val(stack,c,(*(int*)(st->script+st->pos))&0xffffff);
+ st->pos+=3;
+ break;
+ case C_ARG:
+ push_val(stack,c,0);
+ break;
+ case C_STR:
+ push_str(stack,C_CONSTSTR,(unsigned char *) (st->script+st->pos));
+ while(st->script[st->pos++]);
+ break;
+ case C_FUNC:
+ run_func(st);
+ if(st->state==GOTO){
+ // rerun_pos=st->pos;
+ st->state=0;
+ if( gotocount>0 && (--gotocount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ st->state=END;
+ }
+ }
+ break;
+
+ case C_ADD:
+ op_add(st);
+ break;
+
+ case C_SUB:
+ case C_MUL:
+ case C_DIV:
+ case C_MOD:
+ case C_EQ:
+ case C_NE:
+ case C_GT:
+ case C_GE:
+ case C_LT:
+ case C_LE:
+ case C_AND:
+ case C_OR:
+ case C_XOR:
+ case C_LAND:
+ case C_LOR:
+ case C_R_SHIFT:
+ case C_L_SHIFT:
+ op_2(st,c);
+ break;
+
+ case C_NEG:
+ case C_NOT:
+ case C_LNOT:
+ op_1num(st,c);
+ break;
+
+ case C_NOP:
+ st->state=END;
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("unknown command : %d @ %d\n",c,pos);
+ st->state=END;
+ break;
+ }
+ if( cmdcount>0 && (--cmdcount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ st->state=END;
+ }
+ }
+ switch(st->state){
+ case STOP:
+ break;
+ case END:
+ {
+ struct map_session_data *sd=map_id2sd(st->rid);
+ st->pos=-1;
+ if(sd && sd->npc_id==st->oid)
+ npc_event_dequeue(sd);
+ }
+ break;
+ case RERUNLINE:
+ // Do not call function of commands two time! [ Eoe / jA 1094 ]
+ // For example: select "1", "2", callsub(...);
+ // If current script position is changed, callsub will be called two time.
+ //
+ // {
+ // st->pos=rerun_pos;
+ // }
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒNƒŠƒvƒg‚ÌŽÀs
+ *------------------------------------------
+ */
+int run_script(unsigned char *script,int pos,int rid,int oid)
+{
+ struct script_state st;
+ struct map_session_data *sd;
+ unsigned char *rootscript = script;
+
+ //Variables for backing up the previous script and restore it if needed. [Skotlex]
+ unsigned char *bck_script = NULL;
+ unsigned char *bck_scriptroot = NULL;
+ int bck_scriptstate = 0;
+ struct script_stack *bck_stack = NULL;
+
+ if (script == NULL || pos < 0)
+ return -1;
+ memset(&st, 0, sizeof(struct script_state));
+
+ if ((sd = map_id2sd(rid)) && sd->stack && sd->npc_scriptroot == rootscript){
+ // we have a stack for the same script, should continue exec.
+ st.script = sd->npc_script;
+ st.stack = sd->stack;
+ st.state = sd->npc_scriptstate;
+ // and clear vars
+ sd->stack = NULL;
+ sd->npc_script = NULL;
+ sd->npc_scriptroot = NULL;
+ sd->npc_scriptstate = 0;
+ } else {
+ // the script is different, make new script_state and stack
+ st.stack = aCalloc (1, sizeof(struct script_stack));
+ st.stack->sp = 0;
+ st.stack->sp_max = 64;
+ st.stack->stack_data = (struct script_data *) aCalloc (st.stack->sp_max,sizeof(st.stack->stack_data[0]));
+ st.stack->defsp = st.stack->sp;
+ st.state = RUN;
+ st.script = rootscript;
+
+ if (sd && sd->stack) { // if there's a sd and a stack - back it up and restore it if possible.
+ bck_script = sd->npc_script;
+ bck_scriptroot = sd->npc_scriptroot;
+ bck_scriptstate = sd->npc_scriptstate;
+ bck_stack = sd->stack;
+ sd->stack = NULL;
+ }
+ }
+ st.pos = pos;
+ st.rid = rid;
+ st.oid = oid;
+ // let's run that stuff
+ run_script_main(&st);
+
+ sd = map_id2sd(st.rid);
+ if (st.state != END && sd) {
+ // script is not finished, store data in sd.
+ sd->npc_script = st.script;
+ sd->npc_scriptroot = rootscript;
+ sd->npc_scriptstate = st.state;
+ sd->stack = st.stack;
+ if (bck_stack) //Get rid of the backup as it can't be restored.
+ script_free_stack (bck_stack);
+ } else {
+ // we are done with stuff, free the stack
+ script_free_stack (st.stack);
+ // and if there was a sd associated - zero vars.
+ if (sd) {
+ //Clear or restore previous script.
+ sd->npc_script = bck_script;
+ sd->npc_scriptroot = bck_scriptroot;
+ sd->npc_scriptstate = bck_scriptstate;
+ sd->stack = bck_stack;
+ //Since the script is done, save any changed account variables [Skotlex]
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd,2);
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd,1);
+ }
+ }
+
+ return st.pos;
+}
+
+
+/*==========================================
+ * ƒ}ƒbƒv•Ï”‚Ì•ÏX
+ *------------------------------------------
+ */
+int mapreg_setreg(int num,int val)
+{
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ int i=num>>24;
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+ char tmp_str[64];
+#endif
+
+ if(val!=0) {
+
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@' && idb_get(mapreg_db,num) == NULL) {
+ sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%d')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,val);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_put(mapreg_db,num,(void*)val);
+ // else
+ } else { // [zBuffer]
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@') { // Remove from database because it is unused.
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_remove(mapreg_db,num);
+ }
+
+ mapreg_dirty=1;
+ return 0;
+}
+/*==========================================
+ * •¶Žš—ñŒ^ƒ}ƒbƒv•Ï”‚Ì•ÏX
+ *------------------------------------------
+ */
+int mapreg_setregstr(int num,const char *str)
+{
+ char *p;
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ char tmp_str[64];
+ char tmp_str2[512];
+ int i=num>>24; // [zBuffer]
+ char *name=str_buf+str_data[num&0x00ffffff].str;
+#endif
+
+ if( str==NULL || *str==0 ){
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ if(name[1] != '@') {
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ idb_remove(mapregstr_db,num);
+ mapreg_dirty=1;
+ return 0;
+ }
+ p=(char *)aCallocA(strlen(str)+1, sizeof(char));
+ strcpy(p,str);
+
+ if (idb_put(mapregstr_db,num,p))
+ ;
+#if !defined(TXT_ONLY) && defined(MAPREGSQL)
+ else { //put returned null, so we must insert.
+ sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%s')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,jstrescapecpy(tmp_str2,p));
+ if(mysql_query(&mmysql_handle,tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+#endif
+ mapreg_dirty=1;
+ return 0;
+}
+
+/*==========================================
+ * ‰i‘±“Iƒ}ƒbƒv•Ï”‚Ì“Ç‚Ýž‚Ý
+ *------------------------------------------
+ */
+static int script_load_mapreg(void)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp;
+ char line[1024];
+
+ if( (fp=fopen(mapreg_txt,"rt"))==NULL )
+ return -1;
+
+ while(fgets(line,sizeof(line),fp)){
+ char buf1[256],buf2[1024],*p;
+ int n,v,s,i;
+ if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 &&
+ (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) )
+ continue;
+ if( buf1[strlen(buf1)-1]=='$' ){
+ if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){
+ ShowError("%s: %s broken data !\n",mapreg_txt,buf1);
+ continue;
+ }
+ p=(char *)aCallocA(strlen(buf2) + 1,sizeof(char));
+ strcpy(p,buf2);
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapregstr_db,(i<<24)|s,p);
+ }else{
+ if( sscanf(line+n,"%d",&v)!=1 ){
+ ShowError("%s: %s broken data !\n",mapreg_txt,buf1);
+ continue;
+ }
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapreg_db,(i<<24)|s,(void*)v);
+ }
+ }
+ fclose(fp);
+ mapreg_dirty=0;
+ return 0;
+#else
+ // SQL mapreg code start [zBuffer]
+ /*
+ 0 1 2
+ +-------------------------+
+ | varname | index | value |
+ +-------------------------+
+ */
+ int perfomance = gettick_nocache();
+ sprintf(tmp_sql,"SELECT * FROM `%s`",mapregsql_db);
+ ShowInfo("Querying script_load_mapreg ...\n");
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+ ShowInfo("Success! Returning results ...\n");
+ mapregsql_res = mysql_store_result(&mapregsql_handle);
+ if (mapregsql_res) {
+ while ((mapregsql_row = mysql_fetch_row(mapregsql_res))) {
+ char buf1[33], *p = NULL;
+ int i,v,s;
+ strcpy(buf1,mapregsql_row[0]);
+ if( buf1[strlen(buf1)-1]=='$' ){
+ i = atoi(mapregsql_row[1]);
+ p=(char *)aCallocA(strlen(mapregsql_row[2]) + 1,sizeof(char));
+ strcpy(p,mapregsql_row[2]);
+ s= add_str((unsigned char *) buf1);
+ idb_put(mapregstr_db,(i<<24)|s,p);
+ }else{
+ s= add_str((unsigned char *) buf1);
+ v= atoi(mapregsql_row[2]);
+ i = atoi(mapregsql_row[1]);
+ idb_put(mapreg_db,(i<<24)|s,(void *)v);
+ }
+ }
+ }
+ ShowInfo("Freeing results...\n");
+ mysql_free_result(mapregsql_res);
+ mapreg_dirty=0;
+ perfomance = (gettick_nocache() - perfomance) / 1000;
+ ShowInfo("SQL Mapreg Loading Completed Under %d Seconds.\n",perfomance);
+ return 0;
+#endif /* TXT_ONLY */
+}
+/*==========================================
+ * ‰i‘±“Iƒ}ƒbƒv•Ï”‚Ì‘‚«ž‚Ý
+ *------------------------------------------
+ */
+static int script_save_mapreg_intsub(DBKey key,void *data,va_list ap)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp=va_arg(ap,FILE*);
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if( name[1]!='@' ){
+ if(i==0)
+ fprintf(fp,"%s\t%d\n", name, (int)data);
+ else
+ fprintf(fp,"%s,%d\t%d\n", name, i, (int)data);
+ }
+ return 0;
+#else
+ int num=key.i&0x00ffffff, i=key.i>>24; // [zBuffer]
+ char *name=str_buf+str_data[num].str;
+ if ( name[1] != '@') {
+ sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%d' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,(int)data,mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+#endif
+}
+static int script_save_mapreg_strsub(DBKey key,void *data,va_list ap)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp=va_arg(ap,FILE*);
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if( name[1]!='@' ){
+ if(i==0)
+ fprintf(fp,"%s\t%s\n", name, (char *)data);
+ else
+ fprintf(fp,"%s,%d\t%s\n", name, i, (char *)data);
+ }
+ return 0;
+#else
+ char tmp_str2[512];
+ int num=key.i&0x00ffffff, i=key.i>>24;
+ char *name=str_buf+str_data[num].str;
+ if ( name[1] != '@') {
+ sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%s' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,jstrescapecpy(tmp_str2,(char *)data),mapregsql_db_varname,name,mapregsql_db_index,i);
+ if(mysql_query(&mapregsql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mapregsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ return 0;
+#endif
+}
+static int script_save_mapreg(void)
+{
+#if defined(TXT_ONLY) || !defined(MAPREGSQL)
+ FILE *fp;
+ int lock;
+
+ if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) {
+ ShowError("script_save_mapreg: Unable to lock-open file [%s]\n",mapreg_txt);
+ return -1;
+ }
+ mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub,fp);
+ mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub,fp);
+ lock_fclose(fp,mapreg_txt,&lock);
+#else
+ int perfomance = gettick_nocache();
+ mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub); // [zBuffer]
+ mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub);
+ perfomance = (gettick_nocache() - perfomance) / 1000;
+ ShowInfo("Mapreg saved in %d seconds.\n", perfomance);
+#endif
+ mapreg_dirty=0;
+ return 0;
+}
+static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data)
+{
+ if(mapreg_dirty)
+ if (script_save_mapreg() == -1)
+ ShowError("Failed to save the mapreg data!\n");
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int set_posword(char *p)
+{
+ char* np,* str[15];
+ int i=0;
+ for(i=0;i<11;i++) {
+ if((np=strchr(p,','))!=NULL) {
+ str[i]=p;
+ *np=0;
+ p=np+1;
+ } else {
+ str[i]=p;
+ p+=strlen(p);
+ }
+ if(str[i])
+ strcpy(pos[i],str[i]);
+ }
+ return 0;
+}
+
+int script_config_read_sub(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+
+ fp = fopen(cfgName, "r");
+ if (fp == NULL) {
+ ShowError("file not found: [%s]\n", cfgName);
+ return 1;
+ }
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ i = sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if (i != 2)
+ continue;
+ if(strcmpi(w1,"refine_posword")==0) {
+ set_posword(w2);
+ }
+ else if(strcmpi(w1,"verbose_mode")==0) {
+ script_config.verbose_mode = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_no_comma")==0) {
+ script_config.warn_func_no_comma = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_cmd_no_comma")==0) {
+ script_config.warn_cmd_no_comma = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) {
+ script_config.warn_func_mismatch_paramnum = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_cmd_mismatch_paramnum")==0) {
+ script_config.warn_cmd_mismatch_paramnum = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_cmdcount")==0) {
+ script_config.check_cmdcount = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_gotocount")==0) {
+ script_config.check_gotocount = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"event_script_type")==0) {
+ script_config.event_script_type = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"event_requires_trigger")==0) {
+ script_config.event_requires_trigger = battle_config_switch(w2);
+ }
+ else if(strcmpi(w1,"die_event_name")==0) {
+ strncpy(script_config.die_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.die_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.die_event_name);
+ }
+ else if(strcmpi(w1,"kill_event_name")==0) {
+ strncpy(script_config.kill_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.kill_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_event_name);
+ }
+ else if(strcmpi(w1,"login_event_name")==0) {
+ strncpy(script_config.login_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.login_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.login_event_name);
+ }
+ else if(strcmpi(w1,"logout_event_name")==0) {
+ strncpy(script_config.logout_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.logout_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.logout_event_name);
+ }
+ else if(strcmpi(w1,"loadmap_event_name")==0) {
+ strncpy(script_config.loadmap_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.loadmap_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.loadmap_event_name);
+ }
+ else if(strcmpi(w1,"baselvup_event_name")==0) {
+ strncpy(script_config.baselvup_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.baselvup_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.baselvup_event_name);
+ }
+ else if(strcmpi(w1,"joblvup_event_name")==0) {
+ strncpy(script_config.joblvup_event_name, w2, NAME_LENGTH-1);
+ if (strlen(script_config.joblvup_event_name) != strlen(w2))
+ ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.joblvup_event_name);
+ }
+ else if(strcmpi(w1,"import")==0){
+ script_config_read_sub(w2);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+int script_config_read(char *cfgName)
+{ //Script related variables should be initialized once! [Skotlex]
+
+ memset (&script_config, 0, sizeof(script_config));
+ script_config.verbose_mode = 0;
+ script_config.warn_func_no_comma = 1;
+ script_config.warn_cmd_no_comma = 1;
+ script_config.warn_func_mismatch_paramnum = 1;
+ script_config.warn_cmd_mismatch_paramnum = 1;
+ script_config.check_cmdcount = 65535;
+ script_config.check_gotocount = 2048;
+
+ script_config.event_script_type = 0;
+ script_config.event_requires_trigger = 1;
+
+ return script_config_read_sub(cfgName);
+}
+
+/*==========================================
+ * I—¹
+ *------------------------------------------
+ */
+int do_final_script()
+{
+ if(mapreg_dirty>=0)
+ script_save_mapreg();
+
+ mapreg_db->destroy(mapreg_db,NULL);
+ mapregstr_db->destroy(mapregstr_db,NULL);
+ scriptlabel_db->destroy(scriptlabel_db,NULL);
+ userfunc_db->destroy(userfunc_db,NULL);
+
+ if (str_data)
+ aFree(str_data);
+ if (str_buf)
+ aFree(str_buf);
+
+ return 0;
+}
+/*==========================================
+ * ‰Šú‰»
+ *------------------------------------------
+ */
+int do_init_script()
+{
+ mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50);
+ scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_ALLOW_NULL_DATA,50);
+
+ script_load_mapreg();
+
+ add_timer_func_list(script_autosave_mapreg,"script_autosave_mapreg");
+ add_timer_interval(gettick()+MAPREG_AUTOSAVE_INTERVAL,
+ script_autosave_mapreg,0,0,MAPREG_AUTOSAVE_INTERVAL);
+
+ return 0;
+}
+
+int script_reload()
+{
+ if(mapreg_dirty>=0)
+ script_save_mapreg();
+
+ mapreg_db->clear(mapreg_db, NULL);
+ mapregstr_db->clear(mapreg_db, NULL);
+ userfunc_db->clear(mapreg_db, NULL);
+ scriptlabel_db->clear(mapreg_db, NULL);
+
+ script_load_mapreg();
+ return 0;
+}
diff --git a/src/map/script.h b/src/map/script.h
new file mode 100644
index 000000000..ecd326de9
--- /dev/null
+++ b/src/map/script.h
@@ -0,0 +1,73 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SCRIPT_H_
+#define _SCRIPT_H_
+
+extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
+extern int potion_target;
+
+extern struct Script_Config {
+ unsigned verbose_mode : 1;
+ unsigned warn_func_no_comma : 1;
+ unsigned warn_cmd_no_comma : 1;
+ unsigned warn_func_mismatch_paramnum : 1;
+ unsigned warn_cmd_mismatch_paramnum : 1;
+ int check_cmdcount;
+ int check_gotocount;
+
+ unsigned event_script_type : 1;
+ unsigned event_requires_trigger : 1;
+ char die_event_name[NAME_LENGTH];
+ char kill_event_name[NAME_LENGTH];
+ char login_event_name[NAME_LENGTH];
+ char logout_event_name[NAME_LENGTH];
+ char loadmap_event_name[NAME_LENGTH];
+ char baselvup_event_name[NAME_LENGTH];
+ char joblvup_event_name[NAME_LENGTH];
+} script_config;
+
+struct script_data {
+ int type;
+ union {
+ int num;
+ char *str;
+ } u;
+};
+
+// Moved defsp from script_state to script_stack since
+// it must be saved when script state is RERUNLINE. [Eoe / jA 1094]
+struct script_stack {
+ int sp,sp_max,defsp;
+ struct script_data *stack_data;
+};
+struct script_state {
+ struct script_stack *stack;
+ int start,end;
+ int pos,state;
+ int rid,oid;
+ unsigned char *script,*new_script;
+ int new_pos,new_defsp;
+};
+
+unsigned char * parse_script(unsigned char *,int);
+int run_script(unsigned char *,int,int,int);
+
+int set_var(struct map_session_data *sd, char *name, void *val);
+int conv_num(struct script_state *st,struct script_data *data);
+char* conv_str(struct script_state *st,struct script_data *data);
+
+struct dbt* script_get_label_db(void);
+struct dbt* script_get_userfunc_db(void);
+
+int script_config_read(char *cfgName);
+void script_free_stack(struct script_stack*);
+int do_init_script(void);
+int do_final_script(void);
+int script_reload(void);
+
+extern char mapreg_txt[];
+
+#endif
+
diff --git a/src/map/skill.c b/src/map/skill.c
new file mode 100644
index 000000000..7c393fd40
--- /dev/null
+++ b/src/map/skill.c
@@ -0,0 +1,12348 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "timer.h"
+#include "nullpo.h"
+#include "malloc.h"
+
+#include "skill.h"
+#include "map.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "pet.h"
+#include "mob.h"
+#include "battle.h"
+#include "party.h"
+#include "itemdb.h"
+#include "script.h"
+#include "intif.h"
+#include "log.h"
+#include "chrif.h"
+#include "guild.h"
+#include "showmsg.h"
+#include "grfio.h"
+#include "date.h"
+
+#define SKILLUNITTIMER_INVERVAL 100
+#define swap(x,y) { int t; t = x; x = y; y = t; }
+
+int skill_names_id[MAX_SKILL_DB];
+const struct skill_name_db skill_names[] = {
+ { AC_CHARGEARROW, "AC_CHARGEARROW", "Arrow_Repel" } ,
+ { AC_CONCENTRATION, "AC_CONCENTRATION", "Improve_Concentration" } ,
+ { AC_DOUBLE, "AC_DOUBLE", "Double_Strafe" } ,
+ { AC_MAKINGARROW, "AC_MAKINGARROW", "Arrow_Crafting" } ,
+ { AC_OWL, "AC_OWL", "Owl's_Eye" } ,
+ { AC_SHOWER, "AC_SHOWER", "Arrow_Shower" } ,
+ { AC_VULTURE, "AC_VULTURE", "Vulture's_Eye" } ,
+ { ALL_RESURRECTION, "ALL_RESURRECTION", "Resurrection" } ,
+ { AL_ANGELUS, "AL_ANGELUS", "Angelus" } ,
+ { AL_BLESSING, "AL_BLESSING", "Blessing" } ,
+ { AL_CRUCIS, "AL_CRUCIS", "Signum_Crusis" } ,
+ { AL_CURE, "AL_CURE", "Cure" } ,
+ { AL_DECAGI, "AL_DECAGI", "Decrease_AGI" } ,
+ { AL_DEMONBANE, "AL_DEMONBANE", "Demon_Bane" } ,
+ { AL_DP, "AL_DP", "Divine_Protection" } ,
+ { AL_HEAL, "AL_HEAL", "Heal" } ,
+ { AL_HOLYLIGHT, "AL_HOLYLIGHT", "Holy_Light" } ,
+ { AL_HOLYWATER, "AL_HOLYWATER", "Aqua_Benedicta" } ,
+ { AL_INCAGI, "AL_INCAGI", "Increase_AGI" } ,
+ { AL_PNEUMA, "AL_PNEUMA", "Pneuma" } ,
+ { AL_RUWACH, "AL_RUWACH", "Ruwach" } ,
+ { AL_TELEPORT, "AL_TELEPORT", "Teleport" } ,
+ { AL_WARP, "AL_WARP", "Warp_Portal" } ,
+ { AM_ACIDTERROR, "AM_ACIDTERROR", "Acid_Terror" } ,
+ { AM_AXEMASTERY, "AM_AXEMASTERY", "Axe_Mastery" } ,
+ { AM_BERSERKPITCHER, "AM_BERSERKPITCHER", "Aid_Berserk_Potion" } ,
+ { AM_BIOETHICS, "AM_BIOETHICS", "Bioethics" } ,
+ { AM_CALLHOMUN, "AM_CALLHOMUN", "Call_Homunculus" } ,
+ { AM_CANNIBALIZE, "AM_CANNIBALIZE", "Summon_Flora" } ,
+ { AM_CP_ARMOR, "AM_CP_ARMOR", "Synthetic_Armor" } ,
+ { AM_CP_HELM, "AM_CP_HELM", "Biochemical_Helm" } ,
+ { AM_CP_SHIELD, "AM_CP_SHIELD", "Synthetized_Shield" } ,
+ { AM_CP_WEAPON, "AM_CP_WEAPON", "Alchemical_Weapon" } ,
+ { AM_CULTIVATION, "AM_CULTIVATION", "Cultivation" } ,
+ { AM_DEMONSTRATION, "AM_DEMONSTRATION", "Bomb" } ,
+ { AM_LEARNINGPOTION, "AM_LEARNINGPOTION", "Potion_Research" } ,
+ { AM_PHARMACY, "AM_PHARMACY", "Prepare_Potion" } ,
+ { AM_POTIONPITCHER, "AM_POTIONPITCHER", "Aid_Potion" } ,
+ { AM_REST, "AM_REST", "Vaporize" } ,
+ { AM_RESURRECTHOMUN, "AM_RESURRECTHOMUN", "Homunculus_Resurrection" } ,
+ { AM_SPHEREMINE, "AM_SPHEREMINE", "Summon_Marine_Sphere" } ,
+ { AM_TWILIGHT1, "AM_TWILIGHT1", "Twilight_Pharmacy_1" } ,
+ { AM_TWILIGHT2, "AM_TWILIGHT2", "Twilight_Pharmacy_2" } ,
+ { AM_TWILIGHT3, "AM_TWILIGHT3", "Twilight_Pharmacy_3" } ,
+ { ASC_BREAKER, "ASC_BREAKER", "Soul_Destroyer" } ,
+ { ASC_CDP, "ASC_CDP", "Create_Deadly_Poison" } ,
+ { ASC_EDP, "ASC_EDP", "Enchant_Deadly_Poison" } ,
+ { ASC_KATAR, "ASC_KATAR", "Advanced_Katar_Mastery" } ,
+ { ASC_METEORASSAULT, "ASC_METEORASSAULT", "Meteor_Assault" } ,
+ { AS_CLOAKING, "AS_CLOAKING", "Cloaking" } ,
+ { AS_ENCHANTPOISON, "AS_ENCHANTPOISON", "Enchant_Poison" } ,
+ { AS_GRIMTOOTH, "AS_GRIMTOOTH", "Grimtooth" } ,
+ { AS_KATAR, "AS_KATAR", "Katar_Mastery" } ,
+ { AS_LEFT, "AS_LEFT", "Lefthand_Mastery" } ,
+ { AS_POISONREACT, "AS_POISONREACT", "Poison_React" } ,
+ { AS_RIGHT, "AS_RIGHT", "Righthand_Mastery" } ,
+ { AS_SONICACCEL, "AS_SONICACCEL", "Sonic_Acceleration" } ,
+ { AS_SONICBLOW, "AS_SONICBLOW", "Sonic_Blow" } ,
+ { AS_SPLASHER, "AS_SPLASHER", "Venom_Splasher" } ,
+ { AS_VENOMDUST, "AS_VENOMDUST", "Venom_Dust" } ,
+ { AS_VENOMKNIFE, "AS_VENOMKNIFE", "Throw_Venom_Knife" } ,
+ { BA_APPLEIDUN, "BA_APPLEIDUN", "Song_of_Lutie" } ,
+ { BA_ASSASSINCROSS, "BA_ASSASSINCROSS", "Impressive_Riff" } ,
+ { BA_DISSONANCE, "BA_DISSONANCE", "Unchained_Serenade" } ,
+ { BA_FROSTJOKE, "BA_FROSTJOKE", "Unbarring_Octave" } ,
+ { BA_MUSICALLESSON, "BA_MUSICALLESSON", "Music_Lessons" } ,
+ { BA_MUSICALSTRIKE, "BA_MUSICALSTRIKE", "Melody_Strike" } ,
+ { BA_PANGVOICE, "BA_PANGVOICE", "Pang_Voice" } ,
+ { BA_POEMBRAGI, "BA_POEMBRAGI", "Magic_Strings" } ,
+ { BA_WHISTLE, "BA_WHISTLE", "Perfect_Tablature" } ,
+ { BD_ADAPTATION, "BD_ADAPTATION", "Amp" } ,
+ { BD_DRUMBATTLEFIELD, "BD_DRUMBATTLEFIELD", "Battle_Theme" } ,
+ { BD_ENCORE, "BD_ENCORE", "Encore" } ,
+ { BD_ETERNALCHAOS, "BD_ETERNALCHAOS", "Down_Tempo" } ,
+ { BD_INTOABYSS, "BD_INTOABYSS", "Power_Cord" } ,
+ { BD_LULLABY, "BD_LULLABY", "Lullaby" } ,
+ { BD_RICHMANKIM, "BD_RICHMANKIM", "Mental_Sensing" } ,
+ { BD_RINGNIBELUNGEN, "BD_RINGNIBELUNGEN", "Harmonic_Lick" } ,
+ { BD_ROKISWEIL, "BD_ROKISWEIL", "Classical_Pluck" } ,
+ { BD_SIEGFRIED, "BD_SIEGFRIED", "Acoustic_Rhythm" } ,
+ { BS_ADRENALINE, "BS_ADRENALINE", "Adrenaline_Rush" } ,
+ { BS_ADRENALINE2, "BS_ADRENALINE2", "Advanced_Adrenaline_Rush" } ,
+ { BS_AXE, "BS_AXE", "Smith_Axe" } ,
+ { BS_DAGGER, "BS_DAGGER", "Smith_Dagger" } ,
+ { BS_ENCHANTEDSTONE, "BS_ENCHANTEDSTONE", "Enchantedstone_Craft" } ,
+ { BS_FINDINGORE, "BS_FINDINGORE", "Ore_Discovery" } ,
+ { BS_GREED, "BS_GREED", "Greed" } ,
+ { BS_HAMMERFALL, "BS_HAMMERFALL", "Hammer_Fall" } ,
+ { BS_HILTBINDING, "BS_HILTBINDING", "Hilt_Binding" } ,
+ { BS_IRON, "BS_IRON", "Iron_Tempering" } ,
+ { BS_KNUCKLE, "BS_KNUCKLE", "Smith_Knucklebrace" } ,
+ { BS_MACE, "BS_MACE", "Smith_Mace" } ,
+ { BS_MAXIMIZE, "BS_MAXIMIZE", "Power_Maximize" } ,
+ { BS_ORIDEOCON, "BS_ORIDEOCON", "Oridecon_Research" } ,
+ { BS_OVERTHRUST, "BS_OVERTHRUST", "Power-Thrust" } ,
+ { BS_REPAIRWEAPON, "BS_REPAIRWEAPON", "Weapon_Repair" } ,
+ { BS_SKINTEMPER, "BS_SKINTEMPER", "Skin_Tempering" } ,
+ { BS_SPEAR, "BS_SPEAR", "Smith_Spear" } ,
+ { BS_STEEL, "BS_STEEL", "Steel_Tempering" } ,
+ { BS_SWORD, "BS_SWORD", "Smith_Sword" } ,
+ { BS_TWOHANDSWORD, "BS_TWOHANDSWORD", "Smith_Two-handed_Sword" } ,
+ { BS_UNFAIRLYTRICK, "BS_UNFAIRLYTRICK", "Unfair_Trick" } ,
+ { BS_WEAPONPERFECT, "BS_WEAPONPERFECT", "Weapon_Perfection" } ,
+ { BS_WEAPONRESEARCH, "BS_WEAPONRESEARCH", "Weaponry_Research" } ,
+ { CG_ARROWVULCAN, "CG_ARROWVULCAN", "Vulcan_Arrow" } ,
+ { CG_HERMODE, "CG_HERMODE", "Wand_of_Hermode" } ,
+ { CG_LONGINGFREEDOM, "CG_LONGINGFREEDOM", "Longing_for_Freedom" } ,
+ { CG_MARIONETTE, "CG_MARIONETTE", "Marionette_Control" } ,
+ { CG_MOONLIT, "CG_MOONLIT", "Sheltering_Bliss" } ,
+ { CG_TAROTCARD, "CG_TAROTCARD", "Tarot_Card_of_Fate" } ,
+ { CH_CHAINCRUSH, "CH_CHAINCRUSH", "Chain_Crush_Combo" } ,
+ { CH_PALMSTRIKE, "CH_PALMSTRIKE", "Raging_Palm_Strike" } ,
+ { CH_SOULCOLLECT, "CH_SOULCOLLECT", "Zen" } ,
+ { CH_TIGERFIST, "CH_TIGERFIST", "Glacier_Fist" } ,
+ { CR_ACIDDEMONSTRATION, "CR_ACIDDEMONSTRATION", "Acid_Demonstration" } ,
+ { CR_ALCHEMY, "CR_ALCHEMY", "Alchemy" } ,
+ { CR_CULTIVATION, "CR_CULTIVATION", "Plant_Cultivation" } ,
+ { CR_SLIMPITCHER, "CR_SLIMPITCHER", "Slim_Pitcher" } ,
+ { CR_FULLPROTECTION, "CR_FULLPROTECTION", "Full_Protection" } ,
+ { CR_SYNTHESISPOTION, "CR_SYNTHESISPOTION", "Potion_Synthesis" } ,
+ { CR_AUTOGUARD, "CR_AUTOGUARD", "Guard" } ,
+ { CR_DEFENDER, "CR_DEFENDER", "Defending_Aura" } ,
+ { CR_DEVOTION, "CR_DEVOTION", "Sacrifice" } ,
+ { CR_GRANDCROSS, "CR_GRANDCROSS", "Grand_Cross" } ,
+ { CR_HOLYCROSS, "CR_HOLYCROSS", "Holy_Cross" } ,
+ { CR_PROVIDENCE, "CR_PROVIDENCE", "Resistant_Souls" } ,
+ { CR_REFLECTSHIELD, "CR_REFLECTSHIELD", "Shield_Reflect" } ,
+ { CR_SHIELDBOOMERANG, "CR_SHIELDBOOMERANG", "Shield_Boomerang" } ,
+ { CR_SHIELDCHARGE, "CR_SHIELDCHARGE", "Smite" } ,
+ { CR_SHRINK, "CR_SHRINK", "Shrink" } ,
+ { CR_SPEARQUICKEN, "CR_SPEARQUICKEN", "Spear_Quicken" } ,
+ { CR_TRUST, "CR_TRUST", "Faith" } ,
+ { DC_DANCINGLESSON, "DC_DANCINGLESSON", "Dance_Lessons" } ,
+ { DC_DONTFORGETME, "DC_DONTFORGETME", "Slow_Grace" } ,
+ { DC_FORTUNEKISS, "DC_FORTUNEKISS", "Lady_Luck" } ,
+ { DC_HUMMING, "DC_HUMMING", "Focus_Ballet" } ,
+ { DC_SCREAM, "DC_SCREAM", "Dazzler" } ,
+ { DC_SERVICEFORYOU, "DC_SERVICEFORYOU", "Gypsy's_Kiss" } ,
+ { DC_THROWARROW, "DC_THROWARROW", "Slinging_Arrow" } ,
+ { DC_UGLYDANCE, "DC_UGLYDANCE", "Hip_Shaker" } ,
+ { DC_WINKCHARM, "DC_WINKCHARM", "Sexy_Wink" } ,
+ { GD_APPROVAL, "GD_APPROVAL", "Official_Guild_Approval" } ,
+ { GD_BATTLEORDER, "GD_BATTLEORDER", "Battle_Command" } ,
+ { GD_DEVELOPMENT, "GD_DEVELOPMENT", "Permanent_Development" } ,
+ { GD_EMERGENCYCALL, "GD_EMERGENCYCALL", "Urgent_Call" } ,
+ { GD_EXTENSION, "GD_EXTENSION", "Guild_Extension" } ,
+ { GD_GLORYGUILD, "GD_GLORYGUILD", "Glory_of_Guild" } ,
+ { GD_GLORYWOUNDS, "GD_GLORYWOUNDS", "Glorious_Wounds" } ,
+ { GD_GUARDUP, "GD_GUARDUP", "Strengthen_Guardian" } ,
+ { GD_LEADERSHIP, "GD_LEADERSHIP", "Great_Leadership" } ,
+ { GD_HAWKEYES, "GD_HAWKEYES", "Sharp_Gaze" } ,
+ { GD_KAFRACONTRACT, "GD_KAFRACONTRACT", "Contract_with_Kafra" } ,
+ { GD_REGENERATION, "GD_REGENERATION", "Regeneration" } ,
+ { GD_RESTORE, "GD_RESTORE", "Restoration" } ,
+ { GD_SOULCOLD, "GD_SOULCOLD", "Cold_Heart" } ,
+ { HP_ASSUMPTIO, "HP_ASSUMPTIO", "Assumptio" } ,
+ { HP_BASILICA, "HP_BASILICA", "Basilica" } ,
+ { HP_MANARECHARGE, "HP_MANARECHARGE", "Mana_Recharge" } ,
+ { HP_MEDITATIO, "HP_MEDITATIO", "Meditatio" } ,
+ { HT_ANKLESNARE, "HT_ANKLESNARE", "Ankle_Snare" } ,
+ { HT_BEASTBANE, "HT_BEASTBANE", "Beast_Bane" } ,
+ { HT_BLASTMINE, "HT_BLASTMINE", "Blast_Mine" } ,
+ { HT_BLITZBEAT, "HT_BLITZBEAT", "Blitz_Beat" } ,
+ { HT_CLAYMORETRAP, "HT_CLAYMORETRAP", "Claymore_Trap" } ,
+ { HT_DETECTING, "HT_DETECTING", "Detect" } ,
+ { HT_FALCON, "HT_FALCON", "Falconry_Mastery" } ,
+ { HT_FLASHER, "HT_FLASHER", "Flasher" } ,
+ { HT_FREEZINGTRAP, "HT_FREEZINGTRAP", "Freezing_Trap" } ,
+ { HT_LANDMINE, "HT_LANDMINE", "Land_Mine" } ,
+ { HT_PHANTASMIC, "HT_PHANTASMIC", "Phantasmic_Arrow" } ,
+ { HT_POWER, "HT_POWER", "Beast_Strafing" } ,
+ { HT_REMOVETRAP, "HT_REMOVETRAP", "Remove_Trap" } ,
+ { HT_SANDMAN, "HT_SANDMAN", "Sandman" } ,
+ { HT_SHOCKWAVE, "HT_SHOCKWAVE", "Shockwave_Trap" } ,
+ { HT_SKIDTRAP, "HT_SKIDTRAP", "Skid_Trap" } ,
+ { HT_SPRINGTRAP, "HT_SPRINGTRAP", "Spring_Trap" } ,
+ { HT_STEELCROW, "HT_STEELCROW", "Steel_Crow" } ,
+ { HT_TALKIEBOX, "HT_TALKIEBOX", "Talkie_Box" } ,
+ { HW_GANBANTEIN, "HW_GANBANTEIN", "Ganbantein" } ,
+ { HW_GRAVITATION, "HW_GRAVITATION", "Gravitation_Field" } ,
+ { HW_MAGICCRASHER, "HW_MAGICCRASHER", "Stave_Crasher" } ,
+ { HW_MAGICPOWER, "HW_MAGICPOWER", "Mystical_Amplification" } ,
+ { HW_NAPALMVULCAN, "HW_NAPALMVULCAN", "Napalm_Vulcan" } ,
+ { HW_SOULDRAIN, "HW_SOULDRAIN", "Soul_Drain" } ,
+ { ITM_TOMAHAWK, "ITM_TOMAHAWK", "Tomahawk_Throwing" } ,
+ { KN_AUTOCOUNTER, "KN_AUTOCOUNTER", "Counter_Attack" } ,
+ { KN_BOWLINGBASH, "KN_BOWLINGBASH", "Bowling_Bash" } ,
+ { KN_BRANDISHSPEAR, "KN_BRANDISHSPEAR", "Brandish_Spear" } ,
+ { KN_CAVALIERMASTERY, "KN_CAVALIERMASTERY", "Cavalier_Mastery" } ,
+ { KN_CHARGEATK, "KN_CHARGEATK", "Charge_Attack" } ,
+ { KN_ONEHAND, "KN_ONEHAND", "Onehand_Quicken" } ,
+ { KN_PIERCE, "KN_PIERCE", "Pierce" } ,
+ { KN_RIDING, "KN_RIDING", "Peco_Peco_Ride" } ,
+ { KN_SPEARBOOMERANG, "KN_SPEARBOOMERANG", "Spear_Boomerang" } ,
+ { KN_SPEARMASTERY, "KN_SPEARMASTERY", "Spear_Mastery" } ,
+ { KN_SPEARSTAB, "KN_SPEARSTAB", "Spear_Stab" } ,
+ { KN_TWOHANDQUICKEN, "KN_TWOHANDQUICKEN", "Twohand_Quicken" } ,
+ { LK_AURABLADE, "LK_AURABLADE", "Aura_Blade" } ,
+ { LK_BERSERK, "LK_BERSERK", "Frenzy" } ,
+ { LK_CONCENTRATION, "LK_CONCENTRATION", "Spear_Dynamo" } ,
+ { LK_HEADCRUSH, "LK_HEADCRUSH", "Traumatic_Blow" } ,
+ { LK_JOINTBEAT, "LK_JOINTBEAT", "Vital_Strike" } ,
+ { LK_PARRYING, "LK_PARRYING", "Parry" } ,
+ { LK_SPIRALPIERCE, "LK_SPIRALPIERCE", "Clashing_Spiral" } ,
+ { LK_TENSIONRELAX, "LK_TENSIONRELAX", "Relax" } ,
+ { MC_CARTREVOLUTION, "MC_CARTREVOLUTION", "Cart_Revolution" } ,
+ { MC_CHANGECART, "MC_CHANGECART", "Change_Cart" } ,
+ { MC_DISCOUNT, "MC_DISCOUNT", "Discount" } ,
+ { MC_IDENTIFY, "MC_IDENTIFY", "Item_Appraisal" } ,
+ { MC_INCCARRY, "MC_INCCARRY", "Enlarge_Weight_Limit" } ,
+ { MC_LOUD, "MC_LOUD", "Crazy_Uproar" } ,
+ { MC_MAMMONITE, "MC_MAMMONITE", "Mammonite" } ,
+ { MC_OVERCHARGE, "MC_OVERCHARGE", "Overcharge" } ,
+ { MC_PUSHCART, "MC_PUSHCART", "Pushcart" } ,
+ { MC_VENDING, "MC_VENDING", "Vending" } ,
+ { MG_COLDBOLT, "MG_COLDBOLT", "Cold_Bolt" } ,
+ { MG_ENERGYCOAT, "MG_ENERGYCOAT", "Energy_Coat" } ,
+ { MG_FIREBALL, "MG_FIREBALL", "Fire_Ball" } ,
+ { MG_FIREBOLT, "MG_FIREBOLT", "Fire_Bolt" } ,
+ { MG_FIREWALL, "MG_FIREWALL", "Fire_Wall" } ,
+ { MG_FROSTDIVER, "MG_FROSTDIVER", "Frost_Diver" } ,
+ { MG_LIGHTNINGBOLT, "MG_LIGHTNINGBOLT", "Lightening_Bolt" } ,
+ { MG_NAPALMBEAT, "MG_NAPALMBEAT", "Napalm_Beat" } ,
+ { MG_SAFETYWALL, "MG_SAFETYWALL", "Safety_Wall" } ,
+ { MG_SIGHT, "MG_SIGHT", "Sight" } ,
+ { MG_SOULSTRIKE, "MG_SOULSTRIKE", "Soul_Strike" } ,
+ { MG_SRECOVERY, "MG_SRECOVERY", "Increase_SP_Recovery" } ,
+ { MG_STONECURSE, "MG_STONECURSE", "Stone_Curse" } ,
+ { MG_THUNDERSTORM, "MG_THUNDERSTORM", "Thunderstorm" } ,
+ { MO_ABSORBSPIRITS, "MO_ABSORBSPIRITS", "Spiritual_Sphere_Absorption" } ,
+ { MO_BALKYOUNG, "MO_BALKYOUNG", "Ki_Explosion" } ,
+ { MO_BLADESTOP, "MO_BLADESTOP", "Root" } ,
+ { MO_BODYRELOCATION, "MO_BODYRELOCATION", "Snap" } ,
+ { MO_CALLSPIRITS, "MO_CALLSPIRITS", "Summon_Spirit_Sphere" } ,
+ { MO_CHAINCOMBO, "MO_CHAINCOMBO", "Raging_Quadruple_Blow" } ,
+ { MO_COMBOFINISH, "MO_COMBOFINISH", "Raging_Thrust" } ,
+ { MO_DODGE, "MO_DODGE", "Flee" } ,
+ { MO_EXPLOSIONSPIRITS, "MO_EXPLOSIONSPIRITS", "Fury" } ,
+ { MO_EXTREMITYFIST, "MO_EXTREMITYFIST", "Guillotine_Fist" } ,
+ { MO_FINGEROFFENSIVE, "MO_FINGEROFFENSIVE", "Throw_Spirit_Sphere" } ,
+ { MO_INVESTIGATE, "MO_INVESTIGATE", "Occult_Impaction" } ,
+ { MO_IRONHAND, "MO_IRONHAND", "Iron_Fists" } ,
+ { MO_KITRANSLATION, "MO_KITRANSLATION", "Ki_Translation" } ,
+ { MO_SPIRITSRECOVERY, "MO_SPIRITSRECOVERY", "Spiritual_Cadence" } ,
+ { MO_STEELBODY, "MO_STEELBODY", "Mental_Strength" } ,
+ { MO_TRIPLEATTACK, "MO_TRIPLEATTACK", "Raging_Trifecta_Blow" } ,
+ { NPC_AGIUP, "NPC_AGIUP", "NPC_AGIUP" } ,
+ { NPC_ATTRICHANGE, "NPC_ATTRICHANGE", "NPC_ATTRICHANGE" } ,
+ { NPC_BARRIER, "NPC_BARRIER", "NPC_BARRIER" } ,
+ { NPC_BLINDATTACK, "NPC_BLINDATTACK", "NPC_BLINDATTACK" } ,
+ { NPC_BLOODDRAIN, "NPC_BLOODDRAIN", "NPC_BLOODDRAIN" } ,
+ { NPC_BREAKARMOR, "NPC_BREAKARMOR", "NPC_BREAKARMOR" } ,
+ { NPC_BREAKHELM, "NPC_BREAKHELM", "NPC_BREAKHELM" } ,
+ { NPC_BREAKSHIELD, "NPC_BREAKSHIELD", "NPC_BREAKSHIELD" } ,
+ { NPC_BREAKWEAPON, "NPC_BREAKWEAPON", "NPC_BREAKWEAPON" } ,
+ { NPC_CALLSLAVE, "NPC_CALLSLAVE", "NPC_CALLSLAVE" } ,
+ { NPC_CHANGEDARKNESS, "NPC_CHANGEDARKNESS", "NPC_CHANGEDARKNESS" } ,
+ { NPC_CHANGEFIRE, "NPC_CHANGEFIRE", "NPC_CHANGEFIRE" } ,
+ { NPC_CHANGEGROUND, "NPC_CHANGEGROUND", "NPC_CHANGEGROUND" } ,
+ { NPC_CHANGEHOLY, "NPC_CHANGEHOLY", "NPC_CHANGEHOLY" } ,
+ { NPC_CHANGEPOISON, "NPC_CHANGEPOISON", "NPC_CHANGEPOISON" } ,
+ { NPC_CHANGETELEKINESIS, "NPC_CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS" } ,
+ { NPC_CHANGEUNDEAD, "NPC_CHANGEUNDEAD", "NPC_CHANGEUNDEAD" } ,
+ { NPC_CHANGEWATER, "NPC_CHANGEWATER", "NPC_CHANGEWATER" } ,
+ { NPC_CHANGEWIND, "NPC_CHANGEWIND", "NPC_CHANGEWIND" } ,
+ { NPC_COMBOATTACK, "NPC_COMBOATTACK", "NPC_COMBOATTACK" } ,
+ { NPC_CRITICALSLASH, "NPC_CRITICALSLASH", "NPC_CRITICALSLASH" } ,
+ { NPC_CURSEATTACK, "NPC_CURSEATTACK", "NPC_CURSEATTACK" } ,
+ { NPC_DARKBLESSING, "NPC_DARKBLESSING", "NPC_DARKBLESSING" } ,
+ { NPC_DARKBREATH, "NPC_DARKBREATH", "NPC_DARKBREATH" } ,
+ { NPC_DARKCROSS, "NPC_DARKCROSS", "NPC_DARKCROSS" } ,
+ { NPC_DARKNESSATTACK, "NPC_DARKNESSATTACK", "NPC_DARKNESSATTACK" } ,
+ { NPC_DARKSTRIKE, "NPC_DARKSTRIKE", "NPC_DARKSTRIKE" } ,
+ { NPC_DARKTHUNDER, "NPC_DARKTHUNDER", "NPC_DARKTHUNDER" } ,
+ { NPC_DEFENDER, "NPC_DEFENDER", "NPC_DEFENDER" } ,
+ { NPC_EMOTION, "NPC_EMOTION", "NPC_EMOTION" } ,
+ { NPC_EMOTION_ON, "NPC_EMOTION_ON", "NPC_EMOTION_ON" } ,
+ { NPC_ENERGYDRAIN, "NPC_ENERGYDRAIN", "NPC_ENERGYDRAIN" } ,
+ { NPC_FIREATTACK, "NPC_FIREATTACK", "NPC_FIREATTACK" } ,
+ { NPC_GRANDDARKNESS, "NPC_GRANDDARKNESS", "NPC_GRANDDARKNESS" } ,
+ { NPC_GROUNDATTACK, "NPC_GROUNDATTACK", "NPC_GROUNDATTACK" } ,
+ { NPC_GUIDEDATTACK, "NPC_GUIDEDATTACK", "NPC_GUIDEDATTACK" } ,
+ { NPC_HALLUCINATION, "NPC_HALLUCINATION", "NPC_HALLUCINATION" } ,
+ { NPC_HOLYATTACK, "NPC_HOLYATTACK", "NPC_HOLYATTACK" } ,
+ { NPC_INVISIBLE, "NPC_INVISIBLE", "NPC_INVISIBLE" } ,
+ { NPC_KEEPING, "NPC_KEEPING", "NPC_KEEPING" } ,
+ { NPC_LICK, "NPC_LICK", "NPC_LICK" } ,
+ { NPC_MAGICALATTACK, "NPC_MAGICALATTACK", "NPC_MAGICALATTACK" } ,
+ { NPC_MENTALBREAKER, "NPC_MENTALBREAKER", "NPC_MENTALBREAKER" } ,
+ { NPC_METAMORPHOSIS, "NPC_METAMORPHOSIS", "NPC_METAMORPHOSIS" } ,
+ { NPC_PETRIFYATTACK, "NPC_PETRIFYATTACK", "NPC_PETRIFYATTACK" } ,
+ { NPC_PIERCINGATT, "NPC_PIERCINGATT", "NPC_PIERCINGATT" } ,
+ { NPC_POISON, "NPC_POISON", "NPC_POISON" } ,
+ { NPC_POISONATTACK, "NPC_POISONATTACK", "NPC_POISONATTACK" } ,
+ { NPC_POWERUP, "NPC_POWERUP", "NPC_POWERUP" } ,
+ { NPC_PROVOCATION, "NPC_PROVOCATION", "NPC_PROVOCATION" } ,
+ { NPC_RANDOMATTACK, "NPC_RANDOMATTACK", "NPC_RANDOMATTACK" } ,
+ { NPC_RANDOMMOVE, "NPC_RANDOMMOVE", "NPC_RANDOMMOVE" } ,
+ { NPC_RANGEATTACK, "NPC_RANGEATTACK", "NPC_RANGEATTACK" } ,
+ { NPC_REBIRTH, "NPC_REBIRTH", "NPC_REBIRTH" } ,
+ { NPC_REVENGE, "NPC_REVENGE", "NPC_REVENGE" } ,
+ { NPC_RUN, "NPC_RUN", "NPC_RUN" },
+ { NPC_SELFDESTRUCTION, "NPC_SELFDESTRUCTION", "Kabooooom!" } ,
+ { NPC_SIEGEMODE, "NPC_SIEGEMODE", "NPC_SIEGEMODE" } ,
+ { NPC_SILENCEATTACK, "NPC_SILENCEATTACK", "NPC_SILENCEATTACK" } ,
+ { NPC_SLEEPATTACK, "NPC_SLEEPATTACK", "NPC_SLEEPATTACK" } ,
+ { NPC_SMOKING, "NPC_SMOKING", "NPC_SMOKING" } ,
+ { NPC_SPEEDUP, "NPC_SPEEDUP", "NPC_SPEEDUP" } ,
+ { NPC_SPLASHATTACK, "NPC_SPLASHATTACK", "NPC_SPLASHATTACK" } ,
+ { NPC_STOP, "NPC_STOP", "NPC_STOP" } ,
+ { NPC_STUNATTACK, "NPC_STUNATTACK", "NPC_STUNATTACK" } ,
+ { NPC_SUICIDE, "NPC_SUICIDE", "NPC_SUICIDE" } ,
+ { NPC_SUMMONMONSTER, "NPC_SUMMONMONSTER", "NPC_SUMMONMONSTER" } ,
+ { NPC_SUMMONSLAVE, "NPC_SUMMONSLAVE", "NPC_SUMMONSLAVE" } ,
+ { NPC_TELEKINESISATTACK, "NPC_TELEKINESISATTACK", "NPC_TELEKINESISATTACK" } ,
+ { NPC_TRANSFORMATION, "NPC_TRANSFORMATION", "NPC_TRANSFORMATION" } ,
+ { NPC_UNDEADATTACK, "NPC_UNDEADATTACK", "NPC_UNDEADATTACK" } ,
+ { NPC_WATERATTACK, "NPC_WATERATTACK", "NPC_WATERATTACK" } ,
+ { NPC_WINDATTACK, "NPC_WINDATTACK", "NPC_WINDATTACK" } ,
+ { NV_BASIC, "NV_BASIC", "Basic_Skill" } ,
+ { NV_FIRSTAID, "NV_FIRSTAID", "First Aid" } ,
+ { NV_TRICKDEAD, "NV_TRICKDEAD", "Play_Dead" } ,
+ { PA_GOSPEL, "PA_GOSPEL", "Battle_Chant" } ,
+ { PA_PRESSURE, "PA_PRESSURE", "Gloria_Domini" } ,
+ { PA_SACRIFICE, "PA_SACRIFICE", "Martyr's_Reckoning" } ,
+ { PA_SHIELDCHAIN, "PA_SHIELDCHAIN", "Shield_Chain" } ,
+ { PF_DOUBLECASTING, "PF_DOUBLECASTING", "Double_Casting" } ,
+ { PF_FOGWALL, "PF_FOGWALL", "Blinding_Mist" } ,
+ { PF_HPCONVERSION, "PF_HPCONVERSION", "Indulge" } ,
+ { PF_MEMORIZE, "PF_MEMORIZE", "Foresight" } ,
+ { PF_MINDBREAKER, "PF_MINDBREAKER", "Mind_Breaker" } ,
+ { PF_SOULBURN, "PF_SOULBURN", "Soul_Siphon" } ,
+ { PF_SOULCHANGE, "PF_SOULCHANGE", "Soul_Exhale" } ,
+ { PF_SPIDERWEB, "PF_SPIDERWEB", "Fiber_Lock" } ,
+ { PR_ASPERSIO, "PR_ASPERSIO", "Aspersio" } ,
+ { PR_BENEDICTIO, "PR_BENEDICTIO", "B.S_Sacramenti" } ,
+ { PR_GLORIA, "PR_GLORIA", "Gloria" } ,
+ { PR_IMPOSITIO, "PR_IMPOSITIO", "Impositio_Manus" } ,
+ { PR_KYRIE, "PR_KYRIE", "Kyrie_Eleison" } ,
+ { PR_LEXAETERNA, "PR_LEXAETERNA", "Lex_Aeterna" } ,
+ { PR_LEXDIVINA, "PR_LEXDIVINA", "Lex_Divina" } ,
+ { PR_MACEMASTERY, "PR_MACEMASTERY", "Mace_Mastery" } ,
+ { PR_MAGNIFICAT, "PR_MAGNIFICAT", "Magnificat" } ,
+ { PR_MAGNUS, "PR_MAGNUS", "Magnus_Exorcismus" } ,
+ { PR_REDEMPTIO, "PR_REDEMPTIO", "Redemptio" } ,
+ { PR_SANCTUARY, "PR_SANCTUARY", "Sanctuary" } ,
+ { PR_SLOWPOISON, "PR_SLOWPOISON", "Slow_Poison" } ,
+ { PR_STRECOVERY, "PR_STRECOVERY", "Status_Recovery" } ,
+ { PR_SUFFRAGIUM, "PR_SUFFRAGIUM", "Suffragium" } ,
+ { PR_TURNUNDEAD, "PR_TURNUNDEAD", "Turn_Undead" } ,
+ { RG_BACKSTAP, "RG_BACKSTAP", "Back_Stab" } ,
+ { RG_CLEANER, "RG_CLEANER", "Remover" } ,
+ { RG_CLOSECONFINE, "RG_CLOSECONFINE", "Close_Confine"} ,
+ { RG_COMPULSION, "RG_COMPULSION", "Haggle" } ,
+ { RG_FLAGGRAFFITI, "RG_FLAGGRAFFITI", "Piece" } ,
+ { RG_GANGSTER, "RG_GANGSTER", "Slyness" } ,
+ { RG_GRAFFITI, "RG_GRAFFITI", "Scribble" } ,
+ { RG_INTIMIDATE, "RG_INTIMIDATE", "Snatch" } ,
+ { RG_PLAGIARISM, "RG_PLAGIARISM", "Intimidate" } ,
+ { RG_RAID, "RG_RAID", "Sightless_Mind" } ,
+ { RG_SNATCHER, "RG_SNATCHER", "Gank" } ,
+ { RG_STEALCOIN, "RG_STEALCOIN", "Mug" } ,
+ { RG_STRIPARMOR, "RG_STRIPARMOR", "Divest_Armor" } ,
+ { RG_STRIPHELM, "RG_STRIPHELM", "Divest_Helm" } ,
+ { RG_STRIPSHIELD, "RG_STRIPSHIELD", "Divest_Shield" } ,
+ { RG_STRIPWEAPON, "RG_STRIPWEAPON", "Divest_Weapon" } ,
+ { RG_TUNNELDRIVE, "RG_TUNNELDRIVE", "Stalk" } ,
+ { SA_ABRACADABRA, "SA_ABRACADABRA", "Hocus-pocus" } ,
+ { SA_ADVANCEDBOOK, "SA_ADVANCEDBOOK", "Advanced_Book" } ,
+ { SA_AUTOSPELL, "SA_AUTOSPELL", "Hindsight" } ,
+ { SA_CASTCANCEL, "SA_CASTCANCEL", "Cast_Cancel" } ,
+ { SA_CLASSCHANGE, "SA_CLASSCHANGE", "Class_Change" } ,
+ { SA_CREATECON, "SA_CREATECON", "Create_Elemental_Converter" } ,
+ { SA_COMA, "SA_COMA", "Coma" } ,
+ { SA_DEATH, "SA_DEATH", "Grim_Reaper" } ,
+ { SA_DELUGE, "SA_DELUGE", "Deluge" } ,
+ { SA_DISPELL, "SA_DISPELL", "Dispell" } ,
+ { SA_DRAGONOLOGY, "SA_DRAGONOLOGY", "Dragonology" } ,
+ { SA_ELEMENTFIRE, "SA_ELEMENTFIRE", "Elemental_Change_Fire" } ,
+ { SA_ELEMENTGROUND, "SA_ELEMENTGROUND", "Elemental_Change_Earth" } ,
+ { SA_ELEMENTWATER, "SA_ELEMENTWATER", "Elemental_Change_Water" } ,
+ { SA_ELEMENTWIND, "SA_ELEMENTWIND", "Elemental_Change_Wind" } ,
+ { SA_FLAMELAUNCHER, "SA_FLAMELAUNCHER", "Endow_Blaze" } ,
+ { SA_FORTUNE, "SA_FORTUNE", "Gold_Digger" } ,
+ { SA_FREECAST, "SA_FREECAST", "Free_Cast" } ,
+ { SA_FROSTWEAPON, "SA_FROSTWEAPON", "Endow_Tsunami" } ,
+ { SA_FULLRECOVERY, "SA_FULLRECOVERY", "Rejuvenation" } ,
+ { SA_GRAVITY, "SA_GRAVITY", "Gravity" } ,
+ { SA_INSTANTDEATH, "SA_INSTANTDEATH", "Suicide" } ,
+ { SA_LANDPROTECTOR, "SA_LANDPROTECTOR", "Magnetic_Earth" } ,
+ { SA_LEVELUP, "SA_LEVELUP", "Leveling" } ,
+ { SA_LIGHTNINGLOADER, "SA_LIGHTNINGLOADER", "Endow_Tornado" } ,
+ { SA_MAGICROD, "SA_MAGICROD", "Magic_Rod" } ,
+ { SA_MONOCELL, "SA_MONOCELL", "Mono_Cell" } ,
+ { SA_QUESTION, "SA_QUESTION", "Questioning" } ,
+ { SA_REVERSEORCISH, "SA_REVERSEORCISH", "Grampus_Morph" } ,
+ { SA_SEISMICWEAPON, "SA_SEISMICWEAPON", "Endow_Quake" } ,
+ { SA_SPELLBREAKER, "SA_SPELLBREAKER", "Spell_Breaker" } ,
+ { SA_SUMMONMONSTER, "SA_SUMMONMONSTER", "Monster_Chant" } ,
+ { SA_TAMINGMONSTER, "SA_TAMINGMONSTER", "Beastly_Hypnosis" } ,
+ { SA_VIOLENTGALE, "SA_VIOLENTGALE", "Whirlwind" } ,
+ { SA_VOLCANO, "SA_VOLCANO", "Volcano" } ,
+ { SG_DEVIL, "SG_DEVIL", "Devil_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_FEEL, "SG_FEEL", "Feeling_of_the_Sun,_Moon_and_Star" } ,
+ { SG_FRIEND, "SG_FRIEND", "Companion_of_the_Sun_and_Moon" } ,
+ { SG_FUSION, "SG_FUSION", "Union_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_HATE, "SG_HATE", "Hatred_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_KNOWLEDGE, "SG_KNOWLEDGE", "Knowledge_of_the_Sun,_Moon_and_Stars" } ,
+ { SG_MOON_ANGER, "SG_MOON_ANGER", "Fury_of_the_Moon" } ,
+ { SG_MOON_BLESS, "SG_MOON_BLESS", "Bless_of_the_Moon" } ,
+ { SG_MOON_COMFORT, "SG_MOON_COMFORT", "Comfort_of_the_Moon" } ,
+ { SG_MOON_WARM, "SG_MOON_WARM", "Warmth_of_the_Moon" } ,
+ { SG_STAR_ANGER, "SG_STAR_ANGER", "Fury_of_the_Stars" } ,
+ { SG_STAR_BLESS, "SG_STAR_BLESS", "Bless_of_the_Stars" } ,
+ { SG_STAR_COMFORT, "SG_STAR_COMFORT", "Comfort_of_the_Stars" } ,
+ { SG_STAR_WARM, "SG_STAR_WARM", "Warmth_of_the_Stars" } ,
+ { SG_SUN_ANGER, "SG_SUN_ANGER", "Fury_of_the_Sun" } ,
+ { SG_SUN_BLESS, "SG_SUN_BLESS", "Bless_of_the_Sun" } ,
+ { SG_SUN_COMFORT, "SG_SUN_COMFORT", "Comfort_of_the_Sun" } ,
+ { SG_SUN_WARM, "SG_SUN_WARM", "Warmth_of_the_Sun" } ,
+ { SL_ALCHEMIST, "SL_ALCHEMIST", "Spirit_of_Alchemist" } ,
+ { SL_ASSASIN, "SL_ASSASIN", "Spirit_of_Assassin" } ,
+ { SL_BARDDANCER, "SL_BARDDANCER", "Spirit_of_Bard_and_Dancer" } ,
+ { SL_BLACKSMITH, "SL_BLACKSMITH", "Spirit_of_Blacksmith" } ,
+ { SL_CRUSADER, "SL_CRUSADER", "Spirit_of_Crusader" } ,
+ { SL_HIGH, "SL_HIGH", "Spirit_of_Advanced_1st_Class" } ,
+ { SL_HUNTER, "SL_HUNTER", "Spirit_of_Hunter" } ,
+ { SL_KAAHI, "SL_KAAHI", "Kaahi" } ,
+ { SL_KAINA, "SL_KAINA", "Kaina" } ,
+ { SL_KAITE, "SL_KAITE", "Kaite" } ,
+ { SL_KAIZEL, "SL_KAIZEL", "Kaizel" } ,
+ { SL_KAUPE, "SL_KAUPE", "Kaupe" } ,
+ { SL_KNIGHT, "SL_KNIGHT", "Spirit_of_Knight" } ,
+ { SL_MONK, "SL_MONK", "Spirit_of_Monk" } ,
+ { SL_PRIEST, "SL_PRIEST", "Spirit_of_Priest" } ,
+ { SL_ROGUE, "SL_ROGUE", "Spirit_of_Rogue" } ,
+ { SL_SAGE, "SL_SAGE", "Spirit_of_Sage" } ,
+ { SL_SKA, "SL_SKA", "Eska" } ,
+ { SL_SKE, "SL_SKE", "Eske" } ,
+ { SL_SMA, "SL_SMA", "Esma" } ,
+ { SL_SOULLINKER, "SL_SOULLINKER", "Spirit_of_Soul_Linker" } ,
+ { SL_STAR, "SL_STAR", "Spirit_of_Stars" } ,
+ { SL_STIN, "SL_STIN", "Estin" } ,
+ { SL_STUN, "SL_STUN", "Estun" } ,
+ { SL_SUPERNOVICE, "SL_SUPERNOVICE", "Spirit_of_Super_Novice" } ,
+ { SL_SWOO, "SL_SWOO", "Eswoo" } ,
+ { SL_WIZARD, "SL_WIZARD", "Spirit_of_Wizard" } ,
+ { SM_AUTOBERSERK, "SM_AUTOBERSERK", "Berserk" } ,
+ { SM_BASH, "SM_BASH", "Bash" } ,
+ { SM_ENDURE, "SM_ENDURE", "Endure" } ,
+ { SM_FATALBLOW, "SM_FATALBLOW", "Fatal_Blow" } ,
+ { SM_MAGNUM, "SM_MAGNUM", "Magnum_Break" } ,
+ { SM_MOVINGRECOVERY, "SM_MOVINGRECOVERY", "HP_Recovery_While_Moving" } ,
+ { SM_PROVOKE, "SM_PROVOKE", "Provoke" } ,
+ { SM_RECOVERY, "SM_RECOVERY", "Increase_HP_Recovery" } ,
+ { SM_SWORD, "SM_SWORD", "Sword_Mastery" } ,
+ { SM_TWOHAND, "SM_TWOHAND", "Two-Handed_Sword_Mastery" } ,
+ { SN_FALCONASSAULT, "SN_FALCONASSAULT", "Falcon_Assault" } ,
+ { SN_SHARPSHOOTING, "SN_SHARPSHOOTING", "Focused_Arrow_Strike" } ,
+ { SN_SIGHT, "SN_SIGHT", "Falcon_Eyes" } ,
+ { SN_WINDWALK, "SN_WINDWALK", "Wind_Walker" } ,
+ { ST_CHASEWALK, "ST_CHASEWALK", "Stealth" } ,
+ { ST_REJECTSWORD, "ST_REJECTSWORD", "Counter_Instinct" } ,
+ { ST_PRESERVE, "ST_PRESERVE", "Preserve" } ,
+ { ST_FULLSTRIP, "ST_FULLSTRIP", "Full_Divestment" } ,
+ { TF_BACKSLIDING, "TF_BACKSLIDING", "Back_Slide" } ,
+ { TF_DETOXIFY, "TF_DETOXIFY", "Detoxify" } ,
+ { TF_DOUBLE, "TF_DOUBLE", "Double_Attack" } ,
+ { TF_HIDING, "TF_HIDING", "Hiding" } ,
+ { TF_MISS, "TF_MISS", "Improve_Dodge" } ,
+ { TF_PICKSTONE, "TF_PICKSTONE", "Find_Stone" } ,
+ { TF_POISON, "TF_POISON", "Envenom" } ,
+ { TF_SPRINKLESAND, "TF_SPRINKLESAND", "Sand_Attack" } ,
+ { TF_STEAL, "TF_STEAL", "Steal" } ,
+ { TF_THROWSTONE, "TF_THROWSTONE", "Stone_Fling" } ,
+ { TK_COUNTER, "TK_COUNTER", "Spin_Kick" } ,
+ { TK_DODGE, "TK_DODGE", "Sprint" } ,
+ { TK_DOWNKICK, "TK_DOWNKICK", "Heel_Drop" } ,
+ { TK_HIGHJUMP, "TK_HIGHJUMP", "Taekwon_Jump" } ,
+ { TK_HPTIME, "TK_HPTIME", "Peaceful_Break" } ,
+ { TK_JUMPKICK, "TK_JUMPKICK", "Flying_Kick" } ,
+ { TK_MISSION, "TK_MISSION", "Mission" } ,
+ { TK_POWER, "TK_POWER", "Kihop" } ,
+ { TK_READYCOUNTER, "TK_READYCOUNTER", "Spin_Kick_Stance" } ,
+ { TK_READYDOWN, "TK_READYDOWN", "Heel_Drop_Stance" } ,
+ { TK_READYSTORM, "TK_READYSTORM", "Tornado_Stance" } ,
+ { TK_READYTURN, "TK_READYTURN", "Roundhouse_Stance" } ,
+ { TK_RUN, "TK_RUN", "Sprint" } ,
+ { TK_SEVENWIND, "TK_SEVENWIND", "Mild_Wind" } ,
+ { TK_SPTIME, "TK_SPTIME", "Happy_Break" } ,
+ { TK_STORMKICK, "TK_STORMKICK", "Storm_Kick" } ,
+ { TK_TURNKICK, "TK_TURNKICK", "Turn_Kick" } ,
+ { WE_BABY, "WE_BABY", "Mom,_Dad,_I_love_you!" } ,
+ { WE_CALLBABY, "WE_CALLBABY", "Come_to_me,_honey~" } ,
+ { WE_CALLPARENT, "WE_CALLPARENT", "Mom,_Dad,_I_miss_you!" } ,
+ { WE_CALLPARTNER, "WE_CALLPARTNER", "Romantic_Rendezvous" } ,
+ { WE_FEMALE, "WE_FEMALE", "Loving_Touch" } ,
+ { WE_MALE, "WE_MALE", "Undying_Love" } ,
+ { WS_CARTBOOST, "WS_CARTBOOST", "Cart_Boost" } ,
+ { WS_CARTTERMINATION, "WS_CARTTERMINATION", "Cart_Termination" } ,
+ { WS_CREATECOIN, "WS_CREATECOIN", "Coin_Craft" } ,
+ { WS_CREATENUGGET, "WS_CREATENUGGET", "Nugget_Craft" } ,
+ { WS_MELTDOWN, "WS_MELTDOWN", "Shattering_Strike" } ,
+ { WS_OVERTHRUSTMAX, "WS_OVERTHRUSTMAX", "Max_Power-Thust" } ,
+ { WS_SYSTEMCREATE, "WS_SYSTEMCREATE", "Auto_Attacking_Machine_Craft" } ,
+ { WS_WEAPONREFINE, "WS_WEAPONREFINE", "Weapon_Refine" } ,
+ { WZ_EARTHSPIKE, "WZ_EARTHSPIKE", "Earth_Spike" } ,
+ { WZ_ESTIMATION, "WZ_ESTIMATION", "Sense" } ,
+ { WZ_FIREPILLAR, "WZ_FIREPILLAR", "Fire_Pillar" } ,
+ { WZ_FROSTNOVA, "WZ_FROSTNOVA", "Frost_Nova" } ,
+ { WZ_HEAVENDRIVE, "WZ_HEAVENDRIVE", "Heaven's_Drive" } ,
+ { WZ_ICEWALL, "WZ_ICEWALL", "Ice_Wall" } ,
+ { WZ_JUPITEL, "WZ_JUPITEL", "Jupitel_Thunder" } ,
+ { WZ_METEOR, "WZ_METEOR", "Meteor_Storm" } ,
+ { WZ_QUAGMIRE, "WZ_QUAGMIRE", "Quagmire" } ,
+ { WZ_SIGHTBLASTER, "WZ_SIGHTBLASTER", "Sight_Blaster" } ,
+ { WZ_SIGHTRASHER, "WZ_SIGHTRASHER", "Sightrasher" } ,
+ { WZ_STORMGUST, "WZ_STORMGUST", "Storm_Gust" } ,
+ { WZ_VERMILION, "WZ_VERMILION", "Lord_of_Vermilion" } ,
+ { WZ_WATERBALL, "WZ_WATERBALL", "Water_Ball" } ,
+ { 0, "UNKNOWN_SKILL", "Unknown_Skill" }
+};
+
+static const int dirx[8]={0,-1,-1,-1,0,1,1,1};
+static const int diry[8]={1,1,0,-1,-1,-1,0,1};
+
+static int rdamage;
+
+/* ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+struct skill_db skill_db[MAX_SKILL_DB];
+
+/* ƒAƒCƒeƒ€?ì?¬ƒf?ƒ^ƒx?ƒX */
+struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+
+/* –î?ì?¬ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+
+/* ƒAƒuƒ‰ƒJƒ_ƒuƒ‰?“®ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+
+// macros to check for out of bounds errors [celest]
+// i: Skill ID, l: Skill Level, var: Value to return after checking
+// for values that don't require level just put a one (putting 0 will trigger return 0; instead
+// for values that might need to use a different function just skill_chk would suffice.
+#define skill_chk(i, l) \
+ if (i >= 10000 && i < 10015) {i -= 9500;} \
+ if (i < 1 || i > MAX_SKILL_DB) {return 0;} \
+ if (l <= 0 || l > MAX_SKILL_LEVEL) {return 0;}
+#define skill_get(var, i, l) \
+ { skill_chk(i, l); return var; }
+
+// Skill DB
+int skill_get_hit( int id ){ skill_get (skill_db[id].hit, id, 1); }
+int skill_get_inf( int id ){ skill_chk (id, 1); return (id < 500 || id > 1000) ? skill_db[id].inf : guild_skill_get_inf(id); }
+int skill_get_pl( int id ){ skill_get (skill_db[id].pl, id, 1); }
+int skill_get_nk( int id ){ skill_get (skill_db[id].nk, id, 1); }
+int skill_get_max( int id ){ skill_chk (id, 1); return (id < 500 || id > 1000) ? skill_db[id].max : guild_skill_get_max(id); }
+int skill_get_range( int id , int lv ){ skill_chk (id, lv); return (id < 500 || id > 1000) ? skill_db[id].range[lv-1] : 0; }
+int skill_get_hp( int id ,int lv ){ skill_get (skill_db[id].hp[lv-1], id, lv); }
+int skill_get_sp( int id ,int lv ){ skill_get (skill_db[id].sp[lv-1], id, lv); }
+int skill_get_zeny( int id ,int lv ){ skill_get (skill_db[id].zeny[lv-1], id, lv); }
+int skill_get_num( int id ,int lv ){ skill_get (skill_db[id].num[lv-1], id, lv); }
+int skill_get_cast( int id ,int lv ){ skill_get (skill_db[id].cast[lv-1], id, lv); }
+int skill_get_delay( int id ,int lv ){ skill_get (skill_db[id].delay[lv-1], id, lv); }
+int skill_get_time( int id ,int lv ){ skill_get (skill_db[id].upkeep_time[lv-1], id, lv); }
+int skill_get_time2( int id ,int lv ){ skill_get (skill_db[id].upkeep_time2[lv-1], id, lv); }
+int skill_get_castdef( int id ){ skill_get (skill_db[id].cast_def_rate, id, 1); }
+int skill_get_weapontype( int id ){ skill_get (skill_db[id].weapon, id, 1); }
+int skill_get_inf2( int id ){ skill_get (skill_db[id].inf2, id, 1); }
+int skill_get_castcancel( int id ){ skill_get (skill_db[id].castcancel, id, 1); }
+int skill_get_maxcount( int id ){ skill_get (skill_db[id].maxcount, id, 1); }
+int skill_get_blewcount( int id ,int lv ){ skill_get (skill_db[id].blewcount[lv-1], id, lv); }
+int skill_get_mhp( int id ,int lv ){ skill_get (skill_db[id].mhp[lv-1], id, lv); }
+int skill_get_castnodex( int id ,int lv ){ skill_get (skill_db[id].castnodex[lv-1], id, lv); }
+int skill_get_delaynodex( int id ,int lv ){ skill_get (skill_db[id].delaynodex[lv-1], id, lv); }
+int skill_get_delaynowalk( int id ,int lv ){ skill_get (skill_db[id].delaynowalk[lv-1], id, lv); }
+int skill_get_nocast ( int id ){ skill_get (skill_db[id].nocast, id, 1); }
+int skill_get_type( int id ){ skill_get (skill_db[id].skill_type, id, 1); }
+int skill_get_unit_id ( int id, int flag ){ skill_get (skill_db[id].unit_id[flag], id, 1); }
+int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); }
+int skill_get_unit_interval( int id ){ skill_get (skill_db[id].unit_interval, id, 1); }
+int skill_get_unit_range( int id ){ skill_get (skill_db[id].unit_range, id, 1); }
+int skill_get_unit_target( int id ){ skill_get ((skill_db[id].unit_target&BCT_ALL), id, 1); }
+int skill_get_unit_bl_target( int id ){ skill_get ((skill_db[id].unit_target&BL_ALL), id, 1); }
+int skill_get_unit_flag( int id ){ skill_get (skill_db[id].unit_flag, id, 1); }
+const char* skill_get_name( int id ){
+ if (id >= 10000 && id < 10015) id -= 9500;
+ if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL) return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string.
+ return skill_db[id].name;
+}
+
+int skill_tree_get_max(int id, int b_class){
+ int i, skillid;
+ for(i=0;(skillid=skill_tree[b_class][i].id)>0;i++)
+ if (id == skillid) return skill_tree[b_class][i].max;
+ return skill_get_max (id);
+}
+
+/* ƒvƒ?ƒgƒ^ƒCƒv */
+int skill_check_condition( struct map_session_data *sd,int type);
+int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_frostjoke_scream(struct block_list *bl,va_list ap);
+int status_change_timer_sub(struct block_list *bl, va_list ap);
+int skill_attack_area(struct block_list *bl,va_list ap);
+int skill_clear_element_field(struct block_list *bl);
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
+int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris]
+int skill_greed(struct block_list *bl, va_list ap);
+int skill_landprotector(struct block_list *bl, va_list ap);
+int skill_ganbatein(struct block_list *bl, va_list ap);
+int skill_trap_splash(struct block_list *bl, va_list ap);
+int skill_count_target(struct block_list *bl, va_list ap);
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick);
+static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick);
+static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick);
+int skill_unit_effect(struct block_list *bl,va_list ap);
+int skill_castend_delay (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag);
+static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv);
+static int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag);
+
+int enchant_eff[5] = { 10, 14, 17, 19, 20 };
+int deluge_eff[5] = { 5, 9, 12, 14, 15 };
+
+//Returns actual skill range taking into account attack range and AC_OWL [Skotlex]
+int skill_get_range2(struct block_list *bl, int id, int lv) {
+ int range = skill_get_range(id, lv);
+ if(range < 0) {
+ if (battle_config.use_weapon_skill_range)
+ return status_get_range(bl);
+ range *=-1;
+ }
+ //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE.
+ if (id == AC_SHOWER || id == AC_DOUBLE || id == HT_BLITZBEAT || id == AC_CHARGEARROW
+ || id == SN_FALCONASSAULT || id == SN_SHARPSHOOTING || id == HT_POWER) {
+ if (bl->type == BL_PC)
+ range += pc_checkskill((struct map_session_data *)bl, AC_VULTURE);
+ else
+ range += 10; //Assume level 10?
+ }
+ return range;
+}
+
+// Making plagiarize check its own function [Aru]
+int can_copy(struct map_session_data *sd, int skillid)
+{
+ // Never copy NPC/Wedding Skills
+ if (skill_get_inf2(skillid)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
+ return 0;
+
+ // High-class skills
+ if((skillid >= LK_AURABLADE && skillid <= ASC_CDP) || (skillid >= ST_PRESERVE && skillid <= CR_CULTIVATION))
+ {
+ if(battle_config.copyskill_restrict == 2)
+ return 0;
+ else if(battle_config.copyskill_restrict)
+ return (sd->status.class_ == JOB_STALKER);
+ }
+
+ return 1;
+}
+
+// [MouseJstr] - skill ok to cast? and when?
+int skillnotok(int skillid, struct map_session_data *sd)
+{
+ nullpo_retr (1, sd);
+ //if (sd == 0)
+ //return 0;
+ //return 1;
+ // I think it was meant to be "no skills allowed when not a valid sd"
+
+ if (!(skillid >= 10000 && skillid < 10015))
+ if ((skillid > MAX_SKILL) || (skillid < 0))
+ return 1;
+
+ {
+ int i = skillid;
+ if (i >= 10000 && i < 10015)
+ i -= 9500;
+ if (sd->blockskill[i] > 0)
+ return 1;
+ }
+
+ if (pc_isGM(sd) >= 20)
+ return 0; // gm's can do anything damn thing they want
+
+ // Check skill restrictions [Celest]
+ if(!map_flag_vs(sd->bl.m) && skill_get_nocast (skillid) & 1)
+ return 1;
+ if(map[sd->bl.m].flag.pvp && skill_get_nocast (skillid) & 2)
+ return 1;
+ if(map_flag_gvg(sd->bl.m) && skill_get_nocast (skillid) & 4)
+ return 1;
+ if (agit_flag && skill_get_nocast (skillid) & 8)
+ return 1;
+ if (battle_config.pk_mode && !map[sd->bl.m].flag.nopvp && skill_get_nocast (skillid) & 16)
+ return 1;
+
+ switch (skillid) {
+ case AL_WARP:
+ case AL_TELEPORT:
+ case MC_VENDING:
+ case MC_IDENTIFY:
+ return 0; // always allowed
+ default:
+ return (map[sd->bl.m].flag.noskill);
+ }
+}
+
+/* ƒXƒLƒ‹ƒ†ƒjƒbƒg‚Ì”z’u?î•ñ‚ð•Ô‚· */
+struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
+int firewall_unit_pos;
+int icewall_unit_pos;
+
+struct skill_unit_layout *skill_get_unit_layout (int skillid, int skilllv, struct block_list *src, int x, int y)
+{
+ int pos = skill_get_unit_layout_type(skillid,skilllv);
+ int dir;
+
+ if (pos != -1)
+ return &skill_unit_layout[pos];
+
+ if (src->x == x && src->y == y)
+ dir = 2;
+ else
+ dir = map_calc_dir(src,x,y);
+
+ if (skillid == MG_FIREWALL)
+ return &skill_unit_layout [firewall_unit_pos + dir];
+ else if (skillid == WZ_ICEWALL)
+ return &skill_unit_layout [icewall_unit_pos + dir];
+
+ ShowError("Unknown unit layout for skill %d, %d\n",skillid,skilllv);
+ return &skill_unit_layout[0];
+}
+
+/*==========================================
+ * ƒXƒLƒ‹’ljÁ?‰Ê
+ *------------------------------------------
+ */
+int skill_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick)
+{
+ /* MOB’ljÁ?‰ÊƒXƒLƒ‹—p */
+ const int sc[]={
+ SC_POISON, SC_BLIND, SC_SILENCE, SC_STAN,
+ SC_STONE, SC_CURSE, SC_SLEEP
+ };
+ const int sc2[]={ //Note: We use Sonic Blow's stun duration for the confusion lasting time (dummy value): 12 secs at lv7
+ MG_STONECURSE,MG_FROSTDIVER,NPC_STUNATTACK,
+ NPC_SLEEPATTACK,TF_POISON,NPC_CURSEATTACK,
+ NPC_SILENCEATTACK,AS_SONICBLOW,NPC_BLINDATTACK,
+ LK_HEADCRUSH
+ };
+
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+ struct mob_data *md=NULL;
+ struct mob_data *dstmd=NULL;
+ struct pet_data *pd=NULL;
+
+ int skill,skill2;
+ int rate;
+
+ int sc_def_mdef,sc_def_vit,sc_def_int,sc_def_luk;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,attack_type,tick);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest
+
+ switch (src->type) {
+ case BL_PC:
+ sd = (struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md = (struct mob_data *)src;
+ break;
+ case BL_PET:
+ pd = (struct pet_data *)src; // [Valaris]
+ break;
+ }
+
+ switch (bl->type) {
+ case BL_PC:
+ dstsd=(struct map_session_data *)bl;
+ break;
+ case BL_MOB:
+ dstmd=(struct mob_data *)bl;
+ break;
+ default:
+ return 0;
+ }
+
+ //??Û‚Ì‘Ï?«
+ sc_def_mdef = status_get_sc_def_mdef(bl);
+ sc_def_vit = status_get_sc_def_vit(bl);
+ sc_def_int = status_get_sc_def_int(bl);
+ sc_def_luk = status_get_sc_def_luk(bl);
+
+ switch(skillid){
+ case 0: // Normal attacks (no skill used)
+ {
+ struct status_change *sc_data;
+ struct status_change *tsc_data;
+ if(sd) {
+ // Automatic trigger of Blitz Beat
+ if (pc_isfalcon(sd) && sd->status.weapon == 11 && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 &&
+ rand()%1000 <= sd->paramc[5]*10/3+1 ) {
+ int lv=(sd->status.job_level+9)/10;
+ skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<lv)?skill:lv,tick,0xf00000);
+ }
+ // Gank
+ if(dstmd && !dstmd->state.steal_flag && sd->status.weapon != 11 && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 &&
+ (skill*15 + 55) + (skill2 = pc_checkskill(sd,TF_STEAL))*10 > rand()%1000) {
+ if(pc_steal_item(sd,bl))
+ clif_skill_nodamage(src,bl,TF_STEAL,skill2,1);
+ else if (battle_config.display_snatcher_skill_fail)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ // Chance to trigger Taekwon kicks [Dralnu]
+ if(sd->sc_data[SC_READYSTORM].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_STORMKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYDOWN].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_DOWNKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYTURN].timer != -1 && sd->sc_data[SC_COMBO].timer == -1 && rand()%100 < 15) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_TURNKICK,0,0,0,rate,0);
+ } else if(sd->sc_data[SC_READYCOUNTER].timer != -1 && sd->sc_data[SC_COMBO].timer == -1) //additional chance from SG_FRIEND [Komurka]
+ {
+ rate = 20;
+ if (sd->sc_data[SC_SKILLRATE_UP].timer != -1 && sd->sc_data[SC_SKILLRATE_UP].val1 == TK_COUNTER) {
+ rate += rate*sd->sc_data[SC_SKILLRATE_UP].val2/100;
+ status_change_end(src,SC_SKILLRATE_UP,-1);
+ }
+ if (rand()%100 < rate) {
+ rate = 2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ status_change_start(src,SC_COMBO, TK_COUNTER,bl->id,0,0,rate,0);
+ }
+ }
+ }
+
+ sc_data = status_get_sc_data(src);
+ tsc_data = status_get_sc_data(bl);
+
+ // Enchant Poison gives a chance to poison attacked enemies
+ if(sc_data && sc_data[SC_ENCPOISON].timer != -1 && tsc_data && tsc_data[SC_POISON].timer == -1 &&
+ rand() % 100 < sc_data[SC_ENCPOISON].val1 * sc_def_vit / 100)
+ status_change_start(bl,SC_POISON,sc_data[SC_ENCPOISON].val1,0,0,0,skill_get_time2(AS_ENCHANTPOISON,sc_data[SC_ENCPOISON].val1),0);
+ // Enchant Deadly Poison gives a chance to deadly poison attacked enemies
+ if(sc_data && sc_data[SC_EDP].timer != -1 && !(status_get_mode(bl)&MD_BOSS) && tsc_data && tsc_data[SC_DPOISON].timer == -1 &&
+ rand() % 100 < tsc_data[SC_EDP].val2 * sc_def_vit / 100)
+ status_change_start(bl,SC_DPOISON,sc_data[SC_EDP].val1,0,0,0,skill_get_time2(ASC_EDP,sc_data[SC_EDP].val1),0);
+
+ if (tsc_data && tsc_data[SC_KAAHI].timer != -1) {
+ if (dstsd && dstsd->status.sp < 5*tsc_data[SC_KAAHI].val1)
+ ; //Not enough SP to cast
+ else {
+ rate = battle_heal(bl, bl, 200*tsc_data[SC_KAAHI].val1, -5*tsc_data[SC_KAAHI].val1, 1);
+ if(dstsd && dstsd->fd)
+ clif_heal(dstsd->fd,SP_HP,rate);
+ }
+ }
+ }
+ break;
+
+ case SM_BASH: /* ƒoƒbƒVƒ…?i‹}?Š?U??j */
+ if( sd && skilllv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){
+ if( rand()%100 < (5*(skilllv-5)+(int)sd->status.base_level/10)*sc_def_vit/100 ) //TODO: How much % per base level it actually is?
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(SM_FATALBLOW,skilllv),0);
+ }
+ break;
+
+ case AS_VENOMKNIFE:
+ if (sd) //Poison chance must be that of Envenom. [Skotlex]
+ skilllv = pc_checkskill(sd, TF_POISON);
+ case TF_POISON: /* ƒCƒ“ƒxƒiƒ€ */
+ case AS_SPLASHER: /* ƒxƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ? */
+ if(rand()%100< (2*skilllv+10)*sc_def_vit/100 )
+ status_change_start(bl,SC_POISON,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ else{
+ if(sd && skillid==TF_POISON)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case AS_SONICBLOW: /* ƒ\ƒjƒbƒNƒuƒ?? */
+ if( rand()%100 < (2*skilllv+10)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case AS_GRIMTOOTH:
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sd)
+ {
+ if (sc_data && sc_data[SC_SLOWDOWN].timer == -1)
+ status_change_start(bl,SC_SLOWDOWN,0,0,0,0,skill_get_time2(skillid, skilllv),0);
+ }
+ else
+ if (sc_data && sc_data[SC_STOP].timer == -1)
+ status_change_start(bl,SC_STOP,0,0,0,0,skill_get_time2(skillid, skilllv), 0);
+ break;
+ }
+ case MG_FROSTDIVER: /* ƒtƒ?ƒXƒgƒ_ƒCƒo? */
+ case WZ_FROSTNOVA: /* ƒtƒ?ƒXƒgƒmƒ”ƒ@ */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ rate = (skilllv*3+35)*sc_def_mdef/100-(status_get_int(bl)+status_get_luk(bl))/15;
+ if (rate <= 5)
+ rate = 5;
+ if(sc_data && sc_data[SC_FREEZE].timer == -1 && rand()%100 < rate)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv)*(1-sc_def_mdef/100),0);
+ }
+ break;
+
+ case WZ_STORMGUST: /* ƒXƒg?ƒ€ƒKƒXƒg */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if(sc_data) {
+ sc_data[SC_FREEZE].val3++;
+ if(sc_data[SC_FREEZE].val3 >= 3)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ }
+ break;
+
+ case WZ_METEOR:
+ if(rand()%100 < 3*skilllv*sc_def_vit/100)
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case WZ_VERMILION:
+ if(rand()%100 < 4*skilllv*sc_def_int/100)
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_FREEZINGTRAP: /* ƒtƒŠ?ƒWƒ“ƒOƒgƒ‰ƒbƒv */
+ if(rand()%100 < (3*skilllv+35)*sc_def_mdef/100)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_FLASHER: /* Flasher */
+ if (!(status_get_mode(bl) & (MD_BOSS|MD_PLANT)) &&
+ rand()%100 < (10*skilllv+30)*sc_def_int/100)
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_LANDMINE: /* ƒ‰ƒ“ƒhƒ}ƒCƒ“ */
+ if( rand()%100 < (5*skilllv+30)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case HT_SHOCKWAVE: //it can't affect mobs, because they have no SP...
+ if(dstsd){
+ dstsd->status.sp -= dstsd->status.sp*(15*skilllv+5)/100;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ case HT_SANDMAN: /* ƒTƒ“ƒhƒ}ƒ“ */
+ if( rand()%100 < (10*skilllv+40)*sc_def_int/100 )
+ status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case TF_SPRINKLESAND: /* ?»‚Ü‚« */
+ if( rand()%100 < 20*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case TF_THROWSTONE: /* ?ΓŠ‚° */
+ if( rand()%100 < 3*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if( rand()%100 < 3*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS: /* ƒz?ƒŠ?ƒNƒƒX */
+ if( rand()%100 < 3*skilllv*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case CR_GRANDCROSS: /* ƒOƒ‰ƒ“ƒhƒNƒ?ƒX */
+ case NPC_GRANDDARKNESS: /*ˆÅƒOƒ‰ƒ“ƒhƒNƒ?ƒX*/
+ {
+ int race = status_get_race(bl);
+ if( (battle_check_undead(race,status_get_elem_type(bl)) || race == 6) && rand()%100 < 100000*sc_def_int/100) //??§•t?‚¾‚ªŠ®‘S‘Ï?«‚É‚Í–³?
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ break;
+
+ case AM_ACIDTERROR:
+ if (rand()%100 < (skilllv*3)*sc_def_vit/100 ) {
+ status_change_start(bl,SC_BLEEDING,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ if (dstsd && rand()%100 < skill_get_time(skillid,skilllv) * battle_config.equip_skill_break_rate / 100) { //fixed
+ if(pc_breakarmor(dstsd))
+ clif_emotion(bl,23);
+ }
+
+ break;
+
+ case AM_DEMONSTRATION:
+ if (dstsd && rand()%10000 < skilllv * battle_config.equip_skill_break_rate )
+ pc_breakweapon(dstsd);
+ break;
+
+ case CR_SHIELDCHARGE: /* ƒV?ƒ‹ƒhƒ`ƒƒ?ƒW */
+ if( rand()%100 < (15 + skilllv*5)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PA_PRESSURE: /* ƒvƒŒƒbƒVƒƒ? */
+ if (dstsd) {
+ dstsd->status.sp -= dstsd->status.sp * (15 + 5 * skilllv) / 100;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ case RG_RAID: /* ƒTƒvƒ‰ƒCƒYƒAƒ^ƒbƒN */
+ if( rand()%100 < (10+3*skilllv)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if( rand()%100 < (10+3*skilllv)*sc_def_int/100 )
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case BA_FROSTJOKE:
+ if(rand()%100 < (15+5*skilllv)*sc_def_mdef/100)
+ status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case DC_SCREAM:
+ if( rand()%100 < (25+5*skilllv)*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case BD_LULLABY: /* ŽqŽç‰S */
+ if( rand()%100 < 15*sc_def_int/100 )
+ status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case DC_UGLYDANCE:
+ if (dstsd) {
+ int skill, sp = 5+5*skilllv;
+ if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON)))
+ sp += 5+skill;
+ dstsd->status.sp -= sp;
+ if(dstsd->status.sp<0)
+ dstsd->status.sp=0;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+ case SL_STUN:
+ if (status_get_size(bl)==1 && rand()%100 < (30+10*skilllv)*sc_def_vit/100 ) //Only stuns mid-sized mobs.
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (dstsd) {
+ dstsd->status.sp -= 5;
+ if(dstsd->status.sp < 0)
+ dstsd->status.sp = 0;
+ clif_updatestatus(dstsd,SP_SP);
+ }
+ break;
+
+ /* MOB‚̒ljÁ?‰Ê•t‚«ƒXƒLƒ‹ */
+
+ case NPC_PETRIFYATTACK:
+ if(rand()%100 < sc_def_mdef)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ case NPC_POISON:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ if(rand()%100 < sc_def_vit && src->type!=BL_PET)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if(src->type==BL_PET)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skilllv*1000,0);
+ break;
+ case NPC_CURSEATTACK:
+ if(rand()%100 < sc_def_luk)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ case NPC_SLEEPATTACK:
+ case NPC_BLINDATTACK:
+ if(rand()%100 < sc_def_int)
+ status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case NPC_MENTALBREAKER:
+ if(dstsd) {
+ int sp = dstsd->status.max_sp*(10+skilllv)/100;
+ if(sp < 1) sp = 1;
+ pc_heal(dstsd,0,-sp);
+ }
+ break;
+
+ case CH_TIGERFIST:
+ if (rand()%100 < (10 + skilllv*10)*sc_def_vit/100) {
+ int sec = skill_get_time2 (skillid,skilllv) - status_get_agi(bl)/10;
+ if (dstsd) {
+ dstsd->canmove_tick += sec;
+ dstsd->canact_tick += sec;
+ } else if (dstmd)
+ dstmd->canmove_tick += sec;
+ }
+ break;
+
+ case LK_SPIRALPIERCE:
+ if (rand()%100 < (15 + skilllv*5)*sc_def_vit/100)
+ status_change_start(bl,SC_STOP,0,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case ST_REJECTSWORD: /* ƒtƒŠ?ƒWƒ“ƒOƒgƒ‰ƒbƒv */
+ if( rand()%100 < (skilllv*15) )
+ status_change_start(bl,SC_AUTOCOUNTER,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PF_FOGWALL: /* ƒz?ƒŠ?ƒNƒ?ƒX */
+ if (src != bl) {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data && sc_data[SC_DELUGE].timer == -1 && !(status_get_mode(bl)&MD_BOSS))
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ }
+ break;
+
+ case LK_HEADCRUSH: /* ƒwƒbƒhƒNƒ‰ƒbƒVƒ… */
+ {
+ //?Œ?‚ª—Ç‚­•ª‚©‚ç‚È‚¢‚Ì‚Å“K?‚É
+ int race = status_get_race(bl);
+ if (!(battle_check_undead(race, status_get_elem_type(bl)) || race == 6) && rand()%100 < 50 * sc_def_vit/100)
+ status_change_start(bl, SC_BLEEDING, skilllv, 0, 0, 0, skill_get_time2(skillid,skilllv), 0);
+ }
+ break;
+
+ case LK_JOINTBEAT: /* ƒWƒ‡ƒCƒ“ƒgƒr?ƒg */
+ //?Œ?‚ª—Ç‚­•ª‚©‚ç‚È‚¢‚Ì‚Å“K?‚É
+ if( rand()%100 < (5*skilllv+5)*sc_def_vit/100 )
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case PF_SPIDERWEB: /* ƒXƒpƒCƒ_?ƒEƒFƒbƒu */
+ {
+ int sec = skill_get_time2(skillid,skilllv);
+ if(map[src->m].flag.pvp) //PvP‚Å‚Í?S‘©ŽžŠÔ”¼Œ¸?H
+ sec = sec/2;
+ battle_stopwalking(bl,1);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,sec,0);
+ }
+ break;
+
+ case ASC_METEORASSAULT: /* ƒ?ƒeƒIƒAƒTƒ‹ƒg */
+ //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*SkillLV% chance.
+ if( rand()%100 < (5+skilllv*5) ) //5%+5*SkillLV%
+ switch(rand()%3) {
+ case 0:
+ status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,1),0);
+ break;
+ case 1:
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,2),0);
+ break;
+ default:
+ status_change_start(bl,SC_BLEEDING,skilllv,0,0,0,skill_get_time2(skillid,3),0);
+ }
+ break;
+
+ case HW_NAPALMVULCAN: /* ƒiƒp?ƒ€ƒoƒ‹ƒJƒ“ */
+ // skilllv*5%‚ÌŠm—¦‚ÅŽô‚¢
+ if (rand()%10000 < 5*skilllv*sc_def_luk)
+ status_change_start(bl,SC_CURSE,7,0,0,0,skill_get_time2(NPC_CURSEATTACK,7),0);
+ break;
+
+ case WS_CARTTERMINATION: // Cart termination
+ if (rand() % 10000 < 5 * skilllv * sc_def_vit)
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ if (dstsd) {
+ if (rand()%10000 < skilllv * battle_config.equip_skill_break_rate)
+ pc_breakweapon(dstsd);
+ // separate chances?
+ if (rand()%10000 < skilllv * battle_config.equip_skill_break_rate)
+ pc_breakarmor(dstsd);
+ }
+ break;
+
+ case TK_DOWNKICK:
+ if(rand()%100 < 100*sc_def_vit/100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ break;
+
+ case TK_JUMPKICK:
+ { //Cancel out Soul Linker status of the target. [Skotlex]
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data) {
+ if (sc_data[SC_PRESERVE].timer != -1) //preserve blocks the cleaning
+ break;
+ //Remove pitched potions effect.
+ if (sc_data[SC_ASPDPOTION0].timer != -1 && sc_data[SC_ASPDPOTION0].val4)
+ status_change_end(bl, SC_ASPDPOTION0, -1);
+ if (sc_data[SC_ASPDPOTION1].timer != -1 && sc_data[SC_ASPDPOTION1].val4)
+ status_change_end(bl, SC_ASPDPOTION1, -1);
+ if (sc_data[SC_ASPDPOTION2].timer != -1 && sc_data[SC_ASPDPOTION2].val4)
+ status_change_end(bl, SC_ASPDPOTION2, -1);
+ if (sc_data[SC_ASPDPOTION3].timer != -1 && sc_data[SC_ASPDPOTION3].val4)
+ status_change_end(bl, SC_ASPDPOTION3, -1);
+ if (sc_data[SC_SPIRIT].timer != -1)
+ status_change_end(bl, SC_SPIRIT, -1);
+ if (sc_data[SC_ONEHAND].timer != -1)
+ status_change_end(bl, SC_ONEHAND, -1);
+ if (sc_data[SC_ADRENALINE2].timer != -1)
+ status_change_end(bl, SC_ADRENALINE2, -1);
+ }
+ }
+ break;
+ case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs.
+ if(attack_type == BF_MISC && rand()%100 < 70*sc_def_vit/100 ) //70% base stun chance...
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ break;
+ }
+
+ if (md && battle_config.summons_inherit_effects && md->master_id && md->special_state.ai)
+ { //Pass heritage to Master for status causing effects. [Skotlex]
+ sd = map_id2sd(md->master_id);
+ }
+
+ if(sd && skillid != MC_CARTREVOLUTION && skillid != AM_DEMONSTRATION && skillid != CR_REFLECTSHIELD && attack_type&BF_WEAPON){ /* ƒJ?ƒh‚É‚æ‚é’ljÁ?‰Ê */
+ int i, type;
+ int sc_def_card=100;
+
+ for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){
+ type=i-SC_COMMON_MIN;
+ if (!sd->addeff[type] && (!sd->state.arrow_atk || !sd->arrow_addeff[type]))
+ continue; //Code Speedup.
+ //??Û‚É?‘ÔˆÙ?í
+ switch (i) {
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def_card=sc_def_mdef;
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def_card=sc_def_vit;
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ sc_def_card=sc_def_int;
+ break;
+ case SC_CURSE:
+ sc_def_card=sc_def_luk;
+ }
+
+ if (rand()%10000 < (sd->addeff[type]+(sd->state.arrow_atk?sd->arrow_addeff[type]:0))*sc_def_card/100 )
+ { //Inflicted status effect.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_additional_effect: caused status effect (pos %d): %d\n",sd->bl.id,i,sd->addeff[type]);
+ status_change_start(bl,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ }
+ }
+
+ //Reports say that autospell effects get triggered on skills and pretty much everything including splash attacks. [Skotlex]
+ //Here we use the nk value to trigger spells only on damage causing skills (otherwise stuff like AL_HEAL will trigger them)
+ if(sd && !status_isdead(bl) && src != bl &&
+ (!skillid || skillid == KN_AUTOCOUNTER || skillid == CR_REFLECTSHIELD || skill_get_nk(skillid)!=NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ int i, auto_skillid, auto_skilllv, rate;
+
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (sd->autospell[i].id == 0)
+ break;
+
+ auto_skillid = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
+
+ if (auto_skillid == skillid) //Prevents skill from retriggering themselves. [Skotlex]
+ continue;
+
+ auto_skilllv = (sd->autospell[i].lv > 0) ? sd->autospell[i].lv : 1;
+ rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
+
+ if (rand()%1000 > rate)
+ continue;
+ if (sd->autospell[i].id < 0)
+ tbl = src;
+ else
+ tbl = bl;
+
+ if (tbl != src && !battle_check_range(src, tbl, skill_get_range2(src, auto_skillid, auto_skilllv)))
+ continue; //Autoskills DO check for target-src range. [Skotlex]
+
+ if (skill_get_inf(auto_skillid) & INF_GROUND_SKILL)
+ skill_castend_pos2(src, tbl->x, tbl->y, auto_skillid, auto_skilllv, tick, 0);
+ else {
+ switch (skill_get_nk(auto_skillid)) {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(src, tbl, auto_skillid, auto_skilllv, tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(src, tbl, auto_skillid, auto_skilllv, tick, 0);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Splitted off from skill_additional_effect, which is never called when the
+ * attack skill kills the enemy. Place in this function counter status effects
+ * when using skills (eg: Asura's sp regen penalty, or counter-status effects
+ * from cards) that will take effect on the source, not the target. [Skotlex]
+ * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
+ * type of skills, so not every instance of skill_additional_effect needs a call
+ * to this one.
+ */
+int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick)
+{
+ const int sc2[]={
+ MG_STONECURSE,MG_FROSTDIVER,NPC_STUNATTACK,
+ NPC_SLEEPATTACK,TF_POISON,NPC_CURSEATTACK,
+ NPC_SILENCEATTACK,AS_SONICBLOW,NPC_BLINDATTACK,
+ LK_HEADCRUSH
+ };
+
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+ struct mob_data *md=NULL;
+ struct mob_data *dstmd=NULL;
+// struct pet_data *pd=NULL; Pet's can't be inflicted!
+
+ int sc_def_mdef,sc_def_vit,sc_def_int,sc_def_luk;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_counter_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,attack_type,tick);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest
+
+ switch (src->type) {
+ case BL_PC:
+ sd = (struct map_session_data *)src;
+ break;
+ case BL_MOB:
+ md = (struct mob_data *)src;
+ break;
+ case BL_PET: //Only mobs/players can be affected. [Skotlex]
+// pd = (struct pet_data *)src;
+// break;
+ default:
+ return 0;
+ }
+
+ switch (bl->type) {
+ case BL_PC:
+ dstsd=(struct map_session_data *)bl;
+ break;
+ case BL_MOB:
+ dstmd=(struct mob_data *)bl;
+ break;
+ default:
+ return 0;
+ }
+
+ //Ž©•ª‚Ì‘Ï?«
+ sc_def_mdef = status_get_sc_def_mdef(src);
+ sc_def_vit = status_get_sc_def_vit(src);
+ sc_def_int = status_get_sc_def_int(src);
+ sc_def_luk = status_get_sc_def_luk(src);
+
+ switch(skillid){
+ case 0: //Normal Attack - Nothing here yet.
+ break;
+ case MO_EXTREMITYFIST: /* ˆ¢?C—…”e™€Œ? */
+ //ˆ¢?C—…‚ðŽg‚¤‚Æ5•ªŠÔŽ©‘R‰ñ•œ‚µ‚È‚¢‚悤‚É‚È‚é
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ break;
+ }
+
+ if((sd||dstsd) && skillid != MC_CARTREVOLUTION && attack_type&BF_WEAPON){ /* ƒJ?ƒh‚É‚æ‚é’ljÁ?‰Ê */
+ int i, type;
+ int sc_def_card=100;
+
+ for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){
+ type=i-SC_COMMON_MIN;
+
+ switch (i) {
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def_card=sc_def_mdef;
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def_card=sc_def_vit;
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ sc_def_card=sc_def_int;
+ break;
+ case SC_CURSE:
+ sc_def_card=sc_def_luk;
+ }
+
+ if (sd && (rand()%10000 < (sd->addeff2[type]+(sd->state.arrow_atk?sd->arrow_addeff2[type]:0))*sc_def_card/100 ))
+ { //Self infliced status from attacking.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_addeff: self inflicted effect (pos %d): %d\n",src->id,i,sd->addeff2[type]);
+ status_change_start(src,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ if (dstsd &&
+ (dstsd->addeff3_type[type] == 1 || ((sd && sd->state.arrow_atk) || (status_get_range(src)>2))) &&
+ (rand()%10000 < dstsd->addeff3[type]*sc_def_card/100)
+ ) { //Counter status effect.
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_addeff: counter inflicted effect (pos %d): %d\n",src->id,i,dstsd->addeff3[type]);
+ status_change_start(src,i,7,0,0,0,skill_get_time2(sc2[type],7),0);
+ }
+ }
+ }
+
+ //Trigger counter-spells to retaliate against damage causing skills. [Skotlex]
+ if(dstsd && !status_isdead(bl) && src != bl &&(!skillid || skill_get_nk(skillid)!=NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ int i, skillid, skilllv, rate;
+
+ for (i = 0; i < MAX_PC_BONUS; i++) {
+ if (dstsd->autospell2[i].id == 0)
+ break;
+
+ skillid = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id;
+ skilllv = (dstsd->autospell2[i].lv > 0) ? dstsd->autospell2[i].lv : 1;
+ rate = ((sd && !sd->state.arrow_atk) || (status_get_range(src)<=2)) ?
+ dstsd->autospell2[i].rate : dstsd->autospell2[i].rate / 2;
+
+ if (rand()%1000 > rate)
+ continue;
+ if (dstsd->autospell2[i].id < 0)
+ tbl = bl;
+ else
+ tbl = src;
+
+ if (skill_get_inf(skillid) & INF_GROUND_SKILL)
+ skill_castend_pos2(bl, tbl->x, tbl->y, skillid, skilllv, tick, 0);
+ else {
+ switch (skill_get_nk(skillid)) {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(bl, tbl, skillid, skilllv, tick, 0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(bl, tbl, skillid, skilllv, tick, 0);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*=========================================================================
+ Used to knock back players, monsters, traps, etc
+ If count&0xf00000, the direction is send in the 6th byte.
+ If count&0x10000, the direction is to the back of the target, otherwise is away from the src.
+ If count&0x20000, position update packets must not be sent.
+-------------------------------------------------------------------------*/
+int skill_blown( struct block_list *src, struct block_list *target,int count)
+{
+ int dx=0,dy=0,nx,ny;
+ int x=target->x,y=target->y;
+ int dir,ret;
+ struct map_session_data *sd=NULL;
+ struct mob_data *md=NULL;
+ struct pet_data *pd=NULL;
+ struct skill_unit *su=NULL;
+ struct status_change* sc_data=NULL;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, target);
+
+ if (src != target && map_flag_gvg(target->m) && target->type != BL_SKILL)
+ return 0; //No knocking back in WoE, except for skills... because traps CAN be knocked back.
+
+ switch (target->type) {
+ case BL_PC:
+ sd=(struct map_session_data *)target;
+ break;
+ case BL_MOB:
+ md=(struct mob_data *)target;
+ break;
+ case BL_PET:
+ pd=(struct pet_data *)target;
+ break;
+ case BL_SKILL:
+ su=(struct skill_unit *)target;
+ break;
+ default:
+ return 0;
+ }
+ if (target->type != BL_SKILL)
+ sc_data = status_get_sc_data(target);
+
+ if (count&0xf00000)
+ dir = (count>>20)&0xf;
+ else if (count&0x10000 || (target->x==src->x && target->y==src->y))
+ dir = status_get_dir(target);
+ else
+ dir = map_calc_dir(target,src->x,src->y);
+ if (dir>=0 && dir<8){
+ dx = -dirx[dir];
+ dy = -diry[dir];
+ }
+
+ ret=path_blownpos(target->m,x,y,dx,dy,count&0xffff);
+ nx=ret>>16;
+ ny=ret&0xffff;
+
+ battle_stopwalking(target,0);
+
+ dx = nx - x;
+ dy = ny - y;
+
+ if(sd) /* ?–ÊŠO‚É?o‚½‚Ì‚Å?Á‹Ž */
+ map_foreachinmovearea(clif_pcoutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_ALL,sd);
+ else if(md)
+ map_foreachinmovearea(clif_moboutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md);
+ else if(pd)
+ map_foreachinmovearea(clif_petoutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,pd);
+
+ if (sc_data) {
+ if (sc_data[SC_DANCING].timer != -1) { //Move the song/dance [Skotlex]
+ if (sc_data[SC_DANCING].val1 == CG_MOONLIT) //Cancel Moonlight Petals if moved from casting position. [Skotlex]
+ skill_stop_dancing(target);
+ else
+ skill_unit_move_unit_group((struct skill_unit_group *)sc_data[SC_DANCING].val2, target->m, dx, dy);
+ }
+ if (sc_data[SC_CLOSECONFINE].timer != -1)
+ status_change_end(target, SC_CLOSECONFINE, -1);
+ if (sc_data[SC_CLOSECONFINE2].timer != -1)
+ status_change_end(target, SC_CLOSECONFINE2, -1);
+ }
+
+ if(su){
+ skill_unit_move_unit_group(su->group,target->m,dx,dy);
+ }else{
+ map_moveblock(target, nx, ny, gettick());
+ }
+
+ if(sd) /* ?–Ê?‚É“ü‚Á‚Ä‚«‚½‚Ì‚Å•\Ž¦ */
+ map_foreachinmovearea(clif_pcinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_ALL,sd);
+ else if(md)
+ map_foreachinmovearea(clif_mobinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_PC,md);
+ else if(pd)
+ map_foreachinmovearea(clif_petinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_PC,pd);
+
+ if(!(count&0x20000))
+ clif_blown(target);
+
+ return 0;
+}
+
+/*
+ * =========================================================================
+ * ƒXƒLƒ‹?U??‰Ê?—?‚Ü‚Æ‚ß
+ * flag‚Ì?–¾?B16?i?
+ * 00XRTTff
+ * ff = magic‚ÅŒvŽZ‚É“n‚³‚ê‚é?j
+ * TT = ƒpƒPƒbƒg‚Ìtype•”•ª(0‚ŃfƒtƒHƒ‹ƒg?j
+ * X = ƒpƒPƒbƒg‚̃XƒLƒ‹Lv
+ * R = —\–ñ?iskill_area_sub‚ÅŽg—p‚·‚é?j
+ *-------------------------------------------------------------------------
+ */
+
+int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
+ struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag )
+{
+ struct Damage dmg;
+ struct status_change *sc_data;
+ struct map_session_data *sd=NULL, *tsd=NULL;
+ int type,lv,damage;
+ static int tmpdmg = 0;
+
+ if(skillid > 0 && skilllv <= 0) return 0;
+
+ rdamage = 0;
+ nullpo_retr(0, src); //Source is the master behind the attack (player/mob/pet)
+ nullpo_retr(0, dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src.
+ nullpo_retr(0, bl); //Target to be attacked.
+
+ if(src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL)
+ return 0;
+ //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex]
+ if (src != dsrc && !status_check_skilluse(NULL, bl, skillid, 1))
+ return 0;
+
+ //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex]
+ if ((skill_get_nk(skillid) == NK_SPLASH_DAMAGE || skillid == ASC_METEORASSAULT || skillid == SN_SHARPSHOOTING)
+ && !status_check_skilluse(dsrc, bl, skillid, 1))
+ return 0;
+
+ //uncomment the following to do a check between caster and target. [Skotlex]
+ //eg: if you want storm gust to do no damage if the caster runs to another map after invoking the skill.
+// if(src->m != bl->m)
+// return 0;
+
+ //Uncomment the following to disable trap-ground skills from hitting when the caster is dead [Skotlex]
+ //eg: You cast meteor and then are killed, if you uncomment the following the meteors that fall afterwards cause no damage.
+// if(src != dsrc && status_isdead(src))
+// return 0;
+
+ if (dsrc->type == BL_PC)
+ sd = (struct map_session_data *)dsrc;
+ if (bl->type == BL_PC)
+ tsd = (struct map_session_data *)bl;
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if(dsrc->type == BL_PC && skillnotok(skillid, (struct map_session_data *)dsrc))
+// return 0; // [MouseJstr]
+// Is this check really needed? FrostNova won't hurt you if you step right where the caster is?
+ if(skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //Žg—pƒXƒLƒ‹‚ªƒtƒ?ƒXƒgƒmƒ”ƒ@‚Å?Adsrc‚Æbl‚ª“¯‚¶?ê?Š‚Ȃ牽‚à‚µ‚È‚¢
+ return 0;
+ if(sd && sd->chatID) //pŽÒ‚ªPC‚Ń`ƒƒƒbƒg’†‚Ȃ牽‚à‚µ‚È‚¢
+ return 0;
+
+//‰½‚à‚µ‚È‚¢”»’肱‚±‚Ü‚Å
+
+ sc_data = status_get_sc_data(bl);
+ if (attack_type&BF_MAGIC && sc_data && sc_data[SC_KAITE].timer != -1 && src == dsrc
+ && !(status_get_mode(src)&MD_BOSS) && (sd || status_get_lv(dsrc) <= 80) //Works on players or mobs with level under 80.
+ ) { //Bounce back the skill.
+ if (--sc_data[SC_KAITE].val2 <= 0)
+ status_change_end(bl, SC_KAITE, -1);
+ bl = src; //Just make the skill attack yourself @.@
+ sc_data = status_get_sc_data(bl);
+ tsd = (bl->type == BL_PC)?(struct map_session_data *)bl:NULL;
+ if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ return 0; //Spirit of Wizard blocks bounced back spells.
+ }
+
+ type=-1;
+ lv=(flag>>20)&0xf;
+ dmg=battle_calc_attack(attack_type,src,bl,skillid,skilllv,flag&0xff ); //ƒ_ƒ??ƒWŒvŽZ
+
+ //Skotlex: Adjusted to the new system
+ if(src->type==BL_PET && (struct pet_data *)src)
+ { // [Valaris]
+ struct pet_data *pd = (struct pet_data *)src;
+ if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skillid)
+ {
+ int element = skill_get_pl(skillid);
+ if (skillid == -1)
+ element = status_get_attack_element(src);
+ dmg.damage=battle_attr_fix(src, bl, skilllv, element, status_get_element(bl));
+ dmg.damage2=0;
+ dmg.div_= pd->a_skill->div_;
+ }
+ }
+
+//ƒ}ƒWƒbƒNƒƒbƒh?—‚±‚±‚©‚ç
+ if(attack_type&BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 && src == dsrc) { //–‚–@U?‚Ń}ƒWƒbƒNƒƒbƒh?‘Ô‚Åsrc=dsrc‚È‚ç
+ dmg.damage = dmg.damage2 = 0; //ƒ_ƒ?ƒW0
+ dmg.dmg_lv = ATK_FLEE; //This will prevent skill additional effect from taking effect. [Skotlex]
+ if(tsd) {
+ int sp = skill_get_sp(skillid,skilllv); //Žg—p‚³‚ꂽƒXƒLƒ‹‚ÌSP‚ð‹z?
+ sp = sp * sc_data[SC_MAGICROD].val2 / 100; //‹z?—¦ŒvŽZ
+ if(skillid == WZ_WATERBALL && skilllv > 1) //ƒEƒH?ƒ^?ƒ{?ƒ‹Lv1ˆÈã
+ sp = sp/((skilllv|1)*(skilllv|1)); //‚³‚ç‚ÉŒvŽZH
+ if(sp > 0x7fff) sp = 0x7fff; //SP‘½‚·‚¬‚Ìꇂ͗˜_Å‘å’l
+ else if(sp < 1) sp = 1; //1ˆÈ‰º‚ÌꇂÍ1
+ if(tsd->status.sp + sp > tsd->status.max_sp) { //‰ñ•œSP+Œ»Ý‚ÌSP‚ªMSP‚æ‚è‘å‚«‚¢ê‡
+ sp = tsd->status.max_sp - tsd->status.sp; //SP‚ðMSP-Œ»ÝSP‚É‚·‚é
+ tsd->status.sp = tsd->status.max_sp; //Œ»Ý‚ÌSP‚ÉMSP‚ð‘ã“ü
+ }
+ else //‰ñ•œSP+Œ»Ý‚ÌSP‚ªMSP‚æ‚謂³‚¢ê‡‚͉ñ•œSP‚ð‰ÁŽZ
+ tsd->status.sp += sp;
+ clif_heal(tsd->fd,SP_SP,sp); //SP‰ñ•œƒGƒtƒFƒNƒg‚Ì•\Ž¦
+ tsd->canact_tick = tick + skill_delayfix(bl, SA_MAGICROD, sc_data[SC_MAGICROD].val1, 0);
+ }
+ clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1); //ƒ}ƒWƒbƒNƒƒbƒhƒGƒtƒFƒNƒg‚ð•\Ž¦
+ }
+//ƒ}ƒWƒbƒNƒƒbƒh?—‚±‚±‚Ü‚Å
+
+ damage = dmg.damage + dmg.damage2;
+
+ if(lv==15)
+ lv=-1;
+
+ if( flag&0xff00 )
+ type=(flag&0xff00)>>8;
+
+ if((damage <= 0 || damage < dmg.div_)
+ && skillid != CH_PALMSTRIKE) //Palm Strike is the only skill that will knockback even if it misses. [Skotlex]
+ dmg.blewcount = 0;
+
+ if(skillid == CR_GRANDCROSS||skillid == NPC_GRANDDARKNESS) {//ƒOƒ‰ƒ“ƒhƒNƒƒX
+ if(battle_config.gx_disptype) dsrc = src; // “Gƒ_ƒ?ƒW”’•¶Žš•\Ž¦
+ if(src == bl) type = 4; // ”½“®‚̓_ƒ?ƒWƒ‚?ƒVƒ‡ƒ“‚È‚µ
+ }
+
+//Žg—pŽÒ‚ªPC‚Ì?ê?‡‚Ì?—?‚±‚±‚©‚ç
+ if(sd) {
+ //Sorry for removing the Japanese comments, but they were actually distracting
+ //from the actual code and I couldn't understand a thing anyway >.< [Skotlex]
+ if (skillid && sd->sc_data[SC_COMBO].timer != -1)
+ status_change_end(src,SC_COMBO,-1); //Interrupt previous combo if you used a skill already. [Skotlex]
+ switch(skillid)
+ {
+ case MO_TRIPLEATTACK:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if (damage < status_get_hp(bl) &&
+ pc_checkskill(sd, MO_CHAINCOMBO) > 0)
+ delay += 300 * battle_config.combo_delay_rate / 100;
+ status_change_start(src,SC_COMBO,MO_TRIPLEATTACK,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src, delay);
+
+ if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
+ party_skill_check(sd, sd->status.party_id, MO_TRIPLEATTACK, skilllv);
+ break;
+ }
+ case MO_CHAINCOMBO:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,MO_CHAINCOMBO,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case MO_COMBOFINISH:
+ {
+ int delay = 700 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (
+ (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) ||
+ (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) ||
+ (pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
+ ))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,MO_COMBOFINISH,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case CH_TIGERFIST:
+ { //Tigerfist is now a combo-only skill. [Skotlex]
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl) &&
+ (
+ (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 3 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) ||
+ (pc_checkskill(sd, CH_CHAINCRUSH) > 0)
+ ))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,CH_TIGERFIST,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case CH_CHAINCRUSH:
+ {
+ int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src);
+ if(damage < status_get_hp(bl))
+ delay += 300 * battle_config.combo_delay_rate /100;
+ status_change_start(src,SC_COMBO,CH_CHAINCRUSH,skilllv,0,0,delay,0);
+ sd->attackabletime = sd->canmove_tick = tick + delay;
+ clif_combo_delay(src,delay);
+ break;
+ }
+ case AC_DOUBLE:
+ {
+ int race = status_get_race(bl);
+ if((race == 2 || race == 4) && damage < status_get_hp(bl) && pc_checkskill(sd, HT_POWER)) {
+ //TODO: This code was taken from Triple Blows,is this even how it should be? [Skotlex]
+ status_change_start(src,SC_COMBO,HT_POWER,bl->id,0,0,2000,0);
+ clif_combo_delay(src,2000);
+ }
+ break;
+ }
+ case TK_COUNTER:
+ { //bonus from SG_FRIEND [Komurka]
+ int level;
+ if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
+ party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
+ }
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_TURNKICK:
+ // Delay normal attack table until skill's delay has passed. Let's make it skip one attack motion. [Skotlex]
+ sd->attackabletime = sd->canmove_tick = tick + status_get_amotion(&sd->bl);
+ break;
+ case SL_STIN:
+ case SL_STUN:
+ if (skilllv >= 7 && sd->sc_data[SC_COMBO].timer == -1)
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid, skilllv),0);
+ break;
+ } //Switch End
+ }
+ if(attack_type&BF_WEAPON && damage > 0 && src != bl && src == dsrc)
+ { //•ŠíƒXƒLƒ‹•ƒ_ƒ?ƒW‚ ‚è•Žg—pŽÒ‚Æ?ÛŽÒ‚ªˆá‚¤•src=dsrc
+ if(dmg.flag&BF_SHORT) { //‹ß‹——£U?ŽžH¦
+ if(tsd && tsd->short_weapon_damage_return > 0)
+ { //‹ß‹——£U?’µ‚Ë•Ô‚µH¦
+ rdamage += damage * tsd->short_weapon_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ if(sc_data && sc_data[SC_REFLECTSHIELD].timer != -1) { //ƒŠƒtƒŒƒNƒgƒV?ƒ‹ƒhŽž
+ rdamage += damage * sc_data[SC_REFLECTSHIELD].val2 / 100; //’µ‚Ë•Ô‚µŒvŽZ
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+ else if(dmg.flag&BF_LONG) { //‰“‹——£U?ŽžH¦
+ if(tsd && tsd->long_weapon_damage_return > 0)
+ { //‰“‹——£U?’µ‚Ë•Ô‚µH¦
+ rdamage += damage * tsd->long_weapon_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+ } else
+ // magic_damage_return by [AppleGirl] and [Valaris]
+ if(attack_type&BF_MAGIC && damage > 0 && src != bl && src == dsrc)
+ {
+ if(tsd && tsd->magic_damage_return > 0 )
+ {
+ rdamage += damage * tsd->magic_damage_return / 100;
+ if(rdamage < 1) rdamage = 1;
+ }
+ }
+
+//•ŠíƒXƒLƒ‹H‚±‚±‚Ü‚Å
+ switch(skillid){
+ case AS_SPLASHER:
+ clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5);
+ break;
+ case ASC_BREAKER: // [celest]
+ if (attack_type&BF_WEAPON) { // the 1st attack won't really deal any damage
+ tmpdmg = damage; // store the temporary weapon damage
+ } else { // only display damage for the 2nd attack
+ if (tmpdmg == 0 || damage == 0) // if one or both attack(s) missed, display a 'miss'
+ clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, 0, dmg.div_, skillid, skilllv, type);
+ damage += tmpdmg; // add weapon and magic damage
+ tmpdmg = 0; // clear the temporary weapon damage
+ if (damage > 0) // if both attacks missed, do not display a 2nd 'miss'
+ clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, skilllv, type);
+ }
+ break;
+ case NPC_SELFDESTRUCTION:
+ if(src->type==BL_PC)
+ dmg.blewcount = 10;
+ break;
+ case KN_AUTOCOUNTER: //Skills that need be passed as a normal attack for the client to display correctly.
+ case SN_SHARPSHOOTING:
+ case TF_DOUBLE:
+ clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
+ break;
+ default:
+ clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, (skillid==0)? 5:type );
+ }
+
+ map_freeblock_lock();
+
+ if(damage > 0 && dmg.flag&BF_SKILL && tsd
+ && pc_checkskill(tsd,RG_PLAGIARISM) && sc_data[SC_PRESERVE].timer == -1
+ && damage < tsd->status.hp)
+ { //Updated to not be able to copy skills if the blow will kill you. [Skotlex]
+ if ((!tsd->status.skill[skillid].id || tsd->status.skill[skillid].flag >= 13) &&
+ can_copy(tsd,skillid)) // Split all the check into their own function [Aru]
+ {
+ //?‚É?‚ñ‚Å‚¢‚éƒXƒLƒ‹‚ª‚ ‚ê‚ΊY?ƒXƒLƒ‹‚ðÁ‚·
+ if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == 13){
+ tsd->status.skill[tsd->cloneskill_id].id = 0;
+ tsd->status.skill[tsd->cloneskill_id].lv = 0;
+ tsd->status.skill[tsd->cloneskill_id].flag = 0;
+ }
+ tsd->cloneskill_id = skillid;
+ tsd->status.skill[skillid].id = skillid;
+ tsd->status.skill[skillid].lv = skilllv;
+ if ((lv = pc_checkskill(tsd,RG_PLAGIARISM)) < skilllv)
+ tsd->status.skill[skillid].lv = lv;
+ tsd->status.skill[skillid].flag = 13;//cloneskill flag
+ pc_setglobalreg(tsd, "CLONE_SKILL", tsd->cloneskill_id);
+ pc_setglobalreg(tsd, "CLONE_SKILL_LV", tsd->status.skill[skillid].lv);
+ clif_skillinfoblock(tsd);
+ }
+ }
+ if (skillid != WZ_HEAVENDRIVE && bl->type == BL_SKILL && damage > 0) {
+ struct skill_unit* su = (struct skill_unit*)bl;
+ if (su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ damage = 0; //Only Heaven's drive may damage traps. [Skotlex]
+ }
+ if ((skillid || flag) && !(attack_type&BF_WEAPON)) { // do not really deal damage for ASC_BREAKER's 1st attack
+ battle_damage(src,bl,damage, 0); //Deal damage before knockback to allow stuff like firewall+storm gust combo.
+ if (!status_isdead(bl) && (dmg.dmg_lv == ATK_DEF || damage > 0))
+ skill_additional_effect(src,bl,skillid,skilllv,attack_type,tick);
+ }
+
+ //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex]
+ if (dmg.blewcount > 0 && !status_isdead(bl))
+ skill_blown(dsrc,bl,dmg.blewcount);
+
+ //Delayed damage must be dealt after the knockback (it needs to know actual position of target)
+ if ((skillid || flag) && attack_type&BF_WEAPON && skillid != ASC_BREAKER) { // do not really deal damage for ASC_BREAKER's 1st attack
+ battle_delay_damage(tick+dmg.amotion,src,bl,attack_type,skillid,skilllv,damage,dmg.dmg_lv,0);
+ }
+
+ if(skillid == RG_INTIMIDATE && damage > 0 && !(status_get_mode(bl)&MD_BOSS)/* && !map_flag_gvg(src->m)*/) {
+ int s_lv = status_get_lv(src),t_lv = status_get_lv(bl);
+ int rate = 50 + skilllv * 5;
+ rate = rate + (s_lv - t_lv);
+ if(rand()%100 < rate)
+ skill_addtimerskill(src,tick + 800,bl->id,0,0,skillid,skilllv,0,flag);
+ }
+
+ if (dmg.dmg_lv == ATK_DEF || damage > 0) //Counter status effects [Skotlex]
+ skill_counter_additional_effect(dsrc,bl,skillid,skilllv,attack_type,tick);
+
+ /* ƒ_ƒ??ƒW‚ª‚ ‚é‚È‚ç’ljÁ?‰Ê”»’è */
+ if(!status_isdead(bl) && bl->type==BL_MOB && src!=bl) /* ƒXƒLƒ‹Žg—p?Œ‚ÌMOBƒXƒLƒ‹ */
+ {
+ struct mob_data *md=(struct mob_data *)bl;
+// nullpo_retr(0, md); //Just so you know.. these are useless. When you cast a pointer, the pointer still is the same, so if bl is not null, the after-casted pointer will never be nulll :/ [Skotlex]
+ if(battle_config.mob_changetarget_byskill && sd)
+ {
+ int target ;
+ target=md->target_id;
+ md->target_id=src->id;
+ mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16));
+ md->target_id=target;
+ }
+ else
+ mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16));
+ }
+
+ if(sd && dmg.flag&BF_WEAPON && src != bl && src == dsrc && damage > 0) {
+ int hp = 0,sp = 0;
+ if(sd->right_weapon.hp_drain_rate && sd->right_weapon.hp_drain_per > 0 && dmg.damage > 0 && rand()%1000 < sd->right_weapon.hp_drain_rate) {
+ hp += (dmg.damage * sd->right_weapon.hp_drain_per)/100;
+ if(sd->right_weapon.hp_drain_rate > 0 && hp < 1) hp = 1;
+ else if(sd->right_weapon.hp_drain_rate < 0 && hp > -1) hp = -1;
+ }
+ if(sd->left_weapon.hp_drain_rate && sd->left_weapon.hp_drain_per > 0 && dmg.damage2 > 0 && rand()%1000 < sd->left_weapon.hp_drain_rate) {
+ hp += (dmg.damage2 * sd->left_weapon.hp_drain_per)/100;
+ if(sd->left_weapon.hp_drain_rate > 0 && hp < 1) hp = 1;
+ else if(sd->left_weapon.hp_drain_rate < 0 && hp > -1) hp = -1;
+ }
+ if(sd->right_weapon.sp_drain_rate > 0 && sd->right_weapon.sp_drain_per > 0 && dmg.damage > 0 && rand()%1000 < sd->right_weapon.sp_drain_rate) {
+ sp += (dmg.damage * sd->right_weapon.sp_drain_per)/100;
+ if(sd->right_weapon.sp_drain_rate > 0 && sp < 1) sp = 1;
+ else if(sd->right_weapon.sp_drain_rate < 0 && sp > -1) sp = -1;
+ }
+ if(sd->left_weapon.sp_drain_rate > 0 && sd->left_weapon.sp_drain_per > 0 && dmg.damage2 > 0 && rand()%1000 < sd->left_weapon.sp_drain_rate) {
+ sp += (dmg.damage2 * sd->left_weapon.sp_drain_per)/100;
+ if(sd->left_weapon.sp_drain_rate > 0 && sp < 1) sp = 1;
+ else if(sd->left_weapon.sp_drain_rate < 0 && sp > -1) sp = -1;
+ }
+ if(hp || sp)
+ pc_heal(sd,hp,sp);
+ if (sd->sp_drain_type && bl->type == BL_PC)
+ battle_heal(NULL,bl,0,-sp,0);
+ }
+
+ if (/*(skillid || flag) &&*/ rdamage>0) { //Is the skillid/flag check really necessary? [Skotlex]
+ if (attack_type&BF_WEAPON)
+ battle_delay_damage(tick+dmg.amotion,bl,src,0,0,0,rdamage,ATK_DEF,0);
+ else
+ battle_damage(bl,src,rdamage,0);
+ clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0);
+ //Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
+ skill_additional_effect(bl,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick);
+ }
+
+ if (!(flag & 1) &&
+ (
+ skillid == MG_COLDBOLT || skillid == MG_FIREBOLT || skillid == MG_LIGHTNINGBOLT
+ ) &&
+ (sc_data = status_get_sc_data(src)) &&
+ sc_data[SC_DOUBLECAST].timer != -1 &&
+ rand() % 100 < 40+10*sc_data[SC_DOUBLECAST].val1)
+ {
+ skill_castend_delay (src, bl, skillid, skilllv, tick + dmg.div_*dmg.amotion, flag|1);
+ }
+
+ map_freeblock_unlock();
+
+ return (dmg.damage+dmg.damage2); /* ?ƒ_ƒ?‚ð•Ô‚· */
+}
+
+/*==========================================
+ * ƒXƒLƒ‹”Í??U?—p(map_foreachinarea‚©‚çŒÄ‚΂ê‚é)
+ * flag‚ɂ‚¢‚Ä?F16?i?‚ðŠm”F
+ * MSB <- 00fTffff ->LSB
+ * T =ƒ^?ƒQƒbƒg‘I?—p(BCT_*)
+ * ffff=Ž©—R‚ÉŽg—p‰Â”\
+ * 0 =—\–ñ?B0‚ɌŒè
+ *------------------------------------------
+ */
+static int skill_area_temp[8]; /* ˆêŽž???B•K—v‚È‚çŽg‚¤?B */
+static int skill_unit_temp[8]; /* For storing skill_unit ids as players move in/out of them. [Skotlex] */
+static int skill_unit_index=0; //Well, yeah... am too lazy to pass pointers around :X
+typedef int (*SkillFunc)(struct block_list *,struct block_list *,int,int,unsigned int,int);
+int skill_area_sub( struct block_list *bl,va_list ap )
+{
+ struct block_list *src;
+ int skill_id,skill_lv,flag;
+ unsigned int tick;
+ SkillFunc func;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ if(bl->type!=BL_PC && bl->type!=BL_MOB && bl->type!=BL_SKILL)
+ return 0;
+
+ src=va_arg(ap,struct block_list *); //‚±‚±‚Å‚Ísrc‚Ì’l‚ð??Æ‚µ‚Ä‚¢‚È‚¢‚Ì‚ÅNULLƒ`ƒFƒbƒN‚Í‚µ‚È‚¢
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ func=va_arg(ap,SkillFunc);
+
+ if(battle_check_target(src,bl,flag) > 0)
+ func(src,bl,skill_id,skill_lv,tick,flag);
+ return 0;
+}
+
+static int skill_check_unit_range_sub( struct block_list *bl,va_list ap )
+{
+ struct skill_unit *unit;
+ int *c;
+ int skillid,g_skillid;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit = (struct skill_unit *)bl);
+ nullpo_retr(0, c = va_arg(ap,int *));
+
+ if(bl->prev == NULL || bl->type != BL_SKILL)
+ return 0;
+
+ if(!unit->alive)
+ return 0;
+
+ skillid = va_arg(ap,int);
+ g_skillid = unit->group->skill_id;
+
+ switch (skillid)
+ {
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ if(g_skillid != MG_SAFETYWALL && g_skillid != AL_PNEUMA)
+ return 0;
+ break;
+ case AL_WARP:
+ case HT_SKIDTRAP:
+ case HT_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case HT_TALKIEBOX:
+ case HP_BASILICA:
+ //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set)
+ if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST)
+ return 0;
+ break;
+ default: //Avoid stacking with same kind of trap. [Skotlex]
+ if (g_skillid != skillid)
+ return 0;
+ break;
+ }
+
+ (*c)++;
+
+ return 1;
+}
+
+int skill_check_unit_range(int m,int x,int y,int skillid,int skilllv)
+{
+ int c = 0;
+ int range = skill_get_unit_range(skillid);
+ int layout_type = skill_get_unit_layout_type(skillid,skilllv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skillid);
+ return 0;
+ }
+
+ // ‚Æ‚è‚ ‚¦‚¸?³•ûŒ`‚̃†ƒjƒbƒgƒŒƒCƒAƒEƒg‚̂ݑΉž
+ range += layout_type;
+ map_foreachinarea(skill_check_unit_range_sub,m,
+ x-range,y-range,x+range,y+range,BL_SKILL,&c,skillid);
+
+ return c;
+}
+
+static int skill_check_unit_range2_sub( struct block_list *bl,va_list ap )
+{
+ int *c;
+ int skillid;
+
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, c = va_arg(ap,int *));
+
+ if(bl->prev == NULL)
+ return 0;
+
+ if(status_isdead(bl))
+ return 0;
+
+ skillid = va_arg(ap,int);
+ if (skillid==HP_BASILICA && bl->type==BL_PC)
+ return 0;
+
+ if (skillid==AM_DEMONSTRATION && bl->type==BL_MOB && ((struct mob_data*)bl)->class_ == MOBID_EMPERIUM)
+ return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex]
+
+ (*c)++;
+
+ return 1;
+}
+
+int skill_check_unit_range2(struct block_list *bl, int m,int x,int y,int skillid, int skilllv)
+{
+ int c = 0, range, type;
+
+ switch (skillid) { // to be expanded later
+ case WZ_ICEWALL:
+ range = 2;
+ break;
+ default:
+ {
+ int layout_type = skill_get_unit_layout_type(skillid,skilllv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skillid);
+ return 0;
+ }
+ // ‚Æ‚è‚ ‚¦‚¸?³•ûŒ`‚̃†ƒjƒbƒgƒŒƒCƒAƒEƒg‚̂ݑΉž
+ range = skill_get_unit_range(skillid) + layout_type;
+ }
+ break;
+ }
+
+ // if the caster is a monster/NPC, only check for players
+ // otherwise just check characters
+ if (bl->type == BL_PC)
+ type = BL_CHAR;
+ else
+ type = BL_PC;
+
+ map_foreachinarea(skill_check_unit_range2_sub, m,
+ x - range, y - range, x + range, y + range,
+ type, &c, skillid);
+
+ return c;
+}
+
+int skill_guildaura_sub (struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ int gid, id, *flag;
+
+ nullpo_retr(0, sd = (struct map_session_data *)bl);
+ nullpo_retr(0, ap);
+
+ id = va_arg(ap,int);
+ gid = va_arg(ap,int);
+ if (sd->status.guild_id != gid)
+ return 0;
+ nullpo_retr(0, flag = va_arg(ap,int *));
+
+ if (flag && *flag > 0) {
+ if (sd->sc_count && sd->sc_data[SC_GUILDAURA].timer != -1) {
+ if (sd->sc_data[SC_GUILDAURA].val4 != *flag) {
+ sd->sc_data[SC_GUILDAURA].val4 = *flag;
+ status_calc_pc (sd, 0);
+ }
+ return 0;
+ }
+ status_change_start(&sd->bl, SC_GUILDAURA, 1, id, 0, *flag, 0, 0);
+ }
+
+ return 0;
+}
+
+/*=========================================================================
+ * ”Í?ƒXƒLƒ‹Žg—p?—??¬•ª‚¯‚±‚±‚©‚ç
+ */
+/* ??Û‚Ì?‚ðƒJƒEƒ“ƒg‚·‚é?B?iskill_area_temp[0]‚ð?‰Šú‰»‚µ‚Ä‚¨‚­‚±‚Æ?j */
+int skill_area_sub_count(struct block_list *src,struct block_list *target,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ if(skill_area_temp[0] < 0xffff)
+ skill_area_temp[0]++;
+ return 1;
+}
+
+int skill_count_water(struct block_list *src,int range)
+{
+ int i,x,y,cnt = 0,size = range*2+1;
+ struct skill_unit *unit;
+
+ for (i=0;i<size*size;i++) {
+ x = src->x+(i%size-range);
+ y = src->y+(i/size-range);
+ if (map_getcell(src->m,x,y,CELL_CHKWATER)) {
+ cnt++;
+ continue;
+ }
+ unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL);
+ if (unit) {
+ cnt++;
+ skill_delunit(unit);
+ }
+ }
+ return cnt;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int skill_timerskill(int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data *sd = NULL;
+ struct mob_data *md = NULL;
+ struct pet_data *pd = NULL;
+ struct block_list *src = map_id2bl(id),*target;
+ struct skill_timerskill *skl = NULL;
+ int range;
+
+ nullpo_retr(0, src);
+
+ if(src->type == BL_PC) {
+ nullpo_retr(0, sd = (struct map_session_data *)src);
+ skl = &sd->skilltimerskill[data];
+ }
+ else if(src->type == BL_MOB) {
+ nullpo_retr(0, md = (struct mob_data *)src);
+ skl = &md->skilltimerskill[data];
+ }
+ else if(src->type == BL_PET) { // [Valaris]
+ nullpo_retr(0, pd = (struct pet_data *)src);
+ skl = &pd->skilltimerskill[data];
+ }
+
+ else
+ return 0;
+
+ nullpo_retr(0, skl);
+
+ skl->timer = -1;
+
+ if (sd) {
+ sd->timerskill_count--;
+ }
+
+ //Check moved here because otherwise the timer is not reset to -1 and later on we'll see problems when clearing. [Skotlex]
+ if(src->prev == NULL)
+ return 0;
+
+ if(skl->target_id) {
+ struct block_list tbl;
+ target = map_id2bl(skl->target_id);
+ if(skl->skill_id == RG_INTIMIDATE) {
+ if(target == NULL) {
+ target = &tbl; //?‰Šú‰»‚µ‚Ä‚È‚¢‚̂ɃAƒhƒŒƒX“Ë‚Á?‚ñ‚Å‚¢‚¢‚Ì‚©‚È?H
+ target->type = BL_NUL;
+ target->m = src->m;
+ target->prev = target->next = NULL;
+ }
+ }
+ if(target == NULL)
+ return 0;
+
+ if(target->prev == NULL && skl->skill_id != RG_INTIMIDATE)
+ return 0;
+ if(src->m != target->m)
+ return 0;
+ if(sd && pc_isdead(sd))
+ return 0;
+ if(target->type == BL_PC && pc_isdead((struct map_session_data *)target) && skl->skill_id != RG_INTIMIDATE)
+ return 0;
+
+ switch(skl->skill_id) {
+ case RG_INTIMIDATE:
+ if(sd && !map[src->m].flag.noteleport) {
+ int x,y,i,j;
+ pc_randomwarp(sd,3);
+ for(i=0;i<16;i++) {
+ j = rand()%8;
+ x = sd->bl.x + dirx[j];
+ y = sd->bl.y + diry[j];
+ if(map_getcell(sd->bl.m,x,y,CELL_CHKPASS))
+ break;
+ }
+ if(i >= 16) {
+ x = sd->bl.x;
+ y = sd->bl.y;
+ }
+ if(target->prev != NULL) {
+ if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target))
+ pc_setpos((struct map_session_data *)target,map[sd->bl.m].index,x,y,3);
+ else if(target->type == BL_MOB)
+ mob_warp((struct mob_data *)target,-1,x,y,3);
+ }
+ }
+ else if(md && !map[src->m].flag.monster_noteleport) {
+ int x,y,i,j;
+ mob_warp(md,-1,-1,-1,3);
+ for(i=0;i<16;i++) {
+ j = rand()%8;
+ x = md->bl.x + dirx[j];
+ y = md->bl.y + diry[j];
+ if(map_getcell(md->bl.m,x,y,CELL_CHKPASS))
+ break;
+ }
+ if(i >= 16) {
+ x = md->bl.x;
+ y = md->bl.y;
+ }
+ if(target->prev != NULL) {
+ if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target))
+ pc_setpos((struct map_session_data *)target,map[md->bl.m].index,x,y,3);
+ else if(target->type == BL_MOB)
+ mob_warp((struct mob_data *)target,-1,x,y,3);
+ }
+ }
+ break;
+
+ case BA_FROSTJOKE: /* Š¦‚¢ƒWƒ‡?ƒN */
+ case DC_SCREAM: /* ƒXƒNƒŠ?ƒ€ */
+ range=battle_config.area_size; //Ž‹ŠE‘S?
+ map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range,
+ skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick);
+ break;
+
+ case WZ_WATERBALL:
+ if (skl->type>1) {
+ skl->timer = 0; // skill_addtimerskill‚ÅŽg—p‚³‚ê‚È‚¢‚悤‚É
+ skill_addtimerskill(src,tick+150,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ skl->timer = -1;
+ }
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ if (skl->type <= 1) { // partial fix: it still doesn't end if the target dies
+ // should put outside of the switch, but since this is the only
+ // mage targetted spell for now,
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_MAGICPOWER].timer != -1) //ƒ}ƒWƒbƒNƒpƒ??‚Ì?‰Ê?I—¹
+ status_change_end(src,SC_MAGICPOWER,-1);
+ }
+ break;
+ default:
+ skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ break;
+ }
+ }
+ else {
+ if(src->m != skl->map)
+ return 0;
+ switch(skl->skill_id) {
+ case WZ_METEOR:
+ if(skl->type >= 0) {
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->type>>16,skl->type&0xFFFF,skl->flag);
+ clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
+ }
+ else
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag)
+{
+ int i;
+
+ nullpo_retr(1, src);
+
+ if(src->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)src;
+ nullpo_retr(1, sd);
+ for(i=0;i<MAX_SKILLTIMERSKILL;i++) {
+ if(sd->skilltimerskill[i].timer == -1) {
+ sd->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ sd->skilltimerskill[i].src_id = src->id;
+ sd->skilltimerskill[i].target_id = target;
+ sd->skilltimerskill[i].skill_id = skill_id;
+ sd->skilltimerskill[i].skill_lv = skill_lv;
+ sd->skilltimerskill[i].map = src->m;
+ sd->skilltimerskill[i].x = x;
+ sd->skilltimerskill[i].y = y;
+ sd->skilltimerskill[i].type = type;
+ sd->skilltimerskill[i].flag = flag;
+ sd->timerskill_count++;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if(src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ nullpo_retr(1, md);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(md->skilltimerskill[i].timer == -1) {
+ md->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ md->skilltimerskill[i].src_id = src->id;
+ md->skilltimerskill[i].target_id = target;
+ md->skilltimerskill[i].skill_id = skill_id;
+ md->skilltimerskill[i].skill_lv = skill_lv;
+ md->skilltimerskill[i].map = src->m;
+ md->skilltimerskill[i].x = x;
+ md->skilltimerskill[i].y = y;
+ md->skilltimerskill[i].type = type;
+ md->skilltimerskill[i].flag = flag;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if(src->type == BL_PET) { // [Valaris]
+ struct pet_data *pd = (struct pet_data *)src;
+ nullpo_retr(1, pd);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(pd->skilltimerskill[i].timer == -1) {
+ pd->skilltimerskill[i].timer = add_timer(tick, skill_timerskill, src->id, i);
+ pd->skilltimerskill[i].src_id = src->id;
+ pd->skilltimerskill[i].target_id = target;
+ pd->skilltimerskill[i].skill_id = skill_id;
+ pd->skilltimerskill[i].skill_lv = skill_lv;
+ pd->skilltimerskill[i].map = src->m;
+ pd->skilltimerskill[i].x = x;
+ pd->skilltimerskill[i].y = y;
+ pd->skilltimerskill[i].type = type;
+ pd->skilltimerskill[i].flag = flag;
+
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_cleartimerskill(struct block_list *src)
+{
+ int i;
+
+ nullpo_retr(0, src);
+
+ if(src->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)src;
+ nullpo_retr(0, sd);
+
+ if (sd->timerskill_count <= 0)
+ return 0;
+
+ for(i=0;i<MAX_SKILLTIMERSKILL && sd->timerskill_count > 0;i++) {
+ if(sd->skilltimerskill[i].timer != -1) {
+ delete_timer(sd->skilltimerskill[i].timer, skill_timerskill);
+ sd->skilltimerskill[i].timer = -1;
+ sd->timerskill_count--;
+ }
+ }
+ }
+ else if(src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ nullpo_retr(0, md);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(md->skilltimerskill[i].timer != -1) {
+ delete_timer(md->skilltimerskill[i].timer, skill_timerskill);
+ md->skilltimerskill[i].timer = -1;
+ }
+ }
+ }
+ else if(src->type == BL_PET) { // Ya forgot this one, Valaris. [Skotlex]
+ struct pet_data *pd = (struct pet_data *)src;
+ nullpo_retr(1, pd);
+ for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) {
+ if(pd->skilltimerskill[i].timer != -1) {
+ delete_timer(pd->skilltimerskill[i].timer, skill_timerskill);
+ pd->skilltimerskill[i].timer = -1;
+ }
+ }
+ }
+ return 0;
+}
+
+struct castend_delay {
+ struct block_list *src;
+ int target;
+ int id;
+ int lv;
+ int flag;
+};
+int skill_castend_delay_sub (int tid, unsigned int tick, int id, int data)
+{
+ struct castend_delay *dat = (struct castend_delay *)data;
+ struct block_list *target = map_id2bl(dat->target);
+
+ if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL)
+ skill_castend_damage_id(dat->src, target, dat->id, dat->lv, tick, dat->flag);
+ aFree(dat);
+ return 0;
+}
+int skill_castend_delay (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct castend_delay *dat;
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ dat = (struct castend_delay *)aCalloc(1, sizeof(struct castend_delay));
+ dat->src = src;
+ dat->target = bl->id;
+ dat->id = skillid;
+ dat->lv = skilllv;
+ dat->flag = flag;
+ add_timer (tick, skill_castend_delay_sub, src->id, (int)dat);
+
+ return 0;
+}
+
+/* ”Í?ƒXƒLƒ‹Žg—p?—??¬•ª‚¯‚±‚±‚Ü‚Å
+ * -------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?AIDŽw’è?U?Œn?j
+ * ?iƒXƒpƒQƒbƒeƒB‚ÉŒü‚¯‚Ä‚P?‘O?i?I(ƒ_ƒ?ƒ|)?j
+ *------------------------------------------
+ */
+int skill_castend_damage_id (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct map_session_data *sd = NULL, *tsd = NULL;
+ struct status_change *sc_data;
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_castend_damage_id: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,tick,flag);
+ return 0;
+ }
+ if (skillid > 0 && skilllv <= 0) return 0;
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (src->type == BL_PC) {
+ nullpo_retr(1, sd = (struct map_session_data *)src);
+ }
+
+ if (bl->prev == NULL)
+ return 1;
+ if (bl->type == BL_PC) {
+ nullpo_retr(1, tsd = (struct map_session_data *)bl);
+ }
+
+ if ((skillid == CR_GRANDCROSS || skillid == NPC_GRANDDARKNESS) && src != bl)
+ bl = src;
+
+ if (status_isdead(src) || (src != bl && status_isdead(bl)))
+ return 1;
+
+ sc_data = status_get_sc_data(src);
+
+ map_freeblock_lock();
+
+ switch(skillid)
+ {
+ /* •?Ší?U?ŒnƒXƒLƒ‹ */
+ case SM_BASH: /* ƒoƒbƒVƒ… */
+ case MC_MAMMONITE: /* ƒ?ƒ}?ƒiƒCƒg */
+ case TF_DOUBLE:
+ case AC_DOUBLE: /* ƒ_ƒuƒ‹ƒXƒgƒŒƒCƒtƒBƒ“ƒO */
+ case AC_SHOWER: /* ƒAƒ??ƒVƒƒƒ?? */
+ case AS_SONICBLOW: /* ƒ\ƒjƒbƒNƒuƒ?? */
+ case KN_PIERCE: /* ƒsƒA?ƒX */
+ case KN_SPEARBOOMERANG: /* ƒXƒsƒAƒu?ƒ?ƒ‰ƒ“ */
+ case KN_BRANDISHSPEAR: /* ƒuƒ‰ƒ“ƒfƒBƒbƒVƒ…ƒXƒsƒA */
+ case TF_POISON: /* ƒCƒ“ƒxƒiƒ€ */
+ case TF_SPRINKLESAND: /* ?»‚Ü‚« */
+ case AC_CHARGEARROW: /* ƒ`ƒƒ?ƒWƒAƒ?? */
+ case RG_RAID: /* ƒTƒvƒ‰ƒCƒYƒAƒ^ƒbƒN */
+ case RG_INTIMIDATE: /* ƒCƒ“ƒeƒBƒ~ƒfƒCƒg */
+ case AM_ACIDTERROR: /* ƒAƒVƒbƒhƒeƒ‰? */
+ case BA_MUSICALSTRIKE: /* ƒ~ƒ…?ƒWƒJƒ‹ƒXƒgƒ‰ƒCƒN */
+ case DC_THROWARROW: /* –î?‚¿ */
+ case BA_DISSONANCE: /* •s‹¦˜a‰¹ */
+ case CR_HOLYCROSS: /* ƒz?ƒŠ?ƒNƒ?ƒX */
+ case NPC_DARKCROSS:
+ case CR_SHIELDCHARGE:
+ case CR_SHIELDBOOMERANG:
+ /* ˆÈ‰ºMOB?—p */
+ /* ???U??ASPŒ¸?­?U??A‰“‹——£?U??A–hŒä–³Ž‹?U??A‘½’i?U? */
+ case NPC_PIERCINGATT:
+ case NPC_MENTALBREAKER:
+ case NPC_RANGEATTACK:
+ case NPC_CRITICALSLASH:
+ case NPC_COMBOATTACK:
+ /* •K’†?U??A“Å?U??AˆÃ??U??A’¾??U??AƒXƒ^ƒ“?U? */
+ case NPC_GUIDEDATTACK:
+ case NPC_POISON:
+ case NPC_BLINDATTACK:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ /* ?Ή»?U??AŽô‚¢?U??A?‡–°?U??Aƒ‰ƒ“ƒ_ƒ€ATK?U? */
+ case NPC_PETRIFYATTACK:
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case NPC_RANDOMATTACK:
+ /* ?…??«?U??A’n??«?U??A‰Î??«?U??A•—??«?U? */
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ /* “Å??«?U??A?¹??«?U??AˆÅ??«?U??A”O??«?U??ASPŒ¸?­?U? */
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_TELEKINESISATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_BREAKARMOR:
+ case NPC_BREAKWEAPON:
+ case NPC_BREAKHELM:
+ case NPC_BREAKSHIELD:
+ case LK_AURABLADE: /* ƒI?ƒ‰ƒuƒŒ?ƒh */
+ case LK_SPIRALPIERCE: /* ƒXƒpƒCƒ‰ƒ‹ƒsƒA?ƒX */
+ case LK_HEADCRUSH: /* ƒwƒbƒhƒNƒ‰ƒbƒVƒ… */
+ case LK_JOINTBEAT: /* ƒWƒ‡ƒCƒ“ƒgƒr?ƒg */
+ case CG_ARROWVULCAN: /* ƒAƒ??ƒoƒ‹ƒJƒ“ */
+ case HW_MAGICCRASHER: /* ƒ}ƒWƒbƒNƒNƒ‰ƒbƒVƒƒ? */
+ case ASC_METEORASSAULT: /* ƒ?ƒeƒIƒAƒTƒ‹ƒg */
+ case ITM_TOMAHAWK:
+ case MO_TRIPLEATTACK:
+ case CH_CHAINCRUSH: /* ˜A’Œ•ö? */
+ case CH_TIGERFIST: /* •šŒÕŒ? */
+ case PA_SHIELDCHAIN: // Shield Chain
+ case PA_SACRIFICE: // Sacrifice, Aru's style.
+ case WS_CARTTERMINATION: // Cart Termination
+ case AS_VENOMKNIFE:
+ case HT_PHANTASMIC:
+ case HT_POWER:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case MO_COMBOFINISH:
+ if (!(flag&1) && sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_MONK)
+ { //Becomes a splash attack when Soul Linked.
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-5,bl->y-5,bl->x+5,bl->y+5,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ } else
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_attack_area, src->m,
+ src->x-2, src->y-2, src->x+2, src->y+2, BL_CHAR,
+ BF_WEAPON, src, src, skillid, skilllv, tick, flag, BCT_ENEMY);
+ break;
+ case TK_JUMPKICK:
+ if(sd) {
+ if (!pc_can_move(sd))
+ return 0;
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ pc_movepos(sd,bl->x,bl->y,0);
+ clif_slide(src,bl->x,bl->y);
+ }
+ break;
+ case ASC_BREAKER: /* ƒ\ƒEƒ‹ƒuƒŒ?ƒJ? */ // [DracoRPG]
+ // Separate weapon and magic attacks
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case SN_SHARPSHOOTING: /* ƒVƒƒ?ƒvƒVƒ…?ƒeƒBƒ“ƒO */
+ // Does it stop if touch an obstacle? it shouldn't shoot trough walls
+ map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,2,BL_CHAR,
+ BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs
+ break;
+
+ case MO_INVESTIGATE: /* ?™¤ */
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ break;
+
+ case RG_BACKSTAP: /* ƒoƒbƒNƒXƒ^ƒu */
+ {
+ int dir = map_calc_dir(src, bl->x, bl->y), t_dir = status_get_dir(bl);
+ if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) {
+ if (sc_data && sc_data[SC_HIDING].timer != -1)
+ status_change_end(src, SC_HIDING, -1); // ƒnƒCƒfƒBƒ“ƒO‰ð?œ
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag);
+ dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest]
+ if (tsd)
+ tsd->dir = dir;
+ else if (bl->type == BL_MOB) {
+ struct mob_data *tmd = (struct mob_data *)bl;
+ if (tmd) tmd->dir = dir;
+ }
+ clif_changed_dir(bl);
+ }
+ else if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE: /* Žw? */
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (battle_config.finger_offensive_type && sd) {
+ int i;
+ for (i = 1; i < sd->spiritball_old; i++)
+ skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag);
+ sd->canmove_tick = tick + (sd->spiritball_old - 1) * 200;
+ }
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ }
+ break;
+
+ case MO_CHAINCOMBO: /* ˜A‘Å?¶ */
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ if (sc_data && sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ }
+ break;
+
+ case KN_CHARGEATK:
+ case MO_EXTREMITYFIST: /* ˆ¢?C—…”e–PŒ? */
+ {
+ if(sd && !check_distance_bl(src, bl, 1)) { //Need to move to target.
+ struct walkpath_data wpd;
+ int dx,dy,speed;
+
+ if (!pc_can_move(sd)) { //You need to be able to move to attack/reach target.
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ dx = bl->x - sd->bl.x;
+ dy = bl->y - sd->bl.y;
+ if(dx > 0) dx++;
+ else if(dx < 0) dx--;
+ if (dy > 0) dy++;
+ else if(dy < 0) dy--;
+ if(dx == 0 && dy == 0) dx++;
+ if (path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) {
+ dx = bl->x - sd->bl.x;
+ dy = bl->y - sd->bl.y;
+ if(path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ sd->to_x = sd->bl.x + dx;
+ sd->to_y = sd->bl.y + dy;
+ if (skillid == KN_CHARGEATK) //Store distance in flag [Skotlex]
+ flag = wpd.path_len; //Path_len is a pretty good approximate of the distance.
+ if (skillid != MO_EXTREMITYFIST || battle_check_target(src, bl, BCT_ENEMY) > 0) //Check must be done here because EF should be broken this way.. [Skotlex]
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ speed = sd->speed;
+ sd->speed = 20; //Simulate ultra fast walk speed. [Skotlex]
+// clif_updatestatus(sd, SP_SPEED); //Not sure yet if this is needed.
+ clif_walkok(sd);
+ clif_movechar(sd);
+ if(dx < 0) dx = -dx;
+ if(dy < 0) dy = -dy;
+ sd->attackabletime = sd->canmove_tick = tick + 100 + sd->speed * ((dx > dy)? dx:dy);
+ if(sd->canact_tick < sd->canmove_tick)
+ sd->canact_tick = sd->canmove_tick;
+ sd->speed = speed;
+// clif_updatestatus(sd, SP_SPEED);
+ pc_movepos(sd,sd->to_x,sd->to_y,1);
+ }
+ else //Assume minimum distance of 1 for Charge.
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,skillid == KN_CHARGEATK?1:flag);
+ if (skillid == MO_EXTREMITYFIST && sc_data)
+ {
+ if (sc_data[SC_EXPLOSIONSPIRITS].timer != -1)
+ status_change_end(src, SC_EXPLOSIONSPIRITS, -1);
+ if (sc_data[SC_BLADESTOP].timer != -1)
+ status_change_end(src,SC_BLADESTOP,-1);
+ if (sc_data[SC_COMBO].timer != -1) //This is one is here to make combo end even if skill failed.
+ status_change_end(src,SC_COMBO,-1);
+ }
+ }
+ break;
+
+ /* •?ŠíŒn”Í??U?ƒXƒLƒ‹ */
+ case AS_GRIMTOOTH: /* ƒOƒŠƒ€ƒgƒD?ƒX */
+ case MC_CARTREVOLUTION: /* ƒJ?ƒgƒŒƒ”ƒHƒŠƒ…?ƒVƒ‡ƒ“ */
+ case NPC_SPLASHATTACK: /* ƒXƒvƒ‰ƒbƒVƒ…ƒAƒ^ƒbƒN */
+ if(flag&1){
+ /* ŒÂ•Ê‚Ƀ_ƒ??ƒW‚ð?‚¦‚é */
+ if(bl->id!=skill_area_temp[1]){
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
+ 0x0500);
+ }
+ } else {
+ int ar = 1;
+ int x = bl->x, y = bl->y;
+ switch (skillid) {
+ case NPC_SPLASHATTACK:
+ ar=3;
+ break;
+ }
+
+ skill_area_temp[1]=bl->id;
+ skill_area_temp[2]=x;
+ skill_area_temp[3]=y;
+ /* ‚Ü‚¸ƒ^?ƒQƒbƒg‚É?U?‚ð‰Á‚¦‚é */
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0);
+ /* ‚»‚ÌŒãƒ^?ƒQƒbƒgˆÈŠO‚Ì”Í??‚Ì“G‘S?‚É?—?‚ð?s‚¤ */
+ map_foreachinarea(skill_area_sub,
+ bl->m,x-ar,y-ar,x+ar,y+ar,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case AS_SPLASHER:
+ if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by.
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ else
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-2, bl->y-2, bl->x+2, bl->y+2, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count);
+ skill_area_temp[0]--; //Substract one, the original target shouldn't count. [Skotlex]
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+
+ case SM_MAGNUM:
+ if(flag&1){
+ int dist = distance_blxy (bl, skill_area_temp[2], skill_area_temp[3]);
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,
+ 0x0500|dist);
+ } else {
+ skill_area_temp[1]=src->id;
+ skill_area_temp[2]=src->x;
+ skill_area_temp[3]=src->y;
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-2,src->y-2,src->x+2,src->y+2,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_start (src,SC_WATK_ELEMENT,3,20,0,0,10000,0); //Initiate 10% of your damage becomes fire element.
+ clif_skill_nodamage (src,src,skillid,skilllv,1);
+ }
+ break;
+
+ case KN_BOWLINGBASH: /* ƒ{ƒEƒŠƒ“ƒOƒoƒbƒVƒ… */
+ if(flag&1){
+ /* ŒÂ•Ê‚Ƀ_ƒ??ƒW‚ð?‚¦‚é */
+ if(bl->id!=skill_area_temp[1])
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500);
+ } else {
+ int i,c; /* ‘¼?l‚©‚ç•·‚¢‚½“®‚«‚È‚Ì‚ÅŠÔˆá‚Á‚Ä‚é‰Â”\?«‘å?•?—¦‚ª?‚¢‚Á‚·?„?ƒ */
+ /* ‚Ü‚¸ƒ^?[ƒQƒbƒg‚É?UŒ‚‚ð‰Á‚¦‚é */
+ c = skill_get_blewcount(skillid,skilllv);
+ if(map_flag_gvg(bl->m) || status_get_mexp(bl))
+ c = 0;
+ for(i=0;i<c;i++){
+ skill_blown(src,bl,0x20000|1);
+ skill_area_temp[0]=0;
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY ,
+ skill_area_sub_count);
+ if(skill_area_temp[0]>1) break;
+ }
+ clif_blown(bl); //Update target pos.
+ skill_area_temp[1]=bl->id;
+ /* ‚»‚ÌŒãƒ^?ƒQƒbƒgˆÈŠO‚Ì”Í??‚Ì“G‘S?‚É?—?‚ð?s‚¤ */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0);
+ }
+ break;
+
+ case KN_SPEARSTAB: /* ƒXƒsƒAƒXƒ^ƒu */
+ if(flag&1){
+ /* ŒÂ•Ê‚Ƀ_ƒ??[ƒW‚ð—^‚¦‚é */
+ if (bl->id==skill_area_temp[1])
+ break;
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500) && !status_get_mexp(bl))
+ skill_blown(src,bl,skill_area_temp[2]);
+ } else {
+ int x=bl->x,y=bl->y,i,dir;
+ /* ‚Ü‚¸ƒ^?[ƒQƒbƒg‚É?UŒ‚‚ð‰Á‚¦‚é */
+ dir = map_calc_dir(bl,src->x,src->y);
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = skill_get_blewcount(skillid,skilllv)|dir<<20;
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0) && !status_get_mexp(bl))
+ skill_blown(src,bl,skill_area_temp[2]);
+ for (i=0;i<4;i++) {
+ map_foreachinarea(skill_area_sub,bl->m,x,y,x,y,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ x += dirx[dir];
+ y += diry[dir];
+ }
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex]
+ {
+ skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target.
+ if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0))
+ map_foreachinarea(skill_area_sub,bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex]
+ // clif_skill_nodamage(src,bl,skillid,skilllv,0); //Can't make this one display the correct attack animation delay :/
+ clif_damage(src,bl,tick,status_get_amotion(src),0,0,1,4,0); //Displays MISS, but better than nothing :X
+ skill_addtimerskill(src, tick + 1000, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag);
+ break;
+
+ case ALL_RESURRECTION: /* ƒŠƒUƒŒƒNƒVƒ‡ƒ“ */
+ case PR_TURNUNDEAD: /* ƒ^?ƒ“ƒAƒ“ƒfƒbƒh */
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl)))
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ /* –‚–@ŒnƒXƒLƒ‹ */
+ case MG_SOULSTRIKE: /* ƒ\ƒEƒ‹ƒXƒgƒ‰ƒCƒN */
+ case NPC_DARKSTRIKE: /*ˆÅƒ\ƒEƒ‹ƒXƒgƒ‰ƒCƒN*/
+ case MG_COLDBOLT: /* ƒR?[ƒ‹ƒhƒ{ƒ‹ƒg */
+ case MG_FIREBOLT: /* ƒtƒ@ƒCƒA?[ƒ{ƒ‹ƒg */
+ case MG_LIGHTNINGBOLT: /* ƒ‰ƒCƒgƒjƒ“ƒOƒ{ƒ‹ƒg */
+ case WZ_EARTHSPIKE: /* ƒA?[ƒXƒXƒpƒCƒN */
+ case AL_HEAL: /* ƒq?[ƒ‹ */
+ case AL_HOLYLIGHT: /* ƒz?[ƒŠ?[ƒ‰ƒCƒg */
+ case WZ_JUPITEL: /* ƒ†ƒsƒeƒ‹ƒTƒ“ƒ_?[ */
+ case NPC_DARKTHUNDER: /*ˆÅƒ†ƒsƒeƒ‹*/
+ case NPC_MAGICALATTACK: /* MOB:–‚–@‘Å??U? */
+ case PR_ASPERSIO: /* ƒAƒXƒyƒ‹ƒVƒI */
+ case MG_FROSTDIVER: /* ƒtƒ?ƒXƒgƒ_ƒCƒo?[ */
+ case WZ_SIGHTBLASTER:
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case WZ_WATERBALL: /* ƒEƒH?ƒ^?ƒ{?ƒ‹ */
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ if (skilllv>1) {
+ int range = skilllv > 5 ? 2 : skilllv/2;
+ //Rain doesn't affect WATERBALL (Rain has been removed at kRO) [Lupus]
+ //int cnt = (!map[src->m].flag.rain) ? skill_count_water(src,range) - 1 : skill_get_num(skillid,skilllv) - 1;
+ int cnt = (src->type==BL_PC)?skill_count_water(src,range) - 1:(skilllv>3?24:8);
+ if (cnt > 0)
+ skill_addtimerskill(src,tick+150,bl->id,0,0,
+ skillid,skilllv,cnt,flag);
+ }
+ break;
+
+ case PR_BENEDICTIO: /* ?¹??~•Ÿ */
+ { //Should attack undead and demons. [Skotlex]
+ int race = status_get_race(bl);
+ if (battle_check_undead(race, status_get_elem_type(bl)) || race == 6)
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag);
+ }
+ break;
+
+ /* –‚–@Œn”Í??U?ƒXƒLƒ‹ */
+ case MG_NAPALMBEAT: /* ƒiƒp?ƒ€ƒr?ƒg */
+ case MG_FIREBALL: /* ƒtƒ@ƒCƒ„?ƒ{?ƒ‹ */
+ case WZ_SIGHTRASHER: /* ƒTƒCƒgƒ‰ƒbƒVƒƒ?[ */
+ if (flag & 1) {
+ /* ŒÂ•Ê‚Ƀ_ƒ??ƒW‚ð?‚¦‚é */
+ if (bl->id != skill_area_temp[1]){
+ if(skillid == MG_FIREBALL){ /* ƒtƒ@ƒCƒ„?ƒ{?ƒ‹‚È‚ç’†?S‚©‚ç‚Ì‹——£‚ðŒvŽZ */
+ skill_area_temp[0] = distance_blxy(bl, skill_area_temp[2], skill_area_temp[3]);
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,
+ skill_area_temp[0]| 0x0500);
+ }
+ } else {
+ int ar;
+ skill_area_temp[0]=0;
+ skill_area_temp[1]=bl->id;
+ switch (skillid) {
+ case MG_NAPALMBEAT:
+ ar = 1;
+ /* ƒiƒp?[ƒ€ƒr?[ƒg‚Í•ªŽUƒ_ƒ??[ƒW‚È‚Ì‚Å“G‚Ì?”‚ð?”‚¦‚é */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY,
+ skill_area_sub_count);
+ break;
+ case MG_FIREBALL:
+ ar = 2;
+ skill_area_temp[2]=bl->x;
+ skill_area_temp[3]=bl->y;
+ break;
+ case WZ_SIGHTRASHER:
+ default:
+ ar = 3;
+ bl = src;
+ status_change_end(src,SC_SIGHT,-1);
+ break;
+ }
+ if (skillid==WZ_SIGHTRASHER) {
+ /* ƒXƒLƒ‹ƒGƒtƒFƒNƒg•\Ž¦ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else {
+ /* ƒ^?[ƒQƒbƒg‚É?UŒ‚‚ð‰Á‚¦‚é(ƒXƒLƒ‹ƒGƒtƒFƒNƒg•\Ž¦) */
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,
+ skill_area_temp[0]);
+ }
+ /* ƒ^?[ƒQƒbƒgˆÈŠO‚͈͓̔à‚Ì“G‘S‘Ì‚É?ˆ—?‚ð?s‚¤ */
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case HW_NAPALMVULCAN: // Fixed By SteelViruZ
+ if (flag & 1) {
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY,
+ skill_area_sub_count);
+ skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case WZ_FROSTNOVA: /* ƒtƒ?ƒXƒgƒmƒ”ƒ@ */
+ map_foreachinarea(skill_attack_area, src->m,
+ src->x-5, src->y-5, bl->x+5, bl->y+5, BL_CHAR,
+ BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY);
+ break;
+
+ case SL_STIN:
+ case SL_STUN:
+ case SL_SMA:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ /* ‚»‚Ì‘¼ */
+ case HT_BLITZBEAT: /* ƒuƒŠƒbƒcƒr?ƒg */
+ if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by.
+ skill_attack(BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]);
+ } else {
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ if (flag & 0xf00000) //Warning, 0x100000 is currently BCT_NEUTRAL, so don't mix it when asking for the enemy. [Skotlex]
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count);
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_CHAR,
+ src, skillid, skilllv, tick, BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case CR_GRANDCROSS: /* ƒOƒ‰ƒ“ƒhƒNƒ?ƒX */
+ case NPC_GRANDDARKNESS: /*ˆÅƒOƒ‰ƒ“ƒhƒNƒ?ƒX*/
+ /* ƒXƒLƒ‹ƒ†ƒjƒbƒg”z’u */
+ skill_castend_pos2(src,bl->x,bl->y,skillid,skilllv,tick,0);
+ if(sd)
+ sd->canmove_tick = tick + 900;
+ else if(src->type == BL_MOB)
+ mob_changestate((struct mob_data *)src,MS_DELAY,900);
+ break;
+
+ case NPC_DARKBREATH:
+ clif_emotion(src,7);
+ skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case SN_FALCONASSAULT: /* ƒtƒ@ƒ‹ƒRƒ“ƒAƒTƒ‹ƒg */
+ case PA_PRESSURE: /* ƒvƒŒƒbƒVƒƒ? */
+ case CR_ACIDDEMONSTRATION: // Acid Demonstration
+ case TF_THROWSTONE: /* ?ΓŠ‚° */
+ case NPC_SMOKING: /* ƒXƒ‚?ƒLƒ“ƒO */
+ skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ // Celest
+ case PF_SOULBURN:
+ if (rand()%100 < (skilllv < 5 ? 30 + skilllv * 10 : 70)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (skilllv == 5)
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 );
+ if (tsd) {
+ tsd->status.sp = 0;
+ clif_updatestatus(tsd,SP_SP);
+ }
+ } else {
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ if (skilllv == 5)
+ skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 );
+ if (sd) {
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_SP);
+ }
+ }
+ if (sd) pc_blockskill_start (sd, skillid, (skilllv < 5 ? 10000: 15000));
+ break;
+
+ case NPC_SELFDESTRUCTION: /* Ž©”š */
+ if (flag & 1) {
+ /* ŒÂ•Ê‚Ƀ_ƒ??[ƒW‚ð—^‚¦‚é */
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_MISC, src, src, bl, NPC_SELFDESTRUCTION, skilllv, tick, flag);
+ } else {
+ skill_area_temp[1] = bl->id;
+ if(bl->type==BL_PC)
+ skill_area_temp[2] = 999999;
+ else
+ skill_area_temp[2] = status_get_hp(src);
+ clif_skill_nodamage(src, src, NPC_SELFDESTRUCTION, -1, 1);
+ if(bl->type==BL_PC)
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-10, bl->y-10, bl->x+10, bl->y+10, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ else
+ map_foreachinarea(skill_area_sub, bl->m,
+ bl->x-5, bl->y-5, bl->x+5, bl->y+5, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ battle_damage(src, src, skill_area_temp[2], 0);
+ }
+ break;
+
+ /* HP‹z?/HP‹z?–‚–@ */
+ case NPC_BLOODDRAIN:
+ case NPC_ENERGYDRAIN:
+ {
+ int heal = skill_attack( (skillid == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC,
+ src, src, bl, skillid, skilllv, tick, flag);
+ if (heal > 0){
+ struct block_list tbl;
+ tbl.id = 0;
+ tbl.type = BL_NUL;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(&tbl, src, AL_HEAL, heal, 1);
+ battle_heal(NULL, src, heal, 0, 0);
+ }
+ }
+ break;
+
+ case 0:
+ if(sd) {
+ if (flag & 3){
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500);
+ } else {
+ int ar = sd->splash_range;
+ skill_area_temp[1] = bl->id;
+ map_foreachinarea(skill_area_sub,
+ bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, BL_CHAR,
+ src, skillid, skilllv, tick, flag | BCT_ENEMY | 1,
+ skill_castend_damage_id);
+ }
+ }
+ break;
+
+ default:
+ ShowWarning("Unknown skill used:%d\n",skillid);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?AIDŽw’èŽx‰‡Œn?j
+ *------------------------------------------
+ */
+int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct map_session_data *dstsd = NULL;
+ struct mob_data *md = NULL;
+ struct mob_data *dstmd = NULL;
+ int i;
+ int sc_def_vit, sc_def_mdef;
+// int sc_dex, sc_luk;
+
+ if(skillid < 0)
+ { // remove the debug print when this case is finished
+ ShowDebug("skill_castend_damage_id: skillid=%i\ncall: %p %p %i %i %i %i",skillid,
+ src, bl,skillid,skilllv,tick,flag);
+ return 0;
+ }
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (src->type == BL_PC) {
+ nullpo_retr (1, sd = (struct map_session_data *)src);
+ } else if (src->type == BL_MOB) {
+ nullpo_retr (1, md = (struct mob_data *)src);
+ }
+
+ sc_def_vit = status_get_sc_def_vit (bl);
+ sc_def_mdef = status_get_sc_def_mdef (bl);
+
+ if (bl->type == BL_PC){
+ nullpo_retr (1, dstsd = (struct map_session_data *)bl);
+ } else if (bl->type == BL_MOB){
+ nullpo_retr (1, dstmd = (struct mob_data *)bl);
+ }
+
+ if(bl->prev == NULL)
+ return 1;
+ if(sd && pc_isdead(sd))
+ return 1;
+ if(dstsd && pc_isdead(dstsd) && skillid != ALL_RESURRECTION && skillid != PR_REDEMPTIO)
+ return 1;
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if (sd && skillnotok(skillid, sd)) // [MouseJstr]
+// return 0;
+
+ //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
+ switch (skillid) {
+ case AL_HEAL:
+ case ALL_RESURRECTION:
+ case PR_ASPERSIO:
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) {
+ if (battle_check_target(src, bl, BCT_ENEMY) < 1) {
+ //Offensive heal does not works on non-enemies. [Skotlex]
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ return 0;
+ }
+ if(!sd) {
+ //Prevent non-players from casting offensive heal. [Skotlex]
+ clif_emotion(src, 4);
+ return 0;
+ }
+ return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag);
+ }
+ break;
+ }
+
+ map_freeblock_lock();
+ switch(skillid)
+ {
+ case AL_HEAL: /* ƒq?ƒ‹ */
+ {
+ int heal = skill_calc_heal(src, skilllv);
+ int heal_get_jobexp;
+ int skill;
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+
+ if (skilllv > 10)
+ heal = 9999; //9999ƒq?[ƒ‹
+ if (status_isimmune(bl) || (dstmd && dstmd->class_ == MOBID_EMPERIUM))
+ heal=0; /* ?‹à峃J?ƒh?iƒq?ƒ‹—Ê‚O?j */
+ if (sd) {
+ if ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) // ƒ?ƒfƒBƒeƒCƒeƒBƒI
+ heal += heal * skill * 2 / 100;
+ if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id &&
+ (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) //Ž©•ª‚à?Û‚àPCA?Û‚ªŽ©•ª‚̃p?ƒgƒi?AŽ©•ª‚ªƒXƒpƒmƒrAŽ©•ª‚ªŠ‚È‚ç
+ heal = heal*2; //ƒXƒpƒmƒr‚̉łª’U“߂Ƀq?ƒ‹‚·‚é‚Æ2”{‚É‚È‚é
+ }
+
+ if (sc_data && sc_data[SC_KAITE].timer != -1
+ && !(status_get_mode(src)&MD_BOSS)
+ ) { //Bounce back heal
+ if (--sc_data[SC_KAITE].val2 <= 0)
+ status_change_end(bl, SC_KAITE, -1);
+ if (src == bl) heal=0; //When you try to heal yourself and you are under Kaite, the heal is voided.
+ clif_skill_nodamage (src, src, skillid, heal, 1);
+ heal_get_jobexp = battle_heal(NULL,src,heal,0,0);
+ } else {
+ clif_skill_nodamage (src, bl, skillid, heal, 1);
+ heal_get_jobexp = battle_heal(NULL,bl,heal,0,0);
+ }
+
+ // JOB??’lŠl“¾
+ if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){
+ heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100;
+ if (heal_get_jobexp <= 0)
+ heal_get_jobexp = 1;
+ if (bl->type == BL_PC) // Give heal experience only when healing players [Harbin]
+ pc_gainexp (sd, 0, heal_get_jobexp);
+ }
+ }
+ break;
+
+ case PR_REDEMPTIO:
+ if (sd && !(flag&1)) {
+ if (sd->status.party_id == 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_area_temp[0] = 0;
+ party_foreachsamemap(skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ if (skill_area_temp[0] == 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty...
+ if (skill_area_temp[0] > 0 && !map[src->m].flag.nopenalty) { //Apply penalty
+ sd->status.base_exp -= pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000; //0.2% penalty per each.
+ sd->status.job_exp -= pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000;
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,0,0); //SC_COMA :P
+ break;
+ } else if (dstsd && pc_isdead(dstsd) && flag&1) { //Revive
+ skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code.
+ skilllv = 3; //Resurrection level 3 is used
+ } else //Invalid target, skip resurrection.
+ break;
+
+ case ALL_RESURRECTION: /* ƒŠƒUƒŒƒNƒVƒ‡ƒ“ */
+ if(sd && map_flag_gvg(bl->m))
+ { //No reviving in WoE grounds!
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(dstsd) {
+ int per = 0;
+ if (map[bl->m].flag.pvp && dstsd->pvp_point < 0)
+ break; /* PVP‚Å•œŠˆ•s‰Â”\?‘Ô */
+
+ if (pc_isdead(dstsd)) { /* Ž€–S”»’è */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ switch(skilllv){
+ case 1: per=10; break;
+ case 2: per=30; break;
+ case 3: per=50; break;
+ case 4: per=80; break;
+ }
+ dstsd->status.hp = dstsd->status.max_hp * per / 100;
+ if (dstsd->status.hp <= 0) dstsd->status.hp = 1;
+ if (dstsd->special_state.restart_full_recover) { /* ƒIƒVƒŠƒXƒJ?ƒh */
+ dstsd->status.hp = dstsd->status.max_hp;
+ dstsd->status.sp = dstsd->status.max_sp;
+ }
+ pc_setstand(dstsd);
+ if(battle_config.pc_invincible_time > 0)
+ pc_setinvincibletimer(dstsd, battle_config.pc_invincible_time);
+ clif_updatestatus(dstsd, SP_HP);
+ clif_resurrection(bl, 1);
+ if(sd && sd != dstsd && battle_config.resurrection_exp > 0) {
+ int exp = 0,jexp = 0;
+ int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
+ if(lv > 0) {
+ exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (exp < 1) exp = 1;
+ }
+ if(jlv > 0) {
+ jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (jexp < 1) jexp = 1;
+ }
+ if(exp > 0 || jexp > 0)
+ pc_gainexp (sd, exp, jexp);
+ }
+ }
+ }
+ break;
+
+ case AL_DECAGI: /* ‘¬“xŒ¸?­ */
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if (rand() % 100 < (40 + skilllv * 2 + (status_get_lv(src) + status_get_int(src))/5 +(sc_def_mdef-100))) { //0 defense is sc_def_mdef == 100! [Skotlex]
+ i = skill_get_time(skillid,skilllv);
+ if (bl->type == BL_PC) i/=2; //Halved duration for Players
+ status_change_start (bl, SkillStatusChangeTable[skillid], skilllv, 0, 0, 0, i, 0);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ }
+ break;
+
+ case AL_CRUCIS:
+ if (flag & 1) {
+ int race = status_get_race (bl), ele = status_get_elem_type (bl);
+ if (battle_check_target (src, bl, BCT_ENEMY) && (race == 6 || battle_check_undead (race, ele))) {
+ int slv = status_get_lv (src),tlv = status_get_lv (bl);
+ int rate = 23 + skilllv*4 + slv - tlv;
+ if (rand()%100 < rate)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,0,0);
+ }
+ } else {
+ map_foreachinarea(skill_area_sub,
+ src->m, src->x-15, src->y-15, src->x+15, src->y+15, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ }
+ break;
+
+ case PR_LEXDIVINA: /* ƒŒƒbƒNƒXƒfƒBƒr?ƒi */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if (sc_data && sc_data[SC_SILENCE].timer != -1) {
+ status_change_end(bl,SC_SILENCE, -1);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ } else if (rand() % 100 < sc_def_vit) {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ } else
+ clif_skill_nodamage (src, bl, skillid, skilllv, 0);
+ }
+ break;
+
+ case SA_ABRACADABRA:
+ {
+ int abra_skillid = 0, abra_skilllv;
+ if (sd)
+ { //Crash-fix [Skotlex]
+ //require 1 yellow gemstone even with mistress card or Into the Abyss
+ if ((i = pc_search_inventory(sd, 715)) < 0 )
+ { //bug fixed by Lupus (item pos can be 0, too!)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ pc_delitem(sd, i, 1, 0);
+ }
+ do {
+ abra_skillid = rand() % MAX_SKILL_ABRA_DB;
+ if (skill_abra_db[abra_skillid].req_lv > skilllv ||
+ rand()%10000 >= skill_abra_db[abra_skillid].per || //db‚ÉŠî‚­ƒŒƒxƒ‹?Šm—¦”»’è
+ (abra_skillid >= NPC_PIERCINGATT && abra_skillid <= NPC_SUMMONMONSTER) || //NPCƒXƒLƒ‹‚̓_ƒ?
+ skill_get_unit_flag(abra_skillid) & UF_DANCE) //‰‰‘tƒXƒLƒ‹‚̓_ƒ?
+ abra_skillid = 0; // reset to get a new id
+ } while (abra_skillid == 0);
+ abra_skilllv = skill_get_max(abra_skillid) > skilllv ? skilllv : skill_get_max(abra_skillid);
+ clif_skill_nodamage (src, bl, skillid, skilllv, 1);
+ if (sd)
+ { //Crash-protection against Abracadabra casting pets
+ sd->skillitem = abra_skillid;
+ sd->skillitemlv = abra_skilllv;
+ sd->state.abra_flag = 1;
+ clif_item_skill (sd, abra_skillid, abra_skilllv, "Abracadabra");
+ } else if(src->type == BL_PET)
+ { // [Skotlex]
+ struct pet_data *pd = (struct pet_data *)src;
+ int inf = skill_get_inf(abra_skillid);
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ nullpo_retr(1,(struct map_session_data *)pd->msd);
+ petskill_use(pd, &pd->msd->bl, abra_skillid, abra_skilllv, tick);
+ } else //Assume offensive skills
+ petskill_use(pd, bl, abra_skillid, abra_skilllv, tick);
+ }
+ }
+ break;
+
+ case SA_COMA:
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_FULLRECOVERY:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (status_isimmune(bl))
+ break;
+ if (dstsd) pc_heal (dstsd, dstsd->status.max_hp, dstsd->status.max_sp);
+ else if (dstmd) dstmd->hp = status_get_max_hp(bl);
+ break;
+ case SA_SUMMONMONSTER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd) mob_once_spawn(sd,map[src->m].name,src->x,src->y,"--ja--",-1,1,"");
+ break;
+ case SA_LEVELUP:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, pc_nextbaseexp(sd) * 10 / 100, 0);
+ break;
+ case SA_INSTANTDEATH:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,src,status_get_hp(src)-1,0);
+ break;
+ case SA_QUESTION:
+ case SA_GRAVITY:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_CLASSCHANGE:
+ {
+ //ƒNƒ‰ƒXƒ`ƒFƒ“ƒW—pƒ{ƒXƒ‚ƒ“ƒXƒ^?ID
+ static int changeclass[]={1038,1039,1046,1059,1086,1087,1112,1115
+ ,1157,1159,1190,1272,1312,1373,1492};
+ int class_ = mob_random_class (changeclass,sizeof(changeclass)/sizeof(changeclass[0]));
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd) mob_class_change(dstmd,class_);
+ }
+ break;
+ case SA_MONOCELL:
+ {
+ static int poringclass[]={1002};
+ int class_ = mob_random_class (poringclass,sizeof(poringclass)/sizeof(poringclass[0]));
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd) mob_class_change(dstmd,class_);
+ }
+ break;
+ case SA_DEATH:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,bl,status_get_max_hp(bl),1);
+ break;
+ case SA_REVERSEORCISH:
+ status_change_start(bl, SkillStatusChangeTable[skillid], skilllv,0,0,0,skill_get_time(skillid, skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SA_FORTUNE:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd) pc_getzeny(sd,status_get_lv(bl)*100);
+ break;
+ case SA_TAMINGMONSTER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && dstmd) {
+ for (i = 0; i < MAX_PET_DB; i++) {
+ if (dstmd->class_ == pet_db[i].class_) {
+ pet_catch_process1 (sd, dstmd->class_);
+ break;
+ }
+ }
+ }
+ break;
+
+ case AL_INCAGI: /* ‘¬“x?‰Á */
+ case AL_BLESSING: /* ƒuƒŒƒbƒVƒ“ƒO */
+ case PR_SLOWPOISON:
+ case PR_IMPOSITIO: /* ƒCƒ€ƒ|ƒVƒeƒBƒIƒ}ƒkƒX */
+ case PR_LEXAETERNA: /* ƒŒƒbƒNƒXƒG?ƒeƒ‹ƒi */
+ case PR_SUFFRAGIUM: /* ƒTƒtƒ‰ƒMƒEƒ€ */
+ case PR_BENEDICTIO: /* ?¹??~•Ÿ */
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ else {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ }
+ break;
+
+ case CR_PROVIDENCE: /* ƒvƒ?ƒ”ƒBƒfƒ“ƒX */
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else {
+ if(sd && dstsd){ //Check they are not another crusader [Skotlex]
+ if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case CG_MARIONETTE: /* ƒ}ƒŠƒIƒlƒbƒgƒRƒ“ƒgƒ??ƒ‹ */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ int sc2 = SC_MARIONETTE2;
+
+ if(sc_data && tsc_data){
+ if (sc_data[sc].timer == -1 && tsc_data[sc2].timer == -1) {
+ status_change_start (src,sc,skilllv,0,bl->id,0,skill_get_time(skillid,skilllv),0);
+ status_change_start (bl,sc2,skilllv,0,src->id,0,skill_get_time(skillid,skilllv),0);
+ clif_marionette(src, bl);
+ }
+ else if (sc_data[sc].timer != -1 && tsc_data[sc2].timer != -1 &&
+ sc_data[sc].val3 == bl->id && tsc_data[sc2].val3 == src->id) {
+ status_change_end(src, sc, -1);
+ status_change_end(bl, sc2, -1);
+ clif_marionette(src, 0);
+ }
+ else {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ }
+ break;
+
+ case RG_CLOSECONFINE:
+ if (status_isimmune(bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ else {
+ status_change_start (bl,SkillStatusChangeTable[skillid],skilllv,src->id,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris]
+ case SA_FROSTWEAPON:
+ case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON:
+ if (dstsd) {
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(dstsd->status.weapon == 0 ||
+ dstsd->sc_data[SC_FIREWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WATERWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WINDWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_EARTHWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_SHADOWWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_GHOSTWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_ENCPOISON].timer != -1) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ }
+ if (sd) {
+ int i = pc_search_inventory (sd, skill_db[skillid].itemid[0]);
+ if(i < 0 || sd->status.inventory[i].amount < skill_db[skillid].amount[0]) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ pc_delitem(sd, i, skill_db[skillid].amount[0], 0);
+ }
+
+ if(skilllv < 4 && rand()%100 > (60+skilllv*10) ) { // 100% success rate at lv4 & 5, but lasts longer at lv5
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(dstsd && battle_config.equip_self_break_rate) {
+ if(sd && sd != dstsd) clif_displaymessage(sd->fd,"You broke target's weapon");
+ pc_breakweapon(dstsd);
+ }
+ break;
+ } else {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PR_ASPERSIO: /* ƒAƒXƒyƒ‹ƒVƒI */
+ if (status_isimmune(bl) || dstmd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TK_SEVENWIND:
+ {
+ int sc=-1;
+ switch(skilllv){
+ case 1:
+ sc=SC_EARTHWEAPON;
+ break;
+ case 2:
+ sc=SC_WINDWEAPON;
+ break;
+ case 3:
+ sc=SC_WATERWEAPON;
+ break;
+ case 4:
+ sc=SC_FIREWEAPON;
+ break;
+ case 5:
+ sc=SC_GHOSTWEAPON;
+ break;
+ case 6:
+ sc=SC_SHADOWWEAPON;
+ break;
+ case 7:
+ sc=SC_ASPERSIO;
+ break;
+ }
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PR_KYRIE: /* ƒLƒŠƒGƒGƒŒƒCƒ\ƒ“ */
+ if (status_isimmune(bl)) {
+ clif_skill_nodamage(bl,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ break;
+
+ case LK_BERSERK: /* ƒo?ƒT?ƒN */
+ if(battle_config.berserk_cancels_buffs)
+ {
+ status_change_end(bl,SC_ONEHAND,-1);
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ status_change_end(bl,SC_CONCENTRATION,-1);
+ status_change_end(bl,SC_PARRYING,-1);
+ status_change_end(bl,SC_ENDURE,-1);
+ status_change_end(bl,SC_AURABLADE,-1);
+ }
+ case KN_AUTOCOUNTER: /* ƒI?ƒgƒJƒEƒ“ƒ^? */
+ case KN_TWOHANDQUICKEN: /* ƒc?ƒnƒ“ƒhƒNƒCƒbƒPƒ“ */
+ case KN_ONEHAND:
+ case CR_SPEARQUICKEN: /* ƒXƒsƒAƒNƒCƒbƒPƒ“ */
+ case CR_REFLECTSHIELD:
+ case AS_POISONREACT: /* ƒ|ƒCƒYƒ“ƒŠƒAƒNƒg */
+ case MC_LOUD: /* ƒ‰ƒEƒhƒ{ƒCƒX */
+ case MG_ENERGYCOAT: /* ƒGƒiƒW?ƒR?ƒg */
+ case MG_SIGHT: /* ƒTƒCƒg */
+ case AL_RUWACH: /* ƒ‹ƒAƒt */
+ case MO_EXPLOSIONSPIRITS: // ”š—ô”g“®
+ case MO_STEELBODY: // ‹à?„
+ case LK_AURABLADE: /* ƒI?ƒ‰ƒuƒŒ?ƒh */
+ case LK_PARRYING: /* ƒpƒŠƒCƒ“ƒO */
+ case LK_CONCENTRATION: /* ƒRƒ“ƒZƒ“ƒgƒŒ?ƒVƒ‡ƒ“ */
+ case WS_CARTBOOST: /* ƒJ?ƒgƒu?ƒXƒg */
+ case SN_SIGHT: /* ƒgƒDƒ‹?ƒTƒCƒg */
+ case WS_MELTDOWN: /* ƒ?ƒ‹ƒgƒ_ƒEƒ“ */
+ case WS_OVERTHRUSTMAX: // Overthrust Max [Celest]
+ case ST_REJECTSWORD: /* ƒŠƒWƒFƒNƒgƒ\?ƒh */
+ case HW_MAGICPOWER: /* –‚–@—Í?•? */
+ case PF_MEMORIZE: /* ƒ?ƒ‚ƒ‰ƒCƒY */
+ case PA_SACRIFICE:
+ case ASC_EDP: // [Celest]
+ case NPC_STOP:
+ case WZ_SIGHTBLASTER:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case CG_MOONLIT: /* ŒŽ–¾‚è‚Ìò‚É—Ž‚¿‚é‰Ô‚Ñ‚ç */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd && battle_config.player_skill_partner_check) {
+ skill_check_pc_partner(sd, skillid, &skilllv, 1, 1);
+ } else
+ skill_moonlit(bl, NULL, skilllv); //The knockback must be invoked before starting the effect which places down the map cells. [Skotlex]
+
+ break;
+
+ case HP_ASSUMPTIO:
+ if (flag&1)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ else
+ {
+ map_foreachinarea(skill_area_sub,
+ bl->m, bl->x-1, bl->y-1, bl->x+1, bl->y+1, BL_PC,
+ src, skillid, skilllv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SM_ENDURE: /* ƒCƒ“ƒfƒ…ƒA */
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (sd)
+ pc_blockskill_start (sd, skillid, 10000);
+ break;
+
+ case AS_ENCHANTPOISON: // Prevent spamming [Valaris]
+ if (dstsd) {
+ if(dstsd->sc_data[SC_FIREWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WATERWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_WINDWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_EARTHWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_SHADOWWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_GHOSTWEAPON].timer != -1 ||
+ dstsd->sc_data[SC_ENCPOISON].timer != -1) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case LK_TENSIONRELAX: /* ƒeƒ“ƒVƒ‡ƒ“ƒŠƒ‰ƒbƒNƒX */
+ if (sd) {
+ pc_setsit(sd);
+ clif_sitting(sd);
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case MC_CHANGECART:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TK_MISSION:
+ if (sd) {
+ int id;
+ if (sd->mission_mobid && (sd->mission_count || rand()%100)) { //Cannot change target when already have one
+ clif_mission_mob(sd, sd->mission_mobid, sd->mission_count);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ id = mob_get_random_id(0,0, sd->status.base_level);
+ if (!id) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ sd->mission_mobid = id;
+ sd->mission_count = 0;
+ pc_setglobalreg(sd,"TK_MISSION_ID", id);
+ clif_mission_mob(sd, id, 0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case AC_CONCENTRATION: /* ?W’†—ÍŒü?ã */
+ {
+ int range = 1;
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea( status_change_timer_sub,
+ src->m, src->x-range, src->y-range, src->x+range,src->y+range,BL_CHAR,
+ src,SkillStatusChangeTable[skillid],tick);
+ }
+ break;
+
+ case SM_PROVOKE: /* ƒvƒ?ƒ{ƒbƒN */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ /* MVPmob‚Æ•sŽ€‚É‚Í?‚©‚È‚¢ */
+ if((status_get_mode(bl)&MD_BOSS) || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { //•sŽ€‚É‚Í?‚©‚È‚¢
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (rand()%100 > 50 + 3*skilllv + status_get_lv(src) - status_get_lv(bl)) //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
+ {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // ‰r?¥–WŠQ
+ skill_castcancel(bl,0);
+ if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map_flag_gvg(bl->m))
+ && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2)
+ skill_castcancel(bl,0);
+
+ if(sc_data){
+ if(sc_data[SC_FREEZE].timer!=-1)
+ status_change_end(bl,SC_FREEZE,-1);
+ if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ if(sc_data[SC_SLEEP].timer!=-1)
+ status_change_end(bl,SC_SLEEP,-1);
+ }
+
+ if(dstmd) {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ }
+ }
+ break;
+
+ case CR_DEVOTION: /* ƒfƒBƒ{?ƒVƒ‡ƒ“ */
+ if(sd && dstsd)
+ {
+ //??¶‚â—{Žq‚Ì?ê?‡‚ÌŒ³‚Ì?E‹Æ‚ðŽZ?o‚·‚é
+
+ int lv = sd->status.base_level - dstsd->status.base_level;
+ if (lv < 0) lv = -lv;
+ if (lv > battle_config.devotion_level_difference ||
+ (dstsd->sc_data[SC_DEVOTION].timer != -1 && dstsd->sc_data[SC_DEVOTION].val1 != src->id) || //Avoid overriding [Skotlex]
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ //Look for an empty slot (or reuse in case you cast it twice in the same char. [Skotlex]
+ for (i = 0; i < skilllv && i < 5 && sd->devotion[i]!=bl->id && sd->devotion[i]; i++);
+ if (i == skilllv)
+ {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ sd->devotion[i] = bl->id;
+ status_change_start(bl,SkillStatusChangeTable[skillid],src->id,i,skill_get_range2(src,skillid,skilllv),skill_get_time2(skillid, skilllv),1000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_devotion(sd);
+ }
+ else
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+
+ case MO_CALLSPIRITS: // ?Œ÷
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ pc_addspiritball(sd,skill_get_time(skillid,skilllv),skilllv);
+ }
+ break;
+
+ case CH_SOULCOLLECT: // ‹¶?Œ÷
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ for (i = 0; i < 5; i++)
+ pc_addspiritball(sd,skill_get_time(skillid,skilllv),5);
+ }
+ break;
+
+ case MO_KITRANSLATION:
+ if(dstsd) {
+ pc_addspiritball(dstsd,skill_get_time(skillid,skilllv),5);
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex]
+ if (skill_area_temp[1] != bl->id) {
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv));
+ skill_additional_effect(src,bl,skillid,skilllv,BF_MISC,tick); //Use Misc rather than weapon to signal passive pushback
+ }
+ break;
+
+ case MO_BLADESTOP: // ”’?nŽæ‚è
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case MO_ABSORBSPIRITS: // ?’D
+ i = 0;
+ if (dstsd && dstsd->spiritball > 0 &&
+ ((sd && sd == dstsd) || map_flag_vs(src->m)))
+ {
+ i = dstsd->spiritball * 7;
+ pc_delspiritball(dstsd,dstsd->spiritball,0);
+ } else if (dstmd && //??Û‚ªƒ‚ƒ“ƒXƒ^?‚Ì?ê?‡
+ //20%‚ÌŠm—¦‚Å??Û‚ÌLv*2‚ÌSP‚ð‰ñ•œ‚·‚é?B?¬Œ÷‚µ‚½‚Æ‚«‚̓^?ƒQƒbƒg(ƒÐ?„D?)ƒÐ????!!
+ !(status_get_mode(bl)&MD_BOSS) && rand() % 100 < 20)
+ {
+ i = 2 * dstmd->db->lv;
+ mob_target(dstmd,src,0);
+ }
+ if (sd){
+ if (i > 0x7FFF)
+ i = 0x7FFF;
+ if (sd->status.sp + i > sd->status.max_sp)
+ i = sd->status.max_sp - sd->status.sp;
+ if (i) {
+ sd->status.sp += i;
+ clif_heal(sd->fd,SP_SP,i);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+
+ case AC_MAKINGARROW: /* –î?ì?¬ */
+ if(sd) {
+ clif_arrow_create_list(sd);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case AM_PHARMACY: /* ƒ|?ƒVƒ‡ƒ“?ì?¬ */
+ if(sd) {
+ clif_skill_produce_mix_list(sd,22);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SA_CREATECON:
+ if(sd) {
+ clif_skill_produce_mix_list(sd,23);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case BS_HAMMERFALL: /* ƒnƒ“ƒ}?ƒtƒH?ƒ‹ */
+ if(dstsd && dstsd->special_state.no_weapon_damage) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(rand() % 100 < (20 + 10 * skilllv) * sc_def_vit / 100 )
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case RG_RAID: /* ƒTƒvƒ‰ƒCƒYƒAƒ^ƒbƒN */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_end(src, SC_HIDING, -1); // ƒnƒCƒfƒBƒ“ƒO‰ð?œ
+ break;
+
+ case ASC_METEORASSAULT: /* ƒ?ƒeƒIƒAƒTƒ‹ƒg */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,bl->x-2,bl->y-2,bl->x+2,bl->y+2,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case KN_BRANDISHSPEAR: /*ƒuƒ‰ƒ“ƒfƒBƒbƒVƒ…ƒXƒsƒA*/
+ {
+ int c,n=4,ar;
+ int dir = map_calc_dir(src,bl->x,bl->y);
+ struct square tc;
+ int x=bl->x,y=bl->y;
+ ar=skilllv/3;
+ skill_brandishspear_first(&tc,dir,x,y);
+ skill_brandishspear_dir(&tc,dir,4);
+ /* ”Í?‡C */
+ if(skilllv == 10){
+ for(c=1;c<4;c++){
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ }
+ }
+ /* ”Í?‡B‡A */
+ if(skilllv > 6){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;
+ }else{
+ skill_brandishspear_dir(&tc,dir,-2);
+ n-=2;
+ }
+
+ if(skilllv > 3){
+ for(c=0;c<5;c++){
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ if(skilllv > 6 && n==3 && c==4){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;c=-1;
+ }
+ }
+ }
+ /* ”Í?‡@ */
+ for(c=0;c<10;c++){
+ if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1);
+ map_foreachinarea(skill_area_sub,
+ bl->m,tc.val1[c%5],tc.val2[c%5],tc.val1[c%5],tc.val2[c%5],BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ }
+ break;
+
+ /* ƒp?ƒeƒBƒXƒLƒ‹ */
+ case AL_ANGELUS: /* ƒGƒ“ƒWƒFƒ‰ƒX */
+ case PR_MAGNIFICAT: /* ƒ}ƒOƒjƒtƒBƒJ?ƒg */
+ case PR_GLORIA: /* ƒOƒ?ƒŠƒA */
+ case SN_WINDWALK: /* ƒEƒCƒ“ƒhƒEƒH?ƒN */
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ /* ŒÂ•Ê‚Ì?—? */
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(bl,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ } else if (sd) {
+ /* ƒp?ƒeƒB‘S?‚Ö‚Ì?—? */
+ party_foreachsamemap (skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_ADRENALINE: /* ƒAƒhƒŒƒiƒŠƒ“ƒ‰ƒbƒVƒ… */
+ case BS_ADRENALINE2:
+ case BS_WEAPONPERFECT: /* ƒEƒFƒ|ƒ“ƒp?ƒtƒFƒNƒVƒ‡ƒ“ */
+ case BS_OVERTHRUST: /* ƒI?ƒo?ƒgƒ‰ƒXƒg */
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ /* ŒÂ•Ê‚Ì?—? */
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,(src == bl)? 1:0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(bl,bl,skillid,skilllv,1);
+ } else if (sd) {
+ /* ƒp?ƒeƒB‘S?‚Ö‚Ì?—? */
+ party_foreachsamemap(skill_area_sub,
+ sd,1,
+ src,skillid,skilllv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_MAXIMIZE:
+ case NV_TRICKDEAD:
+ case CR_DEFENDER:
+ case CR_AUTOGUARD:
+ case TK_READYSTORM:
+ case TK_READYDOWN:
+ case TK_READYTURN:
+ case TK_READYCOUNTER:
+ case TK_DODGE:
+ case CR_SHRINK:
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SL_KAITE:
+ case SL_KAAHI:
+ case SL_KAIZEL:
+ case SL_KAUPE:
+ if (sd) {
+ if (!dstsd || !(
+ (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SOULLINKER) ||
+ dstsd->char_id == sd->char_id ||
+ dstsd->char_id == sd->status.partner_id ||
+ dstsd->char_id == sd->status.child
+ )) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid, skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SM_AUTOBERSERK: // Celest
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,0,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case TF_HIDING: /* ƒnƒCƒfƒBƒ“ƒO */
+ case ST_CHASEWALK: /* ƒnƒCƒfƒBƒ“ƒO */
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc = SkillStatusChangeTable[skillid];
+ if (tsc_data && tsc_data[sc].timer != -1)
+ status_change_end(bl, sc, -1);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,-1,1); // Don't display the skill name as it is a hiding skill
+ }
+ break;
+ case TK_RUN:
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ int type = SkillStatusChangeTable[skillid];
+ if(!sc_data)
+ break;
+ if (sc_data[type].timer!=-1)
+ status_change_end(bl,type,-1);
+ else{
+ status_change_start(bl,type,skilllv,status_get_dir(bl),0,0,0,0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case AS_CLOAKING: /* ƒNƒ??ƒLƒ“ƒO */
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int sc=SkillStatusChangeTable[skillid];
+ if(tsc_data && tsc_data[sc].timer!=-1 )
+ /* ‰ð?œ‚·‚é */
+ status_change_end(bl, sc, -1);
+ else
+ /* •t‰Á‚·‚é */
+ { //Avoid cloaking with no wall and low skill level. [Skotlex]
+ if (sd && skilllv < 3 && skill_check_cloaking(bl))
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ }
+ clif_skill_nodamage(src,bl,skillid,-1,1);
+ }
+ break;
+
+ /* ?’nƒXƒLƒ‹ */
+ case BD_LULLABY: /* ŽqŽç‰S */
+ case BD_RICHMANKIM: /* ƒjƒˆƒ‹ƒh‚̉ƒ */
+ case BD_ETERNALCHAOS: /* ‰i‰“‚Ì?¬“× */
+ case BD_DRUMBATTLEFIELD: /* ?‘¾ŒÛ‚Ì‹¿‚« */
+ case BD_RINGNIBELUNGEN: /* ƒj?ƒxƒ‹ƒ“ƒO‚ÌŽw—Ö */
+ case BD_ROKISWEIL: /* ƒ?ƒL‚Ì‹©‚Ñ */
+ case BD_INTOABYSS: /* ?[•£‚Ì’†‚É */
+ case BD_SIEGFRIED: /* •sŽ€?g‚̃W?ƒNƒtƒŠ?ƒh */
+ case BA_DISSONANCE: /* •s‹¦˜a‰¹ */
+ case BA_POEMBRAGI: /* ƒuƒ‰ƒM‚ÌŽ? */
+ case BA_WHISTLE: /* Œû“J */
+ case BA_ASSASSINCROSS: /* —[—z‚̃AƒTƒVƒ“ƒNƒ?ƒX */
+ case BA_APPLEIDUN: /* ƒCƒhƒDƒ“‚Ì—ÑŒç */
+ case DC_UGLYDANCE: /* Ž©•ª?ŸŽè‚ȃ_ƒ“ƒX */
+ case DC_HUMMING: /* ƒnƒ~ƒ“ƒO */
+ case DC_DONTFORGETME: /* Ž„‚ð–Y‚ê‚È‚¢‚Å?c */
+ case DC_FORTUNEKISS: /* ?K‰^‚̃LƒX */
+ case DC_SERVICEFORYOU: /* ƒT?ƒrƒXƒtƒH?ƒ†? */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ break;
+
+ case HP_BASILICA: /* ƒoƒWƒŠƒJ */
+ case CG_HERMODE: // Wand of Hermod
+ {
+ struct skill_unit_group *sg;
+ battle_stopwalking(src,1);
+ skill_clear_unitgroup(src);
+ sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ if(skillid == CG_HERMODE)
+ status_change_start(src,SC_DANCING,skillid,0,0,sg->group_id,skill_get_time(skillid,skilllv),0);
+ else
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,BCT_SELF,sg->group_id,
+ skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case PA_GOSPEL: /* ƒSƒXƒyƒ‹ */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (!sc_data) break;
+ if (sc_data[SC_GOSPEL].timer != -1 && sc_data[SC_GOSPEL].val4 == BCT_SELF) {
+ status_change_end(src,SC_GOSPEL,-1);
+ } else {
+ struct skill_unit_group *sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0);
+ if (sc_data[SC_GOSPEL].timer != -1)
+ status_change_end(src,SC_GOSPEL,-1); //Was under someone else's Gospel. [Skotlex]
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,(int)sg,BCT_SELF,skill_get_time(skillid,skilllv),0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case BD_ADAPTATION: /* ƒAƒhƒŠƒu */
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_DANCING].timer!=-1){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_stop_dancing(src);
+ }
+ }
+ break;
+
+ case BA_FROSTJOKE: /* Š¦‚¢ƒWƒ‡?ƒN */
+ case DC_SCREAM: /* ƒXƒNƒŠ?ƒ€ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag);
+ if (md) { // Mob‚Í’?‚ê‚È‚¢‚©‚ç?AƒXƒLƒ‹–¼‚ð‹©‚Î‚¹‚Ä‚Ý‚é
+ char temp[128];
+ if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120)
+ break; //Message won't fit on buffer. [Skotlex]
+ sprintf(temp,"%s : %s !!",md->name,skill_db[skillid].desc);
+ clif_GlobalMessage(&md->bl,temp);
+ }
+ break;
+
+
+ case BA_PANGVOICE://ƒpƒ“ƒ{ƒCƒX
+ if(status_get_mode(bl)&MD_BOSS){
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(rand()%100 < 50){
+ status_change_start(bl,SC_CONFUSION,7,0,0,0,10000+7000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case DC_WINKCHARM://–£˜f‚̃EƒBƒ“ƒN
+ if(dstsd){
+ if(rand()%100 < 30) {
+ status_change_start(bl,SC_CONFUSION,7,0,0,0,10000+7000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }else if(dstmd)
+ {
+ int race = status_get_race(bl);
+ if(!(status_get_mode(bl)&MD_BOSS) && status_get_lv(src)>status_get_lv(bl) && (race == 6 || race == 7 || race == 8) && rand()%100 < 70) {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,10000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else{
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+
+ case TF_STEAL: // ƒXƒeƒB?ƒ‹
+ if(sd) {
+ if(pc_steal_item(sd,bl))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else
+ clif_skill_fail(sd,skillid,0x0a,0);
+ }
+ break;
+
+ case RG_STEALCOIN: // ƒXƒeƒB?ƒ‹ƒRƒCƒ“
+ if(sd) {
+ if(pc_steal_coin(sd,bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ mob_target((struct mob_data *)bl,src,skill_get_range2(src,skillid,skilllv));
+ }
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case MG_STONECURSE: /* ƒXƒg?ƒ“ƒJ?ƒX */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ // Level 6-10 doesn't consume a red gem if it fails [celest]
+ int i, gem_flag = 1, fail_flag = 0;
+ if (status_get_mode(bl)&MD_BOSS) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(status_isimmune(bl) || !sc_data)
+ break;
+ if (sc_data[SC_STONE].timer != -1) {
+ status_change_end(bl,SC_STONE,-1);
+ if (sd) {
+ fail_flag = 1;
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ else if( rand()%100 < (skilllv*4+20)*status_get_sc_def(bl, SC_STONE)/100
+ && !battle_check_undead(status_get_race(bl),status_get_elem_type(bl)))
+ {
+ status_change_start(bl,SC_STONE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ } else if(sd) {
+ if (skilllv > 5) gem_flag = 0;
+ clif_skill_fail(sd,skillid,0,0);
+ fail_flag = 1;
+ }
+ if (dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ if (sd && gem_flag) {
+ if ((i=pc_search_inventory(sd, skill_db[skillid].itemid[0])) < 0 ) {
+ // if (!fail_flag) clif_skill_fail(sd,skillid,0,0); This is actually a bug! Altough the gem is checked in skill_check_condition...
+ break;
+ }
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ break; //Do not delete the gemstone.
+ pc_delitem(sd, i, skill_db[skillid].amount[0], 0);
+ }
+ }
+ break;
+
+ case NV_FIRSTAID: /* ?‹}Žè? */
+ clif_skill_nodamage(src,bl,skillid,5,1);
+ battle_heal(NULL,bl,5,0,0);
+ break;
+
+ case AL_CURE: /* ƒLƒ…ƒA? */
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_end(bl, SC_SILENCE , -1 );
+ status_change_end(bl, SC_BLIND , -1 );
+ status_change_end(bl, SC_CONFUSION, -1 );
+ if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl)) ){//ƒAƒ“ƒfƒbƒh‚È‚çˆÃˆÅ?‰Ê
+ status_change_start(bl, SC_CONFUSION,1,0,0,0,6000,0);
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case TF_DETOXIFY: /* ‰ð“Å */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_end(bl, SC_POISON , -1 );
+ status_change_end(bl, SC_DPOISON , -1 );
+ break;
+
+ case PR_STRECOVERY: /* ƒŠƒJƒoƒŠ? */
+ {
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_end(bl, SC_FREEZE , -1 );
+ status_change_end(bl, SC_STONE , -1 );
+ status_change_end(bl, SC_SLEEP , -1 );
+ status_change_end(bl, SC_STAN , -1 );
+ if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl)) ){//ƒAƒ“ƒfƒbƒh‚È‚çˆÃˆÅ?‰Ê
+ if(rand()%100 < (100-(status_get_int(bl)/2+status_get_vit(bl)/3+status_get_luk(bl)/10))) {
+ status_change_start(bl, SC_BLIND,1,0,0,0,
+ 1000 * 30 * (100-(status_get_int(bl)+status_get_vit(bl))/2)/100,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstmd){
+ dstmd->attacked_id=0;
+ dstmd->target_id=0;
+ dstmd->state.targettype = NONE_ATTACKABLE;
+ dstmd->state.skillstate=MSS_IDLE;
+ dstmd->next_walktime=tick+rand()%3000+3000;
+ }
+ }
+ break;
+
+ case WZ_ESTIMATION: /* ƒ‚ƒ“ƒXƒ^??î•ñ */
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_estimation((struct map_session_data *)src,bl);
+ }
+ break;
+
+ case BS_REPAIRWEAPON: /* •?Ší?C—? */
+ if(sd && dstsd)
+ clif_item_repair_list(sd,dstsd);
+ break;
+
+ case MC_IDENTIFY: /* ƒAƒCƒeƒ€ŠÓ’è */
+ if(sd)
+ clif_item_identify_list(sd);
+ break;
+
+ // Weapon Refining [Celest]
+ case WS_WEAPONREFINE:
+ if(sd)
+ clif_item_refine_list(sd);
+ break;
+
+ case MC_VENDING: /* ˜I“XŠJ?Ý */
+ if(sd)
+ { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex]
+ if ( pc_can_give_items(pc_isGM(sd)) )
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ clif_openvendingreq(sd,2+sd->skilllv);
+ }
+ break;
+
+ case AL_TELEPORT: /* ƒeƒŒƒ|?ƒg */
+ if(sd) {
+ if (map[sd->bl.m].flag.noteleport) { /* ƒeƒŒƒ|‹ÖŽ~ */
+ clif_skill_teleportmessage(sd,0);
+ break;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ clif_displaymessage(sd->fd, "Duel: Can't use teleport in duel.");
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd->skilllv == 1)
+ if(!battle_config.skip_teleport_lv1_menu) // possibility to skip menu [LuzZza]
+ clif_skill_warppoint(sd,skillid,"Random","","","");
+ else
+ pc_randomwarp(sd,3);
+ else {
+ clif_skill_warppoint(sd,skillid,"Random",
+ mapindex_id2name(sd->status.save_point.map),"","");
+ }
+ } else if(dstmd)
+ mob_warp(dstmd,-1,-1,-1,3);
+ break;
+
+ case AL_HOLYWATER: /* ƒAƒNƒAƒxƒlƒfƒBƒNƒ^ */
+ if(sd) {
+ if (skill_produce_mix(sd, skillid, 523, 0, 0, 0, 1))
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+
+ case TF_PICKSTONE:
+ if(sd) {
+ int eflag;
+ struct item item_tmp;
+ struct block_list tbl;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ memset(&item_tmp,0,sizeof(item_tmp));
+ memset(&tbl,0,sizeof(tbl)); // [MouseJstr]
+ item_tmp.nameid = 7049;
+ item_tmp.identify = 1;
+ tbl.id = 0;
+ clif_takeitem(&sd->bl,&tbl);
+ eflag = pc_additem(sd,&item_tmp,1);
+ if(eflag) {
+ clif_additem(sd,0,0,eflag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ break;
+ case ASC_CDP:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_produce_mix(sd, skillid, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle.
+ }
+ break;
+
+ case RG_STRIPWEAPON: /* ƒXƒgƒŠƒbƒvƒEƒFƒ|ƒ“ */
+ case RG_STRIPSHIELD: /* ƒXƒgƒŠƒbƒvƒV?[ƒ‹ƒh */
+ case RG_STRIPARMOR: /* ƒXƒgƒŠƒbƒvƒA?[ƒ}?[ */
+ case RG_STRIPHELM: /* ƒXƒgƒŠƒbƒvƒwƒ‹ƒ€ */
+ case ST_FULLSTRIP: // Rewritten most of the code [DracoRPG]
+ {
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ int strip_fix, equip = 0;
+ int sclist[4] = {0,0,0,0};
+
+ if (skillid == RG_STRIPWEAPON || skillid == ST_FULLSTRIP)
+ equip |= EQP_WEAPON;
+ if (skillid == RG_STRIPSHIELD || skillid == ST_FULLSTRIP)
+ equip |= EQP_SHIELD;
+ if (skillid == RG_STRIPARMOR || skillid == ST_FULLSTRIP)
+ equip |= EQP_ARMOR;
+ if (skillid == RG_STRIPHELM || skillid == ST_FULLSTRIP)
+ equip |= EQP_HELM;
+
+ strip_fix = status_get_dex(src) - status_get_dex(bl);
+ if(strip_fix < 0)
+ strip_fix=0;
+ if (rand()%100 >= 5+2*skilllv+strip_fix/5)
+ {
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if (dstsd) {
+ for (i=0;i<11;i++) {
+ if (dstsd->equip_index[i]>=0 && dstsd->inventory_data[dstsd->equip_index[i]]) {
+ if (equip &EQP_WEAPON && (i == 9 || (i == 8 && dstsd->inventory_data[dstsd->equip_index[8]]->type == 4)) && !(dstsd->unstripable_equip &EQP_WEAPON) && !(tsc_data && tsc_data[SC_CP_WEAPON].timer != -1)) {
+ sclist[0] = SC_STRIPWEAPON; // Okay, we found a weapon to strip - It can be a right-hand, left-hand or two-handed weapon
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_SHIELD && i == 8 && dstsd->inventory_data[dstsd->equip_index[8]]->type == 5 && !(dstsd->unstripable_equip &EQP_SHIELD) && !(tsc_data && tsc_data[SC_CP_SHIELD].timer != -1)) {
+ sclist[1] = SC_STRIPSHIELD; // Okay, we found a shield to strip - It is really a shield, not a two-handed weapon or a left-hand weapon
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_ARMOR && i == 7 && !(dstsd->unstripable_equip &EQP_ARMOR) && !(tsc_data && tsc_data[SC_CP_ARMOR].timer != -1)) {
+ sclist[2] = SC_STRIPARMOR; // Okay, we found an armor to strip
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ } else if (equip &EQP_HELM && i == 6 && !(dstsd->unstripable_equip &EQP_HELM) && !(tsc_data && tsc_data[SC_CP_HELM].timer != -1)) {
+ sclist[3] = SC_STRIPHELM; // Okay, we found a helm to strip
+ pc_unequipitem(dstsd,dstsd->equip_index[i],3);
+ }
+ }
+ }
+ } else if (dstmd && !(status_get_mode(bl)&MD_BOSS)) {
+ if (equip &EQP_WEAPON)
+ sclist[0] = SC_STRIPWEAPON;
+ if (equip &EQP_SHIELD)
+ sclist[1] = SC_STRIPSHIELD;
+ if (equip &EQP_ARMOR)
+ sclist[2] = SC_STRIPARMOR;
+ if (equip &EQP_HELM)
+ sclist[3] = SC_STRIPHELM;
+ }
+
+ for (i=0;i<4;i++) {
+ if (sclist[i] != 0) // Start the SC only if an equipment was stripped from this location
+ status_change_start(bl,sclist[i],skilllv,0,0,0,skill_get_time(skillid,skilllv)+strip_fix/2,0);
+ }
+
+ break;
+ }
+
+ /* PotionPitcher */
+ case AM_BERSERKPITCHER:
+ case AM_POTIONPITCHER: /* ƒ|?ƒVƒ‡ƒ“ƒsƒbƒ`ƒƒ? */
+ {
+ struct block_list tbl;
+ int i,x,hp = 0,sp = 0,bonus=100;
+ if(sd) {
+ x = skilllv%11 - 1;
+ i = pc_search_inventory(sd,skill_db[skillid].itemid[x]);
+ if(i < 0 || skill_db[skillid].itemid[x] <= 0) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(skillid == AM_BERSERKPITCHER) { //Does not override use-level, and cannot be used on bows.
+ if (dstsd && (dstsd->status.base_level<(unsigned int)sd->inventory_data[i]->elv || dstsd->weapontype1 == 11)) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ potion_flag = 1;
+ potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
+ potion_target = bl->id;
+ run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
+ pc_delitem(sd,i,skill_db[skillid].amount[x],0);
+ potion_flag = potion_target = 0;
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_ALCHEMIST)
+ bonus += sd->status.base_level;
+ if(potion_per_hp > 0 || potion_per_sp > 0) {
+ hp = status_get_max_hp(bl) * potion_per_hp / 100;
+ hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ if(dstsd) {
+ sp = dstsd->status.max_sp * potion_per_sp / 100;
+ sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ }
+ }
+ else {
+ if(potion_hp > 0) {
+ hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ hp = hp * (100 + (status_get_vit(bl)<<1)) / 100;
+ if(dstsd)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if(potion_sp > 0) {
+ sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ sp = sp * (100 + (status_get_int(bl)<<1)) / 100;
+ if(dstsd)
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
+ }
+ }
+ }
+ else {
+ hp = (1 + rand()%400) * (100 + skilllv*10) / 100;
+ hp = hp * (100 + (status_get_vit(bl)<<1)) / 100;
+ if(dstsd)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ tbl.id = 0;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(hp > 0 || (skillid == AM_POTIONPITCHER && hp <= 0 && sp <= 0))
+ clif_skill_nodamage(&tbl,bl,AL_HEAL,hp,1);
+ if(sp > 0)
+ clif_skill_nodamage(&tbl,bl,MG_SRECOVERY,sp,1);
+ battle_heal(src,bl,hp,sp,0);
+ }
+ break;
+ case AM_CP_WEAPON:
+ case AM_CP_SHIELD:
+ case AM_CP_ARMOR:
+ case AM_CP_HELM:
+ {
+ int scid = SC_STRIPWEAPON + (skillid - AM_CP_WEAPON);
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ if(tsc_data && tsc_data[scid].timer != -1)
+ status_change_end(bl, scid, -1 );
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case AM_TWILIGHT1:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ //Prepare 200 White Potions.
+ if (!skill_produce_mix(sd, skillid, 504, 0, 0, 0, 200))
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+ case AM_TWILIGHT2:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ //Prepare 200 Slim White Potions.
+ if (!skill_produce_mix(sd, skillid, 547, 0, 0, 0, 200))
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ break;
+ case AM_TWILIGHT3:
+ if (sd) {
+ //check if you can produce all three, if not, then fail:
+ if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol
+ || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle
+ || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle
+ ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_produce_mix(sd, skillid, 970, 0, 0, 0, 100);
+ skill_produce_mix(sd, skillid, 7136, 0, 0, 0, 50);
+ skill_produce_mix(sd, skillid, 7135, 0, 0, 0, 50);
+ }
+ break;
+ case SA_DISPELL: /* ƒfƒBƒXƒyƒ‹ */
+ {
+ int i;
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (rand()%100 >= (50+10*skilllv)*sc_def_mdef/100 // Fixed & changed to use a proportionnal reduction (no info, but seems far more logical) [DracoRPG]
+ || tsc_data == NULL || (tsc_data[SC_SPIRIT].timer != -1 && tsc_data[SC_SPIRIT].val2 == SL_ROGUE)) //Rogue's spirit defends againt dispel.
+ {
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if(status_isimmune(bl))
+ break;
+ for(i=0;i<SC_MAX;i++){
+ if (tsc_data[i].timer == -1)
+ continue;
+ if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90
+ || i==SC_STRIPWEAPON || i==SC_STRIPSHIELD || i==SC_STRIPARMOR || i==SC_STRIPHELM
+ || i==SC_CP_WEAPON || i==SC_CP_SHIELD || i==SC_CP_ARMOR || i==SC_CP_HELM
+ || i==SC_COMBO || i==SC_DANCING || i==SC_GUILDAURA || i==SC_STEELBODY || i==SC_EDP
+ || i==SC_CARTBOOST || i==SC_MELTDOWN || i==SC_MOONLIT
+ )
+ continue;
+ if(i==SC_BERSERK) tsc_data[i].val4=1; //Mark a dispelled berserk to avoid setting hp to 100.
+ status_change_end(bl,i,-1);
+ }
+ }
+ break;
+
+ case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)|0x10000);
+ break;
+
+ case TK_HIGHJUMP:
+ {
+ int x,y, dir = status_get_dir(src);
+
+ if (sd && !pc_can_move(sd))
+ return 0;
+ if (md && !mob_can_move(md))
+ return 0;
+
+ x = src->x + dirx[dir]*skilllv*2;
+ y = src->y + diry[dir]*skilllv*2;
+
+ clif_skill_nodamage(src,bl,TK_HIGHJUMP,skilllv,1);
+ if(map_getcell(src->m,x,y,CELL_CHKPASS)) {
+ if (sd) pc_movepos(sd,x,y,0);
+ if (md) mob_warp(md, src->m, x, y, 0);
+ clif_slide(src,x,y);
+ }
+ }
+ break;
+
+ case SA_CASTCANCEL:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_castcancel(src,1);
+ if(sd) {
+ int sp = skill_get_sp(sd->skillid_old,sd->skilllv_old);
+ sp = sp * (90 - (skilllv-1)*20) / 100;
+ if(sp < 0) sp = 0;
+ pc_heal(sd,0,-sp);
+ }
+ break;
+ case SA_SPELLBREAKER: // ƒXƒyƒ‹ƒuƒŒƒCƒJ?
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ int sp;
+ if(sc_data && sc_data[SC_MAGICROD].timer != -1) {
+ if(dstsd) {
+ sp = skill_get_sp(skillid,skilllv);
+ sp = sp * sc_data[SC_MAGICROD].val2 / 100;
+ if(sp > 0x7fff) sp = 0x7fff;
+ else if(sp < 1) sp = 1;
+ if(dstsd->status.sp + sp > dstsd->status.max_sp) {
+ sp = dstsd->status.max_sp - dstsd->status.sp;
+ dstsd->status.sp = dstsd->status.max_sp;
+ }
+ else
+ dstsd->status.sp += sp;
+ clif_heal(dstsd->fd,SP_SP,sp);
+ }
+ clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1);
+ if(sd) {
+ sp = sd->status.max_sp/5;
+ if(sp < 1) sp = 1;
+ pc_heal(sd,0,-sp);
+ }
+ }
+ else {
+ int bl_skillid=0,bl_skilllv=0,hp = 0;
+ if(bl->type == BL_PC) {
+ if(dstsd && dstsd->skilltimer != -1) {
+ bl_skillid = dstsd->skillid;
+ bl_skilllv = dstsd->skilllv;
+ if (map_flag_vs(bl->m))
+ hp = status_get_max_hp(bl)/50; //Recover 2% HP [Skotlex]
+ }
+ }
+ else if(bl->type == BL_MOB) {
+ if(dstmd && dstmd->skilltimer != -1) {
+ if (status_get_mode(bl) & MD_BOSS)
+ { //Only 10% success chance against bosses. [Skotlex]
+ if (rand()%100 < 90)
+ {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ } else
+ hp = status_get_max_hp(bl)/50; //Recover 2% HP [Skotlex]
+ bl_skillid = dstmd->skillid;
+ bl_skilllv = dstmd->skilllv;
+ }
+ }
+ if(bl_skillid > 0 /*&& bl_skillid != PA_PRESSURE && skill_db[bl_skillid].skill_type == BF_MAGIC*/) { //Reports indicate Spell Break cancels any type of skill, except Pressure. [Skotlex]
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ skill_castcancel(bl,0);
+ sp = skill_get_sp(bl_skillid,bl_skilllv);
+ if(dstsd)
+ pc_heal(dstsd,-hp,-sp);
+ if(sd) {
+ sp = sp*(25*(skilllv-1))/100;
+ if(skilllv > 1 && sp < 1) sp = 1;
+ if(sp > 0x7fff) sp = 0x7fff;
+ else if(sp < 1) sp = 1;
+ if(sd->status.sp + sp > sd->status.max_sp) {
+ sp = sd->status.max_sp - sd->status.sp;
+ sd->status.sp = sd->status.max_sp;
+ }
+ else
+ sd->status.sp += sp;
+
+ if (hp && skilllv > 5)
+ { //Recover half damaged HP at levels 6-10 [Skotlex]
+ hp /=2;
+ if(sd->status.hp + hp > sd->status.max_hp) {
+ hp = sd->status.max_hp - sd->status.hp;
+ sd->status.hp = sd->status.max_hp;
+ }
+ else
+ sd->status.hp += hp;
+
+ clif_heal(sd->fd,SP_HP,hp);
+ }
+ clif_heal(sd->fd,SP_SP,sp);
+ }
+ }
+ else if(sd)
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+ case SA_MAGICROD:
+ if (status_isimmune(bl))
+ break;
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ break;
+ case SA_AUTOSPELL: /* ƒI?ƒgƒXƒyƒ‹ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(sd)
+ clif_autospell(sd,skilllv);
+ else {
+ int maxlv=1,spellid=0;
+ static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT };
+ if(skilllv >= 10) {
+ spellid = MG_FROSTDIVER;
+// if (sc_data && sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SA_SAGE)
+// maxlv = 10;
+// else
+ maxlv = skilllv - 9;
+ }
+ else if(skilllv >=8) {
+ spellid = MG_FIREBALL;
+ maxlv = skilllv - 7;
+ }
+ else if(skilllv >=5) {
+ spellid = MG_SOULSTRIKE;
+ maxlv = skilllv - 4;
+ }
+ else if(skilllv >=2) {
+ int i = rand()%3;
+ spellid = spellarray[i];
+ maxlv = skilllv - 1;
+ }
+ else if(skilllv > 0) {
+ spellid = MG_NAPALMBEAT;
+ maxlv = 3;
+ }
+ if(spellid > 0)
+ status_change_start(src,SC_AUTOSPELL,skilllv,spellid,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skilllv),0);
+ }
+ break;
+
+ case BS_GREED:
+ if(sd){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_greed,bl->m,bl->x-2,bl->y-2,bl->x+2,bl->y+2,BL_ITEM,bl);
+ }
+ break;
+
+ case SA_ELEMENTWATER:
+ case SA_ELEMENTFIRE:
+ case SA_ELEMENTGROUND:
+ case SA_ELEMENTWIND:
+ if(dstmd && !(status_get_mode(bl)&MD_BOSS)){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ dstmd->def_ele = skill_get_pl(skillid);
+ dstmd->def_ele += (1+rand()%4)*20;
+ }
+ break;
+
+ /* ƒ‰ƒ“ƒ_ƒ€??«?‰»?A?…??«?‰»?A’n?A‰Î?A•— */
+ case NPC_ATTRICHANGE:
+ case NPC_CHANGEWATER:
+ case NPC_CHANGEGROUND:
+ case NPC_CHANGEFIRE:
+ case NPC_CHANGEWIND:
+ /* “Å?A?¹?A”O?AˆÅ */
+ case NPC_CHANGEPOISON:
+ case NPC_CHANGEHOLY:
+ case NPC_CHANGEDARKNESS:
+ case NPC_CHANGETELEKINESIS:
+ case NPC_CHANGEUNDEAD:
+ if(md){
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ md->def_ele = skill_get_pl(skillid);
+ if (md->def_ele == 0) /* ƒ‰ƒ“ƒ_ƒ€?‰»?A‚½‚¾‚µ?A*/
+ md->def_ele = rand()%10; /* •sŽ€??«‚Í?œ‚­ */
+ md->def_ele += (1+rand()%4)*20; /* ??«ƒŒƒxƒ‹‚̓‰ƒ“ƒ_ƒ€ */
+ }
+ break;
+
+ case NPC_PROVOCATION:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(md)
+ clif_pet_performance(src,md->db->skill[md->skillidx].val[0]);
+ break;
+
+ case NPC_HALLUCINATION:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_KEEPING:
+ case NPC_BARRIER:
+ {
+ int skill_time = skill_get_time(skillid,skilllv);
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_time,0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if (md)
+ mob_changestate(md,MS_DELAY,skill_time);
+ else if (sd)
+ sd->attackabletime = sd->canmove_tick = tick + skill_time;
+ }
+ break;
+
+ case NPC_REBIRTH:
+ if (md && md->state.state == MS_DEAD) {
+ mob_setdelayspawn (md->bl.id);
+ }
+ break;
+
+ case NPC_DARKBLESSING:
+ {
+ int sc_def = 100 - status_get_mdef(bl);
+ if(status_isimmune(bl) || status_get_elem_type(bl) == 7 || status_get_race(bl) == 6) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ if(rand()%100 < sc_def*(50+skilllv*5)/100)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case NPC_LICK:
+ if (dstsd) {
+ if (dstsd->special_state.no_weapon_damage ) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ break;
+ }
+ pc_heal(dstsd,0,-100);
+ }
+ if(rand()%100 < (skilllv*5)*sc_def_vit/100)
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_SUICIDE: /* Ž©Œˆ */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ battle_damage(NULL,src,status_get_hp(bl),3); //Suicidal Mobs should give neither exp (flag&1) not items (flag&2) [Skotlex]
+ break;
+
+ case NPC_SUMMONSLAVE: /* Žè‰º?¢Š« */
+ case NPC_SUMMONMONSTER: /* MOB?¢Š« */
+ if(md)
+ mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid);
+ break;
+
+ case NPC_CALLSLAVE: //Žæ‚芪‚«ŒÄ‚Ñ–ß‚µ
+ mob_warpslave(src,AREA_SIZE/2);
+ break;
+
+ case NPC_RANDOMMOVE:
+ if (md) {
+ md->next_walktime = tick - 1;
+ mob_randomwalk(md,tick);
+ }
+ break;
+
+ case NPC_SPEEDUP:
+ {
+ // or does it increase casting rate? just a guess xD
+ int i = SC_ASPDPOTION0 + skilllv - 1;
+ if (i > SC_ASPDPOTION3)
+ i = SC_ASPDPOTION3;
+ status_change_start(bl,i,skilllv,0,0,0,skilllv * 60000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case NPC_REVENGE:
+ // not really needed... but adding here anyway ^^
+ if (md && md->master_id > 0) {
+ struct block_list *mbl, *tbl;
+ if ((mbl = map_id2bl(md->master_id)) == NULL ||
+ (tbl = battle_gettargeted(mbl)) == NULL)
+ break;
+ md->state.provoke_flag = tbl->id;
+ mob_target(md, tbl, md->db->range);
+ }
+ break;
+
+ case NPC_RUN: //Œã‘Þ
+ if(md) {
+ int dist = skilllv; //Run skillv tiles.
+ int dir = (bl == src)?md->dir:map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away.
+ int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+
+ md->attacked_id = 0;
+ md->attacked_count = 0;
+ md->target_id = 0;
+ md->state.targettype = NONE_ATTACKABLE;
+ md->state.skillstate = MSS_IDLE;
+ mob_walktoxy(md, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0);
+ }
+ break;
+
+ case NPC_TRANSFORMATION:
+ case NPC_METAMORPHOSIS:
+ if(md) {
+ if (skilllv > 1)
+ { //Multiply skilllv times, the original instance must be silently killed. [Skotlex]
+ mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid);
+ mob_delete(md);
+ }
+ else
+ { //Transform into another class.
+ int class_ = mob_random_class (md->db->skill[md->skillidx].val,0);
+ if (class_) mob_class_change(md, class_);
+ }
+ }
+ break;
+
+ case NPC_EMOTION_ON:
+ case NPC_EMOTION:
+ if(md)
+ {
+ clif_emotion(&md->bl,md->db->skill[md->skillidx].val[0]);
+ if(!md->special_state.ai && (md->db->skill[md->skillidx].val[1] || md->db->skill[md->skillidx].val[2]))
+ { //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
+ //val[1] 'sets' the mode, val[2] can add/remove from the current mode based on skill used:
+ //NPC_EMOTION_ON adds a mode / NPC_EMOTION removes it.
+ int mode, mode2;
+ mode = status_get_mode(src);
+ mode2 = (md->db->skill[md->skillidx].val[1])?(md->db->skill[md->skillidx].val[1]):mode;
+ if (md->db->skill[md->skillidx].val[2]) { //Alter the mode.
+ if (skillid == NPC_EMOTION_ON) //Add a mode
+ mode2|= md->db->skill[md->skillidx].val[2];
+ else //Remove a mode
+ mode2&= ~(md->db->skill[md->skillidx].val[2]);
+ }
+ if (mode == mode2)
+ break; //No change
+ md->mode = mode2;
+ if (md->mode == md->db->mode)
+ md->mode = 0; //Fallback to the db's mode.
+ //Since mode changed, reset their state.
+ mob_stopattack(md);
+ mob_stop_walking(md,0);
+ }
+ }
+ break;
+
+ case NPC_DEFENDER:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ // Equipment breaking monster skills [Celest]
+ case NPC_BREAKWEAPON:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakweapon(dstsd);
+ break;
+
+ case NPC_BREAKARMOR:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakarmor(dstsd);
+ break;
+
+ case NPC_BREAKHELM:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakhelm(dstsd);
+ break;
+
+ case NPC_BREAKSHIELD:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd && battle_config.equip_skill_break_rate)
+ pc_breakshield(dstsd);
+ break;
+
+ case NPC_POWERUP: //NPC”š—ô”g“®
+ status_change_start(bl,SC_EXPLOSIONSPIRITS,skilllv,0,0,0,skilllv * 60000,0);
+ // another random guess xP
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ status_change_start(bl,SC_INCALLSTATUS,skilllv * 5,0,0,0,skilllv * 60000,0);
+ break;
+
+ case NPC_AGIUP:
+ status_change_start(bl,SC_INCAGI,skilllv * 10,0,0,0,skilllv * 60000,0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case NPC_SIEGEMODE:
+ case NPC_INVISIBLE:
+ // not sure what it does
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case WE_MALE: /* ŒN‚¾‚¯‚ÍŒì‚é‚æ */
+ if(sd && dstsd){
+ int hp_rate=(skilllv <= 0)? 0:skill_db[skillid].hp_rate[skilllv-1];
+ int gain_hp=dstsd->status.max_hp*abs(hp_rate)/100;// The earned is the same % of the target HP than it costed the caster. [Skotlex]
+ gain_hp = battle_heal(NULL,bl,gain_hp,0,0);
+ clif_skill_nodamage(src,bl,skillid,gain_hp,1);
+ }
+ break;
+ case WE_FEMALE: /* ‚ ‚È‚½‚Ì?‚É??µ‚É‚È‚è‚Ü‚· */
+ if(sd && dstsd){
+ int sp_rate=(skilllv <= 0)? 0:skill_db[skillid].sp_rate[skilllv-1];
+ int gain_sp=dstsd->status.max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex]
+ gain_sp = battle_heal(NULL,bl,0,gain_sp,0);
+ clif_skill_nodamage(src,bl,skillid,gain_sp,1);
+ }
+ break;
+
+ case WE_CALLPARTNER: /* ‚ ‚È‚½‚É?‚¢‚½‚¢ */
+ if(sd){
+ if((dstsd = pc_get_partner(sd)) == NULL){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto || map[dstsd->bl.m].flag.nowarp){
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ skill_unitsetting(src,skillid,skilllv,sd->bl.x,sd->bl.y,0);
+ pc_blockskill_start (sd, skillid, skill_get_time(skillid, skilllv));
+ }
+ break;
+
+// parent-baby skills
+ case WE_BABY:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0);
+ if (f_sd) status_change_start(&f_sd->bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ if (m_sd) status_change_start(&m_sd->bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ }
+ break;
+
+ case WE_CALLPARENT:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto)
+ {
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if((!f_sd && m_sd && map[m_sd->bl.m].flag.nowarp) ||
+ (!m_sd && f_sd && map[f_sd->bl.m].flag.nowarp))
+ { //Case where neither one can be warped.
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ //Warp those that can be warped.
+ if (f_sd && !map[f_sd->bl.m].flag.nowarp)
+ pc_setpos(f_sd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ if (m_sd && !map[m_sd->bl.m].flag.nowarp)
+ pc_setpos(m_sd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ }
+ break;
+
+ case WE_CALLBABY:
+ if(sd && dstsd)
+ {
+ if(map[sd->bl.m].flag.nomemo || map[sd->bl.m].flag.nowarpto || map[dstsd->bl.m].flag.nowarp){
+ clif_skill_teleportmessage(sd,1);
+ map_freeblock_unlock();
+ return 0;
+ }
+ pc_setpos(dstsd,map[sd->bl.m].index,sd->bl.x,sd->bl.y,3);
+ }
+ break;
+
+ case PF_HPCONVERSION: /* ƒ‰ƒCƒt’u‚«Š·‚¦ */
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ if (sd) {
+ int hp, sp;
+ hp = sd->status.max_hp / 10; //Šî–{‚ÍHP‚Ì10%
+ sp = hp * 10 * skilllv / 100;
+ if (sd->status.sp + sp > sd->status.max_sp)
+ sp = sd->status.max_sp - sd->status.sp;
+ // we need to check with the sp that was taken away when casting too
+ if (sd->status.sp + skill_get_sp(skillid, skilllv) >= sd->status.max_sp)
+ hp = sp = 0;
+ pc_heal(sd, -hp, sp);
+ clif_heal(sd->fd, SP_SP, sp);
+ clif_updatestatus(sd, SP_SP);
+ }
+ break;
+ case HT_REMOVETRAP: /* ƒŠƒ€?ƒuƒgƒ‰ƒbƒv */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ {
+ struct skill_unit *su=NULL;
+ struct item item_tmp;
+ int flag;
+ if((bl->type==BL_SKILL) &&
+ (su=(struct skill_unit *)bl) &&
+ (su->group->src_id == src->id || map_flag_vs(bl->m)) &&
+ (skill_get_inf2(su->group->skill_id) & INF2_TRAP))
+ {
+ if(sd && su->group->val3 != BD_INTOABYSS)
+ { //Avoid collecting traps when it does not costs to place them down. [Skotlex]
+ if(battle_config.skill_removetrap_type){
+ for(i=0;i<10;i++) {
+ if(skill_db[su->group->skill_id].itemid[i] > 0){
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = skill_db[su->group->skill_id].itemid[i];
+ item_tmp.identify = 1;
+ if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i]))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ }else{
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = 1065;
+ item_tmp.identify = 1;
+ if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+ }
+ if(su->group->unit_id == UNT_ANKLESNARE && su->group->val2){
+ struct block_list *target=map_id2bl(su->group->val2);
+ if(target && (target->type == BL_PC || target->type == BL_MOB))
+ status_change_end(target,SC_ANKLE,-1);
+ }
+ skill_delunit(su);
+ }
+ }
+ break;
+ case HT_SPRINGTRAP: /* ƒXƒvƒŠƒ“ƒOƒgƒ‰ƒbƒv */
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ {
+ struct skill_unit *su=NULL;
+ if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){
+ switch(su->group->unit_id){
+ case UNT_ANKLESNARE: // ankle snare
+ if (su->group->val2 != 0)
+ // if it is already trapping something don't spring it,
+ // remove trap should be used instead
+ break;
+ // otherwise fallthrough to below
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ su->group->unit_id = UNT_USED_TRAPS;
+ clif_changelook(bl,LOOK_BASE,su->group->unit_id);
+ su->group->limit=DIFF_TICK(tick+1500,su->group->tick);
+ su->limit=DIFF_TICK(tick+1500,su->group->tick);
+ }
+ }
+ }
+ break;
+ case BD_ENCORE: /* ƒAƒ“ƒR?ƒ‹ */
+ 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: /* ƒxƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ? */
+ if(status_get_max_hp(bl)*2/3 < status_get_hp(bl)) { //HP‚ª2/3ˆÈ?ã?‚Á‚Ä‚¢‚½‚玸”s
+ map_freeblock_unlock();
+ return 1;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,src->id,skill_get_time(skillid,skilllv),1000,0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case PF_MINDBREAKER: /* ƒvƒ?ƒ{ƒbƒN */
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ /* MVPmob‚Æ•sŽ€‚É‚Í?‚©‚È‚¢ */
+ if(status_get_mode(bl)&MD_BOSS || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) //•sŽ€‚É‚Í?‚©‚È‚¢
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (rand()%100 > 55 + skilllv*5)
+ { //Has a 55% + skilllv*5% success chance.
+ clif_skill_nodamage(src,bl,skillid,skilllv,0);
+ if (sd)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+
+ if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // ‰r?¥–WŠQ
+ skill_castcancel(bl,0);
+ if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map_flag_gvg(bl->m))
+ && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2)
+ skill_castcancel(bl,0);
+
+ if(sc_data){
+ if(sc_data[SC_FREEZE].timer!=-1)
+ status_change_end(bl,SC_FREEZE,-1);
+ if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ if(sc_data[SC_SLEEP].timer!=-1)
+ status_change_end(bl,SC_SLEEP,-1);
+ }
+
+ if(dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+ }
+ break;
+
+ case PF_SOULCHANGE:
+ {
+ int sp1 = 0, sp2 = 0;
+ if (sd) {
+ if (dstsd) {
+ sp1 = sd->status.sp > dstsd->status.max_sp ? dstsd->status.max_sp : sd->status.sp;
+ sp2 = dstsd->status.sp > sd->status.max_sp ? sd->status.max_sp : dstsd->status.sp;
+ sd->status.sp = sp2;
+ dstsd->status.sp = sp1;
+ clif_heal(sd->fd,SP_SP,sp2);
+ clif_updatestatus(sd,SP_SP);
+ clif_heal(dstsd->fd,SP_SP,sp1);
+ clif_updatestatus(dstsd,SP_SP);
+ } else if (dstmd) {
+ if (dstmd->state.soul_change_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ sp2 = sd->status.max_sp * 3 /100;
+ if (sd->status.sp + sp2 > sd->status.max_sp)
+ sp2 = sd->status.max_sp - sd->status.sp;
+ sd->status.sp += sp2;
+ clif_heal(sd->fd,SP_SP,sp2);
+ clif_updatestatus(sd,SP_SP);
+ dstmd->state.soul_change_flag = 1;
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ // Slim Pitcher
+ case CR_SLIMPITCHER:
+ {
+ if (sd && flag&1) {
+ struct block_list tbl;
+ int hp = potion_hp * (100 + pc_checkskill(sd,CR_SLIMPITCHER)*10 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100;
+ hp = hp * (100 + (status_get_vit(bl)<<1))/100;
+ if (dstsd) {
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10)/100;
+ }
+ tbl.id = 0;
+ tbl.m = src->m;
+ tbl.x = src->x;
+ tbl.y = src->y;
+ clif_skill_nodamage(&tbl,bl,AL_HEAL,hp,1);
+ battle_heal(NULL,bl,hp,0,0);
+ }
+ }
+ break;
+ // Full Chemical Protection
+ case CR_FULLPROTECTION:
+ {
+ int i, skilltime;
+ struct status_change *tsc_data = status_get_sc_data(bl);
+ skilltime = skill_get_time(skillid,skilllv);
+ for (i=0; i<4; i++) {
+ if(tsc_data && tsc_data[SC_STRIPWEAPON + i].timer != -1)
+ status_change_end(bl, SC_STRIPWEAPON + i, -1 );
+ status_change_start(bl,SC_CP_WEAPON + i,skilllv,0,0,0,skilltime,0 );
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case RG_CLEANER: //AppleGirl
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case ST_PRESERVE:
+ if (sd){
+ if (sd->sc_count && sd->sc_data[SC_PRESERVE].timer != -1)
+ status_change_end(src, SC_PRESERVE, -1 );
+ else
+ status_change_start(src,SC_PRESERVE,skilllv,0,0,0,skill_get_time(skillid, skilllv),0 );
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ }
+ break;
+
+ case PF_DOUBLECASTING:
+ if (rand() % 100 > 30 + skilllv * 10) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 );
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case CG_LONGINGFREEDOM:
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[SC_LONGING].timer == -1 && sc_data[SC_DANCING].timer != -1 && sc_data[SC_DANCING].val4
+ && sc_data[SC_DANCING].val1 != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex]
+ {
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ }
+ break;
+
+ case CG_TAROTCARD:
+ {
+ int eff, count = -1;
+ if (rand() % 100 > skilllv * 8) {
+ if (sd) clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ do {
+ eff = rand() % 14;
+ clif_specialeffect(bl, 523 + eff, 0);
+ switch (eff)
+ {
+ case 0: // heals SP to 0
+ if (dstsd) pc_heal(dstsd,0,-dstsd->status.sp);
+ break;
+ case 1: // matk halved
+ status_change_start(bl,SC_INCMATKRATE,-50,0,0,0,30000,0);
+ break;
+ case 2: // all buffs removed
+ status_change_clear_buffs(bl);
+ break;
+ case 3: // 1000 damage, random armor destroyed
+ {
+ int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM };
+ battle_damage(src, bl, 1000, 0);
+ clif_damage(src,bl,tick,0,0,1000,0,0,0);
+ if (dstsd && battle_config.equip_skill_break_rate) pc_break_equip(dstsd, where[rand() % 3]);
+ }
+ break;
+ case 4: // atk halved
+ status_change_start(bl,SC_INCATKRATE,-50,0,0,0,30000,0);
+ break;
+ case 5: // 2000HP heal, random teleported
+ battle_heal(src, src, 2000, 0, 0);
+ if(sd && !map[src->m].flag.noteleport) pc_setpos(sd,sd->mapindex,-1,-1,3);
+ else if(md && !map[src->m].flag.monster_noteleport) mob_warp(md,-1,-1,-1,3);
+ break;
+ case 6: // random 2 other effects
+ if (count == -1)
+ count = 3;
+ else
+ count++; //Should not retrigger this one.
+ break;
+ case 7: // stop freeze or stoned
+ {
+ int sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
+ status_change_start(bl,sc[rand()%3],skilllv,0,0,0,30000,0);
+ }
+ break;
+ case 8: // curse coma and poison
+ status_change_start(bl,SC_COMA,skilllv,0,0,0,30000,0);
+ status_change_start(bl,SC_CURSE,skilllv,0,0,0,30000,0);
+ status_change_start(bl,SC_POISON,skilllv,0,0,0,30000,0);
+ break;
+ case 9: // chaos
+ status_change_start(bl,SC_CONFUSION,skilllv,0,0,0,30000,0);
+ break;
+ case 10: // 6666 damage, atk matk halved, cursed
+ battle_damage(src, bl, 6666, 0);
+ clif_damage(src,bl,tick,0,0,6666,0,0,0);
+ status_change_start(bl,SC_INCATKRATE,-50,0,0,0,30000,0);
+ status_change_start(bl,SC_INCMATKRATE,-50,0,0,0,30000,0);
+ status_change_start(bl,SC_CURSE,skilllv,0,0,0,30000,0);
+ break;
+ case 11: // 4444 damage
+ battle_damage(src, bl, 4444, 0);
+ clif_damage(src,bl,tick,0,0,4444,0,0,0);
+ break;
+ case 12: // stun
+ status_change_start(bl,SC_STAN,skilllv,0,0,0,5000,0);
+ break;
+ case 13: // atk,matk,hit,flee,def reduced
+ status_change_start(bl,SC_INCATKRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCMATKRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCHITRATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCFLEERATE,-20,0,0,0,30000,0);
+ status_change_start(bl,SC_INCDEFRATE,-20,0,0,0,30000,0);
+ break;
+ default:
+ break;
+ }
+ } while ((--count) > 0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SL_ALCHEMIST:
+ case SL_ASSASIN:
+ case SL_BARDDANCER:
+ case SL_BLACKSMITH:
+ case SL_CRUSADER:
+ case SL_HUNTER:
+ case SL_KNIGHT:
+ case SL_MONK:
+ case SL_PRIEST:
+ case SL_ROGUE:
+ case SL_SAGE:
+ case SL_SOULLINKER:
+ case SL_STAR:
+ case SL_SUPERNOVICE:
+ case SL_WIZARD:
+ if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == SkillStatusChangeTable[skillid])) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SC_SPIRIT,skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0);
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SL_HIGH:
+ if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0 );
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case SL_SKA: // [marquis007]
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if (sd && status_get_mode(bl)&MD_BOSS)
+ clif_skill_fail(sd,skillid,0,0);
+ else
+ {
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+ case SL_SWOO:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,status_get_mode(bl)&MD_BOSS?skill_get_time(skillid,skilllv)/5:skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ case SL_SKE:
+ if (sd && bl->type != BL_MOB) {
+ status_change_start(src,SC_STAN,skilllv,0,0,0,3000,0);
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0 );
+ status_change_start(src,SC_COMBO,SL_SMA,skilllv,0,0,skill_get_time2(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ // New guild skills [Celest]
+ case GD_BATTLEORDER:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ status_change_start(&dstsd->bl,SC_BATTLEORDERS,skilllv,0,0,0,0,0 );
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_REGENERATION:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ status_change_start(&dstsd->bl,SC_REGENERATION,skilllv,0,0,0,0,0 );
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_RESTORE:
+ {
+ struct guild *g = NULL;
+ // Only usable during WoE
+ if (!agit_flag) {
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ if(flag&1) {
+ if (dstsd && dstsd->status.guild_id == sd->status.guild_id) {
+ int hp, sp;
+ hp = dstsd->status.max_hp*9/10;
+ sp = dstsd->status.max_sp*9/10;
+ sp = dstsd->status.sp + sp <= dstsd->status.max_sp ? sp : dstsd->status.max_sp - dstsd->status.sp;
+ clif_skill_nodamage(src,bl,AL_HEAL,hp,1);
+ battle_heal(NULL,bl,hp,sp,0);
+ }
+ }
+ else if (sd && sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) &&
+ strcmp(sd->status.name,g->master)==0) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ map_foreachinarea(skill_area_sub,
+ src->m,src->x-15,src->y-15,src->x+15,src->y+15,BL_CHAR,
+ src,skillid,skilllv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ {
+ int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0};
+ int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0};
+ int j = 0;
+ struct guild *g = NULL;
+ if (!sd || !sd->state.gmaster_flag)
+ break;
+ //Reports say this particular skill is usable anywhere! o.o [Skotlex]
+ //And now people say that's not true... MEH. Will they EVER make up their mind?
+ if (/*map[sd->bl.m].flag.nowarpto &&*/ !map_flag_gvg(sd->bl.m))
+ { //if not allowed to warp to the map (castles are always allowed)
+ clif_skill_fail(sd,skillid,0,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ // i don't know if it actually summons in a circle, but oh well. ;P
+ g = sd->state.gmaster_flag;
+ for(i = 0; i < g->max_member; i++, j++) {
+ if (j>8) j=0;
+ if ((dstsd = g->member[i].sd) != NULL && sd != dstsd) {
+ if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg(dstsd->bl.m))
+ continue;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(map_getcell(sd->bl.m,sd->bl.x+dx[j],sd->bl.y+dy[j],CELL_CHKNOPASS))
+ dx[j] = dy[j] = 0;
+ pc_setpos(dstsd, sd->mapindex, sd->bl.x+dx[j], sd->bl.y+dy[j], 2);
+ }
+ }
+ guild_block_skill(sd,skill_get_time2(skillid,skilllv));
+ }
+ break;
+
+ case SG_FEEL:
+ if (sd) {
+ if(!sd->feel_map[skilllv-1].index) {
+ sd->feel_level=skilllv-1;
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_parse_ReqFeel(sd->fd,sd);
+ }
+ else
+ clif_feel_info(sd, skilllv-1);
+ }
+ break;
+
+ case SG_HATE:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ if(dstsd) //PC
+ {
+ sd->hate_mob[skilllv-1] = dstsd->status.class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[skilllv-1]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ }
+ else if(dstmd) // mob
+ {
+ switch(skilllv)
+ {
+ case 1:
+ if (status_get_size(bl)==0)
+ {
+ sd->hate_mob[0] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_SUN",sd->hate_mob[0]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ case 2:
+ if (status_get_size(bl)==1 && status_get_max_hp(bl)>=6000)
+ {
+ sd->hate_mob[1] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_MOON",sd->hate_mob[1]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ case 3:
+ if (status_get_size(bl)==2 && status_get_max_hp(bl)>=20000)
+ {
+ sd->hate_mob[2] = dstmd->class_;
+ pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[2]+1);
+ clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]);
+ } else clif_skill_fail(sd,skillid,0,0);
+ break;
+ default:
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ }
+ }
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,skillid,skill_get_range(skillid,skilllv),skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case SG_FUSION:
+ if (sd && sd->sc_data && sd->sc_data[SC_FUSION].timer != -1)
+ status_change_end(&sd->bl,SkillStatusChangeTable[skillid],-1);
+ else
+ status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+
+ default:
+ ShowWarning("Unknown skill used:%d\n",skillid);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?AIDŽw’è?j
+ *------------------------------------------
+ */
+int skill_castend_id( int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data* sd = map_id2sd(id)/*,*target_sd=NULL*/;
+ struct block_list *bl;
+ int delay,inf2;
+
+ nullpo_retr(0, sd);
+
+//Code cleanup.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; sd->canact_tick = sd->canmove_tick = tick; }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid )
+ { /* ƒ^ƒCƒ}ID‚ÌŠm”F */
+ ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", sd->skilltimer, tid);
+ sd->skilltimer = -1;
+ return 0;
+ }
+
+ if( sd->bl.prev == NULL || sd->skillid == -1 || sd->skilllv == -1)
+ { //Finished casting between maps, or the skill has failed after starting casting{
+ sd->skilltimer = -1;
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && (delay = pc_checkskill(sd,SA_FREECAST) > 0)) //Hope ya don't mind me borrowing delay :X
+ status_quick_recalc_speed(sd, SA_FREECAST, delay, 0);
+
+ if(sd->skillid != SA_CASTCANCEL)
+ sd->skilltimer=-1;
+
+ if((bl=map_id2bl(sd->skilltarget))==NULL ||
+ bl->prev==NULL || sd->bl.m != bl->m) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(sd->skillid == RG_BACKSTAP) {
+ int dir = map_calc_dir(&sd->bl,bl->x,bl->y),t_dir = status_get_dir(bl);
+ if(bl->type != BL_SKILL && (check_distance_bl(&sd->bl, bl, 0) || map_check_dir(dir,t_dir))) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if (sd->skillid == PR_LEXDIVINA)
+ {
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (battle_check_target(&sd->bl,bl, BCT_ENEMY)<=0 &&
+ (!sc_data || sc_data[SC_SILENCE].timer == -1)) //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex]
+ {
+ clif_skill_nodamage (&sd->bl, bl, sd->skillid, sd->skilllv, 0);
+ skill_failed(sd);
+ return 0;
+ }
+ } else {
+ inf2 = skill_get_inf(sd->skillid);
+ if((inf2&INF_ATTACK_SKILL ||
+ (inf2&INF_SELF_SKILL && sd->bl.id != bl->id && skill_get_nk(sd->skillid) != NK_NO_DAMAGE)) //Self skills that cause damage (EF, Combo Skills, etc)
+ && battle_check_target(&sd->bl,bl, BCT_ENEMY)<=0
+ ) {
+ skill_failed(sd);
+ return 0;
+ }
+ }
+ if (tid != -1 && !status_check_skilluse(&sd->bl, bl, sd->skillid, 1))
+ { //Avoid doing double checks for instant-cast skills.
+ if(sd->skillid == PR_LEXAETERNA) //Eh.. assuming skill failed due to opponent frozen/stone-cursed. [Skotlex]
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+
+ inf2 = skill_get_inf2(sd->skillid);
+ if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && sd->bl.id != bl->id) {
+ int fail_flag = 1;
+ if(inf2 & INF2_PARTY_ONLY && battle_check_target(&sd->bl,bl, BCT_PARTY) > 0)
+ fail_flag = 0;
+ else if(inf2 & INF2_GUILD_ONLY && battle_check_target(&sd->bl,bl, BCT_GUILD) > 0)
+ fail_flag = 0;
+
+ if (sd->skillid == PF_SOULCHANGE && map_flag_vs(sd->bl.m))
+ //Soul Change overrides this restriction during pvp/gvg [Skotlex]
+ fail_flag = 0;
+
+ if(fail_flag) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(!check_distance_bl(&sd->bl, bl, skill_get_range2(&sd->bl,sd->skillid,sd->skilllv)+battle_config.pc_skill_add_range))
+ {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex]
+ skill_check_condition(sd,1);
+
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition(sd,1)) { /* Žg—p?Œ?ƒ`ƒFƒbƒN */
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid);
+ pc_stop_walking(sd,0);
+
+ if (sd->skillid == SA_MAGICROD)
+ delay = 0;
+ else
+ delay = skill_delayfix(&sd->bl, sd->skillid, sd->skilllv, 0);
+
+ sd->canact_tick = tick + delay;
+ if (skill_get_delaynowalk(sd->skillid, sd->skilllv)) //Skills that block you from moving until delay ends. [Skotlex]
+ sd->canmove_tick = tick + delay;
+ switch( skill_get_nk(sd->skillid) )
+ {
+ case NK_NO_DAMAGE:
+ skill_castend_nodamage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0);
+ break;
+ case NK_SPLASH_DAMAGE:
+ default:
+ skill_castend_damage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0);
+ break;
+ }
+
+ if(sd->sc_data[SC_MAGICPOWER].timer != -1 && sd->skillid != HW_MAGICPOWER)
+ status_change_end(&sd->bl,SC_MAGICPOWER,-1);
+
+ if (sd->skillid != AL_TELEPORT && sd->skillid != WS_WEAPONREFINE) {
+ sd->skillid = sd->skilllv = -1; //Clean this up for future references to battle_getcurrentskill. [Skotlex]
+ sd->skilltarget = 0;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------- */
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?A?ê?ŠŽw’è?j
+ *------------------------------------------
+ */
+int skill_castend_pos( int tid, unsigned int tick, int id,int data )
+{
+ struct map_session_data* sd=map_id2sd(id)/*,*target_sd=NULL*/;
+ int delay,maxcount;
+
+ nullpo_retr(0, sd);
+
+//Code cleanup.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; sd->canact_tick = sd->canmove_tick = tick; }
+
+ if( sd->skilltimer != tid )
+ { /* ƒ^ƒCƒ}ID‚ÌŠm”F */
+ ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", sd->skilltimer, tid);
+ sd->skilltimer = -1;
+ return 0;
+ }
+
+ if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && (delay = pc_checkskill(sd,SA_FREECAST) > 0)) //Hope ya don't mind me borrowing delay :X
+ status_quick_recalc_speed(sd, SA_FREECAST, delay, 0);
+
+ sd->skilltimer=-1;
+ if (sd->bl.prev == NULL || sd->skillid == -1 || sd->skilllv <= 0)
+ { // skill has failed after starting casting
+ return 0;
+ }
+
+ if (!battle_config.pc_skill_reiteration &&
+ skill_get_unit_flag(sd->skillid)&UF_NOREITERATION &&
+ skill_check_unit_range(sd->bl.m,sd->skillx,sd->skilly,sd->skillid,sd->skilllv)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+
+ if (battle_config.pc_skill_nofootset &&
+ skill_get_unit_flag(sd->skillid)&UF_NOFOOTSET &&
+ skill_check_unit_range2(&sd->bl,sd->bl.m,sd->skillx,sd->skilly,sd->skillid,sd->skilllv)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ if(battle_config.pc_land_skill_limit) {
+ maxcount = skill_get_maxcount(sd->skillid);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == sd->skillid)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+ }
+
+ if(tid != -1)
+ { //Avoid double checks on instant cast skills. [Skotlex]
+ if (!status_check_skilluse(&sd->bl, NULL, sd->skillid, 1))
+ {
+ skill_failed(sd);
+ return 0;
+ }
+ if(!check_distance_blxy(&sd->bl, sd->skillx, sd->skilly, skill_get_range2(&sd->bl,sd->skillid,sd->skilllv)+battle_config.pc_skill_add_range)) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway.
+ skill_check_condition(sd,1);
+
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(!skill_check_condition(sd,1)) { /* Žg—p?Œ?ƒ`ƒFƒbƒN */
+ skill_failed(sd);
+ return 0;
+ }
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid);
+ pc_stop_walking(sd,0);
+
+ delay = skill_delayfix(&sd->bl, sd->skillid, sd->skilllv, 0);
+ sd->canact_tick = tick + delay;
+ if (skill_get_delaynowalk(sd->skillid, sd->skilllv)) //Skills that block you from moving until delay ends. [Skotlex]
+ sd->canmove_tick = tick + delay;
+
+ skill_castend_pos2(&sd->bl,sd->skillx,sd->skilly,sd->skillid,sd->skilllv,tick,0);
+
+ if (sd->skillid != AL_WARP)
+ sd->skillid = sd->skilllv = -1; //Clean this up for future references to battle_getcurrentskill. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?A?ê?ŠŽw’è‚Ì??Û‚Ì?—??j
+ *------------------------------------------
+ */
+int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag)
+{
+ struct map_session_data *sd=NULL;
+ struct status_change *sc_data;
+ int i,tmpx = 0,tmpy = 0, x1 = 0, y1 = 0;
+
+ //if(skilllv <= 0) return 0;
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+
+ nullpo_retr(0, src);
+
+ if(src->type==BL_PC)
+ sd=(struct map_session_data *)src;
+
+ sc_data = status_get_sc_data(src); //Needed for Magic Power checks.
+
+ if( skillid != WZ_METEOR &&
+ skillid != AM_CANNIBALIZE &&
+ skillid != AM_SPHEREMINE &&
+ skillid != CR_CULTIVATION &&
+ skillid != AC_SHOWER)
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if (sd && skillnotok(skillid, sd)) // [MouseJstr]
+// return 0;
+
+ switch(skillid)
+ {
+ case PR_BENEDICTIO: /* ?¹??~•Ÿ */
+ skill_area_temp[1] = src->id;
+ map_foreachinarea(skill_area_sub,
+ src->m, x-1, y-1, x+1, y+1, BL_PC,
+ src, skillid, skilllv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-1, y-1, x+1, y+1, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case AC_SHOWER:
+ { //One of the few skills that can attack traps.
+ int r = 2;
+ map_foreachinarea (skill_area_sub,
+ src->m, x-r, y-r, x+r, y+r, BL_CHAR|BL_SKILL,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+ break;
+
+ case BS_HAMMERFALL:
+ {
+ int r = 2;
+ if (skilllv > 5) {
+ r = 14;
+ skilllv = 5; // ƒXƒ^ƒ“—¦?オ‚è‚·‚¬‚邽‚ߌvŽZ‚ÍLv5‚ŌŒè
+ }
+ skill_area_temp[1] = src->id;
+ skill_area_temp[2] = x;
+ skill_area_temp[3] = y;
+ map_foreachinarea (skill_area_sub,
+ src->m, x-r, y-r, x+r, y+r, BL_CHAR,
+ src, skillid, skilllv, tick, flag|BCT_ENEMY|2,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case HT_DETECTING: /* ƒfƒBƒeƒNƒeƒBƒ“ƒO */
+ map_foreachinarea( status_change_timer_sub,
+ src->m, x-1, y-1, x+1,y+1,BL_CHAR,
+ src,SC_SIGHT,tick);
+ break;
+
+ case MG_SAFETYWALL: /* ƒZƒCƒtƒeƒBƒEƒH?ƒ‹ */
+ case MG_FIREWALL: /* ƒtƒ@ƒCƒ„?ƒEƒH?ƒ‹ */
+ case MG_THUNDERSTORM: /* ƒTƒ“ƒ_?ƒXƒg?ƒ€ */
+ case AL_PNEUMA: /* ƒjƒ…?ƒ} */
+ case WZ_ICEWALL: /* ƒAƒCƒXƒEƒH?ƒ‹ */
+ case WZ_FIREPILLAR: /* ƒtƒ@ƒCƒAƒsƒ‰? */
+ case WZ_QUAGMIRE: /* ƒNƒ@ƒOƒ}ƒCƒA */
+ case WZ_VERMILION: /* ƒ??ƒhƒIƒuƒ”ƒ@?ƒ~ƒŠƒIƒ“ */
+ case WZ_STORMGUST: /* ƒXƒg?ƒ€ƒKƒXƒg */
+ case WZ_HEAVENDRIVE: /* ƒwƒ”ƒ“ƒYƒhƒ‰ƒCƒu */
+ case PR_SANCTUARY: /* ƒTƒ“ƒNƒ`ƒ…ƒAƒŠ */
+ case PR_MAGNUS: /* ƒ}ƒOƒkƒXƒGƒNƒ\ƒVƒYƒ€ */
+ case CR_GRANDCROSS: /* ƒOƒ‰ƒ“ƒhƒNƒ?ƒX */
+ case NPC_GRANDDARKNESS: /*ˆÅƒOƒ‰ƒ“ƒhƒNƒ?ƒX*/
+ case HT_SKIDTRAP: /* ƒXƒLƒbƒhƒgƒ‰ƒbƒv */
+ case HT_LANDMINE: /* ƒ‰ƒ“ƒhƒ}ƒCƒ“ */
+ case HT_ANKLESNARE: /* ƒAƒ“ƒNƒ‹ƒXƒlƒA */
+ case HT_SHOCKWAVE: /* ƒVƒ‡ƒbƒNƒEƒF?ƒuƒgƒ‰ƒbƒv */
+ case HT_SANDMAN: /* ƒTƒ“ƒhƒ}ƒ“ */
+ case HT_FLASHER: /* ƒtƒ‰ƒbƒVƒƒ? */
+ case HT_FREEZINGTRAP: /* ƒtƒŠ?ƒWƒ“ƒOƒgƒ‰ƒbƒv */
+ case HT_BLASTMINE: /* ƒuƒ‰ƒXƒgƒ}ƒCƒ“ */
+ case HT_CLAYMORETRAP: /* ƒNƒŒƒCƒ‚ƒA?ƒgƒ‰ƒbƒv */
+ case AS_VENOMDUST: /* ƒxƒmƒ€ƒ_ƒXƒg */
+ case AM_DEMONSTRATION: /* ƒfƒ‚ƒ“ƒXƒgƒŒ?ƒVƒ‡ƒ“ */
+ case PF_FOGWALL: /* ƒtƒHƒOƒEƒH?ƒ‹ */
+ case PF_SPIDERWEB: /* ƒXƒpƒCƒ_?ƒEƒFƒbƒu */
+ case HT_TALKIEBOX: /* ƒg?ƒL?ƒ{ƒbƒNƒX */
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ skill_clear_unitgroup(src);
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case RG_CLEANER: // [Valaris]
+ map_foreachinarea(skill_graffitiremover,src->m,x-5,y-5,x+5,y+5,BL_SKILL);
+ break;
+ case SA_VOLCANO: /* ƒ{ƒ‹ƒP?ƒm */
+ case SA_DELUGE: /* ƒfƒŠƒ…?ƒW */
+ case SA_VIOLENTGALE: /* ƒoƒCƒIƒŒƒ“ƒgƒQƒCƒ‹ */
+ case SA_LANDPROTECTOR: /* ƒ‰ƒ“ƒhƒvƒ?ƒeƒNƒ^? */
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
+ case WZ_METEOR: //ƒ?ƒeƒIƒXƒg?ƒ€
+ {
+ int flag=0;
+ if (sc_data && sc_data[SC_MAGICPOWER].timer != -1)
+ flag = flag|2; //Store the magic power flag for future use. [Skotlex]
+ for(i=0;i<2+(skilllv>>1);i++) {
+ int j=0;
+ do {
+ tmpx = x + (rand()%7 - 3);
+ tmpy = y + (rand()%7 - 3);
+ if(tmpx < 0)
+ tmpx = 0;
+ else if(tmpx >= map[src->m].xs)
+ tmpx = map[src->m].xs - 1;
+ if(tmpy < 0)
+ tmpy = 0;
+ else if(tmpy >= map[src->m].ys)
+ tmpy = map[src->m].ys - 1;
+ j++;
+ } while((map_getcell(src->m,tmpx,tmpy,CELL_CHKNOPASS)) && j<100);
+ if(j >= 100)
+ continue;
+ if(!(flag&1)){
+ clif_skill_poseffect(src,skillid,skilllv,tmpx,tmpy,tick);
+ flag=flag|1;
+ }
+ if(i > 0)
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,(x1<<16)|y1,flag&2); //Only pass the Magic Power flag
+ x1 = tmpx;
+ y1 = tmpy;
+ }
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,-1,flag&2); //Only pass the Magic Power flag
+ }
+ break;
+
+ case AL_WARP: /* ƒ??ƒvƒ|?ƒ^ƒ‹ */
+ if(sd) {
+ clif_skill_warppoint(sd,skillid,mapindex_id2name(sd->status.save_point.map),
+ (sd->skilllv>1)?mapindex_id2name(sd->status.memo_point[0].map):"",
+ (sd->skilllv>2)?mapindex_id2name(sd->status.memo_point[1].map):"",
+ (sd->skilllv>3)?mapindex_id2name(sd->status.memo_point[2].map):"");
+ }
+ break;
+
+ case MO_BODYRELOCATION:
+ if (sd) {
+ pc_movepos(sd, x, y, 1);
+ pc_blockskill_start (sd, MO_EXTREMITYFIST, 2000);
+ } else if (src->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)src;
+ mob_warp(md, -1, x, y, 0);
+ clif_spawnmob(md);
+ }
+ break;
+ case AM_CANNIBALIZE: // ƒoƒCƒIƒvƒ‰ƒ“ƒg
+ if(sd) {
+ int id;
+ int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ struct mob_data *md;
+
+ // Correct info, don't change any of this! [celest]
+ id = mob_once_spawn (sd, "this", x, y, sd->status.name, summons[skilllv-1] ,1,"");
+
+ if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){
+ md->master_id = sd->bl.id;
+ // different levels of HP according to skill level
+ md->hp = 1500 + skilllv * 200 + sd->status.base_level * 10;
+ md->max_hp = md->hp; //Update the max, too! [Skotlex]
+ md->special_state.ai = 1;
+ //”ñˆÚ“®‚ŃAƒNƒeƒBƒu‚Å”½Œ‚‚·‚é[0x0:”ñˆÚ“® 0x1:ˆÚ“® 0x4:ACT 0x8:”ñACT 0x40:”½Œ‚–³ 0x80:”½Œ‚—L]
+ md->mode = MD_CANATTACK|MD_AGGRESSIVE;
+ md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0);
+ }
+ // To-do: ?¢ŠÒ‚³‚ê‚郂ƒ“ƒXƒ^?[‚É‚Í?¢ŠÒ‚µ‚½ƒvƒŒ?[ƒ„?[‚Ì–¼‘O‚ª•t‚«‚Ü‚·
+ // (attach name of player?)
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ }
+ break;
+ case AM_SPHEREMINE: // ƒXƒtƒBƒA?ƒ}ƒCƒ“
+ if(sd){
+ int id;
+ struct mob_data *md;
+
+ id = mob_once_spawn(sd, "this", x, y, sd->status.name, 1142, 1, "");
+ if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){
+ md->master_id = sd->bl.id;
+ md->hp = 2000 + skilllv * 400;
+ md->max_hp = md->hp; //Update the max, too! [Skotlex]
+ md->mode = md->db->mode|MD_CANMOVE; //Needed for the skill
+ md->special_state.ai = 2;
+ md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0);
+ }
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ }
+ break;
+
+ // Slim Pitcher [Celest]
+ case CR_SLIMPITCHER:
+ {
+ if (sd) {
+ int i = skilllv%11 - 1;
+ int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]);
+ if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL ||
+ sd->status.inventory[j].amount < skill_db[skillid].amount[i]) {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ potion_flag = 1;
+ potion_hp = 0;
+ run_script(sd->inventory_data[j]->script,0,sd->bl.id,0);
+ pc_delitem(sd,j,skill_db[skillid].amount[i],0);
+ potion_flag = 0;
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ if(potion_hp > 0) {
+ map_foreachinarea(skill_area_sub,
+ src->m,x-3,y-3,x+3,y+3,BL_CHAR,
+ src,skillid,skilllv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ }
+ }
+ break;
+
+ case HW_GANBANTEIN:
+ if (rand()%100 < 80) {
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ map_foreachinarea (skill_ganbatein, src->m, x-1, y-1, x+1, y+1, BL_SKILL);
+ } else {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ break;
+
+ case HW_GRAVITATION:
+ {
+ struct skill_unit_group *sg;
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ sg = skill_unitsetting(src,skillid,skilllv,x,y,0);
+ status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,BCT_SELF,(int)sg,
+ skill_get_time(skillid,skilllv),0);
+ }
+ break;
+
+ // Plant Cultivation [Celest]
+ case CR_CULTIVATION:
+ {
+ if (sd) {
+ int i = skilllv - 1;
+ int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]);
+ if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL ||
+ sd->status.inventory[j].amount < skill_db[skillid].amount[i]) {
+ clif_skill_fail(sd,skillid,0,0);
+ return 1;
+ }
+ pc_delitem(sd,j,skill_db[skillid].amount[i],0);
+ clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
+ if (rand()%100 < 50)
+ mob_once_spawn(sd, "this", x, y, "--ja--",(skilllv < 2 ? 1084+rand()%2 : 1078+rand()%6), 1, "");
+ else
+ clif_skill_fail(sd,skillid,0,0);
+ }
+ }
+ break;
+ }
+
+ if (sc_data && sc_data[SC_MAGICPOWER].timer != -1)
+ status_change_end(&sd->bl,SC_MAGICPOWER,-1);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i‰r?¥Š®—¹?AmapŽw’è?j
+ *------------------------------------------
+ */
+int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map)
+{
+ int x=0,y=0;
+
+ nullpo_retr(0, sd);
+
+//Simplify skill_failed code.
+#undef skill_failed
+#define skill_failed(sd) { sd->skillid = sd->skilllv = sd->skillitem = sd->skillitemlv = -1; }
+
+ if( sd->bl.prev == NULL || pc_isdead(sd) )
+ return 0;
+
+//Shouldn't be needed, skillnotok's return value is highly unlikely to have changed after you started casting. [Skotlex]
+// if(skillnotok(skill_num, sd))
+// return 0;
+
+ if( sd->opt1>0 || sd->status.option&2 ) {
+ skill_failed(sd);
+ return 0;
+ }
+ //ƒXƒLƒ‹‚ªŽg‚¦‚È‚¢?‘ÔˆÙ?í’†
+ if(sd->sc_count){
+ if( sd->sc_data[SC_SILENCE].timer!=-1 ||
+ sd->sc_data[SC_ROKISWEIL].timer!=-1 ||
+ sd->sc_data[SC_AUTOCOUNTER].timer != -1 ||
+ sd->sc_data[SC_STEELBODY].timer != -1 ||
+ sd->sc_data[SC_DANCING].timer!=-1 ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ sd->sc_data[SC_MARIONETTE].timer != -1)
+ return 0;
+ }
+
+ if( skill_num != sd->skillid) /* •s?³ƒpƒPƒbƒg‚炵‚¢ */
+ return 0;
+
+ if (strlen(map) > MAP_NAME_LENGTH-1)
+ { //Map_length check, as it is sent by the client and we shouldn't trust it [Skotlex]
+ if (battle_config.error_log)
+ ShowError("skill_castend_map: Received map name '%s' too long!\n", map);
+ skill_failed(sd);
+ return 0;
+ }
+
+ pc_stopattack(sd);
+
+ if(battle_config.pc_skill_log)
+ ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_num,map);
+ pc_stop_walking(sd,0);
+
+ if(strcmp(map,"cancel")==0) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ switch(skill_num){
+ case AL_TELEPORT: /* ƒeƒŒƒ|?ƒg */
+ 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: /* ƒ??ƒvƒ|?ƒ^ƒ‹ */
+ {
+ const struct point *p[4];
+ struct skill_unit_group *group;
+ int i;
+ int maxcount=0;
+ unsigned short mapindex;
+ mapindex = mapindex_name2id((char*)map);
+ if(!mapindex) { //Given map not found?
+ clif_skill_fail(sd,skill_num,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ p[0] = &sd->status.save_point;
+ p[1] = &sd->status.memo_point[0];
+ p[2] = &sd->status.memo_point[1];
+ p[3] = &sd->status.memo_point[2];
+
+ if((maxcount = skill_get_maxcount(skill_num)) > 0) {
+ int c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill_num)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill_num,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ if(sd->skilllv <= 0) return 0;
+ for(i=0;i<sd->skilllv;i++){
+ if(mapindex == p[i]->map){
+ x=p[i]->x;
+ y=p[i]->y;
+ break;
+ }
+ }
+ if(x==0 || y==0) { /* •s?³ƒpƒPƒbƒg?H */
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition(sd,3))
+ {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(skill_check_unit_range2(&sd->bl,sd->bl.m,sd->skillx,sd->skilly,skill_num,sd->skilllv) > 0) {
+ clif_skill_fail(sd,0,0,0);
+ skill_failed(sd);
+ return 0;
+ }
+ if((group=skill_unitsetting(&sd->bl,skill_num,sd->skilllv,sd->skillx,sd->skilly,0))==NULL) {
+ skill_failed(sd);
+ return 0;
+ }
+ //Now that there's a mapindex, use that in val3 rather than a string. [Skotlex]
+ group->val3 = mapindex;
+// group->valstr=(char *)aCallocA(MAP_NAME_LENGTH,sizeof(char));
+// memcpy(group->valstr,map,MAP_NAME_LENGTH-1);
+ group->val2=(x<<16)|y;
+ }
+ break;
+ }
+
+ sd->skillid = sd->skilllv = -1;
+ return 0;
+}
+
+/*==========================================
+ * Initializes and sets a ground skill.
+ * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
+ * flag&2 is used to determine if this skill was casted with Magic Power active.
+ *------------------------------------------
+ */
+struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag)
+{
+ struct skill_unit_group *group;
+ int i,limit,val1=0,val2=0,val3=0;
+ int count=0;
+ int target,interval,range,unit_flag;
+ struct skill_unit_layout *layout;
+ struct status_change *sc_data;
+ int active_flag=1;
+
+ nullpo_retr(0, src);
+
+ limit = skill_get_time(skillid,skilllv);
+ range = skill_get_unit_range(skillid);
+ interval = skill_get_unit_interval(skillid);
+ target = skill_get_unit_target(skillid);
+ unit_flag = skill_get_unit_flag(skillid);
+ layout = skill_get_unit_layout(skillid,skilllv,src,x,y);
+
+ sc_data = status_get_sc_data(src); // for traps, firewall and fogwall - celest
+
+ switch(skillid){ /* ?Ý’è */
+
+ case MG_SAFETYWALL: /* ƒZƒCƒtƒeƒBƒEƒH?ƒ‹ */
+ val2=skilllv+1;
+ break;
+ case MG_FIREWALL: /* ƒtƒ@ƒCƒ„?ƒEƒH?ƒ‹ */
+ if(sc_data && sc_data[SC_VIOLENTGALE].timer!=-1)
+ limit = limit*3/2;
+ val2=4+skilllv;
+ break;
+
+ case AL_WARP: /* ƒ??ƒvƒ|?ƒ^ƒ‹ */
+ val1=skilllv+6;
+ if(!(flag&1))
+ limit=2000;
+ active_flag=0;
+ break;
+
+ case PR_SANCTUARY: /* ƒTƒ“ƒNƒ`ƒ…ƒAƒŠ */
+ val1=(skilllv+3)*2;
+ val2=(skilllv>6)?777:skilllv*100;
+ break;
+
+ case WZ_FIREPILLAR: /* ƒtƒ@ƒCƒA?ƒsƒ‰? */
+ if((flag&1)!=0)
+ limit=1000;
+ val1=skilllv+2;
+ if(skilllv >= 6)
+ range=2;
+ break;
+ case WZ_METEOR:
+ if (skilllv > 10) //?L”͈̓?ƒeƒI
+ range = 10;
+ break;
+ case WZ_VERMILION:
+ if (skilllv > 10) //?L”͈ÍLOV
+ range = 25;
+ break;
+ case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex]
+ case AM_DEMONSTRATION:
+ if (map_flag_vs(src->m) && battle_config.vs_traps_bctall)
+ target = BCT_ALL;
+ break;
+ case HT_SHOCKWAVE: /* ƒVƒ‡ƒbƒNƒEƒF?ƒuƒgƒ‰ƒbƒv */
+ val1=skilllv*15+10;
+ case HT_SANDMAN: /* ƒTƒ“ƒhƒ}ƒ“ */
+ case HT_CLAYMORETRAP: /* ƒNƒŒƒCƒ‚ƒA?ƒgƒ‰ƒbƒv */
+ case HT_SKIDTRAP: /* ƒXƒLƒbƒhƒgƒ‰ƒbƒv */
+ case HT_LANDMINE: /* ƒ‰ƒ“ƒhƒ}ƒCƒ“ */
+ case HT_ANKLESNARE: /* ƒAƒ“ƒNƒ‹ƒXƒlƒA */
+ case HT_FLASHER: /* ƒtƒ‰ƒbƒVƒƒ? */
+ case HT_FREEZINGTRAP: /* ƒtƒŠ?ƒWƒ“ƒOƒgƒ‰ƒbƒv */
+ case HT_BLASTMINE: /* ƒuƒ‰ƒXƒgƒ}ƒCƒ“ */
+ if (sc_data && sc_data[SC_INTOABYSS].timer != -1)
+ val3 = BD_INTOABYSS; //Store into abyss state, to know it shouldn't give traps back. [Skotlex]
+ if (map_flag_gvg(src->m))
+ limit *= 4; // longer trap times in WOE [celest]
+ if (battle_config.vs_traps_bctall && map_flag_vs(src->m))
+ target = BCT_ALL; //Change target to all [Skotlex]
+ break;
+
+ case SA_LANDPROTECTOR: /* ƒOƒ‰ƒ“ƒhƒNƒ?ƒX */
+ {
+ int aoe_diameter; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills
+ val1=skilllv*15+10;
+ aoe_diameter=skilllv+skilllv%2+5;
+ count=aoe_diameter*aoe_diameter; // -- this will not function if changed to ^2 (moonsoul)
+ }
+ //No break because we also have to check if we use gemstones. [Skotlex]
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ {
+ struct skill_unit_group *old_sg;
+ if ((old_sg = skill_locate_element_field(src)) != NULL)
+ {
+ if (old_sg->skill_id == skillid && old_sg->limit > 0)
+ { //Use the previous limit (minus the elapsed time) [Skotlex]
+ limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick);
+ if (limit < 0) //This can happen...
+ limit = skill_get_time(skillid,skilllv);
+ }
+ skill_clear_element_field(src);
+ }
+ break;
+ }
+
+ case BA_DISSONANCE:
+ case DC_UGLYDANCE:
+ val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BA_WHISTLE:
+ val1 = skilllv+(status_get_agi(src)/10); // Flee increase
+ val2 = ((skilllv+1)/2)+(status_get_luk(src)/10); // Perfect dodge increase
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_HUMMING:
+ val1 = 2*skilllv+(status_get_dex(src)/10); // Hit increase
+ if(src->type == BL_PC)
+ val1 += 2*pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ break;
+ case BA_POEMBRAGI:
+ val1 = 3*skilllv+(status_get_dex(src)/10); // Casting time reduction
+ val2 = 3*skilllv+(status_get_int(src)/10); // After-cast delay reduction
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_DONTFORGETME:
+ val1 = 3*skilllv+(status_get_dex(src)/10); // ASPD decrease
+ val2 = 2*skilllv+(status_get_agi(src)/10); // Movement speed decrease
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_APPLEIDUN:
+ val1 = 5+2*skilllv+(status_get_vit(src)/10); // MaxHP percent increase
+ val2 = 30+5*skilllv+5*(status_get_vit(src)/10); // HP recovery
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ val2 += 5*pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_SERVICEFORYOU:
+ val1 = 10+skilllv+(status_get_int(src)/10); // MaxSP percent increase
+ val2 = 10+3*skilllv+(status_get_int(src)/10); // SP cost reduction
+ if(src->type == BL_PC){
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_ASSASSINCROSS:
+ val1 = 10+skilllv+(status_get_agi(src)/10); // ASPD increase
+ if(src->type == BL_PC)
+ val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON);
+ break;
+ case DC_FORTUNEKISS:
+ val1 = 10+skilllv+(status_get_luk(src)/10); // Critical increase
+ if(src->type == BL_PC)
+ val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON);
+ break;
+ case BD_LULLABY:
+ val1 = 11; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BD_DRUMBATTLEFIELD:
+ val1 = (skilllv+1)*25; //Watk increase
+ val2 = (skilllv+1)*2; //Def increase
+ break;
+ case BD_RINGNIBELUNGEN:
+ val1 = (skilllv+2)*25; //Watk increase
+ break;
+ case BD_SIEGFRIED:
+ val1 = 55 + skilllv*5; //Elemental Resistance
+ val2 = skilllv*10; //Status ailment resistance
+ break;
+ case BD_ETERNALCHAOS:
+ break;
+ case PF_FOGWALL: /* ƒtƒHƒOƒEƒH?ƒ‹ */
+ if(sc_data && sc_data[SC_DELUGE].timer!=-1) limit *= 2;
+ break;
+
+ case RG_GRAFFITI: /* Graffiti */
+ count=1; // Leave this at 1 [Valaris]
+ break;
+ }
+
+ if (val3==0 && (flag&2 || (sc_data && sc_data[SC_MAGICPOWER].timer != -1)))
+ val3 = HW_MAGICPOWER; //Store the magic power flag. [Skotlex]
+
+ nullpo_retr(NULL, group=skill_initunitgroup(src,(count > 0 ? count : layout->count),
+ skillid,skilllv,skill_get_unit_id(skillid,flag&1)));
+ group->limit=limit;
+ group->val1=val1;
+ group->val2=val2;
+ group->val3=val3;
+ group->target_flag=target;
+ group->bl_flag= skill_get_unit_bl_target(skillid);
+ group->interval=interval;
+ if(skillid==HT_TALKIEBOX ||
+ skillid==RG_GRAFFITI){
+ group->valstr=(char *) aCallocA(MESSAGE_SIZE, sizeof(char));
+ if(group->valstr==NULL){
+ ShowFatalError("skill_castend_map: out of memory !\n");
+ exit(1);
+ }
+ memcpy(group->valstr,talkie_mes,MESSAGE_SIZE-1);
+ }
+
+ //Why redefine local variables when the ones of the function can be reused? [Skotlex]
+ val1=skilllv;
+ val2=0;
+ limit=group->limit;
+ for(i=0;i<layout->count;i++){
+ struct skill_unit *unit;
+ int ux,uy,alive=1;
+ ux = x + layout->dx[i];
+ uy = y + layout->dy[i];
+ switch (skillid) {
+ case MG_FIREWALL: /* ƒtƒ@ƒCƒ„?ƒEƒH?ƒ‹ */
+ val2=group->val2;
+ break;
+ case WZ_ICEWALL: /* ƒAƒCƒXƒEƒH?ƒ‹ */
+ if(skilllv <= 1)
+ val1 = 500;
+ else
+ val1 = 200 + 200*skilllv;
+ break;
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ ux+=(i%5-2);
+ uy+=(i/5-2);
+ break;
+ }
+ //’¼?ãƒXƒLƒ‹‚Ì?ê?‡?Ý’u?À•W?ã‚Ƀ‰ƒ“ƒhƒvƒ?ƒeƒNƒ^?‚ª‚È‚¢‚©ƒ`ƒFƒbƒN
+ if(range<=0)
+ map_foreachincell(skill_landprotector,src->m,ux,uy,BL_SKILL,skillid,&alive, src);
+
+ if(alive && map_getcell(src->m,ux,uy,CELL_CHKWALL))
+ alive = 0;
+
+ if (alive && battle_config.skill_wall_check) {
+ //Check if there's a path between cell and center of casting.
+ struct walkpath_data wpd;
+ if (path_search(&wpd,src->m,ux,uy,x,y,1)==-1)
+ alive = 0;
+ }
+
+ if(alive && skillid == WZ_ICEWALL) {
+ if(src->x == x && src->y==y) // Ice Wall not allowed on self [DracoRPG]
+ alive=0;
+ else {
+ val2=map_getcell(src->m,ux,uy,CELL_GETTYPE);
+ if(val2==5 || val2==1)
+ alive=0;
+ else
+ clif_changemapcell(src->m,ux,uy,5,0);
+ }
+ }
+
+ if(alive){
+ nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy));
+ unit->val1=val1;
+ unit->val2=val2;
+ unit->limit=limit;
+ unit->range=range;
+
+ if (range==0 && active_flag)
+ map_foreachincell(skill_unit_effect,unit->bl.m,
+ unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ }
+ }
+
+ return group;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚Ì?“®ƒCƒxƒ“ƒg
+ *------------------------------------------
+ */
+int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ struct status_change *sc_data;
+ int type;
+ short *opt;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if(bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_retr(0, sg=src->group);
+ nullpo_retr(0, ss=map_id2bl(sg->src_id));
+
+ if (map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR))
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if (battle_check_target(&src->bl,bl,sg->target_flag)<=0)
+ return 0;
+
+ if ((opt = status_get_option(bl)) && ((*opt)&OPTION_HIDE) && sg->skill_id != WZ_HEAVENDRIVE)
+ return 0; //Hidden characters are inmune to AoE skills except Heaven's Drive. [Skotlex]
+
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ switch (sg->unit_id) {
+ case UNT_SAFETYWALL:
+ //TODO: Find a more reliable way to handle the link to sg, this could cause dangling pointers. [Skotlex]
+ if (sc_data && sc_data[type].timer == -1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,(int)sg,0,sg->limit,0);
+ break;
+
+ case UNT_PNEUMA:
+ if (sc_data && sc_data[type].timer == -1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,sg->limit,0);
+ break;
+
+ case UNT_WARP_WAITING:
+ if(bl->type==BL_PC){
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if((!sd->chatID || battle_config.chat_warpportal)
+ && sd->to_x == src->bl.x && sd->to_y == src->bl.y) {
+ if (pc_setpos(sd,sg->val3,sg->val2>>16,sg->val2&0xffff,3) == 0) {
+ if (--sg->val1<=0 || sg->src_id == bl->id)
+ skill_delunitgroup(sg);
+ }
+ }
+ } else if(bl->type==BL_MOB && battle_config.mob_warpportal){
+ int m = map_mapindex2mapid(sg->val3);
+ mob_warp((struct mob_data *)bl,m,sg->val2>>16,sg->val2&0xffff,3);
+ }
+ break;
+
+ case UNT_QUAGMIRE:
+ if(status_isimmune(bl))
+ break;
+ if(sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,sg->limit,0);
+ break;
+
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ if(sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,
+ skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+ case UNT_RICHMANKIM:
+ case UNT_ETERNALCHAOS:
+ case UNT_DRUMBATTLEFIELD:
+ case UNT_RINGNIBELUNGEN:
+ case UNT_ROKISWEIL:
+ case UNT_INTOABYSS:
+ case UNT_SIEGFRIED:
+ //Needed to check when a dancer/bard leaves their ensemble area.
+ if (sg->src_id==bl->id && (!sc_data || sc_data[SC_SPIRIT].timer == -1 || sc_data[SC_SPIRIT].val2 != SL_BARDDANCER))
+ return sg->skill_id;
+ if (sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2,0,sg->limit,0);
+ break;
+
+ case UNT_WHISTLE:
+ case UNT_ASSASSINCROSS:
+ case UNT_POEMBRAGI:
+ case UNT_APPLEIDUN:
+ case UNT_HUMMING:
+ case UNT_DONTFORGETME:
+ case UNT_FORTUNEKISS:
+ case UNT_SERVICEFORYOU:
+ case UNT_HERMODE:
+ if (sg->src_id==bl->id && (!sc_data || sc_data[SC_SPIRIT].timer == -1 || sc_data[SC_SPIRIT].val2 != SL_BARDDANCER))
+ return 0;
+ if (sc_data && sc_data[type].timer==-1)
+ status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2,0,sg->limit,0);
+ break;
+
+ case UNT_BASILICA:
+ if (!(status_get_mode(bl)&MD_BOSS) && battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_blown(&src->bl,bl,1);
+ break;
+
+ case UNT_FOGWALL:
+ if (sc_data && sc_data[type].timer==-1)
+ {
+ status_change_start (bl, type, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit, 0);
+ if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick);
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ if (sc_data && sc_data[type].timer==-1 && !status_get_mode(bl)&MD_BOSS)
+ status_change_start(bl,type,sg->skill_lv,5*sg->skill_lv,BCT_ENEMY,sg->group_id,sg->limit,0);
+ break;
+
+ case UNT_ICEWALL: //Destroy the cell. [Skotlex]
+ src->val1 = 0;
+ if(src->limit + sg->tick > tick + 700)
+ src->limit = DIFF_TICK(tick+700,sg->tick);
+ break;
+ case UNT_GOSPEL:
+ if (sg->src_id != bl->id && battle_check_target(ss,bl,BCT_PARTY)>0 &&
+ sc_data && sc_data[type].timer==-1) //Start Gospel Effect to prevent item usage affects party only. [Skotlex]
+ status_change_start(bl,type,sg->skill_lv,0,0,BCT_ALL,sg->limit,0);
+ break;
+ }
+
+ return sg->skill_id;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚Ì”­“®ƒCƒxƒ“ƒg(ƒ^ƒCƒ}?[”­“®)
+ *------------------------------------------
+ */
+int skill_unit_onplace_timer(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ struct map_session_data *sd = NULL;
+ int splash_count=0;
+ struct status_change *sc_data, *ssc_data;
+ struct skill_unit_group_tickset *ts;
+ int type;
+ int diff=0;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+
+ if (bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_retr(0, sg=src->group);
+ nullpo_retr(0, ss=map_id2bl(sg->src_id));
+ if (ss->type == BL_PC) sd = (struct map_session_data*)ss;
+ ssc_data = status_get_sc_data(ss); //For magic power.
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ if (sg->interval == -1 && (sg->unit_id == UNT_ANKLESNARE || sg->unit_id == UNT_SPIDERWEB || sg->unit_id == UNT_FIREPILLAR_ACTIVE))
+ //Ok, this case only happens with Ankle Snare/Spider Web (only skills that sets its interval to -1),
+ //and only happens when more than one target is stepping on the trap at the moment it was triggered
+ //(yet only the first mob standing on the trap will be captured) [Skotlex]
+ return 0;
+
+ if ((ts = skill_unitgrouptickset_search(bl,sg,tick)))
+ { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex]
+ diff = DIFF_TICK(tick,ts->tick);
+ if (diff < 0)
+ return 0;
+ ts->tick = tick+sg->interval;
+
+ // GX‚Í?d‚È‚Á‚Ä‚¢‚½‚ç3HIT‚µ‚È‚¢
+ if ((sg->skill_id==CR_GRANDCROSS || sg->skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
+ ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,0)-1);
+ }
+ //Temporarily set magic power to have it take effect. [Skotlex]
+ if (sg->val3 == HW_MAGICPOWER && ssc_data && ssc_data[SC_MAGICPOWER].timer == -1 && ssc_data[SC_MAGICPOWER].val1 > 0)
+ {
+ if (sd)
+ { //This is needed since we are not going to recall status_calc_pc...
+ sd->matk1 += sd->matk1 * 5*ssc_data[SC_MAGICPOWER].val1/100;
+ sd->matk2 += sd->matk2 * 5*ssc_data[SC_MAGICPOWER].val1/100;
+ } else
+ ssc_data[SC_MAGICPOWER].timer = -2; //Note to NOT return from the function until this is unset!
+ }
+
+ switch (sg->unit_id) {
+ case UNT_FIREWALL:
+ {
+ int flag=0, t_ele = status_get_elem_type(bl);
+ if (t_ele == 3 || battle_check_undead(status_get_race(bl), t_ele))
+ flag = src->val2>battle_config.firewall_hits_on_undead?battle_config.firewall_hits_on_undead:src->val2;
+
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,flag);
+ src->val2-=flag?flag:1;
+ if (src->val2<=0)
+ skill_delunit(src);
+ break;
+ }
+ case UNT_SANCTUARY:
+ {
+ int race = status_get_race(bl);
+
+ if (battle_check_undead(race, status_get_elem_type(bl)) || race==6) {
+ if (skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0)) {
+ // reduce healing count if this was meant for damaging [hekate]
+ sg->val1 -= 2;
+ }
+ } else {
+ int heal = sg->val2;
+ if (status_get_hp(bl) >= status_get_max_hp(bl))
+ break;
+ if (status_isimmune(bl))
+ heal = 0; /* ‰©‹à峃J?[ƒh?iƒq?[ƒ‹—Ê‚O?j */
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ battle_heal(NULL, bl, heal, 0, 0);
+ if (diff >= 500)
+ sg->val1--; // ?V‹K‚É“ü‚Á‚½ƒ†ƒjƒbƒg‚¾‚¯ƒJƒEƒ“ƒg
+ }
+ if (sg->val1 <= 0)
+ skill_delunitgroup(sg);
+ break;
+ }
+
+ case UNT_MAGNUS:
+ {
+ int race = status_get_race(bl);
+ if (!battle_check_undead(race,status_get_elem_type(bl)) && race!=6)
+ break;
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ src->val2++;
+ break;
+ }
+
+ case UNT_MAGIC_SKILLS:
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_FIREPILLAR_WAITING:
+ skill_delunit(src);
+ skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1);
+ break;
+
+ case UNT_FIREPILLAR_ACTIVE:
+ map_foreachinarea(skill_attack_area,bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,sg->bl_flag,
+ BF_MAGIC,ss,&src->bl,sg->skill_id,sg->skill_lv,tick,0,BCT_ENEMY); // area damage [Celest]
+ sg->interval = -1; //Mark it used up so others can't trigger it for massive splash damage. [Skotlex]
+ sg->limit=DIFF_TICK(tick,sg->tick) + 1500;
+ break;
+
+ case UNT_SKIDTRAP:
+ {
+ skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv)|0x10000);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,sg->unit_id);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ break;
+
+ case UNT_ANKLESNARE:
+ if(sg->val2==0 && sc_data && sc_data[SC_ANKLE].timer==-1){
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv) - status_get_agi(bl)*100;
+ if(status_get_mode(bl)&MD_BOSS) // Lasts 5 times less on bosses
+ sec = sec/5;
+ if (sec < 3000+30*sg->skill_lv) // Minimum trap time of 3+0.03*skilllv seconds [celest]
+ sec = 3000+30*sg->skill_lv;
+ battle_stopwalking(bl,1);
+ status_change_start(bl,SC_ANKLE,sg->skill_lv,0,0,0,sec,0);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ //clif_01ac(&src->bl); //Removed? Check the openkore description of this packet: [Skotlex]
+ // 01AC: long ID
+ // Indicates that an object is trapped, but ID is not a
+ // valid monster or player ID.
+ sg->limit=DIFF_TICK(tick,sg->tick) + sec;
+ sg->val2=bl->id;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_VENOMDUST:
+ if(sc_data && sc_data[type].timer==-1 )
+ status_change_start(bl,type,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+ case UNT_LANDMINE:
+ skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,UNT_FIREPILLAR_ACTIVE);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ break;
+
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ map_foreachinarea(skill_count_target,src->bl.m
+ ,src->bl.x-src->range,src->bl.y-src->range
+ ,src->bl.x+src->range,src->bl.y+src->range
+ ,sg->bl_flag,&src->bl,&splash_count);
+ map_foreachinarea(skill_trap_splash,src->bl.m
+ ,src->bl.x-src->range,src->bl.y-src->range
+ ,src->bl.x+src->range,src->bl.y+src->range
+ ,sg->bl_flag,&src->bl,tick,splash_count);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl,LOOK_BASE,sg->unit_id);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ break;
+
+ case UNT_TALKIEBOX:
+ if (sg->src_id == bl->id) //Ž©•ª‚ª“¥‚ñ‚Å‚à”­“®‚µ‚È‚¢
+ break;
+ if (sg->val2 == 0){
+ clif_talkiebox(&src->bl, sg->valstr);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changelook(&src->bl, LOOK_BASE, sg->unit_id);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 5000;
+ sg->val2 = -1; //“¥‚ñ‚¾
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ break;
+
+ case UNT_LULLABY:
+ if (ss->id == bl->id)
+ break;
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick);
+ break;
+
+ case UNT_UGLYDANCE: //Ugly Dance [Skotlex]
+ if (ss->id == bl->id)
+ break;
+ if (bl->type == BL_PC)
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick);
+ break;
+
+ case UNT_DISSONANCE:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_APPLEIDUN: //Apple of Idun [Skotlex]
+ {
+ int heal;
+ if (sg->src_id == bl->id)
+ break;
+ heal = sg->val2;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ battle_heal(NULL, bl, heal, 0, 0);
+ break;
+ }
+
+ case UNT_DEMONSTRATION:
+ skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_GOSPEL:
+ if (rand()%100 > sg->skill_lv*10)
+ break;
+ if (ss != bl && battle_check_target(ss,bl,BCT_PARTY)>0) { // Support Effect only on party, not guild
+ int i = rand()%13; // Positive buff count
+ switch (i)
+ {
+ case 0: // Heal 1~9999 HP
+ {
+ int heal = rand() %9999+1;
+ clif_skill_nodamage(ss,bl,AL_HEAL,heal,1);
+ battle_heal(NULL,bl,heal,0,0);
+ }
+ break;
+ case 1: // End all negative status
+ status_change_clear_debuffs (bl);
+ break;
+ case 2: // Level 10 Blessing
+ status_change_start(bl,SC_BLESSING,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 3: // Level 10 Increase AGI
+ status_change_start(bl,SC_INCREASEAGI,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 4: // Enchant weapon with Holy element
+ status_change_start(bl,SC_ASPERSIO,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 5: // Enchant armor with Holy element
+ status_change_start(bl,SC_BENEDICTIO,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 6: // MaxHP +100%
+ status_change_start(bl,SC_INCMHPRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 7: // MaxSP +100%
+ status_change_start(bl,SC_INCMSPRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 8: // All stats +20
+ status_change_start(bl,SC_INCALLSTATUS,20,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 9: // DEF +25%
+ status_change_start(bl,SC_INCDEFRATE,25,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 10: // ATK +100%
+ status_change_start(bl,SC_INCATKRATE,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 11: // HIT/Flee +50
+ status_change_start(bl,SC_INCHIT,50,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ status_change_start(bl,SC_INCFLEE,50,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 12: // Immunity to all status
+ status_change_start(bl,SC_SCRESIST,100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ }
+ }
+ else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) { // Offensive Effect
+ int i = rand()%9; // Negative buff count
+ switch (i)
+ {
+ case 0: // Deal 1~9999 damage
+ {
+ int dmg = rand() % 9999 +1;
+ clif_skill_damage(bl, bl, sg->tick,0,0,dmg,0,CR_HOLYCROSS,1,-1);
+ battle_damage(ss, bl, dmg,0);
+ break;
+ }
+ case 1: // Curse
+ status_change_start(bl,SC_CURSE,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 2: // Blind
+ status_change_start(bl,SC_BLIND,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 3: // Poison
+ status_change_start(bl,SC_POISON,1,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 4: // Level 10 Provoke
+ status_change_start(bl,SC_PROVOKE,10,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 5: // DEF -100%
+ status_change_start(bl,SC_INCDEFRATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 6: // ATK -100%
+ status_change_start(bl,SC_INCATKRATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 7: // Flee -100%
+ status_change_start(bl,SC_INCFLEERATE,-100,0,0,0,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ case 8: // Speed/ASPD -25%
+ status_change_start(bl,SC_GOSPEL,1,0,0,BCT_ENEMY,skill_get_time2(sg->skill_id, sg->skill_lv),0);
+ break;
+ }
+ }
+ break;
+
+ case UNT_SPIDERWEB:
+ if(sg->val2==0 && (!sc_data || sc_data[type].timer==-1 )){
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ sg->limit = DIFF_TICK(tick,sg->tick)+skill_get_time2(sg->skill_id,sg->skill_lv);
+ sg->val2=bl->id;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ }
+ if (sg->val3 == HW_MAGICPOWER && ssc_data && ssc_data[SC_MAGICPOWER].timer < 0 && ssc_data[SC_MAGICPOWER].val1 > 0)
+ { //Unset Magic Power.
+ if (sd)
+ {
+ sd->matk1 = 100*sd->matk1/(100 + 5*ssc_data[SC_MAGICPOWER].val1);
+ sd->matk2 = 100*sd->matk2/(100 + 5*ssc_data[SC_MAGICPOWER].val1);
+ } else
+ ssc_data[SC_MAGICPOWER].timer = -1;
+ }
+
+ if (bl->type == BL_MOB && ss != bl) { /* ƒXƒLƒ‹Žg—p?Œ?‚ÌMOBƒXƒLƒ‹ */
+ struct mob_data *md = (struct mob_data *)bl;
+ if (!md) return 0;
+ if (battle_config.mob_changetarget_byskill == 1) {
+ int target = md->target_id;
+ if (ss->type == BL_PC)
+ md->target_id = ss->id;
+ mobskill_use(md, tick, MSC_SKILLUSED|(sg->skill_id << 16));
+ md->target_id = target;
+ } else
+ mobskill_use(md, tick, MSC_SKILLUSED|(sg->skill_id << 16));
+ }
+
+ return sg->skill_id;
+}
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚©‚ç—£?‚·‚é(‚à‚µ‚­‚Í‚µ‚Ä‚¢‚é)?ê?‡
+ *------------------------------------------
+ */
+int skill_unit_onout(struct skill_unit *src,struct block_list *bl,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct status_change *sc_data;
+ int type;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sg=src->group);
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[sg->skill_id];
+
+ if (bl->prev==NULL || !src->alive || //Need to delete the trap if the source died.
+ (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB))
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SAFETYWALL:
+ if (sc_data && sc_data[type].timer!=-1)
+ status_change_end(bl,type,-1);
+ break;
+ case UNT_ANKLESNARE:
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if(target && target == bl){
+ status_change_end(bl,SC_ANKLE,-1);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1000;
+ sg->val3 = BD_INTOABYSS; //Prevent Remove Trap from giving you the trap back. [Skotlex]
+ }
+ else
+ return 0;
+ break;
+ }
+ case UNT_BASILICA: //Clear basilica if the owner moved [Skotlex]
+ case UNT_HERMODE: //Clear Hermode if the owner moved.
+ if (sc_data && sc_data[type].timer!=-1 && sc_data[type].val3 == BCT_SELF && sc_data[type].val4 == sg->group_id)
+ status_change_end(bl,type,-1);
+ break;
+
+ case UNT_SPIDERWEB: /* ƒXƒpƒCƒ_?ƒEƒFƒbƒu */
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if (target && target==bl)
+ {
+ status_change_end(bl,SC_SPIDERWEB,-1);
+ sg->limit = DIFF_TICK(tick,sg->tick)+1000;
+ }
+ break;
+ }
+ }
+ return sg->skill_id;
+}
+
+/*==========================================
+ * Triggered when a char steps out of a skill group [Skotlex]
+ *------------------------------------------
+ */
+static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick)
+{
+ struct status_change *sc_data;
+ int type;
+
+ sc_data = status_get_sc_data(bl);
+ type = SkillStatusChangeTable[skill_id];
+
+ switch (skill_id)
+ {
+ case WZ_QUAGMIRE:
+ if (bl->type==BL_MOB)
+ break;
+ if (sc_data && sc_data[type].timer != -1)
+ status_change_end(bl, type, -1);
+ break;
+
+ case BD_RICHMANKIM:
+ case BD_ETERNALCHAOS:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_ROKISWEIL:
+ case BD_INTOABYSS:
+ case BD_SIEGFRIED:
+ if(sc_data && sc_data[SC_DANCING].timer != -1 && sc_data[SC_DANCING].val1 == skill_id)
+ { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex]
+ //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance.
+ //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner,
+ //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble
+ //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel
+ //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it.
+ skill_stop_dancing(bl);
+ }
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case CG_HERMODE:
+ case HW_GRAVITATION:
+ if (sc_data && sc_data[type].timer != -1)
+ status_change_end(bl, type, -1);
+ break;
+
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ if (sc_data && sc_data[type].timer != -1)
+ {
+ delete_timer(sc_data[type].timer, status_change_timer);
+ //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
+ //not possible on our current implementation.
+ sc_data[type].timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type);
+ }
+ break;
+ case PF_FOGWALL:
+ if (sc_data && sc_data[type].timer != -1)
+ {
+ status_change_end(bl,type,-1);
+ if (sc_data[SC_BLIND].timer!=-1)
+ {
+ if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex]
+ status_change_end(bl, SC_BLIND, -1);
+ else {
+ delete_timer(sc_data[SC_BLIND].timer, status_change_timer);
+ sc_data[SC_BLIND].timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND);
+ }
+ }
+ }
+ break;
+ case UNT_GOSPEL:
+ if (sc_data && sc_data[type].timer != -1 && sc_data[type].val4 == BCT_ALL) //End item-no-use Gospel Effect. [Skotlex]
+ status_change_end(bl, type, -1);
+ break;
+
+ }
+ return skill_id;
+}
+
+/*==========================================
+ * Invoked when a unit cell has been placed/removed/deleted.
+ * flag values:
+ * flag&1: Invoke onplace function (otherwise invoke onout)
+ * flag&4: Invoke a onleft call (the unit might be scheduled for deletion)
+ *------------------------------------------
+ */
+int skill_unit_effect(struct block_list *bl,va_list ap)
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ int flag;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit=va_arg(ap,struct skill_unit*));
+ tick = va_arg(ap,unsigned int);
+ flag = va_arg(ap,unsigned int);
+
+ if (!unit->alive || bl->prev==NULL)
+ return 0;
+
+ nullpo_retr(0, group=unit->group);
+
+ if (flag&1)
+ skill_unit_onplace(unit,bl,tick);
+ else
+ skill_unit_onout(unit,bl,tick);
+
+ if (flag&4) skill_unit_onleft(group->skill_id, bl, tick);
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚ÌŒÀŠEƒCƒxƒ“ƒg
+ *------------------------------------------
+ */
+int skill_unit_onlimit(struct skill_unit *src,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ nullpo_retr(0, src);
+ nullpo_retr(0, sg=src->group);
+
+ switch(sg->unit_id){
+ case UNT_WARP_ACTIVE: /* ƒ??ƒvƒ|?ƒ^ƒ‹(?“®‘O) */
+ {
+ struct skill_unit_group *group=
+ skill_unitsetting(map_id2bl(sg->src_id),sg->skill_id,sg->skill_lv,
+ src->bl.x,src->bl.y,1);
+ if(group == NULL)
+ return 0;
+ group->val2=sg->val2; //Copy the (x,y) position you warp to
+ group->val3=sg->val3; //as well as the mapindex to warp to.
+ }
+ break;
+
+ case UNT_ICEWALL: /* ƒAƒCƒXƒEƒH?ƒ‹ */
+ clif_changemapcell(src->bl.m,src->bl.x,src->bl.y,src->val2,1);
+ break;
+ case UNT_CALLPARTNER: /* ‚ ‚È‚½‚É?‚¢‚½‚¢ */
+ {
+ struct map_session_data *sd = NULL;
+ if((sd = map_id2sd(sg->src_id)) == NULL)
+ return 0;
+ if((sd = pc_get_partner(sd)) == NULL)
+ return 0;
+
+ pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3);
+ }
+ break;
+ }
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚̃_ƒ??ƒWƒCƒxƒ“ƒg
+ *------------------------------------------
+ */
+int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
+ int damage,unsigned int tick)
+{
+ struct skill_unit_group *sg;
+
+ nullpo_retr(0, src);
+ nullpo_retr(0, sg=src->group);
+
+ if (skill_get_inf2(sg->skill_id)&INF2_TRAP && damage > 0)
+ skill_delunitgroup(sg);
+ else
+ switch(sg->unit_id){
+ case UNT_ICEWALL:
+ src->val1-=damage;
+ break;
+ default:
+ damage = 0;
+ break;
+ }
+ return damage;
+}
+
+static int skill_moonlit_sub(struct block_list *bl, va_list ap) {
+ struct block_list *src = va_arg(ap, struct block_list*);
+ struct block_list *partner = va_arg(ap, struct block_list*);
+ int blowcount = va_arg(ap, int);
+ if (bl == src || bl == partner)
+ return 0;
+ skill_blown(src, bl, blowcount);
+ return 1;
+}
+
+/*==========================================
+ * Starts the moonlit effect by first knocking back all other characters in the vecinity.
+ * partner may be null, but src cannot be.
+ *------------------------------------------
+ */
+static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv)
+{
+ int range = skill_get_range2(src, CG_MOONLIT, skilllv);
+ int blowcount = range+1, time = skill_get_time(CG_MOONLIT,skilllv);
+
+ map_foreachinarea(skill_moonlit_sub,src->m
+ ,src->x-range,src->y-range
+ ,src->x+range,src->y+range
+ ,BL_CHAR,src,partner,blowcount);
+ if(partner)
+ map_foreachinarea(skill_moonlit_sub,partner->m
+ ,partner->x-range,partner->y-range
+ ,partner->x+range,partner->y+range
+ ,BL_CHAR,src,partner,blowcount);
+
+ status_change_start(src,SC_DANCING,CG_MOONLIT,0,0,partner?partner->id:BCT_SELF,time+1000,0);
+ status_change_start(src,SkillStatusChangeTable[CG_MOONLIT],skilllv,0,0,0,time,0);
+
+ if (partner) {
+ status_change_start(partner,SC_DANCING,CG_MOONLIT,0,0,src->id,time+1000,0);
+ status_change_start(partner,SkillStatusChangeTable[CG_MOONLIT],skilllv,0,0,0,time,0);
+ }
+
+}
+/*==========================================
+ * ”Í??ƒLƒƒƒ‰‘¶?ÝŠm”F”»’è?—?(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_check_condition_char_sub (struct block_list *bl, va_list ap)
+{
+ int *c, skillid;
+ struct block_list *src;
+ struct map_session_data *sd;
+ struct map_session_data *tsd;
+ int *p_sd; //Contains the list of characters found.
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, tsd=(struct map_session_data*)bl);
+ nullpo_retr(0, src=va_arg(ap,struct block_list *));
+ nullpo_retr(0, sd=(struct map_session_data*)src);
+
+ c=va_arg(ap,int *);
+ p_sd = va_arg(ap, int *);
+ skillid = va_arg(ap,int);
+
+ if ((skillid != PR_BENEDICTIO && *c >=1) || *c >=2)
+ return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex]
+
+ if (bl == src)
+ return 0;
+
+ if(pc_isdead(tsd))
+ return 0;
+
+ if (tsd->sc_count && (tsd->sc_data[SC_SILENCE].timer != -1 || tsd->sc_data[SC_STAN].timer != -1))
+ return 0;
+
+ switch(skillid)
+ {
+ case PR_BENEDICTIO: /* ?¹??~•Ÿ */
+ {
+ int dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y);
+ dir = (status_get_dir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing.
+ if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest.
+ && sd->status.sp >= 10)
+ p_sd[(*c)++]=tsd->bl.id;
+ return 1;
+ }
+ default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex]
+ {
+ int skilllv;
+ if(pc_issit(tsd) || tsd->skilltimer!=-1 || tsd->canmove_tick > tick)
+ return 0;
+ if (sd->status.sex != tsd->status.sex &&
+ (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER &&
+ (skilllv = pc_checkskill(tsd, skillid)) > 0 &&
+ (tsd->weapontype1==13 || tsd->weapontype1==14) &&
+ sd->status.party_id && tsd->status.party_id &&
+ sd->status.party_id == tsd->status.party_id &&
+ tsd->sc_data[SC_DANCING].timer == -1)
+ {
+ p_sd[(*c)++]=tsd->bl.id;
+ return skilllv;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks and stores partners for ensemble skills [Skotlex]
+ *------------------------------------------
+ */
+static int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag)
+{
+ static int c=0;
+ static int p_sd[2] = { 0, 0 };
+ int i;
+ if (cast_flag)
+ { //Execute the skill on the partners.
+ struct map_session_data* tsd;
+ switch (skill_id)
+ {
+ case PR_BENEDICTIO:
+ for (i = 0; i < c; i++)
+ {
+ if ((tsd = map_id2sd(p_sd[i])) != NULL)
+ {
+ tsd->status.sp -= 10;
+ if (tsd->status.sp < 0)
+ tsd->status.sp = 0;
+ clif_updatestatus(tsd,SP_SP);
+ }
+ }
+ return c;
+ case CG_MOONLIT:
+ if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL)
+ {
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ skill_moonlit(&sd->bl, &tsd->bl, *skill_lv);
+ tsd->skillid_dance = tsd->skillid = skill_id;
+ tsd->skilllv_dance = tsd->skilllv = *skill_lv;
+ }
+ return c;
+ default: //Warning: Assuming Ensemble skills here (for speed)
+ if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL)
+ {
+ sd->sc_data[SC_DANCING].val4= tsd->bl.id;
+ status_change_start(&tsd->bl,SC_DANCING,skill_id,sd->sc_data[SC_DANCING].val2,0,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000,0);
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ tsd->skillid_dance = tsd->skillid = skill_id;
+ tsd->skilllv_dance = tsd->skilllv = *skill_lv;
+ }
+ return c;
+ }
+ }
+ //Else: new search for partners.
+ c = 0;
+ memset (p_sd, 0, sizeof(p_sd));
+ i = map_foreachinarea(skill_check_condition_char_sub, sd->bl.m,
+ sd->bl.x-range, sd->bl.y-range, sd->bl.x+range,
+ sd->bl.y+range, BL_PC, &sd->bl, &c, &p_sd, skill_id);
+
+ if (skill_id != PR_BENEDICTIO) //Apply the average lv to encore skills.
+ *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners.
+ return c;
+}
+
+/*==========================================
+ * ”Í??ƒoƒCƒIƒvƒ‰ƒ“ƒg?AƒXƒtƒBƒAƒ}ƒCƒ“—pMob‘¶?ÝŠm”F”»’è?—?(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_check_condition_mob_master_sub(struct block_list *bl,va_list ap)
+{
+ int *c,src_id=0,mob_class=0;
+ struct mob_data *md;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, md=(struct mob_data*)bl);
+ nullpo_retr(0, src_id=va_arg(ap,int));
+ nullpo_retr(0, mob_class=va_arg(ap,int));
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ if(md->class_==mob_class && md->master_id==src_id)
+ (*c)++;
+ return 0;
+}
+
+static int skill_check_condition_hermod_sub(struct block_list *bl,va_list ap)
+{
+ int *c;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=(struct npc_data*)bl);
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ if (nd->bl.subtype == WARP)
+ (*c)++;
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?Œ??i?‚ÅŽg—pŽ¸”s?j
+ *------------------------------------------
+ */
+int skill_check_condition(struct map_session_data *sd,int type)
+{
+ int i,hp,sp,hp_rate,sp_rate,zeny,weapon,state,spiritball,skill,lv,mhp;
+ int index[10],itemid[10],amount[10];
+ int arrow_flag = 0;
+ int force_gem_flag = 0;
+ int delitem_flag = 1, checkitem_flag = 1;
+
+ nullpo_retr(0, sd);
+
+ if( battle_config.gm_skilluncond>0 &&
+ pc_isGM(sd)>= battle_config.gm_skilluncond &&
+ sd->skillitem != sd->skillid)
+ { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->skillitem = sd->skillitemlv = -1;
+ return 1;
+ }
+
+ if( sd->opt1>0) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+ if(pc_is90overweight(sd)) {
+ clif_skill_fail(sd,sd->skillid,9,0);
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+
+ if (sd->state.abra_flag)
+ {
+ sd->skillitem = sd->skillitemlv = -1;
+ if(type&1) sd->state.abra_flag = 0;
+ return 1;
+ }
+
+ if (sd->state.produce_flag &&
+ (sd->skillid == AM_PHARMACY || sd->skillid == AC_MAKINGARROW || sd->skillid == BS_REPAIRWEAPON ||
+ sd->skillid == AM_TWILIGHT1 || sd->skillid == AM_TWILIGHT2 || sd->skillid == AM_TWILIGHT3
+ )) {
+ sd->skillitem = sd->skillitemlv = -1;
+ return 0;
+ }
+
+ if(sd->skillitem == sd->skillid) { /* ƒAƒCƒeƒ€‚Ì?ê?‡–³?Œ??¬Œ÷ */
+ if(!type) //When a target was selected
+ { //Consume items that were skipped in pc_use_item [Skotlex]
+ if((i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ sd->inventory_data[i] == NULL ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, item exploit?
+ sd->itemid = sd->itemindex = -1;
+ return 0;
+ }
+ //Consume
+ sd->itemid = sd->itemindex = -1;
+ if(sd->skillid == WZ_EARTHSPIKE
+ && sd->sc_data[SC_TKDORI].timer != -1 && rand()%100 > sd->sc_data[SC_TKDORI].val2) // [marquis007]
+ ; //Do not consume item.
+ else
+ pc_delitem(sd,i,1,0);
+ }
+ if (type&1) //Casting finished
+ sd->skillitem = sd->skillitemlv = -1;
+ return 1;
+ }
+ if( sd->opt1>0 ){
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0;
+ }
+ if(sd->sc_count){
+ if( sd->sc_data[SC_SILENCE].timer!=-1 ||
+ sd->sc_data[SC_ROKISWEIL].timer!=-1 ||
+ (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) ||
+ sd->sc_data[SC_STEELBODY].timer != -1 ||
+ sd->sc_data[SC_BERSERK].timer != -1 ||
+ (sd->sc_data[SC_MARIONETTE].timer != -1 && sd->skillid != CG_MARIONETTE)){
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0; /* ?‘ÔˆÙ?í‚â’¾?‚È‚Ç */
+ }
+ }
+
+ skill = sd->skillid;
+ lv = sd->skilllv;
+ if (lv <= 0) return 0;
+ // for the guild skills [celest]
+ if (skill >= 10000 && skill < 10015) skill-= 9500;
+ hp = skill_get_hp(skill, lv); /* ?Á”ïHP */
+ sp = skill_get_sp(skill, lv); /* ?Á”ïSP */
+ if((sd->skillid_old == BD_ENCORE) && skill == sd->skillid_dance)
+ sp=sp/2; //ƒAƒ“ƒR?ƒ‹Žž‚ÍSP?Á””¼•ª
+ hp_rate = (lv <= 0)? 0:skill_db[skill].hp_rate[lv-1];
+ sp_rate = (lv <= 0)? 0:skill_db[skill].sp_rate[lv-1];
+ zeny = skill_get_zeny(skill,lv);
+ weapon = skill_db[skill].weapon;
+ state = skill_db[skill].state;
+ spiritball = (lv <= 0)? 0:skill_db[skill].spiritball[lv-1];
+ mhp = skill_get_mhp(skill, lv); /* ?Á”ïHP */
+ for(i = 0; i < 10; i++) {
+ itemid[i] = skill_db[skill].itemid[i];
+ amount[i] = skill_db[skill].amount[i];
+ }
+ if(mhp > 0)
+ hp += (sd->status.max_hp * mhp)/100;
+ if(hp_rate > 0)
+ hp += (sd->status.hp * hp_rate)/100;
+ else
+ hp += (sd->status.max_hp * abs(hp_rate))/100;
+ if(sp_rate > 0)
+ sp += (sd->status.sp * sp_rate)/100;
+ else
+ sp += (sd->status.max_sp * abs(sp_rate))/100;
+
+ switch(skill) { // Check for cost reductions due to skills & SCs
+ case MC_MAMMONITE:
+ if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0)
+ zeny -= zeny*10/100;
+ break;
+ case AL_HOLYLIGHT:
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_PRIEST)
+ sp *= 5;
+ break;
+ case SL_SMA:
+ case SL_STUN:
+ case SL_STIN:
+ {
+ int kaina_lv = pc_checkskill(sd,SL_KAINA);
+
+ if(kaina_lv==0 || sd->status.base_level<70)
+ break;
+ if(sd->status.base_level>=90)
+ sp -= sp*7*kaina_lv/100;
+ else if(sd->status.base_level>=80)
+ sp -= sp*5*kaina_lv/100;
+ else if(sd->status.base_level>=70)
+ sp -= sp*3*kaina_lv/100;
+ }
+ break;
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_MONK)
+ sp -= sp*25/100; //FIXME: Need real data. this is a custom value.
+ break;
+ }
+
+ if(sd->dsprate!=100)
+ sp=sp*sd->dsprate/100; /* ?Á”ïSP?C?³ */
+
+ switch(skill) {
+ case SA_CASTCANCEL:
+ if(sd->skilltimer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case BS_MAXIMIZE: /* ƒ}ƒLƒVƒ}ƒCƒYƒpƒ?? */
+ case NV_TRICKDEAD: /* Ž€‚ñ‚¾‚Ó‚è */
+ case TF_HIDING: /* ƒnƒCƒfƒBƒ“ƒO */
+ case AS_CLOAKING: /* ƒNƒ??ƒLƒ“ƒO */
+ case CR_AUTOGUARD: /* ƒI?ƒgƒK?ƒh */
+ case CR_DEFENDER: /* ƒfƒBƒtƒFƒ“ƒ_? */
+ case ST_CHASEWALK:
+ case PA_GOSPEL:
+ case CR_SHRINK:
+ if(sd->sc_data[SkillStatusChangeTable[skill]].timer!=-1)
+ return 1; /* ‰ð?œ‚·‚é?ê?‡‚ÍSP?Á”‚È‚¢ */
+ break;
+
+ case TK_RUN:
+ if(sd->sc_data[SC_RUN].timer!=-1){
+ status_change_end(&sd->bl,SC_RUN,-1);
+ if(sd->sc_data[SC_SPURT].timer!=-1)
+ status_change_end(&sd->bl,SC_SPURT,-1);
+ return 0;
+ }
+ break;
+
+ case AL_WARP:
+ if(!(type&2)) //Delete the item when the portal has been selected (type&2). [Skotlex]
+ delitem_flag = 0;
+ if(map[sd->bl.m].flag.nowarp) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ clif_displaymessage(sd->fd, "Duel: Can't use warp in duel.");
+ return 0;
+ }
+ break;
+ case AL_TELEPORT:
+ if(map[sd->bl.m].flag.noteleport) {
+ clif_skill_teleportmessage(sd,0);
+ return 0;
+ }
+ break;
+ case MO_CALLSPIRITS: /* ?Œ÷ */
+ if(sd->spiritball >= lv) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case CH_SOULCOLLECT: /* ‹¶?Œ÷ */
+ if(sd->spiritball >= 5) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE: //Žw?
+ if (sd->spiritball > 0 && sd->spiritball < spiritball) {
+ spiritball = sd->spiritball;
+ sd->spiritball_old = sd->spiritball;
+ }
+ else sd->spiritball_old = lv;
+ break;
+ case MO_BODYRELOCATION:
+ if (sd->sc_data[SC_EXPLOSIONSPIRITS].timer!=-1)
+ spiritball = 0;
+ break;
+ case MO_CHAINCOMBO: //˜A‘Å?¶
+ 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: //˜A’Œ•ö?
+ if(sd->sc_data[SC_COMBO].timer == -1)
+ return 0;
+ if(sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST)
+ return 0;
+ break;
+ case MO_EXTREMITYFIST: // ˆ¢?C—…”e–PŒ?
+// if(sd->sc_data[SC_EXTREMITYFIST].timer != -1) //To disable Asura during the 5 min skill block uncomment this...
+// return 0;
+ if(sd->sc_data[SC_BLADESTOP].timer!=-1)
+ spiritball--;
+ else if (sd->sc_data[SC_COMBO].timer != -1) {
+ if (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH)
+ spiritball = 4;
+ else if (sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)
+ spiritball = 3;
+ else if (sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)
+ spiritball = sd->spiritball?sd->spiritball:1;
+ //It should consume whatever is left as long as it's at least 1.
+ }
+ break;
+
+ case TK_MISSION: //Does not works on Non-Taekwon
+ if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ case TK_READYCOUNTER:
+ case TK_READYDOWN:
+ case TK_READYSTORM:
+ case TK_READYTURN:
+ case TK_JUMPKICK:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) {
+ //They do not work on Soul Linkers.
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if(sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == skill)
+ break; //Combo ready.
+ if (pc_istop10fame(sd->char_id,MAPID_TAEKWON))
+ break; //Unlimited Combo
+ return 0;
+ case BD_ADAPTATION: /* ƒAƒhƒŠƒu */
+ {
+ 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))){ //ƒ_ƒ“ƒX’†‚ÅŽg—pŒã5•bˆÈ?ã‚Ì‚Ý?H
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case PR_BENEDICTIO: /* ?¹??~•Ÿ */
+ {
+ if (!battle_config.player_skill_partner_check)
+ break; //No need to do any partner checking [Skotlex]
+ if (!(type&1))
+ { //Started casting.
+ if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ else
+ { //Done casting
+ //Should I repeat the check? If so, it would be best to only do this on cast-ending. [Skotlex]
+ skill_check_pc_partner(sd, skill, &lv, 1, 1);
+ }
+ }
+ break;
+ case WE_CALLPARTNER: /* ‚ ‚È‚½‚Ɉ§‚¢‚½‚¢ */
+ if(!sd->status.partner_id){
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case AM_CANNIBALIZE: /* ƒoƒCƒIƒvƒ‰ƒ“ƒg */
+ case AM_SPHEREMINE: /* ƒXƒtƒBƒA?ƒ}ƒCƒ“ */
+ if(type&1){
+ int c=0;
+ int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int maxcount = (skill==AM_CANNIBALIZE)? 6-lv : skill_get_maxcount(skill);
+ int mob_class = (skill==AM_CANNIBALIZE)? summons[lv-1] :1142;
+ if(battle_config.pc_land_skill_limit && maxcount>0) {
+ map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class,&c );
+ if(c >= maxcount){
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case MG_FIREWALL: /* ƒtƒ@ƒCƒA?ƒEƒH?ƒ‹ */
+ case WZ_QUAGMIRE:
+ case PF_FOGWALL:
+ /* ??§ŒÀ */
+ if(battle_config.pc_land_skill_limit) {
+ int maxcount = skill_get_maxcount(skill);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case WZ_FIREPILLAR: // celest
+ if (lv <= 5) // no gems required at level 1-5
+ itemid[0] = 0;
+ if(battle_config.pc_land_skill_limit) {
+ int maxcount = skill_get_maxcount(skill);
+ if(maxcount > 0) {
+ int i,c;
+ for(i=c=0;i<MAX_SKILLUNITGROUP;i++) {
+ if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill)
+ c++;
+ }
+ if(c >= maxcount) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case SL_SMA:
+ if(type) break; //Only do the combo check when the target is selected (type == 0)
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != skill)
+ return 0;
+ break;
+ // skills require arrows as of 12/07 [celest]
+ case HT_POWER:
+ if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != skill)
+ return 0;
+ case AC_DOUBLE:
+ case AC_SHOWER:
+ case AC_CHARGEARROW:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case SN_SHARPSHOOTING:
+ case CG_ARROWVULCAN:
+ arrow_flag = 1; //Venom Knife does not gets the arrow deleted because
+ //it gets deleted as part of the skill requirements. [Skotlex]
+ case AS_VENOMKNIFE:
+ if(sd->equip_index[10] < 0) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ break;
+ case RG_BACKSTAP:
+ if(sd->status.weapon == 11) {
+ if (sd->equip_index[10] < 0) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ }
+ arrow_flag = 1;
+ }
+ break;
+ case HW_GANBANTEIN:
+ force_gem_flag = 1;
+ break;
+ case AM_POTIONPITCHER:
+ case CR_SLIMPITCHER:
+ case MG_STONECURSE:
+ case CR_CULTIVATION:
+ case SA_FLAMELAUNCHER:
+ case SA_FROSTWEAPON:
+ case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON:
+ delitem_flag = 0;
+ break;
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ { //Does not consumes if the skill is already active. [Skotlex]
+ struct skill_unit_group *sg;
+ if ((sg= skill_locate_element_field(&sd->bl)) != NULL && sg->skill_id == skill)
+ {
+ if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0)
+ checkitem_flag = delitem_flag = 0;
+ else sg->limit = 0; //Disable it.
+ }
+ break;
+ }
+ case CG_HERMODE:
+ {
+ int c = 0;
+ map_foreachinarea (skill_check_condition_hermod_sub, sd->bl.m,
+ sd->bl.x-3, sd->bl.y-3, sd->bl.x+3, sd->bl.y+3, BL_NPC, &c);
+ if (c < 1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
+ {
+ int i,x,y,range = skill_get_range2(&sd->bl, skill, lv)+1;
+ int size = range*2+1;
+ for (i=0;i<size*size;i++) {
+ x = sd->bl.x+(i%size-range);
+ y = sd->bl.y+(i/size-range);
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if(((exp = pc_nextbaseexp(sd)) > 0 && sd->status.base_exp*100/exp < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && sd->status.job_exp*100/exp < 1)) {
+ clif_skill_fail(sd,skill,0,0); //Not enough exp.
+ return 0;
+ }
+ break;
+ }
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ if (!party_skill_check(sd, sd->status.party_id, skill, lv))
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ case SG_SUN_WARM:
+ if(sd->bl.m == sd->feel_map[0].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_MOON_WARM:
+ if(sd->bl.m == sd->feel_map[1].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_STAR_WARM:
+ if(sd->bl.m == sd->feel_map[2].m)
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ break;
+ case SG_SUN_COMFORT:
+ if(sd->bl.m == sd->feel_map[0].m && (battle_config.allow_skill_without_day || is_day_of_sun()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_MOON_COMFORT:
+ if(sd->bl.m == sd->feel_map[1].m && (battle_config.allow_skill_without_day || is_day_of_moon()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_STAR_COMFORT:
+ if(sd->bl.m == sd->feel_map[2].m && (battle_config.allow_skill_without_day || is_day_of_star()))
+ break;
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ case SG_FUSION:
+ if ((sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_STAR) || sd->sc_data[SC_FUSION].timer != -1)
+ break;
+ return 0;
+ }
+
+ if(!(type&2)){
+ if( hp>0 && sd->status.hp < hp) { /* HPƒ`ƒFƒbƒN */
+ clif_skill_fail(sd,skill,2,0); /* HP•s‘«?FŽ¸”s’Ê’m */
+ return 0;
+ }
+ if( sp>0 && sd->status.sp < sp) { /* SPƒ`ƒFƒbƒN */
+ clif_skill_fail(sd,skill,1,0); /* SP•s‘«?FŽ¸”s’Ê’m */
+ 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); // Ÿ†‹…•s‘«
+ return 0;
+ }
+ }
+
+ switch(state) {
+ case ST_HIDING:
+ if(!(sd->status.option&OPTION_HIDE)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CLOAKING:
+ if(!pc_iscloaking(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_HIDDEN:
+ if(!pc_ishiding(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_RIDING:
+ if(!pc_isriding(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_FALCON:
+ if(!pc_isfalcon(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CART:
+ if(!pc_iscarton(sd)) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_SHIELD:
+ if(sd->status.shield <= 0) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_SIGHT:
+ if(sd->sc_data[SC_SIGHT].timer == -1 && type&1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_EXPLOSIONSPIRITS:
+ if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_CARTBOOST:
+ if(!pc_iscarton(sd) || sd->sc_data[SC_CARTBOOST].timer == -1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_RECOV_WEIGHT_RATE:
+ if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case ST_MOVE_ENABLE:
+ {
+ struct walkpath_data wpd;
+ if(path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,sd->skillx,sd->skilly,1)==-1) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ }
+ break;
+ case ST_WATER:
+ //?…?ê”»’è
+ //(!map[sd->bl.m].flag.rain) && //they have removed RAIN effect. [Lupus]
+ if ( (sd->sc_data[SC_DELUGE].timer == -1) &&
+ (!map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER)))
+ {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+
+ }
+
+ if (checkitem_flag) {
+ for(i=0;i<10;i++) {
+ int x = lv%11 - 1;
+ index[i] = -1;
+ if(itemid[i] <= 0)
+ continue;
+ if(itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone && !force_gem_flag)
+ continue;
+ if(((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065)
+ && sd->sc_data[SC_INTOABYSS].timer != -1 && !force_gem_flag)
+ continue;
+ if((skill == AM_POTIONPITCHER ||
+ skill == CR_SLIMPITCHER ||
+ skill == CR_CULTIVATION) && i != x)
+ continue;
+
+ index[i] = pc_search_inventory(sd,itemid[i]);
+ if(index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) {
+ if(itemid[i] == 716 || itemid[i] == 717)
+ clif_skill_fail(sd,skill,(7+(itemid[i]-716)),0);
+ else
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ if((itemid[i] >= 715 && itemid[i] <= 717) && sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_WIZARD)
+ index[i] = -1; //Gemstones are checked, but not substracted from inventory.
+
+ }
+ }
+
+ if(!(type&1))
+ return 1;
+
+ if(delitem_flag) {
+ for(i=0;i<10;i++) {
+ if(index[i] >= 0)
+ pc_delitem(sd,index[i],amount[i],0); // ƒAƒCƒeƒ€?Á”ï
+ }
+ if (arrow_flag && battle_config.arrow_decrement)
+ pc_delitem(sd,sd->equip_index[10],1,0);
+ }
+
+ if(type&2)
+ return 1;
+
+ if(sp > 0) { // SP?Á”ï
+ sd->status.sp-=sp;
+ clif_updatestatus(sd,SP_SP);
+ }
+ if(hp > 0) { // HP?Á”ï
+ sd->status.hp-=hp;
+ clif_updatestatus(sd,SP_HP);
+ }
+ if(zeny > 0) // Zeny?Á”ï
+ pc_payzeny(sd,zeny);
+ if(spiritball > 0) // Ÿ†‹…?Á”ï
+ pc_delspiritball(sd,spiritball,0);
+
+
+ return 1;
+}
+
+/*==========================================
+ * ‰r?¥ŽžŠÔŒvŽZ
+ *------------------------------------------
+ */
+int skill_castfix( struct block_list *bl, int skill_id, int skill_lv, int time)
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+
+ if (!time && bl->type != BL_MOB)
+ time = skill_get_cast(skill_id, skill_lv);
+
+ if (bl->type == BL_PC){
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ nullpo_retr(0, sd);
+
+ // calculate base cast time (reduced by dex)
+ if (!skill_get_castnodex(sd->skillid, sd->skilllv) > 0) {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale > 0) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else return 0; // instant cast
+ }
+
+ // config cast time multiplier
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+
+ // calculate cast time reduced by card bonuses
+ if (sd->castrate != 100)
+ time -= time * (100 - sd->castrate) / 100;
+ } else if (bl->type == BL_PET) { //Skotlex: Simple scaling
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale > 0) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else return 0; // instant cast
+
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+ }
+
+ // calculate cast time reduced by skill bonuses
+ sc_data = status_get_sc_data(bl);
+ /* ƒTƒtƒ‰ƒMƒEƒ€ */
+ if (sc_data) {
+ if (sc_data[SC_SUFFRAGIUM].timer != -1) {
+ time -= time * (sc_data[SC_SUFFRAGIUM].val1 * 15) / 100;
+ status_change_end(bl, SC_SUFFRAGIUM, -1);
+ }
+ /* ƒuƒ‰ƒM‚ÌŽ? */
+ if (sc_data[SC_POEMBRAGI].timer != -1)
+ time -= time * sc_data[SC_POEMBRAGI].val2 / 100;
+ }
+
+ // return final cast time
+ return (time > 0) ? time : 0;
+}
+/*==========================================
+ * ƒfƒBƒŒƒCŒvŽZ
+ *------------------------------------------
+ */
+int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv, int time )
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+
+ if (!time && bl->type != BL_MOB)
+ time = skill_get_delay(skill_id, skill_lv);
+
+ if (bl->type == BL_PC){
+ struct map_session_data *sd = (struct map_session_data*)bl;
+ nullpo_retr(0, sd);
+
+ // instant cast attack skills depend on aspd as delay [celest]
+ if (time == 0) {
+ if (skill_get_type(skill_id) == BF_WEAPON)
+ time = status_get_amotion(bl); //Use attack animation as default delay.
+ else
+ time = 300; // default delay, according to official servers
+ } else if (time < 0)
+ time = abs(time) + status_get_amotion(bl); // if set to <0, the attack motion is added.
+
+ if (battle_config.delay_dependon_dex && /* dex‚̉e‹¿‚ðŒvŽZ‚·‚é */
+ !skill_get_delaynodex(skill_id, skill_lv)) // if skill casttime is allowed to be reduced by dex
+ {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale < 0)
+ scale = 0;
+ time = time * scale / battle_config.castrate_dex_scale;
+ }
+
+ if (battle_config.delay_rate != 100)
+ time = time * battle_config.delay_rate / 100;
+
+ if (sd->delayrate != 100)
+ time = time * sd->delayrate / 100;
+
+ if (time < battle_config.min_skill_delay_limit) // check minimum skill delay
+ time = battle_config.min_skill_delay_limit;
+ }
+
+ /* ƒuƒ‰ƒM‚ÌŽ? */
+ sc_data = status_get_sc_data(bl);
+ if (sc_data) {
+ if (sc_data[SC_POEMBRAGI].timer != -1)
+ time -= time * sc_data[SC_POEMBRAGI].val3 / 100;
+ if (sc_data[SC_SPIRIT].timer != -1)
+ switch (skill_id) {
+ case CR_SHIELDBOOMERANG:
+ if (sc_data[SC_SPIRIT].val2 == SL_CRUSADER)
+ time /=2;
+ break;
+ case AS_SONICBLOW:
+ if (!map_flag_gvg(bl->m) && sc_data[SC_SPIRIT].val2 == SL_ASSASIN)
+ time /= 2;
+ break;
+ }
+ }
+
+ return (time > 0) ? time : 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?iIDŽw’è?j
+ *------------------------------------------
+ */
+int skill_use_id (struct map_session_data *sd, int target_id, int skill_num, int skill_lv)
+{
+ struct map_session_data* tsd = NULL;
+ struct block_list *bl = NULL;
+ struct status_change *sc_data;
+ int casttime, forcecast = 0;
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, sd);
+
+ if (skill_lv <= 0)
+ return 0;
+
+ sc_data = sd->sc_data;
+ switch(skill_num)
+ { //Check for skills that auto-select target
+ case MO_CHAINCOMBO:
+ target_id = sd->attacktarget;
+ if (sc_data[SC_BLADESTOP].timer != -1){
+ if ((bl=(struct block_list *)sc_data[SC_BLADESTOP].val4) == NULL) //ƒ^?ƒQƒbƒg‚ª‚¢‚È‚¢H
+ return 0;
+ target_id = bl->id;
+ }
+ break;
+ case MO_COMBOFINISH:
+ case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ case TK_DOWNKICK:
+ case TK_TURNKICK:
+ target_id = sd->attacktarget;
+ break;
+
+ case TK_JUMPKICK:
+ case TK_COUNTER:
+ case HT_POWER:
+ if (sc_data[SC_COMBO].timer != -1 && sc_data[SC_COMBO].val1 == skill_num)
+ target_id = sc_data[SC_COMBO].val2;
+ else if (skill_num == TK_COUNTER) //This one is for Ranking TKers
+ target_id = sd->attacktarget;
+ else if (skill_num == HT_POWER)
+ return 0;
+ break;
+// -- moonsoul (altered to allow proper usage of extremity from new champion combos)
+//
+ case MO_EXTREMITYFIST: /*ˆ¢C—…”e–PŒ*/
+ if (sc_data[SC_COMBO].timer != -1 &&
+ (sc_data[SC_COMBO].val1 == MO_COMBOFINISH ||
+ sc_data[SC_COMBO].val1 == CH_TIGERFIST ||
+ sc_data[SC_COMBO].val1 == CH_CHAINCRUSH))
+ target_id = sd->attacktarget;
+ break;
+ case WE_MALE:
+ case WE_FEMALE:
+ if (!sd->status.partner_id)
+ return 0;
+ tsd = map_charid2sd(sd->status.partner_id);
+ bl = (struct block_list *)tsd;
+ if (bl)
+ target_id = bl->id;
+ else
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ break;
+ case WE_CALLBABY:
+ tsd = pc_get_child(sd);
+ bl = (struct block_list *)tsd;
+ if (bl)
+ target_id = bl->id;
+ else
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ break;
+ }
+ if (bl == NULL && (bl = map_id2bl(target_id)) == NULL)
+ return 0;
+
+ if (bl->type == BL_PC)
+ tsd = (struct map_session_data*)bl;
+
+ if (bl->prev == NULL) //Prevent targeting enemies that are not in the map. [Skotlex]
+ return 0;
+
+ if(sd->bl.m != bl->m)
+ return 0;
+
+ if(sd->skilltimer != -1 && skill_num != SA_CASTCANCEL) //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex]
+ return 0;
+
+ if(skillnotok(skill_num, sd)) // [MouseJstr]
+ return 0;
+ if (tsd && (skill_num == ALL_RESURRECTION || skill_num == PR_REDEMPTIO) && !pc_isdead(tsd))
+ return 0;
+
+ if(skill_get_inf2(skill_num)&INF2_NO_TARGET_SELF && sd->bl.id == target_id)
+ return 0;
+ if(!status_check_skilluse(&sd->bl, bl, skill_num, 0))
+ {
+ if(skill_num == PR_LEXAETERNA) //Eh.. assuming skill failed due to opponent frozen/stone-cursed. [Skotlex]
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+
+ //’¼‘O‚̃XƒLƒ‹‚ª‰½‚©?‚¦‚é•K—v‚Ì‚ ‚éƒXƒLƒ‹
+ switch (skill_num) {
+ case SA_CASTCANCEL:
+ if (sd->skillid != skill_num){ //ƒLƒƒƒXƒgƒLƒƒƒ“ƒZƒ‹Ž©?‚Í?‚¦‚È‚¢
+ sd->skillid_old = sd->skillid;
+ sd->skilllv_old = sd->skilllv;
+ }
+ break;
+
+ case BD_ENCORE: /* ƒAƒ“ƒR?ƒ‹ */
+ if (!sd->skillid_dance) { //‘O‰ñŽg—p‚µ‚½—x‚肪‚È‚¢‚Æ‚¾‚ß
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ } else {
+ sd->skillid_old = skill_num;
+ }
+ break;
+
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ case GD_EMERGENCYCALL:
+ {
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_num);
+ if (skill_lv <= 0) return 0;
+ }
+ break;
+
+ case BD_LULLABY: /* ŽqŽç‰Ì */
+ case BD_RICHMANKIM: /* ƒjƒˆƒ‹ƒh‚̉ƒ */
+ case BD_ETERNALCHAOS: /* ‰i‰“‚Ì?¬“× */
+ case BD_DRUMBATTLEFIELD: /* ?‘¾ŒÛ‚Ì‹¿‚« */
+ case BD_RINGNIBELUNGEN: /* ƒj?ƒxƒ‹ƒ“ƒO‚ÌŽw—Ö */
+ case BD_ROKISWEIL: /* ƒ?ƒL‚Ì‹©‚Ñ */
+ case BD_INTOABYSS: /* ?[•£‚Ì’†‚É */
+ case BD_SIEGFRIED: /* •sŽ€?g‚̃W?ƒNƒtƒŠ?ƒh */
+ case CG_MOONLIT: /* ŒŽ–¾‚è‚Ì?ò‚É—Ž‚¿‚é‰Ô‚Ñ‚ç */
+ {
+ if (battle_config.player_skill_partner_check)
+ {
+ if (skill_check_pc_partner(sd, skill_num, &skill_lv, 1, 0) < 1) //Note that skill_lv is automatically updated.
+ {
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+ }
+ break;
+ }
+ }
+
+ sd->skillid = skill_num;
+ sd->skilllv = skill_lv;
+ if (!skill_check_condition(sd,0)) return 0;
+
+ if(sd->bl.id != target_id){ // Don't check range for self skills, this is useless...
+ if(!battle_check_range(&sd->bl,bl,skill_get_range2(&sd->bl, skill_num,skill_lv)+1))
+ return 0;
+ }
+
+ if ((skill_num != MO_CHAINCOMBO &&
+ skill_num != MO_COMBOFINISH &&
+ skill_num != MO_EXTREMITYFIST &&
+ skill_num != CH_TIGERFIST &&
+ skill_num != CH_CHAINCRUSH &&
+ skill_num != TK_STORMKICK &&
+ skill_num != TK_DOWNKICK &&
+ skill_num != TK_TURNKICK &&
+ skill_num != TK_COUNTER) ||
+ (skill_num == MO_EXTREMITYFIST && sd->state.skill_flag))
+ pc_stopattack(sd);
+
+ casttime = skill_castfix(&sd->bl, skill_num, skill_lv, 0);
+ sd->state.skillcastcancel = skill_get_castcancel(skill_num);
+
+ switch (skill_num) { /* ‰½‚©“ÁŽê‚È?—?‚ª•K—v */
+ case ALL_RESURRECTION: /* ƒŠƒUƒŒƒNƒVƒ‡ƒ“ */
+ if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { /* “G‚ªƒAƒ“ƒfƒbƒh‚È‚ç */
+ forcecast = 1; /* ƒ^?ƒ“ƒAƒ“ƒfƒbƒg‚Æ“¯‚¶‰r?¥ŽžŠÔ */
+ casttime = skill_castfix(&sd->bl, PR_TURNUNDEAD, skill_lv, 0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE: /* Žw? */
+ casttime += casttime * ((skill_lv > sd->spiritball) ? sd->spiritball : skill_lv);
+ break;
+
+// -- moonsoul (altered to allow proper usage of extremity from new champion combos)
+//
+ case MO_EXTREMITYFIST: /*ˆ¢?C—…”e–PŒ?*/
+ if (sc_data && sc_data[SC_COMBO].timer != -1 &&
+ (sc_data[SC_COMBO].val1 == MO_COMBOFINISH ||
+ sc_data[SC_COMBO].val1 == CH_TIGERFIST ||
+ sc_data[SC_COMBO].val1 == CH_CHAINCRUSH))
+ casttime = 0;
+ forcecast = 1;
+ break;
+
+ case SA_MAGICROD:
+ case SA_SPELLBREAKER:
+ forcecast = 1;
+ break;
+
+ case KN_CHARGEATK:
+ casttime *= distance_bl(&sd->bl, bl);
+ break;
+
+ // parent-baby skills
+ case WE_BABY:
+ case WE_CALLPARENT:
+ {
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+
+ // set target as any one of the parent
+ if (f_sd) target_id = f_sd->bl.id;
+ else if (m_sd) target_id = m_sd->bl.id;
+ else return 0; // neither are found
+ }
+ break;
+ case HP_BASILICA: /* ƒoƒWƒŠƒJ */
+ {
+ // cancel Basilica if already in effect
+ if (sc_data && sc_data[SC_BASILICA].timer != -1 && sc_data[SC_BASILICA].val3 == BCT_SELF) {
+ status_change_end(&sd->bl,SC_BASILICA,-1);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ //ƒ?ƒ‚ƒ‰ƒCƒY?‘Ô‚È‚çƒLƒƒƒXƒgƒ^ƒCƒ€‚ª1/3
+ if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0) {
+ casttime = casttime/2;
+ if ((--sc_data[SC_MEMORIZE].val2) <= 0)
+ status_change_end(&sd->bl, SC_MEMORIZE, -1);
+ }
+
+ if (battle_config.pc_skill_log)
+ ShowInfo("PC %d skill use target_id=%d skill=%d lv=%d cast=%d\n",
+ sd->bl.id, target_id, skill_num, skill_lv, casttime);
+
+ if (casttime > 0 || forcecast) {
+ struct mob_data *md;
+ int mode;
+ if(sd->disguise) { // [Valaris]
+ clif_skillcasting(&sd->bl,sd->bl.id, target_id, 0,0, skill_num,0);
+ clif_skillcasting(&sd->bl,-sd->bl.id, target_id, 0,0, skill_num,casttime);
+ }
+ else
+ clif_skillcasting(&sd->bl,sd->bl.id, target_id, 0,0, skill_num,casttime);
+ /* ‰r?¥”½?ƒ‚ƒ“ƒXƒ^? */
+ if (bl->type == BL_MOB && (mode = status_get_mode(bl))&MD_CASTSENSOR && (md = (struct mob_data *)bl) &&
+ (!md->special_state.ai || skill_get_inf(skill_num) != INF_SUPPORT_SKILL) //Avoid having summons target master from supportive skills. [Skotlex]
+ ) {
+ switch (md->state.skillstate) {
+ case MSS_ANGRY:
+ case MSS_RUSH:
+ case MSS_FOLLOW:
+ if (!(mode&(MD_AGGRESSIVE|MD_ANGRY)))
+ break; //Only Aggressive mobs change target while chasing.
+ case MSS_IDLE:
+ case MSS_WALK:
+ md->target_id = sd->bl.id;
+ md->state.targettype = ATTACKABLE;
+ md->state.aggressive = (mode&MD_ANGRY)?1:0;
+ md->min_chase = md->db->range3;
+ }
+ }
+ }
+
+ if (!(battle_config.pc_cloak_check_type&2) &&
+ sc_data && sc_data[SC_CLOAKING].timer != -1 &&
+ sd->skillid != AS_CLOAKING)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ sd->skilltarget = target_id;
+ sd->skillx = 0;
+ sd->skilly = 0;
+ sd->canact_tick = tick + casttime + 100;
+ //Recycling forcecast to store the skill's level. [Skotlex]
+ sd->canmove_tick = tick + (casttime>0 && (forcecast = pc_checkskill(sd,SA_FREECAST)) > 0?0:casttime);
+
+ if (casttime > 0) {
+ sd->skilltimer = add_timer (tick + casttime, skill_castend_id, sd->bl.id, 0);
+ if (forcecast > 0)
+ status_quick_recalc_speed (sd, SA_FREECAST, forcecast, 1);
+ else
+ pc_stop_walking(sd,0);
+ } else {
+ sd->state.skillcastcancel = 0; /* ‰r?¥‚Ì–³‚¢‚à‚̂̓Lƒƒƒ“ƒZƒ‹‚³‚ê‚È‚¢ */
+ if (skill_num != SA_CASTCANCEL)
+ sd->skilltimer = -1;
+ skill_castend_id(sd->skilltimer,tick,sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹Žg—p?i?ê?ŠŽw’è?j
+ *------------------------------------------
+ */
+int skill_use_pos (struct map_session_data *sd, int skill_x, int skill_y, int skill_num, int skill_lv)
+{
+ struct block_list bl;
+ struct status_change *sc_data;
+ int casttime, skill = 0;
+ unsigned int tick = gettick();
+
+ nullpo_retr(0, sd);
+
+ if (pc_isdead(sd))
+ return 0;
+ if (skill_lv <= 0)
+ return 0;
+ if (sd->skilltimer != -1) //Normally not needed since clif.c checks for it, but at/char/script commands don't! [Skotlex]
+ return 0;
+ if (skillnotok(skill_num, sd)) // [MouseJstr]
+ return 0;
+ if (skill_num == WZ_ICEWALL && map[sd->bl.m].flag.noicewall && !map[sd->bl.m].flag.pvp && !map[sd->bl.m].flag.gvg) { // noicewall flag [Valaris]
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return 0;
+ }
+ if (map_getcell(sd->bl.m, skill_x, skill_y, CELL_CHKNOPASS))
+ { //prevent casting ground targeted spells on non-walkable areas. [Skotlex]
+
+ clif_skill_fail(sd,skill_num,0,0);
+ return 0;
+ }
+
+ sc_data = sd->sc_data;
+
+ if (!status_check_skilluse(&sd->bl, NULL, skill_num, 0))
+ return 0;
+
+ sd->skillid = skill_num;
+ sd->skilllv = skill_lv;
+ sd->skillx = skill_x;
+ sd->skilly = skill_y;
+ if (!skill_check_condition(sd,0)) return 0;
+
+ /* ŽË’ö‚Æ?áŠQ•¨ƒ`ƒFƒbƒN */
+ bl.type = BL_NUL;
+ bl.m = sd->bl.m;
+ bl.x = skill_x;
+ bl.y = skill_y;
+
+ if(!battle_check_range(&sd->bl,&bl,skill_get_range2(&sd->bl, skill_num,skill_lv)+1))
+ return 0;
+
+/* Previous code body, left here in case we have to rollback. [Skotlex]
+ {
+ int check_range_flag = 0;
+
+ range = skill_get_range(skill_num,skill_lv);
+ if(range < 0)
+ range = status_get_range(&sd->bl) - (range + 1);
+ // be lenient if the skill was cast before we have moved to the correct position [Celest]
+ if (sd->walktimer != -1)
+ range ++;
+ else check_range_flag = 1;
+ if(!battle_check_range(&sd->bl,&bl,range)) {
+ if (check_range_flag && pc_can_move(sd) && battle_check_range(&sd->bl,&bl,range + 1)) {
+ int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
+ int dir = map_calc_dir(&sd->bl,bl.x,bl.y);
+ pc_walktoxy (sd, sd->bl.x + mask[dir][0], sd->bl.y + mask[dir][1]);
+ } else
+ return 0;
+ }
+ }
+*/
+ pc_stopattack(sd);
+
+ casttime = skill_castfix(&sd->bl, skill_num, skill_lv, 0);
+ sd->state.skillcastcancel = skill_db[skill_num].castcancel;
+
+ if (battle_config.pc_skill_log)
+ ShowInfo("PC %d skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d\n",
+ sd->bl.id, skill_x, skill_y, skill_num, skill_lv, casttime);
+
+ //ƒ?ƒ‚ƒ‰ƒCƒY?‘Ô‚È‚çƒLƒƒƒXƒgƒ^ƒCƒ€‚ª1/3
+ if (sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0){
+ casttime = casttime/3;
+ if ((--sc_data[SC_MEMORIZE].val2)<=0)
+ status_change_end(&sd->bl, SC_MEMORIZE, -1);
+ }
+
+ if( casttime>0 ) { /* ‰r?¥‚ª•K—v */
+ if(sd->disguise) { // [Valaris]
+ clif_skillcasting(&sd->bl,sd->bl.id, 0, skill_x,skill_y, skill_num,0);
+ clif_skillcasting(&sd->bl,-sd->bl.id, 0, skill_x,skill_y, skill_num,casttime);
+ }
+ else
+ clif_skillcasting(&sd->bl,sd->bl.id, 0, skill_x,skill_y, skill_num,casttime);
+ }
+
+ if (!(battle_config.pc_cloak_check_type&2) &&
+ sc_data && sc_data[SC_CLOAKING].timer != -1)
+ status_change_end(&sd->bl,SC_CLOAKING,-1);
+
+ sd->skilltarget = 0;
+ sd->canact_tick = tick + casttime + 100;
+ sd->canmove_tick = tick + (casttime>0 && (skill = pc_checkskill(sd,SA_FREECAST))>0?0:casttime);
+
+ if (casttime > 0) {
+ sd->skilltimer = add_timer(tick + casttime, skill_castend_pos, sd->bl.id, 0);
+ if (skill > 0)
+ status_quick_recalc_speed (sd, SA_FREECAST, skill, 1);
+ else
+ pc_stop_walking(sd,0);
+ } else {
+ sd->state.skillcastcancel = 0; /* ‰r?¥‚Ì–³‚¢‚à‚̂̓Lƒƒƒ“ƒZƒ‹‚³‚ê‚È‚¢ */
+ sd->skilltimer = -1;
+ skill_castend_pos(sd->skilltimer,tick,sd->bl.id,0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹‰r?¥ƒLƒƒƒ“ƒZƒ‹
+ *------------------------------------------
+ */
+int skill_castcancel (struct block_list *bl, int type)
+{
+ int ret = 0;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ unsigned long tick = gettick();
+ nullpo_retr(0, sd);
+ sd->canact_tick = tick;
+ sd->canmove_tick = tick;
+ if (sd->skilltimer != -1) {
+ if ((ret = pc_checkskill(sd,SA_FREECAST)) > 0) {
+ status_quick_recalc_speed(sd, SA_FREECAST, ret, 0); //Updated to use calc_speed [Skotlex]
+ }
+ if (!type) {
+ if (skill_get_inf( sd->skillid ) & INF_GROUND_SKILL)
+ ret = delete_timer( sd->skilltimer, skill_castend_pos );
+ else
+ ret = delete_timer( sd->skilltimer, skill_castend_id );
+ if (ret < 0)
+ ShowError("delete timer error : skillid : %d\n", sd->skillid);
+ } else {
+ if (skill_get_inf( sd->skillid_old ) & INF_GROUND_SKILL)
+ ret = delete_timer( sd->skilltimer, skill_castend_pos );
+ else
+ ret = delete_timer( sd->skilltimer, skill_castend_id );
+ if (ret < 0)
+ ShowError("delete timer error : (old) skillid : %d\n", sd->skillid_old);
+ }
+ sd->skillid = sd->skilllv = -1;
+ sd->skilltimer = -1;
+ clif_skillcastcancel(bl);
+ }
+ return 0;
+ } else if (bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retr(0, md);
+ if (md->skilltimer != -1) {
+ if (skill_get_inf( md->skillid ) & INF_GROUND_SKILL)
+ ret = delete_timer( md->skilltimer, mobskill_castend_pos );
+ else
+ ret = delete_timer( md->skilltimer, mobskill_castend_id );
+ md->skillid = md->skilllv = -1;
+ md->skilltimer = -1;
+ clif_skillcastcancel(bl);
+ }
+ if (ret < 0)
+ ShowError("delete timer error : skillid : %d\n", md->skillid);
+ return 0;
+ } if (bl->type == BL_PET) {
+ struct pet_data *pd = (struct pet_data*)bl;
+ pd->state.casting_flag = 0;
+ clif_skillcastcancel(bl);
+ if (pd->timer != -1)
+ { //Free the data attached to casting. [Skotlex]
+ struct TimerData *td = get_timer(pd->timer);
+ if (td && td->data)
+ {
+ aFree((struct cast_end_delay*)td->data);
+ td->data = 0;
+ }
+ }
+ //The timer is not deleted as the pet's attack will be resumed.
+ return 0;
+ }
+
+ return 1;
+}
+/*=========================================
+ * ƒuƒ‰ƒ“ƒfƒBƒbƒVƒ…ƒXƒsƒA ?‰Šú”Í?Œˆ’è
+ *----------------------------------------
+ */
+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;
+ }
+
+}
+
+/*=========================================
+ * ƒuƒ‰ƒ“ƒfƒBƒbƒVƒ…ƒXƒsƒA •ûŒü”»’è ”Í??’£
+ *-----------------------------------------
+ */
+void skill_brandishspear_dir(struct square *tc,int dir,int are){
+
+ int c;
+
+ nullpo_retv(tc);
+
+ for(c=0;c<5;c++){
+ if(dir==0){
+ tc->val2[c]+=are;
+ }else if(dir==1){
+ tc->val1[c]-=are; tc->val2[c]+=are;
+ }else if(dir==2){
+ tc->val1[c]-=are;
+ }else if(dir==3){
+ tc->val1[c]-=are; tc->val2[c]-=are;
+ }else if(dir==4){
+ tc->val2[c]-=are;
+ }else if(dir==5){
+ tc->val1[c]+=are; tc->val2[c]-=are;
+ }else if(dir==6){
+ tc->val1[c]+=are;
+ }else if(dir==7){
+ tc->val1[c]+=are; tc->val2[c]+=are;
+ }
+ }
+}
+
+/*==========================================
+ * Weapon Repair [Celest/DracoRPG]
+ *------------------------------------------
+ */
+void skill_repairweapon(struct map_session_data *sd, int idx)
+{
+ int material;
+ int materials[4] = { 1002, 998, 999, 756 };
+ struct item *item;
+
+ nullpo_retv(sd);
+ nullpo_retv(sd->repair_target);
+
+ if(idx==0xFFFF) // No item selected ('Cancel' clicked)
+ return;
+
+ item = &sd->repair_target->status.inventory[idx];
+
+ if(sd!=sd->repair_target && !battle_check_range(&sd->bl,&sd->repair_target->bl,skill_get_range2(&sd->bl, sd->skillid,sd->skilllv))){
+ clif_item_repaireffect(sd,item->nameid,1);
+ return;
+ }
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(item->nameid > 0 && item->attribute == 1 ) {
+ if (itemdb_type(item->nameid)==4)
+ material = materials [itemdb_wlv(item->nameid)-1]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon
+ else
+ material = materials [2]; // Armors consume 1 Steel
+ if (pc_search_inventory(sd,material) < 0 ) {
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+ item->attribute=0;
+ clif_equiplist(sd->repair_target);
+ pc_delitem(sd,pc_search_inventory(sd,material),1,0);
+ clif_item_repaireffect(sd,item->nameid,0);
+ if(sd!=sd->repair_target)
+ clif_item_repaireffect(sd->repair_target,item->nameid,0);
+ sd->repair_target=NULL;
+ }
+ }
+}
+
+/*==========================================
+ * Item Appraisal
+ *------------------------------------------
+ */
+void skill_identify(struct map_session_data *sd,int idx)
+{
+ int flag=1;
+
+ nullpo_retv(sd);
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ flag=0;
+ sd->status.inventory[idx].identify=1;
+ }
+ }
+ clif_item_identified(sd,idx,flag);
+}
+
+/*==========================================
+ * Weapon Refine [Celest]
+ *------------------------------------------
+ */
+void skill_weaponrefine(struct map_session_data *sd,int idx)
+{
+ int i = 0, ep = 0, per;
+ int material[5] = { 0, 1010, 1011, 984, 984 };
+ struct item *item;
+
+ nullpo_retv(sd);
+
+ if (idx >= 0 && idx < MAX_INVENTORY) {
+ struct item_data *ditem = sd->inventory_data[idx];
+ item = &sd->status.inventory[idx];
+
+ if(item->nameid > 0 && ditem->type == 4) {
+ if (item->refine >= sd->skilllv ||
+ item->refine >= MAX_REFINE || // if it's no longer refineable
+ ditem->flag.no_refine || // if the item isn't refinable
+ (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 ) { //fixed by Lupus (item pos can be = 0!)
+ clif_skill_fail(sd,sd->skillid,0,0);
+ return;
+ }
+
+ per = percentrefinery [ditem->wlv][(int)item->refine];
+ per += (sd->status.job_level-50)/2; //Updated per the new kro descriptions. [Skotlex]
+
+ if (per > rand() % 100) {
+ item->refine++;
+ pc_delitem(sd, i, 1, 0);
+ if(item->equip) {
+ ep = item->equip;
+ pc_unequipitem(sd,idx,3);
+ }
+ clif_refine(sd->fd,sd,0,idx,item->refine);
+ clif_delitem(sd,idx,1);
+ clif_additem(sd,idx,1,0);
+ if (ep)
+ pc_equipitem(sd,idx,ep);
+ clif_misceffect(&sd->bl,3);
+ if(item->refine == MAX_REFINE && item->card[0] == 0x00ff && MakeDWord(item->card[2],item->card[3]) == sd->char_id){ // Fame point system [DracoRPG]
+ switch(ditem->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ } else {
+ pc_delitem(sd, i, 1, 0);
+ item->refine = 0;
+ if(item->equip)
+ pc_unequipitem(sd,idx,3);
+ clif_refine(sd->fd,sd,1,idx,item->refine);
+ pc_delitem(sd,idx,1,0);
+ clif_misceffect(&sd->bl,2);
+ clif_emotion(&sd->bl, 23);
+ }
+ }
+ }
+}
+
+/*==========================================
+ * ƒI?ƒgƒXƒyƒ‹
+ *------------------------------------------
+ */
+int skill_autospell(struct map_session_data *sd,int skillid)
+{
+ int skilllv;
+ int maxlv=1,lv;
+
+ nullpo_retr(0, sd);
+
+ skilllv = pc_checkskill(sd,SA_AUTOSPELL);
+ if(skilllv <= 0) return 0;
+
+ if(skillid==MG_NAPALMBEAT) maxlv=3;
+ else if(skillid==MG_COLDBOLT || skillid==MG_FIREBOLT || skillid==MG_LIGHTNINGBOLT){
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_SAGE)
+ maxlv =10; //Soul Linker bonus. [Skotlex]
+ else if(skilllv==2) maxlv=1;
+ else if(skilllv==3) maxlv=2;
+ else if(skilllv>=4) maxlv=3;
+ }
+ else if(skillid==MG_SOULSTRIKE){
+ if(skilllv==5) maxlv=1;
+ else if(skilllv==6) maxlv=2;
+ else if(skilllv>=7) maxlv=3;
+ }
+ else if(skillid==MG_FIREBALL){
+ if(skilllv==8) maxlv=1;
+ else if(skilllv>=9) maxlv=2;
+ }
+ else if(skillid==MG_FROSTDIVER) maxlv=1;
+ else return 0;
+
+ if(maxlv > (lv=pc_checkskill(sd,skillid)))
+ maxlv = lv;
+
+ status_change_start(&sd->bl,SC_AUTOSPELL,skilllv,skillid,maxlv,0, // val1:ƒXƒLƒ‹ID val2:Žg—p?Å‘åLv
+ skill_get_time(SA_AUTOSPELL,skilllv),0);// ‚É‚µ‚Ä‚Ý‚½‚¯‚Çbscript‚ª?‘‚«ˆÕ‚¢????H
+ return 0;
+}
+
+/*==========================================
+ * ƒMƒƒƒ“ƒOƒXƒ^?ƒpƒ‰ƒ_ƒCƒX”»’è?—?(foreachinarea)
+ *------------------------------------------
+ */
+
+static int skill_gangster_count(struct block_list *bl,va_list ap)
+{
+ int *c;
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ c=va_arg(ap,int *);
+
+ if(sd && c && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0)
+ (*c)++;
+ return 0;
+}
+
+static int skill_gangster_in(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0)
+ sd->state.gangsterparadise=1;
+ return 0;
+}
+
+static int skill_gangster_out(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && sd->state.gangsterparadise)
+ sd->state.gangsterparadise=0;
+ return 0;
+}
+
+int skill_gangsterparadise(struct map_session_data *sd ,int type)
+{
+ int range=1;
+ int c=0;
+ nullpo_retr(0, sd);
+
+ if(pc_checkskill(sd,RG_GANGSTER) <= 0)
+ return 0;
+
+ if(type==1) {/* ?À‚Á‚½Žž‚Ì?—? */
+ map_foreachinarea(skill_gangster_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c);
+ if(c > 1) {/*ƒMƒƒƒ“ƒOƒXƒ^??¬Œ÷‚µ‚½‚玩•ª‚É‚àƒMƒƒƒ“ƒOƒXƒ^???«•t?*/
+ map_foreachinarea(skill_gangster_in,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.gangsterparadise = 1;
+ }
+ return 0;
+ }
+ else if(type==0) {/* —§‚¿?オ‚Á‚½‚Æ‚«‚Ì?—? */
+ map_foreachinarea(skill_gangster_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c);
+ if(c < 2)
+ map_foreachinarea(skill_gangster_out,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.gangsterparadise = 0;
+ return 0;
+ }
+ return 0;
+}
+/*==========================================
+ * Taekwon TK_HPTIME and TK_SPTIME skills [Dralnu]
+ *------------------------------------------
+ */
+static int skill_rest_count(struct block_list *bl,va_list ap)
+{
+ int *c_r;
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ c_r=va_arg(ap,int *);
+
+ if(sd && c_r && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 ))
+ (*c_r)++;
+ return 0;
+}
+
+static int skill_rest_in(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )){
+ sd->state.rest=1;
+ status_calc_pc(sd,0);
+ }
+ return 0;
+}
+
+static int skill_rest_out(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ sd=(struct map_session_data*)bl;
+ if(sd && sd->state.rest != 0){
+ sd->state.rest=0;
+ }
+ return 0;
+}
+
+int skill_rest(struct map_session_data *sd ,int type)
+{
+ int range=1;
+ int c_r=0;
+ nullpo_retr(0, sd);
+
+ if(pc_checkskill(sd,TK_HPTIME) <= 0 && pc_checkskill(sd,TK_SPTIME) <= 0)
+ return 0;
+
+ if(type==1) { //When you sit down
+ map_foreachinarea(skill_rest_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c_r);
+ if(c_r > 1) {
+ map_foreachinarea(skill_rest_in,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.rest = 1;
+ status_calc_pc(sd,0);
+ }
+ return 0;
+ }
+ else if(type==0) { //When you stand up
+ map_foreachinarea(skill_rest_count,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC,&c_r);
+ if(c_r < 2)
+ map_foreachinarea(skill_rest_out,sd->bl.m,
+ sd->bl.x-range,sd->bl.y-range,
+ sd->bl.x+range,sd->bl.y+range,BL_PC);
+ sd->state.rest = 0;
+ status_calc_pc(sd,0);
+ return 0;
+ }
+ return 0;
+}
+/*==========================================
+ * Š¦‚¢ƒWƒ‡?ƒN?ƒXƒNƒŠ?ƒ€”»’è?—?(foreachinarea)
+ *------------------------------------------
+ */
+int skill_frostjoke_scream(struct block_list *bl,va_list ap)
+{
+ struct block_list *src;
+ int skillnum,skilllv;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src=va_arg(ap,struct block_list*));
+
+ skillnum=va_arg(ap,int);
+ skilllv=va_arg(ap,int);
+ if(skilllv <= 0) return 0;
+ tick=va_arg(ap,unsigned int);
+
+ if (src == bl || //Ž©•ª‚É‚Í?‚©‚È‚¢
+ bl->prev == NULL ||
+ status_isdead(bl))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd && sd->status.option & OPTION_INVISIBLE && pc_isGM(sd) > 0)
+ return 0;
+ }
+ //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex]
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0)
+ skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick);
+ else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rand()%100 < 10)
+ skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒoƒWƒŠƒJ‚̃Zƒ‹‚ð?Ý’è‚·‚é
+ *------------------------------------------
+ */
+void skill_unitsetmapcell(struct skill_unit *src, int skill_num, int flag)
+{
+ int i,x,y,range = skill_get_unit_range(skill_num);
+ int size = range*2+1;
+
+ for (i=0;i<size*size;i++) {
+ x = src->bl.x+(i%size-range);
+ y = src->bl.y+(i/size-range);
+ map_setcell(src->bl.m,x,y,flag);
+ }
+}
+
+/*==========================================
+ * Sets a map cell around the caster, according to the skill's range.
+ *------------------------------------------
+ */
+void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag)
+{
+ int i,x,y,range = skill_get_range2(src, skill_num, skill_lv);
+ int size = range*2+1;
+
+ for (i=0;i<size*size;i++) {
+ x = src->x+(i%size-range);
+ y = src->y+(i/size-range);
+ map_setcell(src->m,x,y,flag);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_attack_area(struct block_list *bl,va_list ap)
+{
+ struct block_list *src,*dsrc;
+ int atk_type,skillid,skilllv,flag,type;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ atk_type = va_arg(ap,int);
+ if((src=va_arg(ap,struct block_list*)) == NULL)
+ return 0;
+ if((dsrc=va_arg(ap,struct block_list*)) == NULL)
+ return 0;
+ skillid=va_arg(ap,int);
+ skilllv=va_arg(ap,int);
+ if(skillid > 0 && skilllv <= 0) return 0; // celest
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+ if(battle_check_target(dsrc,bl,type) > 0)
+ skill_attack(atk_type,src,dsrc,bl,skillid,skilllv,tick,flag);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int skill_clear_element_field(struct block_list *bl)
+{
+ struct mob_data *md=NULL;
+ struct map_session_data *sd=NULL;
+ int i,max,skillid;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type==BL_MOB) {
+ max = MAX_MOBSKILLUNITGROUP;
+ md = (struct mob_data *)bl;
+ } else if(bl->type==BL_PC) {
+ max = MAX_SKILLUNITGROUP;
+ sd = (struct map_session_data *)bl;
+ } else
+ return 0;
+
+ for (i=0;i<max;i++) {
+ if(sd){
+ skillid=sd->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ skill_delunitgroup(&sd->skillunit[i]);
+ }else if(md){
+ skillid=md->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ skill_delunitgroup(&md->skillunit[i]);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Returns the first element field found [Skotlex]
+ *------------------------------------------
+ */
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
+{
+ struct mob_data *md=NULL;
+ struct map_session_data *sd=NULL;
+ int i,max,skillid;
+
+ nullpo_retr(0, bl);
+
+ if (bl->type==BL_MOB) {
+ max = MAX_MOBSKILLUNITGROUP;
+ md = (struct mob_data *)bl;
+ } else if(bl->type==BL_PC) {
+ max = MAX_SKILLUNITGROUP;
+ sd = (struct map_session_data *)bl;
+ } else
+ return NULL;
+
+ for (i=0;i<max;i++) {
+ if(sd){
+ skillid=sd->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ return &sd->skillunit[i];
+ }else if(md){
+ skillid=md->skillunit[i].skill_id;
+ if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR)
+ return &md->skillunit[i];
+ }
+ }
+ return NULL;
+}
+
+// for graffiti cleaner [Valaris]
+int skill_graffitiremover(struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit=NULL;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL)
+ return 0;
+
+ if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI))
+ skill_delunit(unit);
+
+ return 0;
+}
+
+int skill_greed(struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ struct map_session_data *sd=NULL;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src = va_arg(ap,struct block_list *));
+
+ if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl))
+ pc_takeitem(sd, fitem);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒ‰ƒ“ƒhƒvƒ?ƒeƒNƒ^?ƒ`ƒFƒbƒN(foreachinarea)
+ *------------------------------------------
+ */
+int skill_landprotector(struct block_list *bl, va_list ap )
+{
+ int skillid;
+ int *alive;
+ struct skill_unit *unit;
+ struct block_list *src;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ skillid = va_arg(ap,int);
+ alive = va_arg(ap,int *);
+ src = va_arg(ap,struct block_list *);
+
+ if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL)
+ return 0;
+
+ if (alive && skillid == SA_LANDPROTECTOR && unit->group->skill_id == SA_LANDPROTECTOR
+ && battle_check_target(bl, src, BCT_ENEMY) > 0)
+ { //Check for offensive Land Protector to delete both. [Skotlex]
+ (*alive) = 0;
+ skill_delunit(unit);
+ return 0;
+ }
+ if (skillid == SA_LANDPROTECTOR ||
+ skillid == HW_GANBANTEIN)
+ skill_delunit(unit);
+ else if (alive && unit->group->skill_id == SA_LANDPROTECTOR)
+ (*alive) = 0;
+ else if (alive && skillid == HP_BASILICA && unit->group->skill_id == HP_BASILICA)
+ (*alive) = 0; //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
+ return 0;
+}
+
+/*==========================================
+ * variation of skill_landprotector
+ *------------------------------------------
+ */
+int skill_ganbatein(struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL)
+ return 0;
+
+ if (unit->group->skill_id == SA_LANDPROTECTOR)
+ skill_delunit(unit);
+ else skill_delunitgroup(unit->group);
+
+ return 0;
+}
+
+/*==========================================
+ * Žw’è”Í??‚Åsrc‚É?‚µ‚Ä—L?‚ȃ^?ƒQƒbƒg‚Ì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;
+}
+/*==========================================
+ * ƒgƒ‰ƒbƒv”Í??—?(foreachinarea)
+ *------------------------------------------
+ */
+int skill_trap_splash (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ int tick;
+ int splash_count;
+ struct skill_unit *unit;
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ int i;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, src = va_arg(ap,struct block_list *));
+ nullpo_retr(0, unit = (struct skill_unit *)src);
+ nullpo_retr(0, sg = unit->group);
+// nullpo_retr(0, ss = map_id2bl(sg->src_id));
+ if ((ss = map_id2bl(sg->src_id)) == NULL)
+ { //Temporal debug until this case is solved. [Skotlex]
+ ShowDebug("skill_trap_splash: Trap's source (id: %d) not found!\n", sg->src_id);
+ return 0;
+ }
+
+ tick = va_arg(ap,int);
+ splash_count = va_arg(ap,int);
+
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0){
+ switch(sg->unit_id){
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick);
+ break;
+ case UNT_BLASTMINE:
+ case UNT_CLAYMORETRAP:
+ for(i=0;i<splash_count;i++){
+ skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0);
+ }
+ break;
+ case UNT_FREEZINGTRAP:
+ skill_attack(BF_WEAPON, ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒe?ƒ^ƒXˆÙ?í?I—¹
+ *------------------------------------------
+ */
+int skill_enchant_elemental_end (struct block_list *bl, int type)
+{
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sc_data = status_get_sc_data(bl));
+
+ if (type != SC_ENCPOISON && sc_data[SC_ENCPOISON].timer != -1) /* ƒGƒ“ƒ`ƒƒƒ“ƒgƒ|ƒCƒYƒ“‰ð?œ */
+ status_change_end(bl, SC_ENCPOISON, -1);
+ if (type != SC_ASPERSIO && sc_data[SC_ASPERSIO].timer != -1) /* ƒAƒXƒyƒ‹ƒVƒI‰ð?œ */
+ status_change_end(bl, SC_ASPERSIO, -1);
+ if (type != SC_FIREWEAPON && sc_data[SC_FIREWEAPON].timer != -1) /* ƒtƒŒƒCƒ€ƒ‰ƒ“ƒ`ƒƒ‰ð?œ */
+ status_change_end(bl, SC_FIREWEAPON, -1);
+ if (type != SC_WATERWEAPON && sc_data[SC_WATERWEAPON].timer != -1) /* ƒtƒ?ƒXƒgƒEƒFƒ|ƒ“‰ð?œ */
+ status_change_end(bl, SC_WATERWEAPON, -1);
+ if (type != SC_WINDWEAPON && sc_data[SC_WINDWEAPON].timer != -1) /* ƒ‰ƒCƒgƒjƒ“ƒOƒ??ƒ_?‰ð?œ */
+ status_change_end(bl, SC_WINDWEAPON, -1);
+ if (type != SC_EARTHWEAPON && sc_data[SC_EARTHWEAPON].timer != -1) /* ƒTƒCƒXƒ~ƒbƒNƒEƒFƒ|ƒ“‰ð?œ */
+ status_change_end(bl, SC_EARTHWEAPON, -1);
+ if (type != SC_SHADOWWEAPON && sc_data[SC_SHADOWWEAPON].timer != -1)
+ status_change_end(bl, SC_SHADOWWEAPON, -1);
+ if (type != SC_GHOSTWEAPON && sc_data[SC_GHOSTWEAPON].timer != -1)
+ status_change_end(bl, SC_GHOSTWEAPON, -1);
+ return 0;
+}
+
+/* ƒNƒ??ƒLƒ“ƒO??¸?iŽü‚è‚Ɉړ®•s‰Â”\’n?‚ª‚ ‚é‚©?j */
+int skill_check_cloaking(struct block_list *bl)
+{
+ struct map_session_data *sd = NULL;
+ static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; //optimized by Lupus
+ static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
+ int end = 1,i;
+
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC) {
+ nullpo_retr(1, sd = (struct map_session_data *)bl);
+ }
+
+ if ((bl->type == BL_PC && battle_config.pc_cloak_check_type&1) ||
+ (bl->type != BL_PC && battle_config.monster_cloak_check_type&1))
+ { //Check for walls.
+ for (i = 0; i < 8; i++)
+ if (map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS))
+ {
+ end = 0;
+ break;
+ }
+ } else
+ end = 0; //No wall check.
+
+ if(end){
+ if ((sd && pc_checkskill(sd,AS_CLOAKING)<3) || bl->type == BL_MOB) {
+ status_change_end(bl, SC_CLOAKING, -1);
+ }
+ else if (sd && sd->sc_data[SC_CLOAKING].val3 != 130) {
+ status_quick_recalc_speed (sd, AS_CLOAKING, 130, 1);
+ }
+ }
+ else {
+ if (sd && sd->sc_data[SC_CLOAKING].val3 != 103) {
+ status_quick_recalc_speed (sd, AS_CLOAKING, 103, 1);
+ }
+ }
+
+ return end;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg
+ *----------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * ‰‰‘t/ƒ_ƒ“ƒX‚ð‚â‚ß‚é
+ * flag 1‚Å?‡‘t’†‚È‚ç‘Š•û‚Ƀ†ƒjƒbƒg‚ð”C‚¹‚é
+ *
+ *------------------------------------------
+ */
+void skill_stop_dancing(struct block_list *src)
+{
+ struct status_change* sc_data;
+ struct skill_unit_group* group;
+ struct map_session_data* dsd = NULL;
+
+ nullpo_retv(src);
+ nullpo_retv(sc_data = status_get_sc_data(src));
+
+ if(sc_data[SC_DANCING].timer == -1)
+ return;
+
+ group = (struct skill_unit_group *)sc_data[SC_DANCING].val2;
+ sc_data[SC_DANCING].val2 = 0;
+
+ if (sc_data[SC_DANCING].val4)
+ {
+ if (sc_data[SC_DANCING].val4 != BCT_SELF)
+ dsd = map_id2sd(sc_data[SC_DANCING].val4);
+ sc_data[SC_DANCING].val4 = 0;
+ }
+
+ if (group)
+ skill_delunitgroup(group);
+
+ if (dsd)
+ {
+ dsd->sc_data[SC_DANCING].val4 = dsd->sc_data[SC_DANCING].val2 = 0;
+ status_change_end(&dsd->bl, SC_DANCING, -1);
+ }
+ status_change_end(src, SC_DANCING, -1);
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg?‰Šú‰»
+ *------------------------------------------
+ */
+struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y)
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(NULL, group);
+ nullpo_retr(NULL, unit=&group->unit[idx]);
+
+ if(!unit->alive)
+ group->alive_count++;
+
+ unit->bl.id=map_addobject(&unit->bl);
+ unit->bl.type=BL_SKILL;
+ unit->bl.m=group->map;
+ unit->bl.x=x;
+ unit->bl.y=y;
+ unit->group=group;
+ unit->val1=unit->val2=0;
+ unit->alive=1;
+
+ map_addblock(&unit->bl);
+ clif_skill_setunit(unit);
+
+ switch (group->skill_id) {
+ case AL_PNEUMA:
+ skill_unitsetmapcell(unit,AL_PNEUMA,CELL_SETPNEUMA);
+ break;
+ case MG_SAFETYWALL:
+ skill_unitsetmapcell(unit,MG_SAFETYWALL,CELL_SETSAFETYWALL);
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,CELL_SETLANDPROTECTOR);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,CELL_SETBASILICA);
+ break;
+ case WZ_ICEWALL:
+ skill_unitsetmapcell(unit,WZ_ICEWALL,CELL_SETICEWALL);
+ break;
+ }
+ return unit;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg?í?œ
+ *------------------------------------------
+ */
+int skill_delunit(struct skill_unit *unit)
+{
+ struct skill_unit_group *group;
+
+ nullpo_retr(0, unit);
+ if(!unit->alive)
+ return 0;
+ nullpo_retr(0, group=unit->group);
+
+ /* onlimitƒCƒxƒ“ƒgŒÄ‚Ñ?o‚µ */
+ skill_unit_onlimit( unit,gettick() );
+
+ /* onoutƒCƒxƒ“ƒgŒÄ‚Ñ?o‚µ */
+ if (!unit->range) {
+ map_foreachincell(skill_unit_effect,unit->bl.m,
+ unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4);
+ }
+
+ switch (group->skill_id) {
+ case AL_PNEUMA:
+ skill_unitsetmapcell(unit,AL_PNEUMA,CELL_CLRPNEUMA);
+ break;
+ case MG_SAFETYWALL:
+ skill_unitsetmapcell(unit,MG_SAFETYWALL,CELL_CLRSAFETYWALL);
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,CELL_CLRLANDPROTECTOR);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,CELL_CLRBASILICA);
+ break;
+ case WZ_ICEWALL:
+ skill_unitsetmapcell(unit,WZ_ICEWALL,CELL_CLRICEWALL);
+ break;
+ }
+
+ clif_skill_delunit(unit);
+
+ unit->group=NULL;
+ unit->alive=0;
+ map_delobjectnofree(unit->bl.id);
+ if(--group->alive_count==0)
+ skill_delunitgroup(group);
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹?ƒv?‰Šú‰»
+ *------------------------------------------
+ */
+static int skill_unit_group_newid = MAX_SKILL_DB;
+struct skill_unit_group *skill_initunitgroup(struct block_list *src,
+ int count,int skillid,int skilllv,int unit_id)
+{
+ int i;
+ struct skill_unit_group *group=NULL, *list=NULL;
+ int maxsug=0;
+
+ if(skilllv <= 0) return 0;
+
+ nullpo_retr(NULL, src);
+
+ if(src->type==BL_PC){
+ list=((struct map_session_data *)src)->skillunit;
+ maxsug=MAX_SKILLUNITGROUP;
+ }else if(src->type==BL_MOB){
+ list=((struct mob_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ }else if(src->type==BL_PET){
+ list=((struct pet_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ }
+ if(list){
+ for(i=0;i<maxsug;i++) /* ‹ó‚¢‚Ä‚¢‚é‚à‚Ì??õ */
+ if(list[i].group_id==0){
+ group=&list[i];
+ break;
+ }
+
+ if(group==NULL){ /* ‹ó‚¢‚Ä‚È‚¢‚̂Ō¢‚à‚Ì??õ */
+ int j=0;
+ unsigned maxdiff=0,x,tick=gettick();
+ for(i=0;i<maxsug;i++)
+ if((x=DIFF_TICK(tick,list[i].tick))>maxdiff){
+ maxdiff=x;
+ j=i;
+ }
+ skill_delunitgroup(&list[j]);
+ group=&list[j];
+ }
+ }
+
+ if(group==NULL){
+ ShowFatalError("skill_initunitgroup: error unit group !\n");
+ exit(1);
+ }
+
+ group->src_id=src->id;
+ group->party_id=status_get_party_id(src);
+ group->guild_id=status_get_guild_id(src);
+ group->group_id=skill_unit_group_newid++;
+ if(skill_unit_group_newid<=0)
+ skill_unit_group_newid = MAX_SKILL_DB;
+ group->unit=(struct skill_unit *)aCalloc(count,sizeof(struct skill_unit));
+ group->unit_count=count;
+ group->val1=group->val2=0;
+ group->skill_id=skillid;
+ group->skill_lv=skilllv;
+ group->unit_id=unit_id;
+ group->map=src->m;
+ group->limit=10000;
+ group->interval=1000;
+ group->tick=gettick();
+ if (skillid == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex]
+ group->tick += 1500;
+ group->valstr=NULL;
+
+ i = skill_get_unit_flag(skillid); //Reuse for faster access from here on. [Skotlex]
+ if (i&UF_DANCE) {
+ struct map_session_data *sd = NULL;
+ if(src->type==BL_PC && (sd=(struct map_session_data *)src) ){
+ sd->skillid_dance=skillid;
+ sd->skilllv_dance=skilllv;
+ }
+ status_change_start(src,SC_DANCING,skillid,(int)group,0,(i&UF_ENSEMBLE?BCT_SELF:0),skill_get_time(skillid,skilllv)+1000,0);
+ //?‡‘tƒXƒLƒ‹‚Í‘Š•û‚ðƒ_ƒ“ƒX?ó‘Ô‚É‚·‚é
+ if (sd && i&UF_ENSEMBLE &&
+ battle_config.player_skill_partner_check) {
+ skill_check_pc_partner(sd, skillid, &skilllv, 1, 1);
+ }
+ }
+ return group;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹?ƒv?í?œ
+ *------------------------------------------
+ */
+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);
+ //ƒ_ƒ“ƒXƒXƒLƒ‹‚̓_ƒ“ƒX?ó‘Ô‚ð‰ð?œ‚·‚é
+ if(src) {
+ if (skill_get_unit_flag(group->skill_id)&UF_DANCE)
+ {
+ struct status_change* sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[SC_DANCING].timer != -1)
+ {
+ sc_data[SC_DANCING].val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex]
+ status_change_end(src,SC_DANCING,-1);
+ }
+ }
+
+ if (group->unit_id == UNT_GOSPEL) { //Clear Gospel [Skotlex]
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sc_data && sc_data[SC_GOSPEL].timer != -1) {
+ sc_data[SC_GOSPEL].val3 = 0; //Remove reference to this group. [Skotlex]
+ status_change_end(src,SC_GOSPEL,-1);
+ }
+ }
+ }
+
+ group->alive_count=0;
+ if(group->unit!=NULL){
+ for(i=0;i<group->unit_count;i++)
+ if(group->unit[i].alive)
+ skill_delunit(&group->unit[i]);
+ }
+ if(group->valstr!=NULL){
+ //Supposedly Free remembers the size of the original Calloc/Malloc, so this should be safe [Skotlex]
+ aFree(group->valstr);
+ group->valstr=NULL;
+ }
+
+ map_freeblock((struct block_list*)group->unit); /* aFree()‚Ì‘Ö‚í‚è */
+ group->unit=NULL;
+ group->src_id=0;
+ group->group_id=0;
+ group->unit_count=0;
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹?ƒv‘S?í?œ
+ *------------------------------------------
+ */
+int skill_clear_unitgroup(struct block_list *src)
+{
+ struct skill_unit_group *group=NULL;
+ int maxsug=0;
+
+ nullpo_retr(0, src);
+
+ if(src->type==BL_PC){
+ group=((struct map_session_data *)src)->skillunit;
+ maxsug=MAX_SKILLUNITGROUP;
+ } else if(src->type==BL_MOB){
+ group=((struct mob_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ } else if(src->type==BL_PET){ // [Valaris]
+ group=((struct pet_data *)src)->skillunit;
+ maxsug=MAX_MOBSKILLUNITGROUP;
+ } else
+ return 0;
+ if(group){
+ int i;
+ for(i=0;i<maxsug;i++)
+ if(group[i].group_id>0 && group[i].src_id == src->id)
+ skill_delunitgroup(&group[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒOƒ‹?ƒv‚Ì”í‰e‹¿tick??õ
+ *------------------------------------------
+ */
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(
+ struct block_list *bl,struct skill_unit_group *group,int tick)
+{
+ int i,j=-1,k,s,id;
+ struct skill_unit_group_tickset *set;
+
+ nullpo_retr(0, bl);
+ if (group->interval==-1)
+ return NULL;
+
+ if (bl->type == BL_PC)
+ set = ((struct map_session_data *)bl)->skillunittick;
+ else if (bl->type == BL_MOB)
+ set = ((struct mob_data *)bl)->skillunittick;
+ else
+ return 0;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP)
+ id = s = group->skill_id;
+ else
+ id = s = group->group_id;
+
+ for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) {
+ k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
+ if (set[k].id == id)
+ return &set[k];
+ else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0))
+ j=k;
+ }
+
+ if (j == -1) {
+ if(battle_config.error_log) {
+ ShowWarning ("skill_unitgrouptickset_search: tickset is full\n");
+ }
+ j = id % MAX_SKILLUNITGROUPTICKSET;
+ }
+
+ set[j].id = id;
+ set[j].tick = tick;
+ return &set[j];
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒ^ƒCƒ}??“®?—?—p(foreachinarea)
+ *------------------------------------------
+ */
+int skill_unit_timer_sub_onplace( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ unit = va_arg(ap,struct skill_unit *);
+ tick = va_arg(ap,unsigned int);
+
+ if (!unit->alive || bl->prev==NULL)
+ return 0;
+
+ nullpo_retr(0, group=unit->group);
+
+ if (map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR))
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if (battle_check_target(&unit->bl,bl,group->target_flag)<=0)
+ return 0;
+
+ skill_unit_onplace_timer(unit,bl,tick);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒ^ƒCƒ}??—?—p(foreachobject)
+ *------------------------------------------
+ */
+int skill_unit_timer_sub( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit;
+ struct skill_unit_group *group;
+ int range;
+ unsigned int tick;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, unit=(struct skill_unit *)bl);
+ tick=va_arg(ap,unsigned int);
+
+ if(!unit->alive)
+ return 0;
+ group=unit->group;
+
+ nullpo_retr(0, group);
+ range = unit->range;
+
+ /* onplace_timerƒCƒxƒ“ƒgŒÄ‚Ñ?o‚µ */
+ if (range>=0 && group->interval!=-1) {
+ map_foreachinarea(skill_unit_timer_sub_onplace, bl->m,
+ bl->x-range,bl->y-range,bl->x+range,bl->y+range,group->bl_flag,bl,tick);
+ if (!unit->alive)
+ return 0;
+ // ƒ}ƒOƒkƒX‚Í”­“®‚µ‚½ƒ†ƒjƒbƒg‚Í?í?œ‚·‚é
+ if (group->skill_id==PR_MAGNUS && unit->val2) {
+ skill_delunit(unit);
+ return 0;
+ }
+ }
+ /* ŽžŠÔ?Ø‚ê?í?œ */
+ if((DIFF_TICK(tick,group->tick)>=group->limit || DIFF_TICK(tick,group->tick)>=unit->limit)){
+ switch(group->unit_id){
+ case UNT_BLASTMINE:
+ group->unit_id = UNT_USED_TRAPS;
+ clif_changelook(bl,LOOK_BASE,group->unit_id);
+ group->limit=DIFF_TICK(tick+1500,group->tick);
+ unit->limit=DIFF_TICK(tick+1500,group->tick);
+ break;
+ case UNT_SKIDTRAP:
+ case UNT_ANKLESNARE:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ {
+ struct block_list *src=map_id2bl(group->src_id);
+ if(group->unit_id == UNT_ANKLESNARE && group->val2);
+ else{
+ if(src && src->type==BL_PC && group->val3 != BD_INTOABYSS)
+ { //Avoid generating trap items when it did not cost to create them. [Skotlex]
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=1065;
+ item_tmp.identify=1;
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,NULL,NULL,NULL,0); // ?•ÔŠÒ
+ }
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ {
+ struct block_list *src=map_id2bl(group->src_id);
+ if (src)
+ group->tick = tick;
+ }
+ break;
+
+ default:
+ skill_delunit(unit);
+ }
+ }
+
+ if(group->unit_id == UNT_ICEWALL) {
+ unit->val1 -= 5;
+ if(unit->val1 <= 0 && unit->limit + group->tick > tick + 700)
+ unit->limit = DIFF_TICK(tick+700,group->tick);
+ }
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgƒ^ƒCƒ}??—?
+ *------------------------------------------
+ */
+int skill_unit_timer( int tid,unsigned int tick,int id,int data)
+{
+ map_freeblock_lock();
+
+ map_foreachobject( skill_unit_timer_sub, BL_SKILL, tick );
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgˆÚ“®Žž?—?—p(foreachinarea)
+ *------------------------------------------
+ */
+int skill_unit_move_sub( struct block_list *bl, va_list ap )
+{
+ struct skill_unit *unit = (struct skill_unit *)bl;
+ struct block_list *target;
+ unsigned int tick,flag,result;
+ int skill_id;
+
+ target=va_arg(ap,struct block_list*);
+ tick = va_arg(ap,unsigned int);
+ flag = va_arg(ap,int);
+
+ nullpo_retr(0, unit->group);
+
+ if (!(unit->group->bl_flag&target->type))
+ return 0; //we don't target this type of bl
+
+ skill_id = unit->group->skill_id; //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+
+ if (unit->group->interval!=-1 &&
+ !(skill_get_unit_flag(skill_id)&UF_DUALMODE)) //Skills in dual mode have to trigger both. [Skotlex]
+ return 0;
+
+ if (!unit->alive || target->prev==NULL)
+ return 0;
+
+ if (flag&1)
+ {
+ result = skill_unit_onplace(unit,target,tick);
+ if (flag&2 && result)
+ { //Clear skill ids we have stored in onout.
+ int i;
+ for(i=0; i<8 && skill_unit_temp[i]!=result; i++);
+ if (i<8)
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ result = skill_unit_onout(unit,target,tick);
+ if (flag&2 && skill_unit_index < 7 && result) //Store this unit id.
+ skill_unit_temp[skill_unit_index++] = result;
+ }
+ if (flag&4)
+ skill_unit_onleft(skill_id,target,tick);
+ return 1;
+}
+
+/*==========================================
+ * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft)
+ * Flag values:
+ * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout)
+ * flag&2: this function is being invoked twice as a bl moves, store in memory the affected
+ * units to figure out when they have left a group.
+ * flag&4: Force a onleft event (triggered when the bl is killed, for example)
+ *------------------------------------------
+ */
+int skill_unit_move(struct block_list *bl,unsigned int tick,int flag)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->prev==NULL )
+ return 0;
+
+ if (flag&2 && !(flag&1))
+ { //Onout, clear data
+ memset (&skill_unit_temp,0,sizeof(skill_unit_temp));
+ skill_unit_index=0;
+ }
+
+ map_foreachincell(skill_unit_move_sub,
+ bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag);
+
+ if (flag&2 && flag&1)
+ { //Onplace, check any skill units you have left.
+ int i;
+ for (i=0; i< 8 && skill_unit_temp[i]>0; i++)
+ skill_unit_onleft(skill_unit_temp[i], bl, tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒgŽ©?‚̈ړ®Žž?—?
+ * ˆø?‚̓Oƒ‹?ƒv‚ƈړ®—Ê
+ *------------------------------------------
+ */
+int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy)
+{
+ int i,j;
+ unsigned int tick = gettick();
+ int *m_flag;
+ struct skill_unit *unit1;
+ struct skill_unit *unit2;
+
+ nullpo_retr(0, group);
+ if (group->unit_count<=0)
+ return 0;
+ if (group->unit==NULL)
+ return 0;
+
+ i = skill_get_unit_flag(group->skill_id); //Check the flag...
+ if (!(
+ (i&UF_DANCE && !(i&UF_ENSEMBLE)) || //Only non ensemble dances and traps can be moved.
+ skill_get_inf2(group->skill_id)&INF2_TRAP
+ ))
+ return 0;
+
+ m_flag = (int *) aMalloc(sizeof(int)*group->unit_count);
+ memset(m_flag,0,sizeof(int)*group->unit_count);// ˆÚ“®ƒtƒ‰ƒO
+ // m_flag
+ // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed)
+ // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed)
+ // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed)
+ // 3: Both 1+2.
+ for(i=0;i<group->unit_count;i++){
+ unit1=&group->unit[i];
+ if (!unit1->alive || unit1->bl.m!=m)
+ continue;
+ for(j=0;j<group->unit_count;j++){
+ unit2=&group->unit[j];
+ if (!unit2->alive)
+ continue;
+ if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){
+ m_flag[i] |= 0x1;
+ }
+ if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){
+ m_flag[i] |= 0x2;
+ }
+ }
+ }
+ j = 0;
+ for (i=0;i<group->unit_count;i++) {
+ unit1=&group->unit[i];
+ if (!unit1->alive)
+ continue;
+ if (!(m_flag[i]&0x2)) {
+ map_foreachincell(skill_unit_effect,unit1->bl.m,
+ unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4);
+ }
+ //Move Cell using "smart" criteria (avoid useless moving around)
+ switch(m_flag[i])
+ {
+ case 0:
+ //Cell moves independently, safely move it.
+ map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick);
+ clif_skill_setunit(unit1);
+ break;
+ case 1:
+ //Cell moves unto another cell, look for a replacement cell that won't collide
+ //and has no cell moving into it (flag == 2)
+ for(;j<group->unit_count;j++)
+ {
+ if(m_flag[j]!=2 || !group->unit[j].alive)
+ continue;
+ //Move to where this cell would had moved.
+ unit2 = &group->unit[j];
+ map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick);
+ clif_skill_setunit(unit1);
+ j++; //Skip this cell as we have used it.
+ break;
+ }
+ break;
+ case 2:
+ case 3:
+ break; //Don't move the cell as a cell will end on this tile anyway.
+ }
+ if (!(m_flag[i]&2)) { //We only moved the cell in 0-1
+ map_foreachincell(skill_unit_effect,unit1->bl.m,
+ unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1);
+ }
+ }
+ aFree(m_flag);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * ƒAƒCƒeƒ€?‡?¬
+ *----------------------------------------------------------------------------
+ */
+
+/*==========================================
+ * ƒAƒCƒeƒ€?‡?¬‰Â”\”»’è
+ *------------------------------------------
+ */
+int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty)
+{
+ int i,j;
+
+ nullpo_retr(0, sd);
+
+ if(nameid<=0)
+ return 0;
+
+ for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if(skill_produce_db[i].nameid == nameid )
+ break;
+ }
+ if( i >= MAX_SKILL_PRODUCE_DB ) /* ƒf?ƒ^ƒx?ƒX‚É‚È‚¢ */
+ return 0;
+
+ if(trigger>=0){
+ if(trigger>20) { // Non-weapon, non-food item (itemlv must match)
+ if(skill_produce_db[i].itemlv!=trigger)
+ return 0;
+ } else if(trigger>10) { // Food (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>trigger)
+ return 0;
+ } else { // Weapon (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv>trigger)
+ return 0;
+ }
+ }
+ if( (j=skill_produce_db[i].req_skill)>0 && pc_checkskill(sd,j)<=0 )
+ return 0; /* ƒXƒLƒ‹‚ª‘«‚è‚È‚¢ */
+
+ for(j=0;j<MAX_PRODUCE_RESOURCE;j++){
+ int id,x,y;
+ if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) /* ‚±‚êˆÈ?ã‚Í?Þ—¿—v‚ç‚È‚¢ */
+ continue;
+ if(skill_produce_db[i].mat_amount[j] <= 0) {
+ if(pc_search_inventory(sd,id) < 0)
+ return 0;
+ }
+ else {
+ for(y=0,x=0;y<MAX_INVENTORY;y++)
+ if( sd->status.inventory[y].nameid == id )
+ x+=sd->status.inventory[y].amount;
+ if(x<qty*skill_produce_db[i].mat_amount[j]) /* ƒAƒCƒeƒ€‚ª‘«‚è‚È‚¢ */
+ return 0;
+ }
+ }
+ return i+1;
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€?‡?¬‰Â”\”»’è
+ *------------------------------------------
+ */
+int skill_produce_mix( struct map_session_data *sd, int skill_id,
+ int nameid, int slot1, int slot2, int slot3, int qty)
+{
+ int slot[3];
+ int i,sc,ele,idx,equip,wlv,make_per,flag;
+
+ nullpo_retr(0, sd);
+
+ if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) ) /* ?Œ?•s‘« */
+ return 0;
+ idx--;
+
+ if (qty < 1)
+ qty = 1;
+
+ if (!skill_id) //A skill can be specified for some override cases.
+ skill_id = skill_produce_db[idx].req_skill;
+
+ slot[0]=slot1;
+ slot[1]=slot2;
+ slot[2]=slot3;
+
+ /* –„‚ß?‚Ý?—? */
+ for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these!
+ int j;
+ if( slot[i]<=0 )
+ continue;
+ j = pc_search_inventory(sd,slot[i]);
+ if(j < 0) /* •s?³ƒpƒPƒbƒg(ƒAƒCƒeƒ€‘¶?Ý)ƒ`ƒFƒbƒN */
+ continue;
+ if(slot[i]==1000){ /* Star Crumb */
+ pc_delitem(sd,j,1,1);
+ sc++;
+ }
+ if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */
+ static const int ele_table[4]={3,1,4,2};
+ pc_delitem(sd,j,1,1);
+ ele=ele_table[slot[i]-994];
+ }
+ }
+
+ /* ?Þ—¿?Á”ï */
+ for(i=0;i<MAX_PRODUCE_RESOURCE;i++){
+ int j,id,x;
+ if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
+ continue;
+ x=qty*skill_produce_db[idx].mat_amount[i]; /* •K—v‚ÈŒÂ? */
+ do{ /* ‚Q‚ˆÈ?ã‚̃Cƒ“ƒfƒbƒNƒX‚É‚Ü‚½‚ª‚Á‚Ä‚¢‚é‚©‚à‚µ‚ê‚È‚¢ */
+ int y=0;
+ j = pc_search_inventory(sd,id);
+
+ if(j >= 0){
+ y = sd->status.inventory[j].amount;
+ if(y>x)y=x; /* ‘«‚è‚Ä‚¢‚é */
+ pc_delitem(sd,j,y,0);
+ }else {
+ if(battle_config.error_log)
+ ShowError("skill_produce_mix: material item error\n");
+ }
+
+ x-=y; /* ‚Ü‚¾‘«‚è‚È‚¢ŒÂ?‚ðŒvŽZ */
+ }while( j>=0 && x>0 ); /* ?Þ—¿‚ð?Á”ï‚·‚é‚©?AƒGƒ‰?‚É‚È‚é‚Ü‚ÅŒJ‚è•Ô‚· */
+ }
+
+ if((equip=itemdb_isequip(nameid)))
+ wlv = itemdb_wlv(nameid);
+ if(!equip) {
+ switch(skill_id){
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ { // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
+ int skill = pc_checkskill(sd,skill_id);
+ make_per = sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; //Base chance
+ switch(nameid){
+ case 998: // Iron
+ make_per += 4000+skill*500; // Temper Iron bonus: +26/+32/+38/+44/+50
+ break;
+ case 999: // Steel
+ make_per += 3000+skill*500; // Temper Steel bonus: +35/+40/+45/+50/+55
+ break;
+ case 1000: //Star Crumb
+ make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex]
+ break;
+ default: // Enchanted Stones
+ make_per += 1000+skill*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35
+ break;
+ }
+ break;
+ case ASC_CDP:
+ make_per = (2000 + 40*sd->paramc[4] + 20*sd->paramc[5]);
+ break;
+ case AL_HOLYWATER:
+ make_per = 100000; //100% success
+ break;
+ case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG]
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*100
+ + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20
+ + sd->paramc[3]*5 + sd->paramc[4]*10+sd->paramc[5]*10;
+ switch(nameid){
+ case 501: // Red Potion
+ case 503: // Yellow Potion
+ case 504: // White Potion
+ case 605: // Anodyne
+ case 606: // Aloevera
+ make_per += 2000;
+ break;
+ case 505: // Blue Potion
+ make_per -= 500;
+ break;
+ case 545: // Condensed Red Potion
+ case 546: // Condensed Yellow Potion
+ case 547: // Condensed White Potion
+ make_per -= 1000;
+ break;
+ case 970: // Alcohol
+ make_per += 1000;
+ break;
+ case 7139: // Glistening Coat
+ make_per -= 1000;
+ break;
+ case 7135: // Bottle Grenade
+ case 7136: // Acid Bottle
+ case 7137: // Plant Bottle
+ case 7138: // Marine Sphere Bottle
+ default:
+ break;
+ }
+ if(battle_config.pp_rate != 100)
+ make_per = make_per * battle_config.pp_rate / 100;
+ break;
+ case SA_CREATECON: // Elemental Converter Creation - skill bonuses are from kRO [DracoRPG]
+ make_per = pc_checkskill(sd, SA_ADVANCEDBOOK)*100 + //TODO: Advanced Book bonus is custom! [Skotlex]
+ sd->status.job_level*20 + sd->paramc[3]*10 + sd->paramc[4]*10;
+ switch(nameid){
+ case 12114:
+ flag = pc_checkskill(sd,SA_FLAMELAUNCHER);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12115:
+ flag = pc_checkskill(sd,SA_FROSTWEAPON);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12116:
+ flag = pc_checkskill(sd,SA_SEISMICWEAPON);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ case 12117:
+ flag = pc_checkskill(sd,SA_LIGHTNINGLOADER);
+ if (flag > 0)
+ make_per += 1000*flag-500;
+ break;
+ }
+ break;
+ default:
+ make_per = 5000;
+ break;
+ }
+ }
+ } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
+ make_per = 5000 + sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; // Base
+ make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
+ make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5
+ make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30
+ if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10
+ else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5
+ else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3
+ else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0?
+ if(battle_config.wp_rate != 100)
+ make_per = make_per * battle_config.wp_rate / 100;
+ }
+// - Baby Class Penalty = 80% (from adult's chance) ----//
+ if (sd->class_&JOBL_BABY) //if it's a Baby Class
+ make_per = (make_per * 80) / 100; //Lupus
+
+ if(make_per < 1) make_per = 1;
+
+
+ if(rand()%10000 < make_per || qty > 1){ //Success, or crafting multiple items.
+ struct item tmp_item;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid=nameid;
+ tmp_item.amount=1;
+ tmp_item.identify=1;
+ if(equip){
+ tmp_item.card[0]=0x00ff;
+ tmp_item.card[1]=((sc*5)<<8)+ele;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ } else {
+ //Flag is only used on the end, so it can be used here. [Skotlex]
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ flag = battle_config.produce_potion_name_input;
+ break;
+ case AL_HOLYWATER:
+ flag = battle_config.holywater_name_input;
+ break;
+ case ASC_CDP:
+ flag = battle_config.cdp_name_input;
+ break;
+ default:
+ flag = battle_config.produce_item_name_input;
+ break;
+ }
+ if (flag) {
+ tmp_item.card[0]=0x00fe;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ }
+ }
+
+ if(log_config.produce > 0)
+ log_produce(sd,nameid,slot1,slot2,slot3,1);
+
+ if(equip){
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
+ pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
+ } else {
+ int fame = 0;
+ tmp_item.amount = 0;
+ for (i=0; i< qty; i++)
+ { //Apply quantity modifiers.
+ if (rand()%10000 < make_per || qty == 1)
+ { //Success
+ tmp_item.amount++;
+ if(nameid < 545 || nameid > 547)
+ continue;
+ if(skill_id != AM_PHARMACY &&
+ skill_id != AM_TWILIGHT1 &&
+ skill_id != AM_TWILIGHT2 &&
+ skill_id != AM_TWILIGHT3)
+ continue;
+ //Add fame as needed.
+ switch(++sd->potion_success_counter) {
+ case 3:
+ fame+=1; // Success to prepare 3 Condensed Potions in a row
+ break;
+ case 5:
+ fame+=3; // Success to prepare 5 Condensed Potions in a row
+ break;
+ case 7:
+ fame+=10; // Success to prepare 7 Condensed Potions in a row
+ break;
+ case 10:
+ fame+=50; // Success to prepare 10 Condensed Potions in a row
+ sd->potion_success_counter = 0;
+ break;
+ }
+ } else //Failure
+ sd->potion_success_counter = 0;
+ }
+ if (fame)
+ pc_addfame(sd,fame);
+ //Visual effects and the like.
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ case ASC_CDP:
+ clif_produceeffect(sd,2,nameid);
+ clif_misceffect(&sd->bl,5);
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ break;
+ default: //Those that don't require a skill?
+ if (skill_produce_db[idx].itemlv==11) //Cooking items.
+ clif_specialeffect(&sd->bl, 608, 0);
+ break;
+ }
+ }
+ if (tmp_item.amount) { //Success
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ return 1;
+ }
+ }
+ //Failure
+ if(log_config.produce)
+ log_produce(sd,nameid,slot1,slot2,slot3,0);
+
+ if(equip){
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ } else {
+ switch (skill_id) {
+ case ASC_CDP: //Damage yourself, and display same effect as failed potion.
+ pc_heal(sd,-(sd->status.max_hp>>2),0);
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ sd->potion_success_counter = 0; // Fame point system [DracoRPG]
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ break;
+ default:
+ if (skill_produce_db[idx].itemlv==11)
+ clif_specialeffect(&sd->bl, 609, 0);
+ }
+ }
+ return 0;
+}
+
+int skill_arrow_create( struct map_session_data *sd,int nameid)
+{
+ int i,j,flag,index=-1;
+ struct item tmp_item;
+
+ nullpo_retr(0, sd);
+
+ if(nameid <= 0)
+ return 1;
+
+ for(i=0;i<MAX_SKILL_ARROW_DB;i++)
+ if(nameid == skill_arrow_db[i].nameid) {
+ index = i;
+ break;
+ }
+
+ if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
+ return 1;
+
+ pc_delitem(sd,j,1,0);
+ for(i=0;i<5;i++) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.identify = 1;
+ tmp_item.nameid = skill_arrow_db[index].cre_id[i];
+ tmp_item.amount = skill_arrow_db[index].cre_amount[i];
+ if(battle_config.making_arrow_name_input) {
+ tmp_item.card[0]=0x00fe;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->char_id,1);
+ }
+ if(tmp_item.nameid <= 0 || tmp_item.amount <= 0)
+ continue;
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * ?‰Šú‰»Œn
+ */
+
+/*
+ * •¶Žš—ñ?ˆ—?
+ * ',' ‚Å‹æ?Ø‚Á‚Ä val ‚É–ß‚·
+ */
+int skill_split_str(char *str,char **val,int num)
+{
+ int i;
+
+ for (i=0; i<num && str; i++){
+ val[i] = str;
+ str = strchr(str,',');
+ if (str)
+ *str++=0;
+ }
+ return i;
+}
+/*
+ * •¶Žš—ñ?ˆ—?
+ * ':' ‚Å‹æ?Ø‚Á‚Äatoi‚µ‚Äval‚É–ß‚·
+ */
+int skill_split_atoi(char *str,int *val)
+{
+ int i, j, diff, step = 1;
+
+ for (i=0; i<MAX_SKILL_LEVEL; i++) {
+ if (!str) break;
+ val[i] = atoi(str);
+ str = strchr(str,':');
+ if (str)
+ *str++=0;
+ }
+ if(i==0) //No data found.
+ return 0;
+ if(i==1)
+ { //Single value, have the whole range have the same value.
+ for (; i < MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+ }
+ //Check for linear change with increasing steps until we reach half of the data acquired.
+ for (step = 1; step <= i/2; step++)
+ {
+ diff = val[i-1] - val[i-step-1];
+ for(j = i-1; j >= step; j--)
+ if ((val[j]-val[j-step]) != diff)
+ break;
+
+ if (j>=step) //No match, try next step.
+ continue;
+
+ for(; i < MAX_SKILL_LEVEL; i++)
+ { //Apply linear increase
+ val[i] = val[i-step]+diff;
+ if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases.
+ { val[i] = 1; diff = 0; step = 1; }
+ }
+ return i;
+ }
+ //Okay.. we can't figure this one out, just fill out the stuff with the previous value.
+ for (;i<MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+}
+
+/*
+ * ƒXƒLƒ‹ƒ†ƒjƒbƒg‚Ì”z’u?î•ñ?ì?¬
+ */
+void skill_init_unit_layout(void)
+{
+ int i,j,size,pos = 0;
+
+ memset(skill_unit_layout,0,sizeof(skill_unit_layout));
+ // ‹éŒ`‚̃†ƒjƒbƒg”z’u‚ð?ì?¬‚·‚é
+ for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
+ size = i*2+1;
+ skill_unit_layout[i].count = size*size;
+ for (j=0; j<size*size; j++) {
+ skill_unit_layout[i].dx[j] = (j%size-i);
+ skill_unit_layout[i].dy[j] = (j/size-i);
+ }
+ }
+ pos = i;
+ // ‹éŒ`ˆÈŠO‚̃†ƒjƒbƒg”z’u‚ð?ì?¬‚·‚é
+ for (i=0;i<MAX_SKILL_DB;i++) {
+ if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
+ continue;
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ // ƒtƒ@ƒCƒA?[ƒEƒH?[ƒ‹?AƒAƒCƒXƒEƒH?[ƒ‹‚Í•ûŒü‚Å•Ï‚í‚é‚Ì‚Å•Ê?ˆ—?
+ break;
+ case PR_SANCTUARY:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
+ 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
+ static const int dy[]={
+ -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 21;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PR_MAGNUS:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case AS_VENOMDUST:
+ {
+ static const int dx[] = {-1, 0, 0, 0, 1};
+ static const int dy[] = { 0,-1, 0, 1, 0};
+ skill_unit_layout[pos].count = 5;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ {
+ static const int dx[] = {
+ 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
+ -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
+ -1, 0, 1, 2,-1, 0, 1, 0, 0};
+ static const int dy[] = {
+ -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 2, 2, 2, 3, 4};
+ skill_unit_layout[pos].count = 29;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PF_FOGWALL:
+ {
+ static const int dx[] = {
+ -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = {
+ -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+ skill_unit_layout[pos].count = 15;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ case PA_GOSPEL:
+ {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
+ -1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ break;
+ }
+ default:
+ ShowError("unknown unit layout at skill %d\n",i);
+ break;
+ }
+ if (!skill_unit_layout[pos].count)
+ continue;
+ for (j=0;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ pos++;
+ }
+ // ƒtƒ@ƒCƒ„?[ƒEƒH?[ƒ‹
+ firewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ if (i&1) { /* ŽÎ‚ß”z’u */
+ skill_unit_layout[pos].count = 5;
+ if (i&0x2) {
+ int dx[] = {-1,-1, 0, 0, 1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 1, 1 ,0, 0,-1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else { /* ?c‰¡”z’u */
+ skill_unit_layout[pos].count = 3;
+ if (i%4==0) { /* ?㉺ */
+ int dx[] = {-1, 0, 1};
+ int dy[] = { 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else { /* ?¶‰E */
+ int dx[] = { 0, 0, 0};
+ int dy[] = {-1, 0, 1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ // ƒAƒCƒXƒEƒH?[ƒ‹
+ icewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ skill_unit_layout[pos].count = 5;
+ if (i&1) { /* ŽÎ‚ß”z’u */
+ if (i&0x2) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 2,-1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 2, 1 ,0,-1,-2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else { /* ?c‰¡”z’u */
+ if (i%4==0) { /* ?㉺ */
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 0, 0, 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else { /* ?¶‰E */
+ int dx[] = { 0, 0, 0, 0, 0};
+ int dy[] = {-2,-1, 0, 1, 2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+}
+
+/*==========================================
+ * ƒXƒLƒ‹?ŒWƒtƒ@ƒCƒ‹?‚Ý?‚Ý
+ * skill_db.txt ƒXƒLƒ‹ƒf?ƒ^
+ * skill_cast_db.txt ƒXƒLƒ‹‚̉r?¥ŽžŠÔ‚ƃfƒBƒŒƒCƒf?ƒ^
+ * produce_db.txt ƒAƒCƒeƒ€?ì?¬ƒXƒLƒ‹—pƒf?ƒ^
+ * create_arrow_db.txt –î?ì?¬ƒXƒLƒ‹—pƒf?ƒ^
+ * abra_db.txt ƒAƒuƒ‰ƒJƒ_ƒuƒ‰?“®ƒXƒLƒ‹ƒf?ƒ^
+ *------------------------------------------
+ */
+int skill_readdb(void)
+{
+ int i,j,k,l,m;
+ FILE *fp;
+ char line[1024],path[1024],*p;
+ char *filename[]={"produce_db.txt","produce_db2.txt"};
+
+ /* ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+ memset(skill_db,0,sizeof(skill_db));
+ sprintf(path, "%s/skill_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,14);
+ if(j < 14 || split[13]==NULL)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].range);
+ skill_db[i].hit=atoi(split[2]);
+ skill_db[i].inf=atoi(split[3]);
+ skill_db[i].pl=atoi(split[4]);
+ skill_db[i].nk=atoi(split[5]);
+ skill_db[i].max=atoi(split[6]);
+ skill_split_atoi(split[7],skill_db[i].num);
+
+ if(strcmpi(split[8],"yes") == 0)
+ skill_db[i].castcancel=1;
+ else
+ skill_db[i].castcancel=0;
+ skill_db[i].cast_def_rate=atoi(split[9]);
+ skill_db[i].inf2=atoi(split[10]);
+ skill_db[i].maxcount=atoi(split[11]);
+ if(strcmpi(split[12],"weapon") == 0)
+ skill_db[i].skill_type=BF_WEAPON;
+ else if(strcmpi(split[12],"magic") == 0)
+ skill_db[i].skill_type=BF_MAGIC;
+ else if(strcmpi(split[12],"misc") == 0)
+ skill_db[i].skill_type=BF_MISC;
+ else
+ skill_db[i].skill_type=0;
+ skill_split_atoi(split[13],skill_db[i].blewcount);
+
+ for (j = 0; skill_names[j].id != 0; j++)
+ if (skill_names[j].id == i) {
+ skill_db[i].name = skill_names[j].name;
+ skill_db[i].desc = skill_names[j].desc;
+ break;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_require_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,30);
+ if(j < 30 || split[29]==NULL)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].hp);
+ skill_split_atoi(split[2],skill_db[i].mhp);
+ skill_split_atoi(split[3],skill_db[i].sp);
+ skill_split_atoi(split[4],skill_db[i].hp_rate);
+ skill_split_atoi(split[5],skill_db[i].sp_rate);
+ skill_split_atoi(split[6],skill_db[i].zeny);
+
+ p = split[7];
+ for(j=0;j<32;j++){
+ l = atoi(p);
+ if (l==99) {
+ skill_db[i].weapon = 0xffffffff;
+ break;
+ }
+ else
+ skill_db[i].weapon |= 1<<l;
+ p=strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ if( strcmpi(split[8],"hiding")==0 ) skill_db[i].state=ST_HIDING;
+ else if( strcmpi(split[8],"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
+ else if( strcmpi(split[8],"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
+ else if( strcmpi(split[8],"riding")==0 ) skill_db[i].state=ST_RIDING;
+ else if( strcmpi(split[8],"falcon")==0 ) skill_db[i].state=ST_FALCON;
+ else if( strcmpi(split[8],"cart")==0 ) skill_db[i].state=ST_CART;
+ else if( strcmpi(split[8],"shield")==0 ) skill_db[i].state=ST_SHIELD;
+ else if( strcmpi(split[8],"sight")==0 ) skill_db[i].state=ST_SIGHT;
+ else if( strcmpi(split[8],"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(split[8],"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
+ else if( strcmpi(split[8],"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(split[8],"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
+ else if( strcmpi(split[8],"water")==0 ) skill_db[i].state=ST_WATER;
+ else skill_db[i].state=ST_NONE;
+
+ skill_split_atoi(split[9],skill_db[i].spiritball);
+ skill_db[i].itemid[0]=atoi(split[10]);
+ skill_db[i].amount[0]=atoi(split[11]);
+ skill_db[i].itemid[1]=atoi(split[12]);
+ skill_db[i].amount[1]=atoi(split[13]);
+ skill_db[i].itemid[2]=atoi(split[14]);
+ skill_db[i].amount[2]=atoi(split[15]);
+ skill_db[i].itemid[3]=atoi(split[16]);
+ skill_db[i].amount[3]=atoi(split[17]);
+ skill_db[i].itemid[4]=atoi(split[18]);
+ skill_db[i].amount[4]=atoi(split[19]);
+ skill_db[i].itemid[5]=atoi(split[20]);
+ skill_db[i].amount[5]=atoi(split[21]);
+ skill_db[i].itemid[6]=atoi(split[22]);
+ skill_db[i].amount[6]=atoi(split[23]);
+ skill_db[i].itemid[7]=atoi(split[24]);
+ skill_db[i].amount[7]=atoi(split[25]);
+ skill_db[i].itemid[8]=atoi(split[26]);
+ skill_db[i].amount[8]=atoi(split[27]);
+ skill_db[i].itemid[9]=atoi(split[28]);
+ skill_db[i].amount[9]=atoi(split[29]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ /* ƒLƒƒƒXƒeƒBƒ“ƒOƒf?ƒ^ƒx?ƒX */
+
+ sprintf(path, "%s/skill_cast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ memset(split,0,sizeof(split)); // [Valaris] thanks to fov
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,5);
+ if(split[4]==NULL || j<5)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].cast);
+ skill_split_atoi(split[2],skill_db[i].delay);
+ skill_split_atoi(split[3],skill_db[i].upkeep_time);
+ skill_split_atoi(split[4],skill_db[i].upkeep_time2);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ /* ƒXƒLƒ‹ƒ†ƒjƒbƒgƒf?[ƒ^ƒx?[ƒX */
+
+ sprintf(path, "%s/skill_unit_db.txt", db_path);
+ fp=fopen(path,"r");
+ if (fp==NULL) {
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k = 0;
+ while (fgets(line,1020,fp)) {
+ char *split[50];
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,8);
+ if (split[7]==NULL || j<8)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[i].unit_layout_type);
+ skill_db[i].unit_range = atoi(split[4]);
+ skill_db[i].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE;
+ else skill_db[i].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[i].unit_flag = strtol(split[7],NULL,16);
+
+ if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[i].unit_target=BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[i].unit_target |= BL_CHAR;
+ if (skill_db[i].unit_flag&UF_NOPC)
+ skill_db[i].unit_target &= ~BL_PC;
+ if (skill_db[i].unit_flag&UF_NOMOB)
+ skill_db[i].unit_target &= ~BL_MOB;
+ if (skill_db[i].unit_flag&UF_SKILL)
+ skill_db[i].unit_target |= BL_SKILL;
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+ skill_init_unit_layout();
+
+ /* ?»‘¢ŒnƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+ memset(skill_produce_db,0,sizeof(skill_produce_db));
+ for(m=0;m<2;m++){
+ sprintf(path, "%s/%s", db_path, filename[m]);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ if(m>0)
+ continue;
+ ShowError("can't read %s\n",path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[6 + MAX_PRODUCE_RESOURCE * 2];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2));
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0) continue;
+
+ skill_produce_db[k].nameid=i;
+ skill_produce_db[k].itemlv=atoi(split[1]);
+ skill_produce_db[k].req_skill=atoi(split[2]);
+
+ for(x=3,y=0; split[x] && split[x+1] && y<MAX_PRODUCE_RESOURCE; x+=2,y++){
+ skill_produce_db[k].mat_id[y]=atoi(split[x]);
+ skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_PRODUCE_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+ }
+
+ memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+
+ sprintf(path, "%s/create_arrow_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_arrow_db[k].nameid=i;
+
+ for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){
+ skill_arrow_db[k].cre_id[y]=atoi(split[x]);
+ skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_ARROW_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ memset(skill_abra_db,0,sizeof(skill_abra_db));
+ sprintf(path, "%s/abra_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_abra_db[i].req_lv=atoi(split[2]);
+ skill_abra_db[i].per=atoi(split[3]);
+
+ k++;
+ if(k >= MAX_SKILL_ABRA_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ sprintf(path, "%s/skill_castnodex_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,4);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].castnodex);
+ if (!split[2])
+ continue;
+ skill_split_atoi(split[2],skill_db[i].delaynodex);
+ if(!split[3])
+ continue;
+ skill_split_atoi(split[3],skill_db[i].delaynowalk);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_nocast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,2);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].nocast=atoi(split[1]);
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+
+// SQL Skill database reading [Valaris]
+#ifndef TXT_ONLY
+int skill_read_sqldb(void)
+{
+ const char unknown_str[NAME_LENGTH] ="unknown";
+ int i,j,k,l,m;
+ FILE *fp;
+ char line[1024],path[1024],*p;
+ char *filename[]={"produce_db.txt","produce_db2.txt"};
+ long unsigned int ln = 0;
+
+ /* ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+ memset(skill_db,0,sizeof(skill_db));
+
+ //For easier handling of converting. [Skotlex]
+#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a]))
+#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a])
+ sprintf (tmp_sql, "SELECT * FROM `%s`", skill_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", skill_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].range);
+ skill_db[i].hit=TO_INT(2);
+ skill_db[i].inf=TO_INT(3);
+ skill_db[i].pl=TO_INT(4);
+ skill_db[i].nk=TO_INT(5);
+ skill_db[i].max=TO_INT(6);
+ skill_split_atoi(sql_row[7],skill_db[i].num);
+
+ if(strcmpi(TO_STR(8),"yes") == 0)
+ skill_db[i].castcancel=1;
+ else
+ skill_db[i].castcancel=0;
+ skill_db[i].cast_def_rate=TO_INT(9);
+ skill_db[i].inf2=TO_INT(10);
+ skill_db[i].maxcount=TO_INT(11);
+ if(strcmpi(TO_STR(12),"weapon") == 0)
+ skill_db[i].skill_type=BF_WEAPON;
+ else if(strcmpi(TO_STR(12),"magic") == 0)
+ skill_db[i].skill_type=BF_MAGIC;
+ else if(strcmpi(TO_STR(12),"misc") == 0)
+ skill_db[i].skill_type=BF_MISC;
+ else
+ skill_db[i].skill_type=0;
+ skill_split_atoi(sql_row[13],skill_db[i].blewcount);
+
+ for (j = 0; skill_names[j].id != 0; j++)
+ if (skill_names[j].id == i) {
+ skill_db[i].name = skill_names[j].name;
+ skill_db[i].desc = skill_names[j].desc;
+ break;
+ }
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, skill_sqldb);
+ ln=0;
+ }
+
+ sprintf (tmp_sql, "SELECT * FROM `%s`", skill_require_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", skill_require_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].hp);
+ skill_split_atoi(sql_row[2],skill_db[i].mhp);
+ skill_split_atoi(sql_row[3],skill_db[i].sp);
+ skill_split_atoi(sql_row[4],skill_db[i].hp_rate);
+ skill_split_atoi(sql_row[5],skill_db[i].sp_rate);
+ skill_split_atoi(sql_row[6],skill_db[i].zeny);
+
+ p = sql_row[7];
+ for(j=0;j<32;j++){
+ l = atoi(p);
+ if (l==99) {
+ skill_db[i].weapon = 0xffffffff;
+ break;
+ }
+ else
+ skill_db[i].weapon |= 1<<l;
+ p=strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ if( strcmpi(TO_STR(8),"hiding")==0 ) skill_db[i].state=ST_HIDING;
+ else if( strcmpi(TO_STR(8),"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
+ else if( strcmpi(TO_STR(8),"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
+ else if( strcmpi(TO_STR(8),"riding")==0 ) skill_db[i].state=ST_RIDING;
+ else if( strcmpi(TO_STR(8),"falcon")==0 ) skill_db[i].state=ST_FALCON;
+ else if( strcmpi(TO_STR(8),"cart")==0 ) skill_db[i].state=ST_CART;
+ else if( strcmpi(TO_STR(8),"shield")==0 ) skill_db[i].state=ST_SHIELD;
+ else if( strcmpi(TO_STR(8),"sight")==0 ) skill_db[i].state=ST_SIGHT;
+ else if( strcmpi(TO_STR(8),"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(TO_STR(8),"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
+ else if( strcmpi(TO_STR(8),"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(TO_STR(8),"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
+ else if( strcmpi(TO_STR(8),"water")==0 ) skill_db[i].state=ST_WATER;
+ else skill_db[i].state=ST_NONE;
+
+ skill_split_atoi(sql_row[9],skill_db[i].spiritball);
+ skill_db[i].itemid[0]=TO_INT(10);
+ skill_db[i].amount[0]=TO_INT(11);
+ skill_db[i].itemid[1]=TO_INT(12);
+ skill_db[i].amount[1]=TO_INT(13);
+ skill_db[i].itemid[2]=TO_INT(14);
+ skill_db[i].amount[2]=TO_INT(15);
+ skill_db[i].itemid[3]=TO_INT(16);
+ skill_db[i].amount[3]=TO_INT(17);
+ skill_db[i].itemid[4]=TO_INT(18);
+ skill_db[i].amount[4]=TO_INT(19);
+ skill_db[i].itemid[5]=TO_INT(20);
+ skill_db[i].amount[5]=TO_INT(21);
+ skill_db[i].itemid[6]=TO_INT(22);
+ skill_db[i].amount[6]=TO_INT(23);
+ skill_db[i].itemid[7]=TO_INT(24);
+ skill_db[i].amount[7]=TO_INT(25);
+ skill_db[i].itemid[8]=TO_INT(26);
+ skill_db[i].amount[8]=TO_INT(27);
+ skill_db[i].itemid[9]=TO_INT(28);
+ skill_db[i].amount[9]=TO_INT(29);
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, skill_require_sqldb);
+ ln=0;
+ }
+
+ sprintf (tmp_sql, "SELECT * FROM `%s`", cast_sqldb);
+ if (mysql_query(&mmysql_handle, tmp_sql)) {
+ ShowSQL("DB error (%s) - %s\n", cast_sqldb, mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 1;
+ }
+ sql_res = mysql_store_result(&mmysql_handle);
+ if (sql_res) {
+ while((sql_row = mysql_fetch_row(sql_res))){
+ i=TO_INT(0);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ ln++;
+
+ skill_split_atoi(sql_row[1],skill_db[i].cast);
+ skill_split_atoi(sql_row[2],skill_db[i].delay);
+ skill_split_atoi(sql_row[3],skill_db[i].upkeep_time);
+ skill_split_atoi(sql_row[4],skill_db[i].upkeep_time2);
+ }
+ mysql_free_result(sql_res);
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, cast_sqldb);
+ ln=0;
+ }
+
+ /* ƒXƒLƒ‹ƒ†ƒjƒbƒgƒf?[ƒ^ƒx?[ƒX */
+
+ sprintf(path, "%s/skill_unit_db.txt", db_path);
+ fp=fopen(path,"r");
+ if (fp==NULL) {
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k = 0;
+ while (fgets(line,1020,fp)) {
+ char *split[50];
+ if (line[0]=='/' && line[1]=='/')
+ continue;
+ j = skill_split_str(line,split,8);
+ if (split[7]==NULL || j<8)
+ continue;
+
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[i].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[i].unit_layout_type);
+ skill_db[i].unit_range = atoi(split[4]);
+ skill_db[i].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE;
+ else skill_db[i].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[i].unit_flag = strtol(split[7],NULL,16);
+ if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[i].unit_target=BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[i].unit_target |= BL_CHAR;
+ if (skill_db[i].unit_flag&UF_NOPC)
+ skill_db[i].unit_target &= ~BL_PC;
+ if (skill_db[i].unit_flag&UF_NOMOB)
+ skill_db[i].unit_target &= ~BL_MOB;
+ if (skill_db[i].unit_flag&UF_SKILL)
+ skill_db[i].unit_target |= BL_SKILL;
+
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+ skill_init_unit_layout();
+
+ /* ?»‘¢ŒnƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX */
+ memset(skill_produce_db,0,sizeof(skill_produce_db));
+ for(m=0;m<2;m++){
+ sprintf(path, "%s/%s", db_path, filename[m]);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ if(m>0)
+ continue;
+ ShowError("can't read %s\n",path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[6 + MAX_PRODUCE_RESOURCE * 2];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2));
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0) continue;
+
+ skill_produce_db[k].nameid=i;
+ skill_produce_db[k].itemlv=atoi(split[1]);
+ skill_produce_db[k].req_skill=atoi(split[2]);
+
+ for(x=3,y=0; split[x] && split[x+1] && y<MAX_PRODUCE_RESOURCE; x+=2,y++){
+ skill_produce_db[k].mat_id[y]=atoi(split[x]);
+ skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_PRODUCE_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+ }
+
+ memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+
+ sprintf(path, "%s/create_arrow_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ int x,y;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_arrow_db[k].nameid=i;
+
+ for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){
+ skill_arrow_db[k].cre_id[y]=atoi(split[x]);
+ skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]);
+ }
+ k++;
+ if(k >= MAX_SKILL_ARROW_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ memset(skill_abra_db,0,sizeof(skill_abra_db));
+ sprintf(path, "%s/abra_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,13);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if(i<=0)
+ continue;
+
+ skill_abra_db[i].req_lv=atoi(split[2]);
+ skill_abra_db[i].per=atoi(split[3]);
+
+ k++;
+ if(k >= MAX_SKILL_ABRA_DB)
+ break;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path);
+
+ sprintf(path, "%s/skill_castnodex_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,4);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+
+ skill_split_atoi(split[1],skill_db[i].castnodex);
+ if (!split[2])
+ continue;
+ skill_split_atoi(split[2],skill_db[i].delaynodex);
+ if(!split[3])
+ continue;
+ skill_split_atoi(split[3],skill_db[i].delaynowalk);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ sprintf(path, "%s/skill_nocast_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ k=0;
+ while(fgets(line,1020,fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(split,0,sizeof(split));
+ j = skill_split_str(line,split,2);
+ if(split[0]==0) //fixed by Lupus
+ continue;
+ i=atoi(split[0]);
+ if (i>=10000 && i<10015) // for guild skills [Celest]
+ i -= 9500;
+ else if(i<=0 || i>MAX_SKILL_DB)
+ continue;
+ skill_db[i].nocast=atoi(split[1]);
+ k++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+#endif
+
+/*===============================================
+ * For reading leveluseskillspamount.txt [Celest]
+ *-----------------------------------------------
+ */
+static int skill_read_skillspamount(void)
+{
+ char *buf,*p;
+ struct skill_db *skill = NULL;
+ int s, idx, new_flag=1, level=1, sp=0;
+
+ buf=(char *) grfio_reads("data\\leveluseskillspamount.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ char buf2[64];
+
+ if (sscanf(p,"%[@]",buf2) == 1) {
+ level = 1;
+ new_flag = 1;
+ } else if (new_flag && sscanf(p,"%[^#]#",buf2) == 1) {
+ for (idx=0; skill_names[idx].id != 0; idx++) {
+ if (strstr(buf2, skill_names[idx].name) != NULL) {
+ skill = &skill_db[ skill_names[idx].id ];
+ new_flag = 0;
+ break;
+ }
+ }
+ } else if (!new_flag && sscanf(p,"%d#",&sp) == 1) {
+ skill->sp[level-1]=sp;
+ level++;
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\leveluseskillspamount.txt");
+
+ return 0;
+}
+
+void skill_reload(void)
+{
+ skill_readdb();
+ if (battle_config.skill_sp_override_grffile)
+ skill_read_skillspamount();
+}
+
+/*==========================================
+ * ƒXƒLƒ‹?ŒW?‰Šú‰»?—?
+ *------------------------------------------
+ */
+int do_init_skill(void)
+{
+
+#ifndef TXT_ONLY
+ if(db_use_newsqldbs)
+ skill_read_sqldb();
+ else
+#endif /* TXT_ONLY */
+ skill_readdb();
+
+ if (battle_config.skill_sp_override_grffile)
+ skill_read_skillspamount();
+
+ add_timer_func_list(skill_unit_timer,"skill_unit_timer");
+ add_timer_func_list(skill_castend_id,"skill_castend_id");
+ add_timer_func_list(skill_castend_pos,"skill_castend_pos");
+ add_timer_func_list(skill_timerskill,"skill_timerskill");
+ add_timer_func_list(skill_castend_delay_sub,"skill_castend_delay_sub");
+
+ add_timer_interval(gettick()+SKILLUNITTIMER_INVERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INVERVAL);
+
+ return 0;
+}
diff --git a/src/map/skill.h b/src/map/skill.h
new file mode 100644
index 000000000..427533ed8
--- /dev/null
+++ b/src/map/skill.h
@@ -0,0 +1,879 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SKILL_H_
+#define _SKILL_H_
+
+#include "map.h"
+
+#define MAX_SKILL_DB 1100
+#define MAX_SKILL_PRODUCE_DB 150
+#define MAX_PRODUCE_RESOURCE 12
+#define MAX_SKILL_ARROW_DB 150
+#define MAX_SKILL_ABRA_DB 350
+
+//Constants to identify the skill's inf value:
+#define INF_ATTACK_SKILL 1
+//For the time being, all trap-targetted skills ARE ground based:
+#define INF_GROUND_SKILL (2|32)
+// Skills casted on self where target is automatically chosen:
+#define INF_SELF_SKILL 4
+#define INF_SUPPORT_SKILL 16
+#define INF_TARGET_TRAP 32
+
+//Constants to identify a skill's nk value.
+//The NK value applies only to non INF_GROUND_SKILL skills.
+#define NK_NO_DAMAGE 1
+#define NK_SPLASH_DAMAGE 2
+
+//Constants to identify a skill's inf2 value.
+#define INF2_QUEST_SKILL 1
+//NPC skills are those that players can't have in their skill tree.
+#define INF2_NPC_SKILL 2
+#define INF2_WEDDING_SKILL 4
+#define INF2_SPIRIT_SKILL 8
+#define INF2_GUILD_SKILL 16
+#define INF2_SONG_DANCE 32
+#define INF2_ENSEMBLE_SKILL 64
+#define INF2_TRAP 128
+//Refers to ground placed skills that will target the caster as well (like Grandcross)
+#define INF2_TARGET_SELF 256
+#define INF2_NO_TARGET_SELF 512
+#define INF2_PARTY_ONLY 1024
+#define INF2_GUILD_ONLY 2048
+
+// ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX
+struct skill_db {
+ char *name;
+ char *desc;
+ int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,max;
+ int num[MAX_SKILL_LEVEL];
+ int cast[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL];
+ int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL];
+ int castcancel,cast_def_rate;
+ int inf2,maxcount,skill_type;
+ int blewcount[MAX_SKILL_LEVEL];
+ int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL];
+ int weapon,state,spiritball[MAX_SKILL_LEVEL];
+ int itemid[10],amount[10];
+ int castnodex[MAX_SKILL_LEVEL];
+ int delaynodex[MAX_SKILL_LEVEL];
+ int delaynowalk[MAX_SKILL_LEVEL];
+ int nocast;
+ int unit_id[2];
+ int unit_layout_type[MAX_SKILL_LEVEL];
+ int unit_range;
+ int unit_interval;
+ int unit_target;
+ int unit_flag;
+};
+extern struct skill_db skill_db[MAX_SKILL_DB];
+
+struct skill_name_db {
+ int id; // skill id
+ char *name; // search strings
+ char *desc; // description that shows up for search's
+};
+
+#define MAX_SKILL_UNIT_LAYOUT 50
+#define MAX_SQUARE_LAYOUT 5 // 11*11‚̃†ƒjƒbƒg”z’u‚ªÅ‘å
+#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1))
+struct skill_unit_layout {
+ int count;
+ int dx[MAX_SKILL_UNIT_COUNT];
+ int dy[MAX_SKILL_UNIT_COUNT];
+};
+
+enum {
+ UF_DEFNOTENEMY = 0x0001, // defnotenemy Ý’è‚ÅBCT_NOENEMY‚ÉØ‚è‘Ö‚¦
+ UF_NOREITERATION = 0x0002, // d•¡’u‚«‹ÖŽ~
+ UF_NOFOOTSET = 0x0004, // ‘«Œ³’u‚«‹ÖŽ~
+ UF_NOOVERLAP = 0x0008, // ƒ†ƒjƒbƒgŒø‰Ê‚ªd•¡‚µ‚È‚¢
+ UF_NOPC = 0x0010, //May not target players
+ UF_NOMOB = 0x0020, //May not target mobs
+ UF_SKILL = 0x0080, //May target skills
+ UF_DANCE = 0x0100, // ƒ_ƒ“ƒXƒXƒLƒ‹
+ UF_ENSEMBLE = 0x0200, // ‡‘tƒXƒLƒ‹
+ UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects.
+};
+
+// ƒAƒCƒeƒ€ì¬ƒf?ƒ^ƒx?ƒX
+struct skill_produce_db {
+ int nameid, trigger;
+ int req_skill,itemlv;
+ int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE];
+};
+extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+
+// –î쬃f?ƒ^ƒx?ƒX
+struct skill_arrow_db {
+ int nameid, trigger;
+ int cre_id[5],cre_amount[5];
+};
+extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+
+// ƒAƒuƒ‰ƒJƒ_ƒuƒ‰ƒf?ƒ^ƒx?ƒX
+struct skill_abra_db {
+ int nameid;
+ int req_lv;
+ int per;
+};
+extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+
+extern int enchant_eff[5];
+extern int deluge_eff[5];
+
+struct block_list;
+struct map_session_data;
+struct skill_unit;
+struct skill_unit_group;
+
+int do_init_skill(void);
+
+// ƒXƒLƒ‹ƒf?ƒ^ƒx?ƒX‚ւ̃AƒNƒZƒT
+int skill_get_hit( int id );
+int skill_get_inf( int id );
+int skill_get_pl( int id );
+int skill_get_nk( int id );
+int skill_get_max( int id );
+int skill_get_range( int id , int lv );
+int skill_get_range2(struct block_list *bl, int id, int lv);
+int skill_get_hp( int id ,int lv );
+int skill_get_mhp( int id ,int lv );
+int skill_get_sp( int id ,int lv );
+int skill_get_zeny( int id ,int lv );
+int skill_get_num( int id ,int lv );
+int skill_get_cast( int id ,int lv );
+int skill_get_delay( int id ,int lv );
+int skill_get_time( int id ,int lv );
+int skill_get_time2( int id ,int lv );
+int skill_get_castdef( int id );
+int skill_get_weapontype( int id );
+int skill_get_unit_id(int id,int flag);
+int skill_get_inf2( int id );
+int skill_get_maxcount( int id );
+int skill_get_blewcount( int id ,int lv );
+int skill_get_unit_flag( int id );
+int skill_get_unit_target( int id );
+int skill_tree_get_max( int id, int b_class ); // Celest
+const char* skill_get_name( int id ); // [Skotlex]
+
+// ƒXƒLƒ‹‚ÌŽg—p
+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);
+
+// ’ljÁ?‰Ê
+int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_blown( struct block_list *src, struct block_list *target,int count);
+// ƒ†ƒjƒbƒgƒXƒLƒ‹
+struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag);
+struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y);
+int skill_delunit(struct skill_unit *unit);
+struct skill_unit_group *skill_initunitgroup(struct block_list *src,
+ int count,int skillid,int skilllv,int unit_id);
+int skill_delunitgroup(struct skill_unit_group *group);
+int skill_clear_unitgroup(struct block_list *src);
+
+int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
+ int damage,unsigned int tick);
+
+int skill_castfix( struct block_list *bl, int skill_id, int skill_lv, int time);
+int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv, int time);
+int skill_check_unit_range(int m,int x,int y,int skillid, int skilllv);
+int skill_check_unit_range2(struct block_list *bl,int m,int x,int y,int skillid, int skilllv);
+// -- moonsoul (added skill_check_unit_cell)
+int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id);
+int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range);
+int skill_unit_move(struct block_list *bl,unsigned int tick,int flag);
+int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy);
+void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag);
+
+struct skill_unit_group *skill_check_dancing( struct block_list *src );
+void skill_stop_dancing(struct block_list *src);
+
+// Guild skills [celest]
+int skill_guildaura_sub (struct block_list *bl,va_list ap);
+
+// ‰r¥ƒLƒƒƒ“ƒZƒ‹
+int skill_castcancel(struct block_list *bl,int type);
+
+int skill_gangsterparadise(struct map_session_data *sd ,int type);
+int skill_rest(struct map_session_data *sd ,int type);
+void skill_brandishspear_first(struct square *tc,int dir,int x,int y);
+void skill_brandishspear_dir(struct square *tc,int dir,int are);
+void skill_repairweapon(struct map_session_data *sd, int idx);
+void skill_identify(struct map_session_data *sd,int idx);
+void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest]
+int skill_autospell(struct map_session_data *md,int skillid);
+
+#define skill_calc_heal(bl,skill_lv) (( status_get_lv(bl)+status_get_int(bl) )/8 *(4+ skill_lv*8))
+
+// ‚»‚Ì‘¼
+int skill_check_cloaking(struct block_list *bl);
+
+// ƒXƒe?ƒ^ƒXˆÙí
+int skill_enchant_elemental_end(struct block_list *bl, int type);
+int skillnotok(int skillid, struct map_session_data *sd);
+
+// ƒAƒCƒeƒ€ì¬
+int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty);
+int skill_produce_mix( struct map_session_data *sd,
+ int skill_id, int nameid, int slot1, int slot2, int slot3, int qty );
+
+int skill_arrow_create( struct map_session_data *sd,int nameid);
+
+// mobƒXƒLƒ‹‚Ì‚½‚ß
+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);
+
+// ƒXƒLƒ‹U?ˆêŠ‡?—
+int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
+ struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+
+void skill_reload(void);
+
+enum {
+ ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST,
+ ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER,
+};
+
+enum {
+ NV_BASIC = 1,
+
+ SM_SWORD,
+ SM_TWOHAND,
+ SM_RECOVERY,
+ SM_BASH,
+ SM_PROVOKE,
+ SM_MAGNUM,
+ SM_ENDURE,
+
+ MG_SRECOVERY,
+ MG_SIGHT,
+ MG_NAPALMBEAT,
+ MG_SAFETYWALL,
+ MG_SOULSTRIKE,
+ MG_COLDBOLT,
+ MG_FROSTDIVER,
+ MG_STONECURSE,
+ MG_FIREBALL,
+ MG_FIREWALL,
+ MG_FIREBOLT,
+ MG_LIGHTNINGBOLT,
+ MG_THUNDERSTORM,
+
+ AL_DP,
+ AL_DEMONBANE,
+ AL_RUWACH,
+ AL_PNEUMA,
+ AL_TELEPORT,
+ AL_WARP,
+ AL_HEAL,
+ AL_INCAGI,
+ AL_DECAGI,
+ AL_HOLYWATER,
+ AL_CRUCIS,
+ AL_ANGELUS,
+ AL_BLESSING,
+ AL_CURE,
+
+ MC_INCCARRY,
+ MC_DISCOUNT,
+ MC_OVERCHARGE,
+ MC_PUSHCART,
+ MC_IDENTIFY,
+ MC_VENDING,
+ MC_MAMMONITE,
+
+ AC_OWL,
+ AC_VULTURE,
+ AC_CONCENTRATION,
+ AC_DOUBLE,
+ AC_SHOWER,
+
+ TF_DOUBLE,
+ TF_MISS,
+ TF_STEAL,
+ TF_HIDING,
+ TF_POISON,
+ TF_DETOXIFY,
+
+ ALL_RESURRECTION,
+
+ KN_SPEARMASTERY,
+ KN_PIERCE,
+ KN_BRANDISHSPEAR,
+ KN_SPEARSTAB,
+ KN_SPEARBOOMERANG,
+ KN_TWOHANDQUICKEN,
+ KN_AUTOCOUNTER,
+ KN_BOWLINGBASH,
+ KN_RIDING,
+ KN_CAVALIERMASTERY,
+
+ PR_MACEMASTERY,
+ PR_IMPOSITIO,
+ PR_SUFFRAGIUM,
+ PR_ASPERSIO,
+ PR_BENEDICTIO,
+ PR_SANCTUARY,
+ PR_SLOWPOISON,
+ PR_STRECOVERY,
+ PR_KYRIE,
+ PR_MAGNIFICAT,
+ PR_GLORIA,
+ PR_LEXDIVINA,
+ PR_TURNUNDEAD,
+ PR_LEXAETERNA,
+ PR_MAGNUS,
+
+ WZ_FIREPILLAR,
+ WZ_SIGHTRASHER,
+ WZ_FIREIVY,
+ WZ_METEOR,
+ WZ_JUPITEL,
+ WZ_VERMILION,
+ WZ_WATERBALL,
+ WZ_ICEWALL,
+ WZ_FROSTNOVA,
+ WZ_STORMGUST,
+ WZ_EARTHSPIKE,
+ WZ_HEAVENDRIVE,
+ WZ_QUAGMIRE,
+ WZ_ESTIMATION,
+
+ BS_IRON,
+ BS_STEEL,
+ BS_ENCHANTEDSTONE,
+ BS_ORIDEOCON,
+ BS_DAGGER,
+ BS_SWORD,
+ BS_TWOHANDSWORD,
+ BS_AXE,
+ BS_MACE,
+ BS_KNUCKLE,
+ BS_SPEAR,
+ BS_HILTBINDING,
+ BS_FINDINGORE,
+ BS_WEAPONRESEARCH,
+ BS_REPAIRWEAPON,
+ BS_SKINTEMPER,
+ BS_HAMMERFALL,
+ BS_ADRENALINE,
+ BS_WEAPONPERFECT,
+ BS_OVERTHRUST,
+ BS_MAXIMIZE,
+
+ HT_SKIDTRAP,
+ HT_LANDMINE,
+ HT_ANKLESNARE,
+ HT_SHOCKWAVE,
+ HT_SANDMAN,
+ HT_FLASHER,
+ HT_FREEZINGTRAP,
+ HT_BLASTMINE,
+ HT_CLAYMORETRAP,
+ HT_REMOVETRAP,
+ HT_TALKIEBOX,
+ HT_BEASTBANE,
+ HT_FALCON,
+ HT_STEELCROW,
+ HT_BLITZBEAT,
+ HT_DETECTING,
+ HT_SPRINGTRAP,
+
+ AS_RIGHT,
+ AS_LEFT,
+ AS_KATAR,
+ AS_CLOAKING,
+ AS_SONICBLOW,
+ AS_GRIMTOOTH,
+ AS_ENCHANTPOISON,
+ AS_POISONREACT,
+ AS_VENOMDUST,
+ AS_SPLASHER,
+
+ NV_FIRSTAID,
+ NV_TRICKDEAD,
+ SM_MOVINGRECOVERY,
+ SM_FATALBLOW,
+ SM_AUTOBERSERK,
+ AC_MAKINGARROW,
+ AC_CHARGEARROW,
+ TF_SPRINKLESAND,
+ TF_BACKSLIDING,
+ TF_PICKSTONE,
+ TF_THROWSTONE,
+ MC_CARTREVOLUTION,
+ MC_CHANGECART,
+ MC_LOUD,
+ AL_HOLYLIGHT,
+ MG_ENERGYCOAT,
+
+ NPC_PIERCINGATT,
+ NPC_MENTALBREAKER,
+ NPC_RANGEATTACK,
+ NPC_ATTRICHANGE,
+ NPC_CHANGEWATER,
+ NPC_CHANGEGROUND,
+ NPC_CHANGEFIRE,
+ NPC_CHANGEWIND,
+ NPC_CHANGEPOISON,
+ NPC_CHANGEHOLY,
+ NPC_CHANGEDARKNESS,
+ NPC_CHANGETELEKINESIS,
+ NPC_CRITICALSLASH,
+ NPC_COMBOATTACK,
+ NPC_GUIDEDATTACK,
+ NPC_SELFDESTRUCTION,
+ NPC_SPLASHATTACK,
+ NPC_SUICIDE,
+ NPC_POISON,
+ NPC_BLINDATTACK,
+ NPC_SILENCEATTACK,
+ NPC_STUNATTACK,
+ NPC_PETRIFYATTACK,
+ NPC_CURSEATTACK,
+ NPC_SLEEPATTACK,
+ NPC_RANDOMATTACK,
+ NPC_WATERATTACK,
+ NPC_GROUNDATTACK,
+ NPC_FIREATTACK,
+ NPC_WINDATTACK,
+ NPC_POISONATTACK,
+ NPC_HOLYATTACK,
+ NPC_DARKNESSATTACK,
+ NPC_TELEKINESISATTACK,
+ NPC_MAGICALATTACK,
+ NPC_METAMORPHOSIS,
+ NPC_PROVOCATION,
+ NPC_SMOKING,
+ NPC_SUMMONSLAVE,
+ NPC_EMOTION,
+ NPC_TRANSFORMATION,
+ NPC_BLOODDRAIN,
+ NPC_ENERGYDRAIN,
+ NPC_KEEPING,
+ NPC_DARKBREATH,
+ NPC_DARKBLESSING,
+ NPC_BARRIER,
+ NPC_DEFENDER,
+ NPC_LICK,
+ NPC_HALLUCINATION,
+ NPC_REBIRTH,
+ NPC_SUMMONMONSTER,
+
+ RG_SNATCHER,
+ RG_STEALCOIN,
+ RG_BACKSTAP,
+ RG_TUNNELDRIVE,
+ RG_RAID,
+ RG_STRIPWEAPON,
+ RG_STRIPSHIELD,
+ RG_STRIPARMOR,
+ RG_STRIPHELM,
+ RG_INTIMIDATE,
+ RG_GRAFFITI,
+ RG_FLAGGRAFFITI,
+ RG_CLEANER,
+ RG_GANGSTER,
+ RG_COMPULSION,
+ RG_PLAGIARISM,
+
+ AM_AXEMASTERY,
+ AM_LEARNINGPOTION,
+ AM_PHARMACY,
+ AM_DEMONSTRATION,
+ AM_ACIDTERROR,
+ AM_POTIONPITCHER,
+ AM_CANNIBALIZE,
+ AM_SPHEREMINE,
+ AM_CP_WEAPON,
+ AM_CP_SHIELD,
+ AM_CP_ARMOR,
+ AM_CP_HELM,
+ AM_BIOETHICS,
+ AM_BIOTECHNOLOGY,
+ AM_CREATECREATURE,
+ AM_CULTIVATION,
+ AM_FLAMECONTROL,
+ AM_CALLHOMUN,
+ AM_REST,
+ AM_DRILLMASTER,
+ AM_HEALHOMUN,
+ AM_RESURRECTHOMUN,
+
+ CR_TRUST,
+ CR_AUTOGUARD,
+ CR_SHIELDCHARGE,
+ CR_SHIELDBOOMERANG,
+ CR_REFLECTSHIELD,
+ CR_HOLYCROSS,
+ CR_GRANDCROSS,
+ CR_DEVOTION,
+ CR_PROVIDENCE,
+ CR_DEFENDER,
+ CR_SPEARQUICKEN,
+
+ MO_IRONHAND,
+ MO_SPIRITSRECOVERY,
+ MO_CALLSPIRITS,
+ MO_ABSORBSPIRITS,
+ MO_TRIPLEATTACK,
+ MO_BODYRELOCATION,
+ MO_DODGE,
+ MO_INVESTIGATE,
+ MO_FINGEROFFENSIVE,
+ MO_STEELBODY,
+ MO_BLADESTOP,
+ MO_EXPLOSIONSPIRITS,
+ MO_EXTREMITYFIST,
+ MO_CHAINCOMBO,
+ MO_COMBOFINISH,
+
+ SA_ADVANCEDBOOK,
+ SA_CASTCANCEL,
+ SA_MAGICROD,
+ SA_SPELLBREAKER,
+ SA_FREECAST,
+ SA_AUTOSPELL,
+ SA_FLAMELAUNCHER,
+ SA_FROSTWEAPON,
+ SA_LIGHTNINGLOADER,
+ SA_SEISMICWEAPON,
+ SA_DRAGONOLOGY,
+ SA_VOLCANO,
+ SA_DELUGE,
+ SA_VIOLENTGALE,
+ SA_LANDPROTECTOR,
+ SA_DISPELL,
+ SA_ABRACADABRA,
+ SA_MONOCELL,
+ SA_CLASSCHANGE,
+ SA_SUMMONMONSTER,
+ SA_REVERSEORCISH,
+ SA_DEATH,
+ SA_FORTUNE,
+ SA_TAMINGMONSTER,
+ SA_QUESTION,
+ SA_GRAVITY,
+ SA_LEVELUP,
+ SA_INSTANTDEATH,
+ SA_FULLRECOVERY,
+ SA_COMA,
+
+ BD_ADAPTATION,
+ BD_ENCORE,
+ BD_LULLABY,
+ BD_RICHMANKIM,
+ BD_ETERNALCHAOS,
+ BD_DRUMBATTLEFIELD,
+ BD_RINGNIBELUNGEN,
+ BD_ROKISWEIL,
+ BD_INTOABYSS,
+ BD_SIEGFRIED,
+ BD_RAGNAROK,
+
+ BA_MUSICALLESSON,
+ BA_MUSICALSTRIKE,
+ BA_DISSONANCE,
+ BA_FROSTJOKE,
+ BA_WHISTLE,
+ BA_ASSASSINCROSS,
+ BA_POEMBRAGI,
+ BA_APPLEIDUN,
+
+ DC_DANCINGLESSON,
+ DC_THROWARROW,
+ DC_UGLYDANCE,
+ DC_SCREAM,
+ DC_HUMMING,
+ DC_DONTFORGETME,
+ DC_FORTUNEKISS,
+ DC_SERVICEFORYOU,
+
+ NPC_RANDOMMOVE,
+ NPC_SPEEDUP,
+ NPC_REVENGE,
+
+ WE_MALE,
+ WE_FEMALE,
+ WE_CALLPARTNER,
+
+ ITM_TOMAHAWK,
+
+ NPC_DARKCROSS,
+ NPC_GRANDDARKNESS,
+ NPC_DARKSTRIKE,
+ NPC_DARKTHUNDER,
+ NPC_STOP,
+ NPC_BREAKWEAPON,
+ NPC_BREAKARMOR,
+ NPC_BREAKHELM,
+ NPC_BREAKSHIELD,
+ NPC_UNDEADATTACK,
+ NPC_CHANGEUNDEAD,
+ NPC_POWERUP,
+ NPC_AGIUP,
+ NPC_SIEGEMODE,
+ NPC_CALLSLAVE,
+ NPC_INVISIBLE,
+ NPC_RUN,
+
+ LK_AURABLADE,
+ LK_PARRYING,
+ LK_CONCENTRATION,
+ LK_TENSIONRELAX,
+ LK_BERSERK,
+ LK_FURY,
+ HP_ASSUMPTIO,
+ HP_BASILICA,
+ HP_MEDITATIO,
+ HW_SOULDRAIN,
+ HW_MAGICCRASHER,
+ HW_MAGICPOWER,
+ PA_PRESSURE,
+ PA_SACRIFICE,
+ PA_GOSPEL,
+ CH_PALMSTRIKE,
+ CH_TIGERFIST,
+ CH_CHAINCRUSH,
+ PF_HPCONVERSION,
+ PF_SOULCHANGE,
+ PF_SOULBURN,
+ ASC_KATAR,
+ ASC_HALLUCINATION,
+ ASC_EDP,
+ ASC_BREAKER,
+ SN_SIGHT,
+ SN_FALCONASSAULT,
+ SN_SHARPSHOOTING,
+ SN_WINDWALK,
+ WS_MELTDOWN,
+ WS_CREATECOIN,
+ WS_CREATENUGGET,
+ WS_CARTBOOST,
+ WS_SYSTEMCREATE,
+ ST_CHASEWALK,
+ ST_REJECTSWORD,
+ ST_STEALBACKPACK,
+ CR_ALCHEMY,
+ CR_SYNTHESISPOTION,
+ CG_ARROWVULCAN,
+ CG_MOONLIT,
+ CG_MARIONETTE,
+ LK_SPIRALPIERCE,
+ LK_HEADCRUSH,
+ LK_JOINTBEAT,
+ HW_NAPALMVULCAN,
+ CH_SOULCOLLECT,
+ PF_MINDBREAKER,
+ PF_MEMORIZE,
+ PF_FOGWALL,
+ PF_SPIDERWEB,
+ ASC_METEORASSAULT,
+ ASC_CDP,
+ WE_BABY,
+ WE_CALLPARENT,
+ WE_CALLBABY,
+
+ TK_RUN,
+ TK_READYSTORM,
+ TK_STORMKICK,
+ TK_READYDOWN,
+ TK_DOWNKICK,
+ TK_READYTURN,
+ TK_TURNKICK,
+ TK_READYCOUNTER,
+ TK_COUNTER,
+ TK_DODGE,
+ TK_JUMPKICK,
+ TK_HPTIME,
+ TK_SPTIME,
+ TK_POWER,
+ TK_SEVENWIND,
+ TK_HIGHJUMP,
+ SG_FEEL,
+ SG_SUN_WARM,
+ SG_MOON_WARM,
+ SG_STAR_WARM,
+ SG_SUN_COMFORT,
+ SG_MOON_COMFORT,
+ SG_STAR_COMFORT,
+ SG_HATE,
+ SG_SUN_ANGER,
+ SG_MOON_ANGER,
+ SG_STAR_ANGER,
+ SG_SUN_BLESS,
+ SG_MOON_BLESS,
+ SG_STAR_BLESS,
+ SG_DEVIL,
+ SG_FRIEND,
+ SG_KNOWLEDGE,
+ SG_FUSION,
+ SL_ALCHEMIST,
+ AM_BERSERKPITCHER,
+ SL_MONK,
+ SL_STAR,
+ SL_SAGE,
+ SL_CRUSADER,
+ SL_SUPERNOVICE,
+ SL_KNIGHT,
+ SL_WIZARD,
+ SL_PRIEST,
+ SL_BARDDANCER,
+ SL_ROGUE,
+ SL_ASSASIN,
+ SL_BLACKSMITH,
+ BS_ADRENALINE2,
+ SL_HUNTER,
+ SL_SOULLINKER,
+ SL_KAIZEL,
+ SL_KAAHI,
+ SL_KAUPE,
+ SL_KAITE,
+ SL_KAINA,
+ SL_STIN,
+ SL_STUN,
+ SL_SMA,
+ SL_SWOO,
+ SL_SKE,
+ SL_SKA,
+
+ SM_SELFPROVOKE,
+ NPC_EMOTION_ON,
+ ST_PRESERVE,
+ ST_FULLSTRIP,
+ WS_WEAPONREFINE,
+ CR_SLIMPITCHER,
+ CR_FULLPROTECTION,
+ PA_SHIELDCHAIN,
+ HP_MANARECHARGE,
+ PF_DOUBLECASTING,
+ HW_GANBANTEIN,
+ HW_GRAVITATION,
+ WS_CARTTERMINATION,
+ WS_OVERTHRUSTMAX,
+ CG_LONGINGFREEDOM,
+ CG_HERMODE,
+ CG_TAROTCARD,
+ CR_ACIDDEMONSTRATION,
+ CR_CULTIVATION,
+ //492,missing?
+ TK_MISSION = 493,
+ SL_HIGH,
+ KN_ONEHAND,
+ AM_TWILIGHT1,
+ AM_TWILIGHT2,
+ AM_TWILIGHT3,
+ HT_POWER,
+
+ KN_CHARGEATK = 1001,
+ CR_SHRINK,
+ AS_SONICACCEL,
+ AS_VENOMKNIFE,
+ RG_CLOSECONFINE,
+ WZ_SIGHTBLASTER,
+ SA_CREATECON,
+ SA_ELEMENTWATER,
+ HT_PHANTASMIC,
+ BA_PANGVOICE,
+ DC_WINKCHARM,
+ BS_UNFAIRLYTRICK,
+ BS_GREED,
+ PR_REDEMPTIO,
+ MO_KITRANSLATION,
+ MO_BALKYOUNG,
+ SA_ELEMENTGROUND,
+ SA_ELEMENTFIRE,
+ SA_ELEMENTWIND,
+
+ HLIF_HEAL = 8001,
+ HLIF_AVOID,
+ HLIF_BRAIN,
+ HLIF_CHANGE,
+ HAMI_CASTLE,
+ HAMI_DEFENCE,
+ HAMI_SKIN,
+ HAMI_BLOODLUST,
+ HFLI_MOON,
+ HFLI_FLEET,
+ HFLI_SPEED,
+ HFLI_SBR44,
+ HVAN_CAPRICE,
+ HVAN_CHAOTIC,
+ HVAN_INSTRUCT,
+ HVAN_EXPLOSION,
+};
+
+enum {
+ UNT_SAFETYWALL = 0x7e,
+ UNT_FIREWALL,
+ UNT_WARP_WAITING,
+ UNT_WARP_ACTIVE,
+
+ UNT_SANCTUARY = 0x83,
+ UNT_MAGNUS,
+ UNT_PNEUMA,
+ UNT_MAGIC_SKILLS,
+ UNT_FIREPILLAR_WAITING,
+ UNT_FIREPILLAR_ACTIVE,
+
+ UNT_USED_TRAPS = 0x8c,
+ UNT_ICEWALL,
+ UNT_QUAGMIRE,
+ UNT_BLASTMINE,
+ UNT_SKIDTRAP,
+ UNT_ANKLESNARE,
+ UNT_VENOMDUST,
+ UNT_LANDMINE,
+ UNT_SHOCKWAVE,
+ UNT_SANDMAN,
+ UNT_FLASHER,
+ UNT_FREEZINGTRAP,
+ UNT_CLAYMORETRAP,
+ UNT_TALKIEBOX,
+ UNT_VOLCANO,
+ UNT_DELUGE,
+ UNT_VIOLENTGALE,
+ UNT_LANDPROTECTOR,
+ UNT_LULLABY,
+ UNT_RICHMANKIM,
+ UNT_ETERNALCHAOS,
+ UNT_DRUMBATTLEFIELD,
+ UNT_RINGNIBELUNGEN,
+ UNT_ROKISWEIL,
+ UNT_INTOABYSS,
+ UNT_SIEGFRIED,
+ UNT_DISSONANCE,
+ UNT_WHISTLE,
+ UNT_ASSASSINCROSS,
+ UNT_POEMBRAGI,
+ UNT_APPLEIDUN,
+ UNT_UGLYDANCE,
+ UNT_HUMMING,
+ UNT_DONTFORGETME,
+ UNT_FORTUNEKISS,
+ UNT_SERVICEFORYOU,
+ UNT_GRAFFITI,
+ UNT_DEMONSTRATION,
+ UNT_CALLPARTNER,
+ UNT_GOSPEL,
+ UNT_BASILICA,
+
+ UNT_FOGWALL = 0xb6,
+ UNT_SPIDERWEB,
+ UNT_GRAVITATION,
+ UNT_HERMODE,
+};
+
+#endif
diff --git a/src/map/status.c b/src/map/status.c
new file mode 100644
index 000000000..28896aceb
--- /dev/null
+++ b/src/map/status.c
@@ -0,0 +1,6038 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <time.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+
+#include "pc.h"
+#include "map.h"
+#include "pet.h"
+#include "mob.h"
+#include "clif.h"
+#include "guild.h"
+#include "skill.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "chrif.h"
+#include "status.h"
+
+#include "timer.h"
+#include "nullpo.h"
+#include "script.h"
+#include "showmsg.h"
+
+int SkillStatusChangeTable[]={
+ -1,-1,-1,-1,-1,-1,
+ SC_PROVOKE,
+ SC_WATK_ELEMENT, //Adds part of your final attack as elemental damage. [Skotlex]
+ SC_ENDURE,
+ -1,
+/* 10- */
+ SC_SIGHT, /* ƒTƒCƒg */
+ -1,
+ SC_SAFETYWALL, /* ƒZ[ƒtƒeƒB[ƒEƒH[ƒ‹ */
+ -1,-1,-1,
+ SC_FREEZE, /* ƒtƒƒXƒgƒ_ƒCƒo? */
+ SC_STONE, /* ƒXƒg?ƒ“ƒJ?ƒX */
+ -1,-1,
+/* 20- */
+ -1,-1,-1,-1,
+ SC_RUWACH, /* ƒ‹ƒAƒt */
+ -1,//SC_PNEUMA, Pneuma is no longer a status change. It is a cell type.
+ -1,-1,-1,
+ SC_INCREASEAGI, /* ‘¬“x?‰Á */
+/* 30- */
+ SC_DECREASEAGI, /* ‘¬“xŒ¸­ */
+ -1,
+ SC_SIGNUMCRUCIS, /* ƒVƒOƒiƒ€ƒNƒ‹ƒVƒX */
+ SC_ANGELUS, /* ƒGƒ“ƒWƒFƒ‰ƒX */
+ SC_BLESSING, /* ƒuƒŒƒbƒVƒ“ƒO */
+ -1,-1,-1,-1,-1,
+/* 40- */
+ -1,-1,-1,-1,-1,
+ SC_CONCENTRATE, /* W’†—ÍŒüã */
+ -1,-1,-1,-1,
+/* 50- */
+ -1,
+ SC_HIDING, /* ƒnƒCƒfƒBƒ“ƒO */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+/* 60- */
+ SC_TWOHANDQUICKEN, /* 2HQ */
+ SC_AUTOCOUNTER,
+ -1,-1,-1,-1,
+ SC_IMPOSITIO, /* ƒCƒ“ƒ|ƒVƒeƒBƒIƒ}ƒkƒX */
+ SC_SUFFRAGIUM, /* ƒTƒtƒ‰ƒMƒEƒ€ */
+ SC_ASPERSIO, /* ƒAƒXƒyƒ‹ƒVƒI */
+ SC_BENEDICTIO, /* ¹?~•Ÿ */
+/* 70- */
+ -1,
+ SC_SLOWPOISON,
+ -1,
+ SC_KYRIE, /* ƒLƒŠƒGƒGƒŒƒCƒ\ƒ“ */
+ SC_MAGNIFICAT, /* ƒ}ƒOƒjƒtƒBƒJ?ƒg */
+ SC_GLORIA, /* ƒOƒƒŠƒA */
+ SC_SILENCE, /* ƒŒƒbƒNƒXƒfƒBƒr?ƒi */
+ -1,
+ SC_AETERNA, /* ƒŒƒbƒNƒXƒG?ƒeƒ‹ƒi */
+ -1,
+/* 80- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 90- */
+ -1,-1,
+ SC_QUAGMIRE, /* ƒNƒ@ƒOƒ}ƒCƒA */
+ -1,-1,-1,-1,-1,-1,-1,
+/* 100- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 110- */
+ -1,
+ SC_ADRENALINE, /* ƒAƒhƒŒƒiƒŠƒ“ƒ‰ƒbƒVƒ… */
+ SC_WEAPONPERFECTION,/* ƒEƒFƒ|ƒ“ƒp?ƒtƒFƒNƒVƒ‡ƒ“ */
+ SC_OVERTHRUST, /* ƒI?ƒo?ƒgƒ‰ƒXƒg */
+ SC_MAXIMIZEPOWER, /* ƒ}ƒLƒVƒ}ƒCƒYƒpƒ? */
+ -1,-1,-1,-1,-1,
+/* 120- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 130- */
+ -1,-1,-1,-1,-1,
+ SC_CLOAKING, /* ƒNƒ?ƒLƒ“ƒO */
+ SC_STAN, /* ƒ\ƒjƒbƒNƒuƒ? */
+ -1,
+ SC_ENCPOISON, /* ƒGƒ“ƒ`ƒƒƒ“ƒgƒ|ƒCƒYƒ“ */
+ SC_POISONREACT, /* ƒ|ƒCƒYƒ“ƒŠƒAƒNƒg */
+/* 140- */
+ SC_POISON, /* ƒxƒmƒ€ƒ_ƒXƒg */
+ SC_SPLASHER, /* ƒxƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ? */
+ -1,
+ SC_TRICKDEAD, /* Ž€‚ñ‚¾‚Ó‚è */
+ -1,-1,
+ SC_AUTOBERSERK,
+ -1,-1,-1,
+/* 150- */
+ -1,-1,-1,-1,-1,
+ SC_LOUD, /* ƒ‰ƒEƒhƒ{ƒCƒX */
+ -1,
+ SC_ENERGYCOAT, /* ƒGƒiƒW?ƒR?ƒg */
+ -1,-1,
+/* 160- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 200 */
+ -1,
+ SC_KEEPING,
+ -1,
+ SC_COMA,
+ SC_BARRIER,
+ -1,
+ SC_STAN,
+ SC_HALLUCINATION,
+ -1,-1,
+/* 210- */
+ -1,-1,-1,-1,-1,
+ SC_STRIPWEAPON,
+ SC_STRIPSHIELD,
+ SC_STRIPARMOR,
+ SC_STRIPHELM,
+ -1,
+/* 220- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 230- */
+ -1,-1,-1,-1,
+ SC_CP_WEAPON,
+ SC_CP_SHIELD,
+ SC_CP_ARMOR,
+ SC_CP_HELM,
+ -1,-1,
+/* 240- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,
+ SC_AUTOGUARD,
+/* 250- */
+ -1,-1,
+ SC_REFLECTSHIELD,
+ -1,-1,
+ SC_DEVOTION,
+ SC_PROVIDENCE,
+ SC_DEFENDER,
+ SC_SPEARSQUICKEN,
+ -1,
+/* 260- */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+ SC_STEELBODY,
+ SC_BLADESTOP_WAIT,
+/* 270- */
+ SC_EXPLOSIONSPIRITS,
+ SC_EXTREMITYFIST,
+ -1,-1,-1,-1,
+ SC_MAGICROD,
+ -1,-1,-1,
+/* 280- */
+ SC_FIREWEAPON,
+ SC_WATERWEAPON,
+ SC_WINDWEAPON,
+ SC_EARTHWEAPON,
+ -1,
+ SC_VOLCANO,
+ SC_DELUGE,
+ SC_VIOLENTGALE,
+ SC_LANDPROTECTOR,
+ -1,
+/* 290- */
+ -1,-1,-1,-1,
+ SC_ORCISH,
+ -1,-1,-1,-1,-1,
+/* 300- */
+ -1,-1,-1,
+ SC_COMA,
+ -1,-1,
+ SC_LULLABY,
+ SC_RICHMANKIM,
+ SC_ETERNALCHAOS,
+ SC_DRUMBATTLE,
+/* 310- */
+ SC_NIBELUNGEN,
+ SC_ROKISWEIL,
+ SC_INTOABYSS,
+ SC_SIEGFRIED,
+ -1,-1,-1,-1,-1,
+ SC_WHISTLE,
+/* 320- */
+ SC_ASSNCROS,
+ SC_POEMBRAGI,
+ SC_APPLEIDUN,
+ -1,-1,
+ SC_UGLYDANCE,
+ -1,
+ SC_HUMMING,
+ SC_DONTFORGETME,
+ SC_FORTUNE,
+/* 330- */
+ SC_SERVICE4U,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 340- */
+ -1,-1,
+ SC_STOP,
+ -1,-1,-1,-1,-1,-1,-1,
+/* 350- */
+ -1,-1,-1,-1,-1,
+ SC_AURABLADE,
+ SC_PARRYING,
+ SC_CONCENTRATION,
+ SC_TENSIONRELAX,
+ SC_BERSERK,
+/* 360- */
+ -1,
+ SC_ASSUMPTIO,
+ SC_BASILICA,
+ -1,-1,-1,
+ SC_MAGICPOWER,
+ -1,
+ SC_SACRIFICE,
+ SC_GOSPEL,
+/* 370- */
+ -1,-1,-1,-1,-1,-1,-1,-1,
+ SC_EDP,
+ -1,
+/* 380- */
+ SC_TRUESIGHT,
+ -1,-1,
+ SC_WINDWALK,
+ SC_MELTDOWN,
+ -1,-1,
+ SC_CARTBOOST,
+ -1,
+ SC_CHASEWALK,
+/* 390- */
+ SC_REJECTSWORD,
+ -1,-1,-1,-1,
+ SC_MOONLIT,
+ SC_MARIONETTE,
+ -1,
+ SC_BLEEDING,
+ SC_JOINTBEAT,
+/* 400 */
+ -1,-1,
+ SC_MINDBREAKER,
+ SC_MEMORIZE,
+ SC_FOGWALL,
+ SC_SPIDERWEB,
+ -1,-1,
+ SC_BABY,
+ -1,
+/* 410- */
+ -1,
+ SC_RUN,
+ SC_READYSTORM,
+ -1,
+ SC_READYDOWN,
+ -1,
+ SC_READYTURN,
+ -1,
+ SC_READYCOUNTER,
+ -1,
+/* 420- */
+ SC_DODGE,
+ -1,-1,
+ SC_TKDORI,
+ -1,-1,-1,-1,
+ SC_WARM,
+ SC_WARM,
+/* 430- */
+ SC_WARM,
+ SC_SUN_COMFORT,
+ SC_MOON_COMFORT,
+ SC_STAR_COMFORT,
+ -1,-1,-1,-1,-1,-1,
+/* 440- */
+ -1,-1,-1,-1,
+ SC_FUSION,
+ MAPID_ALCHEMIST, // Storing the target job rather than simply SC_SPIRIT simplifies code later on.
+ -1,
+ MAPID_MONK,
+ MAPID_STAR_GLADIATOR,
+ MAPID_SAGE,
+/* 450- */
+ MAPID_CRUSADER,
+ MAPID_SUPER_NOVICE,
+ MAPID_KNIGHT,
+ MAPID_WIZARD,
+ MAPID_PRIEST,
+ MAPID_BARDDANCER,
+ MAPID_ROGUE,
+ MAPID_ASSASSIN,
+ MAPID_BLACKSMITH,
+ SC_ADRENALINE2,
+/* 460- */
+ MAPID_HUNTER,
+ MAPID_SOUL_LINKER,
+ SC_KAIZEL,
+ SC_KAAHI,
+ SC_KAUPE,
+ SC_KAITE,
+ -1,-1,-1,-1,
+/* 470- */
+ SC_SWOO, // [marquis007]
+ SC_SKE,
+ SC_SKA, // [marquis007]
+ -1,-1,
+ SC_PRESERVE,
+ -1,-1,-1,-1,
+/* 480- */
+ -1,-1,
+ SC_DOUBLECAST,
+ -1,
+ SC_GRAVITATION,
+ -1,
+ SC_MAXOVERTHRUST,
+ SC_LONGING,
+ SC_HERMODE,
+ -1,
+/* 490- */
+ -1,-1,-1,-1,
+ SC_SPIRIT,
+ SC_ONEHAND,
+ -1,-1,-1,-1,
+/* 500- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 510- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 520- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 530- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 540- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 550- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 560- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 570- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 580- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 590- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 600- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 610- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 620- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 630- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 640- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 650- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 660- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 670- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 680- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 690- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 700- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 710- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 720- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 730- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 740- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 750- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 760- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 770- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 780- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 790- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 800- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 810- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 820- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 830- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 840- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 850- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 860- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 870- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 880- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 890- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 900- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 910- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 920- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 930- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 940- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 950- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 960- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 970- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 980- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 990- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1000- */
+ -1,-1,
+ SC_SHRINK,
+ -1,-1,
+ SC_CLOSECONFINE2,
+ SC_SIGHTBLASTER,
+ -1,-1,-1,
+/* 1010- */
+ -1,
+ SC_WINKCHARM,
+ -1,-1,
+ SC_COMA,
+ -1,-1,-1,-1,-1,
+/* 1020- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1030- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1040- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1050- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1060- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1070- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1080- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/* 1090- */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+};
+
+int StatusIconChangeTable[SC_MAX];
+
+static int max_weight_base[MAX_PC_CLASS];
+static int hp_coefficient[MAX_PC_CLASS];
+static int hp_coefficient2[MAX_PC_CLASS];
+static int hp_sigma_val[MAX_PC_CLASS][MAX_LEVEL];
+static int sp_coefficient[MAX_PC_CLASS];
+static int aspd_base[MAX_PC_CLASS][20];
+#define MAX_REFINE_BONUS 5
+static int refinebonus[MAX_REFINE_BONUS][3]; // ¸˜Bƒ{[ƒiƒXƒe[ƒuƒ‹(refine_db.txt)
+int percentrefinery[5][MAX_REFINE+1]; // ¸˜B¬Œ÷—¦(refine_db.txt)
+static int atkmods[3][20]; // •ŠíATKƒTƒCƒYC³(size_fix.txt)
+static char job_bonus[MAX_PC_CLASS][MAX_LEVEL];
+
+int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
+//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
+//to avoid cards exploits
+
+//Initializes the StatusIconChangeTable variable. May seem somewhat slower than directly defining the array,
+//but it is much less prone to errors. [Skotlex]
+void initStatusIconChangeTable(void) {
+ int i;
+ for (i = 0; i < SC_MAX; i++)
+ StatusIconChangeTable[i] = SI_BLANK;
+
+ StatusIconChangeTable[SC_PROVOKE] = SI_PROVOKE;
+ StatusIconChangeTable[SC_ENDURE] = SI_ENDURE;
+ StatusIconChangeTable[SC_TWOHANDQUICKEN] = SI_TWOHANDQUICKEN;
+ StatusIconChangeTable[SC_CONCENTRATE] = SI_CONCENTRATE;
+ StatusIconChangeTable[SC_HIDING] = SI_HIDING;
+ StatusIconChangeTable[SC_CLOAKING] = SI_CLOAKING;
+ StatusIconChangeTable[SC_ENCPOISON] = SI_ENCPOISON;
+ StatusIconChangeTable[SC_POISONREACT] = SI_POISONREACT;
+ StatusIconChangeTable[SC_QUAGMIRE] = SI_QUAGMIRE;
+ StatusIconChangeTable[SC_ANGELUS] = SI_ANGELUS;
+ StatusIconChangeTable[SC_BLESSING] = SI_BLESSING;
+ StatusIconChangeTable[SC_SIGNUMCRUCIS] = SI_SIGNUMCRUCIS;
+ StatusIconChangeTable[SC_INCREASEAGI] = SI_INCREASEAGI;
+ StatusIconChangeTable[SC_DECREASEAGI] = SI_DECREASEAGI;
+ StatusIconChangeTable[SC_SLOWPOISON] = SI_SLOWPOISON;
+ StatusIconChangeTable[SC_IMPOSITIO] = SI_IMPOSITIO;
+ StatusIconChangeTable[SC_SUFFRAGIUM] = SI_SUFFRAGIUM;
+ StatusIconChangeTable[SC_ASPERSIO] = SI_ASPERSIO;
+ StatusIconChangeTable[SC_BENEDICTIO] = SI_BENEDICTIO;
+ StatusIconChangeTable[SC_KYRIE] = SI_KYRIE;
+ StatusIconChangeTable[SC_MAGNIFICAT] = SI_MAGNIFICAT;
+ StatusIconChangeTable[SC_GLORIA] = SI_GLORIA;
+ StatusIconChangeTable[SC_AETERNA] = SI_AETERNA;
+ StatusIconChangeTable[SC_ADRENALINE] = SI_ADRENALINE;
+ StatusIconChangeTable[SC_WEAPONPERFECTION] = SI_WEAPONPERFECTION;
+ StatusIconChangeTable[SC_OVERTHRUST] = SI_OVERTHRUST;
+ StatusIconChangeTable[SC_MAXIMIZEPOWER] = SI_MAXIMIZEPOWER;
+ StatusIconChangeTable[SC_TRICKDEAD] = SI_TRICKDEAD;
+ StatusIconChangeTable[SC_LOUD] = SI_LOUD;
+ StatusIconChangeTable[SC_ENERGYCOAT] = SI_ENERGYCOAT;
+ StatusIconChangeTable[SC_BROKENARMOR] = SI_BROKENARMOR;
+ StatusIconChangeTable[SC_BROKENWEAPON] = SI_BROKENWEAPON;
+ StatusIconChangeTable[SC_HALLUCINATION] = SI_HALLUCINATION;
+ StatusIconChangeTable[SC_WEIGHT50 ] = SI_WEIGHT50;
+ StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90;
+ StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTION;
+ StatusIconChangeTable[SC_SPEEDUP0] = SI_SPEEDPOTION;
+ StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION;
+ StatusIconChangeTable[SC_STRIPWEAPON] = SI_STRIPWEAPON;
+ StatusIconChangeTable[SC_STRIPSHIELD] = SI_STRIPSHIELD;
+ StatusIconChangeTable[SC_STRIPARMOR] = SI_STRIPARMOR;
+ StatusIconChangeTable[SC_STRIPHELM] = SI_STRIPHELM;
+ StatusIconChangeTable[SC_CP_WEAPON] = SI_CP_WEAPON;
+ StatusIconChangeTable[SC_CP_SHIELD] = SI_CP_SHIELD;
+ StatusIconChangeTable[SC_CP_ARMOR] = SI_CP_ARMOR;
+ StatusIconChangeTable[SC_CP_HELM] = SI_CP_HELM;
+ StatusIconChangeTable[SC_AUTOGUARD] = SI_AUTOGUARD;
+ StatusIconChangeTable[SC_REFLECTSHIELD] = SI_REFLECTSHIELD;
+ StatusIconChangeTable[SC_PROVIDENCE] = SI_PROVIDENCE;
+ StatusIconChangeTable[SC_DEFENDER] = SI_DEFENDER;
+ StatusIconChangeTable[SC_AUTOSPELL] = SI_AUTOSPELL;
+ StatusIconChangeTable[SC_SPEARSQUICKEN] = SI_SPEARQUICKEN;
+ StatusIconChangeTable[SC_EXPLOSIONSPIRITS] = SI_EXPLOSIONSPIRITS;
+ StatusIconChangeTable[SC_FURY] = SI_FURY;
+ StatusIconChangeTable[SC_FIREWEAPON] = SI_FIREWEAPON;
+ StatusIconChangeTable[SC_WATERWEAPON] = SI_WATERWEAPON;
+ StatusIconChangeTable[SC_WINDWEAPON] = SI_WINDWEAPON;
+ StatusIconChangeTable[SC_EARTHWEAPON] = SI_EARTHWEAPON;
+ StatusIconChangeTable[SC_AURABLADE] = SI_AURABLADE;
+ StatusIconChangeTable[SC_PARRYING] = SI_PARRYING;
+ StatusIconChangeTable[SC_CONCENTRATION] = SI_CONCENTRATION;
+ StatusIconChangeTable[SC_TENSIONRELAX] = SI_TENSIONRELAX;
+ StatusIconChangeTable[SC_BERSERK] = SI_BERSERK;
+ StatusIconChangeTable[SC_ASSUMPTIO] = SI_ASSUMPTIO;
+ StatusIconChangeTable[SC_GUILDAURA] = SI_GUILDAURA;
+ StatusIconChangeTable[SC_MAGICPOWER] = SI_MAGICPOWER;
+ StatusIconChangeTable[SC_EDP] = SI_EDP;
+ StatusIconChangeTable[SC_TRUESIGHT] = SI_TRUESIGHT;
+ StatusIconChangeTable[SC_WINDWALK] = SI_WINDWALK;
+ StatusIconChangeTable[SC_MELTDOWN] = SI_MELTDOWN;
+ StatusIconChangeTable[SC_CARTBOOST] = SI_CARTBOOST;
+ StatusIconChangeTable[SC_CHASEWALK] = SI_CHASEWALK;
+ StatusIconChangeTable[SC_REJECTSWORD] = SI_REJECTSWORD;
+ StatusIconChangeTable[SC_MARIONETTE] = SI_MARIONETTE;
+ StatusIconChangeTable[SC_MARIONETTE2] = SI_MARIONETTE2;
+ StatusIconChangeTable[SC_MOONLIT] = SI_MOONLIT;
+ StatusIconChangeTable[SC_DEVOTION] = SI_DEVOTION;
+ StatusIconChangeTable[SC_STEELBODY] = SI_STEELBODY;
+ StatusIconChangeTable[SC_SPURT] = SI_SPURT;
+ StatusIconChangeTable[SC_SPIRIT] = SI_SPIRIT;
+ StatusIconChangeTable[SC_READYSTORM] = SI_READYSTORM;
+ StatusIconChangeTable[SC_READYDOWN] = SI_READYDOWN;
+ StatusIconChangeTable[SC_READYTURN] = SI_READYTURN;
+ StatusIconChangeTable[SC_READYCOUNTER] = SI_READYCOUNTER;
+ StatusIconChangeTable[SC_DODGE] = SI_DODGE;
+ StatusIconChangeTable[SC_SHADOWWEAPON] = SI_SHADOWWEAPON;
+ StatusIconChangeTable[SC_WARM] = SI_WARM;
+ StatusIconChangeTable[SC_SUN_COMFORT] = SI_SUN_COMFORT;
+ StatusIconChangeTable[SC_MOON_COMFORT] = SI_MOON_COMFORT;
+ StatusIconChangeTable[SC_STAR_COMFORT] = SI_STAR_COMFORT;
+ StatusIconChangeTable[SC_ADRENALINE2] = SI_ADRENALINE2;
+ StatusIconChangeTable[SC_GHOSTWEAPON] = SI_GHOSTWEAPON;
+ StatusIconChangeTable[SC_KAITE] = SI_KAITE;
+ StatusIconChangeTable[SC_KAIZEL] = SI_KAIZEL;
+ StatusIconChangeTable[SC_KAAHI] = SI_KAAHI;
+ StatusIconChangeTable[SC_KAUPE] = SI_KAUPE;
+ StatusIconChangeTable[SC_ONEHAND] = SI_ONEHAND;
+ StatusIconChangeTable[SC_PRESERVE] = SI_PRESERVE;
+ StatusIconChangeTable[SC_BATTLEORDERS] = SI_BATTLEORDERS;
+ StatusIconChangeTable[SC_DOUBLECAST] = SI_DOUBLECAST;
+ StatusIconChangeTable[SC_MAXOVERTHRUST] = SI_MAXOVERTHRUST;
+ StatusIconChangeTable[SC_SHRINK] = SI_SHRINK;
+ StatusIconChangeTable[SC_SIGHTBLASTER] = SI_SIGHTBLASTER;
+ StatusIconChangeTable[SC_WINKCHARM] = SI_WINKCHARM;
+ StatusIconChangeTable[SC_CLOSECONFINE] = SI_CLOSECONFINE;
+ StatusIconChangeTable[SC_CLOSECONFINE2] = SI_CLOSECONFINE2;
+}
+/*==========================================
+ * ¸˜Bƒ{[ƒiƒX
+ *------------------------------------------
+ */
+int status_getrefinebonus(int lv,int type)
+{
+ if (lv >= 0 && lv < 5 && type >= 0 && type < 3)
+ return refinebonus[lv][type];
+ return 0;
+}
+
+/*==========================================
+ * Checks whether the src can use the skill on the target,
+ * taking into account status/option of both source/target. [Skotlex]
+ * flag: 1 to indicate this call is done after the casting (target already selected)
+ * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack.
+ * target MAY Be null, in which case the checks are only to see
+ * whether the source can cast or not the skill on the ground.
+ *------------------------------------------
+ */
+int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag)
+{
+ int mode, race, hide_flag;
+ struct status_change *sc_data=NULL, *tsc_data;
+ short *option=NULL, *opt1=NULL;
+
+ if (src && status_isdead(src))
+ return 0;
+ if (target && status_isdead(target) && skill_num != ALL_RESURRECTION)
+ return 0;
+
+ if (skill_num == PA_PRESSURE && flag)
+ return 1; //Once Gloria Domini has been casted, there's nothing you can do to stop it. [Skotlex]
+
+ mode = src?status_get_mode(src):MD_CANATTACK;
+
+ if (!skill_num && !(mode&MD_CANATTACK))
+ return 0; //This mode is only needed for melee attacking.
+
+ if (((src && map_getcell(src->m,src->x,src->y,CELL_CHKBASILICA)) ||
+ (target && target != src && map_getcell(target->m,target->x,target->y,CELL_CHKBASILICA)))
+ && !(mode&MD_BOSS))
+ { //Basilica Check
+ if (!skill_num) return 0;
+ race = skill_get_inf(skill_num);
+ if (race&INF_ATTACK_SKILL)
+ return 0;
+ if (race&INF_GROUND_SKILL && skill_get_unit_target(skill_num)&BCT_ENEMY)
+ return 0;
+ }
+ if (src) {
+ option = status_get_option(src);
+ opt1 = status_get_opt1(src);
+ sc_data = status_get_sc_data(src);
+ }
+
+ if (opt1 && (*opt1) >0)
+ return 0;
+
+ if(sc_data)
+ {
+ if (
+ (sc_data[SC_TRICKDEAD].timer != -1 && skill_num != NV_TRICKDEAD)
+ || (sc_data[SC_AUTOCOUNTER].timer != -1 && skill_num != KN_AUTOCOUNTER)
+ || (sc_data[SC_GOSPEL].timer != -1 && sc_data[SC_GOSPEL].val4 == BCT_SELF && skill_num != PA_GOSPEL)
+ || sc_data[SC_GRAVITATION].timer != -1
+ )
+ return 0;
+
+ if (sc_data[SC_BLADESTOP].timer != -1) {
+ switch (sc_data[SC_BLADESTOP].val1)
+ {
+ case 1: return 0;
+ case 2: if (skill_num != MO_FINGEROFFENSIVE) return 0; break;
+ case 3: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE) return 0; break;
+ case 4: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) return 0; break;
+ case 5: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO && skill_num!=MO_EXTREMITYFIST) return 0; break;
+ default: return 0;
+ }
+ }
+ if (skill_num)
+ { //Skills blocked through status changes...
+ if ((sc_data[SC_VOLCANO].timer != -1 && skill_num == WZ_ICEWALL) ||
+ (sc_data[SC_ROKISWEIL].timer != -1 && skill_num != BD_ADAPTATION) ||
+ (sc_data[SC_MARIONETTE].timer != -1 && skill_num != CG_MARIONETTE) ||
+ (sc_data[SC_MARIONETTE2].timer != -1 && skill_num == CG_MARIONETTE) ||
+ (sc_data[SC_HERMODE].timer != -1 && skill_get_inf(skill_num) & INF_SUPPORT_SKILL) ||
+ sc_data[SC_SILENCE].timer != -1 || sc_data[SC_STEELBODY].timer != -1 ||
+ sc_data[SC_BERSERK].timer != -1 || sc_data[SC_SKA].timer != -1
+ )
+ return 0;
+
+ if (sc_data[SC_DANCING].timer != -1)
+ {
+ if (skill_num != BD_ADAPTATION && skill_num != CG_LONGINGFREEDOM
+ && skill_num != BA_MUSICALSTRIKE && skill_num != DC_THROWARROW)
+ return 0;
+ if (sc_data[SC_DANCING].val1 == CG_HERMODE && skill_num == BD_ADAPTATION)
+ return 0; //Can't amp out of Wand of Hermode :/ [Skotlex]
+ }
+ }
+ }
+
+ if (option)
+ {
+ if ((*option)&OPTION_HIDE && skill_num != TF_HIDING && skill_num != AS_GRIMTOOTH
+ && skill_num != RG_BACKSTAP && skill_num != RG_RAID)
+ return 0;
+ if ((*option)&OPTION_CLOAK && skill_num == TF_HIDING)
+ return 0;
+ if ((*option)&OPTION_CHASEWALK && skill_num != ST_CHASEWALK)
+ return 0;
+ }
+ if (target == NULL || target == src) //No further checking needed.
+ return 1;
+
+ tsc_data = status_get_sc_data(target);
+ if(tsc_data)
+ {
+ if (!(mode & MD_BOSS) && tsc_data[SC_TRICKDEAD].timer != -1)
+ return 0;
+ if(skill_num == WZ_STORMGUST && tsc_data[SC_FREEZE].timer != -1)
+ return 0;
+ if(skill_num == PR_LEXAETERNA && (tsc_data[SC_FREEZE].timer != -1 || (tsc_data[SC_STONE].timer != -1 && tsc_data[SC_STONE].val2 == 0)))
+ return 0;
+ }
+
+ if (src) {
+ race = status_get_race(src);
+ } else { //Ground skill, only earth-elemental skills have detecting-hitting capabilities.
+ race = 0;
+ if(skill_get_pl(skill_num) == 2)
+ mode|= MD_DETECTOR;
+ }
+ option = status_get_option(target);
+ hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK); //If targetting, cloak+hide protect you, otherwise only hiding does.
+
+ switch (target->type)
+ {
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data*) target;
+ if (pc_isinvisible(sd))
+ return 0;
+ if (((*option)&hide_flag || sd->state.gangsterparadise)
+ && (sd->state.perfect_hiding || !(race == 4 || race == 6 || mode&MD_DETECTOR))
+ && !(mode&MD_BOSS))
+ return 0;
+ }
+ break;
+ case BL_PET:
+ return 0;
+ case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
+ //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
+ if (mode&MD_LOOTER)
+ return 1;
+ else
+ return 0;
+ default:
+ //Check for chase-walk/hiding/cloaking opponents.
+ if (option && !(mode&MD_BOSS))
+ {
+ if ((*option)&hide_flag && !(race == 4 || race == 6 || mode&MD_DETECTOR))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+//Skotlex: Calculates the stats of the given pet.
+int status_calc_pet(struct map_session_data *sd, int first)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ if (sd->status.pet_id == 0 || sd->pd == NULL)
+ return 1;
+
+ pd = sd->pd;
+
+ if (battle_config.pet_lv_rate && pd->status)
+ {
+ sd->pet.level = sd->status.base_level*battle_config.pet_lv_rate/100;
+ if (sd->pet.level < 0)
+ sd->pet.level = 1;
+ if (pd->status->level != sd->pet.level || first)
+ {
+ if (!first) //Lv Up animation
+ clif_misceffect(&pd->bl, 0);
+ pd->status->level = sd->pet.level;
+ pd->status->atk1 = (pd->db->atk1*pd->status->level)/pd->db->lv;
+ pd->status->atk2 = (pd->db->atk2*pd->status->level)/pd->db->lv;
+ pd->status->str = (pd->db->str*pd->status->level)/pd->db->lv;
+ pd->status->agi = (pd->db->agi*pd->status->level)/pd->db->lv;
+ pd->status->vit = (pd->db->vit*pd->status->level)/pd->db->lv;
+ pd->status->int_ = (pd->db->int_*pd->status->level)/pd->db->lv;
+ pd->status->dex = (pd->db->dex*pd->status->level)/pd->db->lv;
+ pd->status->luk = (pd->db->luk*pd->status->level)/pd->db->lv;
+
+ if (pd->status->atk1 > battle_config.pet_max_atk1) pd->status->atk1 = battle_config.pet_max_atk1;
+ if (pd->status->atk2 > battle_config.pet_max_atk2) pd->status->atk2 = battle_config.pet_max_atk2;
+
+ if (pd->status->str > battle_config.pet_max_stats) pd->status->str = battle_config.pet_max_stats;
+ else if (pd->status->str < 1) pd->status->str = 1;
+ if (pd->status->agi > battle_config.pet_max_stats) pd->status->agi = battle_config.pet_max_stats;
+ else if (pd->status->agi < 1) pd->status->agi = 1;
+ if (pd->status->vit > battle_config.pet_max_stats) pd->status->vit = battle_config.pet_max_stats;
+ else if (pd->status->vit < 1) pd->status->vit = 1;
+ if (pd->status->int_ > battle_config.pet_max_stats) pd->status->int_ = battle_config.pet_max_stats;
+ else if (pd->status->int_ < 1) pd->status->int_ = 1;
+ if (pd->status->dex > battle_config.pet_max_stats) pd->status->dex = battle_config.pet_max_stats;
+ else if (pd->status->dex < 1) pd->status->dex = 1;
+ if (pd->status->luk > battle_config.pet_max_stats) pd->status->luk = battle_config.pet_max_stats;
+ else if (pd->status->luk < 1) pd->status->luk = 1;
+
+ if (!first) //Not done the first time because the pet is not visible yet
+ clif_send_petstatus(sd);
+ }
+ }
+ //Support rate modifier (1000 = 100%)
+ pd->rate_fix = 1000*(sd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500;
+ if(battle_config.pet_support_rate != 100)
+ pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100;
+ return 0;
+}
+
+/*==========================================
+ * ƒpƒ‰ƒ[ƒ^ŒvŽZ
+ * first==0‚ÌŽžAŒvŽZ‘Îۂ̃pƒ‰ƒ[ƒ^‚ªŒÄ‚Ño‚µ‘O‚©‚ç
+ * •Ï ‰»‚µ‚½ê‡Ž©“®‚Åsend‚·‚邪A
+ * ”\“®“I‚ɕω»‚³‚¹‚½ƒpƒ‰ƒ[ƒ^‚ÍŽ©‘O‚Åsend‚·‚é‚悤‚É
+ *------------------------------------------
+ */
+
+int status_calc_pc(struct map_session_data* sd,int first)
+{
+ static int calculating = 0; //Check for recursive call preemption. [Skotlex]
+ int b_speed,b_max_hp,b_max_sp,b_hp,b_sp,b_weight,b_max_weight,b_paramb[6],b_parame[6],b_hit,b_flee;
+ int b_aspd,b_watk,b_def,b_watk2,b_def2,b_flee2,b_critical,b_attackrange,b_matk1,b_matk2,b_mdef,b_mdef2,b_class;
+ int b_base_atk;
+ struct skill b_skill[MAX_SKILL];
+ int i,bl,index;
+ int skill,wele,wele_,def_ele,refinedef=0;
+ int pele=0,pdef_ele=0;
+ int str,dstr,dex;
+
+ nullpo_retr(0, sd);
+ if (++calculating > 10) //Too many recursive calls to status_calc_pc!
+ return -1;
+
+ b_speed = sd->speed;
+ b_max_hp = sd->status.max_hp;
+ b_max_sp = sd->status.max_sp;
+ b_hp = sd->status.hp;
+ b_sp = sd->status.sp;
+ b_weight = sd->weight;
+ b_max_weight = sd->max_weight;
+ memcpy(b_paramb,&sd->paramb,sizeof(b_paramb));
+ memcpy(b_parame,&sd->paramc,sizeof(b_parame));
+ memcpy(b_skill,&sd->status.skill,sizeof(b_skill));
+ b_hit = sd->hit;
+ b_flee = sd->flee;
+ b_aspd = sd->aspd;
+ b_watk = sd->right_weapon.watk;
+ b_def = sd->def;
+ b_watk2 = sd->right_weapon.watk2;
+ b_def2 = sd->def2;
+ b_flee2 = sd->flee2;
+ b_critical = sd->critical;
+ b_attackrange = sd->attackrange;
+ b_matk1 = sd->matk1;
+ b_matk2 = sd->matk2;
+ b_mdef = sd->mdef;
+ b_mdef2 = sd->mdef2;
+ b_class = sd->view_class;
+ sd->view_class = sd->status.class_;
+ b_base_atk = sd->base_atk;
+
+ pc_calc_skilltree(sd); // ƒXƒLƒ‹ƒcƒŠ?‚ÌŒvŽZ
+
+ sd->max_weight = max_weight_base[sd->status.class_]+sd->status.str*300;
+
+ if(first&1) {
+ sd->weight=0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL)
+ continue;
+ sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount;
+ }
+ sd->cart_max_weight=battle_config.max_cart_weight;
+ sd->cart_weight=0;
+ sd->cart_max_num=MAX_CART;
+ sd->cart_num=0;
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==0)
+ continue;
+ sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount;
+ sd->cart_num++;
+ }
+ }
+
+ // these are not zeroed. [zzo]
+
+ sd->speed = DEFAULT_WALK_SPEED;
+ sd->hprate=battle_config.hp_rate;
+ sd->sprate=battle_config.sp_rate;
+ sd->castrate=100;
+ sd->delayrate=100;
+ sd->dsprate=100;
+ sd->aspd_rate = 100;
+ sd->speed_rate = 100;
+ sd->hprecov_rate = 100;
+ sd->sprecov_rate = 100;
+ sd->atk_rate = sd->matk_rate = 100;
+ sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
+ sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
+ sd->speed_add_rate = sd->aspd_add_rate = 100;
+
+ // zeroed arays, order follows the order in map.h.
+ // add new arrays to the end of zeroed area in map.h (see comments) and size here. [zzo]
+ memset (sd->paramb, 0, sizeof(sd->paramb)
+ + sizeof(sd->parame)
+ + sizeof(sd->subele)
+ + sizeof(sd->subrace)
+ + sizeof(sd->subrace2)
+ + sizeof(sd->subsize)
+ + sizeof(sd->addeff)
+ + sizeof(sd->addeff2)
+ + sizeof(sd->reseff)
+ + sizeof(sd->weapon_coma_ele)
+ + sizeof(sd->weapon_coma_race)
+ + sizeof(sd->weapon_atk)
+ + sizeof(sd->weapon_atk_rate)
+ + sizeof(sd->arrow_addele)
+ + sizeof(sd->arrow_addrace)
+ + sizeof(sd->arrow_addsize)
+ + sizeof(sd->arrow_addeff)
+ + sizeof(sd->arrow_addeff2)
+ + sizeof(sd->magic_addele)
+ + sizeof(sd->magic_addrace)
+ + sizeof(sd->magic_addsize)
+ + sizeof(sd->critaddrace)
+ + sizeof(sd->expaddrace)
+ + sizeof(sd->itemhealrate)
+ + sizeof(sd->addeff3)
+ + sizeof(sd->addeff3_type)
+ + sizeof(sd->sp_gain_race)
+ + sizeof(sd->unequip_losehp)
+ + sizeof(sd->unequip_losesp)
+ );
+
+
+ memset (&sd->right_weapon.watk, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
+ memset (&sd->left_weapon.watk, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));
+
+ memset(&sd->special_state,0,sizeof(sd->special_state));
+
+ sd->status.max_hp = 0;
+ sd->status.max_sp = 0;
+
+ //zero up structures...
+ memset(&sd->autospell,0,sizeof(sd->autospell)
+ + sizeof(sd->autospell2)
+ + sizeof(sd->skillatk)
+ + sizeof(sd->skillblown)
+ + sizeof(sd->add_def)
+ + sizeof(sd->add_mdef)
+ + sizeof(sd->add_dmg)
+ + sizeof(sd->add_mdmg)
+ + sizeof(sd->add_drop)
+ );
+
+ // vars zeroing. ints, shorts, chars. in that order.
+ memset (&sd->hit, 0, sizeof(sd->hit)
+ + sizeof(sd->flee)
+ + sizeof(sd->flee2)
+ + sizeof(sd->critical)
+ + sizeof(sd->aspd)
+ + sizeof(sd->def)
+ + sizeof(sd->mdef)
+ + sizeof(sd->def2)
+ + sizeof(sd->mdef2)
+ + sizeof(sd->def_ele)
+ + sizeof(sd->matk1)
+ + sizeof(sd->matk2)
+ + sizeof(sd->base_atk)
+ + sizeof(sd->arrow_atk)
+ + sizeof(sd->arrow_ele)
+ + sizeof(sd->arrow_cri)
+ + sizeof(sd->arrow_hit)
+ + sizeof(sd->arrow_range)
+ + sizeof(sd->nhealhp)
+ + sizeof(sd->nhealsp)
+ + sizeof(sd->nshealhp)
+ + sizeof(sd->nshealsp)
+ + sizeof(sd->nsshealhp)
+ + sizeof(sd->nsshealsp)
+ + sizeof(sd->critical_def)
+ + sizeof(sd->double_rate)
+ + sizeof(sd->long_attack_atk_rate)
+ + sizeof(sd->near_attack_def_rate)
+ + sizeof(sd->long_attack_def_rate)
+ + sizeof(sd->magic_def_rate)
+ + sizeof(sd->misc_def_rate)
+ + sizeof(sd->ignore_mdef_ele)
+ + sizeof(sd->ignore_mdef_race)
+ + sizeof(sd->perfect_hit)
+ + sizeof(sd->perfect_hit_add)
+ + sizeof(sd->get_zeny_rate)
+ + sizeof(sd->get_zeny_num)
+ + sizeof(sd->double_add_rate)
+ + sizeof(sd->short_weapon_damage_return)
+ + sizeof(sd->long_weapon_damage_return)
+ + sizeof(sd->magic_damage_return)
+ + sizeof(sd->random_attack_increase_add)
+ + sizeof(sd->random_attack_increase_per)
+ + sizeof(sd->break_weapon_rate)
+ + sizeof(sd->break_armor_rate)
+ + sizeof(sd->crit_atk_rate)
+ + sizeof(sd->hp_loss_rate)
+ + sizeof(sd->sp_loss_rate)
+ + sizeof(sd->classchange)
+ + sizeof(sd->setitem_hash)
+ + sizeof(sd->setitem_hash2)
+ // shorts
+ + sizeof(sd->attackrange)
+ + sizeof(sd->attackrange_)
+ + sizeof(sd->splash_range)
+ + sizeof(sd->splash_add_range)
+ + sizeof(sd->add_steal_rate)
+ + sizeof(sd->hp_loss_value)
+ + sizeof(sd->sp_loss_value)
+ + sizeof(sd->hp_loss_type)
+ + sizeof(sd->sp_drain_type)
+ + sizeof(sd->hp_gain_value)
+ + sizeof(sd->sp_gain_value)
+ + sizeof(sd->add_drop_count)
+ + sizeof(sd->unbreakable)
+ + sizeof(sd->unbreakable_equip)
+ + sizeof(sd->unstripable_equip)
+ + sizeof(sd->no_regen)
+ + sizeof(sd->add_def_count)
+ + sizeof(sd->add_mdef_count)
+ + sizeof(sd->add_dmg_count)
+ + sizeof(sd->add_mdmg_count)
+ );
+
+ if(!sd->state.disguised && sd->disguise) {
+ pc_stop_walking(sd,0);
+ clif_clearchar(&sd->bl, 0);
+ sd->disguise=0;
+ clif_changeoption(&sd->bl);
+ clif_spawnpc(sd);
+ }
+
+ for(i=0;i<10;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == 9 && sd->equip_index[8] == index)
+ continue;
+ if(i == 5 && sd->equip_index[4] == index)
+ continue;
+ if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index))
+ continue;
+
+ if(sd->inventory_data[index]) {
+ if(sd->inventory_data[index]->type == 4) { // Weapon cards
+ if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) {
+ int j;
+ for(j=0;j<sd->inventory_data[index]->slot;j++){ // ƒJ?ƒh
+ int c=sd->status.inventory[index].card[j];
+ if(c>0){
+ if(i == 8 && sd->status.inventory[index].equip == 0x20)
+ sd->state.lr_flag = 1;
+ run_script(itemdb_equipscript(c),0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+ }
+ else if(sd->inventory_data[index]->type==5){ // Non-weapon equipment cards
+ if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) {
+ int j;
+ for(j=0;j<sd->inventory_data[index]->slot;j++){ // ƒJ?ƒh
+ int c=sd->status.inventory[index].card[j];
+ if(c>0) {
+ run_script(itemdb_equipscript(c),0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ wele = sd->right_weapon.atk_ele;
+ wele_ = sd->left_weapon.atk_ele;
+ def_ele = sd->def_ele;
+
+ if(sd->status.pet_id > 0) { // Pet
+ struct pet_data *pd=sd->pd;
+ if((pd && battle_config.pet_status_support) && (!battle_config.pet_equip_required || pd->equip > 0)) {
+ if(sd->pet.intimate > 0 && pd->state.skillbonus == 1 && pd->bonus) { //Skotlex: Readjusted for pets
+ pc_bonus(sd,pd->bonus->type, pd->bonus->val);
+ }
+ pele = sd->right_weapon.atk_ele;
+ pdef_ele = sd->def_ele;
+ sd->right_weapon.atk_ele = sd->def_ele = 0;
+ }
+ }
+ memcpy(sd->paramcard,sd->parame,sizeof(sd->paramcard));
+
+ // ?”õ•i‚É‚æ‚éƒXƒe?ƒ^ƒX?‰»‚Í‚±‚±‚Å?s
+ for(i=0;i<10;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == 9 && sd->equip_index[8] == index)
+ continue;
+ if(i == 5 && sd->equip_index[4] == index)
+ continue;
+ if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index))
+ continue;
+ if(sd->inventory_data[index]) {
+ sd->def += sd->inventory_data[index]->def;
+ if(sd->inventory_data[index]->type == 4) {
+ int r,wlv = sd->inventory_data[index]->wlv;
+ if (wlv >= MAX_REFINE_BONUS)
+ wlv = MAX_REFINE_BONUS - 1;
+ if(i == 8 && sd->status.inventory[index].equip == 0x20) { // Left-hand weapon
+ sd->left_weapon.watk += sd->inventory_data[index]->atk;
+ sd->left_weapon.watk2 = (r=sd->status.inventory[index].refine)* // ¸?U?—Í
+ refinebonus[wlv][0];
+ if( (r-=refinebonus[wlv][2])>0 ) // ‰ß?¸?ƒ{?ƒiƒX
+ sd->left_weapon.overrefine = r*refinebonus[wlv][1];
+
+ if(sd->status.inventory[index].card[0]==0x00ff){ // Forged weapon
+ sd->left_weapon.star = (sd->status.inventory[index].card[1]>>8); // ¯‚Ì‚©‚¯‚ç
+ if(sd->left_weapon.star >= 15) sd->left_weapon.star = 40; // 3 Star Crumbs now give +40 dmg
+ if (pc_istop10fame( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ sd->left_weapon.star += 10;
+ wele_= (sd->status.inventory[index].card[1]&0x0f); // ? «
+ }
+ sd->attackrange_ += sd->inventory_data[index]->range;
+ sd->state.lr_flag = 1;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ else { // Right-hand weapon
+ sd->right_weapon.watk += sd->inventory_data[index]->atk;
+ sd->right_weapon.watk2 += (r=sd->status.inventory[index].refine)* // ¸?U?—Í
+ refinebonus[wlv][0];
+ if( (r-=refinebonus[wlv][2])>0 ) // ‰ß?¸?ƒ{?ƒiƒX
+ sd->right_weapon.overrefine += r*refinebonus[wlv][1];
+
+ if(sd->status.inventory[index].card[0]==0x00ff){ // Forged weapon
+ sd->right_weapon.star += (sd->status.inventory[index].card[1]>>8); // ¯‚Ì‚©‚¯‚ç
+ if(sd->right_weapon.star >= 15) sd->right_weapon.star = 40; // 3 Star Crumbs now give +40 dmg
+ if (pc_istop10fame( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ sd->right_weapon.star += 10;
+ wele = (sd->status.inventory[index].card[1]&0x0f); // ? «
+ }
+ sd->attackrange += sd->inventory_data[index]->range;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ else if(sd->inventory_data[index]->type == 5) {
+ sd->right_weapon.watk += sd->inventory_data[index]->atk;
+ refinedef += sd->status.inventory[index].refine*refinebonus[0][0];
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if(sd->equip_index[10] >= 0){ // –î
+ index = sd->equip_index[10];
+ if(sd->inventory_data[index]){ // Arrows
+ sd->state.lr_flag = 2;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ sd->arrow_atk += sd->inventory_data[index]->atk;
+ }
+ }
+ sd->def += (refinedef+50)/100;
+
+ if(sd->attackrange < 1) sd->attackrange = 1;
+ if(sd->attackrange_ < 1) sd->attackrange_ = 1;
+ if(sd->attackrange < sd->attackrange_)
+ sd->attackrange = sd->attackrange_;
+ if(sd->status.weapon == 11)
+ sd->attackrange += sd->arrow_range;
+ if(wele > 0)
+ sd->right_weapon.atk_ele = wele;
+ if(wele_ > 0)
+ sd->left_weapon.atk_ele = wele_;
+ if(def_ele > 0)
+ sd->def_ele = def_ele;
+ if(battle_config.pet_status_support) {
+ if(pele > 0 && !sd->right_weapon.atk_ele)
+ sd->right_weapon.atk_ele = pele;
+ if(pdef_ele > 0 && !sd->def_ele)
+ sd->def_ele = pdef_ele;
+ }
+ sd->double_rate += sd->double_add_rate;
+ sd->perfect_hit += sd->perfect_hit_add;
+ sd->splash_range += sd->splash_add_range;
+ if(sd->aspd_add_rate != 100)
+ sd->aspd_rate += sd->aspd_add_rate-100;
+ if(sd->speed_add_rate != 100)
+ sd->speed_rate += sd->speed_add_rate-100;
+
+ // Damage modifiers from weapon type
+ sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1];
+ sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1];
+ sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1];
+ sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2];
+ sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2];
+ sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2];
+
+// ----- STATS CALCULATION -----
+
+ // Job bonuses
+ for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){
+ if(job_bonus[sd->status.class_][i])
+ sd->paramb[job_bonus[sd->status.class_][i]-1]++;
+ }
+
+ // If a Super Novice has never died and is at least joblv 70, he gets all stats +10
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){
+ sd->paramb[0] += 10;
+ sd->paramb[1] += 10;
+ sd->paramb[2] += 10;
+ sd->paramb[3] += 10;
+ sd->paramb[4] += 10;
+ sd->paramb[5] += 10;
+ }
+
+ // Absolute modifiers from passive skills
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ sd->paramb[0] ++;
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
+ sd->paramb[3] += (skill+1)/2; // +1 INT / 2 lv
+ if((skill=pc_checkskill(sd,AC_OWL))>0)
+ sd->paramb[4] += skill;
+
+ // Improve Concentration adds a percentage of base stat + job bonus + equipment bonus, but not from card bonus nor status change bonus
+ if(sd->sc_count && sd->sc_data[SC_CONCENTRATE].timer!=-1 && sd->sc_data[SC_QUAGMIRE].timer == -1){
+ sd->paramb[1] += (sd->status.agi+sd->paramb[1]+sd->parame[1]-sd->paramcard[1])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100;
+ sd->paramb[4] += (sd->status.dex+sd->paramb[4]+sd->parame[4]-sd->paramcard[4])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100;
+ }
+
+ // Absolute modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if (sd->sc_data[SC_BATTLEORDERS].timer != -1) {
+ sd->paramb[0] += 5;
+ sd->paramb[3] += 5;
+ sd->paramb[4] += 5;
+ }
+ if (sd->sc_data[SC_GUILDAURA].timer != -1) {
+ int guildflag = sd->sc_data[SC_GUILDAURA].val4;
+ for (i = 12; i >= 0; i -= 4) {
+ skill = guildflag >> i;
+ switch (i) {
+ case 12: sd->paramb[0] += skill; break;
+ case 8: sd->paramb[2] += skill; break;
+ case 4: sd->paramb[1] += skill; break;
+ case 0: sd->paramb[4] += skill; break;
+ }
+ guildflag ^= skill << i;
+ }
+ }
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->paramb[0] = status_calc_str(&sd->bl,sd->paramb[0]);
+ sd->paramb[1] = status_calc_agi(&sd->bl,sd->paramb[1]);
+ sd->paramb[2] = status_calc_vit(&sd->bl,sd->paramb[2]);
+ sd->paramb[3] = status_calc_int(&sd->bl,sd->paramb[3]);
+ sd->paramb[4] = status_calc_dex(&sd->bl,sd->paramb[4]);
+ sd->paramb[5] = status_calc_luk(&sd->bl,sd->paramb[5]);
+
+ // Relative modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if(sd->sc_data[SC_MARIONETTE].timer!=-1){
+ sd->paramb[0]-= sd->status.str/2;
+ sd->paramb[1]-= sd->status.agi/2;
+ sd->paramb[2]-= sd->status.vit/2;
+ sd->paramb[3]-= sd->status.int_/2;
+ sd->paramb[4]-= sd->status.dex/2;
+ sd->paramb[5]-= sd->status.luk/2;
+ }
+ else if(sd->sc_data[SC_MARIONETTE2].timer!=-1){
+ struct map_session_data *psd = map_id2sd(sd->sc_data[SC_MARIONETTE2].val3);
+ if (psd) { // if partner is found
+ sd->paramb[0] += sd->status.str+psd->status.str/2 > 99 ? 99-sd->status.str : psd->status.str/2;
+ sd->paramb[1] += sd->status.agi+psd->status.agi/2 > 99 ? 99-sd->status.agi : psd->status.agi/2;
+ sd->paramb[2] += sd->status.vit+psd->status.vit/2 > 99 ? 99-sd->status.vit : psd->status.vit/2;
+ sd->paramb[3] += sd->status.int_+psd->status.int_/2 > 99 ? 99-sd->status.int_ : psd->status.int_/2;
+ sd->paramb[4] += sd->status.dex+psd->status.dex/2 > 99 ? 99-sd->status.dex : psd->status.dex/2;
+ sd->paramb[5] += sd->status.luk+psd->status.luk/2 > 99 ? 99-sd->status.luk : psd->status.luk/2;
+ }
+ }
+ }
+
+ // Calculate total stats
+ sd->paramc[0]=sd->status.str+sd->paramb[0]+sd->parame[0];
+ sd->paramc[1]=sd->status.agi+sd->paramb[1]+sd->parame[1];
+ sd->paramc[2]=sd->status.vit+sd->paramb[2]+sd->parame[2];
+ sd->paramc[3]=sd->status.int_+sd->paramb[3]+sd->parame[3];
+ sd->paramc[4]=sd->status.dex+sd->paramb[4]+sd->parame[4];
+ sd->paramc[5]=sd->status.luk+sd->paramb[5]+sd->parame[5];
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SPIRIT].timer!=-1 && sd->sc_data[SC_SPIRIT].val2 == SL_HIGH)
+ { //Ups any status under 50 to 50.
+ if (sd->paramc[0] < 50) {
+ sd->paramb[0] += 50-sd->paramc[0];
+ sd->paramc[0] = 50;
+ }
+ if (sd->paramc[1] < 50) {
+ sd->paramb[1] += 50-sd->paramc[1];
+ sd->paramc[1] = 50;
+ }
+ if (sd->paramc[2] < 50) {
+ sd->paramb[2] += 50-sd->paramc[2];
+ sd->paramc[2] = 50;
+ }
+ if (sd->paramc[3] < 50) {
+ sd->paramb[3] += 50-sd->paramc[3];
+ sd->paramc[3] = 50;
+ }
+ if (sd->paramc[4] < 50) {
+ sd->paramb[4] += 50-sd->paramc[4];
+ sd->paramc[4] = 50;
+ }
+ if (sd->paramc[5] < 50) {
+ sd->paramb[5] += 50-sd->paramc[5];
+ sd->paramc[5] = 50;
+ }
+ }
+ }
+ for(i=0;i<6;i++)
+ if(sd->paramc[i] < 0) sd->paramc[i] = 0;
+
+ if (sd->sc_count && sd->sc_data[SC_CURSE].timer!=-1)
+ sd->paramc[5] = 0;
+
+// ------ BASE ATTACK CALCULATION ------
+
+ // Basic Base ATK value
+ switch(sd->status.weapon){
+ case 11: // Bows
+ case 13: // Musical Instruments
+ case 14: // Whips
+ str = sd->paramc[4];
+ dex = sd->paramc[0];
+ break;
+ default:
+ str = sd->paramc[0];
+ dex = sd->paramc[4];
+ break;
+ }
+ dstr = str/10;
+ sd->base_atk += str + dstr*dstr + dex/5 + sd->paramc[5]/5;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
+ sd->base_atk += 4;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->base_atk = status_calc_batk(&sd->bl,sd->base_atk);
+
+ if(sd->base_atk < 1)
+ sd->base_atk = 1;
+
+// ----- WEAPON ATK CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->right_weapon.watk = status_calc_watk(&sd->bl,sd->right_weapon.watk);
+ if((index= sd->equip_index[8]) >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4)
+ sd->left_weapon.watk = status_calc_watk(&sd->bl,sd->left_weapon.watk);
+
+// ----- WEAPON UPGRADE ATK CALCULATION -----
+
+ // Absolute modifiers from status changes (only for PC)
+ if(sd->sc_count){
+ if(sd->sc_data[SC_NIBELUNGEN].timer!=-1){
+ index = sd->equip_index[9];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ sd->right_weapon.watk2 += sd->sc_data[SC_NIBELUNGEN].val2;
+ index = sd->equip_index[8];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ sd->left_weapon.watk2 += sd->sc_data[SC_NIBELUNGEN].val2;
+ }
+ }
+
+// ----- MATK CALCULATION -----
+
+ // Basic MATK value
+ sd->matk1 += sd->paramc[3]+(sd->paramc[3]/5)*(sd->paramc[3]/5);
+ sd->matk2 += sd->paramc[3]+(sd->paramc[3]/7)*(sd->paramc[3]/7);
+ if(sd->matk1 < sd->matk2) {
+ int temp = sd->matk2;
+ sd->matk2 = sd->matk1;
+ sd->matk1 = temp;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->matk1 = status_calc_matk(&sd->bl,sd->matk1);
+ sd->matk2 = status_calc_matk(&sd->bl,sd->matk2);
+
+ // Apply relative modifiers from equipment
+ if(sd->matk_rate != 100){
+ sd->matk1 = sd->matk1 * sd->matk_rate/100;
+ sd->matk2 = sd->matk2 * sd->matk_rate/100;
+ }
+
+// ----- CRIT CALCULATION -----
+
+ // Basic Crit value
+ sd->critical += (sd->paramc[5]*3)+10;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->critical = status_calc_critical(&sd->bl,sd->critical);
+
+ // Apply relative modifiers from equipment
+ if(sd->critical_rate != 100)
+ sd->critical = sd->critical * sd->critical_rate/100;
+
+ if(sd->critical < 10) sd->critical = 10;
+
+// ----- HIT CALCULATION -----
+
+ // Basic Hit value
+ sd->hit += sd->paramc[4] + sd->status.base_level;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
+ sd->hit += skill*2;
+ if((skill=pc_checkskill(sd,AC_VULTURE))>0){
+ sd->hit += skill;
+ if(sd->status.weapon == 11)
+ sd->attackrange += skill;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->hit = status_calc_hit(&sd->bl,sd->hit);
+
+ // Apply relative modifiers from equipment
+ if(sd->hit_rate != 100)
+ sd->hit = sd->hit * sd->hit_rate/100;
+
+ if(sd->hit < 1) sd->hit = 1;
+
+// ----- FLEE CALCULATION -----
+
+ // Basic Flee value
+ sd->flee += sd->paramc[1] + sd->status.base_level;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0)
+ sd->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3);
+ if((skill=pc_checkskill(sd,MO_DODGE))>0)
+ sd->flee += (skill*3)>>1;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->flee = status_calc_flee(&sd->bl,sd->flee);
+
+ // Apply relative modifiers from equipment
+ if(sd->flee_rate != 100)
+ sd->flee = sd->flee * sd->flee_rate/100;
+
+ if(sd->flee < 1) sd->flee = 1;
+
+// ----- PERFECT DODGE CALCULATION -----
+
+ // Basic Perfect Dodge value
+ sd->flee2 += sd->paramc[5]+10;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->flee2 = status_calc_flee2(&sd->bl,sd->flee2);
+
+ // Apply relative modifiers from equipment
+ if(sd->flee2_rate != 100)
+ sd->flee2 = sd->flee2 * sd->flee2_rate/100;
+
+ if(sd->flee2 < 10) sd->flee2 = 10;
+
+// ----- VIT-DEF CALCULATION -----
+
+ // Special fixed values from status changes
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1)
+ sd->def2 = 0;
+ else if(sd->sc_count && sd->sc_data[SC_ETERNALCHAOS].timer!=-1)
+ sd->def2 = 0;
+ else {
+ // Basic VIT-DEF value
+ sd->def2 += sd->paramc[2];
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->def2 = status_calc_def2(&sd->bl,sd->def2);
+
+ // Apply relative modifiers from equipment
+ if(sd->def2_rate != 100)
+ sd->def2 = sd->def2 * sd->def2_rate/100;
+
+ if(sd->def2 < 1) sd->def2 = 1;
+ }
+
+// ----- EQUIPMENT-DEF CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->def = status_calc_def(&sd->bl,sd->def);
+
+ // Apply relative modifiers from equipment
+ if(sd->def_rate != 100)
+ sd->def = sd->def * sd->def_rate/100;
+
+ if(sd->def < 0) sd->def = 0;
+
+ else if (!battle_config.player_defense_type && sd->def > battle_config.max_def)
+ {
+ sd->def2 += battle_config.over_def_bonus*(sd->def -battle_config.max_def);
+ sd->def = battle_config.max_def;
+ }
+
+// ----- INT-MDEF CALCULATION -----
+
+ // Special fixed values from status changes
+ if(sd->sc_count && sd->sc_data[SC_BERSERK].timer!=-1)
+ sd->mdef2 = 0;
+ else {
+ // Basic INT-MDEF value
+ sd->mdef2 += sd->paramc[3];
+
+ // sd->mdef2 = status_calc_mdef2(&sd->bl,sd->mdef2);
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef2_rate != 100)
+ sd->mdef2 = sd->mdef2 * sd->mdef2_rate/100;
+
+ if(sd->mdef2 < 1) sd->mdef2 = 1;
+ }
+
+// ----- EQUIPMENT-MDEF CALCULATION -----
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->mdef = status_calc_mdef(&sd->bl,sd->mdef);
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef_rate != 100)
+ sd->mdef = sd->mdef * sd->mdef_rate/100;
+
+ if(sd->mdef < 0) sd->mdef = 0;
+
+ else if (!battle_config.player_defense_type && sd->mdef > battle_config.max_def)
+ {
+ sd->mdef2 += battle_config.over_def_bonus*(sd->mdef -battle_config.max_def);
+ sd->mdef = battle_config.max_def;
+ }
+
+// ----- WALKING SPEED CALCULATION -----
+
+ // Relative, then absolute modifiers from status changes (shared between PC and NPC)
+ sd->speed = status_calc_speed(&sd->bl,sd->speed);
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0 && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && sd->sc_data[SC_CLOAKING].timer==-1)
+ sd->speed -= sd->speed * skill/100;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->speed -= sd->speed * 25/100;
+ if(pc_ishiding(sd) && (skill=pc_checkskill(sd,RG_TUNNELDRIVE))>0)
+ sd->speed += sd->speed * (100-16*skill)/100;
+ if(pc_iscarton(sd) && (skill=pc_checkskill(sd,MC_PUSHCART))>0)
+ sd->speed += sd->speed * (100-10*skill)/100;
+ if(sd->skilltimer != -1 && (skill=pc_checkskill(sd,SA_FREECAST))>0) {
+ sd->prev_speed = sd->speed; //Store previous speed to correctly restore it. [Skotlex]
+ sd->speed += sd->speed * (75-5*skill)/100;
+ }
+ if(sd->sc_count && sd->sc_data[SC_DANCING].timer!=-1){
+ int s_rate = 500-40*pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON));
+ if (sd->sc_data[SC_SPIRIT].timer != -1 && sd->sc_data[SC_SPIRIT].val2 == SL_BARDDANCER)
+ s_rate -= 40; //TODO: Figure out real bonus rate.
+ if (sd->sc_data[SC_LONGING].timer!=-1)
+ s_rate -= 20 * sd->sc_data[SC_LONGING].val1;
+ sd->speed += sd->speed * s_rate/100;
+ }
+ if(sd->sc_data[SC_FUSION].timer != -1) //Additional movement speed from SG_FUSION [Komurka]
+ sd->speed -= sd->speed * 25/100;
+
+ // Apply relative modifiers from equipment
+ if(sd->speed_rate != 100)
+ sd->speed = sd->speed*sd->speed_rate/100;
+
+ if(sd->speed < battle_config.max_walk_speed)
+ sd->speed = battle_config.max_walk_speed;
+
+// ----- ASPD CALCULATION -----
+// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied
+
+ // Basic ASPD value
+ if (sd->status.weapon <= 16)
+ sd->aspd += aspd_base[sd->status.class_][sd->status.weapon]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->status.weapon]/1000;
+ else
+ sd->aspd += (
+ (aspd_base[sd->status.class_][sd->weapontype1]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype1]/1000) +
+ (aspd_base[sd->status.class_][sd->weapontype2]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype2]/1000)
+ ) *2/3; //From what I read in rodatazone, 2/3 should be more accurate than 0.7 -> 140 / 200; [Skotlex]
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0)
+ sd->aspd_rate -= (skill/2);
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && sd->status.job_level >= battle_config.max_job_level)
+ sd->aspd_rate -= (skill*3);
+
+ if(pc_isriding(sd))
+ sd->aspd_rate += 50-10*pc_checkskill(sd,KN_CAVALIERMASTERY);
+
+ // Relative modifiers from status changes (shared between PC and NPC)
+ sd->aspd_rate = status_calc_aspd_rate(&sd->bl,sd->aspd_rate);
+
+ // Apply all relative modifiers
+ if(sd->aspd_rate != 100)
+ sd->aspd = sd->aspd*sd->aspd_rate/100;
+
+ if(sd->aspd < battle_config.max_aspd) sd->aspd = battle_config.max_aspd;
+ sd->amotion = sd->aspd;
+
+// ----- HP MAX AND REGEN CALCULATION -----
+
+ // Basic MaxHP value
+ // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex]
+ bl = sd->status.base_level;
+ index = (3500 + bl*hp_coefficient2[sd->status.class_] +
+ hp_sigma_val[sd->status.class_][(bl > 0)? bl-1:0])/100 *
+ (100 + sd->paramc[2])/100 + (sd->parame[2] - sd->paramcard[2]);
+ if (sd->class_&JOBL_UPPER)
+ index += index * 30/100;
+ else if (sd->class_&JOBL_BABY)
+ index -= index * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON))
+ index *= 3; //Triple max HP for top ranking Taekwons over level 90.
+
+ sd->status.max_hp += index;
+
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
+ sd->status.max_hp = sd->status.max_hp + 2000;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->status.max_hp += skill*200;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->status.max_hp = status_calc_maxhp(&sd->bl,sd->status.max_hp);
+
+ // Apply relative modifiers from equipment
+ if(sd->hprate!=100)
+ sd->status.max_hp = sd->status.max_hp * sd->hprate/100;
+
+ if(sd->status.max_hp > battle_config.max_hp)
+ sd->status.max_hp = battle_config.max_hp;
+ if(sd->status.hp>sd->status.max_hp)
+ sd->status.hp=sd->status.max_hp;
+ if(sd->status.max_hp <= 0) sd->status.max_hp = 1;
+
+ // Basic natural HP regeneration value
+ sd->nhealhp = 1 + (sd->paramc[2]/5) + (sd->status.max_hp/200);
+
+ // Apply relative modifiers from equipment
+ if(sd->hprecov_rate != 100)
+ sd->nhealhp = sd->nhealhp*sd->hprecov_rate/100;
+
+ if(sd->nhealhp < 1) sd->nhealhp = 1;
+ if(sd->nhealhp > 0x7fff) sd->nhealhp = 0x7fff;
+
+ // Skill-related HP recovery
+ if((skill=pc_checkskill(sd,SM_RECOVERY)) > 0)
+ sd->nshealhp = skill*5 + (sd->status.max_hp*skill/500);
+ // Skill-related HP recovery (only when sit)
+ if((skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0)
+ sd->nsshealhp = skill*4 + (sd->status.max_hp*skill/500);
+ if((skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest == 1)
+ sd->nsshealhp = skill*30 + (sd->status.max_hp*skill/500);
+
+ if(sd->nshealhp > 0x7fff) sd->nshealhp = 0x7fff;
+ if(sd->nsshealhp > 0x7fff) sd->nsshealhp = 0x7fff;
+
+// ----- SP MAX AND REGEN CALCULATION -----
+
+ // Basic MaxSP value
+ // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex]
+ index = ((sp_coefficient[sd->status.class_] * bl) + 1000)/100 * (100 + sd->paramc[3])/100 + (sd->parame[3] - sd->paramcard[3]);
+ if (sd->class_&JOBL_UPPER)
+ index += index * 30/100;
+ else if (sd->class_&JOBL_BABY)
+ index -= index * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_istop10fame(sd->char_id, MAPID_TAEKWON))
+ index *= 3; //Triple max SP for top ranking Taekwons over level 90.
+
+ sd->status.max_sp += index;
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,SL_KAINA))>0)
+ sd->status.max_sp += 30*skill;
+ if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
+ sd->status.max_sp += sd->status.max_sp * skill/100;
+ if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ sd->status.max_sp += sd->status.max_sp * 2*skill/100;
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ sd->status.max_sp = status_calc_maxsp(&sd->bl,sd->status.max_sp);
+
+ // Apply relative modifiers from equipment
+ if(sd->sprate!=100)
+ sd->status.max_sp = sd->status.max_sp * sd->sprate/100;
+
+ if(sd->status.max_sp > battle_config.max_sp)
+ sd->status.max_sp = battle_config.max_sp;
+ if(sd->status.sp>sd->status.max_sp)
+ sd->status.sp=sd->status.max_sp;
+ if(sd->status.max_sp <= 0) sd->status.max_sp = 1;
+
+ if(sd->sc_data[SC_DANCING].timer==-1){
+ // Basic natural SP regeneration value
+ sd->nhealsp = 1 + (sd->paramc[3]/6) + (sd->status.max_sp/100);
+ if(sd->paramc[3] >= 120)
+ sd->nhealsp += ((sd->paramc[3]-120)>>1) + 4;
+
+ // Relative modifiers from passive skills
+ if((skill=pc_checkskill(sd,HP_MEDITATIO)) > 0)
+ sd->nhealsp += sd->nhealsp * 3*skill/100;
+
+ // Apply relative modifiers from equipment
+ if(sd->sprecov_rate != 100)
+ sd->nhealsp = sd->nhealsp*sd->sprecov_rate/100;
+
+ if(sd->nhealsp < 1) sd->nhealsp = 1;
+ if(sd->nhealsp > 0x7fff) sd->nhealsp = 0x7fff;
+
+ // Skill-related SP recovery
+ if((skill=pc_checkskill(sd,MG_SRECOVERY)) > 0)
+ sd->nshealsp = skill*3 + (sd->status.max_sp*skill/500);
+ // Skill-related SP recovery (only when sit)
+ if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0)
+ sd->nsshealsp = skill*2 + (sd->status.max_sp*skill/500);
+ if((skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest == 1) {
+ sd->nsshealsp = skill*3 + (sd->status.max_sp*skill/500);
+ if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest
+ sd->nsshealsp += (30+10*skill)*sd->nsshealsp/100;
+ }
+ if(sd->nshealsp > 0x7fff) sd->nshealsp = 0x7fff;
+ if(sd->nsshealsp > 0x7fff) sd->nsshealsp = 0x7fff;
+ }
+
+// ----- MISC CALCULATIONS -----
+
+ //Even though people insist this is too slow, packet data reports this is the actual real equation.
+ sd->dmotion = 800-sd->paramc[1]*4;
+ if(sd->dmotion<400) sd->dmotion = 400;
+
+ // Weight
+ if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
+ sd->max_weight += 2000*skill;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->max_weight += 10000;
+ if( (skill=pc_checkskill(sd,SG_KNOWLEDGE))>0) //SG skill [Komurka]
+ if(sd->bl.m == sd->feel_map[0].m || sd->bl.m == sd->feel_map[1].m || sd->bl.m == sd->feel_map[2].m)
+ sd->max_weight += sd->max_weight*skill/10;
+
+ // Skill SP cost
+ if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
+ sd->dsprate -= 4*skill;
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SERVICE4U].timer!=-1)
+ sd->dsprate -= sd->sc_data[SC_SERVICE4U].val3;
+ }
+
+ if(sd->dsprate < 0) sd->dsprate = 0;
+
+ // Anti-element and anti-race
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->subele[6] += skill*5;
+ if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
+ sd->subele[0] += skill;
+ sd->subele[3] += skill*4;
+ }
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
+ skill = skill*4;
+ sd->right_weapon.addrace[9]+=skill;
+ sd->left_weapon.addrace[9]+=skill;
+ sd->magic_addrace[9]+=skill;
+ sd->subrace[9]+=skill;
+ }
+
+ if(sd->sc_count){
+ if(sd->sc_data[SC_SIEGFRIED].timer!=-1){
+ sd->subele[1] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[2] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[3] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[4] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[5] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[6] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[7] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[8] += sd->sc_data[SC_SIEGFRIED].val2;
+ sd->subele[9] += sd->sc_data[SC_SIEGFRIED].val2;
+ }
+ if(sd->sc_data[SC_PROVIDENCE].timer!=-1){
+ sd->subele[6] += sd->sc_data[SC_PROVIDENCE].val2;
+ sd->subrace[6] += sd->sc_data[SC_PROVIDENCE].val2;
+ }
+ }
+
+// ----- CLIENT-SIDE REFRESH -----
+ if(first&1) { //Since this is the initial loading, the Falcon and Peco icons must be loaded. [Skotlex]
+ if (sd->status.option&OPTION_FALCON)
+ clif_status_load(&sd->bl, SI_FALCON, 1);
+ if (sd->status.option&OPTION_RIDING)
+ clif_status_load(&sd->bl, SI_RIDING, 1);
+ }
+ if(first&4) {
+ calculating = 0;
+ return 0;
+ }
+ if(first&3) {
+ clif_updatestatus(sd,SP_SPEED);
+ clif_updatestatus(sd,SP_MAXHP);
+ clif_updatestatus(sd,SP_MAXSP);
+ if(first&1) {
+ clif_updatestatus(sd,SP_HP);
+ clif_updatestatus(sd,SP_SP);
+ }
+ calculating = 0;
+ return 0;
+ }
+
+
+ if(sd->sc_data[SC_WEDDING].timer != -1 && sd->view_class != JOB_WEDDING)
+ sd->view_class=JOB_WEDDING;
+
+ if(sd->sc_data[SC_XMAS].timer != -1 && sd->view_class != JOB_XMAS)
+ sd->view_class=JOB_XMAS;
+
+ if(b_class != sd->view_class) {
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ //Restoring cloth dye color after the view class changes. [Skotlex]
+ // Added Xmas Suit [Valaris]
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 &&
+ ((sd->view_class != JOB_WEDDING && sd->view_class !=JOB_XMAS) || (sd->view_class==JOB_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (sd->view_class==JOB_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+
+ if( memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)) || b_attackrange != sd->attackrange)
+ clif_skillinfoblock(sd);
+
+ if(b_speed != sd->speed)
+ clif_updatestatus(sd,SP_SPEED);
+ if(b_weight != sd->weight)
+ clif_updatestatus(sd,SP_WEIGHT);
+ if(b_max_weight != sd->max_weight) {
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ pc_checkweighticon(sd);
+ }
+ for(i=0;i<6;i++)
+ if(b_paramb[i] + b_parame[i] != sd->paramb[i] + sd->parame[i])
+ clif_updatestatus(sd,SP_STR+i);
+ if(b_hit != sd->hit)
+ clif_updatestatus(sd,SP_HIT);
+ if(b_flee != sd->flee)
+ clif_updatestatus(sd,SP_FLEE1);
+ if(b_aspd != sd->aspd)
+ clif_updatestatus(sd,SP_ASPD);
+ if(b_watk != sd->right_weapon.watk || b_base_atk != sd->base_atk)
+ clif_updatestatus(sd,SP_ATK1);
+ if(b_def != sd->def)
+ clif_updatestatus(sd,SP_DEF1);
+ if(b_watk2 != sd->right_weapon.watk2)
+ clif_updatestatus(sd,SP_ATK2);
+ if(b_def2 != sd->def2)
+ clif_updatestatus(sd,SP_DEF2);
+ if(b_flee2 != sd->flee2)
+ clif_updatestatus(sd,SP_FLEE2);
+ if(b_critical != sd->critical)
+ clif_updatestatus(sd,SP_CRITICAL);
+ if(b_matk1 != sd->matk1)
+ clif_updatestatus(sd,SP_MATK1);
+ if(b_matk2 != sd->matk2)
+ clif_updatestatus(sd,SP_MATK2);
+ if(b_mdef != sd->mdef)
+ clif_updatestatus(sd,SP_MDEF1);
+ if(b_mdef2 != sd->mdef2)
+ clif_updatestatus(sd,SP_MDEF2);
+ if(b_attackrange != sd->attackrange)
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ if(b_max_hp != sd->status.max_hp)
+ clif_updatestatus(sd,SP_MAXHP);
+ if(b_max_sp != sd->status.max_sp)
+ clif_updatestatus(sd,SP_MAXSP);
+ if(b_hp != sd->status.hp)
+ clif_updatestatus(sd,SP_HP);
+ if(b_sp != sd->status.sp)
+ clif_updatestatus(sd,SP_SP);
+
+ /* I don't think there's a need for this here. It should be handled in pc_damage and pc_heal. [Skotlex]
+ if(sd->status.hp<sd->status.max_hp>>2 && sd->sc_data[SC_AUTOBERSERK].timer!=-1 &&
+ (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0) && !pc_isdead(sd))
+ status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0);
+ */
+ calculating = 0;
+ return 0;
+}
+
+/*==========================================
+ * Apply shared stat mods from status changes [DracoRPG]
+ *------------------------------------------
+ */
+int status_calc_str(struct block_list *bl, int str)
+{
+ struct status_change *sc_data;
+ nullpo_retr(str,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ str += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCSTR].timer!=-1)
+ str += sc_data[SC_INCSTR].val1;
+ if(sc_data[SC_STRFOOD].timer!=-1)
+ str += sc_data[SC_STRFOOD].val1;
+ if(sc_data[SC_LOUD].timer!=-1)
+ str += 4;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ str += 5;
+ if(sc_data[SC_SPURT].timer!=-1)
+ str += 10; //Bonus is +!0 regardless of skill level
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ str >>= 1;
+ else str += sc_data[SC_BLESSING].val1;
+ }
+ }
+
+ return str;
+}
+
+int status_calc_agi(struct block_list *bl, int agi)
+{
+ struct status_change *sc_data;
+ nullpo_retr(agi,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ agi += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCAGI].timer!=-1)
+ agi += sc_data[SC_INCAGI].val1;
+ if(sc_data[SC_AGIFOOD].timer!=-1)
+ agi += sc_data[SC_AGIFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ agi += 5;
+ if(sc_data[SC_INCREASEAGI].timer!=-1)
+ agi += 2 + sc_data[SC_INCREASEAGI].val1;
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ agi -= 2 + sc_data[SC_DECREASEAGI].val1;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ agi -= sc_data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10);
+ }
+
+ return agi;
+}
+
+int status_calc_vit(struct block_list *bl, int vit)
+{
+ struct status_change *sc_data;
+ nullpo_retr(vit,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ vit += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCVIT].timer!=-1)
+ vit += sc_data[SC_INCVIT].val1;
+ if(sc_data[SC_VITFOOD].timer!=-1)
+ vit += sc_data[SC_VITFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ vit += 5;
+ if(sc_data[SC_STRIPARMOR].timer!=-1 && bl->type != BL_PC)
+ vit -= vit * 8*sc_data[SC_STRIPARMOR].val1/100;
+ }
+
+ return vit;
+}
+
+int status_calc_int(struct block_list *bl, int int_)
+{
+ struct status_change *sc_data;
+ nullpo_retr(int_,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ int_ += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCINT].timer!=-1)
+ int_ += sc_data[SC_INCINT].val1;
+ if(sc_data[SC_INTFOOD].timer!=-1)
+ int_ += sc_data[SC_INTFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ int_ += 5;
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ int_ >>= 1;
+ else int_ += sc_data[SC_BLESSING].val1;
+ }
+ if(sc_data[SC_STRIPHELM].timer!=-1 && bl->type != BL_PC)
+ int_ -= int_ * 8*sc_data[SC_STRIPHELM].val1/100;
+ }
+
+ return int_;
+}
+
+int status_calc_dex(struct block_list *bl, int dex)
+{
+ struct status_change *sc_data;
+ nullpo_retr(dex,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ dex += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCDEX].timer!=-1)
+ dex += sc_data[SC_INCDEX].val1;
+ if(sc_data[SC_DEXFOOD].timer!=-1)
+ dex += sc_data[SC_DEXFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ dex += 5;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ dex -= sc_data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10);
+ if(sc_data[SC_BLESSING].timer != -1){
+ int race = status_get_race(bl);
+ if(battle_check_undead(race,status_get_elem_type(bl)) || race == 6)
+ dex >>= 1;
+ else dex += sc_data[SC_BLESSING].val1;
+ }
+ }
+
+ return dex;
+}
+
+int status_calc_luk(struct block_list *bl, int luk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(luk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCALLSTATUS].timer!=-1)
+ luk += sc_data[SC_INCALLSTATUS].val1;
+ if(sc_data[SC_INCLUK].timer!=-1)
+ luk += sc_data[SC_INCLUK].val1;
+ if(sc_data[SC_LUKFOOD].timer!=-1)
+ luk += sc_data[SC_LUKFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer!=-1)
+ luk += 5;
+ if(sc_data[SC_GLORIA].timer!=-1)
+ luk += 30;
+ }
+
+ return luk;
+}
+
+int status_calc_batk(struct block_list *bl, int batk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(batk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_ATKPOTION].timer!=-1)
+ batk += sc_data[SC_ATKPOTION].val1;
+ if(sc_data[SC_BATKFOOD].timer!=-1)
+ batk += sc_data[SC_BATKFOOD].val1;
+ if(sc_data[SC_INCATKRATE].timer!=-1)
+ batk += batk * sc_data[SC_INCATKRATE].val1/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ batk += batk * (2+3*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ batk += batk * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ batk += batk * 3;
+ if(sc_data[SC_JOINTBEAT].timer!=-1 && sc_data[SC_JOINTBEAT].val2==4)
+ batk -= batk * 25/100;
+ if(sc_data[SC_CURSE].timer!=-1)
+ batk -= batk * 25/100;
+ }
+
+ return batk;
+}
+
+int status_calc_watk(struct block_list *bl, int watk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(watk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_IMPOSITIO].timer!=-1)
+ watk += 5*sc_data[SC_IMPOSITIO].val1;
+ if(sc_data[SC_WATKFOOD].timer!=-1)
+ watk += sc_data[SC_WATKFOOD].val1;
+ if(sc_data[SC_DRUMBATTLE].timer!=-1)
+ watk += sc_data[SC_DRUMBATTLE].val2;
+ if(sc_data[SC_VOLCANO].timer!=-1 && status_get_elem_type(bl)==3)
+ watk += sc_data[SC_VOLCANO].val3;
+ if(sc_data[SC_INCATKRATE].timer!=-1)
+ watk += watk * sc_data[SC_INCATKRATE].val1/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ watk += watk * (2+3*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ watk += watk * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ watk += watk * 3;
+ if(sc_data[SC_NIBELUNGEN].timer!=-1 && bl->type != BL_PC && (status_get_element(bl)/10)>=8)
+ watk += sc_data[SC_NIBELUNGEN].val2;
+ if(sc_data[SC_EXPLOSIONSPIRITS].timer!=-1 && bl->type != BL_PC)
+ watk += (1000*sc_data[SC_EXPLOSIONSPIRITS].val1);
+ if(sc_data[SC_CURSE].timer!=-1)
+ watk -= watk * 25/100;
+ if(sc_data[SC_STRIPWEAPON].timer!=-1 && bl->type != BL_PC)
+ watk -= watk * 5*sc_data[SC_STRIPWEAPON].val1/100;
+ }
+ return watk;
+}
+
+int status_calc_matk(struct block_list *bl, int matk)
+{
+ struct status_change *sc_data;
+ nullpo_retr(matk,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_MATKPOTION].timer!=-1)
+ matk += sc_data[SC_MATKPOTION].val1;
+ if(sc_data[SC_MATKFOOD].timer!=-1)
+ matk += sc_data[SC_MATKFOOD].val1;
+ if(sc_data[SC_MAGICPOWER].timer!=-1)
+ matk += matk * 5*sc_data[SC_MAGICPOWER].val1/100;
+ if(sc_data[SC_MINDBREAKER].timer!=-1)
+ matk += matk * 20*sc_data[SC_MINDBREAKER].val1/100;
+ if(sc_data[SC_INCMATKRATE].timer!=-1)
+ matk += matk * sc_data[SC_INCMATKRATE].val1/100;
+ }
+
+ return matk;
+}
+
+int status_calc_critical(struct block_list *bl, int critical)
+{
+ struct status_change *sc_data;
+ nullpo_retr(critical,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if (sc_data[SC_EXPLOSIONSPIRITS].timer!=-1)
+ critical += sc_data[SC_EXPLOSIONSPIRITS].val2;
+ if (sc_data[SC_FORTUNE].timer!=-1)
+ critical += sc_data[SC_FORTUNE].val2*10;
+ if (sc_data[SC_TRUESIGHT].timer!=-1)
+ critical += sc_data[SC_TRUESIGHT].val1*10;
+ if(sc_data[SC_CLOAKING].timer!=-1)
+ critical += critical;
+ }
+
+ return critical;
+}
+
+int status_calc_hit(struct block_list *bl, int hit)
+{
+ struct status_change *sc_data;
+ nullpo_retr(hit,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCHIT].timer != -1)
+ hit += sc_data[SC_INCHIT].val1;
+ if(sc_data[SC_HITFOOD].timer!=-1)
+ hit += sc_data[SC_HITFOOD].val1;
+ if(sc_data[SC_TRUESIGHT].timer != -1)
+ hit += 3*sc_data[SC_TRUESIGHT].val1;
+ if(sc_data[SC_HUMMING].timer!=-1)
+ hit += sc_data[SC_HUMMING].val2;
+ if(sc_data[SC_CONCENTRATION].timer != -1)
+ hit += 10*sc_data[SC_CONCENTRATION].val1;
+ if(sc_data[SC_INCHITRATE].timer != -1)
+ hit += hit * sc_data[SC_INCHITRATE].val1/100;
+ if(sc_data[SC_BLIND].timer != -1)
+ hit -= hit * 25 / 100;
+ }
+
+ return hit;
+}
+
+int status_calc_flee(struct block_list *bl, int flee)
+{
+ struct status_change *sc_data;
+ nullpo_retr(flee,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_INCFLEE].timer!=-1)
+ flee += sc_data[SC_INCFLEE].val1;
+ if(sc_data[SC_FLEEFOOD].timer!=-1)
+ flee += sc_data[SC_FLEEFOOD].val1;
+ if(sc_data[SC_WHISTLE].timer!=-1)
+ flee += sc_data[SC_WHISTLE].val2;
+ if(sc_data[SC_WINDWALK].timer!=-1)
+ flee += flee * sc_data[SC_WINDWALK].val2/100;
+ if(sc_data[SC_INCFLEERATE].timer!=-1)
+ flee += flee * sc_data[SC_INCFLEERATE].val1/100;
+ if(sc_data[SC_VIOLENTGALE].timer!=-1 && status_get_elem_type(bl)==4)
+ flee += flee * sc_data[SC_VIOLENTGALE].val3/100;
+ if(sc_data[SC_MOON_COMFORT].timer!=-1) //SG skill [Komurka]
+ flee += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10;
+ if(sc_data[SC_CLOSECONFINE].timer!=-1)
+ flee += 10;
+ if(sc_data[SC_SPIDERWEB].timer!=-1)
+ flee -= flee * 50/100;
+ if(sc_data[SC_BERSERK].timer!=-1)
+ flee -= flee * 50/100;
+ if(sc_data[SC_BLIND].timer!=-1)
+ flee -= flee * 25/100;
+ }
+
+ if (bl->type == BL_PC && map_flag_gvg(bl->m)) //GVG grounds flee penalty, placed here because it's "like" a status change. [Skotlex]
+ flee -= flee * battle_config.gvg_flee_penalty/100;
+ return flee;
+}
+
+int status_calc_flee2(struct block_list *bl, int flee2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(flee2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_WHISTLE].timer!=-1)
+ flee2 += sc_data[SC_WHISTLE].val3*10;
+ }
+
+ return flee2;
+}
+
+int status_calc_def(struct block_list *bl, int def)
+{
+ struct status_change *sc_data;
+ nullpo_retr(def,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_KEEPING].timer!=-1)
+ return 100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ return 90;
+ if(sc_data[SC_SKA].timer != -1) // [marquis007]
+ return 90;
+ if(sc_data[SC_DRUMBATTLE].timer!=-1)
+ def += sc_data[SC_DRUMBATTLE].val3;
+ if(sc_data[SC_INCDEFRATE].timer!=-1)
+ def += def * sc_data[SC_INCDEFRATE].val1/100;
+ if(sc_data[SC_SIGNUMCRUCIS].timer!=-1)
+ def -= def * sc_data[SC_SIGNUMCRUCIS].val2/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ def -= def * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def -= def * 50/100;
+ if(sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) // Provoke doesn't alter player defense.
+ def -= def * (5+5*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_STRIPSHIELD].timer!=-1 && bl->type != BL_PC)
+ def -= def * 3*sc_data[SC_STRIPSHIELD].val1/100;
+ }
+
+ return def;
+}
+
+int status_calc_def2(struct block_list *bl, int def2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(def2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_ETERNALCHAOS].timer!=-1)
+ return 0;
+ if(sc_data[SC_SUN_COMFORT].timer!=-1)
+ def2 += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/2;
+ if(sc_data[SC_ANGELUS].timer!=-1)
+ def2 += def2 * (10+5*sc_data[SC_ANGELUS].val1)/100;
+ if(sc_data[SC_CONCENTRATION].timer!=-1)
+ def2 -= def2 * 5*sc_data[SC_CONCENTRATION].val1/100;
+ if(sc_data[SC_POISON].timer!=-1)
+ def2 -= def2 * 25/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def2 -= def2 * 50/100;
+ if(sc_data[SC_PROVOKE].timer!=-1)
+ def2 -= def2 * (5+5*sc_data[SC_PROVOKE].val1)/100;
+ if(sc_data[SC_SKE].timer!=-1)
+ def2 /= 2;
+ if(sc_data[SC_JOINTBEAT].timer!=-1){
+ if(sc_data[SC_JOINTBEAT].val2==3)
+ def2 -= def2 * 50/100;
+ else if(sc_data[SC_JOINTBEAT].val2==4)
+ def2 -= def2 * 25/100;
+ }
+ }
+
+ return def2;
+}
+
+int status_calc_mdef(struct block_list *bl, int mdef)
+{
+ struct status_change *sc_data;
+ nullpo_retr(mdef,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_BARRIER].timer!=-1)
+ return 100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ return 90;
+ if(sc_data[SC_SKA].timer != -1) // [marquis007]
+ return 90; // should it up mdef too?
+ if(sc_data[SC_ENDURE].timer!=-1)
+ mdef += sc_data[SC_ENDURE].val1;
+ }
+
+ return mdef;
+}
+
+int status_calc_mdef2(struct block_list *bl, int mdef2)
+{
+ struct status_change *sc_data;
+ nullpo_retr(mdef2,bl);
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data){
+ if(sc_data[SC_BERSERK].timer!=-1)
+ return 0;
+ if(sc_data[SC_MINDBREAKER].timer!=-1)
+ mdef2 -= mdef2 * 12*sc_data[SC_MINDBREAKER].val1/100;
+ }
+
+ return mdef2;
+}
+
+int status_calc_speed(struct block_list *bl, int speed)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_CURSE].timer!=-1)
+ speed += 450;
+ if(sc_data[SC_SWOO].timer != -1) // [marquis007]
+ speed += 450; //Let's use Curse's slow down momentarily (exact value unknown)
+ if(sc_data[SC_SPEEDUP1].timer!=-1)
+ speed -= speed*50/100;
+ else if(sc_data[SC_SPEEDUP0].timer!=-1)
+ speed -= speed*25/100;
+ else if(sc_data[SC_INCREASEAGI].timer!=-1)
+ speed -= speed * 25/100;
+ else if(sc_data[SC_CARTBOOST].timer!=-1)
+ speed -= speed * 20/100;
+ else if(sc_data[SC_BERSERK].timer!=-1)
+ speed -= speed * 20/100;
+ else if(sc_data[SC_WINDWALK].timer!=-1)
+ speed -= speed * 4*sc_data[SC_WINDWALK].val2/100;
+ if(sc_data[SC_WEDDING].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_SLOWDOWN].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_SKA].timer!=-1)
+ speed += speed * 25/100;
+ if(sc_data[SC_QUAGMIRE].timer!=-1)
+ speed += speed * 50/100;
+ if(sc_data[SC_DONTFORGETME].timer!=-1)
+ speed += speed * sc_data[SC_DONTFORGETME].val3/100;
+ if(sc_data[SC_DEFENDER].timer!=-1)
+ speed += speed * (55-5*sc_data[SC_DEFENDER].val1)/100;
+ if(sc_data[SC_GOSPEL].timer!=-1 && sc_data[SC_GOSPEL].val4 == BCT_ENEMY)
+ speed += speed * 25/100;
+ if(sc_data[SC_JOINTBEAT].timer!=-1) {
+ if (sc_data[SC_JOINTBEAT].val2 == 0)
+ speed += speed * 50/100;
+ else if (sc_data[SC_JOINTBEAT].val2 == 2)
+ speed += speed * 30/100;
+ }
+ if(sc_data[SC_CLOAKING].timer!=-1)
+ speed = speed * (sc_data[SC_CLOAKING].val3-3*sc_data[SC_CLOAKING].val1) /100;
+ if(sc_data[SC_CHASEWALK].timer!=-1)
+ speed = speed * sc_data[SC_CHASEWALK].val3/100;
+ if(sc_data[SC_RUN].timer!=-1)/*‹ì‚¯‘«‚É‚æ‚鑬“x•Ï‰»*/
+ speed -= speed * 25/100;
+
+ }
+
+ return speed;
+}
+
+int status_calc_aspd_rate(struct block_list *bl, int aspd_rate)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ int i;
+ if(sc_data[SC_QUAGMIRE].timer==-1 && sc_data[SC_DONTFORGETME].timer==-1){
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1)
+ aspd_rate -= 30;
+ else if(sc_data[SC_ONEHAND].timer!=-1)
+ aspd_rate -= 30;
+ else if(sc_data[SC_ADRENALINE2].timer!=-1)
+ aspd_rate -= (sc_data[SC_ADRENALINE2].val2 || !battle_config.party_skill_penalty)?30:20;
+ else if(sc_data[SC_ADRENALINE].timer!=-1)
+ aspd_rate -= (sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penalty)?30:20;
+ else if(sc_data[SC_SPEARSQUICKEN].timer!=-1)
+ aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2;
+ else if(sc_data[SC_ASSNCROS].timer!=-1 && (bl->type!=BL_PC || ((struct map_session_data*)bl)->status.weapon != 11))
+ aspd_rate -= sc_data[SC_ASSNCROS].val2;
+ }
+ if(sc_data[SC_BERSERK].timer!=-1)
+ aspd_rate -= 30;
+ if(sc_data[i=SC_ASPDPOTION3].timer!=-1 || sc_data[i=SC_ASPDPOTION2].timer!=-1 || sc_data[i=SC_ASPDPOTION1].timer!=-1 || sc_data[i=SC_ASPDPOTION0].timer!=-1)
+ aspd_rate -= sc_data[i].val2;
+ if(sc_data[SC_DONTFORGETME].timer!=-1)
+ aspd_rate += sc_data[SC_DONTFORGETME].val2;
+ if(sc_data[SC_STEELBODY].timer!=-1)
+ aspd_rate += 25;
+ if(sc_data[SC_SKA].timer!=-1)
+ aspd_rate += 25;
+ if(sc_data[SC_DEFENDER].timer != -1)
+ aspd_rate += 25 -sc_data[SC_DEFENDER].val1*5;
+ if(sc_data[SC_GOSPEL].timer!=-1 && sc_data[SC_GOSPEL].val4 == BCT_ENEMY)
+ aspd_rate += 25;
+ if(sc_data[SC_GRAVITATION].timer!=-1)
+ aspd_rate += sc_data[SC_GRAVITATION].val2;
+ if(sc_data[SC_JOINTBEAT].timer!=-1) {
+ if (sc_data[SC_JOINTBEAT].val2 == 1)
+ aspd_rate += 25;
+ else if (sc_data[SC_JOINTBEAT].val2 == 2)
+ aspd_rate += 10;
+
+ if(sc_data[SC_STAR_COMFORT].timer!=-1 && bl->m == ((struct map_session_data *)bl)->feel_map[2].m) //SG skill [Komurka]
+ aspd_rate -= (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10;
+ }
+ }
+
+ return aspd_rate;
+}
+
+int status_calc_maxhp(struct block_list *bl, int maxhp)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_INCMHPRATE].timer!=-1)
+ maxhp += maxhp * sc_data[SC_INCMHPRATE].val1/100;
+ if(sc_data[SC_APPLEIDUN].timer!=-1)
+ maxhp += maxhp * sc_data[SC_APPLEIDUN].val2/100;
+ if(sc_data[SC_DELUGE].timer!=-1 && status_get_elem_type(bl)==1)
+ maxhp += maxhp * deluge_eff[sc_data[SC_DELUGE].val1-1]/100;
+ if(sc_data[SC_BERSERK].timer!=-1)
+ maxhp += maxhp * 2;
+ }
+
+ return maxhp;
+}
+
+int status_calc_maxsp(struct block_list *bl, int maxsp)
+{
+ struct status_change *sc_data;
+ sc_data = status_get_sc_data(bl);
+
+ if(sc_data) {
+ if(sc_data[SC_INCMSPRATE].timer!=-1)
+ maxsp += maxsp * sc_data[SC_INCMSPRATE].val1/100;
+ if(sc_data[SC_SERVICE4U].timer!=-1)
+ maxsp += maxsp * sc_data[SC_SERVICE4U].val2/100;
+ }
+
+ return maxsp;
+}
+
+/*==========================================
+ * For quick calculating [Celest] Adapted by [Skotlex]
+ *------------------------------------------
+ */
+int status_quick_recalc_speed(struct map_session_data *sd, int skill_num, int skill_lv, char start)
+{
+ /* [Skotlex]
+ This function individually changes a character's speed upon a skill change and restores it upon it's ending.
+ Should only be used on non-inclusive skills to avoid exploits.
+ Currently used for freedom of cast
+ and when cloaking changes it's val3 (in which case the new val3 value comes in the level.
+ */
+
+ int b_speed;
+
+ b_speed = sd->speed;
+
+ switch (skill_num)
+ {
+ case SA_FREECAST:
+ if (start)
+ {
+ sd->prev_speed = sd->speed;
+ sd->speed = sd->speed*(175 - skill_lv*5)/100;
+ }
+ else
+ sd->speed = sd->prev_speed;
+ break;
+ case AS_CLOAKING:
+ if (start && sd->sc_data[SC_CLOAKING].timer != -1)
+ { //There shouldn't be an "stop" case here.
+ //If the previous upgrade was
+ //SPEED_ADD_RATE(3*sd->sc_data[SC_CLOAKING].val1 -sd->sc_data[SC_CLOAKING].val3);
+ //Then just changing val3 should be a net difference of....
+ if (3*sd->sc_data[SC_CLOAKING].val1 != sd->sc_data[SC_CLOAKING].val3) //This reverts the previous value.
+ sd->speed = sd->speed * 100 /(sd->sc_data[SC_CLOAKING].val3-3*sd->sc_data[SC_CLOAKING].val1);
+ sd->sc_data[SC_CLOAKING].val3 = skill_lv;
+ sd->speed = sd->speed * (sd->sc_data[SC_CLOAKING].val3-sd->sc_data[SC_CLOAKING].val1*3) /100;
+ }
+ break;
+ }
+
+ if(sd->speed < battle_config.max_walk_speed)
+ sd->speed = battle_config.max_walk_speed;
+
+ if(b_speed != sd->speed)
+ clif_updatestatus(sd,SP_SPEED);
+
+ return 0;
+}
+
+/*==========================================
+ * ‘ÎÛ‚ÌClass‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_class(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->class_;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.class_;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->class_;
+ return 0;
+}
+/*==========================================
+ * ‘ÎÛ‚Ì•ûŒü‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_dir(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->dir;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->dir;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->dir;
+ return 0;
+}
+/*==========================================
+ * ‘Îۂ̃Œƒxƒ‹‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_lv(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->level;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.base_level;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->pet.level;
+ return 0;
+}
+
+/*==========================================
+ * ‘ÎÛ‚ÌŽË’ö‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_range(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->range;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->attackrange;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->range;
+ return 0;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌHP‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_hp(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->hp;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.hp;
+ return 1;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌMHP‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_max_hp(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.max_hp;
+ else {
+ int max_hp = 1;
+
+ if(bl->type == BL_MOB) {
+ struct mob_data *md;
+ nullpo_retr(1, md = (struct mob_data *)bl);
+ max_hp = md->max_hp;
+
+ if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris]
+ max_hp += (md->level - md->db->lv) * status_get_vit(bl);
+
+ }
+ else if(bl->type == BL_PET) {
+ struct pet_data *pd;
+ nullpo_retr(1, pd = (struct pet_data*)bl);
+ max_hp = pd->db->max_hp;
+ }
+
+ max_hp = status_calc_maxhp(bl,max_hp);
+ if(max_hp < 1) max_hp = 1;
+ return max_hp;
+ }
+}
+/*==========================================
+ * ‘ÎÛ‚ÌStr‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_str(struct block_list *bl)
+{
+ int str = 0;
+ nullpo_retr(0, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[0];
+ else {
+ if(bl->type == BL_MOB) {
+ str = ((struct mob_data *)bl)->db->str;
+ if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris]
+ str += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ str/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ str*=2;
+ } else if(bl->type == BL_PET){ //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ str = ((struct pet_data *)bl)->status->str;
+ else
+ str = ((struct pet_data *)bl)->db->str;
+ }
+
+ str = status_calc_str(bl,str);
+ }
+ if(str < 0) str = 0;
+ return str;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌAgi‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+
+int status_get_agi(struct block_list *bl)
+{
+ int agi=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->paramc[1];
+ else {
+ if(bl->type == BL_MOB) {
+ agi = ((struct mob_data *)bl)->db->agi;
+ if(battle_config.mobs_level_up) // increase of mobs leveling up [Valaris]
+ agi += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ agi/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ agi*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ agi = ((struct pet_data *)bl)->status->agi;
+ else
+ agi = ((struct pet_data *)bl)->db->agi;
+ }
+
+ agi = status_calc_agi(bl,agi);
+ }
+ if(agi < 0) agi = 0;
+ return agi;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌVit‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_vit(struct block_list *bl)
+{
+ int vit = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[2];
+ else {
+ if(bl->type == BL_MOB) {
+ vit = ((struct mob_data *)bl)->db->vit;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ vit += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sizes monsters [Valaris]
+ vit/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ vit*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ vit = ((struct pet_data *)bl)->status->vit;
+ else
+ vit = ((struct pet_data *)bl)->db->vit;
+ }
+
+ vit = status_calc_vit(bl,vit);
+ }
+ if(vit < 0) vit = 0;
+ return vit;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌInt‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_int(struct block_list *bl)
+{
+ int int_=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[3];
+ else {
+ if(bl->type == BL_MOB) {
+ int_ = ((struct mob_data *)bl)->db->int_;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ int_ += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ int_/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ int_*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ int_ = ((struct pet_data *)bl)->status->int_;
+ else
+ int_ = ((struct pet_data *)bl)->db->int_;
+ }
+
+ int_ = status_calc_int(bl,int_);
+ }
+ if(int_ < 0) int_ = 0;
+ return int_;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌDex‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_dex(struct block_list *bl)
+{
+ int dex = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->paramc[4];
+ else {
+ if(bl->type == BL_MOB) {
+ dex = ((struct mob_data *)bl)->db->dex;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ dex += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ dex/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ dex*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ dex = ((struct pet_data *)bl)->status->dex;
+ else
+ dex = ((struct pet_data *)bl)->db->dex;
+ }
+
+ dex = status_calc_dex(bl,dex);
+ }
+ if(dex < 0) dex = 0;
+ return dex;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌLuk‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_luk(struct block_list *bl)
+{
+ int luk = 0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->paramc[5];
+ else {
+ if(bl->type == BL_MOB) {
+ luk = ((struct mob_data *)bl)->db->luk;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ luk += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris]
+ luk/=2;
+ else if(((struct mob_data*)bl)->special_state.size==2)
+ luk*=2;
+ } else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ luk = ((struct pet_data *)bl)->status->luk;
+ else
+ luk = ((struct pet_data *)bl)->db->luk;
+ }
+
+ luk = status_calc_luk(bl,luk);
+ }
+ if(luk < 0) luk = 0;
+ return luk;
+}
+
+/*==========================================
+ * ‘ÎÛ‚ÌFlee‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_flee(struct block_list *bl)
+{
+ int flee = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->flee;
+
+ flee = status_calc_flee(bl,status_get_agi(bl)+status_get_lv(bl));
+ if(flee < 1) flee = 1;
+ return flee;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌHit‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_hit(struct block_list *bl)
+{
+ int hit = 1;
+ nullpo_retr(1, bl);
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->hit;
+
+ hit = status_calc_hit(bl,status_get_dex(bl)+status_get_lv(bl));
+ if(hit < 1) hit = 1;
+ return hit;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌŠ®‘S‰ñ”ð‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_flee2(struct block_list *bl)
+{
+ int flee2 = 1;
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->flee2;
+
+ flee2 = status_calc_flee2(bl,status_get_luk(bl)+10);
+ if (flee2 < 1) flee2 = 1;
+ return flee2;
+}
+/*==========================================
+ * ‘Îۂ̃NƒŠƒeƒBƒJƒ‹‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_critical(struct block_list *bl)
+{
+ int critical = 1;
+ nullpo_retr(1, bl);
+
+ if (bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->critical;
+
+ critical = status_get_luk(bl)*3+10;
+ if(battle_config.enemy_critical_rate != 100)
+ critical = critical*battle_config.enemy_critical_rate/100;
+ critical = status_calc_critical(bl,critical);
+ if (critical < 1) critical = 1;
+ return critical;
+}
+/*==========================================
+ * base_atk‚̎擾
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_batk(struct block_list *bl)
+{
+ int batk = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC) {
+ batk = ((struct map_session_data *)bl)->base_atk;
+ if (((struct map_session_data *)bl)->status.weapon < 16)
+ batk += ((struct map_session_data *)bl)->weapon_atk[((struct map_session_data *)bl)->status.weapon];
+ } else {
+ int str,dstr;
+ str = status_get_str(bl); //STR
+ dstr = str/10;
+ batk = dstr*dstr + str; //base_atk‚ðŒvŽZ‚·‚é
+
+ if(bl->type == BL_MOB && ((struct mob_data *)bl)->guardian_data)
+ batk += batk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+
+ batk = status_calc_batk(bl,batk);
+ }
+ if(batk < 1) batk = 1; //base_atk‚ÍÅ’á‚Å‚à1
+ return batk;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌAtk‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_atk(struct block_list *bl)
+{
+ int atk=0;
+ nullpo_retr(0, bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->right_weapon.watk;
+ case BL_MOB:
+ atk = ((struct mob_data*)bl)->db->atk1;
+ if(((struct mob_data *)bl)->guardian_data)
+ atk += atk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+ break;
+ case BL_PET: //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ atk = ((struct pet_data *)bl)->status->atk1;
+ else
+ atk = ((struct pet_data*)bl)->db->atk1;
+ break;
+ }
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ atk = status_calc_watk(bl,atk);
+ if(atk < 0) atk = 0;
+ return atk;
+}
+/*==========================================
+ * ‘Îۂ̶ŽèAtk‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_atk_(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC){
+ return ((struct map_session_data*)bl)->left_weapon.watk;
+ }
+ return 0;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌAtk2‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_atk2(struct block_list *bl)
+{
+ int atk2=0;
+ nullpo_retr(0, bl);
+
+ switch (bl->type) {
+ case BL_PC:
+ return ((struct map_session_data*)bl)->right_weapon.watk2;
+ case BL_MOB:
+ atk2 = ((struct mob_data*)bl)->db->atk2;
+
+ if(((struct mob_data *)bl)->guardian_data)
+ atk2 += atk2 * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv
+ break;
+ case BL_PET: //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ atk2 = ((struct pet_data *)bl)->status->atk2;
+ else
+ atk2 = ((struct pet_data*)bl)->db->atk2;
+ break;
+ }
+
+ // Absolute, then relative modifiers from status changes (shared between PC and NPC)
+ atk2 = status_calc_watk(bl,atk2);
+
+ if(atk2 < 0) atk2 = 0;
+ return atk2;
+}
+/*==========================================
+ * ‘Îۂ̶ŽèAtk2‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_atk_2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data*)bl)->left_weapon.watk2;
+ return 0;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌMAtk1‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_matk1(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->matk1;
+ else {
+ int matk = 0;
+ int int_ = status_get_int(bl);
+ matk = status_calc_matk(bl,int_+(int_/5)*(int_/5));
+ return matk;
+ }
+}
+/*==========================================
+ * ‘ÎÛ‚ÌMAtk2‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_matk2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->matk2;
+ else {
+ int matk = 0;
+ int int_ = status_get_int(bl);
+ matk = status_calc_matk(bl,int_+(int_/7)*(int_/7));
+ return matk;
+ }
+}
+/*==========================================
+ * ‘ÎÛ‚ÌDef‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_def(struct block_list *bl)
+{
+ int def=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC){
+ def = ((struct map_session_data *)bl)->def;
+ if(((struct map_session_data *)bl)->skilltimer != -1)
+ def -= def * skill_get_castdef(((struct map_session_data *)bl)->skillid)/100;
+ } else if(bl->type==BL_MOB) {
+ def = ((struct mob_data *)bl)->db->def;
+ def -= def * skill_get_castdef(((struct mob_data *)bl)->skillid)/100;
+ } else if(bl->type==BL_PET)
+ def = ((struct pet_data *)bl)->db->def;
+
+ def = status_calc_def(bl,def);
+ if(def < 0) def = 0;
+
+ return def;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌDef2‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ *------------------------------------------
+ */
+int status_get_def2(struct block_list *bl)
+{
+ int def2 = 1;
+ nullpo_retr(1, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->def2;
+ else if(bl->type==BL_MOB)
+ def2 = ((struct mob_data *)bl)->db->vit;
+ else if(bl->type==BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ def2 = ((struct pet_data *)bl)->status->vit;
+ else
+ def2 = ((struct pet_data *)bl)->db->vit;
+ }
+
+ def2 = status_calc_def2(bl,def2);
+ if(def2 < 1) def2 = 1;
+
+ return def2;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌMDef‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_mdef(struct block_list *bl)
+{
+ int mdef=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->mdef;
+ else if(bl->type==BL_MOB)
+ mdef = ((struct mob_data *)bl)->db->mdef;
+ else if(bl->type==BL_PET)
+ mdef = ((struct pet_data *)bl)->db->mdef;
+
+ mdef = status_calc_mdef(bl,mdef);
+ if(mdef < 0) mdef = 0;
+
+ return mdef;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌMDef2‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å0ˆÈã
+ *------------------------------------------
+ */
+int status_get_mdef2(struct block_list *bl)
+{
+ int mdef2=0;
+ nullpo_retr(0, bl);
+
+ if(bl->type == BL_PC)
+ return ((struct map_session_data *)bl)->mdef2 + (((struct map_session_data *)bl)->paramc[2]>>1);
+ else if(bl->type == BL_MOB)
+ mdef2 = ((struct mob_data *)bl)->db->int_ + (((struct mob_data *)bl)->db->vit>>1);
+ else if(bl->type == BL_PET) { //<Skotlex> Use pet's stats
+ if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status)
+ mdef2 = ((struct pet_data *)bl)->status->int_ +(((struct pet_data *)bl)->status->vit>>1);
+ else
+ mdef2 = ((struct pet_data *)bl)->db->int_ + (((struct pet_data *)bl)->db->vit>>1);
+ }
+
+ mdef2 = status_calc_mdef2(bl,mdef2);
+ if(mdef2 < 0) mdef2 = 0;
+
+ return mdef2;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌSpeed(ˆÚ“®‘¬“x)‚ð•Ô‚·(”Ä—p)
+ * –ß‚è‚Í®”‚Å1ˆÈã
+ * Speed‚ͬ‚³‚¢‚Ù‚¤‚ªˆÚ“®‘¬“x‚ª‘¬‚¢
+ *------------------------------------------
+ */
+int status_get_speed(struct block_list *bl)
+{
+ int speed = 1000;
+ nullpo_retr(1000, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->speed;
+ else if(bl->type==BL_MOB) {
+ speed = ((struct mob_data *)bl)->speed;
+ if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris]
+ speed-=((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv;
+ }
+ else if(bl->type==BL_PET)
+ speed = ((struct pet_data *)bl)->msd->petDB->speed;
+ else if(bl->type==BL_NPC) //Added BL_NPC (Skotlex)
+ speed = ((struct npc_data *)bl)->speed;
+
+ speed = status_calc_speed(bl,speed);
+
+ if(speed < 1) speed = 1;
+ return speed;
+}
+/*==========================================
+ * ‘ÎÛ‚ÌaDelay(UŒ‚ŽžƒfƒBƒŒƒC)‚ð•Ô‚·(”Ä—p)
+ * aDelay‚ͬ‚³‚¢‚Ù‚¤‚ªUŒ‚‘¬“x‚ª‘¬‚¢
+ *------------------------------------------
+ */
+int status_get_adelay(struct block_list *bl)
+{
+ int adelay,aspd_rate;
+ nullpo_retr(4000, bl);
+ switch (bl->type) {
+ case BL_PC:
+ return (((struct map_session_data *)bl)->aspd<<1);
+ case BL_MOB:
+ adelay = ((struct mob_data *)bl)->db->adelay;
+ if(((struct mob_data *)bl)->guardian_data)
+ aspd_rate = 100 - 10*((struct mob_data *)bl)->guardian_data->guardup_lv; // Strengthen Guardians - custom value +10% ASPD / lv
+ else
+ aspd_rate = 100;
+ break;
+ case BL_PET:
+ adelay = ((struct pet_data *)bl)->db->adelay;
+ aspd_rate = 100;
+ break;
+ default:
+ adelay=4000;
+ aspd_rate = 100;
+ break;
+ }
+ aspd_rate = status_calc_aspd_rate(bl,aspd_rate);
+
+ if(aspd_rate != 100)
+ adelay = adelay*aspd_rate/100;
+ if(adelay < battle_config.monster_max_aspd<<1) adelay = battle_config.monster_max_aspd<<1;
+ return adelay;
+}
+int status_get_amotion(struct block_list *bl)
+{
+ nullpo_retr(2000, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->amotion;
+ else {
+ int amotion=2000,aspd_rate = 100;
+ if(bl->type==BL_MOB) {
+ amotion = ((struct mob_data *)bl)->db->amotion;
+
+ if(((struct mob_data *)bl)->guardian_data)
+ aspd_rate -= aspd_rate * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ASPD / lv
+ } else if(bl->type==BL_PET)
+ amotion = ((struct pet_data *)bl)->db->amotion;
+
+ aspd_rate = status_calc_aspd_rate(bl,aspd_rate);
+
+ if(aspd_rate != 100)
+ amotion = amotion*aspd_rate/100;
+ if(amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd;
+ return amotion;
+ }
+ return 2000;
+}
+int status_get_dmotion(struct block_list *bl)
+{
+ int ret;
+ struct status_change *sc_data;
+
+ nullpo_retr(0, bl);
+ sc_data = status_get_sc_data(bl);
+ if(bl->type==BL_MOB){
+ ret=((struct mob_data *)bl)->db->dmotion;
+ if(battle_config.monster_damage_delay_rate != 100)
+ ret = ret*battle_config.monster_damage_delay_rate/100;
+ }
+ else if(bl->type==BL_PC){
+ ret=((struct map_session_data *)bl)->dmotion;
+ if(battle_config.pc_damage_delay_rate != 100)
+ ret = ret*battle_config.pc_damage_delay_rate/100;
+ }
+ else if(bl->type==BL_PET)
+ ret=((struct pet_data *)bl)->db->dmotion;
+ else
+ return 2000;
+
+ if(sc_data && (sc_data[SC_ENDURE].timer!=-1 || sc_data[SC_CONCENTRATION].timer!=-1 || sc_data[SC_BERSERK].timer!=-1))
+ if (!map_flag_gvg(bl->m)) //Only works on non-gvg grounds. [Skotlex]
+ return 0;
+
+ return ret;
+}
+int status_get_element(struct block_list *bl)
+{
+ // removed redundant variable ret [zzo]
+ struct status_change *sc_data = status_get_sc_data(bl);
+
+ nullpo_retr(20, bl);
+
+ if(sc_data) {
+ if( sc_data[SC_BENEDICTIO].timer!=-1 ) // ¹‘Ì~•Ÿ
+ return 26;
+ if( sc_data[SC_FREEZE].timer!=-1 ) // “€Œ‹
+ return 21;
+ if( sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ return 22;
+ }
+ if(bl->type==BL_MOB) // 10‚̈ÊLv*2A‚P‚̈ʑ®«
+ return ((struct mob_data *)bl)->def_ele;
+ if(bl->type==BL_PC)
+ return 20+((struct map_session_data *)bl)->def_ele; // –hŒä‘®«Lv1
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->element;
+
+ return 20;
+}
+//Retrieves the object's element acquired by status changes only.
+int status_get_attack_sc_element(struct block_list *bl)
+{
+ struct status_change *sc_data=status_get_sc_data(bl);
+ if(sc_data) {
+ if( sc_data[SC_WATERWEAPON].timer!=-1) // ƒtƒƒXƒgƒEƒFƒ|ƒ“
+ return 1;
+ if( sc_data[SC_EARTHWEAPON].timer!=-1) // ƒTƒCƒYƒ~ƒbƒNƒEƒFƒ|ƒ“
+ return 2;
+ if( sc_data[SC_FIREWEAPON].timer!=-1) // ƒtƒŒ[ƒ€ƒ‰ƒ“ƒ`ƒƒ[
+ return 3;
+ if( sc_data[SC_WINDWEAPON].timer!=-1) // ƒ‰ƒCƒgƒjƒ“ƒOƒ[ƒ_[
+ return 4;
+ if( sc_data[SC_ENCPOISON].timer!=-1) // ƒGƒ“ƒ`ƒƒƒ“ƒgƒ|ƒCƒYƒ“
+ return 5;
+ if( sc_data[SC_ASPERSIO].timer!=-1) // ƒAƒXƒyƒ‹ƒVƒI
+ return 6;
+ if( sc_data[SC_SHADOWWEAPON].timer!=-1)
+ return 7;
+ if( sc_data[SC_GHOSTWEAPON].timer!=-1)
+ return 8;
+ }
+ return 0;
+}
+
+
+int status_get_attack_element(struct block_list *bl)
+{
+ int ret = status_get_attack_sc_element(bl);
+
+ nullpo_retr(0, bl);
+
+ if (ret) return ret;
+
+ if(bl->type==BL_MOB && (struct mob_data *)bl)
+ return 0;
+ if(bl->type==BL_PC && (struct map_session_data *)bl)
+ return ((struct map_session_data *)bl)->right_weapon.atk_ele;
+ if(bl->type==BL_PET && (struct pet_data *)bl)
+ return 0;
+
+ return 0;
+}
+int status_get_attack_element2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC) {
+ // removed redundant var, speeded up a bit [zzo]
+ int ret = status_get_attack_sc_element(bl);
+
+ if(ret) return ret;
+ return ((struct map_session_data *)bl)->left_weapon.atk_ele;
+ }
+ return 0;
+}
+int status_get_party_id(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.party_id;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->status.party_id;
+ if(bl->type==BL_MOB){
+ struct mob_data *md=(struct mob_data *)bl;
+ if( md->master_id>0 )
+ {
+ struct map_session_data *msd;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.party_id;
+ return -md->master_id;
+ }
+ return 0; //No party.
+ }
+ if(bl->type==BL_SKILL)
+ return ((struct skill_unit *)bl)->group->party_id;
+ return 0;
+}
+
+int status_get_guild_id(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_PC)
+ return ((struct map_session_data *)bl)->status.guild_id;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->msd->status.guild_id;
+ if(bl->type==BL_MOB)
+ {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->guild_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.guild_id; //Alchemist's mobs [Skotlex]
+ return 0; //No guild.
+ }
+ if(bl->type==BL_SKILL)
+ return ((struct skill_unit *)bl)->group->guild_id;
+ return 0;
+}
+int status_get_race(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->race;
+ if(bl->type==BL_PC)
+ return 7;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race;
+ return 0;
+}
+int status_get_size(struct block_list *bl)
+{
+ nullpo_retr(1, bl);
+ switch (bl->type) {
+ case BL_MOB:
+ if (((struct mob_data *)bl)->sc_data[SC_SWOO].timer != -1) // [marquis007]
+ return 0;
+ return ((struct mob_data *)bl)->db->size;
+ case BL_PET:
+ return ((struct pet_data *)bl)->db->size;
+ case BL_PC:
+ {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if (sd->sc_data[SC_SWOO].timer != -1)
+ return 0;
+ if (sd->class_&JOBL_BABY) //[Lupus]
+ return (pc_isriding(sd) && battle_config.character_size&2); //Baby Class Peco Rider + enabled option -> size = 1, else 0
+ return 1+(pc_isriding(sd) && battle_config.character_size&1); //Peco Rider + enabled option -> size = 2, else 1
+ }
+ }
+ return 1;
+}
+int status_get_mode(struct block_list *bl)
+{
+ nullpo_retr(MD_CANMOVE, bl);
+ if(bl->type==BL_MOB)
+ {
+ if (((struct mob_data *)bl)->mode)
+ return ((struct mob_data *)bl)->mode;
+ return ((struct mob_data *)bl)->db->mode;
+ }
+ if(bl->type==BL_PC)
+ return (MD_CANMOVE|MD_LOOTER|MD_CANATTACK);
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mode;
+ if (bl->type==BL_SKILL)
+ return (MD_CANATTACK|MD_CANMOVE); //Default mode for skills: Can attack, can move (think dances).
+ //Default universal mode, can move
+ return MD_CANMOVE; // ‚Æ‚è‚ ‚¦‚¸“®‚­‚Æ‚¢‚¤‚±‚Æ‚Å1
+}
+
+int status_get_mexp(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->mexp;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mexp;
+ return 0;
+}
+int status_get_race2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->db->race2;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race2;
+ return 0;
+}
+int status_isdead(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->state.state == MS_DEAD;
+ if(bl->type==BL_PC)
+ return pc_isdead((struct map_session_data *)bl);
+ return 0;
+}
+int status_isimmune(struct block_list *bl)
+{
+ struct map_session_data *sd = (struct map_session_data *)bl;
+
+ nullpo_retr(0, bl);
+ if (bl->type == BL_PC) {
+ if (sd->special_state.no_magic_damage)
+ return 1;
+ if (sd->sc_count && sd->sc_data[SC_HERMODE].timer != -1)
+ return 1;
+ }
+ return 0;
+}
+
+// StatusChangeŒn‚ÌŠ“¾
+struct status_change *status_get_sc_data(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data*)bl)->sc_data;
+ if(bl->type==BL_PC)
+ return ((struct map_session_data*)bl)->sc_data;
+ return NULL;
+}
+short *status_get_sc_count(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->sc_count;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->sc_count;
+ return NULL;
+}
+short *status_get_opt1(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt1;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt1;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt1;
+ return 0;
+}
+short *status_get_opt2(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt2;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt2;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt2;
+ return 0;
+}
+short *status_get_opt3(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->opt3;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->opt3;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->opt3;
+ return 0;
+}
+short *status_get_option(struct block_list *bl)
+{
+ nullpo_retr(0, bl);
+ if(bl->type==BL_MOB)
+ return &((struct mob_data*)bl)->option;
+ if(bl->type==BL_PC)
+ return &((struct map_session_data*)bl)->status.option;
+ if(bl->type==BL_NPC)
+ return &((struct npc_data*)bl)->option;
+ return 0;
+}
+
+int status_get_sc_def(struct block_list *bl, int type)
+{
+ int sc_def;
+ nullpo_retr(0, bl);
+
+ switch (type)
+ {
+ case SP_MDEF1: // mdef
+ sc_def = 100 - (3 + status_get_mdef(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_MDEF2: // int
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_DEF1: // def
+ sc_def = 100 - (3 + status_get_def(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_DEF2: // vit
+ sc_def = 100 - (3 + status_get_vit(bl) + status_get_luk(bl)/3);
+ break;
+ case SP_LUK: // luck
+ sc_def = 100 - (3 + status_get_luk(bl));
+ break;
+
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def = 100 - (3 + status_get_mdef(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_STAN:
+ case SC_POISON:
+ case SC_SILENCE:
+ sc_def = 100 - (3 + status_get_vit(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_SLEEP:
+ case SC_CONFUSION:
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_luk(bl)/3);
+ break;
+ case SC_BLIND:
+ sc_def = 100 - (3 + status_get_int(bl) + status_get_vit(bl)/3);
+ break;
+ case SC_CURSE:
+ sc_def = 100 - (3 + status_get_luk(bl) + status_get_vit(bl)/3);
+ break;
+
+ default:
+ sc_def = 100;
+ break;
+ }
+
+ if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->class_ == MOBID_EMPERIUM)
+ return 0;
+ if (sc_def < 50)
+ sc_def = 50;
+ } else if(bl->type == BL_PC) {
+ struct status_change* sc_data = status_get_sc_data(bl);
+ if (sc_data)
+ {
+ if (sc_data[SC_SCRESIST].timer != -1)
+ sc_def -= sc_data[SC_SCRESIST].val1; //Status resist
+ else if (sc_data[SC_SIEGFRIED].timer != -1)
+ sc_def -= sc_data[SC_SIEGFRIED].val2; //Status resistance.
+ }
+ }
+ return (sc_def < 0) ? 0 : sc_def;
+}
+
+/*==========================================
+ * Starts a status change.
+ * type = type, val1~4 depend on the type.
+ * Tick is base duration
+ * flag:
+ * &1: Cannot be avoided (it has to start)
+ * &2: Tick should not be reduced (by vit, luk, lv, etc)
+ * &4: sc_data loaded, no value has to be altered.
+ *------------------------------------------
+ */
+int status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change* sc_data;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+ int opt_flag = 0, calc_flag = 0,updateflag = 0, save_flag = 0, race, mode, elem, undead_flag;
+ int scdef = 0;
+
+ nullpo_retr(0, bl);
+ switch (bl->type)
+ {
+ case BL_PC:
+ sd=(struct map_session_data *)bl;
+ if (status_isdead(bl))
+ return 0;
+ break;
+ case BL_MOB:
+ if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL)
+ return 0; //Emperium can't be afflicted by status changes.
+ if (status_isdead(bl))
+ return 0;
+ break;
+ case BL_PET: //Because pets can't have status changes.
+ case BL_SKILL: //These may happen by attacking traps or the like. [Skotlex]
+ return 0;
+ default:
+ if(battle_config.error_log)
+ ShowError("status_change_start: invalid source type (%d)!\n", bl->type);
+ return 0;
+ }
+ if(type < 0 || type >= SC_MAX) {
+ if(battle_config.error_log)
+ ShowError("status_change_start: invalid status change (%d)!\n", type);
+ return 0;
+ }
+ sc_data=status_get_sc_data(bl);
+ sc_count=status_get_sc_count(bl);
+ option=status_get_option(bl);
+ opt1=status_get_opt1(bl);
+ opt2=status_get_opt2(bl);
+ opt3=status_get_opt3(bl);
+
+ race=status_get_race(bl);
+ mode=status_get_mode(bl);
+ elem=status_get_elem_type(bl);
+ undead_flag=battle_check_undead(race,elem);
+
+ if(type == SC_AETERNA && (sc_data[SC_STONE].timer != -1 || sc_data[SC_FREEZE].timer != -1) )
+ return 0;
+ if(type == SC_OVERTHRUST && sc_data[SC_MAXOVERTHRUST].timer != -1)
+ return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex]
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ scdef=3+status_get_mdef(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_STAN:
+ case SC_SILENCE:
+ case SC_POISON:
+ case SC_DPOISON:
+ scdef=3+status_get_vit(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_SLEEP:
+ case SC_BLIND:
+ scdef=3+status_get_int(bl)+status_get_luk(bl)/3;
+ break;
+ case SC_CURSE:
+ scdef=3+status_get_luk(bl);
+ break;
+ default:
+ scdef=0;
+ }
+ if(scdef>=100)
+ return 0;
+ if(sd){
+ if(type == SC_ADRENALINE && !(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon)))
+ return 0;
+ if( sd && type == SC_ADRENALINE2 && !(skill_get_weapontype(BS_ADRENALINE2)&(1<<sd->status.weapon)))
+ return 0;
+
+ if(SC_COMMON_MIN<=type && type<=SC_COMMON_MAX && !(flag&1)){
+ if(sd->reseff[type-SC_COMMON_MIN] > 0 && rand()%10000<sd->reseff[type-SC_COMMON_MIN]){
+ if(battle_config.battle_log)
+ ShowInfo("PC %d skill_sc_start: status change %d blocked by reseff card (AID: %d).\n",type,bl->id);
+ return 0;
+ }
+ }
+ }
+
+ if((type==SC_FREEZE || type==SC_STONE) && undead_flag && !(flag&1))
+ //I've been informed that undead chars are inmune to stone curse too. [Skotlex]
+ return 0;
+
+
+ if (type==SC_BLESSING && (bl->type==BL_PC || (!undead_flag && race!=6))) {
+ if (sc_data[SC_CURSE].timer!=-1)
+ status_change_end(bl,SC_CURSE,-1);
+ if (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0)
+ status_change_end(bl,SC_STONE,-1);
+ }
+
+ if((type == SC_ADRENALINE || type==SC_ADRENALINE2 || type == SC_WEAPONPERFECTION || type == SC_OVERTHRUST) &&
+ sc_data[type].timer != -1 && sc_data[type].val2 && !val2)
+ return 0;
+
+ if(mode & MD_BOSS && !(flag&1) && ( (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX)
+ || type==SC_QUAGMIRE || type==SC_DECREASEAGI || type==SC_SIGNUMCRUCIS || type==SC_PROVOKE || type==SC_ROKISWEIL
+ || type==SC_COMA
+ || (type == SC_BLESSING && (undead_flag || race == 6)))){
+ /* ƒ{ƒX‚É‚Í?‚©‚È‚¢(‚½‚¾‚µƒJ?ƒh‚É‚æ‚é?‰Ê‚Í“K—p‚³‚ê‚é) */
+ return 0;
+ }
+
+ if(sc_data[type].timer != -1){ /* ‚·‚Å‚É“¯‚¶ˆÙí‚É‚È‚Á‚Ä‚¢‚éꇃ^ƒCƒ}‰ðœ */
+ if(sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION &&
+ type != SC_ASPDPOTION0 && type != SC_ASPDPOTION1 && type != SC_ASPDPOTION2 && type != SC_ASPDPOTION3
+ && type != SC_ATKPOTION && type != SC_MATKPOTION // added atk and matk potions [Valaris]
+ )
+ return 0;
+
+ if ((type >=SC_STAN && type <= SC_BLIND) || type == SC_DPOISON)
+ return 0;/* ?‚¬‘«‚µ‚ª‚Å‚«‚È‚¢?‘ÔˆÙí‚Å‚ ‚鎞‚Í?‘ÔˆÙí‚ðs‚í‚È‚¢ */
+
+ if (type == SC_GOSPEL && sc_data[type].val4 == BCT_SELF) //Must not override a casting gospel char.
+ return 0;
+
+ (*sc_count)--;
+ delete_timer(sc_data[type].timer, status_change_timer);
+ sc_data[type].timer = -1;
+ }
+
+ if(type==SC_FREEZE || type==SC_STAN || type==SC_SLEEP || type==SC_STOP || type == SC_CONFUSION ||
+ type==SC_CLOSECONFINE || type==SC_CLOSECONFINE2)
+ battle_stopwalking(bl,1);
+
+ // ƒNƒAƒOƒ}ƒCƒA/Ž„‚ð–Y‚ê‚È‚¢‚Å’†‚Í–³Œø‚ȃXƒLƒ‹
+ if ((sc_data[SC_QUAGMIRE].timer!=-1 || sc_data[SC_DONTFORGETME].timer!=-1) &&
+ (type==SC_CONCENTRATE || type==SC_INCREASEAGI ||
+ type==SC_TWOHANDQUICKEN || type==SC_SPEARSQUICKEN ||
+ type==SC_ADRENALINE || type==SC_ADRENALINE2 ||
+ type==SC_TRUESIGHT || type==SC_WINDWALK ||
+ type==SC_CARTBOOST || type==SC_ASSNCROS ||
+ type==SC_ONEHAND))
+ return 0;
+
+ switch(type){ /* ˆÙí‚ÌŽí—Þ‚²‚Æ‚Ì?— */
+ case SC_PROVOKE: /* ƒvƒƒ{ƒbƒN */
+ calc_flag = 1;
+ if(tick <= 0) tick = 1000; /* (ƒI?ƒgƒo?ƒT?ƒN) */
+ break;
+ case SC_ENDURE: /* ƒCƒ“ƒfƒ…ƒA */
+ if(tick <= 0) tick = 1000 * 60;
+ calc_flag = 1; // for updating mdef
+ val2 = 7; // [Celest]
+ break;
+ case SC_AUTOBERSERK:
+ {
+ if (!(flag&4))
+ tick = 60*1000;
+ if (bl->type == BL_PC && sd->status.hp<sd->status.max_hp>>2 &&
+ (sc_data[SC_PROVOKE].timer==-1 || sc_data[SC_PROVOKE].val2==0))
+ status_change_start(bl,SC_PROVOKE,10,1,0,0,0,0);
+ }
+ break;
+
+ case SC_INCREASEAGI: /* ‘¬“x㸠*/
+ calc_flag = 1;
+ if(sc_data[SC_DECREASEAGI].timer!=-1 )
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ break;
+ case SC_DECREASEAGI: /* ‘¬“xŒ¸­ */
+ if (bl->type == BL_PC && !(tick&2)) // Celest
+ tick>>=1;
+ calc_flag = 1;
+ if(sc_data[SC_INCREASEAGI].timer!=-1 )
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 )
+ status_change_end(bl,SC_CARTBOOST,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ break;
+ case SC_SIGNUMCRUCIS: /* ƒVƒOƒiƒ€ƒNƒ‹ƒVƒX */
+ calc_flag = 1;
+ val2 = 10 + val1*2;
+ if (!(flag&4))
+ tick = 600*1000;
+ clif_emotion(bl,4);
+ break;
+ case SC_ONEHAND: //Removes the Aspd potion effect, as reported by Vicious. [Skotlex]
+ if(sc_data[SC_ASPDPOTION0].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION0,-1);
+ if(sc_data[SC_ASPDPOTION1].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION1,-1);
+ if(sc_data[SC_ASPDPOTION2].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION2,-1);
+ if(sc_data[SC_ASPDPOTION3].timer!=-1)
+ status_change_end(bl,SC_ASPDPOTION3,-1);
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ return 0;
+ *opt3 |= 1;
+ calc_flag = 1;
+ break;
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE: /* ƒAƒhƒŒƒiƒŠƒ“ƒ‰ƒbƒVƒ… */
+ if(sc_data[SC_DECREASEAGI].timer!=-1)
+ return 0;
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ calc_flag = 1;
+ break;
+ case SC_WEAPONPERFECTION: /* ƒEƒFƒ|ƒ“ƒp?ƒtƒFƒNƒVƒ‡ƒ“ */
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_OVERTHRUST: /* ƒI?ƒo?ƒXƒ‰ƒXƒg */
+ if(bl->type == BL_PC && !(flag&2))
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ *opt3 |= 2;
+ break;
+ case SC_MAXOVERTHRUST: //Cancels Normal Overthrust. [Skotlex]
+ if (sc_data[SC_OVERTHRUST].timer != -1)
+ status_change_end(bl, SC_OVERTHRUST, -1);
+ break;
+ case SC_MAXIMIZEPOWER: /* ƒ}ƒLƒVƒ}ƒCƒYƒpƒ?(SP‚ª1Œ¸‚鎞ŠÔ,val2‚É‚à) */
+ if (!(flag&4))
+ {
+ if(bl->type != BL_PC)
+ tick = 5000;
+ val2 = tick;
+ }
+ break;
+ case SC_ENCPOISON: /* ƒGƒ“ƒ`ƒƒƒ“ƒgƒ|ƒCƒYƒ“ */
+ calc_flag = 1;
+ val2=(((val1 - 1) / 2) + 3)*100; /* “Å•t?Šm—¦ */
+ skill_enchant_elemental_end(bl,SC_ENCPOISON);
+ break;
+ case SC_EDP: // [Celest]
+ val2 = val1 + 2; /* –Ò“Å•t?Šm—¦(%) */
+ calc_flag = 1;
+ break;
+ case SC_POISONREACT: /* ƒ|ƒCƒYƒ“ƒŠƒAƒNƒg */
+ if (!(flag&4))
+ val2=val1/2 + val1%2; // [Celest]
+ break;
+ case SC_ENERGYCOAT: /* ƒGƒiƒW?ƒR?ƒg */
+ *opt3 |= 4;
+ break;
+ case SC_MAGICROD:
+ val2 = val1*20;
+ break;
+ case SC_KYRIE: /* ƒLƒŠƒGƒGƒŒƒCƒ\ƒ“ */
+ if (!(flag&4))
+ {
+ val2 = status_get_max_hp(bl) * (val1 * 2 + 10) / 100;/* ‘Ï‹v“x */
+ val3 = (val1 / 2 + 5); /* ‰ñ? */
+ }
+// -- moonsoul (added to undo assumptio status if target has it)
+ if(sc_data[SC_ASSUMPTIO].timer!=-1 )
+ status_change_end(bl,SC_ASSUMPTIO,-1);
+ break;
+ case SC_MINDBREAKER:
+ calc_flag = 1;
+ if(tick <= 0) tick = 1000; /* (ƒI?ƒgƒo?ƒT?ƒN) */
+ case SC_TRICKDEAD: /* Ž€‚ñ‚¾‚Ó‚è */
+ if (bl->type == BL_PC) {
+ pc_stopattack(sd);
+ }
+ break;
+ case SC_QUAGMIRE: /* ƒNƒ@ƒOƒ}ƒCƒA */
+ calc_flag = 1;
+ if(sc_data[SC_CONCENTRATE].timer!=-1 ) /* W’†—ÍŒüã‰ðœ */
+ status_change_end(bl,SC_CONCENTRATE,-1);
+ if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* ‘¬“x㸉ðœ */
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* ƒgƒDƒ‹?ƒTƒCƒg */
+ status_change_end(bl,SC_TRUESIGHT,-1);
+ if(sc_data[SC_WINDWALK].timer!=-1 ) /* ƒEƒCƒ“ƒhƒEƒH?ƒN */
+ status_change_end(bl,SC_WINDWALK,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 ) /* ƒJ?ƒgƒu?ƒXƒg */
+ status_change_end(bl,SC_CARTBOOST,-1);
+ break;
+ case SC_MAGICPOWER:
+ calc_flag = 1;
+ val2 = 1;
+ break;
+ case SC_SACRIFICE:
+ if (!(flag&4))
+ val2 = 5;
+ break;
+ case SC_ASPERSIO: /* ƒAƒXƒyƒ‹ƒVƒI */
+ skill_enchant_elemental_end(bl,SC_ASPERSIO);
+ break;
+ case SC_FIREWEAPON: /* ƒtƒŒ?ƒ€ƒ‰ƒ“ƒ`ƒƒ? */
+ skill_enchant_elemental_end(bl,SC_FIREWEAPON);
+ break;
+ case SC_WATERWEAPON: /* ƒtƒƒXƒgƒEƒFƒ|ƒ“ */
+ skill_enchant_elemental_end(bl,SC_WATERWEAPON);
+ break;
+ case SC_WINDWEAPON: /* ƒ‰ƒCƒgƒjƒ“ƒOƒ?ƒ_? */
+ skill_enchant_elemental_end(bl,SC_WINDWEAPON);
+ break;
+ case SC_EARTHWEAPON: /* ƒTƒCƒYƒ~ƒbƒNƒEƒFƒ|ƒ“ */
+ skill_enchant_elemental_end(bl,SC_EARTHWEAPON);
+ break;
+ case SC_SHADOWWEAPON:
+ skill_enchant_elemental_end(bl,SC_SHADOWWEAPON);
+ break;
+ case SC_GHOSTWEAPON:
+ skill_enchant_elemental_end(bl,SC_GHOSTWEAPON);
+ break;
+ case SC_PROVIDENCE: /* ƒvƒƒ”ƒBƒfƒ“ƒX */
+ calc_flag = 1;
+ val2=val1*5;
+ break;
+ case SC_REFLECTSHIELD:
+ val2=10+val1*3;
+ break;
+ case SC_STRIPWEAPON:
+ if (val2==0) val2=90;
+ break;
+ case SC_STRIPSHIELD:
+ if (val2==0) val2=85;
+ break;
+
+ case SC_AUTOSPELL: /* ƒI?ƒgƒXƒyƒ‹ */
+ val4 = 5 + val1*2;
+ break;
+
+ case SC_VOLCANO:
+ calc_flag = 1;
+ val3 = val1*10;
+ break;
+ case SC_DELUGE:
+ calc_flag = 1;
+ if (sc_data[SC_FOGWALL].timer != -1 && sc_data[SC_BLIND].timer != -1)
+ status_change_end(bl,SC_BLIND,-1);
+ break;
+ case SC_VIOLENTGALE:
+ calc_flag = 1;
+ val3 = val1*3;
+ break;
+
+ case SC_SPEARSQUICKEN: /* ƒXƒsƒAƒNƒCƒbƒPƒ“ */
+ calc_flag = 1;
+ val2 = 20+val1;
+ *opt3 |= 1;
+ break;
+
+ case SC_BLADESTOP: /* ”’nŽæ‚è */
+ if(val2==2) clif_bladestop((struct block_list *)val3,(struct block_list *)val4,1);
+ *opt3 |= 32;
+ break;
+
+ case SC_LULLABY: /* ŽqŽç‰S */
+ case SC_RICHMANKIM:
+ case SC_ROKISWEIL: /* ƒƒL‚Ì‹©‚Ñ */
+ case SC_INTOABYSS: /* [•£‚Ì’†‚É */
+ case SC_POEMBRAGI: /* ƒuƒ‰ƒM‚ÌŽ */
+ case SC_UGLYDANCE: /* Ž©•ªŸŽè‚ȃ_ƒ“ƒX */
+ break;
+ case SC_ETERNALCHAOS: /* ƒGƒ^?ƒiƒ‹ƒJƒIƒX */
+ case SC_DRUMBATTLE: /* ?‘¾ŒÛ‚Ì‹¿‚« */
+ case SC_NIBELUNGEN: /* ƒj?ƒxƒ‹ƒ“ƒO‚ÌŽw—Ö */
+ case SC_SIEGFRIED: /* •sŽ€g‚̃W?ƒNƒtƒŠ?ƒh */
+ case SC_WHISTLE: /* Œû“J */
+ case SC_ASSNCROS: /* —[—z‚̃AƒTƒVƒ“ƒNƒƒX */
+ case SC_APPLEIDUN: /* ƒCƒhƒDƒ“‚Ì—ÑŒç */
+ case SC_HUMMING: /* ƒnƒ~ƒ“ƒO */
+ case SC_ATKPOTION: // Valaris
+ case SC_MATKPOTION:
+ case SC_FORTUNE: /* K‰^‚̃LƒX */
+ case SC_SERVICE4U: /* ƒT?ƒrƒXƒtƒH?ƒ†? */
+ calc_flag = 1;
+ break;
+ case SC_DONTFORGETME: /* Ž„‚ð–Y‚ê‚È‚¢‚Å */
+ calc_flag = 1;
+ if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* ‘¬“x㸉ðœ */
+ status_change_end(bl,SC_INCREASEAGI,-1);
+ if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_TWOHANDQUICKEN,-1);
+ if(sc_data[SC_ONEHAND].timer!=-1 )
+ status_change_end(bl,SC_ONEHAND,-1);
+ if(sc_data[SC_SPEARSQUICKEN].timer!=-1 )
+ status_change_end(bl,SC_SPEARSQUICKEN,-1);
+ if(sc_data[SC_ADRENALINE].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE,-1);
+ if(sc_data[SC_ADRENALINE2].timer!=-1 )
+ status_change_end(bl,SC_ADRENALINE2,-1);
+ if(sc_data[SC_ASSNCROS].timer!=-1 )
+ status_change_end(bl,SC_ASSNCROS,-1);
+ if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* ƒgƒDƒ‹?ƒTƒCƒg */
+ status_change_end(bl,SC_TRUESIGHT,-1);
+ if(sc_data[SC_WINDWALK].timer!=-1 ) /* ƒEƒCƒ“ƒhƒEƒH?ƒN */
+ status_change_end(bl,SC_WINDWALK,-1);
+ if(sc_data[SC_CARTBOOST].timer!=-1 ) /* ƒJ?ƒgƒu?ƒXƒg */
+ status_change_end(bl,SC_CARTBOOST,-1);
+ break;
+ case SC_MOONLIT:
+ val2 = bl->id;
+ skill_setmapcell(bl,CG_MOONLIT, val1, CELL_SETMOONLIT);
+ break;
+ case SC_DANCING: /* ƒ_ƒ“ƒX/‰‰‘t’† */
+ calc_flag = 1;
+ if (!(flag&4))
+ {
+ val3= tick / 1000;
+ tick = 1000;
+ }
+ break;
+
+ case SC_EXPLOSIONSPIRITS: // ”š—ô”g“®
+ calc_flag = 1;
+ val2 = 75 + 25*val1;
+ *opt3 |= 8;
+ break;
+ case SC_STEELBODY: // ‹à„
+ case SC_SKA:
+ calc_flag = 1;
+ *opt3 |= 16;
+ break;
+ case SC_AUTOCOUNTER:
+ val3 = val4 = 0;
+ break;
+
+ case SC_ASPDPOTION0: /* ?‘¬ƒ|?ƒVƒ‡ƒ“ */
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ calc_flag = 1;
+ if (!(flag&4))
+ val2 = 5*(2+type-SC_ASPDPOTION0);
+ break;
+
+ case SC_XMAS: // Xmas Suit [Valaris]
+ case SC_WEDDING: //Œ‹¥—p(Œ‹¥ˆßÖ‚É‚È‚Á‚Ä?‚­‚Ì‚ª?‚¢‚Æ‚©)
+ {
+ struct map_session_data *sd;
+ if (bl->type == BL_PC && (sd= (struct map_session_data *)bl))
+ { //Change look.
+ if(type==SC_WEDDING)
+ sd->view_class = JOB_WEDDING;
+ else if(type==SC_XMAS)
+ sd->view_class = JOB_XMAS;
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0 &&
+ ((type==SC_WEDDING && !battle_config.wedding_ignorepalette) ||
+ (type==SC_XMAS && !battle_config.xmas_ignorepalette)))
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+ }
+ break;
+ case SC_NOCHAT: //ƒ`ƒƒƒbƒg‹ÖŽ~?‘Ô
+ {
+ if(!battle_config.muting_players)
+ return 0;
+
+ if (!(flag&4))
+ tick = 60000;
+ updateflag = SP_MANNER;
+ save_flag = 1; // celest
+ }
+ break;
+
+ /* option1 */
+ case SC_STONE: /* Ή» */
+ if(!(flag&2)) {
+ int sc_def = status_get_mdef(bl)*200;
+ tick = tick - sc_def;
+ }
+ if (!(flag&4))
+ val3 = tick/1000;
+ if(val3 < 1) val3 = 1;
+ if (!(flag&4))
+ tick = 5000;
+ val2 = 1;
+ break;
+ case SC_SLEEP: /* ‡–° */
+ if(!(flag&4)) {
+ tick = 30000;//‡–°‚̓Xƒe?ƒ^ƒX‘Ï«‚É?‚í‚炸30•b
+ }
+ break;
+ case SC_FREEZE: /* “€Œ‹ */
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_mdef(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+ case SC_STAN: /* ƒXƒ^ƒ“ival2‚Ƀ~ƒŠ•bƒZƒbƒgj */
+ if(!(flag&2)) {
+ int sc_def = status_get_sc_def_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+
+ /* option2 */
+ case SC_DPOISON: /* –Ò“Å */
+ {
+ int hp = status_get_hp(bl);
+ int mhp = status_get_max_hp(bl);
+
+ // MHP?1/4????????
+ if (hp > mhp>>2) {
+ if(bl->type == BL_PC) {
+ int diff = mhp*10/100;
+ if (hp - diff < mhp>>2)
+ diff = hp - (mhp>>2);
+ pc_heal((struct map_session_data *)bl, -diff, 0);
+ } else if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ hp -= mhp*15/100;
+ if (hp > mhp>>2)
+ md->hp = hp;
+ else
+ md->hp = mhp>>2;
+ }
+ }
+ } // fall through
+ case SC_POISON: /* “Å */
+ {
+ int mhp;
+
+ calc_flag = 1;
+ if (flag&4)
+ break;
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_vit(bl) + status_get_luk(bl)/5);
+ tick = tick * sc_def / 100;
+ }
+ val3 = tick/1000;
+ if(val3 < 1) val3 = 1;
+ tick = 1000;
+ mhp = status_get_max_hp(bl);
+ if (bl->type == BL_PC)
+ val4 = (type == SC_DPOISON) ? 3 + mhp/50 : 3 + mhp*3/200;
+ else
+ val4 = (type == SC_DPOISON) ? 3 + mhp/100 : 3 + mhp/200;
+
+ }
+ break;
+ case SC_SILENCE: /* ’¾?iƒŒƒbƒNƒXƒfƒr?ƒij */
+ if (sc_data && sc_data[SC_GOSPEL].timer!=-1) {
+ if (sc_data[SC_GOSPEL].val4 == BCT_SELF) { //Clear Gospel [Skotlex]
+ status_change_end(bl,SC_GOSPEL,-1);
+ }
+ break;
+ }
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+ case SC_CONFUSION:
+ clif_emotion(bl,1);
+ break;
+ case SC_BLIND: /* ˆÃ? */
+ calc_flag = 1;
+ if(!(flag&4) && tick < 1000)
+ tick = 30000;
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_lv(bl)/10 + status_get_int(bl)/15);
+ tick = tick*sc_def/100;
+ if (tick < 5000) //Minimum 5 secs?
+ tick = 5000;
+ }
+ break;
+ case SC_CURSE:
+ calc_flag = 1;
+ if(!(flag&2)) {
+ int sc_def = 100 - status_get_vit(bl);
+ tick = tick * sc_def / 100;
+ }
+ break;
+
+ case SC_BLEEDING:
+ if(!(flag&2)) {
+ int sc_def = 100 - (status_get_lv(bl)/5 +status_get_vit(bl));
+ tick = tick * sc_def / 100;
+ }
+ if(!(flag&4) && tick < 10000) //Minimum bleed time is 10 secs or this sc does nothing! [Skotlex]
+ tick = 10000;
+ val4 = tick;
+ tick = 10000;
+ break;
+
+ /* option */
+ case SC_HIDING: /* ƒnƒCƒfƒBƒ“ƒO */
+ calc_flag = 1;
+ if(bl->type == BL_PC && !(flag&4)) {
+ val2 = tick / 1000; /* Ž?ŽžŠÔ */
+ tick = 1000;
+ }
+ break;
+ case SC_CHASEWALK:
+ case SC_CLOAKING: /* ƒNƒ?ƒLƒ“ƒO */
+ if (flag&4)
+ break;
+ if(bl->type != BL_PC)
+ tick = 5000*val1;
+ calc_flag = 1; // [Celest]
+ val2 = tick;
+ val3 = type==SC_CLOAKING ? 130-val1*3 : 135-val1*5;
+ break;
+ case SC_SIGHT: /* ƒTƒCƒg/ƒ‹ƒAƒt */
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ if (flag&4)
+ break;
+ val2 = tick/250;
+ tick = 10;
+ break;
+
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_READYSTORM: // Taekwon stances SCs [Dralnu]
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ if (flag&4)
+ break;
+ tick = 600*1000;
+ break;
+
+ case SC_AUTOGUARD:
+ if (!flag)
+ {
+ struct map_session_data *tsd;
+ int i,t;
+ for(i=val2=0;i<val1;i++) {
+ t = 5-(i>>1);
+ val2 += (t < 0)? 1:t;
+ }
+ if (sd)
+ for (i = 0; i < 5; i++)
+ { //Pass the status to the other affected chars. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,SC_AUTOGUARD,val1,val2,0,0,tick,1);
+ }
+ }
+ break;
+
+ case SC_DEFENDER:
+ calc_flag = 1;
+ if (!flag)
+ {
+ struct map_session_data *tsd;
+ int i;
+ val2 = 5 + val1*15;
+ if (sd)
+ for (i = 0; i < 5; i++)
+ { //See if there are devoted characters, and pass the status to them. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,SC_DEFENDER,val1,val2,0,0,tick,1);
+ }
+ }
+ break;
+
+ case SC_CONCENTRATION: /* ƒRƒ“ƒZƒ“ƒgƒŒ?ƒVƒ‡ƒ“ */
+ *opt3 |= 1;
+ calc_flag = 1;
+ break;
+
+ case SC_TENSIONRELAX: /* ƒeƒ“ƒVƒ‡ƒ“ƒŠƒ‰ƒbƒNƒX */
+ if (flag&4)
+ break;
+ if(bl->type == BL_PC) {
+ tick = 10000;
+ } else return 0;
+ break;
+
+ case SC_PARRYING: /* ƒpƒŠƒCƒ“ƒO */
+ val2 = 20 + val1*3;
+ break;
+
+ case SC_WINDWALK: /* ƒEƒCƒ“ƒhƒEƒH?ƒN */
+ calc_flag = 1;
+ val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5, movement speed % increase is 4 times that
+ break;
+
+ case SC_JOINTBEAT: // Random break [DracoRPG]
+ calc_flag = 1;
+ val2 = rand()%6;
+ if (val2 == 5) status_change_start(bl,SC_BLEEDING,val1,0,0,0,skill_get_time2(type,val1),0);
+ break;
+
+ case SC_BERSERK: /* ƒo?ƒT?ƒN */
+ if(sd && !(flag&4)){
+ sd->status.hp = sd->status.max_hp * 3;
+ sd->status.sp = 0;
+ clif_updatestatus(sd,SP_HP);
+ clif_updatestatus(sd,SP_SP);
+ sd->canregen_tick = gettick() + 300000;
+ }
+ *opt3 |= 128;
+ if (!(flag&4))
+ tick = 10000;
+ calc_flag = 1;
+ break;
+
+ case SC_ASSUMPTIO: /* ƒAƒXƒ€ƒvƒeƒBƒI */
+ *opt3 |= 2048;
+ if(sc_data[SC_KYRIE].timer!=-1)
+ status_change_end(bl,SC_KYRIE,-1);
+ break;
+
+ case SC_WARM: //SG skills [Komurka]
+ if (!(flag&4)) {
+ val2 = tick/1000;
+ tick = 1000;
+ }
+ *opt3 |= 4096;
+ opt_flag = 1;
+ break;
+
+ case SC_GOSPEL:
+ if (val4 == BCT_SELF) { // self effect
+ if (flag&4)
+ break;
+ val2 = tick;
+ tick = 1000;
+ status_change_clear_buffs(bl);
+ status_change_clear_debuffs(bl); //Gospel clears both types.
+ } else
+ calc_flag = 1;
+ break;
+
+ case SC_MARIONETTE: /* ƒ}ƒŠƒIƒlƒbƒgƒRƒ“ƒgƒ?ƒ‹ */
+ case SC_MARIONETTE2:
+ if (flag&4)
+ break;
+ val2 = tick;
+ if (!val3)
+ return 0;
+ tick = 1000;
+ calc_flag = 1;
+ *opt3 |= 1024;
+ break;
+
+ case SC_REJECTSWORD: /* ƒŠƒWƒFƒNƒgƒ\?ƒh */
+ val2 = 3; //3‰ñU?‚𒵂˕Ԃ·
+ break;
+
+ case SC_MEMORIZE: /* ƒƒ‚ƒ‰ƒCƒY */
+ val2 = 5; //‰ñ‰r¥‚ð1/3‚É‚·‚é
+ break;
+
+ case SC_GRAVITATION:
+ if (sd) {
+ if (val3 == BCT_SELF) {
+ sd->canmove_tick += tick;
+ sd->canact_tick += tick;
+ } else calc_flag = 1;
+ }
+ break;
+
+ case SC_HERMODE:
+ status_change_clear_buffs(bl);
+ break;
+
+ case SC_REGENERATION:
+ val1 = 2;
+ case SC_BATTLEORDERS:
+ if (!(flag&4))
+ tick = 60000; // 1 minute
+ calc_flag = 1;
+ break;
+ case SC_GUILDAURA:
+ calc_flag = 1;
+ if (!(flag&4))
+ tick = 1000;
+ break;
+
+ case SC_DEVOTION: /* ƒfƒBƒ{?ƒVƒ‡ƒ“ */
+ {
+ struct map_session_data *src;
+ if ((src = map_id2sd(val1)) && src->sc_count)
+ { //Try to inherit the status from the Crusader [Skotlex]
+ //Ideally, we should calculate the remaining time and use that, but we'll trust that
+ //once the Crusader's status changes, it will reflect on the others.
+ if (src->sc_data[SC_AUTOGUARD].timer != -1)
+ status_change_start(bl,SC_AUTOGUARD,src->sc_data[SC_AUTOGUARD].val1,0,0,0,
+ skill_get_time(CR_AUTOGUARD,src->sc_data[SC_AUTOGUARD].val1),0);
+ if (src->sc_data[SC_DEFENDER].timer != -1)
+ status_change_start(bl,SC_DEFENDER,src->sc_data[SC_DEFENDER].val1,0,0,0,
+ skill_get_time(CR_DEFENDER,src->sc_data[SC_DEFENDER].val1),0);
+ }
+ break;
+ }
+
+ case SC_COMA: //Coma. Sends a char to 1HP/SP
+ battle_damage(NULL, bl, status_get_hp(bl)-1, 0);
+ if (sd) pc_heal(sd,0,-sd->status.sp+1);
+ return 0;
+
+ case SC_CARTBOOST: /* ƒJ?ƒgƒu?ƒXƒg */
+ if(sc_data[SC_DECREASEAGI].timer!=-1 )
+ { //Cancel Decrease Agi, but take no further effect [Skotlex]
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ return 0;
+ }
+ calc_flag = 1;
+ break;
+
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = val2?map_id2bl(val2):NULL;
+ struct status_change *sc_data2 = src?status_get_sc_data(src):NULL;
+ if (src && sc_data2) {
+ if (sc_data2[SC_CLOSECONFINE].timer == -1) //Start lock on caster.
+ status_change_start(src,SC_CLOSECONFINE,1,0,0,0,tick+1000,0);
+ else { //Increase count of locked enemies and refresh time.
+ sc_data2[SC_CLOSECONFINE].val1++;
+ delete_timer(sc_data2[SC_CLOSECONFINE].timer, status_change_timer);
+ sc_data2[SC_CLOSECONFINE].timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
+ }
+ }
+ }
+ break;
+ case SC_KAITE:
+ val2 = 1+val1/5; //Number of bounces: 1 + skilllv/5
+ break;
+ case SC_KAUPE:
+ if (flag&4)
+ break; //Do nothing when loading.
+ switch (val1) {
+ case 3: //33*3 + 1 -> 100%
+ val2++;
+ case 1:
+ case 2: //33, 66%
+ val2 += 33*val1;
+ val3 = 1; //Dodge 1 attack total.
+ break;
+ default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex]
+ val2 = 100;
+ val3 = val1-2;
+ break;
+ }
+ break;
+ case SC_COMBO:
+ switch (val1) { //Val1 contains the skill id
+ case TK_STORMKICK:
+ clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_DOWNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_TURNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ case TK_COUNTER:
+ clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
+ if (sd) sd->attackabletime = gettick()+tick;
+ break;
+ }
+ break;
+ case SC_SWOO: // [marquis007]
+ if (!(flag&4) && status_get_mode(bl)&MD_BOSS)
+ tick /= 4; //Reduce skill's duration. But for how long?
+// *opt3 |= 8192; //We haven't figured out this value yet...
+ opt_flag = 1;
+ calc_flag = 1;
+ break;
+ case SC_TKDORI:
+ val2 = 11-val1; //Chance to consume: 11-skilllv%
+ break;
+ case SC_RUN:
+ if (!(flag&4))
+ val4 = gettick(); //Store time at which you started running.
+ calc_flag = 1;
+ break;
+
+ case SC_CONCENTRATE: /* W’†—ÍŒüã */
+ case SC_BLESSING: /* ƒuƒŒƒbƒVƒ“ƒO */
+ case SC_ANGELUS: /* ƒAƒ“ƒ[ƒ‹ƒX */
+ case SC_IMPOSITIO: /* ƒCƒ“ƒ|ƒVƒeƒBƒIƒ}ƒkƒX */
+ case SC_GLORIA: /* ƒOƒƒŠƒA */
+ case SC_LOUD: /* ƒ‰ƒEƒhƒ{ƒCƒX */
+ case SC_KEEPING:
+ case SC_BARRIER:
+ case SC_MELTDOWN: /* ƒƒ‹ƒgƒ_ƒEƒ“ */
+ case SC_TRUESIGHT: /* ƒgƒDƒ‹?ƒTƒCƒg */
+ case SC_SPIDERWEB: /* ƒXƒpƒCƒ_?ƒEƒFƒbƒu */
+ case SC_SLOWDOWN:
+ case SC_SPEEDUP0:
+ case SC_SPEEDUP1:
+ case SC_INCALLSTATUS:
+ case SC_INCHIT: /* HIT㸠*/
+ case SC_INCHITRATE: /* HIT%㸠*/
+ case SC_INCFLEE: /* FLEE㸠*/
+ case SC_INCFLEERATE: /* FLEE%㸠*/
+ case SC_INCMHPRATE: /* MHP%㸠*/
+ case SC_INCMSPRATE: /* MSP%㸠*/
+ case SC_INCATKRATE: /* ATK%㸠*/
+ case SC_INCMATKRATE:
+ case SC_INCDEFRATE:
+ case SC_INCSTR:
+ case SC_INCAGI:
+ case SC_INCVIT:
+ case SC_INCINT:
+ case SC_INCDEX:
+ case SC_INCLUK:
+ case SC_STRFOOD:
+ case SC_AGIFOOD:
+ case SC_VITFOOD:
+ case SC_INTFOOD:
+ case SC_DEXFOOD:
+ case SC_LUKFOOD:
+ case SC_FLEEFOOD:
+ case SC_HITFOOD:
+ case SC_BATKFOOD:
+ case SC_WATKFOOD:
+ case SC_MATKFOOD:
+ case SC_SPURT:
+ case SC_SPIRIT:
+ case SC_SUN_COMFORT:
+ case SC_MOON_COMFORT:
+ case SC_STAR_COMFORT:
+ case SC_FUSION:
+ case SC_SKE:
+ calc_flag = 1;
+ break;
+
+ case SC_SAFETYWALL:
+ case SC_SLOWPOISON: //Slow potion can be activated even if not poisoned.
+ case SC_SUFFRAGIUM: /* ƒTƒtƒ‰ƒMƒ€ */
+ case SC_BENEDICTIO: /* ¹? */
+ case SC_MAGNIFICAT: /* ƒ}ƒOƒjƒtƒBƒJ?ƒg */
+ case SC_AETERNA: /* ƒG?ƒeƒ‹ƒi */
+ case SC_STRIPARMOR:
+ case SC_STRIPHELM:
+ case SC_CP_WEAPON:
+ case SC_CP_SHIELD:
+ case SC_CP_ARMOR:
+ case SC_CP_HELM:
+ case SC_EXTREMITYFIST: /* ˆ¢C—…”e™€Œ */
+ case SC_ANKLE: /* ƒAƒ“ƒNƒ‹ */
+ case SC_BLADESTOP_WAIT: /* ”’nŽæ‚è(‘Ò‚¿) */
+ case SC_HALLUCINATION:
+ case SC_SPLASHER: /* ƒxƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ? */
+ case SC_FOGWALL:
+ case SC_PRESERVE:
+ case SC_DOUBLECAST:
+ case SC_AURABLADE: /* ƒI?ƒ‰ƒuƒŒ?ƒh */
+ case SC_BABY:
+ case SC_WATK_ELEMENT:
+ case SC_ARMOR_ELEMENT:
+ case SC_LONGING:
+ case SC_ORCISH:
+ case SC_SHRINK:
+ case SC_WINKCHARM:
+ case SC_SCRESIST:
+ case SC_STOP:
+ case SC_CLOSECONFINE:
+ case SC_SKILLRATE_UP:
+ case SC_KAIZEL:
+ case SC_KAAHI:
+ case SC_INTRAVISION:
+ case SC_BASILICA:
+ break;
+
+ default:
+ if(battle_config.error_log)
+ ShowError("UnknownStatusChange [%d]\n", type);
+ return 0;
+ }
+
+ if (bl->type == BL_PC && (battle_config.display_hallucination || type != SC_HALLUCINATION))
+ {
+ if (flag&4)
+ clif_status_load(bl,StatusIconChangeTable[type],1); //Sending to owner since they aren't in the map yet. [Skotlex]
+ clif_status_change(bl,StatusIconChangeTable[type],1);
+ }
+
+ /* option‚Ì?X */
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STAN:
+ case SC_SLEEP:
+
+ // Cancel cast when get status [LuzZza]
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl; //Only Pressure is uninterruptable.
+ if (sd->skilltimer != -1 && sd->skillid != PA_PRESSURE) skill_castcancel(bl, 0);
+ } else
+ if (bl->type == BL_MOB) {
+ if (((struct mob_data *)bl)->skilltimer != -1) skill_castcancel(bl, 0);
+ }
+
+ battle_stopattack(bl); /* U?’âŽ~ */
+ skill_stop_dancing(bl); /* ‰‰‘t/ƒ_ƒ“ƒX‚Ì’†? */
+ { /* “¯Žž‚ÉŠ|‚©‚ç‚È‚¢ƒXƒe?ƒ^ƒXˆÙí‚ð‰ðœ */
+ int i;
+ for(i = SC_STONE; i <= SC_SLEEP; i++){
+ if(sc_data[i].timer != -1){
+ (*sc_count)--;
+ delete_timer(sc_data[i].timer, status_change_timer);
+ sc_data[i].timer = -1;
+ }
+ }
+ }
+ if(type == SC_STONE)
+ *opt1 = OPT1_STONEWAIT;
+ else
+ *opt1 = OPT1_STONE + (type - SC_STONE);
+ opt_flag = 1;
+ break;
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ *opt2 |= 1<<(type-SC_POISON);
+ opt_flag = 1;
+ break;
+ case SC_DPOISON: // Žb’è‚œł̃GƒtƒFƒNƒg‚ðŽg—p
+ *opt2 |= OPT2_DPOISON;
+ opt_flag = 1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ *opt2 |= OPT2_SIGNUMCRUCIS;
+ opt_flag = 1;
+ break;
+ case SC_HIDING:
+ case SC_CLOAKING:
+ battle_stopattack(bl); /* U?’âŽ~ */
+ *option |= ((type==SC_HIDING)?OPTION_HIDE:OPTION_CLOAK);
+ opt_flag =1 ;
+ break;
+ case SC_CHASEWALK:
+ battle_stopattack(bl); /* U?’âŽ~ */
+ *option |= OPTION_CHASEWALK|OPTION_CLOAK;
+ opt_flag =1 ;
+ break;
+ case SC_SIGHT:
+ *option |= OPTION_SIGHT;
+ opt_flag = 1;
+ break;
+ case SC_RUWACH:
+ *option |= OPTION_RUWACH;
+ opt_flag = 1;
+ break;
+ case SC_WEDDING:
+ *option |= OPTION_WEDDING;
+ opt_flag = 1;
+ break;
+ case SC_ORCISH:
+ *option |= OPTION_ORCISH;
+ opt_flag = 1;
+ break;
+ case SC_SIGHTTRASHER:
+ *option |= OPTION_SIGHTTRASHER;
+ opt_flag = 1;
+ break;
+ case SC_FUSION:
+ *option |= OPTION_FLYING;
+ opt_flag = 1;
+ break;
+ }
+
+ if(opt_flag) /* option‚Ì?X */
+ clif_changeoption(bl);
+
+ (*sc_count)++; /* ƒXƒe?ƒ^ƒXˆÙí‚Ì? */
+
+ sc_data[type].val1 = val1;
+ sc_data[type].val2 = val2;
+ sc_data[type].val3 = val3;
+ sc_data[type].val4 = val4;
+ /* ƒ^ƒCƒ}?Ý’è */
+ sc_data[type].timer = add_timer(
+ gettick() + tick, status_change_timer, bl->id, type);
+
+ if(bl->type==BL_PC && calc_flag)
+ status_calc_pc(sd,0); /* ƒXƒe?ƒ^ƒXÄŒvŽZ */
+
+ if(bl->type==BL_PC && save_flag)
+ chrif_save(sd,0); // save the player status
+
+ if(bl->type==BL_PC && updateflag)
+ clif_updatestatus(sd,updateflag); /* ƒXƒe?ƒ^ƒX‚ðƒNƒ‰ƒCƒAƒ“ƒg‚É‘—‚é */
+
+ if (bl->type==BL_PC && sd->pd)
+ pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
+
+ if(type==SC_RUN && bl->type==BL_PC)
+ pc_run(sd,val1,val2);
+
+ return 0;
+}
+/*==========================================
+ * ƒXƒe[ƒ^ƒXˆÙí‘S‰ðœ
+ *------------------------------------------
+ */
+int status_change_clear(struct block_list *bl,int type)
+{
+ struct status_change* sc_data;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+ int i;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, sc_data = status_get_sc_data(bl));
+ nullpo_retr(0, sc_count = status_get_sc_count(bl));
+ nullpo_retr(0, option = status_get_option(bl));
+ nullpo_retr(0, opt1 = status_get_opt1(bl));
+ nullpo_retr(0, opt2 = status_get_opt2(bl));
+ nullpo_retr(0, opt3 = status_get_opt3(bl));
+
+ if (*sc_count == 0)
+ return 0;
+ for(i = 0; i < SC_MAX; i++)
+ {
+ //Type 0: PC killed -> EDP and Meltdown must not be dispelled. [Skotlex]
+ // Do not reset Xmas status when killed. [Valaris]
+ if(sc_data[i].timer == -1 ||
+ (type == 0 && (i == SC_EDP || i == SC_MELTDOWN || i == SC_XMAS)))
+ continue;
+
+ status_change_end(bl, i, -1);
+
+ if (type == 1 && sc_data[i].timer != -1)
+ { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
+ (*sc_count)--;
+ delete_timer(sc_data[i].timer, status_change_timer);
+ sc_data[i].timer = -1;
+ }
+ }
+ //We can't assume the count is 0, some status don't end even when dead! [Skotlex]
+ //(*sc_count) = 0;
+ *opt1 = 0;
+ *opt2 = 0;
+ *opt3 = 0;
+ *option &= OPTION_MASK;
+
+ if(!type || type&2)
+ clif_changeoption(bl);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒe[ƒ^ƒXˆÙíI—¹
+ *------------------------------------------
+ */
+int status_change_end( struct block_list* bl , int type,int tid )
+{
+ struct map_session_data *sd;
+ struct status_change* sc_data;
+ int opt_flag=0, calc_flag = 0;
+ short *sc_count, *option, *opt1, *opt2, *opt3;
+
+ nullpo_retr(0, bl);
+ if(bl->type!=BL_PC && bl->type!=BL_MOB) {
+ if(battle_config.error_log)
+ ShowError("status_change_end: neither MOB nor PC !\n");
+ return 0;
+ }
+
+ if(type < 0 || type >= SC_MAX)
+ return 0;
+
+ sd = bl->type==BL_PC?(struct map_session_data *)bl:NULL;
+
+ sc_data = status_get_sc_data(bl);
+ sc_count = status_get_sc_count(bl);
+ option = status_get_option(bl);
+ opt1 = status_get_opt1(bl);
+ opt2 = status_get_opt2(bl);
+ opt3 = status_get_opt3(bl);
+
+ if (sc_data[type].timer != -1 && (sc_data[type].timer == tid || tid == -1)) {
+
+ if (tid == -1) // ƒ^ƒCƒ}‚©‚çŒÄ‚΂ê‚Ä‚¢‚È‚¢‚È‚çƒ^ƒCƒ}íœ‚ð‚·‚é
+ delete_timer(sc_data[type].timer,status_change_timer);
+
+ /* ŠY?‚̈Ùí‚ð³í‚É?‚· */
+ sc_data[type].timer=-1;
+ (*sc_count)--;
+
+ switch(type){ /* ˆÙí‚ÌŽí—Þ‚²‚Æ‚Ì?— */
+ case SC_PROVOKE: /* ƒvƒƒ{ƒbƒN */
+ case SC_ENDURE: // celest
+ case SC_CONCENTRATE: /* W’†—ÍŒüã */
+ case SC_BLESSING: /* ƒuƒŒƒbƒVƒ“ƒO */
+ case SC_ANGELUS: /* ƒAƒ“ƒ[ƒ‹ƒX */
+ case SC_INCREASEAGI: /* ‘¬“x㸠*/
+ case SC_DECREASEAGI: /* ‘¬“xŒ¸­ */
+ case SC_SIGNUMCRUCIS: /* ƒVƒOƒiƒ€ƒNƒ‹ƒVƒX */
+ case SC_HIDING:
+ case SC_ONEHAND:
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE: /* ƒAƒhƒŒƒiƒŠƒ“ƒ‰ƒbƒVƒ… */
+ case SC_ENCPOISON: /* ƒGƒ“ƒ`ƒƒƒ“ƒgƒ|ƒCƒYƒ“ */
+ case SC_IMPOSITIO: /* ƒCƒ“ƒ|ƒVƒeƒBƒIƒ}ƒkƒX */
+ case SC_GLORIA: /* ƒOƒƒŠƒA */
+ case SC_LOUD: /* ƒ‰ƒEƒhƒ{ƒCƒX */
+ case SC_QUAGMIRE: /* ƒNƒ@ƒOƒ}ƒCƒA */
+ case SC_PROVIDENCE: /* ƒvƒƒ”ƒBƒfƒ“ƒX */
+ case SC_SPEARSQUICKEN: /* ƒXƒsƒAƒNƒCƒbƒPƒ“ */
+ case SC_VOLCANO:
+ case SC_DELUGE:
+ case SC_VIOLENTGALE:
+ case SC_ETERNALCHAOS: /* ƒGƒ^?ƒiƒ‹ƒJƒIƒX */
+ case SC_DRUMBATTLE: /* ?‘¾ŒÛ‚Ì‹¿‚« */
+ case SC_NIBELUNGEN: /* ƒj?ƒxƒ‹ƒ“ƒO‚ÌŽw—Ö */
+ case SC_SIEGFRIED: /* •sŽ€g‚̃W?ƒNƒtƒŠ?ƒh */
+ case SC_WHISTLE: /* Œû“J */
+ case SC_ASSNCROS: /* —[—z‚̃AƒTƒVƒ“ƒNƒƒX */
+ case SC_HUMMING: /* ƒnƒ~ƒ“ƒO */
+ case SC_DONTFORGETME: /* Ž„‚ð–Y‚ê‚È‚¢‚Å */
+ case SC_FORTUNE: /* K‰^‚̃LƒX */
+ case SC_SERVICE4U: /* ƒT?ƒrƒXƒtƒH?ƒ†? */
+ case SC_EXPLOSIONSPIRITS: // ”š—ô”g“®
+ case SC_STEELBODY: // ‹à„
+ case SC_APPLEIDUN: /* ƒCƒhƒDƒ“‚Ì—ÑŒç */
+ case SC_BLADESTOP_WAIT:
+ case SC_CONCENTRATION: /* ƒRƒ“ƒZƒ“ƒgƒŒ?ƒVƒ‡ƒ“ */
+ case SC_ASSUMPTIO: /* ƒAƒVƒƒƒ“ƒvƒeƒBƒI */
+ case SC_WINDWALK: /* ƒEƒCƒ“ƒhƒEƒH?ƒN */
+ case SC_TRUESIGHT: /* ƒgƒDƒ‹?ƒTƒCƒg */
+ case SC_SPIDERWEB: /* ƒXƒpƒCƒ_?ƒEƒFƒbƒu */
+ case SC_MAGICPOWER: /* –‚–@—Í?• */
+ case SC_CHASEWALK:
+ case SC_ATKPOTION: // [Valaris]
+ case SC_MATKPOTION: // [Valaris]
+ case SC_MELTDOWN: /* ƒƒ‹ƒgƒ_ƒEƒ“ */
+ case SC_CARTBOOST:
+ case SC_MINDBREAKER: /* ƒ}ƒCƒ“ƒhƒuƒŒ[ƒJ[ */
+ case SC_EDP: // Celest
+ case SC_SLOWDOWN:
+ case SC_ASPDPOTION0: /* ?‘¬ƒ|?ƒVƒ‡ƒ“ */
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ case SC_SPEEDUP0:
+ case SC_SPEEDUP1:
+ case SC_INCALLSTATUS:
+ case SC_INCHIT: /* HIT㸠*/
+ case SC_INCHITRATE: /* HIT%㸠*/
+ case SC_INCFLEE: /* FLEE㸠*/
+ case SC_INCFLEERATE: /* FLEE%㸠*/
+ case SC_INCMHPRATE: /* MHP%㸠*/
+ case SC_INCMSPRATE: /* MSP%㸠*/
+ case SC_INCATKRATE: /* ATK%㸠*/
+ case SC_INCMATKRATE:
+ case SC_INCDEFRATE:
+ case SC_INCSTR:
+ case SC_INCAGI:
+ case SC_INCVIT:
+ case SC_INCINT:
+ case SC_INCDEX:
+ case SC_INCLUK:
+ case SC_STRFOOD:
+ case SC_AGIFOOD:
+ case SC_VITFOOD:
+ case SC_INTFOOD:
+ case SC_DEXFOOD:
+ case SC_LUKFOOD:
+ case SC_FLEEFOOD:
+ case SC_HITFOOD:
+ case SC_BATKFOOD:
+ case SC_WATKFOOD:
+ case SC_MATKFOOD:
+ case SC_BATTLEORDERS:
+ case SC_REGENERATION:
+ case SC_GUILDAURA:
+ case SC_SPURT:
+ case SC_SPIRIT:
+ case SC_SUN_COMFORT:
+ case SC_MOON_COMFORT:
+ case SC_STAR_COMFORT:
+ case SC_FUSION:
+ case SC_SKE:
+ case SC_SWOO: // [marquis007]
+ case SC_SKA: // [marquis007]
+ calc_flag = 1;
+ break;
+
+ case SC_XMAS: // Xmas Suit [Valaris]
+ case SC_WEDDING: //Œ‹¥—p(Œ‹¥ˆßÖ‚É‚È‚Á‚Ä?‚­‚Ì‚ª?‚¢‚Æ‚©)
+ if (sd) {
+ //Restore look
+ sd->view_class = sd->status.class_;
+ clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+ if(battle_config.save_clothcolor && sd->status.clothes_color > 0)
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color);
+ }
+ break;
+ case SC_RUN://‹ì‚¯‘«
+ if (sd && sd->walktimer != -1)
+ pc_stop_walking(sd,1);
+ if (sc_data[type].val1 >= 7 &&
+ DIFF_TICK(gettick(), sc_data[type].val4) <= 1000 &&
+ (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0))
+ )
+ status_change_start(bl,SC_SPURT,sc_data[type].val1,0,0,0,skill_get_time2(TK_RUN, sc_data[type].val1),0);
+ calc_flag = 1;
+ break;
+ case SC_AUTOBERSERK:
+ if (sc_data[SC_PROVOKE].timer != -1 && sc_data[SC_PROVOKE].val2 == 1)
+ status_change_end(bl,SC_PROVOKE,-1);
+ break;
+
+ case SC_DEFENDER:
+ calc_flag = 1;
+ case SC_AUTOGUARD:
+ if (sd) {
+ struct map_session_data *tsd;
+ int i;
+ for (i = 0; i < 5; i++)
+ { //Clear the status from the others too [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc_data[type].timer != -1)
+ status_change_end(&tsd->bl,type,-1);
+ }
+ }
+ break;
+ case SC_DEVOTION: /* ƒfƒBƒ{?ƒVƒ‡ƒ“ */
+ {
+ struct map_session_data *md = map_id2sd(sc_data[type].val1);
+ //The status could have changed because the Crusader left the game. [Skotlex]
+ if (md)
+ {
+ md->devotion[sc_data[type].val2] = 0;
+ clif_devotion(md);
+ }
+ //Remove AutoGuard and Defender [Skotlex]
+ if (sc_data[SC_AUTOGUARD].timer != -1)
+ status_change_end(bl,SC_AUTOGUARD,-1);
+ if (sc_data[SC_DEFENDER].timer != -1)
+ status_change_end(bl,SC_DEFENDER,-1);
+ }
+ break;
+ case SC_BLADESTOP:
+ {
+ struct status_change *t_sc_data = status_get_sc_data((struct block_list *)sc_data[type].val4);
+ //•Ð•û‚ªØ‚ꂽ‚Ì‚Å‘ŠŽè‚Ì”’n?‘Ô‚ªØ‚ê‚Ä‚È‚¢‚Ì‚È‚ç‰ðœ
+ if(t_sc_data && t_sc_data[SC_BLADESTOP].timer!=-1)
+ status_change_end((struct block_list *)sc_data[type].val4,SC_BLADESTOP,-1);
+
+ if(sc_data[type].val2==2)
+ clif_bladestop((struct block_list *)sc_data[type].val3,(struct block_list *)sc_data[type].val4,0);
+ }
+ break;
+ case SC_DANCING:
+ {
+ struct map_session_data *dsd;
+ struct status_change *d_sc_data;
+ if(sc_data[type].val2)
+ {
+ skill_delunitgroup((struct skill_unit_group *)sc_data[type].val2);
+ sc_data[type].val2 = 0;
+ }
+ if(sc_data[type].val4 && sc_data[type].val4 != BCT_SELF && (dsd=map_id2sd(sc_data[type].val4))){
+ d_sc_data = dsd->sc_data;
+ //‡‘t‚Å‘ŠŽè‚ª‚¢‚éꇑŠŽè‚Ìval4‚ð0‚É‚·‚é
+ if(d_sc_data && d_sc_data[type].timer!=-1)
+ {
+ d_sc_data[type].val2 = d_sc_data[type].val4 = 0; //This will prevent recursive loops.
+ status_change_end(&dsd->bl, type, -1);
+ }
+ }
+ if(sc_data[type].val1 == CG_MOONLIT) //Only dance that doesn't has ground tiles... [Skotlex]
+ status_change_end(bl, SC_MOONLIT, -1);
+ }
+ if (sc_data[SC_LONGING].timer!=-1)
+ status_change_end(bl,SC_LONGING,-1);
+ calc_flag = 1;
+ break;
+ case SC_NOCHAT: //ƒ`ƒƒƒbƒg‹ÖŽ~?‘Ô
+ if (sd) {
+ if(battle_config.manner_system){
+ if (sd->status.manner >= 0) // weeee ^^ [celest]
+ sd->status.manner = 0;
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ }
+ break;
+ case SC_SPLASHER: /* ƒxƒiƒ€ƒXƒvƒ‰ƒbƒVƒƒ? */
+ {
+ struct block_list *src=map_id2bl(sc_data[type].val3);
+ if(src && tid!=-1){
+ //Ž©•ª‚Ƀ_ƒ?ƒW•Žü?3*3‚Ƀ_ƒ?ƒW
+ skill_castend_damage_id(src, bl,sc_data[type].val2,sc_data[type].val1,gettick(),0 );
+ }
+ }
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = sc_data[type].val2?map_id2bl(sc_data[type].val2):NULL;
+ struct status_change *sc_data2 = src?status_get_sc_data(src):NULL;
+ if (src && sc_data2) {
+ if (sc_data2[SC_CLOSECONFINE].timer != -1) //If status was already ended, do nothing.
+ { //Decrease count
+ if (--sc_data2[SC_CLOSECONFINE].val1 <= 0) //No more holds, free him up.
+ status_change_end(src, SC_CLOSECONFINE, -1);
+ }
+ }
+ }
+ break;
+ case SC_CLOSECONFINE:
+ if (sc_data[type].val1 > 0) { //Caster has been unlocked... nearby chars need to be unlocked.
+ int range = 2*skill_get_range2(bl, RG_CLOSECONFINE, 1);
+ map_foreachinarea(status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,type,gettick());
+ }
+ break;
+ /* option1 */
+ case SC_FREEZE:
+ sc_data[type].val3 = 0;
+ break;
+
+ /* option2 */
+ case SC_POISON: /* “Å */
+ case SC_BLIND: /* ˆÃ? */
+ case SC_CURSE:
+ calc_flag = 1;
+ break;
+
+ case SC_MARIONETTE: /* ƒ}ƒŠƒIƒlƒbƒgƒRƒ“ƒgƒ?ƒ‹ */
+ case SC_MARIONETTE2: /// Marionette target
+ {
+ // check for partner and end their marionette status as well
+ int type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
+ struct block_list *pbl = map_id2bl(sc_data[type].val3);
+ if (pbl) {
+ struct status_change* sc_data;
+ if ((sc_data = status_get_sc_data(pbl)) && sc_data[type2].timer != -1)
+ status_change_end(pbl, type2, -1);
+ }
+ if (type == SC_MARIONETTE)
+ clif_marionette(bl, 0);
+ calc_flag = 1;
+ }
+ break;
+
+ case SC_BERSERK: //val4 indicates if the skill was dispelled. [Skotlex]
+ if (sd && sd->status.hp > 100 && !sc_data[type].val4) {
+ sd->status.hp = 100;
+ clif_updatestatus(sd,SP_HP);
+ }
+ calc_flag = 1;
+ break;
+
+ case SC_GRAVITATION:
+ if (sd) {
+ if (sc_data[type].val3 == BCT_SELF) {
+ unsigned int tick = gettick();
+ sd->canmove_tick = tick;
+ sd->canact_tick = tick;
+ } else calc_flag = 1;
+ }
+ break;
+
+ case SC_GOSPEL: //Clear the buffs from other chars.
+ if(sc_data[type].val4 != BCT_SELF)
+ calc_flag = 1;
+ else if (sc_data[type].val3) { //Clear the group.
+ struct skill_unit_group *group = (struct skill_unit_group *)sc_data[type].val3;
+ sc_data[type].val3 = 0;
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_HERMODE:
+ case SC_BASILICA: //Clear the skill area. [Skotlex]
+ if(sc_data[type].val3 == BCT_SELF)
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_MOONLIT: //Clear the unit effect. [Skotlex]
+ skill_setmapcell(bl,CG_MOONLIT, sc_data[SC_MOONLIT].val1, CELL_CLRMOONLIT);
+ break;
+ }
+
+
+ if (sd && (battle_config.display_hallucination || type != SC_HALLUCINATION))
+ clif_status_change(bl,StatusIconChangeTable[type],0);
+
+ switch(type){ /* ³í‚É?‚é‚Æ‚«‚È‚É‚©?—‚ª•K—v */
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STAN:
+ case SC_SLEEP:
+ *opt1 = 0;
+ opt_flag = 1;
+ break;
+
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ *opt2 &= ~(1<<(type-SC_POISON));
+ opt_flag = 1;
+ break;
+ case SC_DPOISON:
+ *opt2 &= ~OPT2_DPOISON; // “Å?‘Ô‰ðœ
+ opt_flag = 1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ *opt2 &= ~OPT2_SIGNUMCRUCIS;
+ opt_flag = 1;
+ break;
+
+ case SC_HIDING:
+ *option &= ~OPTION_HIDE;
+ opt_flag = 1 ;
+ break;
+ case SC_CLOAKING:
+ *option &= ~OPTION_CLOAK;
+ calc_flag = 1; // orn
+ opt_flag = 1 ;
+ break;
+ case SC_CHASEWALK:
+ *option &= ~(OPTION_CHASEWALK|OPTION_CLOAK);
+ opt_flag = 1 ;
+ break;
+ case SC_SIGHT:
+ *option &= ~OPTION_SIGHT;
+ opt_flag = 1;
+ break;
+ case SC_WEDDING: //Œ‹¥—p(Œ‹¥ˆßÖ‚É‚È‚Á‚Ä?‚­‚Ì‚ª?‚¢‚Æ‚©)
+ *option &= ~OPTION_WEDDING;
+ opt_flag = 1;
+ break;
+ case SC_ORCISH:
+ *option &= ~OPTION_ORCISH;
+ opt_flag = 1;
+ break;
+ case SC_RUWACH:
+ *option &= ~OPTION_RUWACH;
+ opt_flag = 1;
+ break;
+ case SC_SIGHTTRASHER:
+ *option &= ~OPTION_SIGHTTRASHER;
+ opt_flag = 1;
+ break;
+ case SC_FUSION:
+ *option &= ~OPTION_FLYING;
+ opt_flag = 1;
+ break;
+ //opt3
+ case SC_TWOHANDQUICKEN: /* 2HQ */
+ case SC_ONEHAND: /* 1HQ */
+ case SC_SPEARSQUICKEN: /* ƒXƒsƒAƒNƒCƒbƒPƒ“ */
+ case SC_CONCENTRATION: /* ƒRƒ“ƒZƒ“ƒgƒŒ?ƒVƒ‡ƒ“ */
+ *opt3 &= ~1;
+ break;
+ case SC_OVERTHRUST: /* ƒI?ƒo?ƒXƒ‰ƒXƒg */
+ *opt3 &= ~2;
+ break;
+ case SC_ENERGYCOAT: /* ƒGƒiƒW?ƒR?ƒg */
+ *opt3 &= ~4;
+ break;
+ case SC_EXPLOSIONSPIRITS: // ”š—ô”g“®
+ *opt3 &= ~8;
+ break;
+ case SC_STEELBODY: // ‹à„
+ case SC_SKA:
+ *opt3 &= ~16;
+ break;
+ case SC_BLADESTOP: /* ”’nŽæ‚è */
+ *opt3 &= ~32;
+ break;
+ case SC_BERSERK: /* ƒo?ƒT?ƒN */
+ *opt3 &= ~128;
+ break;
+ case SC_MARIONETTE: /* ƒ}ƒŠƒIƒlƒbƒgƒRƒ“ƒgƒ?ƒ‹ */
+ case SC_MARIONETTE2:
+ *opt3 &= ~1024;
+ break;
+ case SC_ASSUMPTIO: /* ƒAƒXƒ€ƒvƒeƒBƒI */
+ *opt3 &= ~2048;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ *opt3 &= ~4096;
+ opt_flag = 1;
+ break;
+ }
+
+ if(opt_flag) /* option‚Ì?X‚ð?‚¦‚é */
+ clif_changeoption(bl);
+
+ if (sd && calc_flag)
+ status_calc_pc((struct map_session_data *)bl,0); /* ƒXƒe?ƒ^ƒXÄŒvŽZ */
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * ƒXƒe[ƒ^ƒXˆÙíI—¹ƒ^ƒCƒ}[
+ *------------------------------------------
+ */
+int status_change_timer(int tid, unsigned int tick, int id, int data)
+{
+ int type = data;
+ struct block_list *bl;
+ struct map_session_data *sd=NULL;
+ struct status_change *sc_data;
+ //short *sc_count; //Žg‚Á‚Ä‚È‚¢H
+
+// security system to prevent forgetting timer removal
+ int temp_timerid;
+
+ bl=map_id2bl(id);
+#ifndef _WIN32
+ nullpo_retr_f(0, bl, "id=%d data=%d",id,data);
+#endif
+ nullpo_retr(0, sc_data=status_get_sc_data(bl));
+
+ if(bl->type==BL_PC)
+ nullpo_retr(0, sd=(struct map_session_data *)bl);
+
+ //sc_count=status_get_sc_count(bl); //Žg‚Á‚Ä‚È‚¢H
+
+ if(sc_data[type].timer != tid) {
+ if(battle_config.error_log)
+ ShowError("status_change_timer %d != %d\n",tid,sc_data[type].timer);
+ return 0;
+ }
+
+ // security system to prevent forgetting timer removal
+ // you shouldn't be that careless inside the switch here
+ temp_timerid = sc_data[type].timer;
+ sc_data[type].timer = -1;
+
+ switch(type){ /* “ÁŽê‚È?—‚É‚È‚éê‡ */
+ case SC_MAXIMIZEPOWER: /* ƒ}ƒLƒVƒ}ƒCƒYƒpƒ? */
+ case SC_CLOAKING:
+ if(!sd || sd->status.sp > 0)
+ {
+ if (sd)
+ {
+ sd->status.sp--;
+ clif_updatestatus(sd,SP_SP);
+ }
+ sc_data[type].timer=add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ sc_data[type].val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_CHASEWALK:
+ if(sd){
+ int sp = 10+sc_data[SC_CHASEWALK].val1*2;
+ if (map_flag_gvg(sd->bl.m)) sp *= 5;
+ if (sd->status.sp > sp){
+ sd->status.sp -= sp; // update sp cost [Celest]
+ clif_updatestatus(sd,SP_SP);
+ if ((++sc_data[SC_CHASEWALK].val4) == 1) {
+ status_change_start(bl, SC_INCSTR, 1<<(sc_data[SC_CHASEWALK].val1-1), 0, 0, 0,
+ (sc_data[SC_SPIRIT].timer != -1 && sc_data[SC_SPIRIT].val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration
+ *skill_get_time2(ST_CHASEWALK,sc_data[SC_CHASEWALK].val1), 0);
+ }
+ sc_data[type].timer = add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ sc_data[type].val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_HIDING: /* ƒnƒCƒfƒBƒ“ƒO */
+ if(sd){ /* SP‚ª‚ ‚Á‚ÄAŽžŠÔ§ŒÀ‚ÌŠÔ‚ÍŽ? */
+ 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( /* ƒ^ƒCƒ}?ÄÝ’è */
+ 1000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SIGHT: /* ƒTƒCƒg */
+ case SC_RUWACH: /* ƒ‹ƒAƒt */
+ case SC_SIGHTBLASTER:
+ {
+ int range = skill_get_range2(bl, type==SC_SIGHT?MG_SIGHT:(type==SC_RUWACH?AL_RUWACH:WZ_SIGHTBLASTER), sc_data[type].val1);
+ map_foreachinarea( status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,
+ bl,type,tick);
+
+ if( (--sc_data[type].val2)>0 ){
+ sc_data[type].timer=add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ 250+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SIGNUMCRUCIS: /* ƒVƒOƒiƒ€ƒNƒ‹ƒVƒX */
+ {
+ int race = status_get_race(bl);
+ if(race == 6 || battle_check_undead(race,status_get_elem_type(bl))) {
+ sc_data[type].timer=add_timer(1000*600+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ }
+ break;
+
+ case SC_WARM: //SG skills [Komurka]
+ if( (--sc_data[type].val2)>0){
+ map_foreachinarea( status_change_timer_sub,
+ bl->m, bl->x-sc_data[type].val4, bl->y-sc_data[type].val4, bl->x+sc_data[type].val4,bl->y+sc_data[type].val4,BL_CHAR,
+ bl,type,tick);
+ sc_data[type].timer=add_timer(tick+1000, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_PROVOKE: /* ƒvƒƒ{ƒbƒN/ƒI?ƒgƒo?ƒT?ƒN */
+ if(sc_data[type].val2!=0){ /* ƒI?ƒgƒo?ƒT?ƒNi‚P•b‚²‚Æ‚ÉHPƒ`ƒFƒbƒNj */
+ if(sd && sd->status.hp>sd->status.max_hp>>2) /* ’âŽ~ */
+ break;
+ sc_data[type].timer=add_timer( 1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_AUTOBERSERK: //Auto Berserk continues until triggered off manually. [Skotlex]
+ sc_data[type].timer=add_timer( 1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+
+ case SC_ENDURE: /* ƒCƒ“ƒfƒ…ƒA */
+ if(sd && sd->special_state.infinite_endure) {
+ sc_data[type].timer=add_timer( 1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_STONE:
+ if(sc_data[type].val2 != 0) {
+ short *opt1 = status_get_opt1(bl);
+ sc_data[type].val2 = 0;
+ sc_data[type].val4 = 0;
+ battle_stopwalking(bl,1);
+ if(opt1) {
+ *opt1 = OPT1_STONE;
+ clif_changeoption(bl);
+ }
+ sc_data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ else if( (--sc_data[type].val3) > 0) {
+ int hp = status_get_max_hp(bl);
+ if((++sc_data[type].val4)%5 == 0 && status_get_hp(bl) > hp>>2) {
+ hp = hp/100;
+ if(hp < 1) hp = 1;
+ if(sd)
+ pc_heal(sd,-hp,0);
+ else if(bl->type == BL_MOB){
+ struct mob_data *md;
+ if((md=((struct mob_data *)bl)) == NULL)
+ break;
+ md->hp -= hp;
+ }
+ }
+ sc_data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_POISON:
+ if (status_get_hp(bl) <= status_get_max_hp(bl)>>2) //Stop damaging after 25% HP left.
+ break;
+ case SC_DPOISON:
+ if ((--sc_data[type].val3) > 0 && sc_data[SC_SLOWPOISON].timer == -1) {
+ if(sd) {
+ pc_heal(sd, -sc_data[type].val4, 0);
+ } else if (bl->type == BL_MOB) {
+ ((struct mob_data*)bl)->hp -= sc_data[type].val4;
+ if (battle_config.show_mob_hp)
+ clif_charnameack (0, bl);
+ } else
+ battle_heal(NULL, bl, -sc_data[type].val4, 0, 1);
+ }
+ if (sc_data[type].val3 > 0 && !status_isdead(bl))
+ {
+ sc_data[type].timer = add_timer (1000 + tick, status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_TENSIONRELAX: /* ƒeƒ“ƒVƒ‡ƒ“ƒŠƒ‰ƒbƒNƒX */
+ if(sd){ /* SP‚ª‚ ‚Á‚ÄAHP‚ª?ƒ^ƒ“‚Å‚È‚¯‚ê‚Î?? */
+ if( sd->status.sp > 12 && sd->status.max_hp > sd->status.hp ){
+ sc_data[type].timer=add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ if(sd->status.max_hp <= sd->status.hp)
+ {
+ status_change_end(&sd->bl,SC_TENSIONRELAX,-1);
+ return 0;
+ }
+ }
+ break;
+ case SC_BLEEDING: // [celest]
+ // i hope i haven't interpreted it wrong.. which i might ^^;
+ // Source:
+ // - 10õ©ª´ªÈªËHPª¬Êõá´
+ // - õóúìªÎªÞªÞ«µ?«Ðì¹ÔѪä«ê«í«°ª·ªÆªâ?ÍýªÏἪ¨ªÊª¤
+ // To-do: bleeding effect increases damage taken?
+ if ((sc_data[type].val4 -= 10000) >= 0) {
+ int hp = rand()%300 + 400;
+ if(sd) {
+ pc_heal(sd,-hp,0);
+ } else if(bl->type == BL_MOB) {
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md) md->hp -= hp;
+ }
+ if (!status_isdead(bl)) {
+ // walking and casting effect is lost
+ battle_stopwalking (bl, 1);
+ skill_castcancel (bl, 0);
+ sc_data[type].timer = add_timer(10000 + tick, status_change_timer, bl->id, data );
+ }
+ return 0;
+ }
+ break;
+
+ // Status changes that don't have a time limit
+ case SC_AETERNA:
+ case SC_TRICKDEAD:
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_MAGICPOWER:
+ case SC_REJECTSWORD:
+ case SC_MEMORIZE:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_SACRIFICE:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYTURN:
+ case SC_READYCOUNTER:
+ case SC_RUN:
+ case SC_DODGE:
+ sc_data[type].timer=add_timer( 1000*600+tick,status_change_timer, bl->id, data );
+ return 0;
+
+ case SC_DANCING: //ƒ_ƒ“ƒXƒXƒLƒ‹‚ÌŽžŠÔSPÁ”ï
+ {
+ int s = 0;
+ int sp = 1;
+ if(sd && (--sc_data[type].val3) > 0) {
+ switch(sc_data[type].val1){
+ case BD_RICHMANKIM: /* ƒjƒˆƒ‹ƒh‚̉ƒ 3•b‚ÉSP1 */
+ case BD_DRUMBATTLEFIELD: /* ?‘¾ŒÛ‚Ì‹¿‚« 3•b‚ÉSP1 */
+ case BD_RINGNIBELUNGEN: /* ƒj?ƒxƒ‹ƒ“ƒO‚ÌŽw—Ö 3•b‚ÉSP1 */
+ case BD_SIEGFRIED: /* •sŽ€g‚̃W?ƒNƒtƒŠ?ƒh 3•b‚ÉSP1 */
+ case BA_DISSONANCE: /* •s‹¦˜a‰¹ 3•b‚ÅSP1 */
+ case BA_ASSASSINCROSS: /* —[—z‚̃AƒTƒVƒ“ƒNƒƒX 3•b‚ÅSP1 */
+ case DC_UGLYDANCE: /* Ž©•ªŸŽè‚ȃ_ƒ“ƒX 3•b‚ÅSP1 */
+ s=3;
+ break;
+ case BD_LULLABY: /* ŽqŽç‰Ì 4•b‚ÉSP1 */
+ case BD_ETERNALCHAOS: /* ‰i‰“‚̬“× 4•b‚ÉSP1 */
+ case BD_ROKISWEIL: /* ƒƒL‚Ì‹©‚Ñ 4•b‚ÉSP1 */
+ case DC_FORTUNEKISS: /* K‰^‚̃LƒX 4•b‚ÅSP1 */
+ s=4;
+ break;
+ case CG_HERMODE: // Wand of Hermod
+ sp=5; //Upkeep = 5
+ case BD_INTOABYSS: /* [•£‚Ì’†‚É 5•b‚ÉSP1 */
+ case BA_WHISTLE: /* Œû“J 5•b‚ÅSP1 */
+ case DC_HUMMING: /* ƒnƒ~ƒ“ƒO 5•b‚ÅSP1 */
+ case BA_POEMBRAGI: /* ƒuƒ‰ƒM‚ÌŽ 5•b‚ÅSP1 */
+ case DC_SERVICEFORYOU: /* ƒT?ƒrƒXƒtƒH?ƒ†? 5•b‚ÅSP1 */
+ s=5;
+ break;
+ case BA_APPLEIDUN: /* ƒCƒhƒDƒ“‚Ì—ÑŒç 6•b‚ÅSP1 */
+ s=6;
+ break;
+ case CG_MOONLIT: /* ŒŽ–¾‚è‚Ìò‚É—Ž‚¿‚é‰Ô‚Ñ‚ç 10•b‚ÅSP1H */
+ sp= 4*sc_data[type].val2; //Moonlit's cost is 4sp*skill_lv [Skotlex]
+ //Upkeep is also every 10 secs.
+ case DC_DONTFORGETME: /* Ž„‚ð–Y‚ê‚È‚¢‚Åc 10•b‚ÅSP1 */
+ s=10;
+ break;
+ }
+ if (s && ((sc_data[type].val3 % s) == 0)) {
+ if (sc_data[SC_LONGING].timer != -1)
+ sp = s;
+ if (sp > sd->status.sp)
+ sp = sd->status.sp;
+ sd->status.sp -= sp;
+ clif_updatestatus(sd,SP_SP);
+ if (sd->status.sp <= 0)
+ break;
+ }
+ sc_data[type].timer=add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ 1000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_DEVOTION:
+ { //Check range and timeleft to preserve status [Skotlex]
+ //This implementation won't work for mobs because of map_id2sd, but it's a small cost in exchange of the speed of map_id2sd over map_id2sd
+ struct map_session_data *md = map_id2sd(sc_data[type].val1);
+ if (md && battle_check_range(bl, &md->bl, sc_data[type].val3) && (sc_data[type].val4-=1000)>0)
+ {
+ sc_data[type].timer = add_timer(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_BERSERK: /* ƒo?ƒT?ƒN */
+ if(sd){ /* HP‚ª100ˆÈã‚È‚ç?? */
+ if( (sd->status.hp - sd->status.max_hp*5/100) > 100 ){ // 5% every 10 seconds [DracoRPG]
+ sd->status.hp -= sd->status.max_hp*5/100; // changed to max hp [celest]
+ clif_updatestatus(sd,SP_HP);
+ sc_data[type].timer = add_timer( /* ƒ^ƒCƒ}?ÄÝ’è */
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ else
+ sd->canregen_tick = gettick() + 300000;
+ }
+ break;
+ case SC_NOCHAT: //ƒ`ƒƒƒbƒg‹ÖŽ~?‘Ô
+ if(sd && battle_config.manner_system){
+ sd->status.manner++;
+ clif_updatestatus(sd,SP_MANNER);
+ if (sd->status.manner < 0)
+ { //Every 60 seconds your manner goes up by 1 until it gets back to 0.
+ sc_data[type].timer=add_timer(60000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SPLASHER:
+ if (sc_data[type].val4 % 1000 == 0) {
+ char timer[2];
+ sprintf (timer, "%d", sc_data[type].val4/1000);
+ clif_message(bl, timer);
+ }
+ if((sc_data[type].val4 -= 500) > 0) {
+ sc_data[type].timer = add_timer(
+ 500 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_MARIONETTE: /* ƒ}ƒŠƒIƒlƒbƒgƒRƒ“ƒgƒ?ƒ‹ */
+ case SC_MARIONETTE2:
+ {
+ struct block_list *pbl = map_id2bl(sc_data[type].val3);
+ if (pbl && battle_check_range(bl, pbl, 7) &&
+ (sc_data[type].val2 -= 1000)>0) {
+ sc_data[type].timer = add_timer(
+ 1000 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_GOSPEL:
+ if(sc_data[type].val4 == BCT_SELF){
+ int hp, sp;
+ hp = (sc_data[type].val1 > 5) ? 45 : 30;
+ sp = (sc_data[type].val1 > 5) ? 35 : 20;
+ if(status_get_hp(bl) - hp > 0 &&
+ (sd == NULL || sd->status.sp - sp> 0))
+ {
+ if (sd)
+ pc_heal(sd,-hp,-sp);
+ else if (bl->type == BL_MOB)
+ mob_heal((struct mob_data *)bl,-hp);
+
+ if ((sc_data[type].val2 -= 10000) > 0) {
+ sc_data[type].timer = add_timer(
+ 10000+tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case SC_GUILDAURA:
+ {
+ struct block_list *tbl = map_id2bl(sc_data[type].val2);
+
+ if (tbl && battle_check_range(bl, tbl, 2)){
+ sc_data[type].timer = add_timer(
+ 1000 + tick, status_change_timer,
+ bl->id, data);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ // default for all non-handled control paths
+ // security system to prevent forgetting timer removal
+
+ // if we reach this point we need the timer for the next call,
+ // so restore it to have status_change_end handle a valid timer
+ sc_data[type].timer = temp_timerid;
+
+ return status_change_end( bl,type,tid );
+}
+
+/*==========================================
+ * ƒXƒe[ƒ^ƒXˆÙíƒ^ƒCƒ}[”͈͈—
+ *------------------------------------------
+ */
+int status_change_timer_sub(struct block_list *bl, va_list ap )
+{
+ struct block_list *src;
+ struct map_session_data* sd=NULL;
+ struct map_session_data* tsd=NULL;
+
+ int type;
+ unsigned int tick;
+
+ src=va_arg(ap,struct block_list*);
+ type=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+
+ if (status_isdead(bl))
+ return 0;
+ if (src->type==BL_PC) sd= (struct map_session_data*)src;
+ if (bl->type==BL_PC) tsd= (struct map_session_data*)bl;
+
+ switch( type ){
+ case SC_SIGHT: /* ƒTƒCƒg */
+ case SC_CONCENTRATE:
+ if( (*status_get_option(bl))&(OPTION_HIDE|OPTION_CLOAK) ){
+ status_change_end( bl, SC_HIDING, -1);
+ status_change_end( bl, SC_CLOAKING, -1);
+ }
+ break;
+ case SC_RUWACH: /* ƒ‹ƒAƒt */
+ if( (*status_get_option(bl))&(OPTION_HIDE|OPTION_CLOAK) ){
+ struct status_change *sc_data = status_get_sc_data(bl); // check whether the target is hiding/cloaking [celest]
+ if (sc_data && (sc_data[SC_HIDING].timer != -1 || // if the target is using a special hiding, i.e not using normal hiding/cloaking, don't bother
+ sc_data[SC_CLOAKING].timer != -1)) {
+ status_change_end( bl, SC_HIDING, -1);
+ status_change_end( bl, SC_CLOAKING, -1);
+ if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
+ }
+ }
+ break;
+ case SC_SIGHTBLASTER:
+ {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if (sc_data && sc_data[type].val2 > 0 && battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ { //sc_ check prevents a single round of Sight Blaster hitting multiple opponents. [Skotlex]
+ skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0);
+ sc_data[type].val2 = 0; //This signals it to end.
+ }
+ }
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ {
+ if(battle_check_target( src,bl, BCT_ENEMY ) > 0) {
+ struct status_change *sc_data = status_get_sc_data(src);
+ if(sd){
+ if(sd->status.sp<2) {
+ sd->sc_data[type].val2 = 0; //Makes it end on the next tick.
+ break;
+ }
+ sd->status.sp -= 2;
+ clif_updatestatus(sd,SP_SP);
+ }
+ skill_attack(BF_WEAPON,src,src,bl,sc_data?sc_data[type].val3:SG_SUN_WARM,1,tick,0);
+ }
+ }
+ break;
+ case SC_CLOSECONFINE:
+ { //Lock char has released the hold on everyone...
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (sc_data && sc_data[SC_CLOSECONFINE2].timer != -1 && sc_data[SC_CLOSECONFINE2].val2 == src->id) {
+ sc_data[SC_CLOSECONFINE2].val2 = 0;
+ status_change_end(bl, SC_CLOSECONFINE2, -1);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int status_change_clear_buffs (struct block_list *bl)
+{
+ int i;
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (!sc_data)
+ return 0;
+ for (i = 20; i < SC_MAX; i++) {
+ if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90
+ || i == SC_QUAGMIRE || i == SC_SIGNUMCRUCIS || i == SC_DECREASEAGI
+ || i == SC_SLOWDOWN || i == SC_ANKLE|| i == SC_BLADESTOP
+ || i == SC_MINDBREAKER || i == SC_WINKCHARM
+ || i == SC_STOP || i == SC_NOCHAT || i == SC_ORCISH
+ || i == SC_STRIPWEAPON || i == SC_STRIPSHIELD || i == SC_STRIPARMOR || i == SC_STRIPHELM
+ || i == SC_COMBO || i == SC_DANCING || i == SC_GUILDAURA
+ )
+ continue;
+ if(sc_data[i].timer != -1)
+ status_change_end(bl,i,-1);
+ }
+ return 0;
+}
+int status_change_clear_debuffs (struct block_list *bl)
+{
+ int i;
+ struct status_change *sc_data = status_get_sc_data(bl);
+ if (!sc_data)
+ return 0;
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) {
+ if(sc_data[i].timer != -1)
+ status_change_end(bl,i,-1);
+ }
+ //Other ailments not in the common range.
+ if(sc_data[SC_HALLUCINATION].timer != -1)
+ status_change_end(bl,SC_HALLUCINATION,-1);
+ if(sc_data[SC_QUAGMIRE].timer != -1)
+ status_change_end(bl,SC_QUAGMIRE,-1);
+ if(sc_data[SC_SIGNUMCRUCIS].timer != -1)
+ status_change_end(bl,SC_SIGNUMCRUCIS,-1);
+ if(sc_data[SC_DECREASEAGI].timer != -1)
+ status_change_end(bl,SC_DECREASEAGI,-1);
+ if(sc_data[SC_SLOWDOWN].timer != -1)
+ status_change_end(bl,SC_SLOWDOWN,-1);
+ if(sc_data[SC_MINDBREAKER].timer != -1)
+ status_change_end(bl,SC_MINDBREAKER,-1);
+ if(sc_data[SC_WINKCHARM].timer != -1)
+ status_change_end(bl,SC_WINKCHARM,-1);
+ if(sc_data[SC_STOP].timer != -1)
+ status_change_end(bl,SC_STOP,-1);
+ if(sc_data[SC_ORCISH].timer != -1)
+ status_change_end(bl,SC_ORCISH,-1);
+ if(sc_data[SC_STRIPWEAPON].timer != -1)
+ status_change_end(bl,SC_STRIPWEAPON,-1);
+ if(sc_data[SC_STRIPSHIELD].timer != -1)
+ status_change_end(bl,SC_STRIPSHIELD,-1);
+ if(sc_data[SC_STRIPARMOR].timer != -1)
+ status_change_end(bl,SC_STRIPARMOR,-1);
+ if(sc_data[SC_STRIPHELM].timer != -1)
+ status_change_end(bl,SC_STRIPHELM,-1);
+ return 0;
+}
+
+static int status_calc_sigma(void)
+{
+ int i,j,k;
+
+ for(i=0;i<MAX_PC_CLASS;i++) {
+ memset(hp_sigma_val[i],0,sizeof(hp_sigma_val[i]));
+ for(k=0,j=2;j<=MAX_LEVEL;j++) {
+ k += hp_coefficient[i]*j + 50;
+ k -= k%100;
+ hp_sigma_val[i][j-1] = k;
+ }
+ }
+ return 0;
+}
+
+int status_readdb(void) {
+ int i,j;
+ FILE *fp;
+ char line[1024], path[1024],*p;
+
+ sprintf(path, "%s/job_db1.txt", db_path);
+ fp=fopen(path,"r"); // Job-specific values (weight, HP, SP, ASPD)
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[23];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<22 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(j<22)
+ continue;
+ if(atoi(split[0])>=MAX_PC_CLASS)
+ continue;
+ max_weight_base[atoi(split[0])]=atoi(split[1]);
+ hp_coefficient[atoi(split[0])]=atoi(split[2]);
+ hp_coefficient2[atoi(split[0])]=atoi(split[3]);
+ sp_coefficient[atoi(split[0])]=atoi(split[4]);
+ for(j=0;j<17;j++)
+ aspd_base[atoi(split[0])][j]=atoi(split[j+5]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","job_db1.txt");
+
+ memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
+ sprintf(path, "%s/job_db2.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[MAX_LEVEL+1]; //Job Level is limited to MAX_LEVEL, so the bonuses should likewise be limited to it. [Skotlex]
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(j=0,p=line;j<MAX_LEVEL+1 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(atoi(split[0])>=MAX_PC_CLASS)
+ continue;
+ for(i=1;i<j && split[i];i++)
+ job_bonus[atoi(split[0])][i-1]=atoi(split[i]);
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ // ƒTƒCƒY•â³ƒe?ƒuƒ‹
+ for(i=0;i<3;i++)
+ for(j=0;j<20;j++)
+ atkmods[i][j]=100;
+ sprintf(path, "%s/size_fix.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[20];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(atoi(line)<=0)
+ continue;
+ memset(split,0,sizeof(split));
+ for(j=0,p=line;j<20 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ for(j=0;j<20 && split[j];j++)
+ atkmods[i][j]=atoi(split[j]);
+ i++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ // ¸?ƒf?ƒ^ƒe?ƒuƒ‹
+ for(i=0;i<5;i++){
+ for(j=0;j<MAX_REFINE; j++)
+ percentrefinery[i][j]=100;
+ percentrefinery[i][j]=0; //Slot MAX+1 always has 0% success chance [Skotlex]
+ refinebonus[i][0]=0;
+ refinebonus[i][1]=0;
+ refinebonus[i][2]=10;
+ }
+
+ sprintf(path, "%s/refine_db.txt", db_path);
+ fp=fopen(path,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", path);
+ return 1;
+ }
+ i=0;
+ while(fgets(line, sizeof(line)-1, fp)){
+ char *split[16];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(atoi(line)<=0)
+ continue;
+ memset(split,0,sizeof(split));
+ for(j=0,p=line;j<16 && p;j++){
+ split[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ refinebonus[i][0]=atoi(split[0]); // ¸?ƒ{?ƒiƒX
+ refinebonus[i][1]=atoi(split[1]); // ‰ß?¸?ƒ{?ƒiƒX
+ refinebonus[i][2]=atoi(split[2]); // ˆÀ‘S¸?ŒÀŠE
+ for(j=0;j<MAX_REFINE && split[j];j++)
+ percentrefinery[i][j]=atoi(split[j+3]);
+ i++;
+ }
+ fclose(fp); //Lupus. close this file!!!
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
+
+ return 0;
+}
+
+/*==========================================
+ * ƒXƒLƒ‹ŠÖŒW‰Šú‰»ˆ—
+ *------------------------------------------
+ */
+int do_init_status(void)
+{
+ if (SC_MAX > MAX_STATUSCHANGE)
+ {
+ ShowDebug("status.h defines %d status changes, but the MAX_STATUSCHANGE is %d! Fix it.\n", SC_MAX, MAX_STATUSCHANGE);
+ exit(1);
+ }
+ add_timer_func_list(status_change_timer,"status_change_timer");
+ initStatusIconChangeTable();
+ status_readdb();
+ status_calc_sigma();
+ return 0;
+}
diff --git a/src/map/status.h b/src/map/status.h
new file mode 100644
index 000000000..e31667d0c
--- /dev/null
+++ b/src/map/status.h
@@ -0,0 +1,529 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "map.h"
+
+// Status changes listing. These code are for use by the server.
+enum {
+ //First we enumerate common status ailments which are often used around.
+ SC_STONE = 0,
+ SC_FREEZE,
+ SC_STAN,
+ SC_SLEEP,
+ SC_POISON,
+ SC_CURSE,
+ SC_SILENCE,
+ SC_CONFUSION,
+ SC_BLIND,
+ SC_BLEEDING,
+ SC_DPOISON, //10
+
+ //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future.
+ SC_PROVOKE = 20,
+ SC_ENDURE,
+ SC_TWOHANDQUICKEN,
+ SC_CONCENTRATE,
+ SC_HIDING,
+ SC_CLOAKING,
+ SC_ENCPOISON,
+ SC_POISONREACT,
+ SC_QUAGMIRE,
+ SC_ANGELUS,
+ SC_BLESSING, //30
+ SC_SIGNUMCRUCIS,
+ SC_INCREASEAGI,
+ SC_DECREASEAGI,
+ SC_SLOWPOISON,
+ SC_IMPOSITIO ,
+ SC_SUFFRAGIUM,
+ SC_ASPERSIO,
+ SC_BENEDICTIO,
+ SC_KYRIE,
+ SC_MAGNIFICAT, //40
+ SC_GLORIA,
+ SC_AETERNA,
+ SC_ADRENALINE,
+ SC_WEAPONPERFECTION,
+ SC_OVERTHRUST,
+ SC_MAXIMIZEPOWER,
+ SC_TRICKDEAD,
+ SC_LOUD,
+ SC_ENERGYCOAT,
+ SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon...
+ SC_BROKENWEAPON,
+ SC_HALLUCINATION,
+ SC_WEIGHT50 ,
+ SC_WEIGHT90,
+ SC_ASPDPOTION0,
+ SC_ASPDPOTION1,
+ SC_ASPDPOTION2,
+ SC_ASPDPOTION3,
+ SC_SPEEDUP0,
+ SC_SPEEDUP1, //60
+ SC_ATKPOTION,
+ SC_MATKPOTION,
+ SC_WEDDING,
+ SC_SLOWDOWN,
+ SC_ANKLE,
+ SC_KEEPING,
+ SC_BARRIER,
+ SC_STRIPWEAPON,
+ SC_STRIPSHIELD,
+ SC_STRIPARMOR, //70
+ SC_STRIPHELM,
+ SC_CP_WEAPON,
+ SC_CP_SHIELD,
+ SC_CP_ARMOR,
+ SC_CP_HELM,
+ SC_AUTOGUARD,
+ SC_REFLECTSHIELD,
+ SC_SPLASHER,
+ SC_PROVIDENCE,
+ SC_DEFENDER, //80
+ SC_MAGICROD,
+ SC_SPELLBREAKER,
+ SC_AUTOSPELL,
+ SC_SIGHTTRASHER,
+ SC_AUTOBERSERK,
+ SC_SPEARSQUICKEN,
+ SC_AUTOCOUNTER,
+ SC_SIGHT,
+ SC_SAFETYWALL,
+ SC_RUWACH, //90
+ SC_EXTREMITYFIST,
+ SC_EXPLOSIONSPIRITS,
+ SC_COMBO,
+ SC_BLADESTOP_WAIT,
+ SC_BLADESTOP,
+ SC_FIREWEAPON,
+ SC_WATERWEAPON,
+ SC_WINDWEAPON,
+ SC_EARTHWEAPON,
+ SC_VOLCANO, //100,
+ SC_DELUGE,
+ SC_VIOLENTGALE,
+ SC_WATK_ELEMENT,
+ SC_LANDPROTECTOR,
+ SC_ARMOR_ELEMENT,
+ SC_NOCHAT,
+ SC_BABY,
+ SC_AURABLADE,
+ SC_PARRYING,
+ SC_CONCENTRATION, //110
+ SC_TENSIONRELAX,
+ SC_BERSERK,
+ SC_FURY,
+ SC_GOSPEL,
+ SC_ASSUMPTIO,
+ SC_BASILICA,
+ SC_GUILDAURA,
+ SC_MAGICPOWER,
+ SC_EDP,
+ SC_TRUESIGHT, //120
+ SC_WINDWALK,
+ SC_MELTDOWN,
+ SC_CARTBOOST,
+ SC_CHASEWALK,
+ SC_REJECTSWORD,
+ SC_MARIONETTE,
+ SC_MARIONETTE2,
+ SC_MOONLIT,
+ SC_JOINTBEAT,
+ SC_MINDBREAKER, //130
+ SC_MEMORIZE,
+ SC_FOGWALL,
+ SC_SPIDERWEB,
+ SC_DEVOTION,
+ SC_SACRIFICE,
+ SC_STEELBODY,
+ SC_ORCISH,
+ SC_READYSTORM,
+ SC_READYDOWN,
+ SC_READYTURN, //140
+ SC_READYCOUNTER,
+ SC_DODGE,
+ SC_RUN,
+ SC_SHADOWWEAPON,
+ SC_ADRENALINE2,
+ SC_GHOSTWEAPON,
+ SC_KAIZEL,
+ SC_KAAHI,
+ SC_KAUPE,
+ SC_ONEHAND, //150
+ SC_PRESERVE,
+ SC_BATTLEORDERS,
+ SC_REGENERATION,
+ SC_DOUBLECAST,
+ SC_GRAVITATION,
+ SC_MAXOVERTHRUST,
+ SC_LONGING,
+ SC_HERMODE,
+ SC_SHRINK,
+ SC_SIGHTBLASTER, //160
+ SC_WINKCHARM,
+ SC_CLOSECONFINE,
+ SC_CLOSECONFINE2,
+ SC_DANCING,
+ SC_LULLABY,
+ SC_RICHMANKIM,
+ SC_ETERNALCHAOS,
+ SC_DRUMBATTLE,
+ SC_NIBELUNGEN,
+ SC_ROKISWEIL, //170
+ SC_INTOABYSS,
+ SC_SIEGFRIED,
+ SC_WHISTLE,
+ SC_ASSNCROS,
+ SC_POEMBRAGI,
+ SC_APPLEIDUN,
+ SC_UGLYDANCE,
+ SC_HUMMING,
+ SC_DONTFORGETME,
+ SC_FORTUNE, //180
+ SC_SERVICE4U,
+ SC_STOP, //Prevents inflicted chars from walking. [Skotlex]
+ SC_SPURT,
+ SC_SPIRIT,
+ SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1.
+ SC_INTRAVISION,
+ SC_INCALLSTATUS,
+ SC_INCSTR,
+ SC_INCAGI,
+ SC_INCVIT, //190
+ SC_INCINT,
+ SC_INCDEX,
+ SC_INCLUK,
+ SC_INCHIT,
+ SC_INCHITRATE,
+ SC_INCFLEE,
+ SC_INCFLEERATE,
+ SC_INCMHPRATE,
+ SC_INCMSPRATE,
+ SC_INCATKRATE, //200
+ SC_INCMATKRATE,
+ SC_INCDEFRATE,
+ SC_STRFOOD,
+ SC_AGIFOOD,
+ SC_VITFOOD,
+ SC_INTFOOD,
+ SC_DEXFOOD,
+ SC_LUKFOOD,
+ SC_HITFOOD,
+ SC_FLEEFOOD, //210
+ SC_BATKFOOD,
+ SC_WATKFOOD,
+ SC_MATKFOOD,
+ SC_SCRESIST, //Increases resistance to status changes.
+ SC_XMAS, // Xmas Suit [Valaris]
+ SC_WARM, //SG skills [Komurka]
+ SC_SUN_COMFORT,
+ SC_MOON_COMFORT,
+ SC_STAR_COMFORT,
+ SC_FUSION, //220
+ SC_SKILLRATE_UP,
+ SC_SKE,
+ SC_KAITE,
+ SC_SWOO, // [marquis007]
+ SC_SKA, // [marquis007]
+ SC_TKDORI, // [marquis007]
+ //
+ SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex]
+};
+extern int SkillStatusChangeTable[];
+
+//Numerates the Number for the status changes (client-dependent), imported from jA
+enum {
+ SI_BLANK = -1,
+ SI_PROVOKE = 0,
+ SI_ENDURE = 1,
+ SI_TWOHANDQUICKEN = 2,
+ SI_CONCENTRATE = 3,
+ SI_HIDING = 4,
+ SI_CLOAKING = 5,
+ SI_ENCPOISON = 6,
+ SI_POISONREACT = 7,
+ SI_QUAGMIRE = 8,
+ SI_ANGELUS = 9,
+ SI_BLESSING = 10,
+ SI_SIGNUMCRUCIS = 11,
+ SI_INCREASEAGI = 12,
+ SI_DECREASEAGI = 13,
+ SI_SLOWPOISON = 14,
+ SI_IMPOSITIO = 15,
+ SI_SUFFRAGIUM = 16,
+ SI_ASPERSIO = 17,
+ SI_BENEDICTIO = 18,
+ SI_KYRIE = 19,
+ SI_MAGNIFICAT = 20,
+ SI_GLORIA = 21,
+ SI_AETERNA = 22,
+ SI_ADRENALINE = 23,
+ SI_WEAPONPERFECTION = 24,
+ SI_OVERTHRUST = 25,
+ SI_MAXIMIZEPOWER = 26,
+ SI_RIDING = 27,
+ SI_FALCON = 28,
+ SI_TRICKDEAD = 29,
+ SI_LOUD = 30,
+ SI_ENERGYCOAT = 31,
+ SI_BROKENARMOR = 32,
+ SI_BROKENWEAPON = 33,
+ SI_HALLUCINATION = 34,
+ SI_WEIGHT50 = 35,
+ SI_WEIGHT90 = 36,
+ SI_ASPDPOTION = 37,
+ //38: Again Aspd Potion
+ //39: Again Aspd Potion
+ //40: Again Aspd Potion
+ SI_SPEEDPOTION = 41,
+ //42: Again Speed Up
+ SI_STRIPWEAPON = 50,
+ SI_STRIPSHIELD = 51,
+ SI_STRIPARMOR = 52,
+ SI_STRIPHELM = 53,
+ SI_CP_WEAPON = 54,
+ SI_CP_SHIELD = 55,
+ SI_CP_ARMOR = 56,
+ SI_CP_HELM = 57,
+ SI_AUTOGUARD = 58,
+ SI_REFLECTSHIELD = 59,
+ SI_PROVIDENCE = 61,
+ SI_DEFENDER = 62,
+ SI_AUTOSPELL = 65,
+ SI_SPEARQUICKEN = 68,
+ SI_EXPLOSIONSPIRITS = 86,
+ SI_FURY = 87,
+ SI_FIREWEAPON = 90,
+ SI_WATERWEAPON = 91,
+ SI_WINDWEAPON = 92,
+ SI_EARTHWEAPON = 93,
+// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG]
+ SI_AURABLADE = 103,
+ SI_PARRYING = 104,
+ SI_CONCENTRATION = 105,
+ SI_TENSIONRELAX = 106,
+ SI_BERSERK = 107,
+ SI_ASSUMPTIO = 110,
+ SI_GUILDAURA = 112,
+ SI_MAGICPOWER = 113,
+ SI_EDP = 114,
+ SI_TRUESIGHT = 115,
+ SI_WINDWALK = 116,
+ SI_MELTDOWN = 117,
+ SI_CARTBOOST = 118,
+ SI_REJECTSWORD = 120,
+ SI_MARIONETTE = 121,
+ SI_MARIONETTE2 = 122,
+ SI_MOONLIT = 123,
+ SI_BLEEDING = 124,
+ SI_JOINTBEAT = 125,
+ SI_DEVOTION = 130,
+ SI_STEELBODY = 132,
+ SI_CHASEWALK = 134,
+ SI_READYSTORM = 135,
+ SI_READYDOWN = 137,
+ SI_READYTURN = 139,
+ SI_READYCOUNTER = 141,
+ SI_DODGE = 143,
+ SI_SPURT = 145,
+ SI_SHADOWWEAPON = 146,
+ SI_ADRENALINE2 = 147,
+ SI_GHOSTWEAPON = 148,
+ SI_NIGHT = 149,
+ SI_SPIRIT = 149,
+ SI_DEVIL = 152,
+ SI_KAITE = 153,
+ SI_KAIZEL = 156,
+ SI_KAAHI = 157,
+ SI_KAUPE = 158,
+// 159 = blue sparks and item-heal sound effect. Looks like item-use effect.
+// 160
+ SI_ONEHAND = 161,
+ SI_WARM = 165,
+// 166 | The three show the exact same display: ultra red character (165, 166, 167)
+// 167 |
+ SI_SUN_COMFORT = 169,
+ SI_MOON_COMFORT = 170,
+ SI_STAR_COMFORT = 171,
+ SI_PRESERVE = 181,
+ SI_BATTLEORDERS = 182,
+// 184 = WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor
+ SI_DOUBLECAST = 186,
+ SI_MAXOVERTHRUST = 188,
+ SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG]
+ SI_SHRINK = 197,
+ SI_SIGHTBLASTER = 198,
+ SI_WINKCHARM = 199,
+ SI_CLOSECONFINE = 200,
+ SI_CLOSECONFINE2 = 201,
+};
+extern int StatusIconChangeTable[];
+
+extern int current_equip_item_index;
+
+//Mode definitions to clear up code reading. [Skotlex]
+#define MD_CANMOVE 0x001
+#define MD_LOOTER 0x002
+//MD_ANGRY mobs are also aggressive.
+#define MD_AGGRESSIVE 0x804
+#define MD_ASSIST 0x008
+#define MD_CASTSENSOR 0x010
+#define MD_BOSS 0x020
+#define MD_PLANT 0x040
+#define MD_CANATTACK 0x080
+#define MD_DETECTOR 0x100
+//#define MD_CHANGETARGET 0x200 //Mode deprecated, figured out through mob_can_changetarget()
+#define MD_CHANGECHASE 0x400
+#define MD_ANGRY 0x800
+#define MD_MASK 0xFFF
+
+//Status change option definitions (options are what makes status changes visible to chars
+//who were not on your field of sight when it happened)
+//opt1: Non stackable status changes.
+enum {
+ OPT1_STONE = 1, //Petrified
+ OPT1_FREEZE,
+ OPT1_STUN,
+ OPT1_SLEEP,
+ //What is 5?
+ OPT1_STONEWAIT=6 //Petrifying
+};
+
+//opt2: Stackable status changes.
+#define OPT2_POISON 0x001
+#define OPT2_CURSE 0x002
+#define OPT2_SILENCE 0x004
+//0x008 Odd howl sound, Signum crucis?
+#define OPT2_SIGNUMCRUCIS 0x008
+#define OPT2_BLIND 0x010
+//0x020 - nothing
+//0x040 - nothing
+#define OPT2_DPOISON 0x080
+//0x100
+
+//Opt3: Skill state changes, stackable.
+#define OPT3_SPEEDUP 0x001 //Quicken skills
+#define OPT3_POWERUP 0x002 //Power Thrust
+#define OPT3_SHIELD 0x004 //Energy Coat
+#define OPT3_FURY 0x008 //Explosion spirits
+#define OPT3_ELECTRIC 0x010 //Steel Body
+#define OPT3_STOP 0x020 //Blade Stop
+//64 Unknown
+#define OPT3_BERSERK 0x080 //Berserk
+//256 Unknown
+//512 Unknown
+#define OPT3_PINKAURA 0x400 //Marionette
+#define OPT3_AURASHIELD 0x800 //Assumptio
+#define OPT3_HEAT 0x1000 //Warmth Skills
+
+// ƒpƒ‰ƒ[ƒ^Š“¾Œn battle.c ‚æ‚èˆÚ“®
+int status_get_class(struct block_list *bl);
+int status_get_dir(struct block_list *bl);
+int status_get_lv(struct block_list *bl);
+int status_get_range(struct block_list *bl);
+int status_get_hp(struct block_list *bl);
+int status_get_max_hp(struct block_list *bl);
+int status_get_str(struct block_list *bl);
+int status_get_agi(struct block_list *bl);
+int status_get_vit(struct block_list *bl);
+int status_get_int(struct block_list *bl);
+int status_get_dex(struct block_list *bl);
+int status_get_luk(struct block_list *bl);
+int status_get_hit(struct block_list *bl);
+int status_get_flee(struct block_list *bl);
+int status_get_def(struct block_list *bl);
+int status_get_mdef(struct block_list *bl);
+int status_get_flee2(struct block_list *bl);
+int status_get_def2(struct block_list *bl);
+int status_get_mdef2(struct block_list *bl);
+int status_get_batk(struct block_list *bl);
+int status_get_atk(struct block_list *bl);
+int status_get_atk2(struct block_list *bl);
+int status_get_speed(struct block_list *bl);
+int status_get_adelay(struct block_list *bl);
+int status_get_amotion(struct block_list *bl);
+int status_get_dmotion(struct block_list *bl);
+int status_get_element(struct block_list *bl);
+int status_get_attack_sc_element(struct block_list *bl);
+int status_get_attack_element(struct block_list *bl);
+int status_get_attack_element2(struct block_list *bl); //¶Žè•Ší‘®«Žæ“¾
+#define status_get_elem_type(bl) (status_get_element(bl)%10)
+#define status_get_elem_level(bl) (status_get_element(bl)/10/2)
+int status_get_party_id(struct block_list *bl);
+int status_get_guild_id(struct block_list *bl);
+int status_get_race(struct block_list *bl);
+int status_get_size(struct block_list *bl);
+int status_get_mode(struct block_list *bl);
+int status_get_mexp(struct block_list *bl);
+int status_get_race2(struct block_list *bl);
+
+struct status_change *status_get_sc_data(struct block_list *bl);
+short *status_get_sc_count(struct block_list *bl);
+short *status_get_opt1(struct block_list *bl);
+short *status_get_opt2(struct block_list *bl);
+short *status_get_opt3(struct block_list *bl);
+short *status_get_option(struct block_list *bl);
+
+int status_get_matk1(struct block_list *bl);
+int status_get_matk2(struct block_list *bl);
+int status_get_critical(struct block_list *bl);
+int status_get_atk_(struct block_list *bl);
+int status_get_atk_2(struct block_list *bl);
+int status_get_atk2(struct block_list *bl);
+
+int status_isdead(struct block_list *bl);
+int status_isimmune(struct block_list *bl);
+
+int status_get_sc_def(struct block_list *bl, int type);
+#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1))
+#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2))
+#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2))
+#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK))
+
+// ó‘ÔˆÙíŠÖ˜A skill.c ‚æ‚èˆÚ“®
+int status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag);
+int status_change_end( struct block_list* bl , int type,int tid );
+int status_change_timer(int tid, unsigned int tick, int id, int data);
+int status_change_timer_sub(struct block_list *bl, va_list ap );
+int status_change_clear(struct block_list *bl,int type);
+int status_change_clear_buffs(struct block_list *bl);
+int status_change_clear_debuffs(struct block_list *bl);
+
+int status_calc_pet(struct map_session_data* sd, int first); // [Skotlex]
+int status_calc_pc(struct map_session_data* sd,int first);
+int status_calc_str(struct block_list *,int);
+int status_calc_agi(struct block_list *,int);
+int status_calc_vit(struct block_list *,int);
+int status_calc_int(struct block_list *,int);
+int status_calc_dex(struct block_list *,int);
+int status_calc_luk(struct block_list *,int);
+int status_calc_batk(struct block_list *,int);
+int status_calc_watk(struct block_list *,int);
+int status_calc_matk(struct block_list *,int);
+int status_calc_hit(struct block_list *,int);
+int status_calc_critical(struct block_list *,int);
+int status_calc_flee(struct block_list *,int);
+int status_calc_flee2(struct block_list *,int);
+int status_calc_def(struct block_list *,int);
+int status_calc_def2(struct block_list *,int);
+int status_calc_mdef(struct block_list *,int);
+int status_calc_mdef2(struct block_list *,int);
+int status_calc_speed(struct block_list *,int);
+int status_calc_aspd_rate(struct block_list *,int);
+int status_calc_maxhp(struct block_list *,int);
+int status_calc_maxsp(struct block_list *,int);
+int status_quick_recalc_speed(struct map_session_data*, int, int, char); // [Celest] - modified by [Skotlex]
+int status_getrefinebonus(int lv,int type);
+int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex]
+
+//Use this to refer the max refinery level [Skotlex]
+#define MAX_REFINE 10
+extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex]
+
+int status_readdb(void);
+int do_init_status(void);
+
+#endif
diff --git a/src/map/storage.c b/src/map/storage.c
new file mode 100644
index 000000000..b90d2d306
--- /dev/null
+++ b/src/map/storage.c
@@ -0,0 +1,693 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "storage.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "guild.h"
+#include "battle.h"
+#include "atcommand.h"
+
+static struct dbt *storage_db;
+static struct dbt *guild_storage_db;
+
+/*==========================================
+ * ‘qŒÉ“àƒAƒCƒeƒ€ƒ\[ƒg
+ *------------------------------------------
+ */
+int storage_comp_item(const void *_i1, const void *_i2)
+{
+ struct item *i1 = (struct item *)_i1;
+ struct item *i2 = (struct item *)_i2;
+
+ if (i1->nameid == i2->nameid)
+ return 0;
+ else if (!(i1->nameid) || !(i1->amount))
+ return 1;
+ else if (!(i2->nameid) || !(i2->amount))
+ return -1;
+ return i1->nameid - i2->nameid;
+}
+
+void sortage_sortitem (struct storage *stor)
+{
+ nullpo_retv(stor);
+ qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+void sortage_gsortitem (struct guild_storage* gstor)
+{
+ nullpo_retv(gstor);
+ qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+/*==========================================
+ * ‰Šú‰»‚Æ‚©
+ *------------------------------------------
+ */
+int do_init_storage(void) // map.c::do_init()‚©‚çŒÄ‚΂ê‚é
+{
+ storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ return 1;
+}
+void do_final_storage(void) // by [MC Cameri]
+{
+ storage_db->destroy(storage_db,NULL);
+ guild_storage_db->destroy(guild_storage_db,NULL);
+}
+
+
+static int storage_reconnect_sub(DBKey key,void *data,va_list ap)
+{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex]
+ int type = va_arg(ap, int);
+ if (type)
+ { //Guild Storage
+ struct guild_storage* stor = (struct guild_storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_guild_storagesave(0, stor->guild_id);
+ }
+ else
+ { //Account Storage
+ struct storage* stor = (struct storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_storage_save(stor->account_id);
+ }
+ return 0;
+}
+
+//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex
+void do_reconnect_storage(void)
+{
+ storage_db->foreach(storage_db, storage_reconnect_sub, 0);
+ guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1);
+}
+
+static void* create_storage(DBKey key, va_list args) {
+ struct storage *stor;
+ stor = (struct storage *) aCallocA (sizeof(struct storage), 1);
+ stor->account_id = key.i;
+ return stor;
+}
+struct storage *account2storage(int account_id)
+{
+ return idb_ensure(storage_db,account_id,create_storage);
+}
+
+// Just to ask storage, without creation
+struct storage *account2storage2(int account_id)
+{
+ return idb_get(storage_db, account_id);
+}
+
+int storage_delete(int account_id)
+{
+ idb_remove(storage_db,account_id);
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ðŠJ‚­
+ *------------------------------------------
+ */
+int storage_storageopen(struct map_session_data *sd)
+{
+//#ifdef TXT_ONLY
+ struct storage *stor;
+//#endif
+ nullpo_retr(0, sd);
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level is allowed to put items to storage
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+//Storage loading always from sql idea from Komurka [Skotlex] - removed as it opens exploits when server lags.
+//#ifdef TXT_ONLY
+ if((stor = idb_get(storage_db,sd->status.account_id)) != NULL) {
+ if (stor->storage_status == 0 && sd->state.storage_flag == 0) {
+ stor->storage_status = 1;
+ sd->state.storage_flag = 1;
+ clif_storageitemlist(sd,stor);
+ clif_storageequiplist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+ return 0;
+ }
+ } else
+//#endif
+ intif_request_storage(sd->status.account_id);
+
+ return 1;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ÖƒAƒCƒeƒ€’ljÁ
+ *------------------------------------------
+ */
+int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+ nullpo_retr(1, item_data);
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+ nullpo_retr(1, data = itemdb_search(item_data->nameid));
+
+ if (!itemdb_canstore(item_data->nameid, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ i=MAX_STORAGE;
+ if(!itemdb_isequip2(data)){
+ // ‘•”õ•i‚Å‚Í‚È‚¢‚Ì‚ÅAŠùŠ—L•i‚È‚çŒÂ”‚̂ݕω»‚³‚¹‚é
+ for(i=0;i<MAX_STORAGE;i++){
+ if( compare_item (&stor->storage_[i], item_data)) {
+ if(stor->storage_[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_storageitemadded(sd,stor,i,amount);
+ break;
+ }
+ }
+ }
+ if(i>=MAX_STORAGE){
+ // ‘•”õ•i‚©–¢Š—L•i‚¾‚Á‚½‚Ì‚Å‹ó‚«—“‚֒ljÁ
+ for(i=0;i<MAX_STORAGE;i++){
+ if(stor->storage_[i].nameid==0){
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_storageitemadded(sd,stor,i,amount);
+ clif_updatestorageamount(sd,stor);
+ break;
+ }
+ }
+ if(i>=MAX_STORAGE)
+ return 1;
+ }
+
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉƒAƒCƒeƒ€‚ðŒ¸‚ç‚·
+ *------------------------------------------
+ */
+int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ memset(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updatestorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚Ö“ü‚ê‚é
+ *------------------------------------------
+ */
+int storage_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if( (stor->storage_amount <= MAX_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.inventory[index].amount) && (amount > 0) ) { //valid amount
+// log_tostorage(sd, index, 0);
+ if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ // remove item from inventory
+ pc_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚©‚ço‚·
+ *------------------------------------------
+ */
+int storage_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ storage_delitem(sd,stor,index,amount);
+ //else //taken out because it can dupe items if the above fails somehow :) [Kevin]
+ //clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 0);
+ } // valid amount
+ }// valid index
+ }// storage open
+
+ return 0;
+}
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚ÖƒJ[ƒg‚©‚ç“ü‚ê‚é
+ *------------------------------------------
+ */
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if( (stor->storage_amount <= MAX_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.cart[index].amount) && (amount > 0) ) { //valid amount
+ if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+
+ return 0;
+}
+
+/*==========================================
+ * ƒJƒvƒ‰‘qŒÉ‚©‚çƒJ[ƒg‚Öo‚·
+ *------------------------------------------
+ */
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0){
+ storage_delitem(sd,stor,index,amount);
+ }
+ } // valid amount
+ }// valid index
+ }// storage open
+
+ return 0;
+}
+
+
+/*==========================================
+ * Modified By Valaris to save upon closing [massdriller]
+ *------------------------------------------
+ */
+int storage_storageclose(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ clif_storageclose(sd);
+ chrif_save(sd, 0); //This will invoke the storage save function as well. [Skotlex]
+
+ stor->storage_status=0;
+ sd->state.storage_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ * ƒƒOƒAƒEƒgŽžŠJ‚¢‚Ä‚¢‚éƒJƒvƒ‰‘qŒÉ‚Ì•Û‘¶
+ *------------------------------------------
+ */
+int storage_storage_quit(struct map_session_data *sd, int flag)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+
+ stor = account2storage2(sd->status.account_id);
+ if(stor) {
+ chrif_save(sd, flag); //Invokes the storage saving as well.
+ stor->storage_status = 0;
+ sd->state.storage_flag = 0;
+ }
+
+ return 0;
+}
+
+void storage_storage_dirty(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ stor=account2storage2(sd->status.account_id);
+
+ if(stor)
+ stor->dirty = 1;
+}
+
+int storage_storage_save(int account_id)
+{
+ struct storage *stor;
+
+ stor=account2storage2(account_id);
+ if(stor && stor->dirty)
+ {
+ intif_send_storage(stor);
+ return 1;
+ }
+
+ return 0;
+}
+
+//Ack from Char-server indicating the storage was saved. [Skotlex]
+int storage_storage_saved(int account_id)
+{
+ struct storage *stor;
+
+ if((stor=account2storage2(account_id)) != NULL)
+ { //Only mark it clean if it's not in use. [Skotlex]
+ if (stor->dirty && stor->storage_status == 0)
+ {
+ stor->dirty = 0;
+ sortage_sortitem(stor);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void* create_guildstorage(DBKey key, va_list args) {
+ struct guild_storage *gs = NULL;
+ gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1);
+ gs->guild_id=key.i;
+ return gs;
+}
+struct guild_storage *guild2storage(int guild_id)
+{
+ struct guild_storage *gs = NULL;
+ if(guild_search(guild_id) != NULL)
+ gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage);
+ return gs;
+}
+
+struct guild_storage *guild2storage2(int guild_id)
+{ //For just locating a storage without creating one. [Skotlex]
+ return idb_get(guild_storage_db,guild_id);
+}
+
+int guild_storage_delete(int guild_id)
+{
+ idb_remove(guild_storage_db,guild_id);
+ return 0;
+}
+
+int storage_guild_storageopen(struct map_session_data *sd)
+{
+ struct guild_storage *gstor;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id <= 0)
+ return 2;
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus]
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+
+ if((gstor = guild2storage2(sd->status.guild_id)) != NULL) {
+ if(gstor->storage_status)
+ return 1;
+ if(sd->state.storage_flag)
+ return 1; //Can't open both storages at a time.
+ gstor->storage_status = 1;
+ sd->state.storage_flag = 2;
+ clif_guildstorageitemlist(sd,gstor);
+ clif_guildstorageequiplist(sd,gstor);
+ clif_updateguildstorageamount(sd,gstor);
+ return 0;
+ }
+ else {
+ gstor = guild2storage(sd->status.guild_id);
+ gstor->storage_status = 1;
+ intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
+ }
+
+ return 0;
+}
+
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+ nullpo_retr(1, item_data);
+ nullpo_retr(1, data = itemdb_search(item_data->nameid));
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+
+ if (!itemdb_canguildstore(item_data->nameid, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ i=MAX_GUILD_STORAGE;
+ if(!itemdb_isequip2(data)){
+ // ‘•”õ•i‚Å‚Í‚È‚¢‚Ì‚ÅAŠùŠ—L•i‚È‚çŒÂ”‚̂ݕω»‚³‚¹‚é
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(compare_item(&stor->storage_[i], item_data)) {
+ if(stor->storage_[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ break;
+ }
+ }
+ }
+ if(i>=MAX_GUILD_STORAGE){
+ // ‘•”õ•i‚©–¢Š—L•i‚¾‚Á‚½‚Ì‚Å‹ó‚«—“‚֒ljÁ
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(stor->storage_[i].nameid==0){
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ clif_updateguildstorageamount(sd,stor);
+ break;
+ }
+ }
+ if(i>=MAX_GUILD_STORAGE)
+ return 1;
+ }
+ stor->dirty = 1;
+ return 0;
+}
+
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ memset(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updateguildstorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+ stor->dirty = 1;
+ return 0;
+}
+
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if( (stor->storage_amount <= MAX_GUILD_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.inventory[index].amount) && (amount > 0) ) { //valid amount
+// log_tostorage(sd, index, 1);
+ if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ // remove item from inventory
+ pc_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_GUILD_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ guild_storage_delitem(sd,stor,index,amount);
+ else
+ clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 1);
+ } // valid amount
+ }// valid index
+ }// storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if( (stor->storage_amount <= MAX_GUILD_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open
+ if(index>=0 && index<MAX_INVENTORY) { // valid index
+ if( (amount <= sd->status.cart[index].amount) && (amount > 0) ) { //valid amount
+ if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+ } // valid amount
+ }// valid index
+ }// storage not full & storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+
+ if((stor=guild2storage2(sd->status.guild_id)) != NULL) {
+ if(stor->storage_status == 1) { // storage open
+ if(index>=0 && index<MAX_GUILD_STORAGE) { // valid index
+ if( (amount <= stor->storage_[index].amount) && (amount > 0) ) { //valid amount
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0){
+ guild_storage_delitem(sd,stor,index,amount);
+ }
+ } // valid amount
+ }// valid index
+ }// storage open
+ }
+
+ return 0;
+}
+
+int storage_guild_storagesave(int account_id, int guild_id)
+{
+ struct guild_storage *stor = guild2storage2(guild_id);
+
+ if(stor && stor->dirty)
+ {
+ intif_send_guild_storage(account_id,stor);
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storagesaved(int account_id, int guild_id)
+{
+ struct guild_storage *stor;
+
+ if((stor=guild2storage2(guild_id)) != NULL) {
+ if (stor->dirty && stor->storage_status == 0)
+ { //Storage has been correctly saved.
+ stor->dirty = 0;
+ sortage_gsortitem(stor);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storageclose(struct map_session_data *sd)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ clif_storageclose(sd);
+ chrif_save(sd, 0); //This one also saves the storage. [Skotlex]
+
+ stor->storage_status=0;
+ sd->state.storage_flag = 0;
+
+ return 0;
+}
+
+int storage_guild_storage_quit(struct map_session_data *sd,int flag)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ sd->state.storage_flag = 0;
+ stor->storage_status = 0;
+
+ chrif_save(sd,flag);
+ if(!flag) //Only during a guild break flag is 1.
+ storage_guild_storagesave(sd->status.account_id,sd->status.guild_id);
+ else //When the guild was broken, close the storage of he who has it open.
+ clif_storageclose(sd);
+
+ return 0;
+}
diff --git a/src/map/storage.h b/src/map/storage.h
new file mode 100644
index 000000000..946dfa62b
--- /dev/null
+++ b/src/map/storage.h
@@ -0,0 +1,45 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STORAGE_H_
+#define _STORAGE_H_
+
+#include "../common/mmo.h"
+
+int storage_storageopen(struct map_session_data *sd);
+int storage_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_storageget(struct map_session_data *sd,int index,int amount);
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_storageclose(struct map_session_data *sd);
+int do_init_storage(void);
+void do_final_storage(void);
+void do_reconnect_storage(void);
+struct storage *account2storage(int account_id);
+struct storage *account2storage2(int account_id);
+int storage_delete(int account_id);
+int storage_storage_quit(struct map_session_data *sd, int flag);
+int storage_storage_save(int account_id);
+int storage_storage_saved(int account_id); //Ack from char server that guild store was saved.
+void storage_storage_dirty(struct map_session_data *sd);
+
+struct guild_storage *guild2storage(int guild_id);
+int guild_storage_delete(int guild_id);
+int storage_guild_storageopen(struct map_session_data *sd);
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount);
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageclose(struct map_session_data *sd);
+int storage_guild_storage_quit(struct map_session_data *sd,int flag);
+int storage_guild_storagesave(int account_id, int guild_id);
+int storage_guild_storagesaved(int account_id, int guild_id); //Ack from char server that guild store was saved.
+
+int storage_comp_item(const void *_i1, const void *_i2);
+//int storage_comp_item(const struct item* i1, const struct item* i2);
+void sortage_sortitem(struct storage* stor);
+void sortage_gsortitem(struct guild_storage* gstor);
+
+#endif
diff --git a/src/map/trade.c b/src/map/trade.c
new file mode 100644
index 000000000..c9b9b21b0
--- /dev/null
+++ b/src/map/trade.c
@@ -0,0 +1,569 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "trade.h"
+#include "pc.h"
+#include "npc.h"
+#include "battle.h"
+#include "chrif.h"
+#include "storage.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "log.h"
+
+
+/*==========================================
+ * Žæˆø—v¿‚ð‘ŠŽè‚É‘—‚é
+ *------------------------------------------
+ */
+void trade_traderequest(struct map_session_data *sd, int target_id) {
+ struct map_session_data *target_sd;
+ int level;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(target_id)) != NULL) {
+ if (!battle_config.invite_request_check) {
+ if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) {
+ clif_tradestart(sd, 2); // ‘ŠŽè‚ÍPT—v¿’†‚©Guild—v¿’†
+ return;
+ }
+ }
+ level = pc_isGM(sd);
+ if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade
+ {
+ clif_displaymessage(sd->fd, msg_txt(246));
+ trade_tradecancel(sd); // GM is not allowed to trade
+ } else if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) {
+ trade_tradecancel(sd); // person is in another trade
+ } else {
+ //Fixed. Only real GMs can request trade from far away! [Lupus]
+ if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
+ (sd->bl.x - target_sd->bl.x <= -5 || sd->bl.x - target_sd->bl.x >= 5) ||
+ (sd->bl.y - target_sd->bl.y <= -5 || sd->bl.y - target_sd->bl.y >= 5))) {
+ clif_tradestart(sd, 0); // too far
+ } else if (sd != target_sd) {
+ target_sd->trade_partner = sd->status.account_id;
+ sd->trade_partner = target_sd->status.account_id;
+ clif_traderequest(target_sd, sd->status.name);
+ }
+ }
+ } else {
+ clif_tradestart(sd, 1); // character does not exist
+ }
+}
+
+/*==========================================
+ * Žæˆø—v¿
+ *------------------------------------------
+ */
+void trade_tradeack(struct map_session_data *sd, int type) {
+ struct map_session_data *target_sd;
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ clif_tradestart(target_sd, type);
+ clif_tradestart(sd, type);
+ if (type == 4) { // Cancel
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ }
+
+
+ if (type == 3) { //Initiate trade
+ memset(&sd->deal, 0, sizeof(sd->deal));
+ memset(&target_sd->deal, 0, sizeof(target_sd->deal));
+ }
+
+ if (sd->npc_id != 0)
+ npc_event_dequeue(sd);
+ if (target_sd->npc_id != 0)
+ npc_event_dequeue(target_sd);
+
+ //close STORAGE window if it's open. It protects from spooffing packets [Lupus]
+ if (sd->state.storage_flag == 1)
+ storage_storageclose(sd);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storageclose(sd);
+ }
+}
+
+/*==========================================
+ * Check here hacker for duplicate item in trade
+ * normal client refuse to have 2 same types of item (except equipment) in same trade window
+ * normal client authorise only no equiped item and only from inventory
+ *------------------------------------------
+ */
+int impossible_trade_check(struct map_session_data *sd) {
+ struct item inventory[MAX_INVENTORY];
+ char message_to_gm[200];
+ int i, index;
+
+ nullpo_retr(1, sd);
+
+ if(sd->deal.zeny > sd->status.zeny)
+ {
+ pc_setglobalreg(sd,"ZENY_HACKER",1);
+ return -1;
+ }
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // remove this part: arrows can be trade and equiped
+ // re-added! [celest]
+ // remove equiped items (they can not be trade)
+ for (i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & 0x8000))
+ memset(&inventory[i], 0, sizeof(struct item));
+
+ // check items in player inventory
+ for(i = 0; i < 10; i++)
+ if (sd->deal.item[i].amount < 0) { // negativ? -> hack
+// printf("Negativ amount in trade, by hack!\n"); // normal client send cancel when we type negativ amount
+ return -1;
+ } else if (sd->deal.item[i].amount > 0) {
+ index = sd->deal.item[i].index;
+ inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory
+// printf("%d items left\n", inventory[index].amount);
+ if (inventory[index].amount < 0) { // if more than the player have -> hack
+// printf("A player try to trade more items that he has: hack!\n");
+ sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ sprintf(message_to_gm, msg_txt(539), sd->status.inventory[index].amount, sd->status.inventory[index].nameid, sd->status.inventory[index].amount - inventory[index].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_hack_trade < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540), battle_config.ban_spoof_namer); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_hack_trade > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_spoof_namer); // This player has been banned for %d minute(s).
+ } else {
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+ }
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Check here if we can add item in inventory (against full inventory)
+ *------------------------------------------
+ */
+int trade_check(struct map_session_data *sd) {
+ struct item inventory[MAX_INVENTORY];
+ struct item inventory2[MAX_INVENTORY];
+ struct item_data *data;
+ struct map_session_data *target_sd;
+ int trade_i, i, amount;
+
+ target_sd = map_id2sd(sd->trade_partner);
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+ memcpy(&inventory2, &target_sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // check free slot in both inventory
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ amount = sd->deal.item[trade_i].amount;
+ if (amount > 0) {
+ int n = sd->deal.item[trade_i].index;
+ // check quantity
+ if (amount > inventory[n].amount)
+ amount = inventory[n].amount;
+ if (amount > 0) {
+ data = itemdb_search(inventory[n].nameid);
+ i = MAX_INVENTORY;
+ // check for non-equipement item
+ if (!itemdb_isequip2(data)) {
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory2[i].nameid == inventory[n].nameid &&
+ inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] &&
+ inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) {
+ if (inventory2[i].amount + amount > MAX_AMOUNT) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ inventory2[i].amount += amount;
+ inventory[n].amount -= amount;
+ if (inventory[n].amount <= 0)
+ memset(&inventory[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ // check for equipement
+ if (i == MAX_INVENTORY) {
+ for(i = 0; i < MAX_INVENTORY; i++) {
+ if (inventory2[i].nameid == 0) {
+ memcpy(&inventory2[i], &inventory[n], sizeof(struct item));
+ inventory2[i].amount = amount;
+ inventory[n].amount -= amount;
+ if (inventory[n].amount <= 0)
+ memset(&inventory[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ }
+ }
+ }
+ amount = target_sd->deal.item[trade_i].amount;
+ if (amount > 0) {
+ int n = target_sd->deal.item[trade_i].index;
+ // check quantity
+ if (amount > inventory2[n].amount)
+ amount = inventory2[n].amount;
+ if (amount > 0) {
+ // search if it's possible to add item (for full inventory)
+ data = itemdb_search(inventory2[n].nameid);
+ i = MAX_INVENTORY;
+ if (!itemdb_isequip2(data)) {
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid == inventory2[n].nameid &&
+ inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] &&
+ inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) {
+ if (inventory[i].amount + amount > MAX_AMOUNT) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ inventory[i].amount += amount;
+ inventory2[n].amount -= amount;
+ if (inventory2[n].amount <= 0)
+ memset(&inventory2[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ for(i = 0; i < MAX_INVENTORY; i++) {
+ if (inventory[i].nameid == 0) {
+ memcpy(&inventory[i], &inventory2[n], sizeof(struct item));
+ inventory[i].amount = amount;
+ inventory2[n].amount -= amount;
+ if (inventory2[n].amount <= 0)
+ memset(&inventory2[n], 0, sizeof(struct item));
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ clif_displaymessage(sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ clif_displaymessage(target_sd->fd, msg_txt(592)); // Trade can not be done, because one of your doesn't have enough free slots in its inventory.
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * Adds an item/qty to the trade window [rewrite by Skotlex]
+ *------------------------------------------
+ */
+void trade_tradeadditem(struct map_session_data *sd, int index, int amount) {
+ struct map_session_data *target_sd;
+ int trade_i, trade_weight, nameid;
+
+ nullpo_retv(sd);
+ if ((target_sd = map_id2sd(sd->trade_partner)) == NULL || sd->state.deal_locked > 0)
+ return; //Can't add stuff.
+
+ if (index == 0)
+ { //Adding Zeny
+ if (amount >= 0 && amount <= MAX_ZENY && amount <= sd->status.zeny && // check amount
+ (target_sd->status.zeny + amount) <= MAX_ZENY) // fix positiv overflow
+ { //Check Ok
+ sd->deal.zeny = amount;
+ clif_tradeadditem(sd, target_sd, 0, amount);
+ } else //Cancel Transaction
+ trade_tradecancel(sd);
+ return;
+ }
+ //Add an Item
+ index = index -2; //Why the actual index used is -2?
+ //Item checks...
+ if (index < 0 || index > MAX_INVENTORY)
+ return;
+ if (amount < 0 || amount > sd->status.inventory[index].amount)
+ return;
+
+ nameid = sd->inventory_data[index]->nameid;
+
+ if (!itemdb_cantrade(nameid, pc_isGM(sd), pc_isGM(target_sd)) && //Can't trade
+ (pc_get_partner(sd) != target_sd || !itemdb_canpartnertrade(nameid, pc_isGM(sd), pc_isGM(target_sd)))) //Can't partner-trade
+ {
+ clif_displaymessage (sd->fd, msg_txt(260));
+ return;
+ }
+
+ for(trade_i = 0; trade_i < 10; trade_i++)
+ { //Locate a trade position
+ if (sd->deal.item[trade_i].index == index ||
+ sd->deal.item[trade_i].amount == 0)
+ break;
+ }
+ if (trade_i >= 10) //No space left
+ return;
+
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight)
+ { //fail to add item -- the player was over weighted.
+ clif_tradeitemok(sd, index, 1);
+ return;
+ }
+
+ if (sd->deal.item[trade_i].index == index)
+ { //The same item as before is being readjusted.
+ if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount)
+ { //packet deal exploit check
+ amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount;
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ }
+ sd->deal.item[trade_i].amount += amount;
+ } else { //New deal item
+ sd->deal.item[trade_i].index = index;
+ sd->deal.item[trade_i].amount = amount;
+ }
+ sd->deal.weight += trade_weight;
+
+ if (impossible_trade_check(sd))
+ { // check exploit (trade more items that you have)
+ trade_tradecancel(sd);
+ return;
+ }
+
+ clif_tradeitemok(sd, index+2, 0); // Return the index as it was received
+ clif_tradeadditem(sd, target_sd, index+2, amount); //index fix
+}
+
+/*==========================================
+ * ƒAƒCƒeƒ€’ljÁŠ®—¹(ok‰Ÿ‚µ)
+ *------------------------------------------
+ */
+void trade_tradeok(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+
+ nullpo_retv(sd);
+
+ // check items
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ if ((((sd->deal.item[trade_i].index) >= 0) &&
+ (sd->deal.item[trade_i].amount > sd->status.inventory[sd->deal.item[trade_i].index].amount)) ||
+ (sd->deal.item[trade_i].amount < 0)) {
+ trade_tradecancel(sd);
+ return;
+ }
+ }
+
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(sd)) {
+ trade_tradecancel(sd);
+ return;
+ }
+
+ // check zeny
+ if (sd->deal.zeny < 0 || sd->deal.zeny > MAX_ZENY || sd->deal.zeny > sd->status.zeny) { // check amount
+ trade_tradecancel(sd);
+ return;
+ }
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ sd->state.deal_locked = 1;
+ clif_tradeitemok(sd, 0, 0);
+ clif_tradedeal_lock(sd, 0);
+ clif_tradedeal_lock(target_sd, 1);
+ }
+}
+
+/*==========================================
+ * ŽæˆøƒLƒƒƒ“ƒZƒ‹
+ *------------------------------------------
+ */
+void trade_tradecancel(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
+ if (sd->deal.item[trade_i].amount != 0) {
+ clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0);
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+ }
+ if (target_sd->deal.item[trade_i].amount != 0) {
+ clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0);
+ target_sd->deal.item[trade_i].index = 0;
+ target_sd->deal.item[trade_i].amount = 0;
+ }
+ }
+ if (sd->deal.zeny) {
+ clif_updatestatus(sd, SP_ZENY);
+ sd->deal.zeny = 0;
+ }
+ if (target_sd->deal.zeny) {
+ clif_updatestatus(target_sd, SP_ZENY);
+ target_sd->deal.zeny = 0;
+ }
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ clif_tradecancelled(sd);
+ clif_tradecancelled(target_sd);
+ }
+}
+
+/*==========================================
+ * Žæˆø‹–‘ø(trade‰Ÿ‚µ)
+ *------------------------------------------
+ */
+void trade_tradecommit(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+ int flag;
+
+ nullpo_retv(sd);
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) != NULL) {
+ if ((sd->state.deal_locked >= 1) && (target_sd->state.deal_locked >= 1)) { // both have pressed 'ok'
+ if (sd->state.deal_locked < 2) { // set locked to 2
+ sd->state.deal_locked = 2;
+ }
+ if (target_sd->state.deal_locked == 2) { // the other one pressed 'trade' too
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(sd)) {
+ trade_tradecancel(sd);
+ return;
+ }
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(target_sd)) {
+ trade_tradecancel(target_sd);
+ return;
+ }
+ // check zenys value against hackers
+ if (sd->deal.zeny >= 0 && sd->deal.zeny <= MAX_ZENY && sd->deal.zeny <= sd->status.zeny && // check amount
+ (target_sd->status.zeny + sd->deal.zeny) <= MAX_ZENY && // fix positiv overflow
+ target_sd->deal.zeny >= 0 && target_sd->deal.zeny <= MAX_ZENY && target_sd->deal.zeny <= target_sd->status.zeny && // check amount
+ (sd->status.zeny + target_sd->deal.zeny) <= MAX_ZENY) { // fix positiv overflow
+
+ // check for full inventory (can not add traded items)
+ if (!trade_check(sd)) { // check the both players
+ trade_tradecancel(sd);
+ return;
+ }
+
+ // trade is accepted
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ if (sd->deal.item[trade_i].amount != 0) {
+ int n = sd->deal.item[trade_i].index;
+
+ if (sd->status.inventory[n].amount < sd->deal.item[trade_i].amount)
+ sd->deal.item[trade_i].amount = sd->status.inventory[n].amount;
+ log_trade(sd, target_sd, n, sd->deal.item[trade_i].amount);
+
+ flag = pc_additem(target_sd, &sd->status.inventory[n], sd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(sd, "T", 0, sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]);
+ log_pick(target_sd, "T", 0, sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]);
+ //Logs
+ pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1);
+ } else {
+ clif_additem(sd, n, sd->deal.item[trade_i].amount, 0);
+ }
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+
+ }
+ if (target_sd->deal.item[trade_i].amount != 0) {
+ int n = target_sd->deal.item[trade_i].index;
+
+ if (target_sd->status.inventory[n].amount < target_sd->deal.item[trade_i].amount)
+ target_sd->deal.item[trade_i].amount = target_sd->status.inventory[n].amount;
+
+ log_trade(target_sd, sd, n, target_sd->deal.item[trade_i].amount);
+
+ flag = pc_additem(sd, &target_sd->status.inventory[n], target_sd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.pick > 0 )
+ log_pick(target_sd, "T", 0, target_sd->status.inventory[n].nameid, -(target_sd->deal.item[trade_i].amount), &target_sd->status.inventory[n]);
+ log_pick(sd, "T", 0, target_sd->status.inventory[n].nameid, target_sd->deal.item[trade_i].amount, &target_sd->status.inventory[n]);
+ //Logs
+ pc_delitem(target_sd, n, target_sd->deal.item[trade_i].amount, 1);
+ } else {
+ clif_additem(target_sd, n, target_sd->deal.item[trade_i].amount, 0);
+ }
+ target_sd->deal.item[trade_i].index = 0;
+ target_sd->deal.item[trade_i].amount = 0;
+ }
+ }
+ if (sd->deal.zeny) {
+ //Logs Zeny (T)rade [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(target_sd, "T", sd, sd->deal.zeny);
+ //Logs
+ sd->status.zeny -= sd->deal.zeny;
+ target_sd->status.zeny += sd->deal.zeny;
+ }
+ if (target_sd->deal.zeny) {
+ //Logs Zeny (T)rade [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "T", target_sd, target_sd->deal.zeny);
+ //Logs
+ target_sd->status.zeny -= target_sd->deal.zeny;
+ sd->status.zeny += target_sd->deal.zeny;
+ }
+ if (sd->deal.zeny || target_sd->deal.zeny) {
+ clif_updatestatus(sd, SP_ZENY);
+ sd->deal.zeny = 0;
+ clif_updatestatus(target_sd, SP_ZENY);
+ target_sd->deal.zeny = 0;
+ }
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ clif_tradecompleted(sd, 0);
+ clif_tradecompleted(target_sd, 0);
+ // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players
+ chrif_save(sd,0); // do pc_makesavestatus and save storage too
+ chrif_save(target_sd,0); // do pc_makesavestatus and save storage too
+ // zeny value was modified!!!! hacker with packet modified
+ } else {
+ trade_tradecancel(sd);
+ }
+ }
+ }
+ }
+}
diff --git a/src/map/trade.h b/src/map/trade.h
new file mode 100644
index 000000000..2114f5a42
--- /dev/null
+++ b/src/map/trade.h
@@ -0,0 +1,15 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _TRADE_H_
+#define _TRADE_H_
+
+#include "map.h"
+void trade_traderequest(struct map_session_data *sd,int target_id);
+void trade_tradeack(struct map_session_data *sd,int type);
+void trade_tradeadditem(struct map_session_data *sd,int index,int amount);
+void trade_tradeok(struct map_session_data *sd);
+void trade_tradecancel(struct map_session_data *sd);
+void trade_tradecommit(struct map_session_data *sd);
+
+#endif // _TRADE_H_
diff --git a/src/map/vending.c b/src/map/vending.c
new file mode 100644
index 000000000..53fa281f3
--- /dev/null
+++ b/src/map/vending.c
@@ -0,0 +1,261 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "atcommand.h"
+#include "map.h"
+#include "chrif.h"
+#include "vending.h"
+#include "pc.h"
+#include "skill.h"
+#include "battle.h"
+#include "log.h"
+
+/*==========================================
+ * ˜I“X•Â½
+ *------------------------------------------
+*/
+void vending_closevending(struct map_session_data *sd)
+{
+
+ nullpo_retv(sd);
+
+ sd->vender_id=0;
+ clif_closevendingboard(&sd->bl,0);
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€ƒŠƒXƒg—v‹
+ *------------------------------------------
+ */
+void vending_vendinglistreq(struct map_session_data *sd,int id)
+{
+ struct map_session_data *vsd;
+
+ nullpo_retv(sd);
+
+ if( (vsd=map_id2sd(id)) == NULL )
+ return;
+ if(vsd->vender_id==0)
+ return;
+ clif_vendinglist(sd,id,vsd->vending);
+}
+
+/*==========================================
+ * ˜I“XƒAƒCƒeƒ€w“ü
+ *------------------------------------------
+ */
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p)
+{
+ int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING];
+ double z;
+ unsigned short amount;
+ short idx;
+ struct map_session_data *vsd = map_id2sd(id);
+ struct vending vending[MAX_VENDING]; // against duplicate packets
+
+ nullpo_retv(sd);
+
+ if (vsd == NULL)
+ return;
+ if (vsd->vender_id == 0)
+ return;
+ if (vsd->vender_id == sd->bl.id)
+ return;
+
+ // check number of buying items
+ if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) {
+ clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown)
+ return;
+ }
+
+ blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
+
+ // duplicate item in vending to check hacker with multiple packets
+ memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list
+
+ // some checks
+ z = 0.;
+ w = 0;
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(unsigned short*)(p + 4 * i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+
+ if (amount <= 0)
+ return;
+
+ // check of item index in the cart
+ if (idx < 0 || idx >= MAX_CART)
+ return;
+
+ for(j = 0; j < vsd->vend_num; j++) {
+ if (vsd->vending[j].index == idx) {
+ vend_list[i] = j;
+ break;
+ }
+ }
+ if (j == vsd->vend_num)
+ return; //picked non-existing item
+
+ z += ((double)vsd->vending[j].value * (double)amount);
+ if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer)
+ clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
+ return; // zenys'<
+ }
+ if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
+ return; // zenys'<
+ }
+ w += itemdb_weight(vsd->status.cart[idx].nameid) * amount;
+ if (w + sd->weight > sd->max_weight) {
+ clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
+ return;
+ }
+
+ if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync.
+ vending[j].amount = vsd->status.cart[idx].amount;
+
+ // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
+ // here, we check cumulativ amounts
+ if (vending[j].amount < amount) {
+ // send more quantity is not a hack (an other player can have buy items just before)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
+ return;
+ } else
+ vending[j].amount -= amount;
+
+ switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) {
+ case ADDITEM_EXIST:
+ break; //We'd add this item to the existing one (in buyers inventory)
+ case ADDITEM_NEW:
+ new_++;
+ if (new_ > blank)
+ return; //Buyer has no space in his inventory
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return; //too many items
+ }
+ }
+
+ //Logs (V)ending Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(vsd, "V", sd, (int)z);
+ //Logs
+
+ pc_payzeny(sd, (int)z);
+ pc_getzeny(vsd, (int)z);
+
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(short*)(p + 4 *i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+ //if (amount < 0) break; // tested at start of the function
+
+ //Logs sold (V)ending items [Lupus]
+ if(log_config.pick > 0 ) {
+ log_pick(vsd, "V", 0, vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]);
+ log_pick( sd, "V", 0, vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]);
+ }
+ //Logs
+
+ //Old VENDING log added by Lupus
+ if(log_config.vend > 0) {
+ log_vend(sd,vsd, idx, amount, (int)z); // for Item + Zeny. log.
+ //we log ZENY only with the 1st item. Then zero it for the rest items
+ z = 0;
+ }
+
+ // vending item
+ pc_additem(sd, &vsd->status.cart[idx], amount);
+ vsd->vending[vend_list[i]].amount -= amount;
+ pc_cart_delitem(vsd, idx, amount, 0);
+ clif_vendingreport(vsd, idx, amount);
+
+ //print buyer's name
+ if(battle_config.buyer_name) {
+ char temp[256];
+ sprintf(temp, msg_txt(265), sd->status.name);
+ clif_disp_onlyself(vsd,temp,strlen(temp));
+ }
+ }
+
+ //Always save BOTH: buyer and customer
+ chrif_save(sd,0);
+ chrif_save(vsd,0);
+ //check for @AUTOTRADE users [durf]
+ if (vsd->state.autotrade)
+ {
+ //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
+ for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++);
+ if (i == vsd->vend_num)
+ {
+ vending_closevending(vsd);
+ map_quit(vsd); //They have no reason to stay around anymore, do they?
+ }
+ }
+}
+
+/*==========================================
+ * ˜I“XŠJÝ
+ *------------------------------------------
+ */
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p)
+{
+ int i, j;
+ int vending_skill_lvl;
+ nullpo_retv(sd);
+
+ //check shopname len
+ if(message[0] == '\0')
+ return;
+
+ vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
+ if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris]
+ clif_skill_fail(sd,MC_VENDING,0,0);
+ return;
+ }
+
+ if (flag) {
+ // check number of items in shop
+ if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) {
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) {
+ sd->vending[i].index = *(short*)(p+8*j)-2;
+ if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART ||
+ !itemdb_cantrade(sd->status.cart[sd->vending[i].index].nameid, pc_isGM(sd), pc_isGM(sd)))
+ {
+ i--; //Preserve the vending index, skip to the next item.
+ continue;
+ }
+ sd->vending[i].amount = *(short*)(p+2+8*j);
+ sd->vending[i].value = *(int*)(p+4+8*j);
+ if(sd->vending[i].value > battle_config.vending_max_value)
+ sd->vending[i].value=battle_config.vending_max_value;
+ else if(sd->vending[i].value < 1)
+ sd->vending[i].value = 1000000; // auto set to 1 million [celest]
+ // ƒJ[ƒg“à‚̃AƒCƒeƒ€”‚Ɣ̔„‚·‚éƒAƒCƒeƒ€”‚É‘Šˆá‚ª‚ ‚Á‚½‚ç’†Ž~
+ if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0 || sd->vending[i].value < 0) { // fixes by Valaris and fritz
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ }
+ if (i != j)
+ { //Some items were not vended. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(266));
+ }
+ sd->vender_id = sd->bl.id;
+ sd->vend_num = i;
+ memcpy(sd->message,message, MESSAGE_SIZE-1);
+ if (clif_openvending(sd,sd->vender_id,sd->vending) > 0)
+ clif_showvendingboard(&sd->bl,message,0);
+ else
+ sd->vender_id = 0;
+ }
+}
+
diff --git a/src/map/vending.h b/src/map/vending.h
new file mode 100644
index 000000000..021866d25
--- /dev/null
+++ b/src/map/vending.h
@@ -0,0 +1,14 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _VENDING_H_
+#define _VENDING_H_
+
+#include "map.h"
+
+void vending_closevending(struct map_session_data *sd);
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p);
+void vending_vendinglistreq(struct map_session_data *sd,int id);
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p);
+
+#endif // _VENDING_H_
diff --git a/src/mysql/config-win.h b/src/mysql/config-win.h
new file mode 100644
index 000000000..cbc81e580
--- /dev/null
+++ b/src/mysql/config-win.h
@@ -0,0 +1,434 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Defines for Win32 to make it compatible for MySQL */
+
+#ifdef __WIN2000__
+/* We have to do this define before including windows.h to get the AWE API
+functions */
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <sys/locking.h>
+#include <windows.h>
+#include <math.h> /* Because of rint() */
+#include <fcntl.h>
+#include <io.h>
+#include <malloc.h>
+
+#define HAVE_SMEM 1
+
+#if defined(_WIN64) || defined(WIN64)
+#define SYSTEM_TYPE "Win64"
+#elif defined(_WIN32) || defined(WIN32)
+#define SYSTEM_TYPE "Win32"
+#else
+#define SYSTEM_TYPE "Windows"
+#endif
+
+#if defined(_M_IA64)
+#define MACHINE_TYPE "ia64"
+#elif defined(_M_IX86)
+#define MACHINE_TYPE "ia32"
+#elif defined(_M_ALPHA)
+#define MACHINE_TYPE "axp"
+#else
+#define MACHINE_TYPE "unknown" /* Define to machine type name */
+#endif
+
+#if !(defined(_WIN64) || defined(WIN64))
+#ifndef _WIN32
+#define _WIN32 /* Compatible with old source */
+#endif
+#ifndef __WIN32__
+#define __WIN32__
+#endif
+#endif /* _WIN64 */
+#ifndef __WIN__
+#define __WIN__ /* To make it easier in VC++ */
+#endif
+
+/* File and lock constants */
+#define O_SHARE 0x1000 /* Open file in sharing mode */
+#ifdef __BORLANDC__
+#define F_RDLCK LK_NBLCK /* read lock */
+#define F_WRLCK LK_NBRLCK /* write lock */
+#define F_UNLCK LK_UNLCK /* remove lock(s) */
+#else
+#define F_RDLCK _LK_NBLCK /* read lock */
+#define F_WRLCK _LK_NBRLCK /* write lock */
+#define F_UNLCK _LK_UNLCK /* remove lock(s) */
+#endif
+
+#define F_EXCLUSIVE 1 /* We have only exclusive locking */
+#define F_TO_EOF (INT_MAX32/2) /* size for lock of all file */
+#define F_OK 0 /* parameter to access() */
+#define W_OK 2
+
+#define S_IROTH S_IREAD /* for my_lib */
+
+#ifdef __BORLANDC__
+#define FILE_BINARY O_BINARY /* my_fopen in binary mode */
+#define O_TEMPORARY 0
+#define O_SHORT_LIVED 0
+#define SH_DENYNO _SH_DENYNO
+#else
+#define O_BINARY _O_BINARY /* compability with MSDOS */
+#define FILE_BINARY _O_BINARY /* my_fopen in binary mode */
+#define O_TEMPORARY _O_TEMPORARY
+#define O_SHORT_LIVED _O_SHORT_LIVED
+#define SH_DENYNO _SH_DENYNO
+#endif
+#define NO_OPEN_3 /* For my_create() */
+
+#define SIGQUIT SIGTERM /* No SIGQUIT */
+
+#undef _REENTRANT /* Crashes something for win32 */
+#undef SAFE_MUTEX /* Can't be used on windows */
+
+#define LONGLONG_MIN ((__int64) 0x8000000000000000)
+#define LONGLONG_MAX ((__int64) 0x7FFFFFFFFFFFFFFF)
+#define ULONGLONG_MAX ((unsigned __int64) 0xFFFFFFFFFFFFFFFF)
+#define LL(A) ((__int64) A)
+#define ULL(A) ((unsigned __int64) A)
+
+/* Type information */
+
+#if defined(__EMX__) || !defined(HAVE_UINT)
+#undef HAVE_UINT
+#define HAVE_UINT
+typedef unsigned short ushort;
+typedef unsigned int uint;
+#endif /* defined(__EMX__) || !defined(HAVE_UINT) */
+
+typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */
+typedef __int64 longlong;
+#ifndef HAVE_SIGSET_T
+typedef int sigset_t;
+#endif
+#define longlong_defined
+/*
+ off_t should not be __int64 because of conflicts in header files;
+ Use my_off_t or os_off_t instead
+*/
+#ifndef HAVE_OFF_T
+typedef long off_t;
+#endif
+typedef __int64 os_off_t;
+#ifdef _WIN64
+typedef UINT_PTR rf_SetTimer;
+#else
+#ifndef HAVE_SIZE_T
+typedef unsigned int size_t;
+#endif
+typedef uint rf_SetTimer;
+#endif
+
+#define Socket_defined
+#define my_socket SOCKET
+#define bool BOOL
+#define SIGPIPE SIGINT
+#define RETQSORTTYPE void
+#define QSORT_TYPE_IS_VOID
+#define RETSIGTYPE void
+#define SOCKET_SIZE_TYPE int
+#define my_socket_defined
+#define bool_defined
+#define byte_defined
+#define HUGE_PTR
+#define STDCALL __stdcall /* Used by libmysql.dll */
+#define isnan(X) _isnan(X)
+#define finite(X) _finite(X)
+
+#ifndef UNDEF_THREAD_HACK
+#define THREAD
+#endif
+#define VOID_SIGHANDLER
+#define SIZEOF_CHAR 1
+#define SIZEOF_LONG 4
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_OFF_T 8
+#ifdef _WIN64
+#define SIZEOF_CHARP 8
+#else
+#define SIZEOF_CHARP 4
+#endif
+#define HAVE_BROKEN_NETINET_INCLUDES
+#ifdef __NT__
+#define HAVE_NAMED_PIPE /* We can only create pipes on NT */
+#endif
+
+/* ERROR is defined in wingdi.h */
+#undef ERROR
+
+/* We need to close files to break connections on shutdown */
+#ifndef SIGNAL_WITH_VIO_CLOSE
+#define SIGNAL_WITH_VIO_CLOSE
+#endif
+
+/* Use all character sets in MySQL */
+#define USE_MB 1
+#define USE_MB_IDENT 1
+#define USE_STRCOLL 1
+
+/* All windows servers should support .sym files */
+#undef USE_SYMDIR
+#define USE_SYMDIR
+
+/* If LOAD DATA LOCAL INFILE should be enabled by default */
+#define ENABLED_LOCAL_INFILE 1
+
+/* Convert some simple functions to Posix */
+
+#define my_sigset(A,B) signal((A),(B))
+#define finite(A) _finite(A)
+#define sleep(A) Sleep((A)*1000)
+#define popen(A) popen(A,B) _popen((A),(B))
+#define pclose(A) _pclose(A)
+
+#ifndef __BORLANDC__
+#define access(A,B) _access(A,B)
+#endif
+
+#if !defined(__cplusplus)
+#define inline __inline
+#endif /* __cplusplus */
+
+inline double rint(double nr)
+{
+ double f = floor(nr);
+ double c = ceil(nr);
+ return (((c-nr) >= (nr-f)) ? f :c);
+}
+
+#ifdef _WIN64
+#define ulonglong2double(A) ((double) (ulonglong) (A))
+#define my_off_t2double(A) ((double) (my_off_t) (A))
+
+#else
+inline double ulonglong2double(ulonglong value)
+{
+ longlong nr=(longlong) value;
+ if (nr >= 0)
+ return (double) nr;
+ return (18446744073709551616.0 + (double) nr);
+}
+#define my_off_t2double(A) ulonglong2double(A)
+#endif /* _WIN64 */
+
+#if SIZEOF_OFF_T > 4
+#define lseek(A,B,C) _lseeki64((A),(longlong) (B),(C))
+#define tell(A) _telli64(A)
+#endif
+
+#define set_timespec(ABSTIME,SEC) { (ABSTIME).tv_sec=time((time_t*)0) + (time_t) (SEC); (ABSTIME).tv_nsec=0; }
+
+#define STACK_DIRECTION -1
+
+/* Optimized store functions for Intel x86 */
+
+#ifndef _WIN64
+#define sint2korr(A) (*((int16 *) (A)))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (*((long *) (A)))
+#define uint2korr(A) (*((uint16 *) (A)))
+/*
+ ATTENTION !
+
+ Please, note, uint3korr reads 4 bytes (not 3) !
+ It means, that you have to provide enough allocated space !
+*/
+#define uint3korr(A) (long) (*((unsigned int *) (A)) & 0xFFFFFF)
+#define uint4korr(A) (*((unsigned long *) (A)))
+#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 32))
+#define uint8korr(A) (*((ulonglong *) (A)))
+#define sint8korr(A) (*((longlong *) (A)))
+#define int2store(T,A) *((uint16*) (T))= (uint16) (A)
+#define int3store(T,A) { *(T)= (uchar) ((A));\
+ *(T+1)=(uchar) (((uint) (A) >> 8));\
+ *(T+2)=(uchar) (((A) >> 16)); }
+#define int4store(T,A) *((long *) (T))= (long) (A)
+#define int5store(T,A) { *(T)= (uchar)((A));\
+ *((T)+1)=(uchar) (((A) >> 8));\
+ *((T)+2)=(uchar) (((A) >> 16));\
+ *((T)+3)=(uchar) (((A) >> 24)); \
+ *((T)+4)=(uchar) (((A) >> 32)); }
+#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A)
+
+#define doubleget(V,M) { *((long *) &V) = *((long*) M); \
+ *(((long *) &V)+1) = *(((long*) M)+1); }
+#define doublestore(T,V) { *((long *) T) = *((long*) &V); \
+ *(((long *) T)+1) = *(((long*) &V)+1); }
+#define float4get(V,M) { *((long *) &(V)) = *((long*) (M)); }
+#define floatstore(T,V) memcpy((byte*)(T), (byte*)(&V), sizeof(float))
+#define floatget(V,M) memcpy((byte*)(&V), (byte*)(M), sizeof(float))
+#define float8get(V,M) doubleget((V),(M))
+#define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float))
+#define float8store(V,M) doublestore((V),(M))
+#endif /* _WIN64 */
+
+#define HAVE_PERROR
+#define HAVE_VFPRINT
+#define HAVE_RENAME /* Have rename() as function */
+#define HAVE_BINARY_STREAMS /* Have "b" flag in streams */
+#define HAVE_LONG_JMP /* Have long jump function */
+#define HAVE_LOCKING /* have locking() call */
+#define HAVE_ERRNO_AS_DEFINE /* errno is a define */
+#define HAVE_STDLIB /* everything is include in this file */
+#define HAVE_MEMCPY
+#define HAVE_MEMMOVE
+#define HAVE_GETCWD
+#define HAVE_TELL
+#define HAVE_TZNAME
+#define HAVE_PUTENV
+#define HAVE_SELECT
+#define HAVE_SETLOCALE
+#define HAVE_SOCKET /* Giangi */
+#define HAVE_FLOAT_H
+#define HAVE_LIMITS_H
+#define HAVE_STDDEF_H
+#define HAVE_RINT /* defined in this file */
+#define NO_FCNTL_NONBLOCK /* No FCNTL */
+#define HAVE_ALLOCA
+#define HAVE_STRPBRK
+#define HAVE_STRSTR
+#define HAVE_COMPRESS
+#define HAVE_CREATESEMAPHORE
+#define HAVE_ISNAN
+#define HAVE_FINITE
+#define HAVE_QUERY_CACHE
+#define SPRINTF_RETURNS_INT
+#define HAVE_SETFILEPOINTER
+#define HAVE_VIO_READ_BUFF
+
+#ifdef NOT_USED
+#define HAVE_SNPRINTF /* Gave link error */
+#define _snprintf snprintf
+#endif
+
+#ifdef _MSC_VER
+#define HAVE_LDIV /* The optimizer breaks in zortech for ldiv */
+#define HAVE_ANSI_INCLUDE
+#define HAVE_SYS_UTIME_H
+#define HAVE_STRTOUL
+#endif
+#define my_reinterpret_cast(A) reinterpret_cast <A>
+#define my_const_cast(A) const_cast<A>
+
+
+/* MYSQL OPTIONS */
+
+#ifdef _CUSTOMCONFIG_
+#include <custom_conf.h>
+#else
+#define DEFAULT_MYSQL_HOME "c:\\mysql"
+#define PACKAGE "mysql"
+#define DEFAULT_BASEDIR "C:\\"
+#define SHAREDIR "share"
+#define DEFAULT_CHARSET_HOME "C:/mysql/"
+#endif
+#ifndef DEFAULT_HOME_ENV
+#define DEFAULT_HOME_ENV MYSQL_HOME
+#endif
+#ifndef DEFAULT_GROUP_SUFFIX_ENV
+#define DEFAULT_GROUP_SUFFIX_ENV MYSQL_GROUP_SUFFIX
+#endif
+
+/* File name handling */
+
+#define FN_LIBCHAR '\\'
+#define FN_ROOTDIR "\\"
+#define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */
+#define FN_NO_CASE_SENCE /* Files are not case-sensitive */
+#define OS_FILE_LIMIT 2048
+
+#define DO_NOT_REMOVE_THREAD_WRAPPERS
+#define thread_safe_increment(V,L) InterlockedIncrement((long*) &(V))
+#define thread_safe_decrement(V,L) InterlockedDecrement((long*) &(V))
+/* The following is only used for statistics, so it should be good enough */
+#ifdef __NT__ /* This should also work on Win98 but .. */
+#define thread_safe_add(V,C,L) InterlockedExchangeAdd((long*) &(V),(C))
+#define thread_safe_sub(V,C,L) InterlockedExchangeAdd((long*) &(V),-(long) (C))
+#define statistic_add(V,C,L) thread_safe_add((V),(C),(L))
+#else
+#define thread_safe_add(V,C,L) \
+ pthread_mutex_lock((L)); (V)+=(C); pthread_mutex_unlock((L));
+#define thread_safe_sub(V,C,L) \
+ pthread_mutex_lock((L)); (V)-=(C); pthread_mutex_unlock((L));
+#define statistic_add(V,C,L) (V)+=(C)
+#endif
+#define statistic_increment(V,L) thread_safe_increment((V),(L))
+#define statistic_decrement(V,L) thread_safe_decrement((V),(L))
+
+#define shared_memory_buffer_length 16000
+#define default_shared_memory_base_name "MYSQL"
+#define MYSQL_DEFAULT_CHARSET_NAME "latin1"
+#define MYSQL_DEFAULT_COLLATION_NAME "latin1_swedish_ci"
+
+#define HAVE_SPATIAL 1
+#define HAVE_RTREE_KEYS 1
+
+/* #undef HAVE_OPENSSL */
+/* #undef HAVE_YASSL */
+
+/* Define charsets you want */
+/* #undef HAVE_CHARSET_armscii8 */
+/* #undef HAVE_CHARSET_ascii */
+#define HAVE_CHARSET_big5 1
+#define HAVE_CHARSET_cp1250 1
+/* #undef HAVE_CHARSET_cp1251 */
+/* #undef HAVE_CHARSET_cp1256 */
+/* #undef HAVE_CHARSET_cp1257 */
+/* #undef HAVE_CHARSET_cp850 */
+/* #undef HAVE_CHARSET_cp852 */
+/* #undef HAVE_CHARSET_cp866 */
+#define HAVE_CHARSET_cp932 1
+/* #undef HAVE_CHARSET_dec8 */
+#define HAVE_CHARSET_eucjpms 1
+#define HAVE_CHARSET_euckr 1
+#define HAVE_CHARSET_gb2312 1
+#define HAVE_CHARSET_gbk 1
+/* #undef HAVE_CHARSET_greek */
+/* #undef HAVE_CHARSET_hebrew */
+/* #undef HAVE_CHARSET_hp8 */
+/* #undef HAVE_CHARSET_keybcs2 */
+/* #undef HAVE_CHARSET_koi8r */
+/* #undef HAVE_CHARSET_koi8u */
+#define HAVE_CHARSET_latin1 1
+#define HAVE_CHARSET_latin2 1
+/* #undef HAVE_CHARSET_latin5 */
+/* #undef HAVE_CHARSET_latin7 */
+/* #undef HAVE_CHARSET_macce */
+/* #undef HAVE_CHARSET_macroman */
+#define HAVE_CHARSET_sjis 1
+/* #undef HAVE_CHARSET_swe7 */
+#define HAVE_CHARSET_tis620 1
+#define HAVE_CHARSET_ucs2 1
+#define HAVE_CHARSET_ujis 1
+#define HAVE_CHARSET_utf8 1
+#define HAVE_UCA_COLLATIONS 1
+
diff --git a/src/mysql/m_ctype.h b/src/mysql/m_ctype.h
new file mode 100644
index 000000000..87a35f737
--- /dev/null
+++ b/src/mysql/m_ctype.h
@@ -0,0 +1,478 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ A better inplementation of the UNIX ctype(3) library.
+ Notes: my_global.h should be included before ctype.h
+*/
+
+#ifndef _m_ctype_h
+#define _m_ctype_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MY_CS_NAME_SIZE 32
+#define MY_CS_CTYPE_TABLE_SIZE 257
+#define MY_CS_TO_LOWER_TABLE_SIZE 256
+#define MY_CS_TO_UPPER_TABLE_SIZE 256
+#define MY_CS_SORT_ORDER_TABLE_SIZE 256
+#define MY_CS_TO_UNI_TABLE_SIZE 256
+
+#define CHARSET_DIR "charsets/"
+
+#define my_wc_t ulong
+
+typedef struct unicase_info_st
+{
+ uint16 toupper;
+ uint16 tolower;
+ uint16 sort;
+} MY_UNICASE_INFO;
+
+extern MY_UNICASE_INFO *my_unicase_default[256];
+extern MY_UNICASE_INFO *my_unicase_turkish[256];
+
+#define MY_CS_ILSEQ 0
+#define MY_CS_ILUNI 0
+#define MY_CS_TOOSMALL -1
+#define MY_CS_TOOFEW(n) (-1-(n))
+
+#define MY_SEQ_INTTAIL 1
+#define MY_SEQ_SPACES 2
+
+ /* My charsets_list flags */
+#define MY_CS_COMPILED 1 /* compiled-in sets */
+#define MY_CS_CONFIG 2 /* sets that have a *.conf file */
+#define MY_CS_INDEX 4 /* sets listed in the Index file */
+#define MY_CS_LOADED 8 /* sets that are currently loaded */
+#define MY_CS_BINSORT 16 /* if binary sort order */
+#define MY_CS_PRIMARY 32 /* if primary collation */
+#define MY_CS_STRNXFRM 64 /* if strnxfrm is used for sort */
+#define MY_CS_UNICODE 128 /* is a charset is full unicode */
+#define MY_CS_READY 256 /* if a charset is initialized */
+#define MY_CS_AVAILABLE 512 /* If either compiled-in or loaded*/
+#define MY_CS_CSSORT 1024 /* if case sensitive sort order */
+#define MY_CHARSET_UNDEFINED 0
+
+
+typedef struct my_uni_idx_st
+{
+ uint16 from;
+ uint16 to;
+ uchar *tab;
+} MY_UNI_IDX;
+
+typedef struct
+{
+ uint beg;
+ uint end;
+ uint mblen;
+} my_match_t;
+
+enum my_lex_states
+{
+ MY_LEX_START, MY_LEX_CHAR, MY_LEX_IDENT,
+ MY_LEX_IDENT_SEP, MY_LEX_IDENT_START,
+ MY_LEX_REAL, MY_LEX_HEX_NUMBER, MY_LEX_BIN_NUMBER,
+ MY_LEX_CMP_OP, MY_LEX_LONG_CMP_OP, MY_LEX_STRING, MY_LEX_COMMENT, MY_LEX_END,
+ MY_LEX_OPERATOR_OR_IDENT, MY_LEX_NUMBER_IDENT, MY_LEX_INT_OR_REAL,
+ MY_LEX_REAL_OR_POINT, MY_LEX_BOOL, MY_LEX_EOL, MY_LEX_ESCAPE,
+ MY_LEX_LONG_COMMENT, MY_LEX_END_LONG_COMMENT, MY_LEX_SEMICOLON,
+ MY_LEX_SET_VAR, MY_LEX_USER_END, MY_LEX_HOSTNAME, MY_LEX_SKIP,
+ MY_LEX_USER_VARIABLE_DELIMITER, MY_LEX_SYSTEM_VAR,
+ MY_LEX_IDENT_OR_KEYWORD,
+ MY_LEX_IDENT_OR_HEX, MY_LEX_IDENT_OR_BIN, MY_LEX_IDENT_OR_NCHAR,
+ MY_LEX_STRING_OR_DELIMITER
+};
+
+struct charset_info_st;
+
+typedef struct my_collation_handler_st
+{
+ my_bool (*init)(struct charset_info_st *, void *(*alloc)(uint));
+ /* Collation routines */
+ int (*strnncoll)(struct charset_info_st *,
+ const uchar *, uint, const uchar *, uint, my_bool);
+ int (*strnncollsp)(struct charset_info_st *,
+ const uchar *, uint, const uchar *, uint,
+ my_bool diff_if_only_endspace_difference);
+ int (*strnxfrm)(struct charset_info_st *,
+ uchar *, uint, const uchar *, uint);
+ uint (*strnxfrmlen)(struct charset_info_st *, uint);
+ my_bool (*like_range)(struct charset_info_st *,
+ const char *s, uint s_length,
+ pchar w_prefix, pchar w_one, pchar w_many,
+ uint res_length,
+ char *min_str, char *max_str,
+ uint *min_len, uint *max_len);
+ int (*wildcmp)(struct charset_info_st *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape,int w_one, int w_many);
+
+ int (*strcasecmp)(struct charset_info_st *, const char *, const char *);
+
+ uint (*instr)(struct charset_info_st *,
+ const char *b, uint b_length,
+ const char *s, uint s_length,
+ my_match_t *match, uint nmatch);
+
+ /* Hash calculation */
+ void (*hash_sort)(struct charset_info_st *cs, const uchar *key, uint len,
+ ulong *nr1, ulong *nr2);
+ my_bool (*propagate)(struct charset_info_st *cs, const uchar *str, uint len);
+} MY_COLLATION_HANDLER;
+
+extern MY_COLLATION_HANDLER my_collation_mb_bin_handler;
+extern MY_COLLATION_HANDLER my_collation_8bit_bin_handler;
+extern MY_COLLATION_HANDLER my_collation_8bit_simple_ci_handler;
+extern MY_COLLATION_HANDLER my_collation_ucs2_uca_handler;
+
+
+typedef struct my_charset_handler_st
+{
+ my_bool (*init)(struct charset_info_st *, void *(*alloc)(uint));
+ /* Multibyte routines */
+ int (*ismbchar)(struct charset_info_st *, const char *, const char *);
+ int (*mbcharlen)(struct charset_info_st *, uint);
+ uint (*numchars)(struct charset_info_st *, const char *b, const char *e);
+ uint (*charpos)(struct charset_info_st *, const char *b, const char *e, uint pos);
+ uint (*well_formed_len)(struct charset_info_st *,
+ const char *b,const char *e,
+ uint nchars, int *error);
+ uint (*lengthsp)(struct charset_info_st *, const char *ptr, uint length);
+ uint (*numcells)(struct charset_info_st *, const char *b, const char *e);
+
+ /* Unicode convertion */
+ int (*mb_wc)(struct charset_info_st *cs,my_wc_t *wc,
+ const unsigned char *s,const unsigned char *e);
+ int (*wc_mb)(struct charset_info_st *cs,my_wc_t wc,
+ unsigned char *s,unsigned char *e);
+
+ /* Functions for case and sort convertion */
+ void (*caseup_str)(struct charset_info_st *, char *);
+ void (*casedn_str)(struct charset_info_st *, char *);
+ uint (*caseup)(struct charset_info_st *, char *src, uint srclen,
+ char *dst, uint dstlen);
+ uint (*casedn)(struct charset_info_st *, char *src, uint srclen,
+ char *dst, uint dstlen);
+
+ /* Charset dependant snprintf() */
+ int (*snprintf)(struct charset_info_st *, char *to, uint n, const char *fmt,
+ ...);
+ int (*long10_to_str)(struct charset_info_st *, char *to, uint n, int radix,
+ long int val);
+ int (*longlong10_to_str)(struct charset_info_st *, char *to, uint n,
+ int radix, longlong val);
+
+ void (*fill)(struct charset_info_st *, char *to, uint len, int fill);
+
+ /* String-to-number convertion routines */
+ long (*strntol)(struct charset_info_st *, const char *s, uint l,
+ int base, char **e, int *err);
+ ulong (*strntoul)(struct charset_info_st *, const char *s, uint l,
+ int base, char **e, int *err);
+ longlong (*strntoll)(struct charset_info_st *, const char *s, uint l,
+ int base, char **e, int *err);
+ ulonglong (*strntoull)(struct charset_info_st *, const char *s, uint l,
+ int base, char **e, int *err);
+ double (*strntod)(struct charset_info_st *, char *s, uint l, char **e,
+ int *err);
+ longlong (*strtoll10)(struct charset_info_st *cs,
+ const char *nptr, char **endptr, int *error);
+ ulong (*scan)(struct charset_info_st *, const char *b, const char *e,
+ int sq);
+} MY_CHARSET_HANDLER;
+
+extern MY_CHARSET_HANDLER my_charset_8bit_handler;
+extern MY_CHARSET_HANDLER my_charset_ucs2_handler;
+
+
+typedef struct charset_info_st
+{
+ uint number;
+ uint primary_number;
+ uint binary_number;
+ uint state;
+ const char *csname;
+ const char *name;
+ const char *comment;
+ const char *tailoring;
+ uchar *ctype;
+ uchar *to_lower;
+ uchar *to_upper;
+ uchar *sort_order;
+ uint16 *contractions;
+ uint16 **sort_order_big;
+ uint16 *tab_to_uni;
+ MY_UNI_IDX *tab_from_uni;
+ MY_UNICASE_INFO **caseinfo;
+ uchar *state_map;
+ uchar *ident_map;
+ uint strxfrm_multiply;
+ uchar caseup_multiply;
+ uchar casedn_multiply;
+ uint mbminlen;
+ uint mbmaxlen;
+ uint16 min_sort_char;
+ uint16 max_sort_char; /* For LIKE optimization */
+ uchar pad_char;
+ my_bool escape_with_backslash_is_dangerous;
+
+ MY_CHARSET_HANDLER *cset;
+ MY_COLLATION_HANDLER *coll;
+
+} CHARSET_INFO;
+
+
+extern CHARSET_INFO my_charset_bin;
+extern CHARSET_INFO my_charset_big5_chinese_ci;
+extern CHARSET_INFO my_charset_big5_bin;
+extern CHARSET_INFO my_charset_cp932_japanese_ci;
+extern CHARSET_INFO my_charset_cp932_bin;
+extern CHARSET_INFO my_charset_eucjpms_japanese_ci;
+extern CHARSET_INFO my_charset_eucjpms_bin;
+extern CHARSET_INFO my_charset_euckr_korean_ci;
+extern CHARSET_INFO my_charset_euckr_bin;
+extern CHARSET_INFO my_charset_gb2312_chinese_ci;
+extern CHARSET_INFO my_charset_gb2312_bin;
+extern CHARSET_INFO my_charset_gbk_chinese_ci;
+extern CHARSET_INFO my_charset_gbk_bin;
+extern CHARSET_INFO my_charset_latin1;
+extern CHARSET_INFO my_charset_latin1_german2_ci;
+extern CHARSET_INFO my_charset_latin1_bin;
+extern CHARSET_INFO my_charset_latin2_czech_ci;
+extern CHARSET_INFO my_charset_sjis_japanese_ci;
+extern CHARSET_INFO my_charset_sjis_bin;
+extern CHARSET_INFO my_charset_tis620_thai_ci;
+extern CHARSET_INFO my_charset_tis620_bin;
+extern CHARSET_INFO my_charset_ucs2_general_ci;
+extern CHARSET_INFO my_charset_ucs2_bin;
+extern CHARSET_INFO my_charset_ucs2_general_uca;
+extern CHARSET_INFO my_charset_ujis_japanese_ci;
+extern CHARSET_INFO my_charset_ujis_bin;
+extern CHARSET_INFO my_charset_utf8_general_ci;
+extern CHARSET_INFO my_charset_utf8_bin;
+extern CHARSET_INFO my_charset_cp1250_czech_ci;
+
+/* declarations for simple charsets */
+extern int my_strnxfrm_simple(CHARSET_INFO *, uchar *, uint, const uchar *,
+ uint);
+uint my_strnxfrmlen_simple(CHARSET_INFO *, uint);
+extern int my_strnncoll_simple(CHARSET_INFO *, const uchar *, uint,
+ const uchar *, uint, my_bool);
+
+extern int my_strnncollsp_simple(CHARSET_INFO *, const uchar *, uint,
+ const uchar *, uint,
+ my_bool diff_if_only_endspace_difference);
+
+extern void my_hash_sort_simple(CHARSET_INFO *cs,
+ const uchar *key, uint len,
+ ulong *nr1, ulong *nr2);
+
+extern uint my_lengthsp_8bit(CHARSET_INFO *cs, const char *ptr, uint length);
+
+extern uint my_instr_simple(struct charset_info_st *,
+ const char *b, uint b_length,
+ const char *s, uint s_length,
+ my_match_t *match, uint nmatch);
+
+
+/* Functions for 8bit */
+extern void my_caseup_str_8bit(CHARSET_INFO *, char *);
+extern void my_casedn_str_8bit(CHARSET_INFO *, char *);
+extern uint my_caseup_8bit(CHARSET_INFO *, char *src, uint srclen,
+ char *dst, uint dstlen);
+extern uint my_casedn_8bit(CHARSET_INFO *, char *src, uint srclen,
+ char *dst, uint dstlen);
+
+extern int my_strcasecmp_8bit(CHARSET_INFO * cs, const char *, const char *);
+
+int my_mb_wc_8bit(CHARSET_INFO *cs,my_wc_t *wc, const uchar *s,const uchar *e);
+int my_wc_mb_8bit(CHARSET_INFO *cs,my_wc_t wc, uchar *s, uchar *e);
+
+ulong my_scan_8bit(CHARSET_INFO *cs, const char *b, const char *e, int sq);
+
+int my_snprintf_8bit(struct charset_info_st *, char *to, uint n,
+ const char *fmt, ...);
+
+long my_strntol_8bit(CHARSET_INFO *, const char *s, uint l, int base,
+ char **e, int *err);
+ulong my_strntoul_8bit(CHARSET_INFO *, const char *s, uint l, int base,
+ char **e, int *err);
+longlong my_strntoll_8bit(CHARSET_INFO *, const char *s, uint l, int base,
+ char **e, int *err);
+ulonglong my_strntoull_8bit(CHARSET_INFO *, const char *s, uint l, int base,
+ char **e, int *err);
+double my_strntod_8bit(CHARSET_INFO *, char *s, uint l,char **e,
+ int *err);
+int my_long10_to_str_8bit(CHARSET_INFO *, char *to, uint l, int radix,
+ long int val);
+int my_longlong10_to_str_8bit(CHARSET_INFO *, char *to, uint l, int radix,
+ longlong val);
+
+longlong my_strtoll10_8bit(CHARSET_INFO *cs,
+ const char *nptr, char **endptr, int *error);
+longlong my_strtoll10_ucs2(CHARSET_INFO *cs,
+ const char *nptr, char **endptr, int *error);
+
+void my_fill_8bit(CHARSET_INFO *cs, char* to, uint l, int fill);
+
+my_bool my_like_range_simple(CHARSET_INFO *cs,
+ const char *ptr, uint ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ uint res_length,
+ char *min_str, char *max_str,
+ uint *min_length, uint *max_length);
+
+my_bool my_like_range_mb(CHARSET_INFO *cs,
+ const char *ptr, uint ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ uint res_length,
+ char *min_str, char *max_str,
+ uint *min_length, uint *max_length);
+
+my_bool my_like_range_ucs2(CHARSET_INFO *cs,
+ const char *ptr, uint ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ uint res_length,
+ char *min_str, char *max_str,
+ uint *min_length, uint *max_length);
+
+
+int my_wildcmp_8bit(CHARSET_INFO *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape, int w_one, int w_many);
+
+uint my_numchars_8bit(CHARSET_INFO *, const char *b, const char *e);
+uint my_numcells_8bit(CHARSET_INFO *, const char *b, const char *e);
+uint my_charpos_8bit(CHARSET_INFO *, const char *b, const char *e, uint pos);
+uint my_well_formed_len_8bit(CHARSET_INFO *, const char *b, const char *e,
+ uint pos, int *error);
+int my_mbcharlen_8bit(CHARSET_INFO *, uint c);
+
+
+/* Functions for multibyte charsets */
+extern void my_caseup_str_mb(CHARSET_INFO *, char *);
+extern void my_casedn_str_mb(CHARSET_INFO *, char *);
+extern uint my_caseup_mb(CHARSET_INFO *, char *src, uint srclen,
+ char *dst, uint dstlen);
+extern uint my_casedn_mb(CHARSET_INFO *, char *src, uint srclen,
+ char *dst, uint dstlen);
+extern int my_strcasecmp_mb(CHARSET_INFO * cs,const char *, const char *);
+
+int my_wildcmp_mb(CHARSET_INFO *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape, int w_one, int w_many);
+uint my_numchars_mb(CHARSET_INFO *, const char *b, const char *e);
+uint my_numcells_mb(CHARSET_INFO *, const char *b, const char *e);
+uint my_charpos_mb(CHARSET_INFO *, const char *b, const char *e, uint pos);
+uint my_well_formed_len_mb(CHARSET_INFO *, const char *b, const char *e,
+ uint pos, int *error);
+uint my_instr_mb(struct charset_info_st *,
+ const char *b, uint b_length,
+ const char *s, uint s_length,
+ my_match_t *match, uint nmatch);
+
+int my_wildcmp_unicode(CHARSET_INFO *cs,
+ const char *str, const char *str_end,
+ const char *wildstr, const char *wildend,
+ int escape, int w_one, int w_many,
+ MY_UNICASE_INFO **weights);
+
+extern my_bool my_parse_charset_xml(const char *bug, uint len,
+ int (*add)(CHARSET_INFO *cs));
+
+my_bool my_propagate_simple(CHARSET_INFO *cs, const uchar *str, uint len);
+my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, uint len);
+
+
+#define _MY_U 01 /* Upper case */
+#define _MY_L 02 /* Lower case */
+#define _MY_NMR 04 /* Numeral (digit) */
+#define _MY_SPC 010 /* Spacing character */
+#define _MY_PNT 020 /* Punctuation */
+#define _MY_CTR 040 /* Control character */
+#define _MY_B 0100 /* Blank */
+#define _MY_X 0200 /* heXadecimal digit */
+
+
+#define my_isascii(c) (!((c) & ~0177))
+#define my_toascii(c) ((c) & 0177)
+#define my_tocntrl(c) ((c) & 31)
+#define my_toprint(c) ((c) | 64)
+#define my_toupper(s,c) (char) ((s)->to_upper[(uchar) (c)])
+#define my_tolower(s,c) (char) ((s)->to_lower[(uchar) (c)])
+#define my_isalpha(s, c) (((s)->ctype+1)[(uchar) (c)] & (_MY_U | _MY_L))
+#define my_isupper(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_U)
+#define my_islower(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_L)
+#define my_isdigit(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_NMR)
+#define my_isxdigit(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_X)
+#define my_isalnum(s, c) (((s)->ctype+1)[(uchar) (c)] & (_MY_U | _MY_L | _MY_NMR))
+#define my_isspace(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_SPC)
+#define my_ispunct(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_PNT)
+#define my_isprint(s, c) (((s)->ctype+1)[(uchar) (c)] & (_MY_PNT | _MY_U | _MY_L | _MY_NMR | _MY_B))
+#define my_isgraph(s, c) (((s)->ctype+1)[(uchar) (c)] & (_MY_PNT | _MY_U | _MY_L | _MY_NMR))
+#define my_iscntrl(s, c) (((s)->ctype+1)[(uchar) (c)] & _MY_CTR)
+
+/* Some macros that should be cleaned up a little */
+#define my_isvar(s,c) (my_isalnum(s,c) || (c) == '_')
+#define my_isvar_start(s,c) (my_isalpha(s,c) || (c) == '_')
+
+#define my_binary_compare(s) ((s)->state & MY_CS_BINSORT)
+#define use_strnxfrm(s) ((s)->state & MY_CS_STRNXFRM)
+#define my_strnxfrm(s, a, b, c, d) ((s)->coll->strnxfrm((s), (a), (b), (c), (d)))
+#define my_strnncoll(s, a, b, c, d) ((s)->coll->strnncoll((s), (a), (b), (c), (d), 0))
+#define my_like_range(s, a, b, c, d, e, f, g, h, i, j) \
+ ((s)->coll->like_range((s), (a), (b), (c), (d), (e), (f), (g), (h), (i), (j)))
+#define my_wildcmp(cs,s,se,w,we,e,o,m) ((cs)->coll->wildcmp((cs),(s),(se),(w),(we),(e),(o),(m)))
+#define my_strcasecmp(s, a, b) ((s)->coll->strcasecmp((s), (a), (b)))
+#define my_charpos(cs, b, e, num) (cs)->cset->charpos((cs), (const char*) (b), (const char *)(e), (num))
+
+
+#define use_mb(s) ((s)->cset->ismbchar != NULL)
+#define my_ismbchar(s, a, b) ((s)->cset->ismbchar((s), (a), (b)))
+#ifdef USE_MB
+#define my_mbcharlen(s, a) ((s)->cset->mbcharlen((s),(a)))
+#else
+#define my_mbcharlen(s, a) 1
+#endif
+
+#define my_caseup_str(s, a) ((s)->cset->caseup_str((s), (a)))
+#define my_casedn_str(s, a) ((s)->cset->casedn_str((s), (a)))
+#define my_strntol(s, a, b, c, d, e) ((s)->cset->strntol((s),(a),(b),(c),(d),(e)))
+#define my_strntoul(s, a, b, c, d, e) ((s)->cset->strntoul((s),(a),(b),(c),(d),(e)))
+#define my_strntoll(s, a, b, c, d, e) ((s)->cset->strntoll((s),(a),(b),(c),(d),(e)))
+#define my_strntoull(s, a, b, c,d, e) ((s)->cset->strntoull((s),(a),(b),(c),(d),(e)))
+#define my_strntod(s, a, b, c, d) ((s)->cset->strntod((s),(a),(b),(c),(d)))
+
+
+/* XXX: still need to take care of this one */
+#ifdef MY_CHARSET_TIS620
+#error The TIS620 charset is broken at the moment. Tell tim to fix it.
+#define USE_TIS620
+#include "t_ctype.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _m_ctype_h */
diff --git a/src/mysql/my_alloc.h b/src/mysql/my_alloc.h
new file mode 100644
index 000000000..d328b384e
--- /dev/null
+++ b/src/mysql/my_alloc.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Data structures for mysys/my_alloc.c (root memory allocator)
+*/
+
+#ifndef _my_alloc_h
+#define _my_alloc_h
+
+#define ALLOC_MAX_BLOCK_TO_DROP 4096
+#define ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP 10
+
+typedef struct st_used_mem
+{ /* struct for once_alloc (block) */
+ struct st_used_mem *next; /* Next block in use */
+ unsigned int left; /* memory left in block */
+ unsigned int size; /* size of block */
+} USED_MEM;
+
+
+typedef struct st_mem_root
+{
+ USED_MEM *free; /* blocks with free memory in it */
+ USED_MEM *used; /* blocks almost without free memory */
+ USED_MEM *pre_alloc; /* preallocated block */
+ /* if block have less memory it will be put in 'used' list */
+ unsigned int min_malloc;
+ unsigned int block_size; /* initial block size */
+ unsigned int block_num; /* allocated blocks counter */
+ /*
+ first free block in queue test counter (if it exceed
+ MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)
+ */
+ unsigned int first_block_usage;
+
+ void (*error_handler)(void);
+} MEM_ROOT;
+#endif
diff --git a/src/mysql/my_dbug.h b/src/mysql/my_dbug.h
new file mode 100644
index 000000000..bc9b0e3a2
--- /dev/null
+++ b/src/mysql/my_dbug.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _dbug_h
+#define _dbug_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if !defined(DBUG_OFF) && !defined(_lint)
+extern int _db_on_,_no_db_;
+extern FILE *_db_fp_;
+extern char *_db_process_;
+extern int _db_keyword_(const char *keyword);
+extern int _db_strict_keyword_(const char *keyword);
+extern void _db_setjmp_(void);
+extern void _db_longjmp_(void);
+extern void _db_push_(const char *control);
+extern void _db_pop_(void);
+extern void _db_enter_(const char *_func_,const char *_file_,uint _line_,
+ const char **_sfunc_,const char **_sfile_,
+ uint *_slevel_, char ***);
+extern void _db_return_(uint _line_,const char **_sfunc_,const char **_sfile_,
+ uint *_slevel_);
+extern void _db_pargs_(uint _line_,const char *keyword);
+extern void _db_doprnt_ _VARARGS((const char *format,...));
+extern void _db_dump_(uint _line_,const char *keyword,const char *memory,
+ uint length);
+extern void _db_output_(uint flag);
+extern void _db_lock_file(void);
+extern void _db_unlock_file(void);
+
+#define DBUG_ENTER(a) const char *_db_func_, *_db_file_; uint _db_level_; \
+ char **_db_framep_; \
+ _db_enter_ (a,__FILE__,__LINE__,&_db_func_,&_db_file_,&_db_level_, \
+ &_db_framep_)
+#define DBUG_LEAVE \
+ (_db_return_ (__LINE__, &_db_func_, &_db_file_, &_db_level_))
+#define DBUG_RETURN(a1) {DBUG_LEAVE; return(a1);}
+#define DBUG_VOID_RETURN {DBUG_LEAVE; return;}
+#define DBUG_EXECUTE(keyword,a1) \
+ {if (_db_on_) {if (_db_keyword_ (keyword)) { a1 }}}
+#define DBUG_PRINT(keyword,arglist) \
+ {if (_db_on_) {_db_pargs_(__LINE__,keyword); _db_doprnt_ arglist;}}
+#define DBUG_PUSH(a1) _db_push_ (a1)
+#define DBUG_POP() _db_pop_ ()
+#define DBUG_PROCESS(a1) (_db_process_ = a1)
+#define DBUG_FILE (_db_fp_)
+#define DBUG_SETJMP(a1) (_db_setjmp_ (), setjmp (a1))
+#define DBUG_LONGJMP(a1,a2) (_db_longjmp_ (), longjmp (a1, a2))
+#define DBUG_DUMP(keyword,a1,a2)\
+ {if (_db_on_) {_db_dump_(__LINE__,keyword,a1,a2);}}
+#define DBUG_IN_USE (_db_fp_ && _db_fp_ != stderr)
+#define DEBUGGER_OFF _no_db_=1;_db_on_=0;
+#define DEBUGGER_ON _no_db_=0
+#define DBUG_LOCK_FILE { _db_lock_file(); }
+#define DBUG_UNLOCK_FILE { _db_unlock_file(); }
+#define DBUG_OUTPUT(A) { _db_output_(A); }
+#define DBUG_ASSERT(A) assert(A)
+#define DBUG_EXECUTE_IF(keyword,a1) \
+ {if (_db_on_) {if (_db_strict_keyword_ (keyword)) { a1 }}}
+#else /* No debugger */
+
+#define DBUG_ENTER(a1)
+#define DBUG_RETURN(a1) return(a1)
+#define DBUG_VOID_RETURN return
+#define DBUG_EXECUTE(keyword,a1) {}
+#define DBUG_EXECUTE_IF(keyword,a1) {}
+#define DBUG_PRINT(keyword,arglist) {}
+#define DBUG_PUSH(a1) {}
+#define DBUG_POP() {}
+#define DBUG_PROCESS(a1) {}
+#define DBUG_FILE (stderr)
+#define DBUG_SETJMP setjmp
+#define DBUG_LONGJMP longjmp
+#define DBUG_DUMP(keyword,a1,a2) {}
+#define DBUG_IN_USE 0
+#define DEBUGGER_OFF
+#define DEBUGGER_ON
+#define DBUG_LOCK_FILE
+#define DBUG_UNLOCK_FILE
+#define DBUG_OUTPUT(A)
+#define DBUG_ASSERT(A) {}
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/mysql/my_global.h b/src/mysql/my_global.h
new file mode 100644
index 000000000..65e251ab3
--- /dev/null
+++ b/src/mysql/my_global.h
@@ -0,0 +1,1285 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* This is the include file that should be included 'first' in every C file. */
+
+#ifndef _global_h
+#define _global_h
+
+#ifndef EMBEDDED_LIBRARY
+#define HAVE_REPLICATION
+#define HAVE_EXTERNAL_CLIENT
+#endif
+
+#if defined( __EMX__) && !defined( MYSQL_SERVER)
+/* moved here to use below VOID macro redefinition */
+#define INCL_BASE
+#define INCL_NOPMAPI
+#include <os2.h>
+#endif /* __EMX__ */
+
+#ifdef __CYGWIN__
+/* We use a Unix API, so pretend it's not Windows */
+#undef WIN
+#undef WIN32
+#undef _WIN
+#undef _WIN32
+#undef _WIN64
+#undef __WIN__
+#undef __WIN32__
+#define HAVE_ERRNO_AS_DEFINE
+#endif /* __CYGWIN__ */
+
+/* to make command line shorter we'll define USE_PRAGMA_INTERFACE here */
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#define USE_PRAGMA_INTERFACE
+#endif
+
+#if defined(i386) && !defined(__i386__)
+#define __i386__
+#endif
+
+/* Macros to make switching between C and C++ mode easier */
+#ifdef __cplusplus
+#define C_MODE_START extern "C" {
+#define C_MODE_END }
+#else
+#define C_MODE_START
+#define C_MODE_END
+#endif
+
+#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
+#include <config-win.h>
+#elif defined(OS2)
+#include <config-os2.h>
+#elif defined(__NETWARE__)
+#include <my_config.h>
+#include <config-netware.h>
+#if defined(__cplusplus) && defined(inline)
+#undef inline /* fix configure problem */
+#endif
+#else
+#include <my_config.h>
+#if defined(__cplusplus) && defined(inline)
+#undef inline /* fix configure problem */
+#endif
+#endif /* _WIN32... */
+
+/* Some defines to avoid ifdefs in the code */
+#ifndef NETWARE_YIELD
+#define NETWARE_YIELD
+#define NETWARE_SET_SCREEN_MODE(A)
+#endif
+
+/*
+ The macros below are borrowed from include/linux/compiler.h in the
+ Linux kernel. Use them to indicate the likelyhood of the truthfulness
+ of a condition. This serves two purposes - newer versions of gcc will be
+ able to optimize for branch predication, which could yield siginficant
+ performance gains in frequently executed sections of the code, and the
+ other reason to use them is for documentation
+*/
+
+#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#define __builtin_expect(x, expected_value) (x)
+#endif
+
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
+
+/* Fix problem with S_ISLNK() on Linux */
+#if defined(TARGET_OS_LINUX)
+#undef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+/*
+ Temporary solution to solve bug#7156. Include "sys/types.h" before
+ the thread headers, else the function madvise() will not be defined
+*/
+#if defined(HAVE_SYS_TYPES_H) && ( defined(sun) || defined(__sun) )
+#include <sys/types.h>
+#endif
+
+/* The client defines this to avoid all thread code */
+#if defined(UNDEF_THREADS_HACK)
+#undef THREAD
+#undef HAVE_mit_thread
+#undef HAVE_LINUXTHREADS
+#undef HAVE_NPTL
+#undef HAVE_UNIXWARE7_THREADS
+#endif
+
+#ifdef HAVE_THREADS_WITHOUT_SOCKETS
+/* MIT pthreads does not work with unix sockets */
+#undef HAVE_SYS_UN_H
+#endif
+
+#define __EXTENSIONS__ 1 /* We want some extension */
+#ifndef __STDC_EXT__
+#define __STDC_EXT__ 1 /* To get large file support on hpux */
+#endif
+
+/*
+ Solaris 9 include file <sys/feature_tests.h> refers to X/Open document
+
+ System Interfaces and Headers, Issue 5
+
+ saying we should define _XOPEN_SOURCE=500 to get POSIX.1c prototypes,
+ but apparently other systems (namely FreeBSD) don't agree.
+
+ On a newer Solaris 10, the above file recognizes also _XOPEN_SOURCE=600.
+ Furthermore, it tests that if a program requires older standard
+ (_XOPEN_SOURCE<600 or _POSIX_C_SOURCE<200112L) it cannot be
+ run on a new compiler (that defines _STDC_C99) and issues an #error.
+ It's also an #error if a program requires new standard (_XOPEN_SOURCE=600
+ or _POSIX_C_SOURCE=200112L) and a compiler does not define _STDC_C99.
+
+ To add more to this mess, Sun Studio C compiler defines _STDC_C99 while
+ C++ compiler does not!
+
+ So, in a desperate attempt to get correct prototypes for both
+ C and C++ code, we define either _XOPEN_SOURCE=600 or _XOPEN_SOURCE=500
+ depending on the compiler's announced C standard support.
+
+ Cleaner solutions are welcome.
+*/
+#ifdef __sun
+#if __STDC_VERSION__ - 0 >= 199901L
+#define _XOPEN_SOURCE 600
+#else
+#define _XOPEN_SOURCE 500
+#endif
+#endif
+
+#if defined(THREAD) && !defined(__WIN__) && !defined(OS2)
+#ifndef _POSIX_PTHREAD_SEMANTICS
+#define _POSIX_PTHREAD_SEMANTICS /* We want posix threads */
+#endif
+
+#if !defined(SCO)
+#define _REENTRANT 1 /* Some thread libraries require this */
+#endif
+#if !defined(_THREAD_SAFE) && !defined(_AIX)
+#define _THREAD_SAFE /* Required for OSF1 */
+#endif
+#ifndef HAVE_mit_thread
+#ifdef HAVE_UNIXWARE7_THREADS
+#include <thread.h>
+#else
+#if defined(HPUX10) || defined(HPUX11)
+C_MODE_START /* HPUX needs this, signal.h bug */
+#include <pthread.h>
+C_MODE_END
+#else
+#include <pthread.h> /* AIX must have this included first */
+#endif
+#endif /* HAVE_UNIXWARE7_THREADS */
+#endif /* HAVE_mit_thread */
+#if !defined(SCO) && !defined(_REENTRANT)
+#define _REENTRANT 1 /* Threads requires reentrant code */
+#endif
+#endif /* THREAD */
+
+/* Go around some bugs in different OS and compilers */
+#ifdef _AIX /* By soren@t.dk */
+#define _H_STRINGS
+#define _SYS_STREAM_H
+/* #define _AIX32_CURSES */ /* XXX: this breaks AIX 4.3.3 (others?). */
+#define ulonglong2double(A) my_ulonglong2double(A)
+#define my_off_t2double(A) my_ulonglong2double(A)
+C_MODE_START
+double my_ulonglong2double(unsigned long long A);
+C_MODE_END
+#endif /* _AIX */
+
+#ifdef HAVE_BROKEN_SNPRINTF /* HPUX 10.20 don't have this defined */
+#undef HAVE_SNPRINTF
+#endif
+#ifdef HAVE_BROKEN_PREAD
+/*
+ pread()/pwrite() are not 64 bit safe on HP-UX 11.0 without
+ installing the kernel patch PHKL_20349 or greater
+*/
+#undef HAVE_PREAD
+#undef HAVE_PWRITE
+#endif
+#if defined(HAVE_BROKEN_INLINE) && !defined(__cplusplus)
+#undef inline
+#define inline
+#endif
+
+#ifdef UNDEF_HAVE_GETHOSTBYNAME_R /* For OSF4.x */
+#undef HAVE_GETHOSTBYNAME_R
+#endif
+#ifdef UNDEF_HAVE_INITGROUPS /* For AIX 4.3 */
+#undef HAVE_INITGROUPS
+#endif
+
+/* gcc/egcs issues */
+
+#if defined(__GNUC) && defined(__EXCEPTIONS)
+#error "Please add -fno-exceptions to CXXFLAGS and reconfigure/recompile"
+#endif
+
+
+/* Fix a bug in gcc 2.8.0 on IRIX 6.2 */
+#if SIZEOF_LONG == 4 && defined(__LONG_MAX__) && (__GNUC__ == 2 && __GNUC_MINOR__ == 8)
+#undef __LONG_MAX__ /* Is a longlong value in gcc 2.8.0 ??? */
+#define __LONG_MAX__ 2147483647
+#endif
+
+/* egcs 1.1.2 has a problem with memcpy on Alpha */
+#if defined(__GNUC__) && defined(__alpha__) && ! (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
+#define BAD_MEMCPY
+#endif
+
+#if defined(_lint) && !defined(lint)
+#define lint
+#endif
+#if SIZEOF_LONG_LONG > 4 && !defined(_LONG_LONG)
+#define _LONG_LONG 1 /* For AIX string library */
+#endif
+
+#ifndef stdin
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#include <math.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TIMEB_H
+#include <sys/timeb.h> /* Avoid warnings on SCO */
+#endif
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif /* TIME_WITH_SYS_TIME */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(__cplusplus) && defined(NO_CPLUSPLUS_ALLOCA)
+#undef HAVE_ALLOCA
+#undef HAVE_ALLOCA_H
+#endif
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#ifdef HAVE_ATOMIC_ADD
+#define new my_arg_new
+#define need_to_restore_new 1
+C_MODE_START
+#include <asm/atomic.h>
+C_MODE_END
+#ifdef need_to_restore_new /* probably safer than #ifdef new */
+#undef new
+#undef need_to_restore_new
+#endif
+#endif
+#include <errno.h> /* Recommended by debian */
+/* We need the following to go around a problem with openssl on solaris */
+#if defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+/*
+ A lot of our programs uses asserts, so better to always include it
+ This also fixes a problem when people uses DBUG_ASSERT without including
+ assert.h
+*/
+#include <assert.h>
+
+/* Go around some bugs in different OS and compilers */
+#if defined(_HPUX_SOURCE) && defined(HAVE_SYS_STREAM_H)
+#include <sys/stream.h> /* HPUX 10.20 defines ulong here. UGLY !!! */
+#define HAVE_ULONG
+#endif
+#ifdef DONT_USE_FINITE /* HPUX 11.x has is_finite() */
+#undef HAVE_FINITE
+#endif
+#if defined(HPUX10) && defined(_LARGEFILE64_SOURCE) && defined(THREAD)
+/* Fix bug in setrlimit */
+#undef setrlimit
+#define setrlimit cma_setrlimit64
+#endif
+/* Declare madvise where it is not declared for C++, like Solaris */
+#if HAVE_MADVISE && !HAVE_DECL_MADVISE && defined(__cplusplus)
+extern "C" int madvise(void *addr, size_t len, int behav);
+#endif
+
+#ifdef __QNXNTO__
+/* This has to be after include limits.h */
+#define HAVE_ERRNO_AS_DEFINE
+#define HAVE_FCNTL_LOCK
+#undef HAVE_FINITE
+#undef LONGLONG_MIN /* These get wrongly defined in QNX 6.2 */
+#undef LONGLONG_MAX /* standard system library 'limits.h' */
+#ifdef __cplusplus
+#ifndef HAVE_RINT
+#define HAVE_RINT
+#endif /* rint() and isnan() functions are not */
+#define rint(a) std::rint(a) /* visible in C++ scope due to an error */
+#define isnan(a) std::isnan(a) /* in the usr/include/math.h on QNX */
+#endif
+#endif
+
+/* We can not live without the following defines */
+
+#define USE_MYFUNC 1 /* Must use syscall indirection */
+#define MASTER 1 /* Compile without unireg */
+#define ENGLISH 1 /* Messages in English */
+#define POSIX_MISTAKE 1 /* regexp: Fix stupid spec error */
+#define USE_REGEX 1 /* We want the use the regex library */
+/* Do not define for ultra sparcs */
+#ifndef OS2
+#define USE_BMOVE512 1 /* Use this unless system bmove is faster */
+#endif
+
+#define QUOTE_ARG(x) #x /* Quote argument (before cpp) */
+#define STRINGIFY_ARG(x) QUOTE_ARG(x) /* Quote argument, after cpp */
+
+/* Paranoid settings. Define I_AM_PARANOID if you are paranoid */
+#ifdef I_AM_PARANOID
+#define DONT_ALLOW_USER_CHANGE 1
+#define DONT_USE_MYSQL_PWD 1
+#endif
+
+/* Does the system remember a signal handler after a signal ? */
+#ifndef HAVE_BSD_SIGNALS
+#define DONT_REMEMBER_SIGNAL
+#endif
+
+/* Define void to stop lint from generating "null effekt" comments */
+#ifndef DONT_DEFINE_VOID
+#ifdef _lint
+int __void__;
+#define VOID(X) (__void__ = (int) (X))
+#else
+#undef VOID
+#define VOID(X) (X)
+#endif
+#endif /* DONT_DEFINE_VOID */
+
+#if defined(_lint) || defined(FORCE_INIT_OF_VARS)
+#define LINT_INIT(var) var=0 /* No uninitialize-warning */
+#else
+#define LINT_INIT(var)
+#endif
+
+#if defined(_lint) || defined(FORCE_INIT_OF_VARS) || defined(HAVE_purify)
+#define PURIFY_OR_LINT_INIT(var) var=0
+#else
+#define PURIFY_OR_LINT_INIT(var)
+#endif
+
+/* Define some useful general macros */
+#if !defined(max)
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#if defined(__EMX__) || !defined(HAVE_UINT)
+#undef HAVE_UINT
+#define HAVE_UINT
+typedef unsigned int uint;
+typedef unsigned short ushort;
+#endif
+
+#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1)
+#define sgn(a) (((a) < 0) ? -1 : ((a) > 0) ? 1 : 0)
+#define swap_variables(t, a, b) { register t dummy; dummy= a; a= b; b= dummy; }
+#define test(a) ((a) ? 1 : 0)
+#define set_if_bigger(a,b) do { if ((a) < (b)) (a)=(b); } while(0)
+#define set_if_smaller(a,b) do { if ((a) > (b)) (a)=(b); } while(0)
+#define test_all_bits(a,b) (((a) & (b)) == (b))
+#define set_bits(type, bit_count) (sizeof(type)*8 <= (bit_count) ? ~(type) 0 : ((((type) 1) << (bit_count)) - (type) 1))
+#define array_elements(A) ((uint) (sizeof(A)/sizeof(A[0])))
+#ifndef HAVE_RINT
+#define rint(A) floor((A)+(((A) < 0)? -0.5 : 0.5))
+#endif
+
+/* Define some general constants */
+#ifndef TRUE
+#define TRUE (1) /* Logical true */
+#define FALSE (0) /* Logical false */
+#endif
+
+#if defined(__GNUC__)
+#define function_volatile volatile
+#define my_reinterpret_cast(A) reinterpret_cast<A>
+#define my_const_cast(A) const_cast<A>
+#elif !defined(my_reinterpret_cast)
+#define my_reinterpret_cast(A) (A)
+#define my_const_cast(A) (A)
+#endif
+#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8)
+#define __attribute__(A)
+#endif
+
+/* From old s-system.h */
+
+/*
+ Support macros for non ansi & other old compilers. Since such
+ things are no longer supported we do nothing. We keep then since
+ some of our code may still be needed to upgrade old customers.
+*/
+#define _VARARGS(X) X
+#define _STATIC_VARARGS(X) X
+#define _PC(X) X
+
+#if defined(DBUG_ON) && defined(DBUG_OFF)
+#undef DBUG_OFF
+#endif
+
+#if defined(_lint) && !defined(DBUG_OFF)
+#define DBUG_OFF
+#endif
+
+#include <my_dbug.h>
+
+#define MIN_ARRAY_SIZE 0 /* Zero or One. Gcc allows zero*/
+#define ASCII_BITS_USED 8 /* Bit char used */
+#define NEAR_F /* No near function handling */
+
+/* Some types that is different between systems */
+
+typedef int File; /* File descriptor */
+#ifndef Socket_defined
+typedef int my_socket; /* File descriptor for sockets */
+#define INVALID_SOCKET -1
+#endif
+/* Type for fuctions that handles signals */
+#define sig_handler RETSIGTYPE
+C_MODE_START
+typedef void (*sig_return)();/* Returns type from signal */
+C_MODE_END
+#if defined(__GNUC__) && !defined(_lint)
+typedef char pchar; /* Mixed prototypes can take char */
+typedef char puchar; /* Mixed prototypes can take char */
+typedef char pbool; /* Mixed prototypes can take char */
+typedef short pshort; /* Mixed prototypes can take short int */
+typedef float pfloat; /* Mixed prototypes can take float */
+#else
+typedef int pchar; /* Mixed prototypes can't take char */
+typedef uint puchar; /* Mixed prototypes can't take char */
+typedef int pbool; /* Mixed prototypes can't take char */
+typedef int pshort; /* Mixed prototypes can't take short int */
+typedef double pfloat; /* Mixed prototypes can't take float */
+#endif
+C_MODE_START
+typedef int (*qsort_cmp)(const void *,const void *);
+typedef int (*qsort_cmp2)(void*, const void *,const void *);
+C_MODE_END
+#ifdef HAVE_mit_thread
+#define qsort_t void
+#undef QSORT_TYPE_IS_VOID
+#define QSORT_TYPE_IS_VOID
+#else
+#define qsort_t RETQSORTTYPE /* Broken GCC cant handle typedef !!!! */
+#endif
+#ifdef HAVE_mit_thread
+#define size_socket socklen_t /* Type of last arg to accept */
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+typedef SOCKET_SIZE_TYPE size_socket;
+#endif
+
+#ifndef SOCKOPT_OPTLEN_TYPE
+#define SOCKOPT_OPTLEN_TYPE size_socket
+#endif
+
+/* file create flags */
+
+#ifndef O_SHARE /* Probably not windows */
+#define O_SHARE 0 /* Flag to my_open for shared files */
+#ifndef O_BINARY
+#define O_BINARY 0 /* Flag to my_open for binary files */
+#endif
+#ifndef FILE_BINARY
+#define FILE_BINARY O_BINARY /* Flag to my_fopen for binary streams */
+#endif
+#ifdef HAVE_FCNTL
+#define HAVE_FCNTL_LOCK
+#define F_TO_EOF 0L /* Param to lockf() to lock rest of file */
+#endif
+#endif /* O_SHARE */
+
+#ifndef O_TEMPORARY
+#define O_TEMPORARY 0
+#endif
+#ifndef O_SHORT_LIVED
+#define O_SHORT_LIVED 0
+#endif
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+/* additional file share flags for win32 */
+#ifdef __WIN__
+#define _SH_DENYRWD 0x110 /* deny read/write mode & delete */
+#define _SH_DENYWRD 0x120 /* deny write mode & delete */
+#define _SH_DENYRDD 0x130 /* deny read mode & delete */
+#define _SH_DENYDEL 0x140 /* deny delete only */
+#endif /* __WIN__ */
+
+
+/* #define USE_RECORD_LOCK */
+
+ /* Unsigned types supported by the compiler */
+#define UNSINT8 /* unsigned int8 (char) */
+#define UNSINT16 /* unsigned int16 */
+#define UNSINT32 /* unsigned int32 */
+
+ /* General constants */
+#define SC_MAXWIDTH 256 /* Max width of screen (for error messages) */
+#define FN_LEN 256 /* Max file name len */
+#define FN_HEADLEN 253 /* Max length of filepart of file name */
+#define FN_EXTLEN 20 /* Max length of extension (part of FN_LEN) */
+#ifdef PATH_MAX
+#define FN_REFLEN PATH_MAX/* Max length of full path-name */
+#else
+#define FN_REFLEN 512 /* Max length of full path-name */
+#endif
+#define FN_EXTCHAR '.'
+#define FN_HOMELIB '~' /* ~/ is used as abbrev for home dir */
+#define FN_CURLIB '.' /* ./ is used as abbrev for current dir */
+#define FN_PARENTDIR ".." /* Parent directory; Must be a string */
+#define FN_DEVCHAR ':'
+
+#ifndef FN_LIBCHAR
+#ifdef __EMX__
+#define FN_LIBCHAR '\\'
+#define FN_ROOTDIR "\\"
+#else
+#define FN_LIBCHAR '/'
+#define FN_ROOTDIR "/"
+#endif
+#endif
+#define MY_NFILE 64 /* This is only used to save filenames */
+#ifndef OS_FILE_LIMIT
+#define OS_FILE_LIMIT 65535
+#endif
+
+/* #define EXT_IN_LIBNAME */
+/* #define FN_NO_CASE_SENCE */
+/* #define FN_UPPER_CASE TRUE */
+
+/*
+ Io buffer size; Must be a power of 2 and a multiple of 512. May be
+ smaller what the disk page size. This influences the speed of the
+ isam btree library. eg to big to slow.
+*/
+#define IO_SIZE 4096
+/*
+ How much overhead does malloc have. The code often allocates
+ something like 1024-MALLOC_OVERHEAD bytes
+*/
+#ifdef SAFEMALLOC
+#define MALLOC_OVERHEAD (8+24+4)
+#else
+#define MALLOC_OVERHEAD 8
+#endif
+ /* get memory in huncs */
+#define ONCE_ALLOC_INIT (uint) (4096-MALLOC_OVERHEAD)
+ /* Typical record cash */
+#define RECORD_CACHE_SIZE (uint) (64*1024-MALLOC_OVERHEAD)
+ /* Typical key cash */
+#define KEY_CACHE_SIZE (uint) (8*1024*1024-MALLOC_OVERHEAD)
+ /* Default size of a key cache block */
+#define KEY_CACHE_BLOCK_SIZE (uint) 1024
+
+
+ /* Some things that this system doesn't have */
+
+#define NO_HASH /* Not needed anymore */
+#ifdef __WIN__
+#define NO_DIR_LIBRARY /* Not standar dir-library */
+#define USE_MY_STAT_STRUCT /* For my_lib */
+#endif
+
+/* Some defines of functions for portability */
+
+#undef remove /* Crashes MySQL on SCO 5.0.0 */
+#ifndef __WIN__
+#ifdef OS2
+#define closesocket(A) soclose(A)
+#else
+#define closesocket(A) close(A)
+#endif
+#ifndef ulonglong2double
+#define ulonglong2double(A) ((double) (ulonglong) (A))
+#define my_off_t2double(A) ((double) (my_off_t) (A))
+#endif
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#define ulong_to_double(X) ((double) (ulong) (X))
+#define SET_STACK_SIZE(X) /* Not needed on real machines */
+
+#if !defined(HAVE_mit_thread) && !defined(HAVE_STRTOK_R)
+#define strtok_r(A,B,C) strtok((A),(B))
+#endif
+
+/* Remove some things that mit_thread break or doesn't support */
+#if defined(HAVE_mit_thread) && defined(THREAD)
+#undef HAVE_PREAD
+#undef HAVE_REALPATH
+#undef HAVE_MLOCK
+#undef HAVE_TEMPNAM /* Use ours */
+#undef HAVE_PTHREAD_SETPRIO
+#undef HAVE_FTRUNCATE
+#undef HAVE_READLINK
+#endif
+
+/* This is from the old m-machine.h file */
+
+#if SIZEOF_LONG_LONG > 4
+#define HAVE_LONG_LONG 1
+#endif
+
+/*
+ Some pre-ANSI-C99 systems like AIX 5.1 and Linux/GCC 2.95 define
+ ULONGLONG_MAX, LONGLONG_MIN, LONGLONG_MAX; we use them if they're defined.
+ Also on Windows we define these constants by hand in config-win.h.
+*/
+
+#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
+#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
+#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
+#endif
+
+#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
+/* First check for ANSI C99 definition: */
+#ifdef ULLONG_MAX
+#define ULONGLONG_MAX ULLONG_MAX
+#else
+#define ULONGLONG_MAX ((unsigned long long)(~0ULL))
+#endif
+#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
+
+#define INT_MIN32 (~0x7FFFFFFFL)
+#define INT_MAX32 0x7FFFFFFFL
+#define UINT_MAX32 0xFFFFFFFFL
+#define INT_MIN24 (~0x007FFFFF)
+#define INT_MAX24 0x007FFFFF
+#define UINT_MAX24 0x00FFFFFF
+#define INT_MIN16 (~0x7FFF)
+#define INT_MAX16 0x7FFF
+#define UINT_MAX16 0xFFFF
+#define INT_MIN8 (~0x7F)
+#define INT_MAX8 0x7F
+#define UINT_MAX8 0xFF
+
+/* From limits.h instead */
+#ifndef DBL_MIN
+#define DBL_MIN 4.94065645841246544e-324
+#define FLT_MIN ((float)1.40129846432481707e-45)
+#endif
+#ifndef DBL_MAX
+#define DBL_MAX 1.79769313486231470e+308
+#define FLT_MAX ((float)3.40282346638528860e+38)
+#endif
+
+#if !defined(HAVE_ISINF) && !defined(isinf)
+#define isinf(X) 0
+#endif
+
+/* Define missing math constants. */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+#ifndef M_E
+#define M_E 2.7182818284590452354
+#endif
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942
+#endif
+
+/*
+ Max size that must be added to a so that we know Size to make
+ adressable obj.
+*/
+#if SIZEOF_CHARP == 4
+typedef long my_ptrdiff_t;
+#else
+typedef long long my_ptrdiff_t;
+#endif
+
+#define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1))
+#define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double))
+/* Size to make adressable obj. */
+#define ALIGN_PTR(A, t) ((t*) MY_ALIGN((A),sizeof(t)))
+ /* Offset of field f in structure t */
+#define OFFSET(t, f) ((size_t)(char *)&((t *)0)->f)
+#define ADD_TO_PTR(ptr,size,type) (type) ((byte*) (ptr)+size)
+#define PTR_BYTE_DIFF(A,B) (my_ptrdiff_t) ((byte*) (A) - (byte*) (B))
+
+#define NullS (char *) 0
+/* Nowdays we do not support MessyDos */
+#ifndef NEAR
+#define NEAR /* Who needs segments ? */
+#define FAR /* On a good machine */
+#ifndef HUGE_PTR
+#define HUGE_PTR
+#endif
+#endif
+#if defined(__IBMC__) || defined(__IBMCPP__)
+/* This was _System _Export but caused a lot of warnings on _AIX43 */
+#define STDCALL
+#elif !defined( STDCALL)
+#define STDCALL
+#endif
+
+/* Typdefs for easyier portability */
+
+#if defined(VOIDTYPE)
+typedef void *gptr; /* Generic pointer */
+#else
+typedef char *gptr; /* Generic pointer */
+#endif
+#ifndef HAVE_INT_8_16_32
+typedef signed char int8; /* Signed integer >= 8 bits */
+typedef short int16; /* Signed integer >= 16 bits */
+#endif
+#ifndef HAVE_UCHAR
+typedef unsigned char uchar; /* Short for unsigned char */
+#endif
+typedef unsigned char uint8; /* Short for unsigned integer >= 8 bits */
+typedef unsigned short uint16; /* Short for unsigned integer >= 16 bits */
+
+#if SIZEOF_INT == 4
+#ifndef HAVE_INT_8_16_32
+typedef int int32;
+#endif
+typedef unsigned int uint32; /* Short for unsigned integer >= 32 bits */
+#elif SIZEOF_LONG == 4
+#ifndef HAVE_INT_8_16_32
+typedef long int32;
+#endif
+typedef unsigned long uint32; /* Short for unsigned integer >= 32 bits */
+#else
+#error "Neither int or long is of 4 bytes width"
+#endif
+
+#if !defined(HAVE_ULONG) && !defined(TARGET_OS_LINUX) && !defined(__USE_MISC)
+typedef unsigned long ulong; /* Short for unsigned long */
+#endif
+#ifndef longlong_defined
+#if defined(HAVE_LONG_LONG) && SIZEOF_LONG != 8
+typedef unsigned long long int ulonglong; /* ulong or unsigned long long */
+typedef long long int longlong;
+#else
+typedef unsigned long ulonglong; /* ulong or unsigned long long */
+typedef long longlong;
+#endif
+#endif
+
+#if defined(NO_CLIENT_LONG_LONG)
+typedef unsigned long my_ulonglong;
+#elif defined (__WIN__)
+typedef unsigned __int64 my_ulonglong;
+#else
+typedef unsigned long long my_ulonglong;
+#endif
+
+#ifdef USE_RAID
+/*
+ The following is done with a if to not get problems with pre-processors
+ with late define evaluation
+*/
+#if SIZEOF_OFF_T == 4
+#define SYSTEM_SIZEOF_OFF_T 4
+#else
+#define SYSTEM_SIZEOF_OFF_T 8
+#endif
+#undef SIZEOF_OFF_T
+#define SIZEOF_OFF_T 8
+#else
+#define SYSTEM_SIZEOF_OFF_T SIZEOF_OFF_T
+#endif /* USE_RAID */
+
+#if SIZEOF_OFF_T > 4
+typedef ulonglong my_off_t;
+#else
+typedef unsigned long my_off_t;
+#endif
+#define MY_FILEPOS_ERROR (~(my_off_t) 0)
+#if !defined(__WIN__) && !defined(OS2)
+typedef off_t os_off_t;
+#endif
+
+#if defined(__WIN__)
+#define socket_errno WSAGetLastError()
+#define SOCKET_EINTR WSAEINTR
+#define SOCKET_EAGAIN WSAEINPROGRESS
+#define SOCKET_ETIMEDOUT WSAETIMEDOUT
+#define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK
+#define SOCKET_ENFILE ENFILE
+#define SOCKET_EMFILE EMFILE
+#elif defined(OS2)
+#define socket_errno sock_errno()
+#define SOCKET_EINTR SOCEINTR
+#define SOCKET_EAGAIN SOCEINPROGRESS
+#define SOCKET_ETIMEDOUT SOCKET_EINTR
+#define SOCKET_EWOULDBLOCK SOCEWOULDBLOCK
+#define SOCKET_ENFILE SOCENFILE
+#define SOCKET_EMFILE SOCEMFILE
+#define closesocket(A) soclose(A)
+#else /* Unix */
+#define socket_errno errno
+#define closesocket(A) close(A)
+#define SOCKET_EINTR EINTR
+#define SOCKET_EAGAIN EAGAIN
+#define SOCKET_ETIMEDOUT SOCKET_EINTR
+#define SOCKET_EWOULDBLOCK EWOULDBLOCK
+#define SOCKET_ENFILE ENFILE
+#define SOCKET_EMFILE EMFILE
+#endif
+
+typedef uint8 int7; /* Most effective integer 0 <= x <= 127 */
+typedef short int15; /* Most effective integer 0 <= x <= 32767 */
+typedef char *my_string; /* String of characters */
+typedef unsigned long size_s; /* Size of strings (In string-funcs) */
+typedef int myf; /* Type of MyFlags in my_funcs */
+#ifndef byte_defined
+typedef char byte; /* Smallest addressable unit */
+#endif
+typedef char my_bool; /* Small bool */
+#if !defined(bool) && !defined(bool_defined) && (!defined(HAVE_BOOL) || !defined(__cplusplus))
+typedef char bool; /* Ordinary boolean values 0 1 */
+#endif
+ /* Macros for converting *constants* to the right type */
+#define INT8(v) (int8) (v)
+#define INT16(v) (int16) (v)
+#define INT32(v) (int32) (v)
+#define MYF(v) (myf) (v)
+
+#ifndef LL
+#ifdef HAVE_LONG_LONG
+#define LL(A) A ## LL
+#else
+#define LL(A) A ## L
+#endif
+#endif
+
+#ifndef ULL
+#ifdef HAVE_LONG_LONG
+#define ULL(A) A ## ULL
+#else
+#define ULL(A) A ## UL
+#endif
+#endif
+
+/*
+ Defines to make it possible to prioritize register assignments. No
+ longer that important with modern compilers.
+*/
+#ifndef USING_X
+#define reg1 register
+#define reg2 register
+#define reg3 register
+#define reg4 register
+#define reg5 register
+#define reg6 register
+#define reg7 register
+#define reg8 register
+#define reg9 register
+#define reg10 register
+#define reg11 register
+#define reg12 register
+#define reg13 register
+#define reg14 register
+#define reg15 register
+#define reg16 register
+#endif
+
+/*
+ Sometimes we want to make sure that the variable is not put into
+ a register in debugging mode so we can see its value in the core
+*/
+
+#ifndef DBUG_OFF
+#define dbug_volatile volatile
+#else
+#define dbug_volatile
+#endif
+
+/* Defines for time function */
+#define SCALE_SEC 100
+#define SCALE_USEC 10000
+#define MY_HOW_OFTEN_TO_ALARM 2 /* How often we want info on screen */
+#define MY_HOW_OFTEN_TO_WRITE 1000 /* How often we want info on screen */
+
+#ifdef HAVE_TIMESPEC_TS_SEC
+#ifndef set_timespec
+#define set_timespec(ABSTIME,SEC) \
+{ \
+ (ABSTIME).ts_sec=time(0) + (time_t) (SEC); \
+ (ABSTIME).ts_nsec=0; \
+}
+#endif /* !set_timespec */
+#ifndef set_timespec_nsec
+#define set_timespec_nsec(ABSTIME,NSEC) \
+{ \
+ ulonglong now= my_getsystime() + (NSEC/100); \
+ (ABSTIME).ts_sec= (now / ULL(10000000)); \
+ (ABSTIME).ts_nsec= (now % ULL(10000000) * 100 + ((NSEC) % 100)); \
+}
+#endif /* !set_timespec_nsec */
+#else
+#ifndef set_timespec
+#define set_timespec(ABSTIME,SEC) \
+{\
+ struct timeval tv;\
+ gettimeofday(&tv,0);\
+ (ABSTIME).tv_sec=tv.tv_sec+(time_t) (SEC);\
+ (ABSTIME).tv_nsec=tv.tv_usec*1000;\
+}
+#endif /* !set_timespec */
+#ifndef set_timespec_nsec
+#define set_timespec_nsec(ABSTIME,NSEC) \
+{\
+ ulonglong now= my_getsystime() + (NSEC/100); \
+ (ABSTIME).tv_sec= (now / ULL(10000000)); \
+ (ABSTIME).tv_nsec= (now % ULL(10000000) * 100 + ((NSEC) % 100)); \
+}
+#endif /* !set_timespec_nsec */
+#endif /* HAVE_TIMESPEC_TS_SEC */
+
+/*
+ Define-funktions for reading and storing in machine independent format
+ (low byte first)
+*/
+
+/* Optimized store functions for Intel x86 */
+#if defined(__i386__) && !defined(_WIN64)
+#define sint2korr(A) (*((int16 *) (A)))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (*((long *) (A)))
+#define uint2korr(A) (*((uint16 *) (A)))
+#ifdef HAVE_purify
+#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16))
+#else
+/*
+ ATTENTION !
+
+ Please, note, uint3korr reads 4 bytes (not 3) !
+ It means, that you have to provide enough allocated space !
+*/
+#define uint3korr(A) (long) (*((unsigned int *) (A)) & 0xFFFFFF)
+#endif
+#define uint4korr(A) (*((unsigned long *) (A)))
+#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 32))
+#define uint8korr(A) (*((ulonglong *) (A)))
+#define sint8korr(A) (*((longlong *) (A)))
+#define int2store(T,A) *((uint16*) (T))= (uint16) (A)
+#define int3store(T,A) do { *(T)= (uchar) ((A));\
+ *(T+1)=(uchar) (((uint) (A) >> 8));\
+ *(T+2)=(uchar) (((A) >> 16)); } while (0)
+#define int4store(T,A) *((long *) (T))= (long) (A)
+#define int5store(T,A) do { *(T)= (uchar)((A));\
+ *((T)+1)=(uchar) (((A) >> 8));\
+ *((T)+2)=(uchar) (((A) >> 16));\
+ *((T)+3)=(uchar) (((A) >> 24)); \
+ *((T)+4)=(uchar) (((A) >> 32)); } while(0)
+#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A)
+
+typedef union {
+ double v;
+ long m[2];
+} doubleget_union;
+#define doubleget(V,M) \
+do { doubleget_union _tmp; \
+ _tmp.m[0] = *((long*)(M)); \
+ _tmp.m[1] = *(((long*) (M))+1); \
+ (V) = _tmp.v; } while(0)
+#define doublestore(T,V) do { *((long *) T) = ((doubleget_union *)&V)->m[0]; \
+ *(((long *) T)+1) = ((doubleget_union *)&V)->m[1]; \
+ } while (0)
+#define float4get(V,M) do { *((long *) &(V)) = *((long*) (M)); } while(0)
+#define float8get(V,M) doubleget((V),(M))
+#define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float))
+#define floatstore(T,V) memcpy((byte*)(T), (byte*)(&V),sizeof(float))
+#define floatget(V,M) memcpy((byte*) &V,(byte*) (M),sizeof(float))
+#define float8store(V,M) doublestore((V),(M))
+#endif /* __i386__ */
+
+#ifndef sint2korr
+/*
+ We're here if it's not a IA-32 architecture (Win32 and UNIX IA-32 defines
+ were done before)
+*/
+#define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\
+ ((int16) ((int16) (A)[1]) << 8))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\
+ (((int32) ((uchar) (A)[1]) << 8)) +\
+ (((int32) ((uchar) (A)[2]) << 16)) +\
+ (((int32) ((int16) (A)[3]) << 24)))
+#define sint8korr(A) (longlong) uint8korr(A)
+#define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\
+ ((uint16) ((uchar) (A)[1]) << 8))
+#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16))
+#define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24))
+#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 32))
+#define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) (((uint32) ((uchar) (A)[4])) +\
+ (((uint32) ((uchar) (A)[5])) << 8) +\
+ (((uint32) ((uchar) (A)[6])) << 16) +\
+ (((uint32) ((uchar) (A)[7])) << 24))) <<\
+ 32))
+#define int2store(T,A) do { uint def_temp= (uint) (A) ;\
+ *((uchar*) (T))= (uchar)(def_temp); \
+ *((uchar*) (T)+1)=(uchar)((def_temp >> 8)); \
+ } while(0)
+#define int3store(T,A) do { /*lint -save -e734 */\
+ *((uchar*)(T))=(uchar) ((A));\
+ *((uchar*) (T)+1)=(uchar) (((A) >> 8));\
+ *((uchar*)(T)+2)=(uchar) (((A) >> 16)); \
+ /*lint -restore */} while(0)
+#define int4store(T,A) do { *((char *)(T))=(char) ((A));\
+ *(((char *)(T))+1)=(char) (((A) >> 8));\
+ *(((char *)(T))+2)=(char) (((A) >> 16));\
+ *(((char *)(T))+3)=(char) (((A) >> 24)); } while(0)
+#define int5store(T,A) do { *((char *)(T))=((A));\
+ *(((char *)(T))+1)=(((A) >> 8));\
+ *(((char *)(T))+2)=(((A) >> 16));\
+ *(((char *)(T))+3)=(((A) >> 24)); \
+ *(((char *)(T))+4)=(((A) >> 32)); } while(0)
+#define int8store(T,A) do { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \
+ int4store((T),def_temp); \
+ int4store((T+4),def_temp2); } while(0)
+#ifdef WORDS_BIGENDIAN
+#define float4store(T,A) do { *(T)= ((byte *) &A)[3];\
+ *((T)+1)=(char) ((byte *) &A)[2];\
+ *((T)+2)=(char) ((byte *) &A)[1];\
+ *((T)+3)=(char) ((byte *) &A)[0]; } while(0)
+
+#define float4get(V,M) do { float def_temp;\
+ ((byte*) &def_temp)[0]=(M)[3];\
+ ((byte*) &def_temp)[1]=(M)[2];\
+ ((byte*) &def_temp)[2]=(M)[1];\
+ ((byte*) &def_temp)[3]=(M)[0];\
+ (V)=def_temp; } while(0)
+#define float8store(T,V) do { *(T)= ((byte *) &V)[7];\
+ *((T)+1)=(char) ((byte *) &V)[6];\
+ *((T)+2)=(char) ((byte *) &V)[5];\
+ *((T)+3)=(char) ((byte *) &V)[4];\
+ *((T)+4)=(char) ((byte *) &V)[3];\
+ *((T)+5)=(char) ((byte *) &V)[2];\
+ *((T)+6)=(char) ((byte *) &V)[1];\
+ *((T)+7)=(char) ((byte *) &V)[0]; } while(0)
+
+#define float8get(V,M) do { double def_temp;\
+ ((byte*) &def_temp)[0]=(M)[7];\
+ ((byte*) &def_temp)[1]=(M)[6];\
+ ((byte*) &def_temp)[2]=(M)[5];\
+ ((byte*) &def_temp)[3]=(M)[4];\
+ ((byte*) &def_temp)[4]=(M)[3];\
+ ((byte*) &def_temp)[5]=(M)[2];\
+ ((byte*) &def_temp)[6]=(M)[1];\
+ ((byte*) &def_temp)[7]=(M)[0];\
+ (V) = def_temp; } while(0)
+#else
+#define float4get(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(float))
+#define float4store(V,M) memcpy_fixed((byte*) V,(byte*) (&M),sizeof(float))
+
+#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
+#define doublestore(T,V) do { *(((char*)T)+0)=(char) ((byte *) &V)[4];\
+ *(((char*)T)+1)=(char) ((byte *) &V)[5];\
+ *(((char*)T)+2)=(char) ((byte *) &V)[6];\
+ *(((char*)T)+3)=(char) ((byte *) &V)[7];\
+ *(((char*)T)+4)=(char) ((byte *) &V)[0];\
+ *(((char*)T)+5)=(char) ((byte *) &V)[1];\
+ *(((char*)T)+6)=(char) ((byte *) &V)[2];\
+ *(((char*)T)+7)=(char) ((byte *) &V)[3]; }\
+ while(0)
+#define doubleget(V,M) do { double def_temp;\
+ ((byte*) &def_temp)[0]=(M)[4];\
+ ((byte*) &def_temp)[1]=(M)[5];\
+ ((byte*) &def_temp)[2]=(M)[6];\
+ ((byte*) &def_temp)[3]=(M)[7];\
+ ((byte*) &def_temp)[4]=(M)[0];\
+ ((byte*) &def_temp)[5]=(M)[1];\
+ ((byte*) &def_temp)[6]=(M)[2];\
+ ((byte*) &def_temp)[7]=(M)[3];\
+ (V) = def_temp; } while(0)
+#endif /* __FLOAT_WORD_ORDER */
+
+#define float8get(V,M) doubleget((V),(M))
+#define float8store(V,M) doublestore((V),(M))
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* sint2korr */
+
+/*
+ Macro for reading 32-bit integer from network byte order (big-endian)
+ from unaligned memory location.
+*/
+#define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) |\
+ (((uint32) ((uchar) (A)[2])) << 8) |\
+ (((uint32) ((uchar) (A)[1])) << 16) |\
+ (((uint32) ((uchar) (A)[0])) << 24))
+/*
+ Define-funktions for reading and storing in machine format from/to
+ short/long to/from some place in memory V should be a (not
+ register) variable, M is a pointer to byte
+*/
+
+#ifdef WORDS_BIGENDIAN
+
+#define ushortget(V,M) do { V = (uint16) (((uint16) ((uchar) (M)[1]))+\
+ ((uint16) ((uint16) (M)[0]) << 8)); } while(0)
+#define shortget(V,M) do { V = (short) (((short) ((uchar) (M)[1]))+\
+ ((short) ((short) (M)[0]) << 8)); } while(0)
+#define longget(V,M) do { int32 def_temp;\
+ ((byte*) &def_temp)[0]=(M)[0];\
+ ((byte*) &def_temp)[1]=(M)[1];\
+ ((byte*) &def_temp)[2]=(M)[2];\
+ ((byte*) &def_temp)[3]=(M)[3];\
+ (V)=def_temp; } while(0)
+#define ulongget(V,M) do { uint32 def_temp;\
+ ((byte*) &def_temp)[0]=(M)[0];\
+ ((byte*) &def_temp)[1]=(M)[1];\
+ ((byte*) &def_temp)[2]=(M)[2];\
+ ((byte*) &def_temp)[3]=(M)[3];\
+ (V)=def_temp; } while(0)
+#define shortstore(T,A) do { uint def_temp=(uint) (A) ;\
+ *(((char*)T)+1)=(char)(def_temp); \
+ *(((char*)T)+0)=(char)(def_temp >> 8); } while(0)
+#define longstore(T,A) do { *(((char*)T)+3)=((A));\
+ *(((char*)T)+2)=(((A) >> 8));\
+ *(((char*)T)+1)=(((A) >> 16));\
+ *(((char*)T)+0)=(((A) >> 24)); } while(0)
+
+#define floatget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(float))
+#define floatstore(T,V) memcpy_fixed((byte*) (T),(byte*)(&V),sizeof(float))
+#define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double))
+#define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double))
+#define longlongget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(ulonglong))
+#define longlongstore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(ulonglong))
+
+#else
+
+#define ushortget(V,M) do { V = uint2korr(M); } while(0)
+#define shortget(V,M) do { V = sint2korr(M); } while(0)
+#define longget(V,M) do { V = sint4korr(M); } while(0)
+#define ulongget(V,M) do { V = uint4korr(M); } while(0)
+#define shortstore(T,V) int2store(T,V)
+#define longstore(T,V) int4store(T,V)
+#ifndef floatstore
+#define floatstore(T,V) memcpy_fixed((byte*) (T),(byte*) (&V),sizeof(float))
+#define floatget(V,M) memcpy_fixed((byte*) &V, (byte*) (M), sizeof(float))
+#endif
+#ifndef doubleget
+#define doubleget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(double))
+#define doublestore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(double))
+#endif /* doubleget */
+#define longlongget(V,M) memcpy_fixed((byte*) &V,(byte*) (M),sizeof(ulonglong))
+#define longlongstore(T,V) memcpy_fixed((byte*) (T),(byte*) &V,sizeof(ulonglong))
+
+#endif /* WORDS_BIGENDIAN */
+
+/* sprintf does not always return the number of bytes :- */
+#ifdef SPRINTF_RETURNS_INT
+#define my_sprintf(buff,args) sprintf args
+#else
+#ifdef SPRINTF_RETURNS_PTR
+#define my_sprintf(buff,args) ((int)(sprintf args - buff))
+#else
+#define my_sprintf(buff,args) ((ulong) sprintf args, (ulong) strlen(buff))
+#endif
+#endif
+
+#ifndef THREAD
+#define thread_safe_increment(V,L) (V)++
+#define thread_safe_add(V,C,L) (V)+=(C)
+#define thread_safe_sub(V,C,L) (V)-=(C)
+#define statistic_increment(V,L) (V)++
+#define statistic_add(V,C,L) (V)+=(C)
+#endif
+
+#ifdef HAVE_CHARSET_utf8
+#define MYSQL_UNIVERSAL_CLIENT_CHARSET "utf8"
+#else
+#define MYSQL_UNIVERSAL_CLIENT_CHARSET MYSQL_DEFAULT_CHARSET_NAME
+#endif
+
+#if defined(EMBEDDED_LIBRARY) && !defined(HAVE_EMBEDDED_PRIVILEGE_CONTROL)
+#define NO_EMBEDDED_ACCESS_CHECKS
+#endif
+
+#endif /* my_global_h */
diff --git a/src/mysql/my_list.h b/src/mysql/my_list.h
new file mode 100644
index 000000000..20824e9ab
--- /dev/null
+++ b/src/mysql/my_list.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _list_h_
+#define _list_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct st_list {
+ struct st_list *prev,*next;
+ void *data;
+} LIST;
+
+typedef int (*list_walk_action)(void *,void *);
+
+extern LIST *list_add(LIST *root,LIST *element);
+extern LIST *list_delete(LIST *root,LIST *element);
+extern LIST *list_cons(void *data,LIST *root);
+extern LIST *list_reverse(LIST *root);
+extern void list_free(LIST *root,unsigned int free_data);
+extern unsigned int list_length(LIST *);
+extern int list_walk(LIST *,list_walk_action action,gptr argument);
+
+#define list_rest(a) ((a)->next)
+#define list_push(a,b) (a)=list_cons((b),(a))
+#define list_pop(A) {LIST *old=(A); (A)=list_delete(old,old) ; my_free((gptr) old,MYF(MY_FAE)); }
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/mysql/my_pthread.h b/src/mysql/my_pthread.h
new file mode 100644
index 000000000..36afd19d9
--- /dev/null
+++ b/src/mysql/my_pthread.h
@@ -0,0 +1,711 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Defines to make different thread packages compatible */
+
+#ifndef _my_pthread_h
+#define _my_pthread_h
+
+#include <errno.h>
+#ifndef ETIME
+#define ETIME ETIMEDOUT /* For FreeBSD */
+#endif
+
+#ifdef __cplusplus
+#define EXTERNC extern "C"
+extern "C" {
+#else
+#define EXTERNC
+#endif /* __cplusplus */
+
+#if defined(__WIN__) || defined(OS2)
+
+#ifdef OS2
+typedef ULONG HANDLE;
+typedef ULONG DWORD;
+typedef int sigset_t;
+#endif
+
+#ifdef OS2
+typedef HMTX pthread_mutex_t;
+#else
+typedef CRITICAL_SECTION pthread_mutex_t;
+#endif
+typedef HANDLE pthread_t;
+typedef struct thread_attr {
+ DWORD dwStackSize ;
+ DWORD dwCreatingFlag ;
+ int priority ;
+} pthread_attr_t ;
+
+typedef struct { int dummy; } pthread_condattr_t;
+
+/* Implementation of posix conditions */
+
+typedef struct st_pthread_link {
+ DWORD thread_id;
+ struct st_pthread_link *next;
+} pthread_link;
+
+typedef struct {
+ uint32 waiting;
+#ifdef OS2
+ HEV semaphore;
+#else
+ HANDLE semaphore;
+#endif
+} pthread_cond_t;
+
+
+#ifndef OS2
+struct timespec { /* For pthread_cond_timedwait() */
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+
+typedef int pthread_mutexattr_t;
+#define win_pthread_self my_thread_var->pthread_self
+#ifdef OS2
+#define pthread_handler_t EXTERNC void * _Optlink
+typedef void * (_Optlink *pthread_handler)(void *);
+#else
+#define pthread_handler_t EXTERNC void * __cdecl
+typedef void * (__cdecl *pthread_handler)(void *);
+#endif
+
+void win_pthread_init(void);
+int win_pthread_setspecific(void *A,void *B,uint length);
+int pthread_create(pthread_t *,pthread_attr_t *,pthread_handler,void *);
+int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ struct timespec *abstime);
+int pthread_cond_signal(pthread_cond_t *cond);
+int pthread_cond_broadcast(pthread_cond_t *cond);
+int pthread_cond_destroy(pthread_cond_t *cond);
+int pthread_attr_init(pthread_attr_t *connect_att);
+int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack);
+int pthread_attr_setprio(pthread_attr_t *connect_att,int priority);
+int pthread_attr_destroy(pthread_attr_t *connect_att);
+struct tm *localtime_r(const time_t *timep,struct tm *tmp);
+struct tm *gmtime_r(const time_t *timep,struct tm *tmp);
+
+
+void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/
+
+#ifndef OS2
+#define ETIMEDOUT 145 /* Win32 doesn't have this */
+#define getpid() GetCurrentThreadId()
+#endif
+#define pthread_self() win_pthread_self
+#define HAVE_LOCALTIME_R 1
+#define _REENTRANT 1
+#define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1
+
+#ifdef USE_TLS /* For LIBMYSQL.DLL */
+#undef SAFE_MUTEX /* This will cause conflicts */
+#define pthread_key(T,V) DWORD V
+#define pthread_key_create(A,B) ((*A=TlsAlloc())==0xFFFFFFFF)
+#define pthread_key_delete(A) TlsFree(A)
+#define pthread_getspecific(A) (TlsGetValue(A))
+#define my_pthread_getspecific(T,A) ((T) TlsGetValue(A))
+#define my_pthread_getspecific_ptr(T,V) ((T) TlsGetValue(V))
+#define my_pthread_setspecific_ptr(T,V) (!TlsSetValue((T),(V)))
+#define pthread_setspecific(A,B) (!TlsSetValue((A),(B)))
+#else
+#define pthread_key(T,V) __declspec(thread) T V
+#define pthread_key_create(A,B) pthread_dummy(0)
+#define pthread_key_delete(A) pthread_dummy(0)
+#define pthread_getspecific(A) (&(A))
+#define my_pthread_getspecific(T,A) (&(A))
+#define my_pthread_getspecific_ptr(T,V) (V)
+#define my_pthread_setspecific_ptr(T,V) ((T)=(V),0)
+#define pthread_setspecific(A,B) win_pthread_setspecific(&(A),(B),sizeof(A))
+#endif /* USE_TLS */
+
+#define pthread_equal(A,B) ((A) == (B))
+#ifdef OS2
+extern int pthread_mutex_init (pthread_mutex_t *, const pthread_mutexattr_t *);
+extern int pthread_mutex_lock (pthread_mutex_t *);
+extern int pthread_mutex_unlock (pthread_mutex_t *);
+extern int pthread_mutex_destroy (pthread_mutex_t *);
+#define my_pthread_setprio(A,B) DosSetPriority(PRTYS_THREAD,PRTYC_NOCHANGE, B, A)
+#define pthread_kill(A,B) raise(B)
+#define pthread_exit(A) pthread_dummy()
+#else
+#define pthread_mutex_init(A,B) (InitializeCriticalSection(A),0)
+#define pthread_mutex_lock(A) (EnterCriticalSection(A),0)
+#define pthread_mutex_trylock(A) (WaitForSingleObject((A), 0) == WAIT_TIMEOUT)
+#define pthread_mutex_unlock(A) LeaveCriticalSection(A)
+#define pthread_mutex_destroy(A) DeleteCriticalSection(A)
+#define my_pthread_setprio(A,B) SetThreadPriority(GetCurrentThread(), (B))
+#define pthread_kill(A,B) pthread_dummy(0)
+#endif /* OS2 */
+
+/* Dummy defines for easier code */
+#define pthread_attr_setdetachstate(A,B) pthread_dummy(0)
+#define my_pthread_attr_setprio(A,B) pthread_attr_setprio(A,B)
+#define pthread_attr_setscope(A,B)
+#define pthread_detach_this_thread()
+#define pthread_condattr_init(A)
+#define pthread_condattr_destroy(A)
+
+/*Irena: compiler does not like this: */
+/*#define my_pthread_getprio(pthread_t thread_id) pthread_dummy(0) */
+#define my_pthread_getprio(thread_id) pthread_dummy(0)
+
+#elif defined(HAVE_UNIXWARE7_THREADS)
+
+#include <thread.h>
+#include <synch.h>
+
+#ifndef _REENTRANT
+#define _REENTRANT
+#endif
+
+#define HAVE_NONPOSIX_SIGWAIT
+#define pthread_t thread_t
+#define pthread_cond_t cond_t
+#define pthread_mutex_t mutex_t
+#define pthread_key_t thread_key_t
+typedef int pthread_attr_t; /* Needed by Unixware 7.0.0 */
+
+#define pthread_key_create(A,B) thr_keycreate((A),(B))
+#define pthread_key_delete(A) thr_keydelete(A)
+
+#define pthread_handler_t EXTERNC void *
+#define pthread_key(T,V) pthread_key_t V
+
+void * my_pthread_getspecific_imp(pthread_key_t key);
+#define my_pthread_getspecific(A,B) ((A) my_pthread_getspecific_imp(B))
+#define my_pthread_getspecific_ptr(T,V) my_pthread_getspecific(T,V)
+
+#define pthread_setspecific(A,B) thr_setspecific(A,B)
+#define my_pthread_setspecific_ptr(T,V) pthread_setspecific(T,V)
+
+#define pthread_create(A,B,C,D) thr_create(NULL,65536L,(C),(D),THR_DETACHED,(A))
+#define pthread_cond_init(a,b) cond_init((a),USYNC_THREAD,NULL)
+#define pthread_cond_destroy(a) cond_destroy(a)
+#define pthread_cond_signal(a) cond_signal(a)
+#define pthread_cond_wait(a,b) cond_wait((a),(b))
+#define pthread_cond_timedwait(a,b,c) cond_timedwait((a),(b),(c))
+#define pthread_cond_broadcast(a) cond_broadcast(a)
+
+#define pthread_mutex_init(a,b) mutex_init((a),USYNC_THREAD,NULL)
+#define pthread_mutex_lock(a) mutex_lock(a)
+#define pthread_mutex_unlock(a) mutex_unlock(a)
+#define pthread_mutex_destroy(a) mutex_destroy(a)
+
+#define pthread_self() thr_self()
+#define pthread_exit(A) thr_exit(A)
+#define pthread_equal(A,B) (((A) == (B)) ? 1 : 0)
+#define pthread_kill(A,B) thr_kill((A),(B))
+#define HAVE_PTHREAD_KILL
+
+#define pthread_sigmask(A,B,C) thr_sigsetmask((A),(B),(C))
+
+extern int my_sigwait(const sigset_t *set,int *sig);
+
+#define pthread_detach_this_thread() pthread_dummy(0)
+
+#define pthread_attr_init(A) pthread_dummy(0)
+#define pthread_attr_destroy(A) pthread_dummy(0)
+#define pthread_attr_setscope(A,B) pthread_dummy(0)
+#define pthread_attr_setdetachstate(A,B) pthread_dummy(0)
+#define my_pthread_setprio(A,B) pthread_dummy (0)
+#define my_pthread_getprio(A) pthread_dummy (0)
+#define my_pthread_attr_setprio(A,B) pthread_dummy(0)
+
+#else /* Normal threads */
+
+#ifdef HAVE_rts_threads
+#define sigwait org_sigwait
+#include <signal.h>
+#undef sigwait
+#endif
+#include <pthread.h>
+#ifndef _REENTRANT
+#define _REENTRANT
+#endif
+#ifdef HAVE_THR_SETCONCURRENCY
+#include <thread.h> /* Probably solaris */
+#endif
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#if defined(__EMX__) && (!defined(EMX_PTHREAD_REV) || (EMX_PTHREAD_REV < 2))
+#error Requires at least rev 2 of EMX pthreads library.
+#endif
+
+#ifdef __NETWARE__
+void my_pthread_exit(void *status);
+#define pthread_exit(A) my_pthread_exit(A)
+#endif
+
+extern int my_pthread_getprio(pthread_t thread_id);
+
+#define pthread_key(T,V) pthread_key_t V
+#define my_pthread_getspecific_ptr(T,V) my_pthread_getspecific(T,(V))
+#define my_pthread_setspecific_ptr(T,V) pthread_setspecific(T,(void*) (V))
+#define pthread_detach_this_thread()
+#define pthread_handler_t EXTERNC void *
+typedef void *(* pthread_handler)(void *);
+
+/* Test first for RTS or FSU threads */
+
+#if defined(PTHREAD_SCOPE_GLOBAL) && !defined(PTHREAD_SCOPE_SYSTEM)
+#define HAVE_rts_threads
+extern int my_pthread_create_detached;
+#define pthread_sigmask(A,B,C) sigprocmask((A),(B),(C))
+#define PTHREAD_CREATE_DETACHED &my_pthread_create_detached
+#define PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_GLOBAL
+#define PTHREAD_SCOPE_PROCESS PTHREAD_SCOPE_LOCAL
+#define USE_ALARM_THREAD
+#elif defined(HAVE_mit_thread)
+#define USE_ALARM_THREAD
+#undef HAVE_LOCALTIME_R
+#define HAVE_LOCALTIME_R
+#undef HAVE_GMTIME_R
+#define HAVE_GMTIME_R
+#undef HAVE_PTHREAD_ATTR_SETSCOPE
+#define HAVE_PTHREAD_ATTR_SETSCOPE
+#undef HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE /* If we are running linux */
+#undef HAVE_RWLOCK_T
+#undef HAVE_RWLOCK_INIT
+#undef HAVE_PTHREAD_RWLOCK_RDLOCK
+#undef HAVE_SNPRINTF
+
+#define my_pthread_attr_setprio(A,B)
+#endif /* defined(PTHREAD_SCOPE_GLOBAL) && !defined(PTHREAD_SCOPE_SYSTEM) */
+
+#if defined(_BSDI_VERSION) && _BSDI_VERSION < 199910
+int sigwait(sigset_t *set, int *sig);
+#endif
+
+#ifndef HAVE_NONPOSIX_SIGWAIT
+#define my_sigwait(A,B) sigwait((A),(B))
+#else
+int my_sigwait(const sigset_t *set,int *sig);
+#endif
+
+#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT
+#ifndef SAFE_MUTEX
+#define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b))
+extern int my_pthread_mutex_init(pthread_mutex_t *mp,
+ const pthread_mutexattr_t *attr);
+#endif /* SAFE_MUTEX */
+#define pthread_cond_init(a,b) my_pthread_cond_init((a),(b))
+extern int my_pthread_cond_init(pthread_cond_t *mp,
+ const pthread_condattr_t *attr);
+#endif /* HAVE_NONPOSIX_PTHREAD_MUTEX_INIT */
+
+#if defined(HAVE_SIGTHREADMASK) && !defined(HAVE_PTHREAD_SIGMASK)
+#define pthread_sigmask(A,B,C) sigthreadmask((A),(B),(C))
+#endif
+
+#if !defined(HAVE_SIGWAIT) && !defined(HAVE_mit_thread) && !defined(HAVE_rts_threads) && !defined(sigwait) && !defined(alpha_linux_port) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) && !defined(_AIX)
+int sigwait(sigset_t *setp, int *sigp); /* Use our implemention */
+#endif
+
+
+/*
+ We define my_sigset() and use that instead of the system sigset() so that
+ we can favor an implementation based on sigaction(). On some systems, such
+ as Mac OS X, sigset() results in flags such as SA_RESTART being set, and
+ we want to make sure that no such flags are set.
+*/
+#if defined(HAVE_SIGACTION) && !defined(my_sigset)
+#define my_sigset(A,B) do { struct sigaction s; sigset_t set; \
+ sigemptyset(&set); \
+ s.sa_handler = (B); \
+ s.sa_mask = set; \
+ s.sa_flags = 0; \
+ sigaction((A), &s, (struct sigaction *) NULL); \
+ } while (0)
+#elif defined(HAVE_SIGSET) && !defined(my_sigset)
+#define my_sigset(A,B) sigset((A),(B))
+#elif !defined(my_sigset)
+#define my_sigset(A,B) signal((A),(B))
+#endif
+
+#ifndef my_pthread_setprio
+#if defined(HAVE_PTHREAD_SETPRIO_NP) /* FSU threads */
+#define my_pthread_setprio(A,B) pthread_setprio_np((A),(B))
+#elif defined(HAVE_PTHREAD_SETPRIO)
+#define my_pthread_setprio(A,B) pthread_setprio((A),(B))
+#else
+extern void my_pthread_setprio(pthread_t thread_id,int prior);
+#endif
+#endif
+
+#ifndef my_pthread_attr_setprio
+#ifdef HAVE_PTHREAD_ATTR_SETPRIO
+#define my_pthread_attr_setprio(A,B) pthread_attr_setprio((A),(B))
+#else
+extern void my_pthread_attr_setprio(pthread_attr_t *attr, int priority);
+#endif
+#endif
+
+#if !defined(HAVE_PTHREAD_ATTR_SETSCOPE) || defined(HAVE_DEC_3_2_THREADS)
+#define pthread_attr_setscope(A,B)
+#undef HAVE_GETHOSTBYADDR_R /* No definition */
+#endif
+
+#if defined(HAVE_BROKEN_PTHREAD_COND_TIMEDWAIT) && !defined(SAFE_MUTEX)
+extern int my_pthread_cond_timedwait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex,
+ struct timespec *abstime);
+#define pthread_cond_timedwait(A,B,C) my_pthread_cond_timedwait((A),(B),(C))
+#endif
+
+#if defined(OS2)
+#define my_pthread_getspecific(T,A) ((T) &(A))
+#define pthread_setspecific(A,B) win_pthread_setspecific(&(A),(B),sizeof(A))
+#elif !defined( HAVE_NONPOSIX_PTHREAD_GETSPECIFIC)
+#define my_pthread_getspecific(A,B) ((A) pthread_getspecific(B))
+#else
+#define my_pthread_getspecific(A,B) ((A) my_pthread_getspecific_imp(B))
+void *my_pthread_getspecific_imp(pthread_key_t key);
+#endif /* OS2 */
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *clock, struct tm *res);
+#endif
+
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *clock, struct tm *res);
+#endif
+
+#ifdef HAVE_PTHREAD_CONDATTR_CREATE
+/* DCE threads on HPUX 10.20 */
+#define pthread_condattr_init pthread_condattr_create
+#define pthread_condattr_destroy pthread_condattr_delete
+#endif
+
+/* FSU THREADS */
+#if !defined(HAVE_PTHREAD_KEY_DELETE) && !defined(pthread_key_delete)
+#define pthread_key_delete(A) pthread_dummy(0)
+#endif
+
+#ifdef HAVE_CTHREADS_WRAPPER /* For MacOSX */
+#define pthread_cond_destroy(A) pthread_dummy(0)
+#define pthread_mutex_destroy(A) pthread_dummy(0)
+#define pthread_attr_delete(A) pthread_dummy(0)
+#define pthread_condattr_delete(A) pthread_dummy(0)
+#define pthread_attr_setstacksize(A,B) pthread_dummy(0)
+#define pthread_equal(A,B) ((A) == (B))
+#define pthread_cond_timedwait(a,b,c) pthread_cond_wait((a),(b))
+#define pthread_attr_init(A) pthread_attr_create(A)
+#define pthread_attr_destroy(A) pthread_attr_delete(A)
+#define pthread_attr_setdetachstate(A,B) pthread_dummy(0)
+#define pthread_create(A,B,C,D) pthread_create((A),*(B),(C),(D))
+#define pthread_sigmask(A,B,C) sigprocmask((A),(B),(C))
+#define pthread_kill(A,B) pthread_dummy(0)
+#undef pthread_detach_this_thread
+#define pthread_detach_this_thread() { pthread_t tmp=pthread_self() ; pthread_detach(&tmp); }
+#endif
+
+#ifdef HAVE_DARWIN5_THREADS
+#define pthread_sigmask(A,B,C) sigprocmask((A),(B),(C))
+#define pthread_kill(A,B) pthread_dummy(0)
+#define pthread_condattr_init(A) pthread_dummy(0)
+#define pthread_condattr_destroy(A) pthread_dummy(0)
+#undef pthread_detach_this_thread
+#define pthread_detach_this_thread() { pthread_t tmp=pthread_self() ; pthread_detach(tmp); }
+#endif
+
+#if ((defined(HAVE_PTHREAD_ATTR_CREATE) && !defined(HAVE_SIGWAIT)) || defined(HAVE_DEC_3_2_THREADS)) && !defined(HAVE_CTHREADS_WRAPPER)
+/* This is set on AIX_3_2 and Siemens unix (and DEC OSF/1 3.2 too) */
+#define pthread_key_create(A,B) \
+ pthread_keycreate(A,(B) ?\
+ (pthread_destructor_t) (B) :\
+ (pthread_destructor_t) pthread_dummy)
+#define pthread_attr_init(A) pthread_attr_create(A)
+#define pthread_attr_destroy(A) pthread_attr_delete(A)
+#define pthread_attr_setdetachstate(A,B) pthread_dummy(0)
+#define pthread_create(A,B,C,D) pthread_create((A),*(B),(C),(D))
+#ifndef pthread_sigmask
+#define pthread_sigmask(A,B,C) sigprocmask((A),(B),(C))
+#endif
+#define pthread_kill(A,B) pthread_dummy(0)
+#undef pthread_detach_this_thread
+#define pthread_detach_this_thread() { pthread_t tmp=pthread_self() ; pthread_detach(&tmp); }
+#elif !defined(__NETWARE__) /* HAVE_PTHREAD_ATTR_CREATE && !HAVE_SIGWAIT */
+#define HAVE_PTHREAD_KILL
+#endif
+
+#endif /* defined(__WIN__) */
+
+#if defined(HPUX10) && !defined(DONT_REMAP_PTHREAD_FUNCTIONS)
+#undef pthread_cond_timedwait
+#define pthread_cond_timedwait(a,b,c) my_pthread_cond_timedwait((a),(b),(c))
+int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ struct timespec *abstime);
+#endif
+
+#if defined(HPUX10)
+#define pthread_attr_getstacksize(A,B) my_pthread_attr_getstacksize(A,B)
+void my_pthread_attr_getstacksize(pthread_attr_t *attrib, size_t *size);
+#endif
+
+#if defined(HAVE_POSIX1003_4a_MUTEX) && !defined(DONT_REMAP_PTHREAD_FUNCTIONS)
+#undef pthread_mutex_trylock
+#define pthread_mutex_trylock(a) my_pthread_mutex_trylock((a))
+int my_pthread_mutex_trylock(pthread_mutex_t *mutex);
+#endif
+
+ /* safe_mutex adds checking to mutex for easier debugging */
+
+#if defined(__NETWARE__) && !defined(SAFE_MUTEX_DETECT_DESTROY)
+#define SAFE_MUTEX_DETECT_DESTROY
+#endif
+
+typedef struct st_safe_mutex_t
+{
+ pthread_mutex_t global,mutex;
+ const char *file;
+ uint line,count;
+ pthread_t thread;
+#ifdef SAFE_MUTEX_DETECT_DESTROY
+ struct st_safe_mutex_info_t *info; /* to track destroying of mutexes */
+#endif
+} safe_mutex_t;
+
+#ifdef SAFE_MUTEX_DETECT_DESTROY
+/*
+ Used to track the destroying of mutexes. This needs to be a seperate
+ structure because the safe_mutex_t structure could be freed before
+ the mutexes are destroyed.
+*/
+
+typedef struct st_safe_mutex_info_t
+{
+ struct st_safe_mutex_info_t *next;
+ struct st_safe_mutex_info_t *prev;
+ const char *init_file;
+ uint32 init_line;
+} safe_mutex_info_t;
+#endif /* SAFE_MUTEX_DETECT_DESTROY */
+
+int safe_mutex_init(safe_mutex_t *mp, const pthread_mutexattr_t *attr,
+ const char *file, uint line);
+int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line);
+int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line);
+int safe_mutex_destroy(safe_mutex_t *mp,const char *file, uint line);
+int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp,const char *file,
+ uint line);
+int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
+ struct timespec *abstime, const char *file, uint line);
+void safe_mutex_global_init(void);
+void safe_mutex_end(FILE *file);
+
+ /* Wrappers if safe mutex is actually used */
+#ifdef SAFE_MUTEX
+#undef pthread_mutex_init
+#undef pthread_mutex_lock
+#undef pthread_mutex_unlock
+#undef pthread_mutex_destroy
+#undef pthread_mutex_wait
+#undef pthread_mutex_timedwait
+#undef pthread_mutex_t
+#undef pthread_cond_wait
+#undef pthread_cond_timedwait
+#undef pthread_mutex_trylock
+#define pthread_mutex_init(A,B) safe_mutex_init((A),(B),__FILE__,__LINE__)
+#define pthread_mutex_lock(A) safe_mutex_lock((A),__FILE__,__LINE__)
+#define pthread_mutex_unlock(A) safe_mutex_unlock((A),__FILE__,__LINE__)
+#define pthread_mutex_destroy(A) safe_mutex_destroy((A),__FILE__,__LINE__)
+#define pthread_cond_wait(A,B) safe_cond_wait((A),(B),__FILE__,__LINE__)
+#define pthread_cond_timedwait(A,B,C) safe_cond_timedwait((A),(B),(C),__FILE__,__LINE__)
+#define pthread_mutex_trylock(A) pthread_mutex_lock(A)
+#define pthread_mutex_t safe_mutex_t
+#define safe_mutex_assert_owner(mp) DBUG_ASSERT((mp)->count > 0 && pthread_equal(pthread_self(),(mp)->thread))
+#else
+#define safe_mutex_assert_owner(mp)
+#endif /* SAFE_MUTEX */
+
+ /* READ-WRITE thread locking */
+
+#ifdef HAVE_BROKEN_RWLOCK /* For OpenUnix */
+#undef HAVE_PTHREAD_RWLOCK_RDLOCK
+#undef HAVE_RWLOCK_INIT
+#undef HAVE_RWLOCK_T
+#endif
+
+#if defined(USE_MUTEX_INSTEAD_OF_RW_LOCKS)
+/* use these defs for simple mutex locking */
+#define rw_lock_t pthread_mutex_t
+#define my_rwlock_init(A,B) pthread_mutex_init((A),(B))
+#define rw_rdlock(A) pthread_mutex_lock((A))
+#define rw_wrlock(A) pthread_mutex_lock((A))
+#define rw_tryrdlock(A) pthread_mutex_trylock((A))
+#define rw_trywrlock(A) pthread_mutex_trylock((A))
+#define rw_unlock(A) pthread_mutex_unlock((A))
+#define rwlock_destroy(A) pthread_mutex_destroy((A))
+#elif defined(HAVE_PTHREAD_RWLOCK_RDLOCK)
+#define rw_lock_t pthread_rwlock_t
+#define my_rwlock_init(A,B) pthread_rwlock_init((A),(B))
+#define rw_rdlock(A) pthread_rwlock_rdlock(A)
+#define rw_wrlock(A) pthread_rwlock_wrlock(A)
+#define rw_tryrdlock(A) pthread_rwlock_tryrdlock((A))
+#define rw_trywrlock(A) pthread_rwlock_trywrlock((A))
+#define rw_unlock(A) pthread_rwlock_unlock(A)
+#define rwlock_destroy(A) pthread_rwlock_destroy(A)
+#elif defined(HAVE_RWLOCK_INIT)
+#ifdef HAVE_RWLOCK_T /* For example Solaris 2.6-> */
+#define rw_lock_t rwlock_t
+#endif
+#define my_rwlock_init(A,B) rwlock_init((A),USYNC_THREAD,0)
+#else
+/* Use our own version of read/write locks */
+typedef struct _my_rw_lock_t {
+ pthread_mutex_t lock; /* lock for structure */
+ pthread_cond_t readers; /* waiting readers */
+ pthread_cond_t writers; /* waiting writers */
+ int state; /* -1:writer,0:free,>0:readers */
+ int waiters; /* number of waiting writers */
+} my_rw_lock_t;
+
+#define rw_lock_t my_rw_lock_t
+#define rw_rdlock(A) my_rw_rdlock((A))
+#define rw_wrlock(A) my_rw_wrlock((A))
+#define rw_tryrdlock(A) my_rw_tryrdlock((A))
+#define rw_trywrlock(A) my_rw_trywrlock((A))
+#define rw_unlock(A) my_rw_unlock((A))
+#define rwlock_destroy(A) my_rwlock_destroy((A))
+
+extern int my_rwlock_init(my_rw_lock_t *, void *);
+extern int my_rwlock_destroy(my_rw_lock_t *);
+extern int my_rw_rdlock(my_rw_lock_t *);
+extern int my_rw_wrlock(my_rw_lock_t *);
+extern int my_rw_unlock(my_rw_lock_t *);
+extern int my_rw_tryrdlock(my_rw_lock_t *);
+extern int my_rw_trywrlock(my_rw_lock_t *);
+#endif /* USE_MUTEX_INSTEAD_OF_RW_LOCKS */
+
+#define GETHOSTBYADDR_BUFF_SIZE 2048
+
+#ifndef HAVE_THR_SETCONCURRENCY
+#define thr_setconcurrency(A) pthread_dummy(0)
+#endif
+#if !defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) && ! defined(pthread_attr_setstacksize)
+#define pthread_attr_setstacksize(A,B) pthread_dummy(0)
+#endif
+
+/* Define mutex types, see my_thr_init.c */
+#define MY_MUTEX_INIT_SLOW NULL
+#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+extern pthread_mutexattr_t my_fast_mutexattr;
+#define MY_MUTEX_INIT_FAST &my_fast_mutexattr
+#else
+#define MY_MUTEX_INIT_FAST NULL
+#endif
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+extern pthread_mutexattr_t my_errorcheck_mutexattr;
+#define MY_MUTEX_INIT_ERRCHK &my_errorcheck_mutexattr
+#else
+#define MY_MUTEX_INIT_ERRCHK NULL
+#endif
+
+extern my_bool my_thread_global_init(void);
+extern void my_thread_global_end(void);
+extern my_bool my_thread_init(void);
+extern void my_thread_end(void);
+extern const char *my_thread_name(void);
+extern long my_thread_id(void);
+extern int pthread_no_free(void *);
+extern int pthread_dummy(int);
+
+/* All thread specific variables are in the following struct */
+
+#define THREAD_NAME_SIZE 10
+#ifndef DEFAULT_THREAD_STACK
+#if defined(__ia64__)
+/*
+ MySQL can survive with 32K, but some glibc libraries require > 128K stack
+ To resolve hostnames
+*/
+#define DEFAULT_THREAD_STACK (256*1024L)
+#else
+#define DEFAULT_THREAD_STACK (192*1024)
+#endif
+#endif
+
+struct st_my_thread_var
+{
+ int thr_errno;
+ pthread_cond_t suspend;
+ pthread_mutex_t mutex;
+ pthread_mutex_t * volatile current_mutex;
+ pthread_cond_t * volatile current_cond;
+ pthread_t pthread_self;
+ long id;
+ int cmp_length;
+ int volatile abort;
+ my_bool init;
+ struct st_my_thread_var *next,**prev;
+ void *opt_info;
+#ifndef DBUG_OFF
+ gptr dbug;
+ char name[THREAD_NAME_SIZE+1];
+#endif
+};
+
+extern struct st_my_thread_var *_my_thread_var(void) __attribute__ ((const));
+#define my_thread_var (_my_thread_var())
+#define my_errno my_thread_var->thr_errno
+/*
+ Keep track of shutdown,signal, and main threads so that my_end() will not
+ report errors with them
+*/
+extern pthread_t shutdown_th, main_th, signal_th;
+
+ /* statistics_xxx functions are for not essential statistic */
+
+#ifndef thread_safe_increment
+#ifdef HAVE_ATOMIC_ADD
+#define thread_safe_increment(V,L) atomic_inc((atomic_t*) &V)
+#define thread_safe_decrement(V,L) atomic_dec((atomic_t*) &V)
+#define thread_safe_add(V,C,L) atomic_add((C),(atomic_t*) &V)
+#define thread_safe_sub(V,C,L) atomic_sub((C),(atomic_t*) &V)
+#else
+#define thread_safe_increment(V,L) \
+ (pthread_mutex_lock((L)), (V)++, pthread_mutex_unlock((L)))
+#define thread_safe_decrement(V,L) \
+ (pthread_mutex_lock((L)), (V)--, pthread_mutex_unlock((L)))
+#define thread_safe_add(V,C,L) (pthread_mutex_lock((L)), (V)+=(C), pthread_mutex_unlock((L)))
+#define thread_safe_sub(V,C,L) \
+ (pthread_mutex_lock((L)), (V)-=(C), pthread_mutex_unlock((L)))
+#endif /* HAVE_ATOMIC_ADD */
+#ifdef SAFE_STATISTICS
+#define statistic_increment(V,L) thread_safe_increment((V),(L))
+#define statistic_decrement(V,L) thread_safe_decrement((V),(L))
+#define statistic_add(V,C,L) thread_safe_add((V),(C),(L))
+#else
+#define statistic_decrement(V,L) (V)--
+#define statistic_increment(V,L) (V)++
+#define statistic_add(V,C,L) (V)+=(C)
+#endif /* SAFE_STATISTICS */
+#endif /* thread_safe_increment */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _my_ptread_h */
diff --git a/src/mysql/my_sys.h b/src/mysql/my_sys.h
new file mode 100644
index 000000000..83de59990
--- /dev/null
+++ b/src/mysql/my_sys.h
@@ -0,0 +1,904 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _my_sys_h
+#define _my_sys_h
+C_MODE_START
+
+#ifdef HAVE_AIOWAIT
+#include <sys/asynch.h> /* Used by record-cache */
+typedef struct my_aio_result {
+ aio_result_t result;
+ int pending;
+} my_aio_result;
+#endif
+
+#ifndef THREAD
+extern int NEAR my_errno; /* Last error in mysys */
+#else
+#include <my_pthread.h>
+#endif
+
+#ifndef _m_ctype_h
+#include <m_ctype.h> /* for CHARSET_INFO */
+#endif
+
+#include <stdarg.h>
+#include <typelib.h>
+
+#define MYSYS_PROGRAM_USES_CURSES() { error_handler_hook = my_message_curses; mysys_uses_curses=1; }
+#define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;}
+#define MY_INIT(name); { my_progname= name; my_init(); }
+
+#define ERRMSGSIZE (SC_MAXWIDTH) /* Max length of a error message */
+#define NRERRBUFFS (2) /* Buffers for parameters */
+#define MY_FILE_ERROR ((uint) ~0)
+
+ /* General bitmaps for my_func's */
+#define MY_FFNF 1 /* Fatal if file not found */
+#define MY_FNABP 2 /* Fatal if not all bytes read/writen */
+#define MY_NABP 4 /* Error if not all bytes read/writen */
+#define MY_FAE 8 /* Fatal if any error */
+#define MY_WME 16 /* Write message on error */
+#define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */
+#define MY_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */
+#define MY_RAID 64 /* Support for RAID */
+#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */
+#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */
+#define MY_LINK_WARNING 32 /* my_redel() gives warning if links */
+#define MY_COPYTIME 64 /* my_redel() copys time */
+#define MY_DELETE_OLD 256 /* my_create_with_symlink() */
+#define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */
+#define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */
+#define MY_REDEL_MAKE_BACKUP 256
+#define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */
+#define MY_DONT_WAIT 64 /* my_lock() don't wait if can't lock */
+#define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */
+#define MY_ALLOW_ZERO_PTR 64 /* my_realloc() ; zero ptr -> malloc */
+#define MY_FREE_ON_ERROR 128 /* my_realloc() ; Free old ptr on error */
+#define MY_HOLD_ON_ERROR 256 /* my_realloc() ; Return old ptr on error */
+#define MY_THREADSAFE 128 /* pread/pwrite: Don't allow interrupts */
+#define MY_DONT_OVERWRITE_FILE 1024 /* my_copy: Don't overwrite file */
+
+#define MY_CHECK_ERROR 1 /* Params to my_end; Check open-close */
+#define MY_GIVE_INFO 2 /* Give time info about process*/
+
+#define ME_HIGHBYTE 8 /* Shift for colours */
+#define ME_NOCUR 1 /* Don't use curses message */
+#define ME_OLDWIN 2 /* Use old window */
+#define ME_BELL 4 /* Ring bell then printing message */
+#define ME_HOLDTANG 8 /* Don't delete last keys */
+#define ME_WAITTOT 16 /* Wait for errtime secs of for a action */
+#define ME_WAITTANG 32 /* Wait for a user action */
+#define ME_NOREFRESH 64 /* Dont refresh screen */
+#define ME_NOINPUT 128 /* Dont use the input libary */
+#define ME_COLOUR1 ((1 << ME_HIGHBYTE)) /* Possibly error-colours */
+#define ME_COLOUR2 ((2 << ME_HIGHBYTE))
+#define ME_COLOUR3 ((3 << ME_HIGHBYTE))
+
+ /* Bits in last argument to fn_format */
+#define MY_REPLACE_DIR 1 /* replace dir in name with 'dir' */
+#define MY_REPLACE_EXT 2 /* replace extension with 'ext' */
+#define MY_UNPACK_FILENAME 4 /* Unpack name (~ -> home) */
+#define MY_PACK_FILENAME 8 /* Pack name (home -> ~) */
+#define MY_RESOLVE_SYMLINKS 16 /* Resolve all symbolic links */
+#define MY_RETURN_REAL_PATH 32 /* return full path for file */
+#define MY_SAFE_PATH 64 /* Return NULL if too long path */
+#define MY_RELATIVE_PATH 128 /* name is relative to 'dir' */
+
+ /* My seek flags */
+#define MY_SEEK_SET 0
+#define MY_SEEK_CUR 1
+#define MY_SEEK_END 2
+
+ /* Some constants */
+#define MY_WAIT_FOR_USER_TO_FIX_PANIC 60 /* in seconds */
+#define MY_WAIT_GIVE_USER_A_MESSAGE 10 /* Every 10 times of prev */
+#define MIN_COMPRESS_LENGTH 50 /* Don't compress small bl. */
+#define DFLT_INIT_HITS 3
+
+ /* root_alloc flags */
+#define MY_KEEP_PREALLOC 1
+#define MY_MARK_BLOCKS_FREE 2 /* move used to free list and reuse them */
+
+ /* Internal error numbers (for assembler functions) */
+#define MY_ERRNO_EDOM 33
+#define MY_ERRNO_ERANGE 34
+
+ /* Bits for get_date timeflag */
+#define GETDATE_DATE_TIME 1
+#define GETDATE_SHORT_DATE 2
+#define GETDATE_HHMMSSTIME 4
+#define GETDATE_GMT 8
+#define GETDATE_FIXEDLENGTH 16
+
+ /* defines when allocating data */
+#ifdef SAFEMALLOC
+#define my_malloc(SZ,FLAG) _mymalloc((SZ), __FILE__, __LINE__, FLAG )
+#define my_malloc_ci(SZ,FLAG) _mymalloc((SZ), sFile, uLine, FLAG )
+#define my_realloc(PTR,SZ,FLAG) _myrealloc((PTR), (SZ), __FILE__, __LINE__, FLAG )
+#define my_checkmalloc() _sanity( __FILE__, __LINE__ )
+#define my_free(PTR,FLAG) _myfree((PTR), __FILE__, __LINE__,FLAG)
+#define my_memdup(A,B,C) _my_memdup((A),(B), __FILE__,__LINE__,C)
+#define my_strdup(A,C) _my_strdup((A), __FILE__,__LINE__,C)
+#define my_strdup_with_length(A,B,C) _my_strdup_with_length((A),(B),__FILE__,__LINE__,C)
+#define TRASH(A,B) bfill(A, B, 0x8F)
+#define QUICK_SAFEMALLOC sf_malloc_quick=1
+#define NORMAL_SAFEMALLOC sf_malloc_quick=0
+extern uint sf_malloc_prehunc,sf_malloc_endhunc,sf_malloc_quick;
+extern ulonglong sf_malloc_mem_limit;
+
+#define CALLER_INFO_PROTO , const char *sFile, uint uLine
+#define CALLER_INFO , __FILE__, __LINE__
+#define ORIG_CALLER_INFO , sFile, uLine
+#else
+#define my_checkmalloc()
+#undef TERMINATE
+#define TERMINATE(A) {}
+#define QUICK_SAFEMALLOC
+#define NORMAL_SAFEMALLOC
+extern gptr my_malloc(uint Size,myf MyFlags);
+#define my_malloc_ci(SZ,FLAG) my_malloc( SZ, FLAG )
+extern gptr my_realloc(gptr oldpoint,uint Size,myf MyFlags);
+extern void my_no_flags_free(gptr ptr);
+extern gptr my_memdup(const byte *from,uint length,myf MyFlags);
+extern char *my_strdup(const char *from,myf MyFlags);
+extern char *my_strdup_with_length(const byte *from, uint length,
+ myf MyFlags);
+/* we do use FG (as a no-op) in below so that a typo on FG is caught */
+#define my_free(PTR,FG) ((void)FG,my_no_flags_free(PTR))
+#define CALLER_INFO_PROTO /* nothing */
+#define CALLER_INFO /* nothing */
+#define ORIG_CALLER_INFO /* nothing */
+#define TRASH(A,B) /* nothing */
+#endif
+
+#ifdef HAVE_LARGE_PAGES
+extern uint my_get_large_page_size(void);
+extern gptr my_large_malloc(uint size, myf my_flags);
+extern void my_large_free(gptr ptr, myf my_flags);
+#else
+#define my_get_large_page_size() (0)
+#define my_large_malloc(A,B) my_malloc_lock((A),(B))
+#define my_large_free(A,B) my_free_lock((A),(B))
+#endif /* HAVE_LARGE_PAGES */
+
+#ifdef HAVE_ALLOCA
+#if defined(_AIX) && !defined(__GNUC__) && !defined(_AIX43)
+#pragma alloca
+#endif /* _AIX */
+#if defined(__MWERKS__)
+#undef alloca
+#define alloca _alloca
+#endif /* __MWERKS__ */
+#if defined(__GNUC__) && !defined(HAVE_ALLOCA_H) && ! defined(alloca)
+#define alloca __builtin_alloca
+#endif /* GNUC */
+#define my_alloca(SZ) alloca((size_t) (SZ))
+#define my_afree(PTR) {}
+#else
+#define my_alloca(SZ) my_malloc(SZ,MYF(0))
+#define my_afree(PTR) my_free(PTR,MYF(MY_WME))
+#endif /* HAVE_ALLOCA */
+
+#ifdef MSDOS
+#ifdef __ZTC__
+void * __CDECL halloc(long count,size_t length);
+void __CDECL hfree(void *ptr);
+#endif
+#if defined(USE_HALLOC)
+#if defined(_VCM_) || defined(M_IC80386)
+#undef USE_HALLOC
+#endif
+#endif
+#ifdef USE_HALLOC
+#define malloc(a) halloc((long) (a),1)
+#define free(a) hfree(a)
+#endif
+#endif /* MSDOS */
+
+#ifndef errno /* did we already get it? */
+#ifdef HAVE_ERRNO_AS_DEFINE
+#include <errno.h> /* errno is a define */
+#else
+extern int errno; /* declare errno */
+#endif
+#endif /* #ifndef errno */
+extern char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
+extern char *home_dir; /* Home directory for user */
+extern const char *my_progname; /* program-name (printed in errors) */
+extern char NEAR curr_dir[]; /* Current directory for user */
+extern int (*error_handler_hook)(uint my_err, const char *str,myf MyFlags);
+extern int (*fatal_error_handler_hook)(uint my_err, const char *str,
+ myf MyFlags);
+extern uint my_file_limit;
+
+#ifdef HAVE_LARGE_PAGES
+extern my_bool my_use_large_pages;
+extern uint my_large_page_size;
+#endif
+
+/* charsets */
+extern CHARSET_INFO *default_charset_info;
+extern CHARSET_INFO *all_charsets[256];
+extern CHARSET_INFO compiled_charsets[];
+
+/* statistics */
+extern ulong my_file_opened,my_stream_opened, my_tmp_file_created;
+extern uint mysys_usage_id;
+extern my_bool my_init_done;
+
+ /* Point to current my_message() */
+extern void (*my_sigtstp_cleanup)(void),
+ /* Executed before jump to shell */
+ (*my_sigtstp_restart)(void),
+ (*my_abort_hook)(int);
+ /* Executed when comming from shell */
+extern int NEAR my_umask, /* Default creation mask */
+ NEAR my_umask_dir,
+ NEAR my_recived_signals, /* Signals we have got */
+ NEAR my_safe_to_handle_signal, /* Set when allowed to SIGTSTP */
+ NEAR my_dont_interrupt; /* call remember_intr when set */
+extern my_bool NEAR mysys_uses_curses, my_use_symdir;
+extern ulong sf_malloc_cur_memory, sf_malloc_max_memory;
+
+extern ulong my_default_record_cache_size;
+extern my_bool NEAR my_disable_locking,NEAR my_disable_async_io,
+ NEAR my_disable_flush_key_blocks, NEAR my_disable_symlinks;
+extern char wild_many,wild_one,wild_prefix;
+extern const char *charsets_dir;
+extern char *defaults_extra_file;
+extern const char *defaults_group_suffix;
+extern const char *defaults_file;
+
+extern my_bool timed_mutexes;
+
+typedef struct wild_file_pack /* Struct to hold info when selecting files */
+{
+ uint wilds; /* How many wildcards */
+ uint not_pos; /* Start of not-theese-files */
+ my_string *wild; /* Pointer to wildcards */
+} WF_PACK;
+
+enum loglevel {
+ ERROR_LEVEL,
+ WARNING_LEVEL,
+ INFORMATION_LEVEL
+};
+
+enum cache_type
+{
+ TYPE_NOT_SET= 0, READ_CACHE, WRITE_CACHE,
+ SEQ_READ_APPEND /* sequential read or append */,
+ READ_FIFO, READ_NET,WRITE_NET};
+
+enum flush_type
+{
+ FLUSH_KEEP, FLUSH_RELEASE, FLUSH_IGNORE_CHANGED, FLUSH_FORCE_WRITE
+};
+
+typedef struct st_record_cache /* Used when cacheing records */
+{
+ File file;
+ int rc_seek,error,inited;
+ uint rc_length,read_length,reclength;
+ my_off_t rc_record_pos,end_of_file;
+ byte *rc_buff,*rc_buff2,*rc_pos,*rc_end,*rc_request_pos;
+#ifdef HAVE_AIOWAIT
+ int use_async_io;
+ my_aio_result aio_result;
+#endif
+ enum cache_type type;
+} RECORD_CACHE;
+
+enum file_type
+{
+ UNOPEN = 0, FILE_BY_OPEN, FILE_BY_CREATE, STREAM_BY_FOPEN, STREAM_BY_FDOPEN,
+ FILE_BY_MKSTEMP, FILE_BY_DUP
+};
+
+struct st_my_file_info
+{
+ my_string name;
+ enum file_type type;
+#if defined(THREAD) && !defined(HAVE_PREAD)
+ pthread_mutex_t mutex;
+#endif
+};
+
+extern struct st_my_file_info *my_file_info;
+
+typedef struct st_my_tmpdir
+{
+ char **list;
+ uint cur, max;
+#ifdef THREAD
+ pthread_mutex_t mutex;
+#endif
+} MY_TMPDIR;
+
+typedef struct st_dynamic_array
+{
+ char *buffer;
+ uint elements,max_element;
+ uint alloc_increment;
+ uint size_of_element;
+} DYNAMIC_ARRAY;
+
+typedef struct st_dynamic_string
+{
+ char *str;
+ uint length,max_length,alloc_increment;
+} DYNAMIC_STRING;
+
+struct st_io_cache;
+typedef int (*IO_CACHE_CALLBACK)(struct st_io_cache*);
+
+#ifdef THREAD
+typedef struct st_io_cache_share
+{
+ /* to sync on reads into buffer */
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int count, total;
+ /* actual IO_CACHE that filled the buffer */
+ struct st_io_cache *active;
+#ifdef NOT_YET_IMPLEMENTED
+ /* whether the structure should be free'd */
+ my_bool alloced;
+#endif
+} IO_CACHE_SHARE;
+#endif
+
+typedef struct st_io_cache /* Used when cacheing files */
+{
+ /* Offset in file corresponding to the first byte of byte* buffer. */
+ my_off_t pos_in_file;
+ /*
+ The offset of end of file for READ_CACHE and WRITE_CACHE.
+ For SEQ_READ_APPEND it the maximum of the actual end of file and
+ the position represented by read_end.
+ */
+ my_off_t end_of_file;
+ /* Points to current read position in the buffer */
+ byte *read_pos;
+ /* the non-inclusive boundary in the buffer for the currently valid read */
+ byte *read_end;
+ byte *buffer; /* The read buffer */
+ /* Used in ASYNC_IO */
+ byte *request_pos;
+
+ /* Only used in WRITE caches and in SEQ_READ_APPEND to buffer writes */
+ byte *write_buffer;
+ /*
+ Only used in SEQ_READ_APPEND, and points to the current read position
+ in the write buffer. Note that reads in SEQ_READ_APPEND caches can
+ happen from both read buffer (byte* buffer) and write buffer
+ (byte* write_buffer).
+ */
+ byte *append_read_pos;
+ /* Points to current write position in the write buffer */
+ byte *write_pos;
+ /* The non-inclusive boundary of the valid write area */
+ byte *write_end;
+
+ /*
+ Current_pos and current_end are convenience variables used by
+ my_b_tell() and other routines that need to know the current offset
+ current_pos points to &write_pos, and current_end to &write_end in a
+ WRITE_CACHE, and &read_pos and &read_end respectively otherwise
+ */
+ byte **current_pos, **current_end;
+#ifdef THREAD
+ /*
+ The lock is for append buffer used in SEQ_READ_APPEND cache
+ need mutex copying from append buffer to read buffer.
+ */
+ pthread_mutex_t append_buffer_lock;
+ /*
+ The following is used when several threads are reading the
+ same file in parallel. They are synchronized on disk
+ accesses reading the cached part of the file asynchronously.
+ It should be set to NULL to disable the feature. Only
+ READ_CACHE mode is supported.
+ */
+ IO_CACHE_SHARE *share;
+#endif
+ /*
+ A caller will use my_b_read() macro to read from the cache
+ if the data is already in cache, it will be simply copied with
+ memcpy() and internal variables will be accordinging updated with
+ no functions invoked. However, if the data is not fully in the cache,
+ my_b_read() will call read_function to fetch the data. read_function
+ must never be invoked directly.
+ */
+ int (*read_function)(struct st_io_cache *,byte *,uint);
+ /*
+ Same idea as in the case of read_function, except my_b_write() needs to
+ be replaced with my_b_append() for a SEQ_READ_APPEND cache
+ */
+ int (*write_function)(struct st_io_cache *,const byte *,uint);
+ /*
+ Specifies the type of the cache. Depending on the type of the cache
+ certain operations might not be available and yield unpredicatable
+ results. Details to be documented later
+ */
+ enum cache_type type;
+ /*
+ Callbacks when the actual read I/O happens. These were added and
+ are currently used for binary logging of LOAD DATA INFILE - when a
+ block is read from the file, we create a block create/append event, and
+ when IO_CACHE is closed, we create an end event. These functions could,
+ of course be used for other things
+ */
+ IO_CACHE_CALLBACK pre_read;
+ IO_CACHE_CALLBACK post_read;
+ IO_CACHE_CALLBACK pre_close;
+ /*
+ Counts the number of times, when we were forced to use disk. We use it to
+ increase the binlog_cache_disk_use status variable.
+ */
+ ulong disk_writes;
+ void* arg; /* for use by pre/post_read */
+ char *file_name; /* if used with 'open_cached_file' */
+ char *dir,*prefix;
+ File file; /* file descriptor */
+ /*
+ seek_not_done is set by my_b_seek() to inform the upcoming read/write
+ operation that a seek needs to be preformed prior to the actual I/O
+ error is 0 if the cache operation was successful, -1 if there was a
+ "hard" error, and the actual number of I/O-ed bytes if the read/write was
+ partial.
+ */
+ int seek_not_done,error;
+ /* buffer_length is memory size allocated for buffer or write_buffer */
+ uint buffer_length;
+ /* read_length is the same as buffer_length except when we use async io */
+ uint read_length;
+ myf myflags; /* Flags used to my_read/my_write */
+ /*
+ alloced_buffer is 1 if the buffer was allocated by init_io_cache() and
+ 0 if it was supplied by the user.
+ Currently READ_NET is the only one that will use a buffer allocated
+ somewhere else
+ */
+ my_bool alloced_buffer;
+#ifdef HAVE_AIOWAIT
+ /*
+ As inidicated by ifdef, this is for async I/O, which is not currently
+ used (because it's not reliable on all systems)
+ */
+ uint inited;
+ my_off_t aio_read_pos;
+ my_aio_result aio_result;
+#endif
+} IO_CACHE;
+
+typedef int (*qsort2_cmp)(const void *, const void *, const void *);
+
+ /* defines for mf_iocache */
+
+ /* Test if buffer is inited */
+#define my_b_clear(info) (info)->buffer=0
+#define my_b_inited(info) (info)->buffer
+#define my_b_EOF INT_MIN
+
+#define my_b_read(info,Buffer,Count) \
+ ((info)->read_pos + (Count) <= (info)->read_end ?\
+ (memcpy(Buffer,(info)->read_pos,(size_t) (Count)), \
+ ((info)->read_pos+=(Count)),0) :\
+ (*(info)->read_function)((info),Buffer,Count))
+
+#define my_b_write(info,Buffer,Count) \
+ ((info)->write_pos + (Count) <=(info)->write_end ?\
+ (memcpy((info)->write_pos, (Buffer), (size_t)(Count)),\
+ ((info)->write_pos+=(Count)),0) : \
+ (*(info)->write_function)((info),(Buffer),(Count)))
+
+#define my_b_get(info) \
+ ((info)->read_pos != (info)->read_end ?\
+ ((info)->read_pos++, (int) (uchar) (info)->read_pos[-1]) :\
+ _my_b_get(info))
+
+ /* my_b_write_byte dosn't have any err-check */
+#define my_b_write_byte(info,chr) \
+ (((info)->write_pos < (info)->write_end) ?\
+ ((*(info)->write_pos++)=(chr)) :\
+ (_my_b_write(info,0,0) , ((*(info)->write_pos++)=(chr))))
+
+#define my_b_fill_cache(info) \
+ (((info)->read_end=(info)->read_pos),(*(info)->read_function)(info,0,0))
+
+#define my_b_tell(info) ((info)->pos_in_file + \
+ (uint) (*(info)->current_pos - (info)->request_pos))
+
+/* tell write offset in the SEQ_APPEND cache */
+my_off_t my_b_append_tell(IO_CACHE* info);
+my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
+
+#define my_b_bytes_in_cache(info) (uint) (*(info)->current_end - \
+ *(info)->current_pos)
+
+typedef uint32 ha_checksum;
+
+/* Define the type of function to be passed to process_default_option_files */
+typedef int (*Process_option_func)(void *ctx, const char *group_name,
+ const char *option);
+
+#include <my_alloc.h>
+
+ /* Prototypes for mysys and my_func functions */
+
+extern int my_copy(const char *from,const char *to,myf MyFlags);
+extern int my_append(const char *from,const char *to,myf MyFlags);
+extern int my_delete(const char *name,myf MyFlags);
+extern int my_getwd(my_string buf,uint size,myf MyFlags);
+extern int my_setwd(const char *dir,myf MyFlags);
+extern int my_lock(File fd,int op,my_off_t start, my_off_t length,myf MyFlags);
+extern gptr my_once_alloc(uint Size,myf MyFlags);
+extern void my_once_free(void);
+extern char *my_once_strdup(const char *src,myf myflags);
+extern char *my_once_memdup(const char *src, uint len, myf myflags);
+extern File my_open(const char *FileName,int Flags,myf MyFlags);
+extern File my_register_filename(File fd, const char *FileName,
+ enum file_type type_of_file,
+ uint error_message_number, myf MyFlags);
+extern File my_create(const char *FileName,int CreateFlags,
+ int AccsesFlags, myf MyFlags);
+extern int my_close(File Filedes,myf MyFlags);
+extern File my_dup(File file, myf MyFlags);
+extern int my_mkdir(const char *dir, int Flags, myf MyFlags);
+extern int my_readlink(char *to, const char *filename, myf MyFlags);
+extern int my_realpath(char *to, const char *filename, myf MyFlags);
+extern File my_create_with_symlink(const char *linkname, const char *filename,
+ int createflags, int access_flags,
+ myf MyFlags);
+extern int my_delete_with_symlink(const char *name, myf MyFlags);
+extern int my_rename_with_symlink(const char *from,const char *to,myf MyFlags);
+extern int my_symlink(const char *content, const char *linkname, myf MyFlags);
+extern uint my_read(File Filedes,byte *Buffer,uint Count,myf MyFlags);
+extern uint my_pread(File Filedes,byte *Buffer,uint Count,my_off_t offset,
+ myf MyFlags);
+extern int my_rename(const char *from,const char *to,myf MyFlags);
+extern my_off_t my_seek(File fd,my_off_t pos,int whence,myf MyFlags);
+extern my_off_t my_tell(File fd,myf MyFlags);
+extern uint my_write(File Filedes,const byte *Buffer,uint Count,
+ myf MyFlags);
+extern uint my_pwrite(File Filedes,const byte *Buffer,uint Count,
+ my_off_t offset,myf MyFlags);
+extern uint my_fread(FILE *stream,byte *Buffer,uint Count,myf MyFlags);
+extern uint my_fwrite(FILE *stream,const byte *Buffer,uint Count,
+ myf MyFlags);
+extern my_off_t my_fseek(FILE *stream,my_off_t pos,int whence,myf MyFlags);
+extern my_off_t my_ftell(FILE *stream,myf MyFlags);
+extern gptr _mymalloc(uint uSize,const char *sFile,
+ uint uLine, myf MyFlag);
+extern gptr _myrealloc(gptr pPtr,uint uSize,const char *sFile,
+ uint uLine, myf MyFlag);
+extern gptr my_multi_malloc _VARARGS((myf MyFlags, ...));
+extern void _myfree(gptr pPtr,const char *sFile,uint uLine, myf MyFlag);
+extern int _sanity(const char *sFile,unsigned int uLine);
+extern gptr _my_memdup(const byte *from,uint length,
+ const char *sFile, uint uLine,myf MyFlag);
+extern my_string _my_strdup(const char *from, const char *sFile, uint uLine,
+ myf MyFlag);
+extern char *_my_strdup_with_length(const byte *from, uint length,
+ const char *sFile, uint uLine,
+ myf MyFlag);
+
+#ifdef __WIN__
+extern int my_access(const char *path, int amode);
+extern File my_sopen(const char *path, int oflag, int shflag, int pmode);
+#else
+#define my_access access
+#endif
+extern int check_if_legal_filename(const char *path);
+
+#ifndef TERMINATE
+extern void TERMINATE(FILE *file);
+#endif
+extern void init_glob_errs(void);
+extern FILE *my_fopen(const char *FileName,int Flags,myf MyFlags);
+extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags);
+extern int my_fclose(FILE *fd,myf MyFlags);
+extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags);
+extern int my_sync(File fd, myf my_flags);
+extern int my_error _VARARGS((int nr,myf MyFlags, ...));
+extern int my_printf_error _VARARGS((uint my_err, const char *format,
+ myf MyFlags, ...)
+ __attribute__ ((format (printf, 2, 4))));
+extern int my_error_register(const char **errmsgs, int first, int last);
+extern const char **my_error_unregister(int first, int last);
+extern int my_message(uint my_err, const char *str,myf MyFlags);
+extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags);
+extern int my_message_curses(uint my_err, const char *str,myf MyFlags);
+extern my_bool my_init(void);
+extern void my_end(int infoflag);
+extern int my_redel(const char *from, const char *to, int MyFlags);
+extern int my_copystat(const char *from, const char *to, int MyFlags);
+extern my_string my_filename(File fd);
+
+#ifndef THREAD
+extern void dont_break(void);
+extern void allow_break(void);
+#else
+#define dont_break()
+#define allow_break()
+#endif
+
+extern my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist);
+extern char *my_tmpdir(MY_TMPDIR *tmpdir);
+extern void free_tmpdir(MY_TMPDIR *tmpdir);
+
+extern void my_remember_signal(int signal_number,sig_handler (*func)(int));
+extern uint dirname_part(my_string to,const char *name);
+extern uint dirname_length(const char *name);
+#define base_name(A) (A+dirname_length(A))
+extern int test_if_hard_path(const char *dir_name);
+extern my_bool has_path(const char *name);
+extern char *convert_dirname(char *to, const char *from, const char *from_end);
+extern void to_unix_path(my_string name);
+extern my_string fn_ext(const char *name);
+extern my_string fn_same(my_string toname,const char *name,int flag);
+extern my_string fn_format(my_string to,const char *name,const char *dir,
+ const char *form, uint flag);
+extern size_s strlength(const char *str);
+extern void pack_dirname(my_string to,const char *from);
+extern uint unpack_dirname(my_string to,const char *from);
+extern uint cleanup_dirname(my_string to,const char *from);
+extern uint system_filename(my_string to,const char *from);
+extern uint unpack_filename(my_string to,const char *from);
+extern my_string intern_filename(my_string to,const char *from);
+extern my_string directory_file_name(my_string dst, const char *src);
+extern int pack_filename(my_string to, const char *name, size_s max_length);
+extern my_string my_path(my_string to,const char *progname,
+ const char *own_pathname_part);
+extern my_string my_load_path(my_string to, const char *path,
+ const char *own_path_prefix);
+extern int wild_compare(const char *str,const char *wildstr,pbool str_is_pattern);
+extern WF_PACK *wf_comp(my_string str);
+extern int wf_test(struct wild_file_pack *wf_pack,const char *name);
+extern void wf_end(struct wild_file_pack *buffer);
+extern size_s strip_sp(my_string str);
+extern void get_date(my_string to,int timeflag,time_t use_time);
+extern void soundex(CHARSET_INFO *, my_string out_pntr, my_string in_pntr,pbool remove_garbage);
+extern int init_record_cache(RECORD_CACHE *info,uint cachesize,File file,
+ uint reclength,enum cache_type type,
+ pbool use_async_io);
+extern int read_cache_record(RECORD_CACHE *info,byte *to);
+extern int end_record_cache(RECORD_CACHE *info);
+extern int write_cache_record(RECORD_CACHE *info,my_off_t filepos,
+ const byte *record,uint length);
+extern int flush_write_cache(RECORD_CACHE *info);
+extern long my_clock(void);
+extern sig_handler sigtstp_handler(int signal_number);
+extern void handle_recived_signals(void);
+
+extern sig_handler my_set_alarm_variable(int signo);
+extern void my_string_ptr_sort(void *base,uint items,size_s size);
+extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements,
+ size_s size_of_element,uchar *buffer[]);
+extern qsort_t qsort2(void *base_ptr, size_t total_elems, size_t size,
+ qsort2_cmp cmp, void *cmp_argument);
+extern qsort2_cmp get_ptr_compare(uint);
+void my_store_ptr(byte *buff, uint pack_length, my_off_t pos);
+my_off_t my_get_ptr(byte *ptr, uint pack_length);
+extern int init_io_cache(IO_CACHE *info,File file,uint cachesize,
+ enum cache_type type,my_off_t seek_offset,
+ pbool use_async_io, myf cache_myflags);
+extern my_bool reinit_io_cache(IO_CACHE *info,enum cache_type type,
+ my_off_t seek_offset,pbool use_async_io,
+ pbool clear_cache);
+extern void setup_io_cache(IO_CACHE* info);
+extern int _my_b_read(IO_CACHE *info,byte *Buffer,uint Count);
+#ifdef THREAD
+extern int _my_b_read_r(IO_CACHE *info,byte *Buffer,uint Count);
+extern void init_io_cache_share(IO_CACHE *info,
+ IO_CACHE_SHARE *s, uint num_threads);
+extern void remove_io_thread(IO_CACHE *info);
+#endif
+extern int _my_b_seq_read(IO_CACHE *info,byte *Buffer,uint Count);
+extern int _my_b_net_read(IO_CACHE *info,byte *Buffer,uint Count);
+extern int _my_b_get(IO_CACHE *info);
+extern int _my_b_async_read(IO_CACHE *info,byte *Buffer,uint Count);
+extern int _my_b_write(IO_CACHE *info,const byte *Buffer,uint Count);
+extern int my_b_append(IO_CACHE *info,const byte *Buffer,uint Count);
+extern int my_b_safe_write(IO_CACHE *info,const byte *Buffer,uint Count);
+
+extern int my_block_write(IO_CACHE *info, const byte *Buffer,
+ uint Count, my_off_t pos);
+extern int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock);
+
+#define flush_io_cache(info) my_b_flush_io_cache((info),1)
+
+extern int end_io_cache(IO_CACHE *info);
+extern uint my_b_fill(IO_CACHE *info);
+extern void my_b_seek(IO_CACHE *info,my_off_t pos);
+extern uint my_b_gets(IO_CACHE *info, char *to, uint max_length);
+extern my_off_t my_b_filelength(IO_CACHE *info);
+extern uint my_b_printf(IO_CACHE *info, const char* fmt, ...);
+extern uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list ap);
+extern my_bool open_cached_file(IO_CACHE *cache,const char *dir,
+ const char *prefix, uint cache_size,
+ myf cache_myflags);
+extern my_bool real_open_cached_file(IO_CACHE *cache);
+extern void close_cached_file(IO_CACHE *cache);
+File create_temp_file(char *to, const char *dir, const char *pfx,
+ int mode, myf MyFlags);
+#define my_init_dynamic_array(A,B,C,D) init_dynamic_array(A,B,C,D CALLER_INFO)
+#define my_init_dynamic_array_ci(A,B,C,D) init_dynamic_array(A,B,C,D ORIG_CALLER_INFO)
+extern my_bool init_dynamic_array(DYNAMIC_ARRAY *array,uint element_size,
+ uint init_alloc,uint alloc_increment
+ CALLER_INFO_PROTO);
+extern my_bool insert_dynamic(DYNAMIC_ARRAY *array,gptr element);
+extern byte *alloc_dynamic(DYNAMIC_ARRAY *array);
+extern byte *pop_dynamic(DYNAMIC_ARRAY*);
+extern my_bool set_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index);
+extern void get_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index);
+extern void delete_dynamic(DYNAMIC_ARRAY *array);
+extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
+extern void freeze_size(DYNAMIC_ARRAY *array);
+#define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element)
+#define dynamic_element(array,array_index,type) ((type)((array)->buffer) +(array_index))
+#define push_dynamic(A,B) insert_dynamic(A,B)
+#define reset_dynamic(array) ((array)->elements= 0)
+
+extern my_bool init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
+ uint init_alloc,uint alloc_increment);
+extern my_bool dynstr_append(DYNAMIC_STRING *str, const char *append);
+my_bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
+ uint length);
+extern my_bool dynstr_set(DYNAMIC_STRING *str, const char *init_str);
+extern my_bool dynstr_realloc(DYNAMIC_STRING *str, ulong additional_size);
+extern void dynstr_free(DYNAMIC_STRING *str);
+#ifdef HAVE_MLOCK
+extern byte *my_malloc_lock(uint length,myf flags);
+extern void my_free_lock(byte *ptr,myf flags);
+#else
+#define my_malloc_lock(A,B) my_malloc((A),(B))
+#define my_free_lock(A,B) my_free((A),(B))
+#endif
+#define alloc_root_inited(A) ((A)->min_malloc != 0)
+#define ALLOC_ROOT_MIN_BLOCK_SIZE (MALLOC_OVERHEAD + sizeof(USED_MEM) + 8)
+#define clear_alloc_root(A) do { (A)->free= (A)->used= (A)->pre_alloc= 0; (A)->min_malloc=0;} while(0)
+extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
+ uint pre_alloc_size);
+extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size);
+extern gptr multi_alloc_root(MEM_ROOT *mem_root, ...);
+extern void free_root(MEM_ROOT *root, myf MyFLAGS);
+extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
+extern void reset_root_defaults(MEM_ROOT *mem_root, uint block_size,
+ uint prealloc_size);
+extern char *strdup_root(MEM_ROOT *root,const char *str);
+extern char *strmake_root(MEM_ROOT *root,const char *str,uint len);
+extern char *memdup_root(MEM_ROOT *root,const char *str,uint len);
+extern int get_defaults_options(int argc, char **argv,
+ char **defaults, char **extra_defaults,
+ char **group_suffix);
+extern int load_defaults(const char *conf_file, const char **groups,
+ int *argc, char ***argv);
+extern int modify_defaults_file(const char *file_location, const char *option,
+ const char *option_value,
+ const char *section_name, int remove_option);
+extern int my_search_option_files(const char *conf_file, int *argc,
+ char ***argv, uint *args_used,
+ Process_option_func func, void *func_ctx);
+extern void free_defaults(char **argv);
+extern void my_print_default_files(const char *conf_file);
+extern void print_defaults(const char *conf_file, const char **groups);
+extern my_bool my_compress(byte *, ulong *, ulong *);
+extern my_bool my_uncompress(byte *, ulong *, ulong *);
+extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen);
+extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count);
+extern uint my_bit_log2(ulong value);
+extern uint my_count_bits(ulonglong v);
+extern uint my_count_bits_ushort(ushort v);
+extern void my_sleep(ulong m_seconds);
+extern ulong crc32(ulong crc, const uchar *buf, uint len);
+extern uint my_set_max_open_files(uint files);
+void my_free_open_file_info(void);
+
+ulonglong my_getsystime(void);
+my_bool my_gethwaddr(uchar *to);
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+
+#ifndef MAP_NOSYNC
+#define MAP_NOSYNC 0
+#endif
+
+#define my_mmap(a,b,c,d,e,f) mmap(a,b,c,d,e,f)
+#ifdef HAVE_GETPAGESIZE
+#define my_getpagesize() getpagesize()
+#else
+/* qnx ? */
+#define my_getpagesize() 8192
+#endif
+#define my_munmap(a,b) munmap((a),(b))
+
+#else
+/* not a complete set of mmap() flags, but only those that nesessary */
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define MAP_SHARED 0x0001
+#define MAP_NOSYNC 0x0800
+#define MAP_FAILED ((void *)-1)
+#define MS_SYNC 0x0000
+
+#ifndef __NETWARE__
+#define HAVE_MMAP
+#endif
+
+int my_getpagesize(void);
+void *my_mmap(void *, size_t, int, int, int, my_off_t);
+int my_munmap(void *, size_t);
+#endif
+
+int my_msync(int, void *, size_t, int);
+
+/* character sets */
+extern uint get_charset_number(const char *cs_name, uint cs_flags);
+extern uint get_collation_number(const char *name);
+extern const char *get_charset_name(uint cs_number);
+
+extern CHARSET_INFO *get_charset(uint cs_number, myf flags);
+extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags);
+extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
+ uint cs_flags, myf my_flags);
+extern void free_charsets(void);
+extern char *get_charsets_dir(char *buf);
+extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
+extern my_bool init_compiled_charsets(myf flags);
+extern void add_compiled_collation(CHARSET_INFO *cs);
+extern ulong escape_string_for_mysql(CHARSET_INFO *charset_info,
+ char *to, ulong to_length,
+ const char *from, ulong length);
+#ifdef __WIN__
+#define BACKSLASH_MBTAIL
+/* File system character set */
+extern CHARSET_INFO *fs_character_set(void);
+#endif
+extern ulong escape_quotes_for_mysql(CHARSET_INFO *charset_info,
+ char *to, ulong to_length,
+ const char *from, ulong length);
+
+extern void thd_increment_bytes_sent(ulong length);
+extern void thd_increment_bytes_received(ulong length);
+extern void thd_increment_net_big_packet_count(ulong length);
+
+#ifdef __WIN__
+extern my_bool have_tcpip; /* Is set if tcpip is used */
+
+/* implemented in my_windac.c */
+
+int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror,
+ DWORD owner_rights, DWORD everybody_rights);
+
+void my_security_attr_free(SECURITY_ATTRIBUTES *sa);
+
+/* implemented in my_conio.c */
+char* my_cgets(char *string, unsigned long clen, unsigned long* plen);
+
+#endif
+#ifdef __NETWARE__
+void netware_reg_user(const char *ip, const char *user,
+ const char *application);
+#endif
+
+C_MODE_END
+#include "raid.h"
+#endif /* _my_sys_h */
diff --git a/src/mysql/mysql-5.0.16 b/src/mysql/mysql-5.0.16
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/mysql/mysql-5.0.16
diff --git a/src/mysql/mysql.h b/src/mysql/mysql.h
new file mode 100644
index 000000000..9ceb186a5
--- /dev/null
+++ b/src/mysql/mysql.h
@@ -0,0 +1,839 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _mysql_h
+#define _mysql_h
+
+#ifdef __CYGWIN__ /* CYGWIN implements a UNIX API */
+#undef WIN
+#undef _WIN
+#undef _WIN32
+#undef _WIN64
+#undef __WIN__
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _global_h /* If not standard header */
+#include <sys/types.h>
+#ifdef __LCC__
+#include <winsock.h> /* For windows */
+#endif
+typedef char my_bool;
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__WIN__)
+#define __WIN__
+#endif
+#if !defined(__WIN__)
+#define STDCALL
+#else
+#define STDCALL __stdcall
+#endif
+typedef char * gptr;
+
+#ifndef my_socket_defined
+#ifdef __WIN__
+#define my_socket SOCKET
+#else
+typedef int my_socket;
+#endif /* __WIN__ */
+#endif /* my_socket_defined */
+#endif /* _global_h */
+
+#include "mysql_com.h"
+#include "mysql_time.h"
+#include "mysql_version.h"
+#include "typelib.h"
+
+#include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */
+
+extern unsigned int mysql_port;
+extern char *mysql_unix_port;
+
+#define CLIENT_NET_READ_TIMEOUT 365*24*3600 /* Timeout on read */
+#define CLIENT_NET_WRITE_TIMEOUT 365*24*3600 /* Timeout on write */
+
+#ifdef __NETWARE__
+#pragma pack(push, 8) /* 8 byte alignment */
+#endif
+
+#define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG)
+#define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG)
+#define IS_BLOB(n) ((n) & BLOB_FLAG)
+#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL)
+#define IS_NUM_FIELD(f) ((f)->flags & NUM_FLAG)
+#define INTERNAL_NUM_FIELD(f) (((f)->type <= FIELD_TYPE_INT24 && ((f)->type != FIELD_TYPE_TIMESTAMP || (f)->length == 14 || (f)->length == 8)) || (f)->type == FIELD_TYPE_YEAR)
+
+
+typedef struct st_mysql_field {
+ char *name; /* Name of column */
+ char *org_name; /* Original column name, if an alias */
+ char *table; /* Table of column if column was a field */
+ char *org_table; /* Org table name, if table was an alias */
+ char *db; /* Database for table */
+ char *catalog; /* Catalog for table */
+ char *def; /* Default value (set by mysql_list_fields) */
+ unsigned long length; /* Width of column (create length) */
+ unsigned long max_length; /* Max width for selected set */
+ unsigned int name_length;
+ unsigned int org_name_length;
+ unsigned int table_length;
+ unsigned int org_table_length;
+ unsigned int db_length;
+ unsigned int catalog_length;
+ unsigned int def_length;
+ unsigned int flags; /* Div flags */
+ unsigned int decimals; /* Number of decimals in field */
+ unsigned int charsetnr; /* Character set */
+ enum enum_field_types type; /* Type of field. See mysql_com.h for types */
+} MYSQL_FIELD;
+
+typedef char **MYSQL_ROW; /* return data as array of strings */
+typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */
+
+#ifndef _global_h
+#if defined(NO_CLIENT_LONG_LONG)
+typedef unsigned long my_ulonglong;
+#elif defined (__WIN__)
+typedef unsigned __int64 my_ulonglong;
+#else
+typedef unsigned long long my_ulonglong;
+#endif
+#endif
+
+#define MYSQL_COUNT_ERROR (~(my_ulonglong) 0)
+
+typedef struct st_mysql_rows {
+ struct st_mysql_rows *next; /* list of rows */
+ MYSQL_ROW data;
+ unsigned long length;
+} MYSQL_ROWS;
+
+typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */
+
+#include "my_alloc.h"
+
+typedef struct st_mysql_data {
+ my_ulonglong rows;
+ unsigned int fields;
+ MYSQL_ROWS *data;
+ MEM_ROOT alloc;
+#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY)
+ MYSQL_ROWS **prev_ptr;
+#endif
+} MYSQL_DATA;
+
+enum mysql_option
+{
+ MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE,
+ MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP,
+ MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE,
+ MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT,
+ MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT,
+ MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
+ MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
+ MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT
+};
+
+struct st_mysql_options {
+ unsigned int connect_timeout, read_timeout, write_timeout;
+ unsigned int port, protocol;
+ unsigned long client_flag;
+ char *host,*user,*password,*unix_socket,*db;
+ struct st_dynamic_array *init_commands;
+ char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name;
+ char *ssl_key; /* PEM key file */
+ char *ssl_cert; /* PEM cert file */
+ char *ssl_ca; /* PEM CA file */
+ char *ssl_capath; /* PEM directory of CA-s? */
+ char *ssl_cipher; /* cipher to use */
+ char *shared_memory_base_name;
+ unsigned long max_allowed_packet;
+ my_bool use_ssl; /* if to use SSL or not */
+ my_bool compress,named_pipe;
+ /*
+ On connect, find out the replication role of the server, and
+ establish connections to all the peers
+ */
+ my_bool rpl_probe;
+ /*
+ Each call to mysql_real_query() will parse it to tell if it is a read
+ or a write, and direct it to the slave or the master
+ */
+ my_bool rpl_parse;
+ /*
+ If set, never read from a master, only from slave, when doing
+ a read that is replication-aware
+ */
+ my_bool no_master_reads;
+#if !defined(CHECK_EMBEDDED_DIFFERENCES) || defined(EMBEDDED_LIBRARY)
+ my_bool separate_thread;
+#endif
+ enum mysql_option methods_to_use;
+ char *client_ip;
+ /* Refuse client connecting to server if it uses old (pre-4.1.1) protocol */
+ my_bool secure_auth;
+ /* 0 - never report, 1 - always report (default) */
+ my_bool report_data_truncation;
+
+ /* function pointers for local infile support */
+ int (*local_infile_init)(void **, const char *, void *);
+ int (*local_infile_read)(void *, char *, unsigned int);
+ void (*local_infile_end)(void *);
+ int (*local_infile_error)(void *, char *, unsigned int);
+ void *local_infile_userdata;
+};
+
+enum mysql_status
+{
+ MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,MYSQL_STATUS_USE_RESULT
+};
+
+enum mysql_protocol_type
+{
+ MYSQL_PROTOCOL_DEFAULT, MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET,
+ MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY
+};
+/*
+ There are three types of queries - the ones that have to go to
+ the master, the ones that go to a slave, and the adminstrative
+ type which must happen on the pivot connectioin
+*/
+enum mysql_rpl_type
+{
+ MYSQL_RPL_MASTER, MYSQL_RPL_SLAVE, MYSQL_RPL_ADMIN
+};
+
+typedef struct character_set
+{
+ unsigned int number; /* character set number */
+ unsigned int state; /* character set state */
+ const char *csname; /* collation name */
+ const char *name; /* character set name */
+ const char *comment; /* comment */
+ const char *dir; /* character set directory */
+ unsigned int mbminlen; /* min. length for multibyte strings */
+ unsigned int mbmaxlen; /* max. length for multibyte strings */
+} MY_CHARSET_INFO;
+
+struct st_mysql_methods;
+
+typedef struct st_mysql
+{
+ NET net; /* Communication parameters */
+ gptr connector_fd; /* ConnectorFd for SSL */
+ char *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info;
+ char *db;
+ struct charset_info_st *charset;
+ MYSQL_FIELD *fields;
+ MEM_ROOT field_alloc;
+ my_ulonglong affected_rows;
+ my_ulonglong insert_id; /* id if insert on table with NEXTNR */
+ my_ulonglong extra_info; /* Not used */
+ unsigned long thread_id; /* Id for connection in server */
+ unsigned long packet_length;
+ unsigned int port;
+ unsigned long client_flag,server_capabilities;
+ unsigned int protocol_version;
+ unsigned int field_count;
+ unsigned int server_status;
+ unsigned int server_language;
+ unsigned int warning_count;
+ struct st_mysql_options options;
+ enum mysql_status status;
+ my_bool free_me; /* If free in mysql_close */
+ my_bool reconnect; /* set to 1 if automatic reconnect */
+
+ /* session-wide random string */
+ char scramble[SCRAMBLE_LENGTH+1];
+
+ /*
+ Set if this is the original connection, not a master or a slave we have
+ added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave()
+ */
+ my_bool rpl_pivot;
+ /*
+ Pointers to the master, and the next slave connections, points to
+ itself if lone connection.
+ */
+ struct st_mysql* master, *next_slave;
+
+ struct st_mysql* last_used_slave; /* needed for round-robin slave pick */
+ /* needed for send/read/store/use result to work correctly with replication */
+ struct st_mysql* last_used_con;
+
+ LIST *stmts; /* list of all statements */
+ const struct st_mysql_methods *methods;
+ void *thd;
+ /*
+ Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag
+ from mysql_stmt_close if close had to cancel result set of this object.
+ */
+ my_bool *unbuffered_fetch_owner;
+} MYSQL;
+
+typedef struct st_mysql_res {
+ my_ulonglong row_count;
+ MYSQL_FIELD *fields;
+ MYSQL_DATA *data;
+ MYSQL_ROWS *data_cursor;
+ unsigned long *lengths; /* column lengths of current row */
+ MYSQL *handle; /* for unbuffered reads */
+ MEM_ROOT field_alloc;
+ unsigned int field_count, current_field;
+ MYSQL_ROW row; /* If unbuffered read */
+ MYSQL_ROW current_row; /* buffer to current row */
+ my_bool eof; /* Used by mysql_fetch_row */
+ /* mysql_stmt_close() had to cancel this result */
+ my_bool unbuffered_fetch_cancelled;
+ const struct st_mysql_methods *methods;
+} MYSQL_RES;
+
+#define MAX_MYSQL_MANAGER_ERR 256
+#define MAX_MYSQL_MANAGER_MSG 256
+
+#define MANAGER_OK 200
+#define MANAGER_INFO 250
+#define MANAGER_ACCESS 401
+#define MANAGER_CLIENT_ERR 450
+#define MANAGER_INTERNAL_ERR 500
+
+#if !defined(MYSQL_SERVER) && !defined(MYSQL_CLIENT)
+#define MYSQL_CLIENT
+#endif
+
+
+typedef struct st_mysql_manager
+{
+ NET net;
+ char *host,*user,*passwd;
+ unsigned int port;
+ my_bool free_me;
+ my_bool eof;
+ int cmd_status;
+ int last_errno;
+ char* net_buf,*net_buf_pos,*net_data_end;
+ int net_buf_size;
+ char last_error[MAX_MYSQL_MANAGER_ERR];
+} MYSQL_MANAGER;
+
+typedef struct st_mysql_parameters
+{
+ unsigned long *p_max_allowed_packet;
+ unsigned long *p_net_buffer_length;
+} MYSQL_PARAMETERS;
+
+#if !defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)
+#define max_allowed_packet (*mysql_get_parameters()->p_max_allowed_packet)
+#define net_buffer_length (*mysql_get_parameters()->p_net_buffer_length)
+#endif
+
+/*
+ Set up and bring down the server; to ensure that applications will
+ work when linked against either the standard client library or the
+ embedded server library, these functions should be called.
+*/
+int STDCALL mysql_server_init(int argc, char **argv, char **groups);
+void STDCALL mysql_server_end(void);
+/*
+ mysql_server_init/end need to be called when using libmysqld or
+ libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
+ you don't need to call it explicitely; but you need to call
+ mysql_server_end() to free memory). The names are a bit misleading
+ (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
+ names which suit well whether you're using libmysqld or libmysqlclient. We
+ intend to promote these aliases over the mysql_server* ones.
+*/
+#define mysql_library_init mysql_server_init
+#define mysql_library_end mysql_server_end
+
+MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void);
+
+/*
+ Set up and bring down a thread; these function should be called
+ for each thread in an application which opens at least one MySQL
+ connection. All uses of the connection(s) should be between these
+ function calls.
+*/
+my_bool STDCALL mysql_thread_init(void);
+void STDCALL mysql_thread_end(void);
+
+/*
+ Functions to get information from the MYSQL and MYSQL_RES structures
+ Should definitely be used if one uses shared libraries.
+*/
+
+my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res);
+unsigned int STDCALL mysql_num_fields(MYSQL_RES *res);
+my_bool STDCALL mysql_eof(MYSQL_RES *res);
+MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res,
+ unsigned int fieldnr);
+MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res);
+MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res);
+MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res);
+
+unsigned int STDCALL mysql_field_count(MYSQL *mysql);
+my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql);
+my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql);
+unsigned int STDCALL mysql_errno(MYSQL *mysql);
+const char * STDCALL mysql_error(MYSQL *mysql);
+const char *STDCALL mysql_sqlstate(MYSQL *mysql);
+unsigned int STDCALL mysql_warning_count(MYSQL *mysql);
+const char * STDCALL mysql_info(MYSQL *mysql);
+unsigned long STDCALL mysql_thread_id(MYSQL *mysql);
+const char * STDCALL mysql_character_set_name(MYSQL *mysql);
+int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname);
+
+MYSQL * STDCALL mysql_init(MYSQL *mysql);
+my_bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key,
+ const char *cert, const char *ca,
+ const char *capath, const char *cipher);
+my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
+ const char *passwd, const char *db);
+MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
+ const char *user,
+ const char *passwd,
+ const char *db,
+ unsigned int port,
+ const char *unix_socket,
+ unsigned long clientflag);
+int STDCALL mysql_select_db(MYSQL *mysql, const char *db);
+int STDCALL mysql_query(MYSQL *mysql, const char *q);
+int STDCALL mysql_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql);
+MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql);
+
+/* perform query on master */
+my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+/* perform query on slave */
+my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+void STDCALL mysql_get_character_set_info(MYSQL *mysql,
+ MY_CHARSET_INFO *charset);
+
+/* local infile support */
+
+#define LOCAL_INFILE_ERROR_LEN 512
+
+void
+mysql_set_local_infile_handler(MYSQL *mysql,
+ int (*local_infile_init)(void **, const char *,
+ void *),
+ int (*local_infile_read)(void *, char *,
+ unsigned int),
+ void (*local_infile_end)(void *),
+ int (*local_infile_error)(void *, char*,
+ unsigned int),
+ void *);
+
+void
+mysql_set_local_infile_default(MYSQL *mysql);
+
+
+/*
+ enable/disable parsing of all queries to decide if they go on master or
+ slave
+*/
+void STDCALL mysql_enable_rpl_parse(MYSQL* mysql);
+void STDCALL mysql_disable_rpl_parse(MYSQL* mysql);
+/* get the value of the parse flag */
+int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql);
+
+/* enable/disable reads from master */
+void STDCALL mysql_enable_reads_from_master(MYSQL* mysql);
+void STDCALL mysql_disable_reads_from_master(MYSQL* mysql);
+/* get the value of the master read flag */
+my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql);
+
+enum mysql_rpl_type STDCALL mysql_rpl_query_type(const char* q, int len);
+
+/* discover the master and its slaves */
+my_bool STDCALL mysql_rpl_probe(MYSQL* mysql);
+
+/* set the master, close/free the old one, if it is not a pivot */
+int STDCALL mysql_set_master(MYSQL* mysql, const char* host,
+ unsigned int port,
+ const char* user,
+ const char* passwd);
+int STDCALL mysql_add_slave(MYSQL* mysql, const char* host,
+ unsigned int port,
+ const char* user,
+ const char* passwd);
+
+int STDCALL mysql_shutdown(MYSQL *mysql,
+ enum mysql_enum_shutdown_level
+ shutdown_level);
+int STDCALL mysql_dump_debug_info(MYSQL *mysql);
+int STDCALL mysql_refresh(MYSQL *mysql,
+ unsigned int refresh_options);
+int STDCALL mysql_kill(MYSQL *mysql,unsigned long pid);
+int STDCALL mysql_set_server_option(MYSQL *mysql,
+ enum enum_mysql_set_option
+ option);
+int STDCALL mysql_ping(MYSQL *mysql);
+const char * STDCALL mysql_stat(MYSQL *mysql);
+const char * STDCALL mysql_get_server_info(MYSQL *mysql);
+const char * STDCALL mysql_get_client_info(void);
+unsigned long STDCALL mysql_get_client_version(void);
+const char * STDCALL mysql_get_host_info(MYSQL *mysql);
+unsigned long STDCALL mysql_get_server_version(MYSQL *mysql);
+unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql);
+MYSQL_RES * STDCALL mysql_list_dbs(MYSQL *mysql,const char *wild);
+MYSQL_RES * STDCALL mysql_list_tables(MYSQL *mysql,const char *wild);
+MYSQL_RES * STDCALL mysql_list_processes(MYSQL *mysql);
+int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option,
+ const char *arg);
+void STDCALL mysql_free_result(MYSQL_RES *result);
+void STDCALL mysql_data_seek(MYSQL_RES *result,
+ my_ulonglong offset);
+MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result,
+ MYSQL_ROW_OFFSET offset);
+MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result,
+ MYSQL_FIELD_OFFSET offset);
+MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
+unsigned long * STDCALL mysql_fetch_lengths(MYSQL_RES *result);
+MYSQL_FIELD * STDCALL mysql_fetch_field(MYSQL_RES *result);
+MYSQL_RES * STDCALL mysql_list_fields(MYSQL *mysql, const char *table,
+ const char *wild);
+unsigned long STDCALL mysql_escape_string(char *to,const char *from,
+ unsigned long from_length);
+unsigned long STDCALL mysql_hex_string(char *to,const char *from,
+ unsigned long from_length);
+unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql,
+ char *to,const char *from,
+ unsigned long length);
+void STDCALL mysql_debug(const char *debug);
+char * STDCALL mysql_odbc_escape_string(MYSQL *mysql,
+ char *to,
+ unsigned long to_length,
+ const char *from,
+ unsigned long from_length,
+ void *param,
+ char *
+ (*extend_buffer)
+ (void *, char *to,
+ unsigned long *length));
+void STDCALL myodbc_remove_escape(MYSQL *mysql,char *name);
+unsigned int STDCALL mysql_thread_safe(void);
+my_bool STDCALL mysql_embedded(void);
+MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con);
+MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con,
+ const char* host,
+ const char* user,
+ const char* passwd,
+ unsigned int port);
+void STDCALL mysql_manager_close(MYSQL_MANAGER* con);
+int STDCALL mysql_manager_command(MYSQL_MANAGER* con,
+ const char* cmd, int cmd_len);
+int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con,
+ char* res_buf,
+ int res_buf_size);
+my_bool STDCALL mysql_read_query_result(MYSQL *mysql);
+
+
+/*
+ The following definitions are added for the enhanced
+ client-server protocol
+*/
+
+/* statement state */
+enum enum_mysql_stmt_state
+{
+ MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE,
+ MYSQL_STMT_FETCH_DONE
+};
+
+
+/*
+ This structure is used to define bind information, and
+ internally by the client library.
+ Public members with their descriptions are listed below
+ (conventionally `On input' refers to the binds given to
+ mysql_stmt_bind_param, `On output' refers to the binds given
+ to mysql_stmt_bind_result):
+
+ buffer_type - One of the MYSQL_* types, used to describe
+ the host language type of buffer.
+ On output: if column type is different from
+ buffer_type, column value is automatically converted
+ to buffer_type before it is stored in the buffer.
+ buffer - On input: points to the buffer with input data.
+ On output: points to the buffer capable to store
+ output data.
+ The type of memory pointed by buffer must correspond
+ to buffer_type. See the correspondence table in
+ the comment to mysql_stmt_bind_param.
+
+ The two above members are mandatory for any kind of bind.
+
+ buffer_length - the length of the buffer. You don't have to set
+ it for any fixed length buffer: float, double,
+ int, etc. It must be set however for variable-length
+ types, such as BLOBs or STRINGs.
+
+ length - On input: in case when lengths of input values
+ are different for each execute, you can set this to
+ point at a variable containining value length. This
+ way the value length can be different in each execute.
+ If length is not NULL, buffer_length is not used.
+ Note, length can even point at buffer_length if
+ you keep bind structures around while fetching:
+ this way you can change buffer_length before
+ each execution, everything will work ok.
+ On output: if length is set, mysql_stmt_fetch will
+ write column length into it.
+
+ is_null - On input: points to a boolean variable that should
+ be set to TRUE for NULL values.
+ This member is useful only if your data may be
+ NULL in some but not all cases.
+ If your data is never NULL, is_null should be set to 0.
+ If your data is always NULL, set buffer_type
+ to MYSQL_TYPE_NULL, and is_null will not be used.
+
+ is_unsigned - On input: used to signify that values provided for one
+ of numeric types are unsigned.
+ On output describes signedness of the output buffer.
+ If, taking into account is_unsigned flag, column data
+ is out of range of the output buffer, data for this column
+ is regarded truncated. Note that this has no correspondence
+ to the sign of result set column, if you need to find it out
+ use mysql_stmt_result_metadata.
+ error - where to write a truncation error if it is present.
+ possible error value is:
+ 0 no truncation
+ 1 value is out of range or buffer is too small
+
+ Please note that MYSQL_BIND also has internals members.
+*/
+
+typedef struct st_mysql_bind
+{
+ unsigned long *length; /* output length pointer */
+ my_bool *is_null; /* Pointer to null indicator */
+ void *buffer; /* buffer to get/put data */
+ /* set this if you want to track data truncations happened during fetch */
+ my_bool *error;
+ enum enum_field_types buffer_type; /* buffer type */
+ /* output buffer length, must be set when fetching str/binary */
+ unsigned long buffer_length;
+ unsigned char *row_ptr; /* for the current data position */
+ unsigned long offset; /* offset position for char/binary fetch */
+ unsigned long length_value; /* Used if length is 0 */
+ unsigned int param_number; /* For null count and error messages */
+ unsigned int pack_length; /* Internal length for packed data */
+ my_bool error_value; /* used if error is 0 */
+ my_bool is_unsigned; /* set if integer type is unsigned */
+ my_bool long_data_used; /* If used with mysql_send_long_data */
+ my_bool is_null_value; /* Used if is_null is 0 */
+ void (*store_param_func)(NET *net, struct st_mysql_bind *param);
+ void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
+ unsigned char **row);
+ void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
+ unsigned char **row);
+} MYSQL_BIND;
+
+
+/* statement handler */
+typedef struct st_mysql_stmt
+{
+ MEM_ROOT mem_root; /* root allocations */
+ LIST list; /* list to keep track of all stmts */
+ MYSQL *mysql; /* connection handle */
+ MYSQL_BIND *params; /* input parameters */
+ MYSQL_BIND *bind; /* output parameters */
+ MYSQL_FIELD *fields; /* result set metadata */
+ MYSQL_DATA result; /* cached result set */
+ MYSQL_ROWS *data_cursor; /* current row in cached result */
+ /* copy of mysql->affected_rows after statement execution */
+ my_ulonglong affected_rows;
+ my_ulonglong insert_id; /* copy of mysql->insert_id */
+ /*
+ mysql_stmt_fetch() calls this function to fetch one row (it's different
+ for buffered, unbuffered and cursor fetch).
+ */
+ int (*read_row_func)(struct st_mysql_stmt *stmt,
+ unsigned char **row);
+ unsigned long stmt_id; /* Id for prepared statement */
+ unsigned long flags; /* i.e. type of cursor to open */
+ unsigned long prefetch_rows; /* number of rows per one COM_FETCH */
+ /*
+ Copied from mysql->server_status after execute/fetch to know
+ server-side cursor status for this statement.
+ */
+ unsigned int server_status;
+ unsigned int last_errno; /* error code */
+ unsigned int param_count; /* input parameter count */
+ unsigned int field_count; /* number of columns in result set */
+ enum enum_mysql_stmt_state state; /* statement state */
+ char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
+ char sqlstate[SQLSTATE_LENGTH+1];
+ /* Types of input parameters should be sent to server */
+ my_bool send_types_to_server;
+ my_bool bind_param_done; /* input buffers were supplied */
+ unsigned char bind_result_done; /* output buffers were supplied */
+ /* mysql_stmt_close() had to cancel this result */
+ my_bool unbuffered_fetch_cancelled;
+ /*
+ Is set to true if we need to calculate field->max_length for
+ metadata fields when doing mysql_stmt_store_result.
+ */
+ my_bool update_max_length;
+} MYSQL_STMT;
+
+enum enum_stmt_attr_type
+{
+ /*
+ When doing mysql_stmt_store_result calculate max_length attribute
+ of statement metadata. This is to be consistent with the old API,
+ where this was done automatically.
+ In the new API we do that only by request because it slows down
+ mysql_stmt_store_result sufficiently.
+ */
+ STMT_ATTR_UPDATE_MAX_LENGTH,
+ /*
+ unsigned long with combination of cursor flags (read only, for update,
+ etc)
+ */
+ STMT_ATTR_CURSOR_TYPE,
+ /*
+ Amount of rows to retrieve from server per one fetch if using cursors.
+ Accepts unsigned long attribute in the range 1 - ulong_max
+ */
+ STMT_ATTR_PREFETCH_ROWS
+};
+
+
+typedef struct st_mysql_methods
+{
+ my_bool (*read_query_result)(MYSQL *mysql);
+ my_bool (*advanced_command)(MYSQL *mysql,
+ enum enum_server_command command,
+ const char *header,
+ unsigned long header_length,
+ const char *arg,
+ unsigned long arg_length,
+ my_bool skip_check);
+ MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ unsigned int fields);
+ MYSQL_RES * (*use_result)(MYSQL *mysql);
+ void (*fetch_lengths)(unsigned long *to,
+ MYSQL_ROW column, unsigned int field_count);
+ void (*flush_use_result)(MYSQL *mysql);
+#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
+ MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
+ my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
+ int (*stmt_execute)(MYSQL_STMT *stmt);
+ int (*read_binary_rows)(MYSQL_STMT *stmt);
+ int (*unbuffered_fetch)(MYSQL *mysql, char **row);
+ void (*free_embedded_thd)(MYSQL *mysql);
+ const char *(*read_statistics)(MYSQL *mysql);
+ my_bool (*next_result)(MYSQL *mysql);
+ int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
+#endif
+} MYSQL_METHODS;
+
+
+MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql);
+int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
+ unsigned long length);
+int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt);
+int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt);
+int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
+ unsigned int column,
+ unsigned long offset);
+int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt);
+unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt);
+my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
+ enum enum_stmt_attr_type attr_type,
+ const void *attr);
+my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
+ enum enum_stmt_attr_type attr_type,
+ void *attr);
+my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
+my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
+my_bool STDCALL mysql_stmt_close(MYSQL_STMT * stmt);
+my_bool STDCALL mysql_stmt_reset(MYSQL_STMT * stmt);
+my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt);
+my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt,
+ unsigned int param_number,
+ const char *data,
+ unsigned long length);
+MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt);
+MYSQL_RES *STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt);
+unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT * stmt);
+const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt);
+const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT * stmt);
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt,
+ MYSQL_ROW_OFFSET offset);
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt);
+void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset);
+my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt);
+my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt);
+my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt);
+unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt);
+
+my_bool STDCALL mysql_commit(MYSQL * mysql);
+my_bool STDCALL mysql_rollback(MYSQL * mysql);
+my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
+my_bool STDCALL mysql_more_results(MYSQL *mysql);
+int STDCALL mysql_next_result(MYSQL *mysql);
+void STDCALL mysql_close(MYSQL *sock);
+
+
+/* status return codes */
+#define MYSQL_NO_DATA 100
+#define MYSQL_DATA_TRUNCATED 101
+
+#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT)
+
+#ifdef USE_OLD_FUNCTIONS
+MYSQL * STDCALL mysql_connect(MYSQL *mysql, const char *host,
+ const char *user, const char *passwd);
+int STDCALL mysql_create_db(MYSQL *mysql, const char *DB);
+int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
+#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT)
+#endif
+#define HAVE_MYSQL_REAL_CONNECT
+
+/*
+ The following functions are mainly exported because of mysqlbinlog;
+ They are not for general usage
+*/
+
+#define simple_command(mysql, command, arg, length, skip_check) \
+ (*(mysql)->methods->advanced_command)(mysql, command, \
+ NullS, 0, arg, length, skip_check)
+unsigned long net_safe_read(MYSQL* mysql);
+
+#ifdef __NETWARE__
+#pragma pack(pop) /* restore alignment */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _mysql_h */
diff --git a/src/mysql/mysql_com.h b/src/mysql/mysql_com.h
new file mode 100644
index 000000000..02a95d699
--- /dev/null
+++ b/src/mysql/mysql_com.h
@@ -0,0 +1,444 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+** Common definition between mysql server & client
+*/
+
+#ifndef _mysql_com_h
+#define _mysql_com_h
+
+#define NAME_LEN 64 /* Field/table name length */
+#define HOSTNAME_LENGTH 60
+#define USERNAME_LENGTH 16
+#define SERVER_VERSION_LENGTH 60
+#define SQLSTATE_LENGTH 5
+
+#define LOCAL_HOST "localhost"
+#define LOCAL_HOST_NAMEDPIPE "."
+
+
+#if defined(__WIN__) && !defined( _CUSTOMCONFIG_)
+#define MYSQL_NAMEDPIPE "MySQL"
+#define MYSQL_SERVICENAME "MySQL"
+#endif /* __WIN__ */
+
+/*
+ You should add new commands to the end of this list, otherwise old
+ servers won't be able to handle them as 'unsupported'.
+*/
+
+enum enum_server_command
+{
+ COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST,
+ COM_CREATE_DB, COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS,
+ COM_PROCESS_INFO, COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING,
+ COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP,
+ COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE,
+ COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE,
+ COM_STMT_RESET, COM_SET_OPTION, COM_STMT_FETCH,
+ /* don't forget to update const char *command_name[] in sql_parse.cc */
+
+ /* Must be last */
+ COM_END
+};
+
+
+/*
+ Length of random string sent by server on handshake; this is also length of
+ obfuscated password, recieved from client
+*/
+#define SCRAMBLE_LENGTH 20
+#define SCRAMBLE_LENGTH_323 8
+/* length of password stored in the db: new passwords are preceeded with '*' */
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1)
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2)
+
+
+#define NOT_NULL_FLAG 1 /* Field can't be NULL */
+#define PRI_KEY_FLAG 2 /* Field is part of a primary key */
+#define UNIQUE_KEY_FLAG 4 /* Field is part of a unique key */
+#define MULTIPLE_KEY_FLAG 8 /* Field is part of a key */
+#define BLOB_FLAG 16 /* Field is a blob */
+#define UNSIGNED_FLAG 32 /* Field is unsigned */
+#define ZEROFILL_FLAG 64 /* Field is zerofill */
+#define BINARY_FLAG 128 /* Field is binary */
+
+/* The following are only sent to new clients */
+#define ENUM_FLAG 256 /* field is an enum */
+#define AUTO_INCREMENT_FLAG 512 /* field is a autoincrement field */
+#define TIMESTAMP_FLAG 1024 /* Field is a timestamp */
+#define SET_FLAG 2048 /* field is a set */
+#define NO_DEFAULT_VALUE_FLAG 4096 /* Field doesn't have default value */
+#define NUM_FLAG 32768 /* Field is num (for clients) */
+#define PART_KEY_FLAG 16384 /* Intern; Part of some key */
+#define GROUP_FLAG 32768 /* Intern: Group field */
+#define UNIQUE_FLAG 65536 /* Intern: Used by sql_yacc */
+#define BINCMP_FLAG 131072 /* Intern: Used by sql_yacc */
+
+#define REFRESH_GRANT 1 /* Refresh grant tables */
+#define REFRESH_LOG 2 /* Start on new log file */
+#define REFRESH_TABLES 4 /* close all tables */
+#define REFRESH_HOSTS 8 /* Flush host cache */
+#define REFRESH_STATUS 16 /* Flush status variables */
+#define REFRESH_THREADS 32 /* Flush thread cache */
+#define REFRESH_SLAVE 64 /* Reset master info and restart slave
+ thread */
+#define REFRESH_MASTER 128 /* Remove all bin logs in the index
+ and truncate the index */
+
+/* The following can't be set with mysql_refresh() */
+#define REFRESH_READ_LOCK 16384 /* Lock tables for read */
+#define REFRESH_FAST 32768 /* Intern flag */
+
+/* RESET (remove all queries) from query cache */
+#define REFRESH_QUERY_CACHE 65536
+#define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */
+#define REFRESH_DES_KEY_FILE 0x40000L
+#define REFRESH_USER_RESOURCES 0x80000L
+
+#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
+#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
+#define CLIENT_LONG_FLAG 4 /* Get all column flags */
+#define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */
+#define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */
+#define CLIENT_COMPRESS 32 /* Can use compression protocol */
+#define CLIENT_ODBC 64 /* Odbc client */
+#define CLIENT_LOCAL_FILES 128 /* Can use LOAD DATA LOCAL */
+#define CLIENT_IGNORE_SPACE 256 /* Ignore spaces before '(' */
+#define CLIENT_PROTOCOL_41 512 /* New 4.1 protocol */
+#define CLIENT_INTERACTIVE 1024 /* This is an interactive client */
+#define CLIENT_SSL 2048 /* Switch to SSL after handshake */
+#define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */
+#define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */
+#define CLIENT_RESERVED 16384 /* Old flag for 4.1 protocol */
+#define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */
+#define CLIENT_MULTI_STATEMENTS 65536 /* Enable/disable multi-stmt support */
+#define CLIENT_MULTI_RESULTS 131072 /* Enable/disable multi-results */
+#define CLIENT_REMEMBER_OPTIONS (((ulong) 1) << 31)
+
+#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
+#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
+#define SERVER_STATUS_MORE_RESULTS 4 /* More results on server */
+#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
+#define SERVER_QUERY_NO_GOOD_INDEX_USED 16
+#define SERVER_QUERY_NO_INDEX_USED 32
+/*
+ The server was able to fulfill the clients request and opened a
+ read-only non-scrollable cursor for a query. This flag comes
+ in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
+*/
+#define SERVER_STATUS_CURSOR_EXISTS 64
+/*
+ This flag is sent when a read-only cursor is exhausted, in reply to
+ COM_STMT_FETCH command.
+*/
+#define SERVER_STATUS_LAST_ROW_SENT 128
+#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
+#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512
+
+#define MYSQL_ERRMSG_SIZE 512
+#define NET_READ_TIMEOUT 30 /* Timeout on read */
+#define NET_WRITE_TIMEOUT 60 /* Timeout on write */
+#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */
+
+#define ONLY_KILL_QUERY 1
+
+struct st_vio; /* Only C */
+typedef struct st_vio Vio;
+
+#define MAX_TINYINT_WIDTH 3 /* Max width for a TINY w.o. sign */
+#define MAX_SMALLINT_WIDTH 5 /* Max width for a SHORT w.o. sign */
+#define MAX_MEDIUMINT_WIDTH 8 /* Max width for a INT24 w.o. sign */
+#define MAX_INT_WIDTH 10 /* Max width for a LONG w.o. sign */
+#define MAX_BIGINT_WIDTH 20 /* Max width for a LONGLONG */
+#define MAX_CHAR_WIDTH 255 /* Max length for a CHAR colum */
+#define MAX_BLOB_WIDTH 8192 /* Default width for blob */
+
+typedef struct st_net {
+#if !defined(CHECK_EMBEDDED_DIFFERENCES) || !defined(EMBEDDED_LIBRARY)
+ Vio* vio;
+ unsigned char *buff,*buff_end,*write_pos,*read_pos;
+ my_socket fd; /* For Perl DBI/dbd */
+ unsigned long max_packet,max_packet_size;
+ unsigned int pkt_nr,compress_pkt_nr;
+ unsigned int write_timeout, read_timeout, retry_count;
+ int fcntl;
+ my_bool compress;
+ /*
+ The following variable is set if we are doing several queries in one
+ command ( as in LOAD TABLE ... FROM MASTER ),
+ and do not want to confuse the client with OK at the wrong time
+ */
+ unsigned long remain_in_buf,length, buf_length, where_b;
+ unsigned int *return_status;
+ unsigned char reading_or_writing;
+ char save_char;
+ my_bool no_send_ok; /* For SPs and other things that do multiple stmts */
+ my_bool no_send_eof; /* For SPs' first version read-only cursors */
+ /*
+ Set if OK packet is already sent, and we do not need to send error
+ messages
+ */
+ my_bool no_send_error;
+ /*
+ Pointer to query object in query cache, do not equal NULL (0) for
+ queries in cache that have not stored its results yet
+ */
+#endif
+ char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1];
+ unsigned int last_errno;
+ unsigned char error;
+ gptr query_cache_query;
+ my_bool report_error; /* We should report error (we have unreported error) */
+ my_bool return_errno;
+} NET;
+
+#define packet_error (~(unsigned long) 0)
+
+enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
+ MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
+ MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
+ MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP,
+ MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,
+ MYSQL_TYPE_DATE, MYSQL_TYPE_TIME,
+ MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
+ MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
+ MYSQL_TYPE_BIT,
+ MYSQL_TYPE_NEWDECIMAL=246,
+ MYSQL_TYPE_ENUM=247,
+ MYSQL_TYPE_SET=248,
+ MYSQL_TYPE_TINY_BLOB=249,
+ MYSQL_TYPE_MEDIUM_BLOB=250,
+ MYSQL_TYPE_LONG_BLOB=251,
+ MYSQL_TYPE_BLOB=252,
+ MYSQL_TYPE_VAR_STRING=253,
+ MYSQL_TYPE_STRING=254,
+ MYSQL_TYPE_GEOMETRY=255
+
+};
+
+/* For backward compatibility */
+#define CLIENT_MULTI_QUERIES CLIENT_MULTI_STATEMENTS
+#define FIELD_TYPE_DECIMAL MYSQL_TYPE_DECIMAL
+#define FIELD_TYPE_NEWDECIMAL MYSQL_TYPE_NEWDECIMAL
+#define FIELD_TYPE_TINY MYSQL_TYPE_TINY
+#define FIELD_TYPE_SHORT MYSQL_TYPE_SHORT
+#define FIELD_TYPE_LONG MYSQL_TYPE_LONG
+#define FIELD_TYPE_FLOAT MYSQL_TYPE_FLOAT
+#define FIELD_TYPE_DOUBLE MYSQL_TYPE_DOUBLE
+#define FIELD_TYPE_NULL MYSQL_TYPE_NULL
+#define FIELD_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP
+#define FIELD_TYPE_LONGLONG MYSQL_TYPE_LONGLONG
+#define FIELD_TYPE_INT24 MYSQL_TYPE_INT24
+#define FIELD_TYPE_DATE MYSQL_TYPE_DATE
+#define FIELD_TYPE_TIME MYSQL_TYPE_TIME
+#define FIELD_TYPE_DATETIME MYSQL_TYPE_DATETIME
+#define FIELD_TYPE_YEAR MYSQL_TYPE_YEAR
+#define FIELD_TYPE_NEWDATE MYSQL_TYPE_NEWDATE
+#define FIELD_TYPE_ENUM MYSQL_TYPE_ENUM
+#define FIELD_TYPE_SET MYSQL_TYPE_SET
+#define FIELD_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB
+#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB
+#define FIELD_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB
+#define FIELD_TYPE_BLOB MYSQL_TYPE_BLOB
+#define FIELD_TYPE_VAR_STRING MYSQL_TYPE_VAR_STRING
+#define FIELD_TYPE_STRING MYSQL_TYPE_STRING
+#define FIELD_TYPE_CHAR MYSQL_TYPE_TINY
+#define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM
+#define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY
+#define FIELD_TYPE_BIT MYSQL_TYPE_BIT
+
+
+/* Shutdown/kill enums and constants */
+
+/* Bits for THD::killable. */
+#define MYSQL_SHUTDOWN_KILLABLE_CONNECT (unsigned char)(1 << 0)
+#define MYSQL_SHUTDOWN_KILLABLE_TRANS (unsigned char)(1 << 1)
+#define MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE (unsigned char)(1 << 2)
+#define MYSQL_SHUTDOWN_KILLABLE_UPDATE (unsigned char)(1 << 3)
+
+enum mysql_enum_shutdown_level {
+ /*
+ We want levels to be in growing order of hardness (because we use number
+ comparisons). Note that DEFAULT does not respect the growing property, but
+ it's ok.
+ */
+ SHUTDOWN_DEFAULT = 0,
+ /* wait for existing connections to finish */
+ SHUTDOWN_WAIT_CONNECTIONS= MYSQL_SHUTDOWN_KILLABLE_CONNECT,
+ /* wait for existing trans to finish */
+ SHUTDOWN_WAIT_TRANSACTIONS= MYSQL_SHUTDOWN_KILLABLE_TRANS,
+ /* wait for existing updates to finish (=> no partial MyISAM update) */
+ SHUTDOWN_WAIT_UPDATES= MYSQL_SHUTDOWN_KILLABLE_UPDATE,
+ /* flush InnoDB buffers and other storage engines' buffers*/
+ SHUTDOWN_WAIT_ALL_BUFFERS= (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1),
+ /* don't flush InnoDB buffers, flush other storage engines' buffers*/
+ SHUTDOWN_WAIT_CRITICAL_BUFFERS= (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1,
+ /* Now the 2 levels of the KILL command */
+#if MYSQL_VERSION_ID >= 50000
+ KILL_QUERY= 254,
+#endif
+ KILL_CONNECTION= 255
+};
+
+
+enum enum_cursor_type
+{
+ CURSOR_TYPE_NO_CURSOR= 0,
+ CURSOR_TYPE_READ_ONLY= 1,
+ CURSOR_TYPE_FOR_UPDATE= 2,
+ CURSOR_TYPE_SCROLLABLE= 4
+};
+
+
+/* options for mysql_set_option */
+enum enum_mysql_set_option
+{
+ MYSQL_OPTION_MULTI_STATEMENTS_ON,
+ MYSQL_OPTION_MULTI_STATEMENTS_OFF
+};
+
+#define net_new_transaction(net) ((net)->pkt_nr=0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+my_bool my_net_init(NET *net, Vio* vio);
+void my_net_local_init(NET *net);
+void net_end(NET *net);
+void net_clear(NET *net);
+my_bool net_realloc(NET *net, unsigned long length);
+my_bool net_flush(NET *net);
+my_bool my_net_write(NET *net,const char *packet,unsigned long len);
+my_bool net_write_command(NET *net,unsigned char command,
+ const char *header, unsigned long head_len,
+ const char *packet, unsigned long len);
+int net_real_write(NET *net,const char *packet,unsigned long len);
+unsigned long my_net_read(NET *net);
+
+/*
+ The following function is not meant for normal usage
+ Currently it's used internally by manager.c
+*/
+struct sockaddr;
+int my_connect(my_socket s, const struct sockaddr *name, unsigned int namelen,
+ unsigned int timeout);
+
+struct rand_struct {
+ unsigned long seed1,seed2,max_value;
+ double max_value_dbl;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+ /* The following is for user defined functions */
+
+enum Item_result {STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT,
+ DECIMAL_RESULT};
+
+typedef struct st_udf_args
+{
+ unsigned int arg_count; /* Number of arguments */
+ enum Item_result *arg_type; /* Pointer to item_results */
+ char **args; /* Pointer to argument */
+ unsigned long *lengths; /* Length of string arguments */
+ char *maybe_null; /* Set to 1 for all maybe_null args */
+ char **attributes; /* Pointer to attribute name */
+ unsigned long *attribute_lengths; /* Length of attribute arguments */
+} UDF_ARGS;
+
+ /* This holds information about the result */
+
+typedef struct st_udf_init
+{
+ my_bool maybe_null; /* 1 if function can return NULL */
+ unsigned int decimals; /* for real functions */
+ unsigned long max_length; /* For string functions */
+ char *ptr; /* free pointer for function data */
+ my_bool const_item; /* 0 if result is independent of arguments */
+} UDF_INIT;
+
+ /* Constants when using compression */
+#define NET_HEADER_SIZE 4 /* standard header size */
+#define COMP_HEADER_SIZE 3 /* compression header extra size */
+
+ /* Prototypes to password functions */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ These functions are used for authentication by client and server and
+ implemented in sql/password.c
+*/
+
+void randominit(struct rand_struct *, unsigned long seed1,
+ unsigned long seed2);
+double my_rnd(struct rand_struct *);
+void create_random_string(char *to, unsigned int length, struct rand_struct *rand_st);
+
+void hash_password(unsigned long *to, const char *password, unsigned int password_len);
+void make_scrambled_password_323(char *to, const char *password);
+void scramble_323(char *to, const char *message, const char *password);
+my_bool check_scramble_323(const char *, const char *message,
+ unsigned long *salt);
+void get_salt_from_password_323(unsigned long *res, const char *password);
+void make_password_from_salt_323(char *to, const unsigned long *salt);
+
+void make_scrambled_password(char *to, const char *password);
+void scramble(char *to, const char *message, const char *password);
+my_bool check_scramble(const char *reply, const char *message,
+ const unsigned char *hash_stage2);
+void get_salt_from_password(unsigned char *res, const char *password);
+void make_password_from_salt(char *to, const unsigned char *hash_stage2);
+char *octet2hex(char *to, const char *str, unsigned int len);
+
+/* end of password.c */
+
+char *get_tty_password(char *opt_message);
+const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
+
+/* Some other useful functions */
+
+my_bool my_init(void);
+extern int modify_defaults_file(const char *file_location, const char *option,
+ const char *option_value,
+ const char *section_name, int remove_option);
+int load_defaults(const char *conf_file, const char **groups,
+ int *argc, char ***argv);
+my_bool my_thread_init(void);
+void my_thread_end(void);
+
+#ifdef _global_h
+ulong STDCALL net_field_length(uchar **packet);
+my_ulonglong net_field_length_ll(uchar **packet);
+char *net_store_length(char *pkg, ulonglong length);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#define NULL_LENGTH ((unsigned long) ~0) /* For net_store_length */
+#define MYSQL_STMT_HEADER 4
+#define MYSQL_LONG_DATA_HEADER 6
+
+#endif
diff --git a/src/mysql/mysql_time.h b/src/mysql/mysql_time.h
new file mode 100644
index 000000000..75683d39b
--- /dev/null
+++ b/src/mysql/mysql_time.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _mysql_time_h_
+#define _mysql_time_h_
+
+/*
+ Time declarations shared between the server and client API:
+ you should not add anything to this header unless it's used
+ (and hence should be visible) in mysql.h.
+ If you're looking for a place to add new time-related declaration,
+ it's most likely my_time.h. See also "C API Handling of Date
+ and Time Values" chapter in documentation.
+*/
+
+enum enum_mysql_timestamp_type
+{
+ MYSQL_TIMESTAMP_NONE= -2, MYSQL_TIMESTAMP_ERROR= -1,
+ MYSQL_TIMESTAMP_DATE= 0, MYSQL_TIMESTAMP_DATETIME= 1, MYSQL_TIMESTAMP_TIME= 2
+};
+
+
+/*
+ Structure which is used to represent datetime values inside MySQL.
+
+ We assume that values in this structure are normalized, i.e. year <= 9999,
+ month <= 12, day <= 31, hour <= 23, hour <= 59, hour <= 59. Many functions
+ in server such as my_system_gmt_sec() or make_time() family of functions
+ rely on this (actually now usage of make_*() family relies on a bit weaker
+ restriction). Also functions that produce MYSQL_TIME as result ensure this.
+ There is one exception to this rule though if this structure holds time
+ value (time_type == MYSQL_TIMESTAMP_TIME) days and hour member can hold
+ bigger values.
+*/
+typedef struct st_mysql_time
+{
+ unsigned int year, month, day, hour, minute, second;
+ unsigned long second_part;
+ my_bool neg;
+ enum enum_mysql_timestamp_type time_type;
+} MYSQL_TIME;
+
+#endif /* _mysql_time_h_ */
diff --git a/src/mysql/mysql_version.h b/src/mysql/mysql_version.h
new file mode 100644
index 000000000..03683b203
--- /dev/null
+++ b/src/mysql/mysql_version.h
@@ -0,0 +1,29 @@
+/* Copyright Abandoned 1996, 1999, 2001 MySQL AB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Version numbers for protocol & mysqld */
+
+#ifndef _mysql_version_h
+#define _mysql_version_h
+#ifdef _CUSTOMCONFIG_
+#include <custom_conf.h>
+#else
+#define PROTOCOL_VERSION 10
+#define MYSQL_SERVER_VERSION "5.0.16"
+#define MYSQL_BASE_VERSION "mysqld-5.0"
+#define MYSQL_SERVER_SUFFIX_DEF ""
+#define FRM_VER 6
+#define MYSQL_VERSION_ID 50016
+#define MYSQL_PORT 3306
+#define MYSQL_UNIX_ADDR "/tmp/mysql.sock"
+#define MYSQL_CONFIG_NAME "my"
+#define MYSQL_COMPILATION_COMMENT "Official MySQL binary"
+
+/* mysqld compile time options */
+#endif /* _CUSTOMCONFIG_ */
+
+#ifndef LICENSE
+#define LICENSE GPL
+#endif /* LICENSE */
+
+#endif /* _mysql_version_h */
diff --git a/src/mysql/raid.h b/src/mysql/raid.h
new file mode 100644
index 000000000..519ba9e4d
--- /dev/null
+++ b/src/mysql/raid.h
@@ -0,0 +1,159 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Parser needs these defines always, even if USE_RAID is not defined */
+#define RAID_TYPE_0 1 /* Striping */
+#define RAID_TYPE_x 2 /* Some new modes */
+#define RAID_TYPE_y 3
+
+#define RAID_DEFAULT_CHUNKS 4
+#define RAID_DEFAULT_CHUNKSIZE 256*1024 /* 256kB */
+
+C_MODE_START
+#define my_raid_type(raid_type) raid_type_string[(int)(raid_type)]
+extern const char *raid_type_string[];
+C_MODE_END
+
+#ifdef DONT_USE_RAID
+#undef USE_RAID
+#endif
+#if defined(USE_RAID)
+
+#include "my_dir.h"
+
+/* Trap all occurences of my_...() in source and use our wrapper around this function */
+
+#ifdef MAP_TO_USE_RAID
+#define my_read(A,B,C,D) my_raid_read(A,B,C,D)
+#define my_write(A,B,C,D) my_raid_write(A,B,C,D)
+#define my_pwrite(A,B,C,D,E) my_raid_pwrite(A,B,C,D,E)
+#define my_pread(A,B,C,D,E) my_raid_pread(A,B,C,D,E)
+#define my_chsize(A,B,C,D) my_raid_chsize(A,B,C,D)
+#define my_close(A,B) my_raid_close(A,B)
+#define my_tell(A,B) my_raid_tell(A,B)
+#define my_seek(A,B,C,D) my_raid_seek(A,B,C,D)
+#define my_lock(A,B,C,D,E) my_raid_lock(A,B,C,D,E)
+#define my_fstat(A,B,C) my_raid_fstat(A,B,C)
+#endif /* MAP_TO_USE_RAID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void init_raid(void);
+ void end_raid(void);
+
+ bool is_raid(File fd);
+ File my_raid_create(const char *FileName, int CreateFlags, int access_flags,
+ uint raid_type, uint raid_chunks, ulong raid_chunksize,
+ myf MyFlags);
+ File my_raid_open(const char *FileName, int Flags,
+ uint raid_type, uint raid_chunks, ulong raid_chunksize,
+ myf MyFlags);
+ int my_raid_rename(const char *from, const char *to, uint raid_chunks,
+ myf MyFlags);
+ int my_raid_delete(const char *from, uint raid_chunks, myf MyFlags);
+ int my_raid_redel(const char *old_name, const char *new_name,
+ uint raid_chunks, myf MyFlags);
+
+ my_off_t my_raid_seek(File fd, my_off_t pos, int whence, myf MyFlags);
+ my_off_t my_raid_tell(File fd, myf MyFlags);
+
+ uint my_raid_write(File,const byte *Buffer, uint Count, myf MyFlags);
+ uint my_raid_read(File Filedes, byte *Buffer, uint Count, myf MyFlags);
+
+ uint my_raid_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
+ myf MyFlags);
+ uint my_raid_pwrite(int Filedes, const byte *Buffer, uint Count,
+ my_off_t offset, myf MyFlags);
+
+ int my_raid_lock(File,int locktype, my_off_t start, my_off_t length,
+ myf MyFlags);
+ int my_raid_chsize(File fd, my_off_t newlength, int filler, myf MyFlags);
+ int my_raid_close(File, myf MyFlags);
+ int my_raid_fstat(int Filedes, struct stat *buf, myf MyFlags);
+
+#ifdef __cplusplus
+}
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+class RaidName {
+ public:
+ RaidName(const char *FileName);
+ ~RaidName();
+ bool IsRaid();
+ int Rename(const char * from, const char * to, myf MyFlags);
+ private:
+ uint _raid_type; /* RAID_TYPE_0 or RAID_TYPE_1 or RAID_TYPE_5 */
+ uint _raid_chunks; /* 1..n */
+ ulong _raid_chunksize; /* 1..n in bytes */
+};
+
+class RaidFd {
+ public:
+ RaidFd(uint raid_type, uint raid_chunks , ulong raid_chunksize);
+ ~RaidFd();
+ File Create(const char *FileName, int CreateFlags, int access_flags,
+ myf MyFlags);
+ File Open(const char *FileName, int Flags, myf MyFlags);
+ my_off_t Seek(my_off_t pos,int whence,myf MyFlags);
+ my_off_t Tell(myf MyFlags);
+ int Write(const byte *Buffer, uint Count, myf MyFlags);
+ int Read(const byte *Buffer, uint Count, myf MyFlags);
+ int Lock(int locktype, my_off_t start, my_off_t length, myf MyFlags);
+ int Chsize(File fd, my_off_t newlength, int filler, myf MyFlags);
+ int Fstat(int fd, MY_STAT *stat_area, myf MyFlags );
+ int Close(myf MyFlags);
+ static bool IsRaid(File fd);
+ static DYNAMIC_ARRAY _raid_map; /* Map of RaidFD* */
+ private:
+
+ uint _raid_type; /* RAID_TYPE_0 or RAID_TYPE_1 or RAID_TYPE_5 */
+ uint _raid_chunks; /* 1..n */
+ ulong _raid_chunksize; /* 1..n in bytes */
+
+ ulong _total_block; /* We are operating with block no x (can be 0..many). */
+ uint _this_block; /* can be 0.._raid_chunks */
+ uint _remaining_bytes; /* Maximum bytes that can be written in this block */
+
+ my_off_t _position;
+ my_off_t _size; /* Cached file size for faster seek(SEEK_END) */
+ File _fd;
+ File *_fd_vector; /* Array of File */
+ off_t *_seek_vector; /* Array of cached seek positions */
+
+ inline void Calculate()
+ {
+ DBUG_ENTER("RaidFd::_Calculate");
+ DBUG_PRINT("info",("_position: %lu _raid_chunksize: %d, _size: %lu",
+ (ulong) _position, _raid_chunksize, (ulong) _size));
+
+ _total_block = (ulong) (_position / _raid_chunksize);
+ _this_block = _total_block % _raid_chunks; /* can be 0.._raid_chunks */
+ _remaining_bytes = (uint) (_raid_chunksize -
+ (_position - _total_block * _raid_chunksize));
+ DBUG_PRINT("info",
+ ("_total_block: %d this_block: %d _remaining_bytes:%d",
+ _total_block, _this_block, _remaining_bytes));
+ DBUG_VOID_RETURN;
+ }
+};
+
+#endif /* __cplusplus */
+#endif /* USE_RAID */
diff --git a/src/mysql/typelib.h b/src/mysql/typelib.h
new file mode 100644
index 000000000..4280bab43
--- /dev/null
+++ b/src/mysql/typelib.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#ifndef _typelib_h
+#define _typelib_h
+
+typedef struct st_typelib { /* Different types saved here */
+ unsigned int count; /* How many types */
+ const char *name; /* Name of typelib */
+ const char **type_names;
+ unsigned int *type_lengths;
+} TYPELIB;
+
+extern int find_type(char *x,TYPELIB *typelib,unsigned int full_name);
+extern void make_type(char *to,unsigned int nr,TYPELIB *typelib);
+extern const char *get_type(TYPELIB *typelib,unsigned int nr);
+
+extern TYPELIB sql_protocol_typelib;
+
+#endif /* _typelib_h */
diff --git a/src/plugins/Makefile b/src/plugins/Makefile
new file mode 100644
index 000000000..430f632be
--- /dev/null
+++ b/src/plugins/Makefile
@@ -0,0 +1,46 @@
+
+OBJECTS = sample.dll sig.dll pid.dll gui.dll upnp.dll httpd.dll
+
+ifdef CYGWIN
+ PLUGINEXT = dll
+else
+ PLUGINEXT = so
+endif
+
+PLUGINS = $(OBJECTS:%.dll=%.$(PLUGINEXT))
+COMMON_H = ../common/plugin.h
+
+txt sql all: $(PLUGINS)
+
+%.$(PLUGINEXT): %.c
+ $(CC) $(CFLAGS) -shared -o ../../plugins/$@ $<
+ @touch $@
+
+httpd.$(PLUGINEXT): httpd.c
+ $(CC) $(CFLAGS) -shared -o ../../plugins/$@ $< \
+ ../common/obj/minimalloc.o ../common/obj/db.o ../common/obj/showmsg.o \
+ ../common/obj/utils.o
+ @touch $@
+
+gui.$(PLUGINEXT): ../../plugins/gui.conf
+httpd.$(PLUGINEXT): ../../plugins/httpd.conf
+upnp.$(PLUGINEXT): ../../plugins/upnp.conf
+
+../../plugins/%.conf: %.txt
+ cp -r $< $@
+
+../../plugins/gui.conf: gui.txt
+../../plugins/httpd.conf: httpd.txt
+../../plugins/upnp.conf: upnp.txt
+
+depend:
+ makedepend -fGNUmakefile -o.$(PLUGINEXT) -Y. -Y../common *.c
+clean:
+ rm -rf $(PLUGINS)
+
+# DO NOT DELETE
+
+sample.$(PLUGINEXT): sample.c $(COMMON_H)
+sig.$(PLUGINEXT): sig.c $(COMMON_H)
+pid.$(PLUGINEXT): pid.c $(COMMON_H)
+gui.$(PLUGINEXT): gui.c $(COMMON_H)
diff --git a/src/plugins/gui.c b/src/plugins/gui.c
new file mode 100644
index 000000000..6c9a8d85c
--- /dev/null
+++ b/src/plugins/gui.c
@@ -0,0 +1,101 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../common/plugin.h"
+//Needed for strcmpi
+#include "../common/mmo.h"
+
+// "I'm Alive" and "Flush stdout" Originally by Mugendai
+// Ported to plugin by Celest
+
+PLUGIN_INFO = {
+ "AthenaGUI",
+ PLUGIN_CORE,
+ "1.0",
+ PLUGIN_VERSION,
+ "Core plugin for Athena GUI functions"
+};
+
+PLUGIN_EVENTS_TABLE = {
+ { "gui_init", "Plugin_Init" },
+ { NULL, NULL }
+};
+
+unsigned int (*gettick)();
+int (*add_timer_func_list)(int (*)(int,unsigned int,int,int),char*);
+int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+void gui_init ()
+{
+ char line[1024], w1[1024], w2[1024];
+ int flush_on = 0;
+ int flush_time = 100;
+ int imalive_on = 0;
+ int imalive_time = 30;
+ char **argv;
+ int *argc;
+ FILE *fp;
+ int i;
+
+ IMPORT_SYMBOL(argc, 2);
+ IMPORT_SYMBOL(argv, 3);
+ IMPORT_SYMBOL(gettick, 5);
+ IMPORT_SYMBOL(add_timer_interval, 8);
+ IMPORT_SYMBOL(add_timer_func_list, 9);
+
+ do {
+ fp = fopen("plugins/gui.conf","r");
+ if (fp == NULL)
+ break;
+
+ while(fgets(line, sizeof(line) -1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if(strcmpi(w1,"imalive_on")==0){
+ imalive_on = atoi(w2);
+ } else if(strcmpi(w1,"imalive_time")==0){
+ imalive_time = atoi(w2);
+ } else if(strcmpi(w1,"flush_on")==0){
+ flush_on = atoi(w2);
+ } else if(strcmpi(w1,"flush_time")==0){
+ flush_time = atoi(w2);
+ }
+ }
+ }
+ fclose(fp);
+ } while (0);
+
+ for (i = 1; i < *argc ; i++)
+ if (strcmp(argv[i], "--gui") == 0)
+ flush_on = imalive_on = 1;
+
+ if (flush_on) {
+ add_timer_func_list(flush_timer, "flush_timer");
+ add_timer_interval(gettick()+1000,flush_timer,0,0,flush_time);
+ }
+ if (imalive_on) {
+ add_timer_func_list(imalive_timer, "imalive_timer");
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+ }
+}
diff --git a/src/plugins/gui.txt b/src/plugins/gui.txt
new file mode 100644
index 000000000..87f5ac742
--- /dev/null
+++ b/src/plugins/gui.txt
@@ -0,0 +1,15 @@
+//
+// GUI Plugin Configuration
+//
+
+// Enable I'm Alive?
+imalive_on: 0
+
+// How often to display I'm Alive (in seconds)
+imalive_time: 30
+
+// Enable GUI flushing for Mugendai's GUI?
+flush_on: 0
+
+// How often to flush the buffer on-screen (in seconds)
+flush_time: 60 \ No newline at end of file
diff --git a/src/plugins/httpd.c b/src/plugins/httpd.c
new file mode 100644
index 000000000..de994766f
--- /dev/null
+++ b/src/plugins/httpd.c
@@ -0,0 +1,751 @@
+
+#ifdef __WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/db.h"
+//mmo required for definition of stricmp
+#include "../common/mmo.h"
+#include "../common/utils.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/plugin.h"
+//#include "httpd.h"
+
+/** Created by End_of_Exam, ported to plugin and modified by Celest **/
+
+PLUGIN_INFO = {
+ "HttpDaemon",
+ PLUGIN_CORE,
+ "0.1",
+ PLUGIN_VERSION,
+ "HTTP Daemon"
+};
+
+PLUGIN_EVENTS_TABLE = {
+ { "do_init", "Plugin_Init" },
+ { "do_final", "Plugin_Final" },
+ { NULL, NULL }
+};
+
+enum HTTPD_STATUS {
+ HTTPD_REQUEST_WAIT = 0, // ƒŠƒNƒGƒXƒg‘Ò‚¿
+ HTTPD_REQUEST_WAIT_POST, // ƒŠƒNƒGƒXƒg‘Ò‚¿(post)
+ HTTPD_REQUEST_OK, // ƒŠƒNƒGƒXƒg‰ðŽßŠ®—¹
+ HTTPD_SEND_HEADER, // ƒwƒbƒ_‘—MŠ®—¹
+ HTTPD_WAITING_SEND // ƒf[ƒ^‚ª‘—M‚µI‚í‚é‚Ü‚Å‘Ò‚Á‚Ä‚¢‚éó‘Ô
+};
+enum {
+ HTTPD_METHOD_UNKNOWN = 0,
+ HTTPD_METHOD_GET,
+ HTTPD_METHOD_POST
+};
+
+struct httpd_session_data {
+ int fd;
+ int status;
+ int http_ver;
+ int header_len;
+ int data_len;
+ int method;
+ int persist;
+ int request_count;
+ unsigned int tick;
+ const unsigned char* url;
+ const unsigned char* query;
+};
+
+// undefine socket operations included from socket.h,
+// since we are going to use 'sessiond' instead
+#undef RFIFOP
+#undef RFIFOREST
+#undef WFIFOP
+
+#define RFIFOP(fd,pos) (sessiond[fd]->rdata+sessiond[fd]->rdata_pos+(pos))
+#define RFIFOREST(fd) (sessiond[fd]->rdata_size-sessiond[fd]->rdata_pos)
+#define WFIFOP(fd,pos) (sessiond[fd]->wdata+sessiond[fd]->wdata_size+(pos))
+
+struct socket_data **sessiond;
+char *server_type;
+unsigned int (*gettick)();
+int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
+int *max_fd;
+int (*delete_sessiond)(int);
+int (*_WFIFOSET)(int,int);
+int (*_RFIFOSKIP)(int,int);
+
+static int max_persist_requests = 32; // Ž‘±’ÊM‚Å‚Ìő僊ƒNƒGƒXƒg”
+static int request_timeout[] = { 2500, 60*1000 }; // ƒ^ƒCƒ€ƒAƒEƒg(ʼnAŽ‘±)
+static char document_root[256] = "./httpd/"; // ƒhƒLƒ…ƒƒ“ƒgƒ‹[ƒg
+
+// httpd ‚É“ü‚Á‚Ä‚¢‚éƒy[ƒW‚ÆAŒÄ‚Ño‚·ƒR[ƒ‹ƒoƒbƒNŠÖ”‚̈ꗗ
+struct dbt *httpd_files;
+
+void httpd_send(struct httpd_session_data*, int, const char *, int, const void *);
+
+int httpd_check (struct socket_data *sd)
+{
+ // httpd ‚ɉñ‚·‚Ç‚¤‚©‚Ì”»’肪‚Ü‚¾s‚í‚ê‚Ä‚È‚¢
+ // 擪‚QƒoƒCƒg‚ª GE ‚È‚çhttpd ‚ɉñ‚µ‚Ä‚Ý‚é
+ if (sd->rdata_size >= 2 &&
+ sd->rdata[0] == 'G' && sd->rdata[1] == 'E')
+ return 1;
+
+ return 0;
+}
+
+int httpd_strcasencmp(const char *s1, const char *s2,int len)
+{
+ while(len-- && (*s1 || *s2) ) {
+ if((*s1 | 0x20) != (*s2 | 0x20)) {
+ return ((*s1 | 0x20) > (*s2 | 0x20) ? 1 : -1);
+ }
+ s1++; s2++;
+ }
+ return 0;
+}
+
+// httpd ‚Ƀy[ƒW‚ð’ljÁ‚·‚é
+// for ‚ȂǂŃy[ƒW–¼‚ð‡¬‚Å‚«‚é‚悤‚ÉAkey ‚Ístrdup()‚µ‚½‚à‚Ì‚ðŽg‚¤
+
+void httpd_pages (const char* url, void (*httpd_func)(struct httpd_session_data*, const char*))
+{
+ if (strdb_get(httpd_files,(unsigned char*)(url+1)) == NULL) {
+ strdb_put(httpd_files, (unsigned char*)aStrdup(url+1), httpd_func);
+ } else {
+ strdb_put(httpd_files, (unsigned char*)(url+1), httpd_func);
+ }
+}
+
+static void (*httpd_default)(struct httpd_session_data* sd,const char* url);
+
+const char *httpd_get_error( struct httpd_session_data* sd, int* status )
+{
+ const char* msg;
+ // httpd ‚̃Xƒe[ƒ^ƒX‚ðŒˆ‚ß‚é
+ switch(*status) {
+ case 200: msg = "OK"; break;
+ case 400: msg = "Bad Request"; break;
+ case 401: msg = "Unauthorized"; break; // –¢Žg—p
+ case 403: msg = "Forbidden"; break; // –¢Žg—p
+ case 404: msg = "Not Found"; break;
+ case 408: msg = "Request Timedout"; break;
+ case 411: msg = "Length Required"; break;
+ case 413: msg = "Request Entity Too Large"; break;
+ default:
+ *status = 500; msg = "Internal Server Error"; break;
+ }
+ return msg;
+}
+
+void httpd_send_error(struct httpd_session_data* sd,int status)
+{
+ const char* msg = httpd_get_error( sd, &status );
+ httpd_send(sd, status, "text/plain",strlen(msg),msg);
+}
+
+void httpd_send_head (struct httpd_session_data* sd, int status, const char *content_type, int content_len)
+{
+ char head[256];
+ int len;
+ const char* msg;
+
+ if (sd->status != HTTPD_REQUEST_OK)
+ return;
+ msg = httpd_get_error( sd, &status );
+
+ if(content_len == -1 || ++sd->request_count >= max_persist_requests ) {
+ // ’·‚³‚ª•ª‚©‚ç‚È‚¢ or ƒŠƒNƒGƒXƒgŒÀŠE‚ð’´‚¦‚½‚Ì‚ÅØ’f‚·‚é
+ len = sprintf(
+ head,
+ "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nConnection: close\r\n\r\n",
+ sd->http_ver,status,msg,content_type
+ );
+ sd->persist = 0;
+ len = sprintf(
+ head,
+ "HTTP/1.%d %d %s\r\nContent-Type: %s\r\n\r\n",
+ sd->http_ver,status,msg,content_type
+ );
+ sd->http_ver = 0; // ’·‚³‚ª•ª‚©‚ç‚È‚¢‚Ì‚ÅAHTTP/1.0 ˆµ‚¢(Ž©“®Ø’f)‚É‚·‚é
+ } else {
+ len = sprintf(
+ head,
+ "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n",
+ sd->http_ver,status,msg,content_type,content_len
+ );
+ }
+ memcpy(WFIFOP(sd->fd,0),head,len);
+ _WFIFOSET(sd->fd,len);
+ sd->status = HTTPD_SEND_HEADER;
+ sd->data_len = content_len;
+}
+
+void httpd_send_data (struct httpd_session_data* sd, int content_len, const void *data)
+{
+ const char* msg = (const char*)data;
+ if (sd->status == HTTPD_REQUEST_OK) {
+ // ƒwƒbƒ_‚Ì‘—M–Y‚ê‚Ä‚¢‚é‚Ì‚ÅA“K“–‚ɕ₤
+ httpd_send_head(sd,200,"application/octet-stream",-1);
+ } else if(sd->status != HTTPD_SEND_HEADER && sd->status != HTTPD_WAITING_SEND) {
+ return;
+ }
+ sd->data_len -= content_len;
+
+ // ‹‘å‚ȃTƒCƒY‚̃tƒ@ƒCƒ‹‚à‘—Mo—ˆ‚é‚悤‚É•ªŠ„‚µ‚Ä‘—‚é
+ while (content_len > 0) {
+ int send_byte = content_len;
+ if(send_byte > 12*1024) send_byte = 12*1024;
+ memcpy(WFIFOP(sd->fd,0),msg,send_byte);
+ _WFIFOSET(sd->fd,send_byte);
+ msg += send_byte; content_len -= send_byte;
+ }
+ sd->status = HTTPD_WAITING_SEND;
+}
+
+void httpd_send (struct httpd_session_data* sd, int status, const char *content_type, int content_len, const void *data)
+{
+ httpd_send_head(sd,status,content_type,content_len);
+ httpd_send_data(sd,content_len,data);
+}
+
+void httpd_parse_header(struct httpd_session_data* sd);
+void httpd_parse_request_ok(struct httpd_session_data *sd);
+
+int httpd_parse (int fd)
+{
+ struct httpd_session_data *sd = (struct httpd_session_data *)sessiond[fd]->session_data2;
+ if (sessiond[fd]->eof) {
+ delete_sessiond(fd);
+ return 0;
+ }
+ if (sd == NULL) {
+ sd = (struct httpd_session_data*) aMalloc (sizeof(struct httpd_session_data));
+ sd->fd = fd;
+ sessiond[fd]->session_data2 = sd;
+ sd->tick = gettick();
+ sd->persist = 0;
+ sd->request_count = 0;
+ }
+ printf ("status %d\n", sd->status);
+ switch(sd->status) {
+ case HTTPD_REQUEST_WAIT:
+ // ƒŠƒNƒGƒXƒg‘Ò‚¿
+ if(RFIFOREST(fd) > 1024) {
+ // ƒŠƒNƒGƒXƒg‚ª’·‚·‚¬‚é‚Ì‚ÅAƒGƒ‰[ˆµ‚¢‚·‚é
+ sd->status = HTTPD_REQUEST_OK;
+ httpd_send_error(sd,400); // Bad Request
+ } else if( (int)( gettick() - sd->tick ) > request_timeout[sd->persist] ) {
+ // ƒŠƒNƒGƒXƒg‚ÉŽžŠÔ‚ª‚©‚©‚è‚·‚¬‚Ä‚¢‚é‚Ì‚ÅAƒGƒ‰[ˆµ‚¢‚·‚é
+ sd->status = HTTPD_REQUEST_OK;
+ httpd_send_error(sd,408); // Request Timeout
+ } else if(sd->header_len == RFIFOREST(fd)) {
+ // ó‘Ô‚ªˆÈ‘O‚Æ“¯‚¶‚È‚Ì‚ÅAƒŠƒNƒGƒXƒg‚ðĉðÍ‚·‚é•K—v‚Í–³‚¢
+ } else {
+ int limit = RFIFOREST(fd);
+ unsigned char *req = RFIFOP(fd,0);
+ sd->header_len = RFIFOREST(fd);
+ do {
+ if(*req == '\n' && limit > 0) {
+ limit--; req++;
+ if(*req == '\r' && limit > 0) { limit--; req++; }
+ if(*req == '\n') {
+ // HTTPƒwƒbƒ_‚ÌI“_‚ðŒ©‚Â‚¯‚½
+ *req = 0;
+ sd->header_len = (req - RFIFOP(fd,0)) + 1;
+ httpd_parse_header(sd);
+ break;
+ }
+ }
+ } while(req++,--limit > 0);
+ }
+ break;
+ case HTTPD_REQUEST_WAIT_POST:
+ if(RFIFOREST(sd->fd) >= sd->header_len) {
+ unsigned char temp = RFIFOB(sd->fd,sd->header_len);
+ RFIFOB(sd->fd,sd->header_len) = 0;
+ httpd_parse_request_ok(sd);
+ RFIFOB(sd->fd,sd->header_len) = temp;
+ }
+ break;
+ case HTTPD_REQUEST_OK:
+ case HTTPD_SEND_HEADER:
+ // ƒŠƒNƒGƒXƒg‚ªI‚í‚Á‚½‚܂܉½‚à‘—M‚³‚ê‚Ä‚¢‚È‚¢ó‘Ô‚È‚Ì‚ÅA
+ // ‹­§Ø’f
+ printf ("httpd: eof\n");
+ sessiond[fd]->eof = 1;
+ break;
+ case HTTPD_WAITING_SEND:
+ // ƒf[ƒ^‚Ì‘—M‚ªI‚í‚é‚Ü‚Å‘Ò‹@
+ //if(sessiond[fd]->wdata_size == sessiond[fd]->wdata_pos) {
+ // i *hope* this is correct o.o;
+ if(sessiond[fd]->wdata_size == 0) {
+ // HTTP/1.0‚͎蓮ؒf
+// if(sd->http_ver == 0) {
+ if(sd->persist == 0) {
+ printf ("httpd: eof\n");
+ sessiond[fd]->eof = 1;
+ }
+ // RFIFO ‚©‚烊ƒNƒGƒXƒgƒf[ƒ^‚ÌÁ‹Ž‚Æ\‘¢‘̂̉Šú‰»
+ _RFIFOSKIP(fd,sd->header_len);
+ sd->status = HTTPD_REQUEST_WAIT;
+ sd->tick = gettick();
+ sd->header_len = 0;
+ sd->query = NULL;
+// sd->http_ver = 0; // ver ‚Í•ÛŽ
+ sd->method = HTTPD_METHOD_UNKNOWN;
+ printf("httpd_parse: [% 3d] request sended RFIFOREST:%d\n", fd, RFIFOREST(fd));
+ }
+ break;
+ }
+ return 0;
+}
+
+void httpd_parse_header_sub( struct httpd_session_data* sd, const char *p1, int* plen )
+{
+ int len = 0;
+ // HTTP‚̃o[ƒWƒ‡ƒ“‚𒲸
+ if(!strncmp(p1 ,"HTTP/1.1",8)) {
+ sd->http_ver = 1;
+ sd->persist = 1;
+ } else {
+ sd->http_ver = 0;
+ sd->persist = 0;
+ }
+
+ p1 = strchr(p1,'\n');
+ while(p1) {
+ // Content-Length: ‚Ì’²¸
+ if(!httpd_strcasencmp(p1+1,"Content-Length: ",16)) {
+ len = atoi(p1 + 17);
+ }
+ // Connection: ‚Ì’²¸
+ if(!httpd_strcasencmp(p1+1,"Connection: ",12)) {
+ if( httpd_strcasencmp(p1+13,"close",5)==0 && sd->http_ver==1 )
+ sd->persist = 0;
+ if( httpd_strcasencmp(p1+13,"Keep-Alive",10)==0 && sd->http_ver==0 )
+ sd->persist = 1;
+ }
+ p1 = strchr(p1+1,'\n');
+ }
+ if(plen) *plen = len;
+ return;
+}
+
+void httpd_parse_header(struct httpd_session_data* sd)
+{
+ int i;
+ int status = 400; // Bad Request
+ unsigned char* req = RFIFOP(sd->fd,0);
+ do {
+ if(!strncmp(req,"GET /",5)) {
+ // GET ƒŠƒNƒGƒXƒg
+ req += 5;
+ for(i = 0;req[i]; i++) {
+ if(req[i] == ' ' || req[i] == '?') break;
+ if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break;
+ }
+ if(req[i] == ' ') {
+ req[i] = 0;
+ sd->url = req;
+ sd->query = NULL;
+ sd->status = HTTPD_REQUEST_OK;
+ } else if(req[i] == '?') {
+ req[i] = 0;
+ sd->query = &req[++i];
+ for(;req[i];i++) {
+ if(
+ isalnum(req[i]) || req[i] == '+' || req[i] == '%' || req[i] == '&' ||
+ req[i] == '='
+ ) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if(req[i] != ' ') {
+ break;
+ }
+ req[i] = 0;
+ sd->url = req;
+ } else {
+ break;
+ }
+ // ƒwƒbƒ_‰ðÍ
+ httpd_parse_header_sub( sd, &req[i+1], NULL );
+
+ printf("httpd: request %s %s\n", sd->url, sd->query);
+ sd->method = HTTPD_METHOD_GET;
+ httpd_parse_request_ok(sd);
+ } else if(!strncmp(req,"POST /",6)) {
+ int len;
+ req += 6; status = 404;
+ for(i = 0;req[i]; i++) {
+ if(req[i] == ' ') break;
+ if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break;
+ }
+ if(req[i] != ' ') {
+ break;
+ }
+ req[i] = 0;
+ sd->url = req;
+
+ // ƒwƒbƒ_‰ðÍ
+ httpd_parse_header_sub( sd, &req[i+1], &len );
+
+ if(len <= 0 || len >= 32*1024) {
+ // ‚Æ‚è‚ ‚¦‚¸32KBˆÈã‚̃ŠƒNƒGƒXƒg‚Í•s³ˆµ‚¢
+ status = ( len==0 )? 411 : ( len>32*1024 )? 413 : 400;
+ break;
+ }
+
+ sd->query = RFIFOP(sd->fd,sd->header_len);
+ sd->method = HTTPD_METHOD_POST;
+ sd->header_len += len;
+ if(RFIFOREST(sd->fd) >= sd->header_len) {
+ unsigned char temp = RFIFOB(sd->fd,sd->header_len);
+ RFIFOB(sd->fd,sd->header_len) = 0;
+ httpd_parse_request_ok(sd);
+ RFIFOB(sd->fd,sd->header_len) = temp;
+ } else {
+ // POST‚̃f[ƒ^‚ª‘—‚ç‚ê‚Ä‚­‚é‚Ì‚ð‘Ò‚Â
+ sd->status = HTTPD_REQUEST_WAIT_POST;
+ }
+ } else {
+ break;
+ }
+ } while(0);
+ if(sd->status == HTTPD_REQUEST_WAIT) {
+ sd->status = HTTPD_REQUEST_OK;
+ httpd_send_error(sd,status);
+ }
+}
+
+void httpd_parse_request_ok (struct httpd_session_data *sd)
+{
+ void (*httpd_parse_func)(struct httpd_session_data*,const char*);
+ sd->status = HTTPD_REQUEST_OK;
+
+ // ƒtƒ@ƒCƒ‹–¼‚ª‹‚Ü‚Á‚½‚Ì‚ÅAƒy[ƒW‚ª–³‚¢‚©ŒŸõ‚·‚é
+ // printf("httpd_parse: [% 3d] request /%s\n", fd, req);
+ httpd_parse_func = strdb_get(httpd_files,(unsigned char*)sd->url);
+ if(httpd_parse_func == NULL) {
+ httpd_parse_func = httpd_default;
+ }
+ if(httpd_parse_func == NULL) {
+ httpd_send_error(sd,404); // Not Found
+ } else {
+ httpd_parse_func(sd,sd->url);
+ if(sd->status == HTTPD_REQUEST_OK) {
+ httpd_send_error(sd,404); // Not Found
+ }
+ }
+ if(sd->persist == 1 && sd->data_len) {
+ // ’·‚³‚ª•Ï‚ȃf[ƒ^(‚±‚ñ‚È‚Ì‘—‚é‚È‚æc)
+ printf("httpd_parse: send size mismatch when parsing /%s\n", sd->url);
+ sessiond[sd->fd]->eof = 1;
+ }
+ if(sd->status == HTTPD_REQUEST_OK) {
+ httpd_send_error(sd,404);
+ }
+}
+
+char* httpd_get_value(struct httpd_session_data* sd,const char* val)
+{
+ int src_len = strlen(val);
+ const unsigned char* src_p = sd->query;
+ if(src_p == NULL) return aStrdup("");
+
+ do {
+ if(!memcmp(src_p,val,src_len) && src_p[src_len] == '=') {
+ break;
+ }
+ src_p = strchr(src_p + 1,'&');
+ if(src_p) src_p++;
+ } while(src_p);
+
+ if(src_p != NULL) {
+ // –Ú“I‚Ì•¶Žš—ñ‚ðŒ©‚Â‚¯‚½
+ const unsigned char* p2;
+ int dest_len;
+ char* dest_p;
+ src_p += src_len + 1;
+ p2 = strchr(src_p,'&');
+ if(p2 == NULL) {
+ src_len = strlen(src_p);
+ } else {
+ src_len = (p2 - src_p);
+ }
+ dest_p = aMalloc(src_len + 1);
+ dest_len = 0;
+ while(src_len > 0) {
+ if(*src_p == '%' && src_len > 2) {
+ int c1 = 0,c2 = 0;
+ if(src_p[1] >= '0' && src_p[1] <= '9') c1 = src_p[1] - '0';
+ if(src_p[1] >= 'A' && src_p[1] <= 'F') c1 = src_p[1] - 'A' + 10;
+ if(src_p[1] >= 'a' && src_p[1] <= 'f') c1 = src_p[1] - 'a' + 10;
+ if(src_p[2] >= '0' && src_p[2] <= '9') c2 = src_p[2] - '0';
+ if(src_p[2] >= 'A' && src_p[2] <= 'F') c2 = src_p[2] - 'A' + 10;
+ if(src_p[2] >= 'a' && src_p[2] <= 'f') c2 = src_p[2] - 'a' + 10;
+ dest_p[dest_len++] = (c1 << 4) | c2;
+ src_p += 3; src_len -= 3;
+ } else if(*src_p == '+') {
+ dest_p[dest_len++] = ' ';
+ src_p++; src_len--;
+ } else {
+ dest_p[dest_len++] = *(src_p++); src_len--;
+ }
+ }
+ dest_p[dest_len] = 0;
+ return dest_p;
+ }
+ return aStrdup("");
+}
+
+// MIMEƒ^ƒCƒv”»’èBŽå—v‚È‚à‚Ì‚¾‚¯”»’肵‚ÄAŽc‚è‚Íapplication/octet-stream
+static const char* httpd_mimetype(const char* url)
+{
+ char *ext = strrchr(url,'.');
+ if(ext) {
+ if(!strcmp(ext,".html")) return "text/html";
+ if(!strcmp(ext,".htm")) return "text/html";
+ if(!strcmp(ext,".css")) return "text/css";
+ if(!strcmp(ext,".js")) return "text/javascript";
+ if(!strcmp(ext,".txt")) return "text/plain";
+ if(!strcmp(ext,".gif")) return "image/gif";
+ if(!strcmp(ext,".jpg")) return "image/jpeg";
+ if(!strcmp(ext,".jpeg")) return "image/jpeg";
+ if(!strcmp(ext,".png")) return "image/png";
+ if(!strcmp(ext,".xbm")) return "image/xbm";
+ if(!strcmp(ext,".zip")) return "application/zip";
+ }
+ return "application/octet-stream";
+}
+
+void httpd_send_file(struct httpd_session_data* sd, const char* url)
+{
+ FILE *fp;
+ int file_size;
+ char file_buf[8192];
+ if(sd->status != HTTPD_REQUEST_OK) return;
+ if(url[0] == '\0') url = "index.html";
+
+ // url ‚ÌÅ‘å’·‚Í–ñ1010ƒoƒCƒg‚È‚Ì‚ÅAƒoƒbƒtƒ@ƒI[ƒo[ƒtƒ[‚ÌS”z‚Í–³‚µ
+ sprintf(file_buf, "%s%s", document_root, url);
+
+ fp = fopen(file_buf,"rb");
+ if(fp == NULL) {
+ httpd_send_error(sd,404);
+ } else {
+ fseek(fp,0,SEEK_END);
+ file_size = ftell(fp);
+ fseek(fp,0,SEEK_SET);
+ httpd_send_head(sd,200,httpd_mimetype(url),file_size);
+ while(file_size > 0) {
+ int read_byte = file_size;
+ if(file_size > 8192) read_byte = 8192;
+ fread(file_buf,1,read_byte,fp);
+ httpd_send_data(sd,read_byte,file_buf);
+ file_size -= read_byte;
+ }
+ fclose(fp);
+ }
+}
+
+
+char* httpd_binary_encode(const char* val)
+{
+ char *buf = aMalloc(strlen(val) * 3 + 1);
+ char *p = buf;
+ while(*val) {
+ if(isalnum((unsigned char)*val)) {
+ *(p++) = *(val++);
+ } else {
+ unsigned char c1 = *(val++);
+ unsigned char c2 = (c1 >> 4);
+ unsigned char c3 = (c1 & 0x0F);
+ *(p++) = '%';
+ *(p++) = c2 + (c2 >= 10 ? 'A'-10 : '0');
+ *(p++) = c3 + (c3 >= 10 ? 'A'-10 : '0');
+ }
+ }
+ *p = 0;
+ return buf;
+}
+
+char* httpd_quote_meta(const char* p1)
+{
+ char *buf = aMalloc(strlen(p1) * 6 + 1);
+ char *p2 = buf;
+ while(*p1) {
+ switch(*p1) {
+ case '<': memcpy(p2,"&lt;",4); p2 += 4; p1++; break;
+ case '>': memcpy(p2,"&gt;",4); p2 += 4; p1++; break;
+ case '&': memcpy(p2,"&amp;",5); p2 += 5; p1++; break;
+ case '"': memcpy(p2,"&quot;",6); p2 += 6; p1++; break;
+ default: *(p2++) = *(p1++);
+ }
+ }
+ *p2 = 0;
+ return buf;
+}
+
+///////// Graph / HTML snippets functions /////////////////////////////
+
+struct file_entry {
+ char *filename;
+ struct file_entry *next;
+};
+struct file_entry *fileentry_head = NULL;
+
+static void httpd_graph_load (const char *filename)
+{
+ struct file_entry *entry;
+ char type = *server_type + 'a';
+ int len = strlen(filename);
+
+ if (len <= 7 || filename[len - 7] != type)
+ return;
+
+ entry = fileentry_head;
+ while (entry) {
+ if (strcmpi(entry->filename, filename) == 0)
+ return;
+ entry = entry->next;
+ }
+
+ entry = (struct file_entry *) aMalloc (sizeof(struct file_entry));
+ entry->filename = aStrdup(filename);
+ entry->next = fileentry_head;
+ fileentry_head = entry;
+}
+
+// scan for available html snippets
+static int httpd_graph_find (int tid, unsigned int tick, int id, int data)
+{
+ findfile("httpd", ".graph", httpd_graph_load);
+ return 0;
+}
+
+static void httpd_graph_parse (struct httpd_session_data *sd,const char* url)
+{
+ // output html
+ struct file_entry *entry = fileentry_head;
+ char buf[8192];
+ char *p = buf;
+ FILE *fp;
+
+ p += sprintf(p,"<html><head><title>Athena Sensors</title></head>\n\n<body>\n");
+ p += sprintf(p,"<h1>Athena Sensors</h1>\n\n");
+
+ while (entry) {
+ // insert snippets into html
+ char line[1024];
+ fp = fopen(entry->filename, "r");
+ if (fp == NULL) {
+ entry = entry->next;
+ continue;
+ }
+ while(fgets(line, sizeof(line) -1, fp))
+ p += sprintf(p, line);
+ fclose(fp);
+ entry = entry->next;
+ }
+ p += sprintf(p,"</body></html>\n");
+ httpd_send(sd,200,"text/html",p - buf,buf);
+}
+
+//////////////// Initialise / Finalise /////////////////////////////
+
+void do_final (void)
+{
+ int fd;
+ struct file_entry *entry = fileentry_head, *entry2;
+
+ // clear up graph entries
+ while (entry) {
+ entry2 = entry->next;
+ aFree(entry->filename);
+ aFree(entry);
+ entry = entry2;
+ }
+ // clear up existing http connections
+ for (fd = 0; fd < *max_fd; fd++)
+ if (sessiond[fd] && sessiond[fd]->type == SESSION_HTTP)
+ delete_sessiond(fd);
+
+ httpd_files->destroy(httpd_files,NULL);
+ // clear up the database
+ db_final();
+ // clear up allocated memory
+ // note: the memory manager, if enabled, would be
+ // separate from the parent program, which is also
+ // why we need to delete our http sessions
+ // separately above
+ malloc_final();
+}
+
+void do_init (void)
+{
+ struct func_parse_table *parse_table;
+ int enable_httpd = 1;
+
+ do {
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp = fopen("plugins/httpd.conf","r");
+ if (fp == NULL)
+ break;
+
+ while(fgets(line, sizeof(line) -1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) {
+ if(strcmpi(w1,"enable_httpd")==0){
+ enable_httpd = atoi(w2);
+ } else if(strcmpi(w1,"document_root")==0){
+ strcpy(document_root, w2);
+ } else if(strcmpi(w1,"request_timeout_first")==0){
+ request_timeout[0] = atoi(w2);
+ } else if(strcmpi(w1,"request_timeout_persist")==0){
+ request_timeout[1] = atoi(w2);
+ } else if(strcmpi(w1,"max_persist_request")==0){
+ max_persist_requests = atoi(w2);
+ }
+ }
+ }
+ fclose(fp);
+ } while (0);
+
+ if (!enable_httpd)
+ return;
+
+ malloc_init();
+ db_init();
+ IMPORT_SYMBOL(server_type, 0);
+ IMPORT_SYMBOL(gettick, 5);
+ IMPORT_SYMBOL(add_timer_interval, 8);
+ IMPORT_SYMBOL(max_fd, 13);
+ IMPORT_SYMBOL(sessiond, 14);
+ IMPORT_SYMBOL(delete_sessiond, 15);
+ IMPORT_SYMBOL(_WFIFOSET, 16);
+ IMPORT_SYMBOL(_RFIFOSKIP, 17);
+ IMPORT_SYMBOL(parse_table, 18);
+
+ // register http parsing function
+ parse_table[SESSION_HTTP].check = httpd_check;
+ parse_table[SESSION_HTTP].func = httpd_parse;
+
+ httpd_files = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_KEY,50);
+ httpd_default = httpd_send_file;
+
+ httpd_pages ("/graph", httpd_graph_parse);
+ add_timer_interval(gettick()+10000,httpd_graph_find,0,0,10000);
+
+ return;
+}
diff --git a/src/plugins/httpd.h b/src/plugins/httpd.h
new file mode 100644
index 000000000..9eef7d915
--- /dev/null
+++ b/src/plugins/httpd.h
@@ -0,0 +1,107 @@
+#ifndef _HTTPD_H_
+#define _HTTPD_H_
+
+struct httpd_session_data;
+
+// NOTE by Celest: This file is not used by httpd.c, but included only as an API reference.
+
+// ’ˆÓ
+// 1.athena“à‘ ‚Ìhttpd ‚Å‘å‚«‚ȃtƒ@ƒCƒ‹‚ð‘—M‚·‚邱‚Æ‚Í‚¨Š©‚ß‚µ‚Ü‚¹‚ñB
+// 200KB ‚ð’´‚¦‚é‚悤‚ȃtƒ@ƒCƒ‹‚ÍA•Ê‚̃\ƒtƒg‚ð—˜—p‚·‚邱‚Æ‚ðŠ©‚ß‚Ü‚·B
+// 2.ƒtƒ@ƒCƒ‹–¼‚ÉŽg‚¦‚镶Žš‚ÍA[A-Za-z0-9-_\.] ‚Å‚·B‘¼‚Ì•¶Žš‚ðŽg‚¤‚ÆA
+// BAD REQUEST ‚Å’e‚©‚ê‚Ü‚·B
+
+
+
+void httpd_pages(const char* url,void(*httpd_func)(struct httpd_session_data* sd,const char* url));
+
+// Žw’肳‚ꂽURL ‚ɑ΂·‚éƒR[ƒ‹ƒoƒbƒNŠÖ”‚ðÝ’è‚·‚éB‚±‚ÌŠÖ”‚ÍAˆÈ‰º‚̂悤‚É
+// ŽÀ‘•‚·‚é•K—v‚ª‚ ‚éB
+//
+// 1. URL ‚ÍA擪‚̃Xƒ‰ƒbƒVƒ…‚ªÈ‚©‚ꂽƒtƒ@ƒCƒ‹–¼‚Å‚·B—Ⴆ‚ÎA"GET / HTTP/1.0"
+// ‚Æ‚¢‚¤•—‚ɃŠƒNƒGƒXƒg‚³‚ꂽŽžAURL ‚É‚Í""(‹ó•¶Žš)‚ª“ü‚èA"GET /hoge HTTP/1.0"
+// ‚ÌŽž‚É‚ÍA"hoge"‚ª“ü‚è‚Ü‚·B
+// 2. ƒŠƒNƒGƒXƒg‚³‚ꂽƒy[ƒW‚ªŒ©‚‚©‚Á‚½‚çAhttpd_send() ‚Ü‚½‚ÍAhttpd_send_head()
+// ‚Æhttpd_send_data() ‚Ì‘g‚ðŒÄ‚Ño‚µAƒf[ƒ^‚ðo—Í‚·‚éB
+// 3. httpd_send_file ‚ðŽw’è‚·‚é‚ÆAhttpd/ ˆÈ‰º‚É‚ ‚éƒtƒ@ƒCƒ‹‚ðo—Í‚·‚éBƒtƒ@ƒCƒ‹‚É
+// ‹ó•¶Žš‚ªŽw’肳‚ꂽŽž‚ÍAindex.html‚ªŽw’肳‚ꂽ‚à‚Ì‚Æ‚Ý‚È‚³‚ê‚éB
+
+
+
+char* httpd_get_value(struct httpd_session_data* sd,const char* val);
+
+// ƒŠƒNƒGƒXƒg‚³‚ꂽƒAƒhƒŒƒX‚É“n‚³‚ꂽƒtƒH[ƒ€ƒf[ƒ^‚Ì‚¤‚¿AŠY“–‚·‚镶Žš—ñ‚ð•Ô‚·B
+// —Ⴆ‚ÎA"GET /status/graph?image=users HTTP/1.0"‚Æ‚¢‚¤ƒŠƒNƒGƒXƒg‚Ìê‡A
+// httpd_get_value(sd,"image"); ‚ÍA "users"‚ð•Ô‚·B‚±‚ÌŠÖ”‚Ì–ß‚è’l‚ÍAŒÄ‚Ño‚µŒ³‚ª
+// ‰ð•ú‚µ‚È‚¯‚ê‚΂Ȃç‚È‚¢B‚Ü‚½AŠY“–‚·‚镶Žš—ñ‚ª–³‚¢Žž‚ÍA‹ó‚Ì•¶Žš—ñ‚ð•Ô‚·B
+
+unsigned int httpd_get_ip(struct httpd_session_data *sd);
+
+// ƒNƒ‰ƒCƒAƒ“ƒg‚ÌIP‚ð•Ô‚·B
+
+
+void httpd_default_page(void(*httpd_func)(struct httpd_session_data* sd,const char* url));
+
+// Žw’肳‚ꂽURL ‚ª“o˜^‚³‚ê‚Ä‚¢‚È‚¢Žž‚ɌĂÑo‚·ŠÖ”‚ðÝ’è‚·‚éB‚±‚ÌŠÖ”‚ðŒÄ‚Ño‚³‚È‚¢‚©A
+// ŠÖ”‚̈ø”‚ÉNULL‚ðŽw’è‚·‚é‚ÆA404 Not Found ‚ð•Ô‚·B
+
+
+
+
+void httpd_send(struct httpd_session_data* sd,int status,const char *content_type,int content_len,const void *data);
+
+// HTTPƒwƒbƒ_Aƒf[ƒ^‚ð‘g‚É‚µ‚Ä‘—M‚·‚éB‚±‚ÌŠÖ”‚ðŒÄ‚Ño‚µ‚½Œã‚ÉAhttpd_send_data ‚ð
+// ŒÄ‚Ño‚µ‚Ä‚Í‚È‚ç‚È‚¢B
+//
+// sd : httpd_set_parse_func() ‚É“n‚³‚ꂽ‚à‚Ì‚ð‚»‚Ì‚Ü‚Ü“n‚·‚±‚ÆB
+// status : HTTPƒwƒbƒ_‚ɉÁ‚¦‚éstatusB’Êí‚Í200B
+// content_type : ‘—M‚·‚éƒf[ƒ^‚̃^ƒCƒvBtext/html , image/jpeg ‚È‚ÇB
+// content_len : ‘—M‚·‚éƒf[ƒ^‚Ì’·‚³B
+// data : ‘—M‚·‚éƒf[ƒ^‚ւ̃|ƒCƒ“ƒ^
+
+
+
+void httpd_send_head(struct httpd_session_data* sd,int status,const char *content_type,int content_len);
+
+// HTTPƒwƒbƒ_‚ð‘—M‚·‚éB
+//
+// sd : “¯ã
+// status : “¯ã
+// content_type : “¯ã
+// content_len : content_len‚ð-1‚ÉŽw’è‚·‚邱‚Æ‚ÅA‚±‚ÌŠÖ”‚ªŒÄ‚΂ꂽŽž“_‚Å
+// ’·‚³‚ª•ª‚©‚ç‚È‚¢ƒf[ƒ^‚ð‘—M‚·‚邱‚Æ‚ª‚Å‚«‚éB‚±‚ÌꇂÍ
+// ‹­§“I‚ÉHTTP/1.0 Ú‘±‚Æ‚È‚èAƒI[ƒo[ƒwƒbƒh‚ª‘å‚«‚­‚È‚é‚Ì‚ÅA
+// ‚ ‚܂肨Š©‚ß‚Í‚µ‚È‚¢B
+
+
+
+
+void httpd_send_data(struct httpd_session_data* sd,int content_len,const void *data);
+
+// ƒf[ƒ^‚ð‘—M‚·‚éB‚±‚ÌŠÖ”‚ðAhttpd_send_head() ‚ðŒÄ‚Ño‚·‘O‚ɌĂÑo‚³‚ꂽê‡A
+// content_type = application/octet-stream, content_len = -1 ‚Æ‚µ‚ăwƒbƒ_‚ª‘—M‚³‚ê‚éB
+// sd : “¯ã
+// content_len : ‘—M‚·‚éƒf[ƒ^‚Ìdata’·‚³‚ðŽw’è‚·‚éB
+// data : ‘—M‚·‚éƒf[ƒ^
+
+
+
+void httpd_send_file(struct httpd_session_data* sd,const char* url);
+
+// ƒtƒ@ƒCƒ‹‚ð‘—M‚·‚éB‚±‚ÌŠÖ”‚ÍAhttpd_send_head() ‚ðŒÄ‚Ño‚·‘O‚ɌĂÑo‚³‚È‚¯‚ê‚Î
+// ‚È‚ç‚È‚¢Bƒtƒ@ƒCƒ‹‚ɋ󕶎š‚ªŽw’肳‚ꂽ‚Æ‚«‚ÍAindex.html‚ªŽw’肳‚ꂽ‚ÆŒ©‚È‚³‚ê‚éB
+
+
+
+void httpd_send_error(struct httpd_session_data* sd,int status);
+
+// HTTPƒGƒ‰[ƒƒbƒZ[ƒW‚ð‘—M‚·‚éBstatus ‚ÍHTTP‚̃Gƒ‰[ƒR[ƒh‚Æ“¯‚¶B
+// 400 Bad Request, 404 Not Found, 500 Internal Server Error ‚È‚ÇB
+
+int httpd_parse(int fd);
+
+// ‰Šú‰»ˆ—
+void do_init_httpd(void);
+void do_final_httpd(void);
+
+#endif
diff --git a/src/plugins/httpd.txt b/src/plugins/httpd.txt
new file mode 100644
index 000000000..2de84e3d8
--- /dev/null
+++ b/src/plugins/httpd.txt
@@ -0,0 +1,20 @@
+//
+// HTTP Daemon Plugin Configuration
+//
+
+// Enabled the http daemon?
+enable_httpd: 1
+
+// WWW Root path
+//(The ending slash is required!)
+document_root: httpd/
+
+// Request timeout (first request)
+// Both of the following are in milliseconds
+request_timeout_first: 2500
+
+// Request timeout (consequent requests)
+request_timeout_persist: 60000
+
+// Maximum persistent requests
+max_persist_request: 32 \ No newline at end of file
diff --git a/src/plugins/pid.c b/src/plugins/pid.c
new file mode 100644
index 000000000..1ceb49b6f
--- /dev/null
+++ b/src/plugins/pid.c
@@ -0,0 +1,54 @@
+
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+ #include <unistd.h>
+#else
+ #define getpid GetCurrentProcessId
+#endif
+#ifdef MINGW
+ #include <process.h>
+ #include <io.h>
+#endif
+#include "../common/plugin.h"
+
+PLUGIN_INFO = {
+ "ProcessId",
+ PLUGIN_ALL,
+ "1.0",
+ PLUGIN_VERSION,
+ "Logs the process ID"
+};
+
+PLUGIN_EVENTS_TABLE = {
+ { "pid_create", "Plugin_Init" },
+ { "pid_delete", "Plugin_Final" },
+ { NULL, NULL }
+};
+
+char pid_file[256];
+char *server_name;
+
+void pid_create ()
+{
+ FILE *fp;
+ int len;
+
+ IMPORT_SYMBOL(server_name, 1);
+ len = strlen(server_name);
+ strcpy(pid_file, server_name);
+ if(len > 4 && pid_file[len - 4] == '.') {
+ pid_file[len - 4] = 0;
+ }
+ strcat(pid_file, ".pid");
+ fp = fopen(pid_file, "w");
+ if (fp) {
+ fprintf(fp, "%d", getpid());
+ fclose(fp);
+ }
+}
+
+void pid_delete ()
+{
+ unlink(pid_file);
+}
diff --git a/src/plugins/sample.c b/src/plugins/sample.c
new file mode 100644
index 000000000..5a8e2a286
--- /dev/null
+++ b/src/plugins/sample.c
@@ -0,0 +1,77 @@
+// Sample Athena plugin
+
+#include <stdio.h>
+#include <string.h>
+#include "../common/plugin.h"
+
+////// Plugin information ////////
+//
+PLUGIN_INFO = {
+// change only the following area
+ "Test", // Plugin name
+ PLUGIN_ALL, // Which servers is this plugin for
+ "0.1", // Plugin version
+ PLUGIN_VERSION, // Minimum plugin engine version to run
+ "A sample plugin" // Short description of plugin
+};
+
+////// Plugin event list //////////
+// Format: <plugin function>,<event name>
+// All registered functions to a event gets executed
+// (In descending order) when its called.
+// Multiple functions can be called by multiple events too,
+// So it's up to your creativity ^^
+//
+PLUGIN_EVENTS_TABLE = {
+// change only the following area
+ { "test_me", "Plugin_Test" }, // when the plugin is tested for compatibility
+ { "do_init", "Plugin_Init" }, // when plugins are loaded
+ { "do_final", "Plugin_Final" }, // when plugins are unloaded
+ { "some_function", "some_event" },
+ { "some_function", "another_event" },
+ { NULL, NULL }
+};
+
+///// Variables /////
+char *server_type;
+char *server_name;
+
+//////// Plugin functions //////////
+int do_init ()
+{
+ // import symbols from the server
+ IMPORT_SYMBOL(server_type, 0);
+ IMPORT_SYMBOL(server_name, 1);
+
+ printf ("Server type is ");
+ switch (*server_type) {
+ case PLUGIN_LOGIN: printf ("Login\n"); break;
+ case PLUGIN_CHAR: printf ("Char\n"); break;
+ case PLUGIN_MAP: printf ("Map\n"); break;
+ }
+ printf ("Filename is %s\n", server_name);
+
+ return 1;
+}
+
+int do_final ()
+{
+ printf ("Bye world\n");
+
+ return 1;
+}
+
+int some_function ()
+{
+ printf ("Some function\n");
+ return 0;
+}
+
+// return 1 if the testing passes, otherwise 0
+// (where the plugin will be deactivated)
+int test_me ()
+{
+ if (1 + 1 == 2)
+ return 1;
+ return 0;
+}
diff --git a/src/plugins/sig.c b/src/plugins/sig.c
new file mode 100644
index 000000000..1e9c36891
--- /dev/null
+++ b/src/plugins/sig.c
@@ -0,0 +1,211 @@
+// $Id: sig.c 1 2005-6-13 3:17:17 PM Celestia $
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include "../common/plugin.h"
+#include "../common/version.h"
+#include "../common/showmsg.h"
+
+PLUGIN_INFO = {
+ "Signals",
+ PLUGIN_CORE,
+ "1.1",
+ PLUGIN_VERSION,
+ "Handles program signals"
+};
+
+PLUGIN_EVENTS_TABLE = {
+ { "sig_init", "Plugin_Init" },
+ { "sig_final", "Plugin_Final" },
+ { NULL, NULL }
+};
+
+//////////////////////////////////////
+
+#if defined(_WIN32) || defined(MINGW)
+ int sig_init() {
+ printf("This plugin is not supported - Enable 'exchndl' instead!");
+ return 0;
+ }
+ int sig_final() { return 0; }
+#elif defined (__NETBSD__) || defined (__FREEBSD__)
+ int sig_init() {
+ printf("This plugin is not supported!");
+ return 0;
+ }
+ int sig_final() { return 0; }
+#else
+
+//////////////////////////////////////
+
+#if !defined(CYGWIN)
+ #include <execinfo.h>
+#endif
+
+const char* (*getrevision)();
+unsigned long (*getuptime)();
+char *server_name;
+int crash_flag = 0;
+
+extern const char *strsignal(int);
+int sig_final ();
+
+// 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_.
+//
+#ifdef WIN32 // windows don't have SIGPIPE
+#define SIGPIPE SIGINT
+#endif
+
+#ifndef POSIX
+#define compat_signal(signo, func) signal(signo, func)
+#else
+sigfunc *compat_signal(int signo, sigfunc *func)
+{
+ struct sigaction sact, oact;
+
+ sact.sa_handler = func;
+ sigemptyset(&sact.sa_mask);
+ sact.sa_flags = 0;
+#ifdef SA_INTERRUPT
+ sact.sa_flags |= SA_INTERRUPT; /* SunOS */
+#endif
+
+ if (sigaction(signo, &sact, &oact) < 0)
+ return (SIG_ERR);
+
+ return (oact.sa_handler);
+}
+#endif
+
+/*=========================================
+ * Dumps the stack using glibc's backtrace
+ *-----------------------------------------
+ */
+#ifdef CYGWIN
+ #define FOPEN_ freopen
+ extern void cygwin_stackdump();
+#else
+ #define FOPEN_(fn,m,s) fopen(fn,m)
+#endif
+void sig_dump(int sn)
+{
+ FILE *fp;
+ char file[256];
+ int no = 0;
+
+ crash_flag = 1;
+ // search for a usable filename
+ do {
+ sprintf (file, "log/%s%04d.stackdump", server_name, ++no);
+ } while((fp = fopen(file,"r")) && (fclose(fp), no < 9999));
+ // dump the trace into the file
+
+ if ((fp = FOPEN_(file, "w", stderr)) != NULL) {
+ const char *revision;
+ #ifndef CYGWIN
+ void* array[20];
+ char **stack;
+ size_t size;
+ #endif
+
+ printf ("Dumping stack to '"CL_WHITE"%s"CL_RESET"'... ", file);
+ if ((revision = getrevision()) != NULL)
+ fprintf(fp, "Version: svn%s \n", revision);
+ else
+ fprintf(fp, "Version: %2d.%02d.%02d mod%02d \n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, ATHENA_MOD_VERSION);
+ fprintf(fp, "Exception: %s \n", strsignal(sn));
+ fflush (fp);
+
+ #ifdef CYGWIN
+ cygwin_stackdump ();
+ #else
+ fprintf(fp, "Stack trace:\n");
+ size = backtrace (array, 20);
+ stack = backtrace_symbols (array, size);
+ for (no = 0; no < size; no++) {
+ fprintf(fp, "%s\n", stack[no]);
+ }
+ fprintf(fp,"End of stack trace\n");
+ free(stack);
+ #endif
+
+ printf ("Done.\n");
+ fflush(stdout);
+ fclose(fp);
+ }
+
+ sig_final(); // Log our uptime
+ // Pass the signal to the system's default handler
+ compat_signal(sn, SIG_DFL);
+ raise(sn);
+}
+
+/*=========================================
+ * Shutting down (Program did not crash ^^)
+ * - Log our current up time
+ *-----------------------------------------
+ */
+int sig_final ()
+{
+ time_t curtime;
+ char curtime2[24];
+ FILE *fp;
+ long seconds = 0, day = 24*60*60, hour = 60*60,
+ minute = 60, days = 0, hours = 0, minutes = 0;
+
+ fp = fopen("log/uptime.log","a");
+ if (fp) {
+ time(&curtime);
+ strftime(curtime2, 24, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+
+ seconds = getuptime();
+ days = seconds/day;
+ seconds -= (seconds/day>0)?(seconds/day)*day:0;
+ hours = seconds/hour;
+ seconds -= (seconds/hour>0)?(seconds/hour)*hour:0;
+ minutes = seconds/minute;
+ seconds -= (seconds/minute>0)?(seconds/minute)*minute:0;
+
+ fprintf(fp, "%s: %s %s - %ld days, %ld hours, %ld minutes, %ld seconds.\n",
+ curtime2, server_name, (crash_flag ? "crashed" : "uptime"),
+ days, hours, minutes, seconds);
+ fclose(fp);
+ }
+
+ return 1;
+}
+
+/*=========================================
+ * Register the signal handlers
+ *-----------------------------------------
+ */
+int sig_init ()
+{
+ void (*func) = sig_dump;
+#ifdef CYGWIN // test if dumper is enabled
+ char *buf = getenv ("CYGWIN");
+ if (buf && strstr(buf, "error_start") != NULL)
+ func = SIG_DFL;
+#endif
+
+ IMPORT_SYMBOL(server_name, 1);
+ IMPORT_SYMBOL(getrevision, 6);
+ IMPORT_SYMBOL(getuptime, 11);
+
+ compat_signal(SIGSEGV, func);
+ compat_signal(SIGFPE, func);
+ compat_signal(SIGILL, func);
+ #ifndef __WIN32
+ compat_signal(SIGBUS, func);
+ #endif
+
+ return 1;
+}
+#endif
+
diff --git a/src/plugins/upnp.txt b/src/plugins/upnp.txt
new file mode 100644
index 000000000..32d0e75bf
--- /dev/null
+++ b/src/plugins/upnp.txt
@@ -0,0 +1,31 @@
+//
+// UPnP Plugin Configuration
+//
+
+// Enable UPnP
+enable_upnp: 1
+
+// Remove mapped router ports when shutting down
+release_mappings: 1
+
+// Close opened firewall ports when shutting down
+close_ports: 1
+
+//
+// You can set these if necessary
+// login server port
+//login_port: 6900
+//
+// char server port
+//char_port: 6121
+//
+// map server port
+//map_port: 5121
+//
+// NAT IP address to map your ports to
+//nat_ip: 192.168.0.1
+
+
+// Note: This plugin only works on Windows XP or higher
+// For more info on UPnP try here:
+// http://www.google.com/search?q=what+is+upnp \ No newline at end of file
diff --git a/src/tool/Makefile b/src/tool/Makefile
new file mode 100644
index 000000000..d8614c300
--- /dev/null
+++ b/src/tool/Makefile
@@ -0,0 +1,10 @@
+all: adduser convert
+
+adduser:
+ $(CC) -o ../../tools/$@ adduser.c
+
+convert:
+ $(CC) -o ../../tools/$@ convert.c
+
+clean:
+ rm -rf ../../tools/adduser ../../tools/convert
diff --git a/src/tool/adduser.c b/src/tool/adduser.c
new file mode 100644
index 000000000..2c8e1524b
--- /dev/null
+++ b/src/tool/adduser.c
@@ -0,0 +1,99 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+/*
+ 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;
+ }
+ }
+ }
+ close(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);
+ close(FPaccout);
+
+ printf("Account added.\n");
+}
diff --git a/src/tool/convert.c b/src/tool/convert.c
new file mode 100644
index 000000000..2e81bfedd
--- /dev/null
+++ b/src/tool/convert.c
@@ -0,0 +1,299 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define RETCODE "\r\n"
+
+#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; // V‹Kƒf[ƒ^
+ 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ŽÀ‘•ˆÈ‘O‚Ìathena.txtŒÝŠ·‚Ì‚½‚߈ꉞ'\n'ƒ`ƒFƒbƒ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" RETCODE,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/txt-converter/Makefile b/src/txt-converter/Makefile
new file mode 100644
index 000000000..7d3203407
--- /dev/null
+++ b/src/txt-converter/Makefile
@@ -0,0 +1,15 @@
+all sql: char-converter login-converter
+
+char-converter: char-converter.o ../common/obj/minicore.o ../common/obj/malloc.o ../common/obj/showmsg.o ../common/obj/strlib.o ../common/obj/mapindex.o ../common/obj/ers.o
+ $(CC) -o ../../tools/$@ $^ $(LIB_S)
+
+login-converter: login-converter.o ../common/obj/minicore.o ../common/obj/db.o ../common/obj/malloc.o ../common/obj/showmsg.o
+ $(CC) -o ../../tools/$@ $^ $(LIB_S)
+
+clean:
+ rm -f *.o ../../tools/login-converter ../../tools/char-converter
+
+# DO NOT DELETE
+
+char-converter.o: char-converter.c
+login-converter.o: login-converter.c
diff --git a/src/txt-converter/char-converter.c b/src/txt-converter/char-converter.c
new file mode 100644
index 000000000..2070b9b9d
--- /dev/null
+++ b/src/txt-converter/char-converter.c
@@ -0,0 +1,1360 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/core.h"
+#include "../common/strlib.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/mapindex.h"
+
+#include <mysql.h>
+
+//Txt Files used.
+char char_txt[1024]="save/athena.txt";
+char friends_txt[1024]="save/friends.txt"; // davidsiaw
+char pet_txt[256]="save/pet.txt";
+char storage_txt[256]="save/storage.txt";
+
+//Databases used.
+char char_db[256] = "char";
+char cart_db[256] = "cart_inventory";
+char inventory_db[256] = "inventory";
+char storage_db[256] = "storage";
+char reg_db[256] = "global_reg_value";
+char skill_db[256] = "skill";
+char memo_db[256] = "memo";
+char pet_db[256] = "pet";
+char friend_db[256] = "friends";
+char guild_storage_db[256] = "guild_storage";
+
+MYSQL mysql_handle;
+MYSQL_RES* sql_res ;
+MYSQL_ROW sql_row ;
+char tmp_sql[65535];
+
+int sql_fields, sql_cnt;
+
+int db_server_port = 3306;
+char db_server_ip[16] = "127.0.0.1";
+char db_server_id[32] = "ragnarok";
+char db_server_pw[32] = "ragnarok";
+char db_server_logindb[32] = "ragnarok";
+
+char t_name[256];
+
+int char_id_count=100000;
+
+struct accreg {
+ int reg_num;
+ struct global_reg reg[ACCOUNT_REG_NUM];
+};
+
+struct character_data {
+ struct mmo_charstatus status;
+ struct accreg global;
+} *char_dat;
+int char_num, char_max;
+
+//Required for sql saving, taken from src/char_sql/char.h
+struct itemtmp {
+ int flag;//checked = 1 else 0
+ int id;
+ short nameid;
+ short amount;
+ unsigned short equip;
+ char identify;
+ char refine;
+ char attribute;
+ short card[4];
+};
+enum {
+ TABLE_INVENTORY,
+ TABLE_CART,
+ TABLE_STORAGE,
+ TABLE_GUILD_STORAGE,
+};
+
+#define CHAR_CONF_NAME "conf/char_athena.conf"
+#define INTER_CONF_NAME "conf/inter_athena.conf"
+//==========================================================================================================
+//NOTE: Update this function from the one in src/char/char.c as needed. [Skotlex]
+int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct accreg *reg) {
+ char tmp_str[3][128]; //To avoid deleting chars with too long names.
+ 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 1488 and after
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0],
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36],
+ tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39],
+ &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next)) != 47)
+ {
+ tmp_int[43] = 0;
+ // If it's not char structure of version 1363 and after
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39],
+ &tmp_int[40], &tmp_int[41], &tmp_int[42], &next)) != 46)
+ {
+ tmp_int[40] = 0; // father
+ tmp_int[41] = 0; // mother
+ tmp_int[42] = 0; // child
+ // If it's not char structure of version 1008 and before 1363
+ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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%127[^\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%127[^,],%d,%d\t%127[^,],%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], //
+ &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],
+ tmp_str[1], &tmp_int[35], &tmp_int[36], //
+ tmp_str[2], &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 {
+ set += 3;
+ //printf("char: new char data ver.3\n");
+ }
+ // Char structture of version 1363+
+ } else {
+ set++;
+ //printf("char: new char data ver.4\n");
+ }
+ // Char structure of version 1488+
+ } else {
+ //printf("char: new char data ver.5\n");
+ }
+ if (set != 47)
+ return 0;
+
+ memcpy(p->name, tmp_str[0], NAME_LENGTH-1); //Overflow protection [Skotlex]
+ 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 = 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.map = mapindex_name2id(tmp_str[1]);
+ p->last_point.x = tmp_int[35];
+ p->last_point.y = tmp_int[36];
+ p->save_point.map = mapindex_name2id(tmp_str[2]);
+ p->save_point.x = tmp_int[37];
+ p->save_point.y = tmp_int[38];
+ p->partner_id = tmp_int[39];
+ p->father = tmp_int[40];
+ p->mother = tmp_int[41];
+ p->child = tmp_int[42];
+ p->fame = tmp_int[43];
+
+ // Some checks
+ for(i = 0; i < char_num; i++) {
+ if (char_dat[i].status.char_id == p->char_id) {
+ ShowError("\033[1;31mmmo_auth_init: a character has an identical id to another.\n");
+ ShowError(" character id #%d -> new character not readed.\n", p->char_id);
+ ShowError(" Character saved in log file.\033[0m\n");
+ return -1;
+ } else if (strcmp(char_dat[i].status.name, p->name) == 0) {
+ ShowError("\033[1;31mmmo_auth_init: a character name already exists.\n");
+ ShowError(" character name '%s' -> new character not read.\n", p->name);
+ ShowError(" Character saved in log file.\033[0m\n");
+ return -2;
+ }
+ }
+
+ if (str[next] == '\n' || str[next] == '\r')
+ return 1; // ÂV‹KƒfÂ[ƒ^
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t'; i++) {
+ if (sscanf(str+next, "%[^,],%d,%d%n", tmp_str[0], &tmp_int[0], &tmp_int[1], &len) != 3)
+ return -3;
+ p->memo_point[i].map = mapindex_name2id(tmp_str[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++) {
+ 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) {
+ // 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) {
+ } 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];
+ 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[10], &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) {
+ } 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];
+ 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];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_regŽÀ‘•ˆÈ‘O‚Ìathena.txtŒÊ·‚Ì‚½‚߈ꉞ'\n'ƒ`ƒFƒbƒN
+ if (sscanf(str + next, "%[^,],%s%n", reg->reg[i].str, reg->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, ",%s%n", reg->reg[i].value, &len) == 1)
+ i--;
+ else
+ return -7;
+ }
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+ reg->reg_num = i;
+
+ return 1;
+}
+/* Because of the friend database update, it is no longer possible to convert friends.
+//Note: Keep updated with the same function found in src/char/char.c [Skotlex]
+int parse_friend_txt(struct mmo_charstatus *p)
+{
+ char line[1024];
+ int i,cid=0,temp[20];
+ FILE *fp;
+
+ // Open the file and look for the ID
+ fp = fopen(friends_txt, "r");
+
+ if(fp == NULL)
+ return 1;
+
+
+ while(fgets(line, sizeof(line)-1, fp)) {
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ //Character names must not exceed the 23+\0 limit. [Skotlex]
+ sscanf(line, "%d,%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23[^,],%d,%23s",&cid,
+ &temp[0],p->friends[0].name,
+ &temp[1],p->friends[1].name,
+ &temp[2],p->friends[2].name,
+ &temp[3],p->friends[3].name,
+ &temp[4],p->friends[4].name,
+ &temp[5],p->friends[5].name,
+ &temp[6],p->friends[6].name,
+ &temp[7],p->friends[7].name,
+ &temp[8],p->friends[8].name,
+ &temp[9],p->friends[9].name,
+ &temp[10],p->friends[10].name,
+ &temp[11],p->friends[11].name,
+ &temp[12],p->friends[12].name,
+ &temp[13],p->friends[13].name,
+ &temp[14],p->friends[14].name,
+ &temp[15],p->friends[15].name,
+ &temp[16],p->friends[16].name,
+ &temp[17],p->friends[17].name,
+ &temp[18],p->friends[18].name,
+ &temp[19],p->friends[19].name);
+ if (cid == p->char_id)
+ break;
+ }
+
+ // No register of friends list
+ if (cid == 0) {
+ fclose(fp);
+ return 0;
+ }
+
+ // Fill in the list
+
+ for (i=0; i<20; i++)
+ p->friends[i].char_id = temp[i];
+
+ fclose(fp);
+ return 0;
+}
+*/
+
+// Note: Remember to keep this function updated with the one in src/char_sql/char.c [Skotlex]
+// Unneded code was left commented for easier merging of changes.
+// Function by [Ilpalazzo-sama]
+
+int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id, int tableswitch)
+{
+ int i; //, flag, id;
+ char *tablename;
+ char selectoption[16];
+
+ switch (tableswitch) {
+ case TABLE_INVENTORY:
+ tablename = inventory_db; // no need for sprintf here as *_db are char*.
+ sprintf(selectoption,"char_id");
+ break;
+ case TABLE_CART:
+ tablename = cart_db;
+ sprintf(selectoption,"char_id");
+ break;
+ case TABLE_STORAGE:
+ tablename = storage_db;
+ sprintf(selectoption,"account_id");
+ break;
+ case TABLE_GUILD_STORAGE:
+ tablename = guild_storage_db;
+ sprintf(selectoption,"guild_id");
+ break;
+ default:
+ ShowError("Invalid table name!\n");
+ return 1;
+ }
+
+ //=======================================mysql database data > memory===============================================
+/*
+ sprintf(tmp_sql, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3` "
+ "FROM `%s` WHERE `%s`='%d'", tablename, selectoption, char_id);
+
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB server Error (select `%s` to Memory)- %s\n",tablename ,mysql_error(&mysql_handle));
+ return 1;
+ }
+ sql_res = mysql_store_result(&mysql_handle);
+ if (sql_res) {
+ while ((sql_row = mysql_fetch_row(sql_res))) {
+ flag = 0;
+ id = atoi(sql_row[0]);
+ for(i = 0; i < count; i++) {
+ if(mapitem[i].flag == 1)
+ continue;
+ if(mapitem[i].nameid == atoi(sql_row[1])) { // produced items fixup
+ if((mapitem[i].equip == atoi(sql_row[3])) &&
+ (mapitem[i].identify == atoi(sql_row[4])) &&
+ (mapitem[i].amount == atoi(sql_row[2])) &&
+ (mapitem[i].refine == atoi(sql_row[5])) &&
+ (mapitem[i].attribute == atoi(sql_row[6])) &&
+ (mapitem[i].card[0] == atoi(sql_row[7])) &&
+ (mapitem[i].card[1] == atoi(sql_row[8])) &&
+ (mapitem[i].card[2] == atoi(sql_row[9])) &&
+ (mapitem[i].card[3] == atoi(sql_row[10]))) {
+ //printf("the same item : %d , equip : %d , i : %d , flag : %d\n", mapitem.equip[i].nameid,mapitem.equip[i].equip , i, mapitem.equip[i].flag); //DEBUG-STRING
+ } else {
+//==============================================Memory data > SQL ===============================
+ if(itemdb_isequip(mapitem[i].nameid) || (mapitem[i].card[0] == atoi(sql_row[7]))) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `refine`='%d',"
+ "`attribute`='%d', `card0`='%d', `card1`='%d', `card2`='%d', `card3`='%d', `amount`='%d' WHERE `id`='%d' LIMIT 1",
+ tablename, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine, mapitem[i].attribute, mapitem[i].card[0],
+ mapitem[i].card[1], mapitem[i].card[2], mapitem[i].card[3], mapitem[i].amount, id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB server Error (UPdate `equ %s`)- %s\n", tablename, mysql_error(&mysql_handle));
+ }
+ //printf("not the same item : %d ; i : %d ; flag : %d\n", mapitem.equip[i].nameid, i, mapitem.equip[i].flag);
+ }
+ flag = mapitem[i].flag = 1;
+ break;
+ }
+ }
+ if(!flag) {
+ sprintf(tmp_sql,"DELETE from `%s` where `id`='%d'", tablename, id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB server Error (DELETE `equ %s`)- %s\n", tablename, mysql_error(&mysql_handle));
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+*/
+ for(i = 0; i < count; i++) {
+ if(mapitem[i].nameid) {
+ sprintf(tmp_sql,"INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3` )"
+ " VALUES ( '%d','%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d' )",
+ tablename, selectoption, char_id, mapitem[i].nameid, mapitem[i].amount, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine,
+ mapitem[i].attribute, mapitem[i].card[0], mapitem[i].card[1], mapitem[i].card[2], mapitem[i].card[3]);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB server Error (INSERT `equ %s`)- %s\n", tablename, mysql_error(&mysql_handle));
+ }
+ }
+ return 0;
+}
+
+//Note: Remember to keep this function updated with src/char_sql/char.c [Skotlex]
+//Unncessary code is left commented for easy merging.
+int mmo_char_tosql(int char_id, struct mmo_charstatus *p){
+ int i=0 ,party_exist=0;//,guild_exist;
+ int count = 0;
+ int diff = 1;
+ char *tmp_ptr; //Building a single query should be more efficient than running
+ //multiple queries for each thing about to be saved, right? [Skotlex]
+ char save_status[100]; //For displaying save information. [Skotlex]
+// struct mmo_charstatus *cp;
+ struct itemtmp mapitem[MAX_GUILD_STORAGE];
+
+ if (char_id!=p->char_id) return 0;
+
+/*
+ cp = (struct mmo_charstatus*)idb_get(char_db_,char_id);
+
+ if (cp == NULL) {
+ cp = (struct mmo_charstatus *) aMalloc(sizeof(struct mmo_charstatus));
+ memset(cp, 0, sizeof(struct mmo_charstatus));
+ idb_put(char_db_, char_id,cp);
+ }
+*/
+ ShowInfo("Saving char "CL_WHITE"%d"CL_RESET" (%s)...\n",char_id,p->name);
+ memset(save_status, 0, sizeof(save_status));
+
+ count = 0;
+// diff = 0;
+ //map inventory data
+ for(i=0;i<MAX_INVENTORY;i++){
+// if (!compare_item(&p->inventory[i], &cp->inventory[i]))
+// diff = 1;
+ if(p->inventory[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->inventory[i].id;
+ mapitem[count].nameid=p->inventory[i].nameid;
+ mapitem[count].amount = p->inventory[i].amount;
+ mapitem[count].equip = p->inventory[i].equip;
+ mapitem[count].identify = p->inventory[i].identify;
+ mapitem[count].refine = p->inventory[i].refine;
+ mapitem[count].attribute = p->inventory[i].attribute;
+ mapitem[count].card[0] = p->inventory[i].card[0];
+ mapitem[count].card[1] = p->inventory[i].card[1];
+ mapitem[count].card[2] = p->inventory[i].card[2];
+ mapitem[count].card[3] = p->inventory[i].card[3];
+ count++;
+ }
+ }
+ //printf("- Save item data to MySQL!\n");
+ if (diff)
+ if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_INVENTORY))
+ strcat(save_status, " inventory");
+
+ count = 0;
+
+// diff = 0;
+ //map cart data
+ for(i=0;i<MAX_CART;i++){
+// if (!compare_item(&p->cart[i], &cp->cart[i]))
+// diff = 1;
+ if(p->cart[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->cart[i].id;
+ mapitem[count].nameid=p->cart[i].nameid;
+ mapitem[count].amount = p->cart[i].amount;
+ mapitem[count].equip = p->cart[i].equip;
+ mapitem[count].identify = p->cart[i].identify;
+ mapitem[count].refine = p->cart[i].refine;
+ mapitem[count].attribute = p->cart[i].attribute;
+ mapitem[count].card[0] = p->cart[i].card[0];
+ mapitem[count].card[1] = p->cart[i].card[1];
+ mapitem[count].card[2] = p->cart[i].card[2];
+ mapitem[count].card[3] = p->cart[i].card[3];
+ count++;
+ }
+ }
+
+ if (diff)
+ if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_CART))
+ strcat(save_status, " cart");
+/*
+ if ((p->base_exp != cp->base_exp) || (p->class_ != cp->class_) ||
+ (p->base_level != cp->base_level) || (p->job_level != cp->job_level) ||
+ (p->job_exp != cp->job_exp) || (p->zeny != cp->zeny) ||
+ (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) ||
+ (p->max_hp != cp->max_hp) || (p->hp != cp->hp) ||
+ (p->max_sp != cp->max_sp) || (p->sp != cp->sp) ||
+ (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) ||
+ (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) ||
+ (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) ||
+ (p->option != cp->option) || (p->karma != cp->karma) || (p->manner != cp->manner) ||
+ (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) ||
+ (p->pet_id != cp->pet_id) || (p->hair != cp->hair) || (p->hair_color != cp->hair_color) ||
+ (p->clothes_color != cp->clothes_color) || (p->weapon != cp->weapon) ||
+ (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
+ (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) ||
+ (p->partner_id != cp->partner_id) || (p->father != cp->father) ||
+ (p->mother != cp->mother) || (p->child != cp->child) || (p->fame != cp->fame))
+ { //Save status
+
+ //Check for party
+ party_exist=1;
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `party_id` = '%d'",party_db, p->party_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB Error - %s\n", mysql_error(&mysql_handle));
+ } else { //In case of failure, don't touch the data. [Skotlex
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = mysql_fetch_row(sql_res);
+ if (sql_row)
+ party_exist = atoi(sql_row[0]);
+ mysql_free_result(sql_res);
+ }
+
+ //check guild_exist
+ guild_exist=1;
+ sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id` = '%d'",guild_db, p->guild_id); // TBR
+ if (mysql_query(&mysql_handle, tmp_sql)) {
+ ShowSQL("DB Error (select guild): %s\n", mysql_error(&mysql_handle));
+ } else { //If we fail to confirm, don't touch the data.
+ sql_res = mysql_store_result(&mysql_handle);
+ sql_row = mysql_fetch_row(sql_res);
+ if (sql_row)
+ guild_exist = atoi(sql_row[0]);
+ mysql_free_result(sql_res);
+ }
+ if (guild_exist==0) p->guild_id=0;
+ */
+ if (party_exist==0) p->party_id=0; //Parties are not converted. [Skotlex]
+
+ //sql query
+ //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9
+ //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15
+ //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21
+ //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27
+ //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35
+ //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`)
+
+ //Rather than converting the update to a insert, let's insert a blank char. [Skotlex]
+ sprintf(tmp_sql, "INSERT INTO `%s` (`char_id`, `account_id`, `name`, `char_num`) VALUES ('%d','%d','%s', '%d')", char_db, char_id, p->account_id, p->name, p->char_num);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert char): %s\n", mysql_error(&mysql_handle));
+ //If there is an error, we could assume it already exists, so just update anyway.
+
+ sprintf(tmp_sql ,"UPDATE `%s` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
+ "`base_exp`='%d', `job_exp`='%d', `zeny`='%d',"
+ "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
+ "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
+ "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+ "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
+ "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
+ "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d'"
+ "WHERE `account_id`='%d' AND `char_id` = '%d'",
+ char_db, p->class_, p->base_level, p->job_level,
+ p->base_exp, p->job_exp, p->zeny,
+ p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
+ p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
+ 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,
+ mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
+ mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->partner_id, p->father, p->mother,
+ p->child, p->fame, p->account_id, p->char_id
+ );
+
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (update char): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " status");
+/*
+ }
+ diff = 0;
+
+ for(i=0;i<MAX_MEMOPOINTS;i++){
+ if((strcmp(p->memo_point[i].map,cp->memo_point[i].map) == 0) && (p->memo_point[i].x == cp->memo_point[i].x) && (p->memo_point[i].y == cp->memo_point[i].y))
+ continue;
+ diff = 1;
+ break;
+ }
+*/
+ if (diff)
+ { //Save memo
+ //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, p->char_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (delete memo): %s\n", mysql_error(&mysql_handle));
+
+ //insert here.
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db);
+ count = 0;
+ for(i=0;i<MAX_MEMOPOINTS;i++){
+ if(p->memo_point[i].map){
+ tmp_ptr += sprintf(tmp_ptr,"('%d', '%s', '%d', '%d'),",
+ char_id, mapindex_id2name(p->memo_point[i].map), p->memo_point[i].x, p->memo_point[i].y);
+ count++;
+ }
+ }
+ if (count)
+ { //Dangerous? Only if none of the above sprintf worked. [Skotlex]
+ tmp_ptr[-1] = '\0'; //Remove the trailing comma.
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert memo): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " memo");
+ } else //Memo Points cleared (how is this possible?).
+ strcat(save_status, " memo");
+ }
+/*
+ diff = 0;
+ for(i=0;i<MAX_SKILL;i++) {
+ if ((p->skill[i].lv != 0) && (p->skill[i].id == 0))
+ p->skill[i].id = i; // Fix skill tree
+
+ if((p->skill[i].id != cp->skill[i].id) || (p->skill[i].lv != cp->skill[i].lv) ||
+ (p->skill[i].flag != cp->skill[i].flag))
+ {
+ diff = 1;
+ break;
+ }
+ }
+*/
+ if (diff)
+ { //Save skills
+
+ //`skill` (`char_id`, `id`, `lv`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, p->char_id);
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (delete skill): %s\n", mysql_error(&mysql_handle));
+
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s`(`char_id`,`id`,`lv`) VALUES ", skill_db);
+ count = 0;
+ //insert here.
+ for(i=0;i<MAX_SKILL;i++){
+ if(p->skill[i].id && p->skill[i].flag!=1)
+ {
+ tmp_ptr += sprintf(tmp_ptr,"('%d','%d','%d'),",
+ char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2);
+ count++;
+ }
+ }
+
+ if (count)
+ {
+ tmp_ptr[-1] = '\0'; //Remove trailing comma.
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert skill): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " skills");
+ } else //Skills removed (reset?)
+ strcat(save_status, " skills");
+ }
+/*
+ diff = 0;
+ for(i=0;i<p->global_reg_num;i++) {
+ if ((p->global_reg[i].str == NULL) && (cp->global_reg[i].str == NULL))
+ continue;
+ if (((p->global_reg[i].str == NULL) != (cp->global_reg[i].str == NULL)) ||
+ (p->global_reg[i].value != cp->global_reg[i].value) ||
+ strcmp(p->global_reg[i].str, cp->global_reg[i].str) != 0
+ ) {
+ diff = 1;
+ break;
+ }
+ }
+*/
+/*
+ if (diff)
+ { //Save global registry.
+ //`global_reg_value` (`char_id`, `str`, `value`)
+
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, p->char_id);
+ if (mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (delete global_reg_value): %s\n", mysql_error(&mysql_handle));
+
+ //insert here.
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s` (`char_id`, `str`, `value`) VALUES", reg_db);
+ count = 0;
+ for(i=0;i<p->global_reg_num;i++)
+ {
+ if (p->global_reg[i].str && p->global_reg[i].value !=0)
+ {
+ tmp_ptr += sprintf(tmp_ptr,"('%d','%s','%s'),",
+ char_id, jstrescapecpy(temp_str,p->global_reg[i].str), p->global_reg[i].value);
+ if (++count%100 == 0)
+ { //Save every X registers to avoid overflowing tmp_sql [Skotlex]
+ tmp_ptr[-1] = '\0';
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert global_reg_value sub): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " global_reg");
+
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s` (`char_id`, `str`, `value`) VALUES", reg_db);
+ count = 0;
+ }
+ }
+ }
+
+ if (count)
+ {
+ tmp_ptr[-1] = '\0';
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert global_reg_value): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " global_reg");
+ } else //Values cleared.
+ strcat(save_status, " global_reg");
+ }
+*/
+/*
+ diff = 0;
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(p->friend_id[i] != cp->friend_id[i]){
+ diff = 1;
+ break;
+ }
+ }
+ if(diff)
+ { //Save friends
+ sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id);
+ if(mysql_query(&mysql_handle, tmp_sql)){
+ ShowSQL("DB Error (delete friend): %s\n", mysql_error(&mysql_handle));
+ }
+
+ tmp_ptr = tmp_sql;
+ tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s` (`char_id`, `friend_id`) VALUES ", friend_db);
+ count = 0;
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(p->friend_id[i] > 0)
+ {
+ tmp_ptr += sprintf(tmp_ptr, "('%d', '%d'),", char_id, p->friend_id[i]);
+ count++;
+ }
+ }
+ if (count)
+ {
+ tmp_ptr[-1] = '\0'; //Remove the last comma. [Skotlex]
+ if(mysql_query(&mysql_handle, tmp_sql))
+ ShowSQL("DB Error (insert friend): %s\n", mysql_error(&mysql_handle));
+ else
+ strcat(save_status, " friends");
+ } else //Friend list cleared.
+ strcat(save_status, " friends");
+
+ }
+*/
+ ShowInfo("Saved char %d:%s.\n", char_id, save_status);
+// memcpy(cp, p, sizeof(struct mmo_charstatus));
+
+ return 0;
+}
+
+//--------------------------------------------------------
+// Save registry to sql
+int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type){
+
+ int j;
+ char temp_str[64]; //Needs be twice the source to ensure it fits [Skotlex]
+ char temp_str2[512];
+ if (account_id<=0) return 0;
+
+ switch (type) {
+ case 3: //Char Reg
+ //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id);
+ break;
+ case 2: //Account Reg
+ //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, account_id);
+ break;
+ case 1: //Account2 Reg
+ ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
+ return 0;
+ default:
+ ShowError("inter_accreg_tosql: Invalid type %d\n", type);
+ return 0;
+
+ }
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ if (reg->reg_num<=0) return 0;
+
+ for(j=0;j<reg->reg_num;j++){
+ if(reg->reg[j].str != NULL){
+ sprintf(tmp_sql,"INSERT INTO `%s` (`type`, `account_id`, `char_id`, `str`, `value`) VALUES ('%d','%d','%d','%s','%s')",
+ reg_db, type, type!=3?account_id:0, type==3?char_id:0,
+ jstrescapecpy(temp_str,reg->reg[j].str), jstrescapecpy(temp_str2,reg->reg[j].value));
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ return 1;
+}
+//Note: Keep this function updated with the one in src/char/int_storage.c [Skotlex]
+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++){
+ 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;
+ }
+ return 0;
+}
+
+//Note: Keep this function synched with the one in src/char_sql/int_storage.c
+int storage_tosql(int account_id,struct storage *p){
+ int i;
+// int eqcount=1;
+// int noteqcount=1;
+ int count=0;
+ struct itemtmp mapitem[MAX_GUILD_STORAGE];
+ for(i=0;i<MAX_STORAGE;i++){
+ if(p->storage_[i].nameid>0){
+ mapitem[count].flag=0;
+ mapitem[count].id = p->storage_[i].id;
+ mapitem[count].nameid=p->storage_[i].nameid;
+ mapitem[count].amount = p->storage_[i].amount;
+ mapitem[count].equip = p->storage_[i].equip;
+ mapitem[count].identify = p->storage_[i].identify;
+ mapitem[count].refine = p->storage_[i].refine;
+ mapitem[count].attribute = p->storage_[i].attribute;
+ mapitem[count].card[0] = p->storage_[i].card[0];
+ mapitem[count].card[1] = p->storage_[i].card[1];
+ mapitem[count].card[2] = p->storage_[i].card[2];
+ mapitem[count].card[3] = p->storage_[i].card[3];
+ count++;
+ }
+ }
+
+ memitemdata_to_sql(mapitem, count, account_id,TABLE_STORAGE);
+
+ //printf ("storage dump to DB - id: %d (total: %d)\n", account_id, j);
+ return 0;
+}
+
+//Note: Keep updated with the function in src/char/int_pet.c [Skotlex]
+int inter_pet_fromstr(char *str,struct s_pet *p)
+{
+ int s;
+ int tmp_int[16];
+ char tmp_str[256];
+
+ memset(p,0,sizeof(struct s_pet));
+
+// printf("sscanf pet main info\n");
+ s=sscanf(str,"%d,%d,%[^\t]\t%d,%d,%d,%d,%d,%d,%d,%d,%d",&tmp_int[0],&tmp_int[1],tmp_str,&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]);
+
+ if(s!=12)
+ return 1;
+
+ p->pet_id = tmp_int[0];
+ p->class_ = tmp_int[1];
+ memcpy(p->name,tmp_str,NAME_LENGTH-1);
+ p->account_id = tmp_int[2];
+ p->char_id = tmp_int[3];
+ p->level = tmp_int[4];
+ p->egg_id = tmp_int[5];
+ p->equip = tmp_int[6];
+ p->intimate = tmp_int[7];
+ p->hungry = tmp_int[8];
+ p->rename_flag = tmp_int[9];
+ p->incuvate = tmp_int[10];
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+
+ return 0;
+}
+
+//Note: Keep updated with the function in src/char_sql/int_pet.c [Skotlex]
+int inter_pet_tosql(int pet_id, struct s_pet *p) {
+ //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`)
+ char t_name[NAME_LENGTH*2];
+
+ ShowInfo("Saving pet (%d)...\n",pet_id);
+
+ jstrescapecpy(t_name, p->name);
+
+ if(p->hungry < 0)
+ p->hungry = 0;
+ else if(p->hungry > 100)
+ p->hungry = 100;
+ if(p->intimate < 0)
+ p->intimate = 0;
+ else if(p->intimate > 1000)
+ p->intimate = 1000;
+ sprintf(tmp_sql,"SELECT * FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id);
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB server Error - %s\n", mysql_error(&mysql_handle) );
+ }
+ sql_res = mysql_store_result(&mysql_handle) ;
+ if (sql_res!=NULL && mysql_num_rows(sql_res)>0)
+ //row reside -> updating
+ sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incuvate`='%d' WHERE `pet_id`='%d'",
+ pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id,
+ p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate, p->pet_id);
+ else //no row -> insert
+ sprintf(tmp_sql,"INSERT INTO `%s` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) VALUES ('%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ pet_db, pet_id, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id,
+ p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate);
+ mysql_free_result(sql_res) ; //resource free
+ if(mysql_query(&mysql_handle, tmp_sql) ) {
+ ShowSQL("DB server Error (inset/update `pet`)- %s\n", mysql_error(&mysql_handle) );
+ }
+
+ ShowInfo("Pet saved (%d). \n", pet_id);
+ return 0;
+}
+
+
+int mmo_char_init(void){
+ char line[65536];
+ struct storage *storage_ = NULL;
+ struct s_pet *p;
+ int ret;
+ int i=0,set,tmp_int[2], c= 0;
+ char input;
+ FILE *fp;
+
+ mysql_init(&mysql_handle);
+ ShowInfo("Connect DB server.... (inter server)\n");
+ if(!mysql_real_connect(&mysql_handle, db_server_ip, db_server_id, db_server_pw,
+ db_server_logindb ,db_server_port, (char *)NULL, 0)) {
+ //pointer check
+ ShowFatalError("%s\n",mysql_error(&mysql_handle));
+ exit(1);
+ }
+ else {
+ ShowStatus ("connect success! (inter server)\n");
+ }
+
+ ShowWarning("Make sure you backup your databases before continuing!\n");
+ printf("\n");
+ ShowNotice("Do you wish to convert your Character Database to SQL? (y/n) : ");
+ input=getchar();
+ if(input == 'y' || input == 'Y'){
+ ShowStatus("Converting Character Database...\n");
+ fp=fopen("save/athena.txt","r");
+ char_dat = (struct character_data*)malloc(sizeof(struct character_data)*256);
+ char_max=256;
+ if(fp==NULL)
+ return 0;
+ while(fgets(line, 65535, fp)){
+ if(char_num>=char_max){
+ char_max+=256;
+ char_dat = (struct character_data*)realloc(char_dat, sizeof(char_dat[0]) *char_max);
+ }
+ memset(&char_dat[char_num], 0, sizeof(char_dat[0]));
+ ret=mmo_char_fromstr(line, &char_dat[char_num].status, &char_dat[char_num].global);
+ if(ret){
+ mmo_char_tosql(char_dat[char_num].status.char_id , &char_dat[char_num].status);
+ inter_accreg_tosql(char_dat[char_num].status.account_id, char_dat[char_num].status.char_id, &char_dat[char_num].global, 3); //Type 3: Character regs
+ if(char_dat[char_num].status.char_id>=char_id_count)
+ char_id_count=char_dat[char_num].status.char_id+1;
+ char_num++;
+ }
+ }
+ ShowStatus("char data convert end\n");
+ fclose(fp);
+ }
+
+ while(getchar() != '\n');
+ printf("\n");
+ ShowNotice("Do you wish to convert your Storage Database to SQL? (y/n) : ");
+ input=getchar();
+ if(input == 'y' || input == 'Y') {
+ printf("\n");
+ ShowStatus("Converting Storage Database...\n");
+ fp=fopen(storage_txt,"r");
+ if(fp==NULL){
+ ShowError("cant't read : %s\n",storage_txt);
+ return 0;
+ }
+
+ while(fgets(line,65535,fp)){
+ set=sscanf(line,"%d,%d",&tmp_int[0],&tmp_int[1]);
+ if(set==2) {
+ if(i==0){
+ storage_ = (struct storage*)malloc(sizeof(struct storage));
+ }else{
+ storage_ = (struct storage*)realloc(storage_,sizeof(struct storage)*(i+1));
+ }
+ memset(&storage_[i],0,sizeof(struct storage));
+ storage_[i].account_id=tmp_int[0];
+ storage_fromstr(line,&storage_[i]);
+ storage_tosql(tmp_int[0],&storage_[i]); //to sql. (dump)
+ i++;
+ }
+ }
+ fclose(fp);
+ }
+
+ while(getchar() != '\n');
+ printf("\n");
+ ShowNotice("Do you wish to convert your Pet Database to SQL? (y/n) : ");
+ input=getchar();
+ if(input == 'y' || input == 'Y') {
+ printf("\n");
+ ShowStatus("Converting Pet Database...\n");
+ if( (fp=fopen(pet_txt,"r")) ==NULL )
+ return 1;
+ p = (struct s_pet*)malloc(sizeof(struct s_pet));
+ while(fgets(line, sizeof(line), fp)){
+ if(p==NULL){
+ ShowFatalError("int_pet: out of memory!\n");
+ exit(0);
+ }
+ if(inter_pet_fromstr(line, p)==0 && p->pet_id>0){
+ //pet dump
+ inter_pet_tosql(p->pet_id,p);
+ }else{
+ ShowError("int_pet: broken data [%s] line %d\n", pet_txt, c);
+ }
+ c++;
+ }
+ fclose(fp);
+ }
+ return 0;
+}
+
+int inter_config_read(const char *cfgName) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ ShowStatus("Start reading interserver configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, 1020, fp)){
+ i=sscanf(line,"%[^:]:%s", w1, w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"storage_txt")==0){
+ ShowInfo ("set storage_txt : %s\n",w2);
+ strncpy(storage_txt, w2, sizeof(storage_txt));
+ } else if(strcmpi(w1,"pet_txt")==0){
+ ShowInfo ("set pet_txt : %s\n",w2);
+ strncpy(pet_txt, w2, sizeof(pet_txt));
+ }
+ //add for DB connection
+ else if(strcmpi(w1,"db_server_ip")==0){
+ strcpy(db_server_ip, w2);
+ ShowInfo ("set db_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_port")==0){
+ db_server_port=atoi(w2);
+ ShowInfo ("set db_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_id")==0){
+ strcpy(db_server_id, w2);
+ ShowInfo ("set db_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_pw")==0){
+ strcpy(db_server_pw, w2);
+ ShowInfo ("set db_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_logindb")==0){
+ strcpy(db_server_logindb, w2);
+ ShowInfo ("set db_server_logindb : %s\n",w2);
+ }
+ else if(strcmpi(w1,"char_db")==0){
+ strcpy(char_db, w2);
+ ShowInfo ("set char_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"cart_db")==0){
+ strcpy(cart_db, w2);
+ ShowInfo ("set cart_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"inventory_db")==0){
+ strcpy(inventory_db, w2);
+ ShowInfo ("set inventory_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"storage_db")==0){
+ strcpy(storage_db, w2);
+ ShowInfo ("set storage_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"reg_db")==0){
+ strcpy(reg_db, w2);
+ ShowInfo ("set reg_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"skill_db")==0){
+ strcpy(skill_db, w2);
+ ShowInfo ("set skill_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"memo_db")==0){
+ strcpy(memo_db, w2);
+ ShowInfo ("set memo_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"pet_db")==0){
+ strcpy(pet_db, w2);
+ ShowInfo ("set pet_db : %s\n",w2);
+ }
+ else if(strcmpi(w1,"friend_db")==0){
+ strcpy(friend_db, w2);
+ ShowInfo ("set friend_db : %s\n",w2);
+ //support the import command, just like any other config
+ }else if(strcmpi(w1,"import")==0){
+ inter_config_read(w2);
+ }
+ }
+ fclose(fp);
+
+ ShowStatus("Done reading %s.\n", cfgName);
+
+ return 0;
+}
+
+int char_config_read(const char *cfgName) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ ShowStatus ("Start reading char-server configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, 1020, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ i=sscanf(line,"%[^:]:%s", w1, w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"char_txt")==0){
+ ShowInfo ("set char_txt : %s\n",w2);
+ strcpy(char_txt, w2);
+ } else
+ if(strcmpi(w1,"friends_txt")==0){
+ ShowInfo ("set friends_txt : %s\n",w2);
+ strcpy(friends_txt, w2);
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading %s.\n", cfgName);
+
+ return 0;
+}
+
+int do_init(int argc, char **argv){
+
+ char_config_read((argc>1)?argv[1]:CHAR_CONF_NAME);
+ inter_config_read((argc>2)?argv[2]:INTER_CONF_NAME);
+ mapindex_init();
+
+ mmo_char_init();
+ ShowStatus("Everything's been converted!\n");
+ mapindex_final();
+ exit (0);
+}
+
+void do_final () {}
diff --git a/src/txt-converter/login-converter.c b/src/txt-converter/login-converter.c
new file mode 100644
index 000000000..1c8684e95
--- /dev/null
+++ b/src/txt-converter/login-converter.c
@@ -0,0 +1,227 @@
+// (c) eAthena Dev Team - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mysql.h>
+
+#include "../common/core.h"
+#include "../common/db.h"
+#include "../common/mmo.h"
+
+struct auth_dat_ {
+ int account_id, sex;
+ char userid[24], pass[24], 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 = You 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;
+
+char login_account_id[256]="account_id";
+char login_userid[256]="userid";
+char login_user_pass[256]="user_pass";
+char login_db[256]="login";
+
+static struct dbt *gm_account_db;
+
+int db_server_port = 3306;
+char db_server_ip[16] = "127.0.0.1";
+char db_server_id[32] = "ragnarok";
+char db_server_pw[32] = "ragnarok";
+char db_server_logindb[32] = "ragnarok";
+
+#define INTER_CONF_NAME "conf/inter_athena.conf"
+
+int isGM(int account_id)
+{
+ struct gm_account *p;
+ p = idb_get(gm_account_db,account_id);
+ if( p == NULL)
+ return 0;
+ return p->level;
+}
+
+int read_gm_account()
+{
+ char line[8192];
+ struct gm_account *p;
+ FILE *fp;
+ int c=0;
+
+ gm_account_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+
+ printf("Starting reading gm_account\n");
+
+ if( (fp=fopen("conf/GM_account.txt","r"))==NULL )
+ return 1;
+ while(fgets(line,sizeof(line),fp)){
+ if(line[0] == '/' || line[1] == '/' || line[2] == '/')
+ continue;
+
+ p = (struct gm_account*)malloc(sizeof(struct gm_account));
+ if(p==NULL){
+ printf("gm_account: out of memory!\n");
+ exit(0);
+ }
+
+ if(sscanf(line,"%d %d",&p->account_id,&p->level) != 2 || p->level <= 0) {
+ printf("gm_account: broken data [conf/GM_account.txt] line %d\n",c);
+ continue;
+ }
+ else {
+ if(p->level > 99)
+ p->level = 99;
+ idb_put(gm_account_db,p->account_id,p);
+ c++;
+ printf("GM ID: %d Level: %d\n",p->account_id,p->level);
+ }
+ }
+ fclose(fp);
+ printf("%d ID of gm_accounts read.\n",c);
+ return 0;
+}
+
+int mmo_auth_init(void)
+{
+ MYSQL mysql_handle;
+ char tmpsql[1024];
+ MYSQL_RES* sql_res ;
+ MYSQL_ROW sql_row ;
+ FILE *fp;
+ int account_id, logincount, user_level, state, n, i;
+ char line[2048], 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 t_uid[256];
+
+ mysql_init(&mysql_handle);
+ if(!mysql_real_connect(&mysql_handle, db_server_ip, db_server_id, db_server_pw,
+ db_server_logindb ,db_server_port, (char *)NULL, 0)) {
+ //pointer check
+ printf("%s\n",mysql_error(&mysql_handle));
+ exit(1);
+ }
+ else {
+ printf ("Connect: Success!\n");
+ }
+ printf ("Convert start...\n");
+
+
+ fp=fopen("save/account.txt","r");
+ auth_dat = (struct auth_dat_*)malloc(sizeof(auth_dat[0])*256);
+ if(fp==NULL)
+ return 0;
+ while(fgets(line,1023,fp)!=NULL){
+
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+
+ 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);
+
+ sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`"
+ " FROM `%s` WHERE `%s`='%s'", login_account_id, login_userid, login_user_pass, login_db, login_userid, t_uid);
+
+ if(mysql_query(&mysql_handle, tmpsql) ) {
+ printf("DB server Error - %s\n", mysql_error(&mysql_handle) );
+ }
+ user_level = isGM(account_id);
+ printf ("userlevel: %s (%d)- %d\n",userid, account_id, user_level);
+ sql_res = mysql_store_result(&mysql_handle) ;
+ sql_row = mysql_fetch_row(sql_res); //row fetching
+ if (!sql_row) //no row -> insert
+ sprintf(tmpsql, "INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `lastlogin`, `sex`, `logincount`, `email`, `level`) VALUES (%d, '%s', '%s', '%s', '%c', %d, 'user@athena', %d);",account_id , userid, pass,lastlogin,sex,logincount, user_level);
+ else //row reside -> updating
+ sprintf(tmpsql, "UPDATE `login` SET `account_id`='%d', `userid`='%s', `user_pass`='%s', `lastlogin`='%s', `sex`='%c', `logincount`='%d', `email`='user@athena', `level`='%d'\nWHERE `account_id`='%d';",account_id , userid, pass,lastlogin,sex,logincount, user_level, account_id);
+ printf ("Query: %s\n",tmpsql);
+ mysql_free_result(sql_res) ; //resource free
+ if(mysql_query(&mysql_handle, tmpsql) ) {
+ printf("DB server Error - %s\n", mysql_error(&mysql_handle) );
+ }
+ }
+ fclose(fp);
+
+ printf ("Convert end...\n");
+
+ return 0;
+}
+
+int login_config_read(const char *cfgName){
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ printf ("Start reading interserver configuration: %s\n",cfgName);
+
+ 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,"%[^:]:%s", w1, w2);
+ if(i!=2)
+ continue;
+
+ //add for DB connection
+ if(strcmpi(w1,"db_server_ip")==0){
+ strcpy(db_server_ip, w2);
+ printf ("set db_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_port")==0){
+ db_server_port=atoi(w2);
+ printf ("set db_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_id")==0){
+ strcpy(db_server_id, w2);
+ printf ("set db_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_pw")==0){
+ strcpy(db_server_pw, w2);
+ printf ("set db_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_logindb")==0){
+ strcpy(db_server_logindb, w2);
+ printf ("set db_server_logindb : %s\n",w2);
+ }
+ //support the import command, just like any other config
+ else if(strcmpi(w1,"import")==0){
+ login_config_read(w2);
+ }
+ }
+ fclose(fp);
+ printf ("End reading interserver configuration...\n");
+ return 0;
+}
+
+int do_init(int argc,char **argv)
+{
+ char input;
+ login_config_read( (argc>1)?argv[1]:INTER_CONF_NAME );
+ read_gm_account();
+
+ printf("\nWarning : Make sure you backup your databases before continuing!\n");
+ printf("\nDo you wish to convert your Login Database to SQL? (y/n) : ");
+ input=getchar();
+ if(input == 'y' || input == 'Y')
+ mmo_auth_init();
+ printf ("Everything's been converted!\n");
+ exit (0);
+}
+
+
+void do_final() {}
diff --git a/src/webserver/Makefile b/src/webserver/Makefile
new file mode 100644
index 000000000..149f5a900
--- /dev/null
+++ b/src/webserver/Makefile
@@ -0,0 +1,20 @@
+all:
+ #Generate framework...
+ $(CC) -c parse.c
+ $(CC) -c generate.c
+ $(CC) -c htmlstyle.c
+ $(CC) -c logs.c
+
+ #Generate "pages"...
+ cd pages && $(CC) -c about.c && cd ..
+ cd pages && $(CC) -c sample.c && cd ..
+ cd pages && $(CC) -c notdone.c && cd ..
+
+ #Building the server...
+ $(CC) -o webserver main.c parse.o generate.o htmlstyle.o \
+ logs.o pages/about.o pages/sample.o pages/notdone.o
+
+clean:
+ rm -f *.o
+ rm -f pages/*.o
+ rm -f webserver
diff --git a/src/webserver/doc/API.txt b/src/webserver/doc/API.txt
new file mode 100644
index 000000000..92f88c5e3
--- /dev/null
+++ b/src/webserver/doc/API.txt
@@ -0,0 +1,50 @@
+Here's the webserver API, so you can work on the webserver.
+
+My personal goal is to make this interface simple, so that coding it
+will be like coding in some scripting language...
+
+
+
+char *get_param(char in_string[500], char swhat[500]);
+
+This function simply returns various data from the query string.
+ *Pass get_param NOTHING longer than 500 in length!
+
+ What do I pass where in_string is?
+ The query string.
+
+ What do I pass where swhat is?
+ One of two things...
+ Either 0 for the path of the 'page'
+ or you can pass it the param you wish to lookup.
+
+
+
+
+
+
+char *get_query(char *inquery);
+
+This function simply returns a query string from the raw server request.
+This is used once in main, I doubt you'll need it.
+
+
+
+
+
+void web_send(int sockin, char *in_data);
+
+Super easy way of sending data to a webpage!
+Simply put in the socket name and then the data.
+
+ Ex:
+ web_send(socket, "I like cheese!\n");
+
+
+
+
+char *html_header(char* title);
+Easy way to print the eAthena header for the server.
+
+ Ex:
+ web_send(sockethere, html_header("About"));
diff --git a/src/webserver/doc/README b/src/webserver/doc/README
new file mode 100644
index 000000000..edcabf1eb
--- /dev/null
+++ b/src/webserver/doc/README
@@ -0,0 +1,11 @@
+This readme is intended for the programmers of eAthena.
+
+This webserver's apis are in API.txt.
+
+To make this simple, generate.c should handle most of the work this sever does
+in terms of what people see.
+
+When a request is made the server shoots it off to generate.c.
+
+You are welcome to create more functions used by generate.c to generate pages
+though, so don't feel limited by that one file.
diff --git a/src/webserver/generate.c b/src/webserver/generate.c
new file mode 100644
index 000000000..26d2c7492
--- /dev/null
+++ b/src/webserver/generate.c
@@ -0,0 +1,38 @@
+
+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
new file mode 100644
index 000000000..a1320a385
--- /dev/null
+++ b/src/webserver/htmlstyle.c
@@ -0,0 +1,51 @@
+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> &laquo; Portal &raquo;</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
new file mode 100644
index 000000000..faa1abf80
--- /dev/null
+++ b/src/webserver/logs.c
@@ -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
new file mode 100644
index 000000000..ac27c5e71
--- /dev/null
+++ b/src/webserver/main.c
@@ -0,0 +1,142 @@
+/***************************************************************************
+ 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
new file mode 100644
index 000000000..f1d94d1e0
--- /dev/null
+++ b/src/webserver/pages/about.c
@@ -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
new file mode 100644
index 000000000..07abd33da
--- /dev/null
+++ b/src/webserver/pages/notdone.c
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 000000000..7bec663f2
--- /dev/null
+++ b/src/webserver/pages/sample.c
@@ -0,0 +1,24 @@
+
+
+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
new file mode 100644
index 000000000..323261c6c
--- /dev/null
+++ b/src/webserver/parse.c
@@ -0,0 +1,135 @@
+#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/zlib/Makefile b/src/zlib/Makefile
new file mode 100644
index 000000000..21bd6cf37
--- /dev/null
+++ b/src/zlib/Makefile
@@ -0,0 +1,21 @@
+
+OBJS = unzip.o ioapi.o
+
+ifeq ($(findstring MINGW,$(CFLAGS)), MINGW)
+ OBJS += iowin32.o
+endif
+
+.c.o:
+ $(CC) -c $(CFLAGS) $*.c
+
+all: unz.o
+
+unz.o: $(OBJS)
+ ar rcs $@ $(OBJS)
+
+clean:
+ rm -f *.o
+
+# DO NOT DELETE
+ioapi.o: ioapi.h
+unzip.o: unzip.h ioapi.h crypt.h
diff --git a/src/zlib/crypt.h b/src/zlib/crypt.h
new file mode 100644
index 000000000..f14a628b4
--- /dev/null
+++ b/src/zlib/crypt.h
@@ -0,0 +1,132 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
+{
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+{
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
+ const char *passwd; /* password string */
+ unsigned char *buf; /* where to write header */
+ int bufSize;
+ unsigned long* pkeys;
+ const unsigned long* pcrc_32_tab;
+ unsigned long crcForCrypting;
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/src/zlib/ioapi.c b/src/zlib/ioapi.c
new file mode 100644
index 000000000..7f20c182f
--- /dev/null
+++ b/src/zlib/ioapi.c
@@ -0,0 +1,177 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+voidpf ZCALLBACK fopen_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK fread_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK fwrite_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK ftell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK fseek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK fclose_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK ferror_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+
+voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ FILE* file = NULL;
+ const char* mode_fopen = NULL;
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ mode_fopen = "rb";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ mode_fopen = "r+b";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ mode_fopen = "wb";
+
+ if ((filename!=NULL) && (mode_fopen != NULL))
+ file = fopen(filename, mode_fopen);
+ return file;
+}
+
+
+uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+
+uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK ftell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret;
+ ret = ftell((FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ int fseek_origin=0;
+ long ret;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ fseek_origin = SEEK_CUR;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ fseek_origin = SEEK_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ fseek_origin = SEEK_SET;
+ break;
+ default: return -1;
+ }
+ ret = 0;
+ fseek((FILE *)stream, offset, fseek_origin);
+ return ret;
+}
+
+int ZCALLBACK fclose_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = fclose((FILE *)stream);
+ return ret;
+}
+
+int ZCALLBACK ferror_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = ferror((FILE *)stream);
+ return ret;
+}
+
+void fill_fopen_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = fopen_file_func;
+ pzlib_filefunc_def->zread_file = fread_file_func;
+ pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+ pzlib_filefunc_def->ztell_file = ftell_file_func;
+ pzlib_filefunc_def->zseek_file = fseek_file_func;
+ pzlib_filefunc_def->zclose_file = fclose_file_func;
+ pzlib_filefunc_def->zerror_file = ferror_file_func;
+ pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/src/zlib/ioapi.h b/src/zlib/ioapi.h
new file mode 100644
index 000000000..e73a3b2bd
--- /dev/null
+++ b/src/zlib/ioapi.h
@@ -0,0 +1,75 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#ifndef _ZLIBIOAPI_H
+#define _ZLIBIOAPI_H
+
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ (1)
+#define ZLIB_FILEFUNC_MODE_WRITE (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
+
+#define ZLIB_FILEFUNC_MODE_EXISTING (4)
+#define ZLIB_FILEFUNC_MODE_CREATE (8)
+
+
+#ifndef ZCALLBACK
+
+#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#define ZCALLBACK CALLBACK
+#else
+#define ZCALLBACK
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
+typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
+typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
+typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
+
+typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+
+
+
+void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
+#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
+#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
+#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
+#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
+#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/zlib/iowin32.c b/src/zlib/iowin32.c
new file mode 100644
index 000000000..694bc033b
--- /dev/null
+++ b/src/zlib/iowin32.c
@@ -0,0 +1,270 @@
+/* iowin32.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdlib.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+#include "iowin32.h"
+
+#ifndef INVALID_HANDLE_VALUE
+#define INVALID_HANDLE_VALUE (0xFFFFFFFF)
+#endif
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+voidpf ZCALLBACK win32_open_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK win32_read_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK win32_write_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK win32_tell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK win32_seek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK win32_close_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK win32_error_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+typedef struct
+{
+ HANDLE hf;
+ int error;
+} WIN32FILE_IOWIN;
+
+voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ const char* mode_fopen = NULL;
+ DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
+ HANDLE hFile = 0;
+ voidpf ret=NULL;
+
+ dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0;
+
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ {
+ dwDesiredAccess = GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ dwShareMode = FILE_SHARE_READ;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = CREATE_ALWAYS;
+ }
+
+ if ((filename!=NULL) && (dwDesiredAccess != 0))
+ hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ hFile = NULL;
+
+ if (hFile != NULL)
+ {
+ WIN32FILE_IOWIN w32fiow;
+ w32fiow.hf = hFile;
+ w32fiow.error = 0;
+ ret = malloc(sizeof(WIN32FILE_IOWIN));
+ if (ret==NULL)
+ CloseHandle(hFile);
+ else *((WIN32FILE_IOWIN*)ret) = w32fiow;
+ }
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ if (!ReadFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+
+ if (hFile !=NULL)
+ if (!WriteFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+long ZCALLBACK win32_tell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret=-1;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=(long)dwSet;
+ }
+ return ret;
+}
+
+long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ DWORD dwMoveMethod=0xFFFFFFFF;
+ HANDLE hFile = NULL;
+
+ long ret=-1;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ dwMoveMethod = FILE_CURRENT;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ dwMoveMethod = FILE_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ dwMoveMethod = FILE_BEGIN;
+ break;
+ default: return -1;
+ }
+
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=0;
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_close_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+
+ if (stream!=NULL)
+ {
+ HANDLE hFile;
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ CloseHandle(hFile);
+ ret=0;
+ }
+ free(stream);
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_error_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+ if (stream!=NULL)
+ {
+ ret = ((WIN32FILE_IOWIN*)stream) -> error;
+ }
+ return ret;
+}
+
+void fill_win32_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = win32_open_file_func;
+ pzlib_filefunc_def->zread_file = win32_read_file_func;
+ pzlib_filefunc_def->zwrite_file = win32_write_file_func;
+ pzlib_filefunc_def->ztell_file = win32_tell_file_func;
+ pzlib_filefunc_def->zseek_file = win32_seek_file_func;
+ pzlib_filefunc_def->zclose_file = win32_close_file_func;
+ pzlib_filefunc_def->zerror_file = win32_error_file_func;
+ pzlib_filefunc_def->opaque=NULL;
+}
diff --git a/src/zlib/iowin32.h b/src/zlib/iowin32.h
new file mode 100644
index 000000000..e9c5f8b90
--- /dev/null
+++ b/src/zlib/iowin32.h
@@ -0,0 +1,21 @@
+/* iowin32.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <windows.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/zlib/unzip.c b/src/zlib/unzip.c
new file mode 100644
index 000000000..37b4f144b
--- /dev/null
+++ b/src/zlib/unzip.c
@@ -0,0 +1,1602 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read unzip.h for more info
+*/
+
+/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+compatibility with older software. The following is from the original crypt.c. Code
+woven in by Terry Thorsen 1/2003.
+*/
+/*
+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2000-Apr-09 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+ */
+
+/*
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+#ifndef CASESENSITIVITYDEFAULT_NO
+# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+# define CASESENSITIVITYDEFAULT_NO
+# endif
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+
+
+const char unz_copyright[] =
+ " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+ uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+ when reading and decompress it */
+typedef struct
+{
+ char *read_buffer; /* internal buffer for compressed data */
+ z_stream stream; /* zLib stream structure for inflate */
+
+ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
+ uLong stream_initialised; /* flag set if stream structure is initialised*/
+
+ uLong offset_local_extrafield;/* offset of the local extra field */
+ uInt size_local_extrafield;/* size of the local extra field */
+ uLong pos_local_extrafield; /* position in the local extra field in read*/
+
+ uLong crc32; /* crc32 of all data uncompressed */
+ uLong crc32_wait; /* crc32 we must obtain after decompress all */
+ uLong rest_read_compressed; /* number of byte to be decompressed */
+ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ uLong compression_method; /* compression method (0==store) */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ int raw;
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ unz_global_info gi; /* public global information */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ uLong num_file; /* number of the current file in the zipfile*/
+ uLong pos_in_central_dir; /* pos of the current file in the central dir*/
+ uLong current_file_ok; /* flag about the usability of the current file*/
+ uLong central_pos; /* position of the beginning of the central dir*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory with
+ respect to the starting disk number */
+
+ unz_file_info cur_file_info; /* public info about the current file in zip*/
+ unz_file_info_internal cur_file_info_internal; /* private info about it*/
+ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+ file if we are decompressing it */
+ int encrypted;
+# ifndef NOUNCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+# endif
+} unz_s;
+
+
+#ifndef NOUNCRYPT
+#include "crypt.h"
+#endif
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return UNZ_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return UNZ_ERRNO;
+ else
+ return UNZ_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int unzlocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int unzlocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+ const char* fileName1;
+ const char* fileName2;
+{
+ for (;;)
+ {
+ char c1=*(fileName1++);
+ char c2=*(fileName2++);
+ if ((c1>='a') && (c1<='z'))
+ c1 -= 0x20;
+ if ((c2>='a') && (c2<='z'))
+ c2 -= 0x20;
+ if (c1=='\0')
+ return ((c2=='\0') ? 0 : -1);
+ if (c2=='\0')
+ return 1;
+ if (c1<c2)
+ return -1;
+ if (c1>c2)
+ return 1;
+ }
+}
+
+
+#ifdef CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+ const char* fileName1;
+ const char* fileName2;
+ int iCaseSensitivity;
+{
+ if (iCaseSensitivity==0)
+ iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+ if (iCaseSensitivity==1)
+ return strcmp(fileName1,fileName2);
+
+ return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong unzlocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
+ "zlib/zlib114.zip".
+ If the zipfile cannot be opened (file doesn't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
+ const char *path;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ unz_s us;
+ unz_s *s;
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+
+ int err=UNZ_OK;
+
+ if (unz_copyright[0]!=' ')
+ return NULL;
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&us.z_filefunc);
+ else
+ us.z_filefunc = *pzlib_filefunc_def;
+
+ us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
+ path,
+ ZLIB_FILEFUNC_MODE_READ |
+ ZLIB_FILEFUNC_MODE_EXISTING);
+ if (us.filestream==NULL)
+ return NULL;
+
+ central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
+ if (central_pos==0)
+ err=UNZ_ERRNO;
+
+ if (ZSEEK(us.z_filefunc, us.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+ /* the signature, already checked */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((number_entry_CD!=us.gi.number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=UNZ_BADZIPFILE;
+
+ /* size of the central directory */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* zipfile comment length */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
+ (err==UNZ_OK))
+ err=UNZ_BADZIPFILE;
+
+ if (err!=UNZ_OK)
+ {
+ ZCLOSE(us.z_filefunc, us.filestream);
+ return NULL;
+ }
+
+ us.byte_before_the_zipfile = central_pos -
+ (us.offset_central_dir+us.size_central_dir);
+ us.central_pos = central_pos;
+ us.pfile_in_zip_read = NULL;
+ us.encrypted = 0;
+
+
+ s=(unz_s*)ALLOC(sizeof(unz_s));
+ *s=us;
+ unzGoToFirstFile((unzFile)s);
+ return (unzFile)s;
+}
+
+
+extern unzFile ZEXPORT unzOpen (path)
+ const char *path;
+{
+ return unzOpen2(path, NULL);
+}
+
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+ unzFile file;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ if (s->pfile_in_zip_read!=NULL)
+ unzCloseCurrentFile(file);
+
+ ZCLOSE(s->z_filefunc, s->filestream);
+ TRYFREE(s);
+ return UNZ_OK;
+}
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+ unzFile file;
+ unz_global_info *pglobal_info;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ *pglobal_info=s->gi;
+ return UNZ_OK;
+}
+
+
+/*
+ Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+ uLong ulDosDate;
+ tm_unz* ptm;
+{
+ uLong uDate;
+ uDate = (uLong)(ulDosDate>>16);
+ ptm->tm_mday = (uInt)(uDate&0x1f) ;
+ ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+ ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+ ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+ ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
+ ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+ Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
+ unz_file_info *pfile_info,
+ unz_file_info_internal
+ *pfile_info_internal,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+ pfile_info,
+ pfile_info_internal,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ unz_file_info_internal *pfile_info_internal;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ unz_s* s;
+ unz_file_info file_info;
+ unz_file_info_internal file_info_internal;
+ int err=UNZ_OK;
+ uLong uMagic;
+ long lSeek=0;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pos_in_central_dir+s->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+
+ /* we check the magic */
+ if (err==UNZ_OK) {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x02014b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ lSeek+=file_info.size_filename;
+ if ((err==UNZ_OK) && (szFileName!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_filename<fileNameBufferSize)
+ {
+ *(szFileName+file_info.size_filename)='\0';
+ uSizeRead = file_info.size_filename;
+ }
+ else
+ uSizeRead = fileNameBufferSize;
+
+ if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek -= uSizeRead;
+ }
+
+
+ if ((err==UNZ_OK) && (extraField!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_extra<extraFieldBufferSize)
+ uSizeRead = file_info.size_file_extra;
+ else
+ uSizeRead = extraFieldBufferSize;
+
+ if (lSeek!=0) {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek += file_info.size_file_extra - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_extra;
+
+
+ if ((err==UNZ_OK) && (szComment!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_comment<commentBufferSize)
+ {
+ *(szComment+file_info.size_file_comment)='\0';
+ uSizeRead = file_info.size_file_comment;
+ }
+ else
+ uSizeRead = commentBufferSize;
+
+ if (lSeek!=0) {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek+=file_info.size_file_comment - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_comment;
+
+ if ((err==UNZ_OK) && (pfile_info!=NULL))
+ *pfile_info=file_info;
+
+ if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+ *pfile_info_internal=file_info_internal;
+
+ return err;
+}
+
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+ pfile_info,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+ szFileName,fileNameBufferSize,
+ extraField,extraFieldBufferSize,
+ szComment,commentBufferSize);
+}
+
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ s->pos_in_central_dir=s->offset_central_dir;
+ s->num_file=0;
+ err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+ unzFile file;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */
+ if (s->num_file+1==s->gi.number_entry)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+ s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+ s->num_file++;
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+ unzFile file;
+ const char *szFileName;
+ int iCaseSensitivity;
+{
+ unz_s* s;
+ int err;
+
+ /* We remember the 'current' position in the file so that we can jump
+ * back there if we fail.
+ */
+ unz_file_info cur_file_infoSaved;
+ unz_file_info_internal cur_file_info_internalSaved;
+ uLong num_fileSaved;
+ uLong pos_in_central_dirSaved;
+
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+
+ if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+ return UNZ_PARAMERROR;
+
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ /* Save the current state */
+ num_fileSaved = s->num_file;
+ pos_in_central_dirSaved = s->pos_in_central_dir;
+ cur_file_infoSaved = s->cur_file_info;
+ cur_file_info_internalSaved = s->cur_file_info_internal;
+
+ err = unzGoToFirstFile(file);
+
+ while (err == UNZ_OK)
+ {
+ char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+ err = unzGetCurrentFileInfo(file,NULL,
+ szCurrentFileName,sizeof(szCurrentFileName)-1,
+ NULL,0,NULL,0);
+ if (err == UNZ_OK)
+ {
+ if (unzStringFileNameCompare(szCurrentFileName,
+ szFileName,iCaseSensitivity)==0)
+ return UNZ_OK;
+ err = unzGoToNextFile(file);
+ }
+ }
+
+ /* We failed, so restore the state of the 'current file' to where we
+ * were.
+ */
+ s->num_file = num_fileSaved ;
+ s->pos_in_central_dir = pos_in_central_dirSaved ;
+ s->cur_file_info = cur_file_infoSaved;
+ s->cur_file_info_internal = cur_file_info_internalSaved;
+ return err;
+}
+
+
+/*
+///////////////////////////////////////////
+// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+// I need random access
+//
+// Further optimization could be realized by adding an ability
+// to cache the directory in memory. The goal being a single
+// comprehensive file read to put the file I need in a memory.
+*/
+
+/*
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; // offset in file
+ uLong num_of_file; // # of file
+} unz_file_pos;
+*/
+
+extern int ZEXPORT unzGetFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ file_pos->pos_in_zip_directory = s->pos_in_central_dir;
+ file_pos->num_of_file = s->num_file;
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ /* jump to the right spot */
+ s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+ s->num_file = file_pos->num_of_file;
+
+ /* set the current file */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ /* return results */
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+// Unzip Helper Functions - should be here?
+///////////////////////////////////////////
+*/
+
+/*
+ Read the local header of the current zipfile
+ Check the coherency of the local header and info in the end of central
+ directory about this file
+ store in *piSizeVar the size of extra info in local header
+ (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+ poffset_local_extrafield,
+ psize_local_extrafield)
+ unz_s* s;
+ uInt* piSizeVar;
+ uLong *poffset_local_extrafield;
+ uInt *psize_local_extrafield;
+{
+ uLong uMagic,uData,uFlags;
+ uLong size_filename;
+ uLong size_extra_field;
+ int err=UNZ_OK;
+
+ *piSizeVar = 0;
+ *poffset_local_extrafield = 0;
+ *psize_local_extrafield = 0;
+
+ if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
+ s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+
+ if (err==UNZ_OK) {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x04034b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+/*
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+ err=UNZ_BADZIPFILE;
+*/
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+ err=UNZ_BADZIPFILE;
+
+ if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+ err=UNZ_BADZIPFILE;
+
+ *piSizeVar += (uInt)size_filename;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
+ err=UNZ_ERRNO;
+ *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+ SIZEZIPLOCALHEADER + size_filename;
+ *psize_local_extrafield = (uInt)size_extra_field;
+
+ *piSizeVar += (uInt)size_extra_field;
+
+ return err;
+}
+
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+ const char* password;
+{
+ int err=UNZ_OK;
+ uInt iSizeVar;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uLong offset_local_extrafield; /* offset of the local extra field */
+ uInt size_local_extrafield; /* size of the local extra field */
+# ifndef NOUNCRYPT
+ char source[12];
+# else
+ if (password != NULL)
+ return UNZ_PARAMERROR;
+# endif
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_PARAMERROR;
+
+ if (s->pfile_in_zip_read != NULL)
+ unzCloseCurrentFile(file);
+
+ if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+ &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+ return UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+ ALLOC(sizeof(file_in_zip_read_info_s));
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_INTERNALERROR;
+
+ pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+ pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+ pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+ pfile_in_zip_read_info->pos_local_extrafield=0;
+ pfile_in_zip_read_info->raw=raw;
+
+ if (pfile_in_zip_read_info->read_buffer==NULL)
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return UNZ_INTERNALERROR;
+ }
+
+ pfile_in_zip_read_info->stream_initialised=0;
+
+ if (method!=NULL)
+ *method = (int)s->cur_file_info.compression_method;
+
+ if (level!=NULL)
+ {
+ *level = 6;
+ switch (s->cur_file_info.flag & 0x06)
+ {
+ case 6 : *level = 1; break;
+ case 4 : *level = 2; break;
+ case 2 : *level = 9; break;
+ }
+ }
+
+ if ((s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+ pfile_in_zip_read_info->crc32=0;
+ pfile_in_zip_read_info->compression_method =
+ s->cur_file_info.compression_method;
+ pfile_in_zip_read_info->filestream=s->filestream;
+ pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
+ pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+ pfile_in_zip_read_info->stream.total_out = 0;
+
+ if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
+ (!raw))
+ {
+ pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+ pfile_in_zip_read_info->stream.zfree = (free_func)0;
+ pfile_in_zip_read_info->stream.opaque = (voidpf)0;
+ pfile_in_zip_read_info->stream.next_in = (voidpf)0;
+ pfile_in_zip_read_info->stream.avail_in = 0;
+
+ err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+ if (err == Z_OK)
+ pfile_in_zip_read_info->stream_initialised=1;
+ else
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return err;
+ }
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END.
+ * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+ * size of both compressed and uncompressed data
+ */
+ }
+ pfile_in_zip_read_info->rest_read_compressed =
+ s->cur_file_info.compressed_size ;
+ pfile_in_zip_read_info->rest_read_uncompressed =
+ s->cur_file_info.uncompressed_size ;
+
+
+ pfile_in_zip_read_info->pos_in_zipfile =
+ s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+ iSizeVar;
+
+ pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+ s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+# ifndef NOUNCRYPT
+ if (password != NULL)
+ {
+ int i;
+ s->pcrc_32_tab = get_crc_table();
+ init_keys(password,s->keys,s->pcrc_32_tab);
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pfile_in_zip_read->pos_in_zipfile +
+ s->pfile_in_zip_read->byte_before_the_zipfile,
+ SEEK_SET)!=0)
+ return UNZ_INTERNALERROR;
+ if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12)
+ return UNZ_INTERNALERROR;
+
+ for (i = 0; i<12; i++)
+ zdecode(s->keys,s->pcrc_32_tab,source[i]);
+
+ s->pfile_in_zip_read->pos_in_zipfile+=12;
+ s->encrypted=1;
+ }
+# endif
+
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile (file)
+ unzFile file;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword (file, password)
+ unzFile file;
+ const char* password;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+{
+ return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/*
+ Read bytes from the current file.
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile (file, buf, len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ int err=UNZ_OK;
+ uInt iRead = 0;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->read_buffer == NULL))
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (len==0)
+ return 0;
+
+ pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+ pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+
+ if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
+ (!(pfile_in_zip_read_info->raw)))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+ if ((len>pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in) &&
+ (pfile_in_zip_read_info->raw))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in;
+
+ while (pfile_in_zip_read_info->stream.avail_out>0)
+ {
+ if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+ (pfile_in_zip_read_info->rest_read_compressed>0))
+ {
+ uInt uReadThis = UNZ_BUFSIZE;
+ if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+ uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+ if (uReadThis == 0)
+ return UNZ_EOF;
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->pos_in_zipfile +
+ pfile_in_zip_read_info->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->read_buffer,
+ uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+
+
+# ifndef NOUNCRYPT
+ if(s->encrypted)
+ {
+ uInt i;
+ for(i=0;i<uReadThis;i++)
+ pfile_in_zip_read_info->read_buffer[i] =
+ zdecode(s->keys,s->pcrc_32_tab,
+ pfile_in_zip_read_info->read_buffer[i]);
+ }
+# endif
+
+
+ pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+ pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+
+ pfile_in_zip_read_info->stream.next_in =
+ (Bytef*)pfile_in_zip_read_info->read_buffer;
+ pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+ }
+
+ if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
+ {
+ uInt uDoCopy,i ;
+
+ if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ return (iRead==0) ? UNZ_EOF : iRead;
+
+ if (pfile_in_zip_read_info->stream.avail_out <
+ pfile_in_zip_read_info->stream.avail_in)
+ uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+ else
+ uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+
+ for (i=0;i<uDoCopy;i++)
+ *(pfile_in_zip_read_info->stream.next_out+i) =
+ *(pfile_in_zip_read_info->stream.next_in+i);
+
+ pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+ pfile_in_zip_read_info->stream.next_out,
+ uDoCopy);
+ pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+ pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+ pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+ pfile_in_zip_read_info->stream.next_out += uDoCopy;
+ pfile_in_zip_read_info->stream.next_in += uDoCopy;
+ pfile_in_zip_read_info->stream.total_out += uDoCopy;
+ iRead += uDoCopy;
+ }
+ else
+ {
+ uLong uTotalOutBefore,uTotalOutAfter;
+ const Bytef *bufBefore;
+ uLong uOutThis;
+ int flush=Z_SYNC_FLUSH;
+
+ uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+ bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+ /*
+ if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+ pfile_in_zip_read_info->stream.avail_out) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ flush = Z_FINISH;
+ */
+ err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+ if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
+ err = Z_DATA_ERROR;
+
+ uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+ uOutThis = uTotalOutAfter-uTotalOutBefore;
+
+ pfile_in_zip_read_info->crc32 =
+ crc32(pfile_in_zip_read_info->crc32,bufBefore,
+ (uInt)(uOutThis));
+
+ pfile_in_zip_read_info->rest_read_uncompressed -=
+ uOutThis;
+
+ iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+
+ if (err==Z_STREAM_END)
+ return (iRead==0) ? UNZ_EOF : iRead;
+ if (err!=Z_OK)
+ break;
+ }
+ }
+
+ if (err==Z_OK)
+ return iRead;
+ return err;
+}
+
+
+/*
+ Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+extern int ZEXPORT unzeof (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field that can be read
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uInt read_now;
+ uLong size_to_read;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
+ pfile_in_zip_read_info->pos_local_extrafield);
+
+ if (buf==NULL)
+ return (int)size_to_read;
+
+ if (len>size_to_read)
+ read_now = (uInt)size_to_read;
+ else
+ read_now = (uInt)len ;
+
+ if (read_now==0)
+ return 0;
+
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->offset_local_extrafield +
+ pfile_in_zip_read_info->pos_local_extrafield,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ buf,read_now)!=read_now)
+ return UNZ_ERRNO;
+
+ return (int)read_now;
+}
+
+/*
+ Close the file in zip opened with unzipOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+ (!pfile_in_zip_read_info->raw))
+ {
+ if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+ err=UNZ_CRCERROR;
+ }
+
+
+ TRYFREE(pfile_in_zip_read_info->read_buffer);
+ pfile_in_zip_read_info->read_buffer = NULL;
+ if (pfile_in_zip_read_info->stream_initialised)
+ inflateEnd(&pfile_in_zip_read_info->stream);
+
+ pfile_in_zip_read_info->stream_initialised = 0;
+ TRYFREE(pfile_in_zip_read_info);
+
+ s->pfile_in_zip_read=NULL;
+
+ return err;
+}
+
+
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+ unzFile file;
+ char *szComment;
+ uLong uSizeBuf;
+{
+ // int err=UNZ_OK; // Unused [Lance]
+ unz_s* s;
+ uLong uReadThis ;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ uReadThis = uSizeBuf;
+ if (uReadThis>s->gi.size_comment)
+ uReadThis = s->gi.size_comment;
+
+ if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (uReadThis>0)
+ {
+ *szComment='\0';
+ if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+ }
+
+ if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+ *(szComment+s->gi.size_comment)='\0';
+ return (int)uReadThis;
+}
+
+/* Additions by RX '2004 */
+extern uLong ZEXPORT unzGetOffset (file)
+ unzFile file;
+{
+ unz_s* s;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return 0;
+ if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
+ if (s->num_file==s->gi.number_entry)
+ return 0;
+ return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset (file, pos)
+ unzFile file;
+ uLong pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ s->pos_in_central_dir = pos;
+ s->num_file = s->gi.number_entry; /* hack */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
diff --git a/src/zlib/unzip.h b/src/zlib/unzip.h
new file mode 100644
index 000000000..c3206a058
--- /dev/null
+++ b/src/zlib/unzip.h
@@ -0,0 +1,354 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__;
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO (Z_ERRNO)
+#define UNZ_EOF (0)
+#define UNZ_PARAMERROR (-102)
+#define UNZ_BADZIPFILE (-103)
+#define UNZ_INTERNALERROR (-104)
+#define UNZ_CRCERROR (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+ uLong number_entry; /* total number of entries in
+ the central dir on this disk */
+ uLong size_comment; /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+ uLong version; /* version made by 2 bytes */
+ uLong version_needed; /* version needed to extract 2 bytes */
+ uLong flag; /* general purpose bit flag 2 bytes */
+ uLong compression_method; /* compression method 2 bytes */
+ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
+ uLong crc; /* crc-32 4 bytes */
+ uLong compressed_size; /* compressed size 4 bytes */
+ uLong uncompressed_size; /* uncompressed size 4 bytes */
+ uLong size_filename; /* filename length 2 bytes */
+ uLong size_file_extra; /* extra field length 2 bytes */
+ uLong size_file_comment; /* file comment length 2 bytes */
+
+ uLong disk_num_start; /* disk number start 2 bytes */
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+
+ tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
+ const char* fileName2,
+ int iCaseSensitivity));
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen OF((const char *path));
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
+ "zlib/zlib113.zip".
+ If the zipfile cannot be opened (file don't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+
+extern unzFile ZEXPORT unzOpen2 OF((const char *path,
+ zlib_filefunc_def* pzlib_filefunc_def));
+/*
+ Open a Zip file, like unzOpen, but provide a set of file low level API
+ for read/write the zip file (see ioapi.h)
+*/
+
+extern int ZEXPORT unzClose OF((unzFile file));
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
+ unz_global_info *pglobal_info));
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+ char *szComment,
+ uLong uSizeBuf));
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile OF((unzFile file,
+ const char *szFileName,
+ int iCaseSensitivity));
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+/* ****************************************** */
+/* Ryan supplied functions */
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; /* offset in zip file directory */
+ uLong num_of_file; /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+extern int ZEXPORT unzGoToFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+/* ****************************************** */
+
+extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
+ unz_file_info *pfile_info,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+/*
+ Get Info about the current file
+ if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+ the current file
+ if szFileName!=NULL, the filemane string will be copied in szFileName
+ (fileNameBufferSize is the size of the buffer)
+ if extraField!=NULL, the extra field information will be copied in extraField
+ (extraFieldBufferSize is the size of the buffer).
+ This is the Central-header version of the extra field
+ if szComment!=NULL, the comment string of the file will be copied in szComment
+ (commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+ from it, and close it (you can close it before reading all the file)
+ */
+
+extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
+ const char* password));
+/*
+ Open for reading data the current file in the zipfile.
+ password is a crypting password
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw,
+ const char* password));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+
+extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+/*
+ Close the file in zip opened with unzOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read bytes from the current file (opened by unzOpenCurrentFile)
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell OF((unzFile file));
+/*
+ Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof OF((unzFile file));
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+
+/***************************************************************************/
+
+/* Get the current file offset */
+extern uLong ZEXPORT unzGetOffset (unzFile file);
+
+/* Set the current file offset */
+extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
diff --git a/src/zlib/zconf.h b/src/zlib/zconf.h
new file mode 100644
index 000000000..e3b0c962e
--- /dev/null
+++ b/src/zlib/zconf.h
@@ -0,0 +1,332 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define alloc_func z_alloc_func
+# define free_func z_free_func
+# define in_func z_in_func
+# define out_func z_out_func
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/zlib/zlib-1.2.3 b/src/zlib/zlib-1.2.3
new file mode 100644
index 000000000..a6a9cc84d
--- /dev/null
+++ b/src/zlib/zlib-1.2.3
Binary files differ
diff --git a/src/zlib/zlib.h b/src/zlib/zlib.h
new file mode 100644
index 000000000..62d0e4675
--- /dev/null
+++ b/src/zlib/zlib.h
@@ -0,0 +1,1357 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.3, July 18th, 2005
+
+ Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.3"
+#define ZLIB_VERNUM 0x1230
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: binary or text */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ gzip header information passed to and from zlib routines. See RFC 1952
+ for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+ int text; /* true if compressed data believed to be text */
+ uLong time; /* modification time */
+ int xflags; /* extra flags (not used when writing a gzip file) */
+ int os; /* operating system */
+ Bytef *extra; /* pointer to extra field or Z_NULL if none */
+ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
+ uInt extra_max; /* space at extra (only when reading header) */
+ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
+ uInt name_max; /* space at name (only when reading header) */
+ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
+ uInt comm_max; /* space at comment (only when reading header) */
+ int hcrc; /* true if there was or will be a header crc */
+ int done; /* true when done reading gzip header (not used
+ when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_FIXED 4
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_TEXT 1
+#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+ decide how much data to accumualte before producing output, in order to
+ maximize compression.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ the value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update strm->data_type if it can make a good guess about
+ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it gets to the next deflate block boundary. When decoding the
+ zlib or gzip format, this will cause inflate() to return immediately after
+ the header and before the first block. When doing a raw inflate, inflate()
+ will go ahead and process the first block, and will return when it gets to
+ the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster approach
+ may be used for the single inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero),
+ no header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+ Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+ parameter only affects the compression ratio but not the correctness of the
+ compressed output even if it is not set appropriately. Z_FIXED prevents the
+ use of dynamic Huffman codes, allowing for a simpler decoder for special
+ applications.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front. In addition, the
+ current implementation of deflate will use at most the window size minus
+ 262 bytes of the provided dictionary.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain));
+/*
+ Fine tune deflate's internal compression parameters. This should only be
+ used by someone who understands the algorithm used by zlib's deflate for
+ searching for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit for their
+ specific input data. Read the deflate.c source code for the meaning of the
+ max_lazy, good_length, nice_length, and max_chain parameters.
+
+ deflateTune() can be called after deflateInit() or deflateInit2(), and
+ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit()
+ or deflateInit2(). This would be used to allocate an output buffer
+ for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the
+ bits leftover from a previous deflate stream when appending to it. As such,
+ this function can only be used for raw deflate, and must be used before the
+ first deflate() call after a deflateInit2() or deflateReset(). bits must be
+ less than or equal to 16, and that many of the least significant bits of
+ value will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ deflateSetHeader() provides gzip header information for when a gzip
+ stream is requested by deflateInit2(). deflateSetHeader() may be called
+ after deflateInit2() or deflateReset() and before the first call of
+ deflate(). The text, time, os, extra field, name, and comment information
+ in the provided gz_header structure are written to the gzip header (xflag is
+ ignored -- the extra flags are set according to the compression level). The
+ caller must assure that, if not Z_NULL, name and comment are terminated with
+ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+ available there. If hcrc is true, a gzip header crc is included. Note that
+ the current versions of the command-line version of gzip (up through version
+ 1.3.x) do not support header crc's, and will report that it is a "multi-part
+ gzip file" and give up.
+
+ If deflateSetHeader is not used, the default gzip header has text false,
+ the time set to zero, and os set to 255, with no extra, name, or comment
+ fields. The gzip header is returned to the default state by deflateReset().
+
+ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is
+ a crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
+ is set to null if there is no error message. inflateInit2 does not perform
+ any decompression apart from reading the zlib header if present: this will
+ be done by inflate(). (So next_in and avail_in may be modified, but next_out
+ and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called
+ immediately after inflateInit2() or inflateReset() and before any call of
+ inflate() to set the dictionary. The application must insure that the
+ dictionary that was used for compression is provided.
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ This function inserts bits in the inflate input stream. The intent is
+ that this function is used to start inflating at a bit position in the
+ middle of a byte. The provided bits will be used before any bytes are used
+ from next_in. This function should only be used with raw inflate, and
+ should be used before the first inflate() call after inflateInit2() or
+ inflateReset(). bits must be less than or equal to 16, and that many of the
+ least significant bits of value will be inserted in the input.
+
+ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ inflateGetHeader() requests that gzip header information be stored in the
+ provided gz_header structure. inflateGetHeader() may be called after
+ inflateInit2() or inflateReset(), and before the first call of inflate().
+ As inflate() processes the gzip stream, head->done is zero until the header
+ is completed, at which time head->done is set to one. If a zlib stream is
+ being decoded, then head->done is set to -1 to indicate that there will be
+ no gzip header information forthcoming. Note that Z_BLOCK can be used to
+ force inflate() to return immediately after header processing is complete
+ and before any actual data is decompressed.
+
+ The text, time, xflags, and os fields are filled in with the gzip header
+ contents. hcrc is set to true if there is a header CRC. (The header CRC
+ was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+ contains the maximum number of bytes to write to extra. Once done is true,
+ extra_len contains the actual extra field length, and extra contains the
+ extra field, or that field truncated if extra_max is less than extra_len.
+ If name is not Z_NULL, then up to name_max characters are written there,
+ terminated with a zero unless the length is greater than name_max. If
+ comment is not Z_NULL, then up to comm_max characters are written there,
+ terminated with a zero unless the length is greater than comm_max. When
+ any of extra, name, or comment are not Z_NULL and the respective field is
+ not present in the header, then that field is set to Z_NULL to signal its
+ absence. This allows the use of deflateSetHeader() with the returned
+ structure to duplicate the header. However if those fields are set to
+ allocated memory, then the application will need to save those pointers
+ elsewhere so that they can be eventually freed.
+
+ If inflateGetHeader is not used, then the header information is simply
+ discarded. The header is always checked for validity, including the header
+ CRC if present. inflateReset() will reset the process to discard the header
+ information. The application would need to call inflateGetHeader() again to
+ retrieve the header from the next gzip stream.
+
+ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+ be allocated, or Z_VERSION_ERROR if the version of the library does not
+ match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free
+ the allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects
+ only the raw deflate stream to decompress. This is different from the
+ normal behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format
+ error in the deflate stream (in which case strm->msg is set to indicate the
+ nature of the error), or Z_STREAM_ERROR if the stream was not properly
+ initialized. In the case of Z_BUF_ERROR, an input or output error can be
+ distinguished using strm->next_in which will be Z_NULL only if in() returned
+ an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+ out() returning non-zero. (in() will always be called before out(), so
+ strm->next_in is assured to be defined if out() returns non-zero.) Note
+ that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least the value returned
+ by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before
+ a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h", or 'R' for run-length encoding
+ as in "wb1R". (See the description of deflateInit2 for more information
+ about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error). The number of
+ uncompressed bytes written is limited to 4095. The caller should assure that
+ this limit is not exceeded. If it is exceeded, then gzprintf() will return
+ return an error (0) with nothing written. In this case, there may also be a
+ buffer overflow with unpredictable consequences, which is possible only if
+ zlib was compiled with the insecure functions sprintf() or vsprintf()
+ because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read again later.
+ Only one character of push-back is allowed. gzungetc() returns the
+ character pushed, or -1 on failure. gzungetc() will fail if a
+ character has been pushed but not read yet, or if c is -1. The pushed
+ character will be discarded if the stream is repositioned with gzseek()
+ or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+ Returns 1 if file is being read directly without decompression, otherwise
+ zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+ z_off_t len2));
+/*
+ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
+ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running CRC-32 with the bytes buf[0..len-1] and return the
+ updated CRC-32. If buf is NULL, this function returns the required initial
+ value for the for the crc. Pre- and post-conditioning (one's complement) is
+ performed within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+/*
+ Combine two CRC-32 check values into one. For two sequences of bytes,
+ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
+ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+ len2.
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */